728x90

최근 중국의 모든 웹사이트들은 모두 회색으로 되어 버렸다.

처음에는 나의 6개월밖에 되지 않은 m1 맥북 모니터가 맞탱이 간줄 알았지만 알고 보니 중국내의 높은 분이 돌아갔다고서 모든 웹페이지가 회색으로 처리 되었던것이다.

아마도 최근 몇년동안 있는 서거한 인물중에 가장 큰 인물이라 그런가 보다. 하지만 개발자인 나는 그것보다 어떻게 나라의 모든 포탈포함해서 모든 국내사이트들을 이렇게 빠짐없이 "빠른 시간에 바로 회색으로 처리 되었을까?" 하는 의문을 가지게 되었고 비교적 효과적으로 처리할수 있는 방법을 알아냈다.

방법1:

<style type="text/css">
html {
  filter:grayscale(100%);
  -webkit-filter:grayscale(100%);
  -moz-filter:grayscale(100%);
  -ms-filter:grayscale(100%);
  -o-filter:grayscale(100%);
 filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
  -webkit-filter:grayscale(1)
}
</style>

filter는 필터를 의미한다. filter:gray는 페이지에 그레이스케일 필터를 붙여서 html의 모든 내용이 흑백이 된다. 그러나 이 필터는 chrome과 safari 브라우저에는 먹히지 않기 때문에 아래에 -webkit-filter: grayscale(100%)이라는 문구가 있다. 이 형식은 webkit 커널을 사용하는 브라우저 전용으로 FILTER: gray;와 비슷하다.

 

방법2:

아래 코드는 css 의 제일 상단에 넣을가.

<style>
 html {
 filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
 -webkit-filter: grayscale(100%);}
</style>

혹시 표준 웹사트가 아니라서 css 코드가 안먹히는경우가 있다.

그려면 아래 처럼 입력해라. 아래 코드는 익숙하지?

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

설마 요즘 플래시 쓰는 웹사이트도 있을까? 하는 마음에 아래코드도 미리 작성해놨다.

<param value="false" name="menu"/>
<param value="opaque" name="wmode"/>

 

마지막으로 아래코드를 최종 참고해서 작성해보면 될것 것다.

html{
    -webkit-filter:grayscale(100%);
    -moz-filter:grayscale(100%);
    -ms-filter:grayscale(100%);
    -o-filter:grayscale(100%);
    filter:grayscale(100%);
    filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale");
    filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)
}

끝!

728x90

mysql 8.0 sequel Pro 접속 안될때. (ssl 이슈 이므로 , 아래 명령어 통해서 임시해결가능)

1. 도커 접속

# 실패!  localhost --> % 로 해야 됨.
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';

# 성공!
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';

끝!

'DB > RDBMS' 카테고리의 다른 글

MySQL 의 고급? 기능들  (0) 2022.12.21
Mysql Incorrect string value: '\xE5\xxxx\xxxxx' 'xxx' at row 1  (0) 2022.12.08
invalid default value for '컬럼명'  (0) 2022.12.08
728x90

사용배경:

모 회사에서 페이서비스를 만든다고 하자. 서로다른 고객 서로 다른 페이방식을 사용할것이다. 예컨대 , 네이버 페잇, 카카오페이, 유니온페이 등등.이때 일반적으로 if else 혹은 switch 로 고객타입을 체크하여 그에 맞는 페이방식을 사용하도록 코드를 작성할것이다.

그래서 한번 전략패턴으로 if 문 혹은 switch 문없이 처리해보겠다.

Base인터페이스 하나 만든다.

public interface BasePayment {
 
    /**
     * 기본 인터페이스
     * @param order
     * @return
     */
    PayResult pay(Order order);
 
}

Order 정보 클래스를 만든다.

@Data
public class Order {
 
    /**
     * 금액
     */
    private int amount;
 
    /**
     * 페이유형
     */
    private String paymentType;
 
}

Return 타입클래스도 만든다.

@Data
@AllArgsConstructor
public class PayResult {
 
    /**
     * 처리결과
     */
    private String result;
 
}

이제 Base 인터페이스 기준으로 각종? 구현 클래스를 한번 만들어 보자. (카카오 페이, 네이버 페이 등등 )

@Service("NaverPay")
public class NaverPay implements BasePayment {
 
    @Override
    public PayResult pay(Order order) {
        return new PayResult("네이버 페이 결제 성공!");
    }
 
}
@Service("KakaoPay")
public class KakaoPay implements BasePayment {
 
    @Override
    public PayResult pay(Order order) {
        return new PayResult("카카오페이 결제 성공!");
    }
 
}

뭐 유니온 페이도 한번 만들어본자 ^^

