Categories
웹개발/Spring
인터셉터 로그인 체크와 login.do 리다이렉트 처리 정리
로그인 검사를 인터셉터로 모으는 이유
로그인이 필요한 화면마다 컨트롤러에서 세션을 확인하고 비로그인 시 돌려보내는 코드를 넣으면, 같은 코드가 수십 군데로 흩어진다. 빠뜨린 화면 하나가 보안 구멍이 된다. 그래서 로그인 검사는 인터셉터에서 한곳에 모아 처리한다. 인터셉터 로그인 체크의 기본 구조는 "요청이 컨트롤러에 닿기 전에 가로채서, 세션에 로그인 정보가 없으면 login.do로 리다이렉트하고 요청을 중단"하는 것이다.
Spring MVC의 HandlerInterceptor는 컨트롤러 실행 전(preHandle), 후(postHandle), 완료 후(afterCompletion) 시점에 끼어들 수 있다. 로그인 검사는 컨트롤러가 실행되기 전인 preHandle에서 한다.
인터셉터 시점 비교
| 메서드 | 실행 시점 | 로그인 검사 활용 |
|---|---|---|
| preHandle | 컨트롤러 실행 전 | 여기서 로그인 검사·차단 |
| postHandle | 컨트롤러 실행 후, 뷰 전 | 공통 모델 추가 등 |
| afterCompletion | 뷰 렌더링 완료 후 | 로그 기록, 리소스 정리 |
preHandle로 로그인 검사 구현
1. 기본 구현
세션에서 로그인 정보를 꺼내, 없으면 login.do로 리다이렉트하고 false를 반환한다. false를 반환하면 컨트롤러로 진행하지 않고 요청이 멈춘다.
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession(false);
Object loginUser = (session != null)
? session.getAttribute("loginUser") : null;
if (loginUser == null) {
// 비로그인 → 로그인 페이지로
response.sendRedirect(request.getContextPath() + "/login.do");
return false; // 요청 중단
}
return true; // 로그인 상태 → 컨트롤러로 진행
}
}
request.getContextPath()를 붙여야 운영·로컬의 배포 경로 차이와 무관하게 올바른 경로로 이동한다. 이걸 빠뜨리면 context path가 붙은 환경에서 엉뚱한 곳으로 리다이렉트된다.
2. 원래 가려던 페이지 기억하기 (선택)
로그인 후 원래 가려던 화면으로 돌려보내려면, 리다이렉트 전에 요청 URL을 세션이나 파라미터로 넘겨 둔다.
if (loginUser == null) {
// 로그인 후 복귀할 URL 저장 (필요 시)
String target = request.getRequestURI();
request.getSession(true).setAttribute("redirectAfterLogin", target);
response.sendRedirect(request.getContextPath() + "/login.do");
return false;
}
로그인 성공 후 컨트롤러에서 redirectAfterLogin 값을 확인해 해당 URL로 보내면 된다. 다만 외부 URL이 끼어들지 않도록 내부 경로인지 검증하는 것이 안전하다.
인터셉터 등록과 제외 경로
인터셉터를 만들었으면 어디에 적용할지 등록해야 한다. 핵심은 로그인 페이지와 정적 리소스를 검사 대상에서 제외하는 것이다. 이걸 빠뜨리면 바로 다음에 설명할 무한 리다이렉트가 난다.
<!-- XML 설정 (레거시) -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> <!-- 전체 적용 -->
<mvc:exclude-mapping path="/login.do"/> <!-- 로그인 페이지 제외 -->
<mvc:exclude-mapping path="/loginProc.do"/> <!-- 로그인 처리 제외 -->
<mvc:exclude-mapping path="/css/**"/> <!-- 정적 리소스 제외 -->
<mvc:exclude-mapping path="/js/**"/>
<bean class="com.example.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
// Java Config 방식
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login.do", "/loginProc.do", "/css/**", "/js/**");
}
제외 목록에는 로그인 화면, 로그인 처리 URL, 정적 리소스, 그리고 회원가입·아이디찾기처럼 비로그인 접근이 필요한 경로를 넣는다.
무한 리다이렉트와 Ajax 함정
1. 무한 리다이렉트
가장 흔한 사고다. login.do를 제외하지 않으면, 로그인 페이지 요청도 인터셉터가 가로채 "비로그인"이라며 다시 login.do로 보낸다. 이게 끝없이 반복돼 브라우저가 리다이렉트 오류를 띄운다. 해결은 단순하다. 로그인 관련 경로와 정적 리소스를 반드시 exclude에 넣는다.
// 잘못된 경우: login.do를 제외 안 함
// → /login.do 요청도 비로그인으로 판정 → /login.do로 또 리다이렉트 → 무한 반복
// 해결: excludePathPatterns에 /login.do 포함
2. Ajax 요청 처리
또 하나의 함정은 Ajax다. 세션이 만료된 상태에서 Ajax 요청이 인터셉터에 걸려 리다이렉트(302)를 받으면, 브라우저가 화면 전체를 바꾸지 못하고 로그인 페이지 HTML이 Ajax 응답으로 들어와 화면이 깨진다. Ajax 요청은 리다이렉트 대신 상태 코드(예: 401)를 내려주고, 화면단 스크립트가 이를 받아 직접 로그인 페이지로 이동하게 처리한다.
if (loginUser == null) {
String requestedWith = request.getHeader("X-Requested-With");
boolean isAjax = "XMLHttpRequest".equals(requestedWith);
if (isAjax) {
// Ajax면 리다이렉트 대신 상태 코드로 알림
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401
return false;
}
// 일반 요청이면 리다이렉트
response.sendRedirect(request.getContextPath() + "/login.do");
return false;
}
프론트에서는 401 응답을 공통으로 가로채 로그인 페이지로 보내면 된다. Ajax를 많이 쓰는 화면이라면 이 처리를 빠뜨리지 않도록 한다.
실무에서 자주 하는 실수
- login.do(로그인 페이지·처리 URL)를 제외하지 않아 무한 리다이렉트가 나는 경우.
- 정적 리소스(css/js/이미지)를 제외하지 않아 화면이 깨지거나 불필요하게 검사되는 경우.
- Ajax 요청에 302 리다이렉트를 줘서 응답이 화면에 끼어드는 경우. 상태 코드로 분리한다.
request.getContextPath()를 안 붙여 운영 환경에서 경로가 어긋나는 경우.- 인터셉터가 컨트롤러 매핑보다 넓게/좁게 걸려 일부 화면이 검사에서 빠지는 경우. 경로 패턴을 점검한다.
FAQ
Q1. 로그인 검사는 왜 인터셉터에서 하나요?
컨트롤러마다 넣으면 코드가 흩어지고 빠뜨리기 쉽다. 인터셉터에서 한곳에 모으면 일관되게 적용되고, 빠진 화면이 생기지 않는다.
Q2. preHandle에서 false를 반환하면 어떻게 되나요?
컨트롤러로 진행하지 않고 요청이 중단된다. 그래서 리다이렉트나 상태 코드 처리를 한 뒤 false를 반환한다.
Q3. 무한 리다이렉트는 왜 생기나요?
로그인 페이지(login.do) 자체를 인터셉터가 막으면, 로그인 페이지 요청도 비로그인으로 판정돼 다시 login.do로 보낸다. 제외 경로에 login.do를 넣으면 해결된다.
Q4. Ajax 요청은 어떻게 처리하나요?
리다이렉트 대신 401 같은 상태 코드를 내려주고, 프론트 스크립트가 이를 받아 로그인 페이지로 이동시킨다. 그래야 화면이 깨지지 않는다.
Q5. 정적 리소스도 인터셉터를 타나요?
경로 패턴에 따라 탈 수 있다. css/js/이미지 경로는 excludePathPatterns로 제외해 불필요한 검사와 화면 깨짐을 막는다.
정리
인터셉터 로그인 체크는 "요청이 컨트롤러에 닿기 전 preHandle에서 세션을 확인하고, 비로그인이면 login.do로 리다이렉트하며 false를 반환해 요청을 중단하는" 구조다. 검사를 인터셉터 한곳에 모으면 화면마다 코드를 넣지 않아도 되고 빠진 화면이 생기지 않는다. 등록할 때는 로그인 페이지·처리 URL·정적 리소스를 반드시 제외 경로에 넣어 무한 리다이렉트를 막고, Ajax 요청은 리다이렉트 대신 상태 코드로 분리해 화면이 깨지지 않게 한다. 리다이렉트 경로에는 getContextPath()를 붙여 환경 차이에 대비한다. 직접 구현하는 레거시라면 이 함정들을 놓치기 쉬우므로, 가능하면 검증된 보안 프레임워크의 도움을 받고 운영 적용 전 점검하는 것이 안전하다.
'웹개발 > Spring' 카테고리의 다른 글
| Spring Security 없이 권한 체크 구현하기 정리 (0) | 2026.07.05 |
|---|---|
| Spring Security 4.x XML 설정 정리 (공공 SI 기준) (0) | 2026.07.05 |
| session attribute에 loginUserVO 담고 꺼내기 정리 (1) | 2026.07.04 |
| Session 기반 로그인 HttpSession 동작 흐름 정리 (0) | 2026.07.03 |
| Spring @Transactional 기본 정리 (propagation, isolation, rollback) (0) | 2026.07.03 |