P3] Ch 06.스프링 기능 활용

uuuu.jini·2022년 1월 10일
0
post-thumbnail

목차

  1. Spring boot Validation
  2. Spring boot Custom Validation
  3. Filter-Interceptor 활용
  4. 비동기 처리

1. Spring Boot Validation

validation 사용을 위해서 build.gradle에 항상
implementation 'org.springframework.boot:spring-boot-starter-validation'을 추가하여 준다.
bean validation spec : 명세

validation 이란 유효하지 않은 값이 들어올 경우를 방지하기 위해 미리 검증하는 과정을 말한다. validation을 일일히 코드로 반복하여 작성하는 경우 코드의 길이가 길어지고 service logic과의 분리가 필요하며, 재사용의 한계가 있다. 그래서 spring에서는 일관된 validation 을 위한 어노테이션들을 제공한다.

각 validation의 어노테이션은 message라는 속성을 가지고 있으며, 이는 잘못된 정보를 입력받은 경우 출력할 에러 메세지를 지정할 수 있다.

@Pattern(regexp  ="---",message="지정한 형식과 일치하지 않습니다.")

해당 어노테이션은 regexp속성으로 지정한 정규식을 따르지 않을 경우 에러가 발생하고 이때에 출력할 문자를 message에 지정한다. 이외에 다양한 어노테이션은 앞으로 사용하며 학습할 예정이다.

예시 코드로 post메소드로 user object를 json형태의 data body로 받아서 validation검사를 진행한 후 에러 메세지를 출력하는 형태를 배웠다. @Valid 어노테이션이 붙은 객체는 해당 클래스의 validation 검사를 진행한다. 이때, 매개변수로 BindingResult 객체를 받을 수 있으며, 이는 validation의 결과를 저장한다.

    @PostMapping("/user")
    public ResponseEntity user(@Valid @RequestBody User user, BindingResult bindingResult){  

        if(bindingResult.hasErrors()){
            StringBuilder sb = new StringBuilder();
            bindingResult.getAllErrors().forEach(objectError -> {
                FieldError field  = (FieldError)objectError;
                String message = objectError.getDefaultMessage();
                System.out.println("field: " + field.getField());
                System.out.println("message: " + message);

                sb.append("field : " + field.getField());
                sb.append("message: " + message);
            });

            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb); 
        }

위의 코드는 에러 발생시 해당 에러를 출력하기 위한 예시이다.

ResponseEntity 객체는 http응답을 의미한다. http응답 상태를 리턴하기 위해 사용하며, 예로 responseEntity.status(HttpStatus.BAD_REQUEST)를 리턴할 경우 bad request를 반환한다. body 메서드에는 반환할 body를 지정할 수 있다.


2. Spring Boot Custom Validation

@AssertTure/False

클래스 내부의 메소드에 지정하여 custom logic을 사용하여 validation 검사를 할 수 있다. 해당 메소드의 반환값이 true인지 fasle인지에 따라 정상 작동인지 에러인지를 결정할 수 있으며, AssertTrue는 True를 정상으로 AssertFalse는 False를 정상으로 본다.

예제에서는 날짜의 형식이 yyyyMM인 경우에 true를 반환하여, 정상동작하고 그렇지 않을 경우 에러를 반환하게 한다.

@AssertTrue(message = "yyyyMM의 형식에 맞지 않습니다.") 
    public boolean isreqYearMonthValidation() { 
        try {
            LocalDate localDate = LocalDate.parse(getReqYearMonth() + "01", DateTimeFormatter.ofPattern("yyyyMMdd")); 
        } catch (Exception e) {
            return false; 
        }
        return true; 
    } 

해당 메소드는 다른 클래스에서 재사용이 불가능하다. 이는 코드의 반복을 증가하게 만든다.

boolean값을 리턴하는 경우의 메소드이름은 is 로 시작하여야 한다. isreqYearMonthValidation()

어노테이션 생성

위의 방법은 재사용이 불가능하여 반복을 증가시킨다. 여러번 사용하기 위한 validation은 어노테이션을 직접 생성하여 사용하는 방법을 이용한다. 어노테이션 생성시에는 @Constraint어노테이션의 validateBy속성으로 검사를 할 class를 넘겨주고, @Target@Retention어노테이션을 이용한다.

