Spring

ServletResponse.isComitted(), FilterChain (with GPT)

99duuk 2024. 12. 19. 10:50

ServletResponse.isCommitted()

ServletResponse.isCommitted() 메서드는 Java의 Servlet API에서 응답(Response)이 클라이언트로 전송되었는지 확인하는 메서드임.

역할 및 동작

  • isCommitted() 메서드는 응답의 헤더 또는 본문 내용이 이미 클라이언트로 전송되었는지 여부boolean 값으로 반환함.
  • 응답이 커밋되었으면 true, 아직 클라이언트로 전송되지 않았다면 false를 반환함.

주요 특징

  1. 커밋의 의미:
    • HTTP 응답이 커밋된다는 것은 응답 헤더와 상태 코드가 클라이언트로 전송되었다는 것을 의미함.
    • 일반적으로, 첫 번째 바이트가 응답 본문에 기록되면 응답이 커밋됨.
    • 또는 flushBuffer() 메서드가 호출되면 강제로 커밋됨.
  2. 커밋 후 작업 제한:
    • 응답이 커밋되면 더 이상 상태 코드나 응답 헤더를 변경할 수 없음.
    • 커밋된 이후에 응답을 수정하려고 하면 IllegalStateException이 발생하거나 무시됨.
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/checkCommit")
public class CheckCommitServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 응답이 커밋되었는지 확인
        if (response.isCommitted()) {
            System.out.println("Response is already committed!");
        } else {
            System.out.println("Response is not committed yet.");
        }

        // 헤더 추가
        response.setHeader("Content-Type", "text/plain");

        // 응답 본문 작성
        response.getWriter().write("Hello, World!");

        // 응답이 커밋되었는지 다시 확인
        if (response.isCommitted()) {
            System.out.println("Response is now committed.");
        }
    }
}

커밋 여부 확인이 필요한 경우

  1. 리다이렉트 또는 오류 처리:
    • 응답이 이미 커밋된 경우에는 sendRedirect()sendError() 같은 메서드를 호출할 수 없음.
    • 따라서, 호출 전에 isCommitted()로 확인 후 처리 로직 작성 필요.
  2. 여러 필터 또는 인터셉터 사용:
    • 필터 체인에서 특정 응답 상태를 유지하려면, 필터에서 응답 커밋 여부를 확인하고 추가 작업 제한 가능.

요약

  • isCommitted()는 응답이 클라이언트로 전송되었는지 여부를 확인하는 데 유용함.
  • 이를 통해 응답 헤더나 상태를 변경할 수 없는 시점을 안전하게 관리할 수 있음.

추가 Q&A

Q: "응답이 성공적으로 전송되어 브라우저에 200으로 보일 때 isCommitted가 true인가?

A: 그렇지 않음.

  • isCommittedtrue라고 해서 반드시 브라우저가 200 OK를 받은 것은 아님.
  • isCommitted서버에서 응답을 클라이언트로 보내기 시작했는지 여부만 확인하며, 상태 코드와는 무관함.
  • 브라우저에 도달 여부와도 관계없이, 서버가 응답 전송을 시도했다면 true가 됨.

doFilter 메서드

doFilter는 Java Servlet API의 필터(Filter) 인터페이스에서 핵심 메서드로, 필터 체인 내에서 요청(Request)과 응답(Response)을 처리하거나 다음 필터로 넘기는 역할을 함.

doFilter 메서드 시그니처

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

주요 파라미터

  1. ServletRequest request:
    • 클라이언트의 요청 객체로, HTTP 요청을 포함한 다양한 프로토콜 요청을 처리할 수 있음.
    • 일반적으로 HttpServletRequest로 캐스팅해서 사용.
  2. ServletResponse response:
    • 클라이언트에 보낼 응답 객체.
    • 일반적으로 HttpServletResponse로 캐스팅해서 사용.
  3. FilterChain chain:
    • 다음 필터 또는 최종 서블릿으로 요청/응답을 넘기기 위한 체인 객체.
    • chain.doFilter(request, response)를 호출하면 체인의 다음 필터가 실행됨.

