Servlet Request/Response

SangLog·2023년 4월 19일
0

기본상식

목록 보기
5/8
post-thumbnail

Servlet

Servlet은 개발자가 Http 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP요청 메시지를 파싱한다.
그 파싱한 결과를 HttpServletRequest에 객체에 담아서 제공한다.
ServletRequest 인터페이스를 HttpServletRequest 가 상속 받는 구조

따라서 사용자의 요청은 http 프로토콜로 서버로 전송되며
전송된 요청 정보는 HttpServletRequest, HttpServletResponse 객체를 가지고 서블릿 컨테이너로 들어가서 HttpServlet클래스의 메소드들을 통하여 처리가 된다

Servlet의 정의
클라이언트의 요청을 처리하고, 그 결과를 반환하는
Servlet클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술


이미지 출처

Spring 요청/응답 흐름


이미지 출처

요청된 Http reqeuset 는 그림과 같은 흐름을 가지고 처리가 된다.

  • 필터
    - 필터는 DispatcherServlet 이전에 실행되기 때문에 스프링 프레임워크와 무관하게 동작하며 Spring 자원을 이용 할 수가 없다.
    - 애플리케이션의 비즈니스 로직과 관계가 없는 작업을 주로 처리한다.
    • ex)logging, 인코딩 변환처리, cors
  • 인터셉터
    - DispatcherServlet이 컨트롤러를 호출하기 전, 후로 수행되며 스프링 컨텍스트 영역 내부에 있기 때문에 스프링의 모든 빈 객체에 접근 할 수 있다.
    • ex) 로그인 체크, 권한체크등
  • AOP
    - Intercepter 와는 다르게 메소드 전후의 지점에 자유롭게 설정이 가능하다
    - Intercepter와 filter는 주소로 대상을 구분하여 걸러내야하는 반면 AOP는 주소, 파라미터, 애노테이션등 다양한 방법으로 대상을 지정할 수 있다.
    - 주로 비즈니스단의 메서드에서 조금 더 세밀하게 컨트롤 하고 싶을때 사용
    • ex) 에러처리, 트랜잭션등

Dispatcher Servlet 이란
디스패처 서블릿도 HttpServlet을 상속하는 Servlet이다. 가장 먼저 요청을 받고 적절하게 처리할 함수(컨트롤러)를 찾아서 정해주는 역할을 한다.

Filter/ OncePerRequestFilter

필터는 기본적으로 Dispatcher Servlet 이전에 수행이되며
Chain.doFilter() 이전 코드가 먼저 수행되고 서블릿이 실행된 이후에 그 이후 코드가 실행이 되는 로직입니다.
하지만 이렇게 서블릿이 수행되는 동안 다른 서블릿 요청이 있을 수 있으며
해당 서블릿에도 동일하게 필터가 수행됩니다.

즉 동일한 요청에서 filter가 반복적으로 호출되는 문제가 발생할 수 도 있고, 불필요한 메모리가 낭비될 수 도 있다. 이러한 문제를 해결하기 위한게 OncePerRequestFilter 이다.
해당 필터는 한 요청에 대해서 딱 한번만 호출된다.

// filter example 
@Component
public class MyFilter implements Filter {
 
    @Override
    public void doFilter(ServletRequest request,
                        ServletResponse response,
                        FilterChain chain) throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
 
        System.out.println("Request URI is: " + req.getRequestURI());
        chain.doFilter(request, response);
        System.out.println("Response Status Code is: " + res.getStatus());
    }
}
// OncePerRequestFilter
@Component
public class MyFilter extends OncePerRequestFilter {
 
    @Override
    public void doFilterInternal(HttpServletRequest request,
                        HttpServletResponse response,
                        FilterChain chain) throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
 
        System.out.println("Request URI is: " + req.getRequestURI());
        filterChain.doFilter(request, response);
        System.out.println("Response Status Code is: " + res.getStatus());
    }
}

Spring Logging

Filter는 Spring framework와 무관하게 동작하며
진입하는 가장 최초 시점이고, 응답하는 가장 마지막 시점이기 때문에 logging 을 남기기에 적합하다.

주의해야할 점은 HttpServletrequest의 body 부분은
최초 1회를 읽고 난 뒤에는 읽을 수 없기 때문에 logging을 남길때 바로 읽으면 안되며 ContentCachingRequestWrappe를 사용해줘야한다.(getInputStream을 통해서 값을 읽으면 readStatred가 true 로 변경)

response 부분도 내부 서블릿 과정중에 변경될 수 도 있기 때문에
ContentCachingResponseWrapper로 감싸서 chain에 값을 넣어서 처리한다.
ps. spring boot 는 기본적으로 UTF-8 이며 contentCachingRequest는 최초에 셋팅 값이 없으면 ISO-8859-1이 설정된다, Sentry를 연동하는경우에 값을 안주면 UTF-8이 설정 안되면서 charset에 문제가 발생하는 케이스도 있었다.

getContentAsByteArray() 에 들어있는 값은 최초에 content wrapper로 감쌀때가 아니라 dofilter에 값이 전달되고 뒤쪽에서 읽어서 처리될때 값이 들어가는것이다.

profile
기록 쌓기

0개의 댓글