만약 검사하려고 하는 클래스 내부의 속성 중 다른 객체가 있을 경우 해당 객체 클래스를 검사하는 것이므로 해당 객체 위에 @Valid 어노테이션이 필요하다. 이를 사용하지 않을 경우 원하는 결과를 얻지 못하니 주의 하여야 한다. (클래스 내부의 객체(클래스)에도 @Valid붙이기)


3. Spring Boot Exception 처리

Exception 처리방법

  1. 에러페이지
  2. 4xx Error or 5xx Error
  3. 클라이언트가 200외에 처리하지 못할 경우 200을 내려주고 별도의 에러 Message 전달
    한번에 처리해주기 위한 기능을 스프링에서 제공해준다. 에러가 발생할 경우 클라이언트에게는 아주 간단한 에러 정보만이 표시가 된다.(불친절?하시다고 표현하셨음) 이것을 위해 정보를 제공해주는 즉 예외를 처리해주는 기능을 스프링에서 제공한다.

    유효성 검사: 요청받은 정보에 대한 올바른 정보인지 검사하는 것
    예외 처리: 에러 발생시 처리 할 방법

@RestControllerAdvice

Rest API 기준은 @RestConrollerAdvice이고 페이징 기준은 @ControllerAdvice를 사용한다. 해당 어노테이션은 global한 예외에 대한 처리를 책임지며, 이는 모든 클래스의 예외에 대한 처리를 실행하겠다는 의미이다.

@ExceptionalHandler

이 어노테이션은 어떠한 예외를 처리할 것인지를 지정해주는 것이다. 즉 , 특정한 예외에 대한 처리를 할 메소드 위에 매핑시켜준다. value 옵션으로 예외의 종류를 지정해줄 수 있으며, 해당 메소드의 매개변수로 에러(각 종류별 Exception)내용을 받아 올 수 있다.

해당 예제는, 모든 예외에 대한 내용을 콘솔출력해주며, body로는 아무런 내용도 반환해주지 않는다.

    @ExceptionHandler(value = Exception.class) 
    public ResponseEntity exception(Exception e){

        System.out.println(e.getClass().getName());
        System.out.println("-------------");
        System.out.println(e.getLocalizedMessage()); 
        System.out.println("-------------");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
    }

밑의 예제는 특정 예외에 대해서만 처리를 해주며, body로 전체 에러의 내용을 반환해준다.

@ExceptionHandler(value = MethodArgumentNotValidException.class) 
    public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); 
    }

전체 global한 클래스의 모든 예외를 처리하는 것이 아닌 특정 클래스 내부에서만 예외를 처리하고 싶은 경우에는 해당 클래스 내에 @ExceptionHandler를 통해 예외시 처리할 메소드를 매핑해주면 된다. global과 특정 클래스 내부에 중복되어 예외처리가 존재 할 경우 우선순위는 항상 클래스 내부에 있는 예외처리 메소드가 먼저이다.

@RequestParam의 required옵션은 false일 경우 필수가 아니어도 된다는 의미로 즉 파라미터로 값이 들어가지 않아도 실행이 되게 한다.

쿼리 파라미터로 받는 값의 유효성 검사

전체 클래스에 @Validated 어노테이션 매핑 해주고 해당 메소드의 매개변수 값들에 validation 어노테이션들의 종류 중 원하는 검사를 매칭해주면 된다. 즉, 쿼리 파라미터로 들어오는 변수들의 유효성을 검사하여 에러를 발생시킬 수 있다.

    @GetMapping("")
    public User get(
                    @Size(min = 2) // validation 어노테이션 작성
                    //각 매개변수의 validation 검사가 가능하다. 
                    @RequestParam String name,

                    @NotNull
                    @Min(1)
                    @RequestParam Integer age)

4. Filter와 Interceptor

Lombok[롬복]

getter와 setter 등을 작성할 필요없이 간단하게 작성할 수 있는 기능을 제공한다. @Getter@Setter는 게터와 세터를 생성하고 @NoArgsConsturctor는 기본 생성자를 @AllrgsConstructor는 전체 인자를 받는 생성자를 생성한다.
게터와 세터 toString등 모두 생성을 원하는 경우에는 @Data를 사용한다.

@Slf4j 는 log.info를 통하여 간편하게 로그를 출력하기 위하여 필요한 어노테이션이다. log.info("User : {}", user); 중괄호 안에 순서대로 뒤의 변수가 매칭되어 출력된다.

Filter