@Service("UnionPay")
public class UnionPay implements IPayment {
 
    @Override
    public PayResult pay(Order order) {
        return new PayResult("인련페이 지불성공");
    }
 
}

이제 컨트롤러를 하나 만들어보자

@RestController
public class PayService {
 
    @Autowired
    private ApplicationContext applicationContext;
 
    /**
     * 컨트롤러
     * @param amount
     * @param paymentType
     * @return
     */
    @RequestMapping("/pay")
    public PayResult pay(@RequestParam("amount") int amount,
                    @RequestParam("paymentType") String paymentType) {
        Order order = new Order();
        order.setAmount(amount);
        order.setPaymentType(paymentType);
 
        // paymentType으로 bean을 컨테이너로 부터 가져올수 있지
        IPayment payment = applicationContext.getBean(order.getPaymentType(), BasePayment.class);
 
        // 지불시작
        PayResult payResult = payment.pay(order);
 
        return payResult;
    }
 
}

끝!

728x90

프로젝트 관리하다가 외주업체에 맞겼는데 if..else로 도배를 해놓았던것이다.
음 개발자입문할때 누군가가 그러더라 "개발자는 if문,for문만 알아도 개발을 할수 있다" 고. 지금와서 생각해보니 틀린말은 아니다.
그런데 우리는 사람이다. 사람은 항상 생각하고 불편함을 해결하고 복잡한것보다 쉬운것을 추구한다.
그래서 나도 오늘 좀 생각없이 개발하는 아재 개발자들한테 if..else를 줄일수 있는 방법을 말해보려고 한다.
이미 알고 있는 고수분들은 그냥 잘난척하는 내앞을 지나가 주라!
일단 ...이런 코드를 많이 봤지?

@RestController
@PostMappting("/create")
public CommonResult testController(@RequestBody TestDto testDto ){
	
    if(StringUtils.isEmpty(testDto.getName()){
    	return CommonResult.validateFailed("이름이 비었음!");
    }
    if(testDto.getSort() == null || testDto.getSort() < 0){
    	return CommonResult.validateFailed("0 보다 작을수 없음!");
    }
    int count = testService.create(testDto);
    if(count ==1){
    	return CommonResult.success(testDto);
    }
}

Springboot 에서 Hibernate Validator library를 이용하여 검증을 하면 편하다.
1. library 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 객체를 만든다.

@Data
public class Student {

    @NotBlank(message = "이름은 필수잆력")
    private String name;
    @NotNull(message = "나이는 필수잆력")
    @Range(min = 1,max =50, message = "나이 범위1-50")
    private Integer age;
    @NotEmpty(message = "성적은 필수잆력")
    private List<Double> scores;
}

3. post 요청 controller 를 만든다.

@RestController
@RequestMapping("/student")
public class StudentController {

    @PostMapping("/add")
    public Rest<?> addStudent(@Valid @RequestBody Student student) {
        return RestBody.okData(student);
    }

요청결과:

POST /student/add HTTP/1.1
Host: localhost:8888
Content-Type: application/json

{
    "name": "felord.cn",
    "age": 77,
    "scores": [
        55
    ]
}

4. get 요청 controller 를 만든다.

@GetMapping("/get")
public Rest<?> getStudent(@Valid Student student) {
    return RestBody.okData(student);
}

요청결과:

GET /student/get?name=blake.com&age=12 HTTP/1.1
Host: localhost:8888

5. 커스텀 어노테이션
주위 깊게 보았으면 발견했을듯! 바로 Student 객체에 @NotNull + @Range 어노테이션을 겹쳐서 사용했다.

@NotNull(message = "나이필수 입력")
@Range(min = 1,max =50, message = "나이범위 1-50")
private Integer age;

원인은 @Range 가 공백값에 대한 제약을 할수 없다는것, 값이 있을때만 제약대상이다. 이처럼 어노테이션을 여러개 사용해야 하는경우가 가끔있는데 이럴때 이런 어노테이션들을 하나로 묶는것도 방법이긴 하다.

import org.hibernate.validator.constraints.Range;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;
import javax.validation.constraintvalidation.SupportedValidationTarget;
import javax.validation.constraintvalidation.ValidationTarget;
import java.lang.annotation.*;


@Constraint(
        validatedBy = {}
)
@SupportedValidationTarget({ValidationTarget.ANNOTATED_ELEMENT})
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD,
        ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,
        ElementType.PARAMETER, ElementType.TYPE_USE})
@NotNull
@Range(min = 1, max = 50)
@Documented
@ReportAsSingleViolation
public @interface Age {
    // message 필수
    String message() default "나이는 필수입력,범위는 1-50 ";

