
1. 인증 (필터)
※ 필터(Filter)
1) 필터(Filter)의 특징과 단점 정리
① 필터는 DispatcherServlet 앞에서 동작
- Spring MVC 요청 흐름
Client → Filter → DispatcherServlet → Controller➡️ 필터는 DispatcherServlet보다 먼저 실행됨
② 예외를 DispatcherServlet으로 넘길 수 없음
- 필터에서 예외 발생 시
- ❌
throw new Exception()→ DispatcherServlet에서 캐치 불가
- 이유
- DispatcherServlet에 도달하기 전에 필터에서 이미 요청 처리됨
- Spring의
@ControllerAdvice,@ExceptionHandler적용 안 됨
③ 필터에서 예외 직접 처리 필요
- 필터 내부에서 처리 방식
- try-catch 사용
- 직접 Response 작성
- 예시 패턴
try {
// 인증 체크
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized");
return;
}2) 필터에서 인증 처리 시 고려사항
① 인증 실패 시 DispatcherServlet 진입 차단이 정상
- 핵심 개념
- 인증/인가 실패 → 비즈니스 로직 진입 자체가 잘못
- DispatcherServlet → Controller까지 갈 필요 없음
➡️ 필터에서 요청을 차단하는 것이 맞는 설계
② 필터는 "입구 차단기" 역할
- 필터의 책임
- 요청 전처리
- 인증 / 인가 검사
- 공통 보안 처리
- DispatcherServlet의 책임
- 핸들러 매핑
- 컨트롤러 실행
- 예외 처리
3) 필터 vs 인터셉터 vs 컨트롤러 예외 처리
구분 | 필터 | 인터셉터 | 컨트롤러 |
실행 위치 | DispatcherServlet 이전 | 이후 | 내부 |
Spring 예외 처리 사용 | ❌ 불가 | ✅ 가능 | ✅ 가능 |
주 용도 | 인증 / 보안 / 인코딩 | 권한 / 로깅 | 비즈니스 |
1) LoginFilter.java
C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\filter\LoginFilter.java
package com.example.boardv1._core.filter;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("----------------login filter----------------");
}
}2) FirstFilter.java
C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\filter\FirstFilter.java
package com.example.boardv1._core.filter;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("----------------first filter----------------");
}
}3) FilterConfig.java
① 코드
C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\filter\FilterConfig.java
package com.example.boardv1._core.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<FirstFilter> firstFilter() {
FilterRegistrationBean<FirstFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new FirstFilter());
bean.addUrlPatterns("/*"); // 모든 요청에 대해 검사
bean.setOrder(1); // 필터 순서
return bean;
}
@Bean
public FilterRegistrationBean<LoginFilter> loginFilter() {
FilterRegistrationBean<LoginFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LoginFilter());
bean.addUrlPatterns("/*"); // 모든 요청에 대해 검사
bean.setOrder(2); // 필터 순서
return bean;
}
}② 테스트


4) FirstFilter.java - 브라우저에 출력
① 코드
package com.example.boardv1._core.filter;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("----------------first filter----------------");
PrintWriter pw = response.getWriter();
pw.println("block");
}
}② 테스트

5) FirstFilter.java - doFilter()
① 코드
package com.example.boardv1._core.filter;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("----------------first filter----------------");
// PrintWriter pw = response.getWriter();
// pw.println("block");
chain.doFilter(request, response);
}
}
② 테스트

6) LoginFilter.java
package com.example.boardv1._core.filter;
import java.io.IOException;
import com.example.boardv1.user.User;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("------Login Filter-----------");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession();
User sessionUser = (User) session.getAttribute("sessionUser");
// 예외: GET /boards/{id} 상세보기는 인증 없이 허용
String uri = req.getRequestURI();
if ("GET".equals(req.getMethod()) && uri.matches(".*/boards/\\d+$")) {
chain.doFilter(request, response);
return;
}
// /boards/*, /replies/* 나머지 전부 인증 필요
if (sessionUser == null) {
resp.sendRedirect("/login-form");
return;
}
chain.doFilter(request, response);
}
}7) FilterConfig.java
① 코드
package com.example.boardv1._core.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<FirstFilter> firstFilter() {
FilterRegistrationBean<FirstFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new FirstFilter());
bean.addUrlPatterns("/*"); // 모든 요청에 대해 검사
bean.setOrder(1); // 필터 순서
return bean;
}
@Bean
public FilterRegistrationBean<LoginFilter> loginFilter() {
FilterRegistrationBean<LoginFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LoginFilter());
bean.addUrlPatterns("/boards/*");
bean.addUrlPatterns("/replies/*");
bean.setOrder(2); // 필터 순서
return bean;
}
}② 테스트

2. 인증 (인터셉트)
1) LoginInterceptor.java
C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\interceptor\LoginInterceptor.java
package com.example.boardv1._core.interceptor;
import org.jspecify.annotations.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.example.boardv1._core.errors.ex.Exception401;
import com.example.boardv1.user.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
// 뷰가 완성되면 실행 -> 쓸 일이 거의 없음
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
System.out.println("------------------view render complete------------------");
}
// 컨트롤러 메서드 호출 직전에 실행 (/boards, /replies)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String uri = request.getRequestURI(); // localhost:8080/hello/5 -> /hello/5
// 예외: /boards/숫자 패턴 -> 인증 체크 안 함
if (uri.matches(".*/boards/\\d+$")) {
return true;
}
HttpSession session = request.getSession();
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) {
throw new Exception401("인증되지 않았습니다");
}
return true; // 로그인 -> true -> 메서드 진입 / 로그인 X -> false -> 진입불가
}
// 컨트롤러 메서드 호출 직후에 실행
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
System.out.println("------------------postHandle complete------------------");
}
}
2) WebMvcConfig.java
C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\_core\interceptor\WebMvcConfig.java
package com.example.boardv1._core.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/boards/**")
.addPathPatterns("/replies/**")
.excludePathPatterns("/boards/[0-9]+");
}
}Share article