filter는 Web Application에서 관리되는 영역으로써, Client로부터 오는 요청/응답에 대해 최초,최종 위치에 존재한다. 여기서, 해당 정보를 변경하거나,데이터가 spring에 의해 변경되기 전에 순수한 값을 확인할 수 있다. 보통은 log용으로 주로 사용한다고 하셨다. ServletRequest와 ServletResponse객체를 변환할 수 있다. (최전방에 위치하여서 정보 변경, logging등을 한다로 기억하기)

Filter를 implement하여 doFilter를 override하여 사용하며, 이때 chain.doFilter(httpServletRequest, httpServletResponse);를 기준으로 전처리과정과 후처리 과정을 작성한다.

@Slf4j
@Component 
public class GlobalFilter implements Filter {


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
        //전처리
        ContentCachingRequestWrapper httpServletRequest = new ContentCachingRequestWrapper((HttpServletRequest)request); 
        ContentCachingResponseWrapper httpServletResponse = new ContentCachingResponseWrapper((HttpServletResponse)response);


        chain.doFilter(httpServletRequest, httpServletResponse);
        
        //후처리 :  여기서 모든 기록 하기
        String url = httpServletRequest.getRequestURI(); 

        String reqcontent  = new String(httpServletRequest.getContentAsByteArray());

        log.info("request url: {}, request Content: {} " , url,reqcontent);


        String resContent = new String(httpServletResponse.getContentAsByteArray());
        int httpStatusCode = httpServletResponse.getStatus();

        httpServletResponse.copyBodyToResponse(); 
        log.info("response stats : {} , responseBody: {} ",httpStatusCode,resContent);

    }
}

특정 주소에만 filter처리를 하고 싶은 경우 @WebFilter(urlPatterns="주소") 를 통하여 특정 주소에만 적용시키는 것이 가능하다.

Interceptor

Annotation

어노테이션 배운 적 없는것같아서 다시 정리해보고자 한다.어노테이션의 기본적인 목적은 자바의 메타데이터로 사용하기 위함이다. 어노테이션의 작성법은 접근제한자 @interface 어노테이션명 {내용}이다. custom annotation을 생성하기 위해서는 Meta Annotations들을 사용하여야 한다.

  • @Retention : 어노테이션의 범위이다. 어떤 시점까지 어노테이션이 영향을 미치는지를 결정한다.
  • @Documented : 문서에도 어노테이션의 정보가 표현된다.javadoc으로 api 문서를 생성 시 현재 어노테이션의 설명이 포함되도록 지정해주는 것이다.
  • @Target : 어노테이션이 적용할 위치를 결정한다. 어노테이션을 붙일 수 있는 대상을 지정하는 것으로, 위의 매개변수로 TYPE ,CONSTRUCTOR ,METHOD ,FIELD가 있다. (TYPE은 클래스,인터페이스,열거타입에 어노테이션을 붙일 수 있다는 의미이다.)
  • @Inherited : 이 어노테이션을 선언하면 부모클래스에서 어노테이션을 상속받을수 있다.
  • @Repetable : 반복적으로 어노테이션을 선언할 수 있게 한다.

참조1 참조2

interceptor는 filter와 매우 유사하지만 spring context에 등록되어 controller와 같은 영역안에서 사용된다.(어떤 controller mapping인지 등의 정보를 알 수 있다.) 주로 인증 단계를 처리하는데 사용한다.

주로 특정 어노테이션을 가지고 있는 컨트롤러에 대하여 접근을 위해서는 세션을 검사하는 등의 인증을 하는 방식으로 사용되며, 즉 세션이 인증되 사용자만 해당 controller(url)에 접근이 가능하도록 하는 방식이다. 권한의 차이를 주기 위하여 특정 어노테이션을 생성하고 그 어노테이션을 가진 컨트롤러에 대해서는(메소드도 가능하다 하심,but 자주 사용안함) 세션을 검사하는 등의 인증 방식을 거친다.

HandlerInteceptor를 상속받은 클래스에서 preHandler를 오버라이드 하여 해당 interceptor를 사용할 수 있다. 이때에 해당 메소드의 반환값이 false일 경우 controller의 메소드를 실행하지 않는다.즉, 해당 false값 이후에 있는 controller까지의 접근이 실행되지 않는 것이다. 이런 방법을 사용하여 사용자를 검사한다.


5. 비동기 처리하기

비동기란? 웹 Mvc에서는 많이 사용하지 않는다고 하심. 별도의 쓰레드를 통해 처리하고 있구나를 이해만 하도록 한다.

profile
멋쟁이 토마토

0개의 댓글