[Spring] Filter

HOJUN·2024년 6월 16일

Backend - Spring

목록 보기
26/34

Filter

스프링에서는 Request를 핸들링하기 전 필터링을 거친다.
입력 혹은 출력에 대한 예외나 오류를 확인하는 경우로
클라이언트가 잘못 입력하거나 서버의 로직이 잘못된 경우와 같은 상황의 로그를 확인하기 위함이다.

@PostMapping("")
    public UserRequest register(
            @RequestBody
            UserRequest userRequest
    ){
        log.info("{}", userRequest);
        return userRequest;
    }

body를 가져오는 POST 요청이 있다고 해보자.

@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserRequest {

    private String name;

    private String phoneNumber;

    private String email;

    private Integer age;
}

네 가지의 필드 중 몇 가지를 잘못 입력했을 때 우리는 null로 서버에 입력된다는 것을 알고 있다.

그래서 Filter라는 interface를 구현할 수 있다.

이름을 Camel Case로, 전화번호를 잘못 입력했다고 가정해보자.

@Component
public class LoggerFilter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

	var req = new HttpServletRequestWrapper((HttpServletRequest) servletRequest);
	var res = new HttpServletResponseWrapper((HttpServletResponse) servletResponse);

	var br = req.getReader();

	var list = br.lines().collect(Collectors.toList());

    list.forEach(it ->{
        log.info("{}", it);
    });
    
    filterChain.doFilter(req, res);
    }
}

doFilter로 들어오는 요청과 리턴되는 응답에 필터링을 할 수 있다.
요청과 응답을 HttpServlet으로 형변환하고 getReader를 통해 body를 모두 읽어보면

꽤 성공적으로 요청을 읽어왔지만

getReader() has already been called for this request 라는 에러를 발생시켰다.

이유는 getReader를 통해 body를 받을 때 inputStream을 Filter에서 읽었기 때문에 Controller에서 body를 읽지 못하는 문제가 발생한 것이다.

var req = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
var res = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);

Request와 Response를 ContentCachingWrapper 객체로 감싸주면 된다.
이름에 나와있듯이 body를 캐싱해서 가지고 있을 수 있다.

InputStream으로 읽히는 요청을 log에 찍어보기 위해 문자열로 바꿔서 찍는다.

    var reqJson = new String(req.getContentAsByteArray());
    log.info("req {}", reqJson);

    var resJson = new String(res.getContentAsByteArray());
    log.info("res {}", resJson);

content를 byte 배열로 읽겠다는 뜻일 것이다.
getReader로 읽지 않고 다른 방식을 취했으니 body에 전달되고 에러도 뜨지 않았을까 ?

무사히 에러는 뜨지 않았다 !

하지만 body에는 전달되지 않았다.

getReader와 같은 이유로 어찌됐든 get으로 읽었기 때문에 body에는 전달되지 않은 것이다.
그래서 마지막 response를 body에 덮어씌우는 작업이 필요하다.
response는 내려갔지만 비어있는 body가 전달이된 것이기 때문에

 res.copyBodyToResponse(); 

덮어씌워준다면

에러도 없고 response도 잘 내려간 모습을 볼 수 있다.

0개의 댓글