[Spring] Filter & Wrapper

배창민·2025년 10월 2일
post-thumbnail

Servlet Filter & Wrapper

목표: 공통 로직(인코딩/보안/로깅 등)을 Filter로, 요청/응답 객체를 변형해야 하면 Wrapper로.

1) Servlet Filter

1-1) 정의 & 역할

  • javax.servlet.Filter 구현 클래스.
  • 요청/응답서블릿 전·후로 가로채서 가공(보안, 인코딩, 로깅, 압축, 리소스 접근 제어 등).
  • 여러 개를 체인(FilterChain) 으로 연결 가능 → 앞에서 뒤로, 돌아오며 역순 실행.

1-2) 동작 구조

  • 클라이언트 → Filter 1 → Filter 2 → … → Servlet(service/doGet/doPost) → … → Filter 2 → Filter 1 → 클라이언트
  • 마지막 필터에서 chain.doFilter(req, res) 호출해야 다음 단계로 진행.

1-3) 생명주기 메서드

void init(FilterConfig config)     // 최초 1회 초기화 (init-param 읽기)
void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) // 매 요청
void destroy()                     // 종료/재배포 시 정리

1-4) 설정 방법

(A) web.xml

<filter>
  <filter-name>encoding</filter-name>
  <filter-class>com.example.EncodingFilter</filter-class>
  <init-param>
    <param-name>encoding-type</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>encoding</filter-name>
  <url-pattern>/*</url-pattern>
  <!-- 요청 타입에 따른 적용 (선택) -->
  <!--
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>INCLUDE</dispatcher>
  <dispatcher>ERROR</dispatcher>
  -->
</filter-mapping>
  • url-pattern 매핑이 우선이며, servlet-name 매핑도 가능.

(B) 어노테이션

import jakarta.servlet.annotation.WebFilter;

@WebFilter("/*")
public class EncodingFilter implements Filter { ... }

1-5) 예시: POST 인코딩 공통 처리

public class EncodingFilter implements Filter {
  private String encoding;

  @Override
  public void init(FilterConfig cfg) {
    encoding = cfg.getInitParameter("encoding-type"); // ex) UTF-8
  }

  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {

    if (req instanceof HttpServletRequest http && "POST".equals(http.getMethod())) {
      req.setCharacterEncoding(encoding);
    }
    chain.doFilter(req, res); // 반드시 호출!
  }
}

2) Servlet Wrapper

2-1) 정의

  • 요청/응답 객체를 데코레이션하는 래퍼:
    HttpServletRequestWrapper, HttpServletResponseWrapper.
  • 원본을 감싸 특정 메서드만 선택적으로 재정의 → 파라미터 조작/마스킹/암호화, 응답 가공 등.

2-2) 주요 클래스

// 요청 래핑
public class RequestWrapper extends HttpServletRequestWrapper {
  public RequestWrapper(HttpServletRequest request) { super(request); }

  @Override
  public String getParameter(String name) {
    if ("password".equals(name)) {
      var raw = super.getParameter(name);
      return new org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder()
               .encode(raw); // 비밀번호는 항상 해시로
    }
    return super.getParameter(name);
  }
}

// 적용 필터
@WebFilter("/member/*")
public class PasswordEncryptFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest http = (HttpServletRequest) req;
    chain.doFilter(new RequestWrapper(http), res);
  }
}
  • 컨트롤러/서블릿에서는 평문이 아닌 해시 문자열을 받게 됨. 검증 시:
BCryptPasswordEncoder enc = new BCryptPasswordEncoder();
enc.matches("plainPassword", hashedFromDb); // true/false

2-3) 언제 쓰나?

  • Filter: “모든 요청에 공통으로” 적용(인코딩, 인증/인가, 로깅, 압축…)
  • Wrapper: “요청/응답 객체의 동작을 바꿔야” 할 때(파라미터 위장/마스킹, 출력 변환 등)

3) 실전 팁

  • doFilter()에서 chain.doFilter()를 반드시 호출(차단 목적이 아니라면).
  • 여러 필터 사용 시 순서를 고려(web.xml 등록 순서, 또는 프레임워크 제공 우선순위).
  • 인코딩은 POST 본문 파싱 전에 설정해야 효과 있음.
  • 보안 데이터(패스워드)는 단방향 해시(BCrypt) 를 표준으로.
  • dispatcher 옵션으로 FORWARD/ERROR 시에도 필터 적용 제어.

핵심 요약

  • Filter = 공통로직(인코딩/보안/로깅) 적용 지점, 서블릿 전·후로 가로채 가공
  • Wrapper = 요청/응답 객체의 메서드 오버라이드로 동작 변경
  • 설정은 web.xml 또는 @WebFilter, url-pattern 매핑이 우선
  • doFilter()chain.doFilter() 호출 잊지 않기
  • 민감정보는 BCrypt 해시 + matches 검증, 평문 저장 금지
profile
개발자 희망자

0개의 댓글