    // 선택
    Class<?>[] groups() default {};

    // 선택
    Class<? extends Payload>[] payload() default {};
}

혹은 상태와 같은 불변의 뭔가를 따로 관리해야 된다고 하면 enum클래스를 만들어서 사용하면 좋다.

public enum Colors {

    RED, YELLOW, BLUE

}

public class ColorConstraintValidator implements ConstraintValidator<Color, String> {
    private static final Set<String> COLOR_CONSTRAINTS = new HashSet<>();

    @Override
    public void initialize(Color constraintAnnotation) {
        Colors[] value = constraintAnnotation.value();
        List<String> list = Arrays.stream(value)
                .map(Enum::name)
                .collect(Collectors.toList());
        COLOR_CONSTRAINTS.addAll(list);

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return COLOR_CONSTRAINTS.contains(value);
    }
}

@Constraint(validatedBy = ColorConstraintValidator.class)
@Documented
@Target({ElementType.METHOD, ElementType.FIELD,
        ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,
 
        ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Color {
    // 에러 메시지
    String message() default "규격에 맞는 않음.";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    // 유형
    Colors[] value();
}

제약이 잘 걸리는지 함 테스트 해보자!

@Data
public class Param {
    @Color({Colors.BLUE,Colors.YELLOW})
   private String color;
}

요청하기

GET /student/color?color=CAY HTTP/1.1
Host: localhost:8888

결과:
BindException 발생한다!
CAY 는 존재하지 않기때문. 그래서 BLUE, YELLOW, RED를 넣으면 리턴이 정상이다.


트러블슈팅:

@GetMapping("/color")
public Rest<?> color(@Valid @Color({Colors.BLUE,Colors.YELLOW}) String color) {
    return RestBody.okData(color);
}

// 혹은

@GetMapping("/rest/{color}")
public Rest<?> rest(@Valid @Color({Colors.BLUE, Colors.YELLOW}) @PathVariable String color) {
    return RestBody.okData(color);
}

// 혹은

@PostMapping("/batchadd")
public Rest<?> batchAddStudent(@Valid @RequestBody List<Student> student) {
    return RestBody.okData(student);
}

이처럼 클래스아닌 method 에 적용하연 제약기능이 작동하지 않는다. 구글링해보면 클래스에 적용해야 되지 메서드에 적용하면 안된다는것을 할수 있다. (직접 검색해 보시길!)
마지막으로
어노테이션을 겹쳐서 사용시 제약이 걸리지 않는 문제 도 있음!

@Data
 
public class Student {

    @NotBlank(message = "이름 필수입력")
    private String name;
    @Age
    private Integer age;
    @NotEmpty(message = "성적 필수입력")
    private List<Double> scores;
    @NotNull(message = "학교 공백일수 없음")
    private School school;
}


@Data
public class School {
    @NotBlank(message = "이름 공백일수 없음")
    private String name;
    @Min(value = 0,message ="나이는 0보다 커야 됨." )
    private Integer age;
}

이런 구조일때 get 요청시 문제 없으니, post 요청시 School 에 대한 제약기능이 작동안된다.
이때 우리는 @Valid 어노테이션을 붙여주면 된다.

@Data
public class Student {
    
    @NotBlank(message = "이름 필수입력")
    private String name;
    @Age
    private Integer age;
    @NotEmpty(message = "성적 필수입력")
    private List<Double> scores;
    @Valid
    @NotNull(message = "학교 공백일수 없음")
    private School school;
}

참고:

// message는 오류 메시지를 정의하는 곳

// null만 허용
@Null(message="")

// null 허용 X,  "", " "는 허용
@NotNull(message="")

 // null, "" 허용 X,  " "는 허용
@NotEmpty(message="")

// null, "", " " 허용 X
@NotBlank  (message="")

// 데이터의 사이즈(최소 길이, 최대 길이) 설정
@Size(min=, max= ,message="")  

// 정규식을 이용해서 검사
@Pattern(regexp = ,message="")  

// value 이하의 값만 허용
@Max(value = ,message="")

// value 이상의 값만 허용
@Min(value = ,message="") 

// 값을 양수만 허용
@Positive(message="")

// 값을 양수와 0만 허용
@PositiveOrZero(message="")  

// 값을 음수만 허용
@Negative(message="")   

// 값을 음수와 0만 허용
@NegativeOrZero(message="")  

// 현재보다 미래의 날짜만 허용
@Future(message="")   

// 현재보다 과거의 날짜만 허용
@Past(message="")   

// True일 때만 허용(null 체크 X)
@AssertTrue(message="")

// False일 때만 허용(null 체크 X)
@AssertFalse(message="")

끝!

+ Recent posts