동작 원리

  1. 필터 체인의 시작:
    • 요청이 서버로 들어오면, 필터 체인이 먼저 실행됨.
    • 여러 필터가 등록되어 있다면 설정된 순서대로 실행됨.
  2. 요청/응답 처리:
    • 각 필터에서 요청/응답을 조작하거나 로깅, 인증, 인코딩 설정 등을 수행할 수 있음.
  3. 다음 필터 호출:
    • chain.doFilter(request, response)를 호출하면, 필터 체인의 다음 필터로 요청을 넘김.
    • 모든 필터가 처리된 후 최종적으로 서블릿이 실행됨.
@WebFilter("/*")
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 요청 처리 전
        System.out.println("Request received at: " + System.currentTimeMillis());

        // 다음 필터 또는 서블릿으로 요청 전달
        chain.doFilter(request, response);

        // 응답 처리 후
        System.out.println("Response sent at: " + System.currentTimeMillis());
    }
}

주요 활용 사례

  1. 인증/인가:
    • 특정 URL에 대한 접근 권한을 확인하고, 허용되지 않는 경우 요청을 차단.
  2. 로깅:
    • 요청/응답의 정보를 로깅하여 디버깅 또는 모니터링.
  3. 요청 데이터 변환:
    • 요청 데이터를 특정 포맷으로 변환하거나 필터링.
  4. 응답 데이터 조작:
    • 응답 데이터 압축, 캐싱 헤더 추가 등.

 


FilterChain과 DispatcherServlet의 순서

FilterChain이 DispatcherServlet보다 먼저 실행됨.

요청 처리 흐름

  1. 클라이언트 요청
    • 브라우저나 다른 HTTP 클라이언트에서 요청을 보냄.
  2. 필터 체인(Filter Chain)
    • 서블릿 컨테이너는 등록된 필터들을 순서대로 실행.
    • 요청을 가로채어 전처리(예: 인증, 로깅 등) 작업 수행.
  3. DispatcherServlet
    • Spring MVC의 중앙 서블릿으로, 요청을 적절한 컨트롤러로 전달.
  4. 컨트롤러
    • 비즈니스 로직 실행.
  5. DispatcherServlet (후처리)
    • 컨트롤러의 결과 데이터를 기반으로 응답 생성.
  6. 필터 체인 (응답 처리)
    • 응답 데이터 후처리(압축, 캐싱 등).
  7. 클라이언트 응답 반환

예제 흐름

요청 URL: /users/123

출력 흐름:

[필터 체인] LoggingFilter: Before DispatcherServlet
[필터 체인] AuthFilter: Before DispatcherServlet
[디스패처 서블릿] HandlerMapping: UserController 매핑
[컨트롤러] UserController.getUserById 실행
[디스패처 서블릿] 응답 생성
[필터 체인] AuthFilter: After DispatcherServlet
[필터 체인] LoggingFilter: After DispatcherServlet
[클라이언트] JSON 데이터 수신

Filter와 AOP 비교

공통점

  • 요청/응답 흐름에 개입:
    • 요청 흐름 전후에 로직을 삽입하여 작업 가능.
    • 예: 로깅, 인증, 데이터 변환.
  • 관심사 분리:
    • 공통 로직(로깅, 보안 등)을 비즈니스 로직과 분리하여 재사용성을 높임.

활용 사례

  • 필터: HTTP 요청과 응답을 관리 (예: 인증, 로깅, 데이터 압축).
  • AOP: 비즈니스 로직에서 트랜잭션 관리, 로깅, 권한 검사 수행.

함께 사용하는 경우

  1. 필터로 클라이언트 요청을 인증.
  2. AOP로 서비스 계층에서 권한 검사 및 로깅 수행.