카테고리 없음

스프링 시큐리티 로그인 리디렉션 문제 해결 경험 공유

트티 2025. 5. 3. 08:00

 

스프링 시큐리티 로그인 리디렉션 문제 해결 경험 공유

스프링 시큐리티 로그인 리디렉션 문제 해결 경험 공유

개발자라면 한 번쯤은 스프링 시큐리티(Spring Security)를 이용한 로그인 기능을 구현하면서 리디렉션 관련 문제에 부딪힌 경험이 있을 거예요. 저 역시, 로그인 후 원래 요청했던 페이지로 이동하지 않고 무조건 루트("/")로 리디렉션되는 현상에 부딪혀 꽤 오랜 시간을 헤맸던 기억이 있습니다.

이번 글에서는 그 문제를 어떻게 발견하고, 어떤 방식으로 해결했는지 실제 경험을 바탕으로 자세히 소개해 보려고 합니다. 혹시나 같은 문제를 겪고 있는 분들에게 작은 도움이 되길 바라며, 함께 코드도 살펴보고 원인도 분석해보겠습니다.

목차

스프링 시큐리티 로그인 리디렉션 문제 해결 경험 공유

개발자라면 한 번쯤은 스프링 시큐리티(Spring Security)를 이용한 로그인 기능을 구현하면서 리디렉션 관련 문제에 부딪힌 경험이 있을 거예요. 저 역시, 로그인 후 원래 요청했던 페이지로 이동하지 않고 무조건 루트("/")로 리디렉션되는 현상에 부딪혀 꽤 오랜 시간을 헤맸던 기억이 있습니다.

이번 글에서는 그 문제를 어떻게 발견하고, 어떤 방식으로 해결했는지 실제 경험을 바탕으로 자세히 소개해 보려고 합니다. 혹시나 같은 문제를 겪고 있는 분들에게 작은 도움이 되길 바라며, 함께 코드도 살펴보고 원인도 분석해보겠습니다.

목차

📌 문제 증상: 왜 리디렉션이 잘못될까?

당시 구현한 로그인 기능은 기본적으로 스프링 시큐리티에서 제공하는 폼 기반 인증을 사용하고 있었고, /login 경로에서 인증을 수행한 후 사용자가 접근하려던 페이지로 자동 리디렉션되길 기대했습니다. 하지만 로그인 후 항상 / 루트 페이지로 이동하는 현상이 반복되었고, 유저 경험에 심각한 불편을 초래했습니다.

예를 들어, /mypage에 로그인 없이 접근했을 때 로그인 페이지로 이동하는 것까지는 잘 작동했지만, 인증이 완료되면 /mypage가 아닌 루트 페이지(/)로 이동하는 상황이었습니다. 분명히 브라우저에서는 이전에 접근했던 URL을 기억하고 있었는데도, 이 정보가 인증 후 반영되지 않는 것이 문제였죠.

📌 원인 분석 과정

처음에는 단순히 loginSuccessHandler 문제라고 생각해서 커스텀 핸들러를 손봐봤지만 해결되지 않았습니다. 이후, 로그를 분석하고 디버깅을 해보니, 핵심 원인은 RequestCache 설정이 제대로 되어 있지 않거나, 커스터마이징된 설정 중 일부가 SavedRequest를 무시하고 있었던 것이었습니다.

또한, 다음과 같은 실수도 있었죠:

  • 커스텀 로그인 성공 핸들러에서 SavedRequest를 체크하지 않고 무조건 /로 리디렉션
  • 정적 리소스 접근에도 필터가 작동하면서 불필요한 요청이 RequestCache에 저장됨
  • 필터 순서와 ExceptionTranslationFilter의 동작을 정확히 이해하지 못함

Tip: 로그인 후 이전 URL로 리디렉션이 되지 않을 때는 RequestCacheSavedRequest의 흐름을 먼저 점검해보는 것이 좋습니다.

📌 해결 방법: 실제 적용한 코드

문제를 해결하기 위해 저는 SavedRequestAwareAuthenticationSuccessHandler를 커스터마이징하여 적용했습니다. 아래는 실제 적용한 코드 일부입니다:

@Bean
public AuthenticationSuccessHandler loginSuccessHandler() {
    SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
    handler.setDefaultTargetUrl("/default");
    handler.setTargetUrlParameter("redirectTo");
    return handler;
}

그리고 Security 설정에서는 다음과 같이 설정했습니다:

http
  .formLogin()
    .loginPage("/login")
    .successHandler(loginSuccessHandler())

이렇게 구성하면 사용자가 로그인 페이지로 이동되기 전에 접근했던 URL이 RequestCache에 저장되고, 인증 후 해당 URL로 정확히 리디렉션되는 구조가 완성됩니다.

📌 배운 점과 적용 팁

이번 문제를 해결하면서 저는 스프링 시큐리티의 인증 흐름에 대해 더 깊이 이해할 수 있었고, 단순히 기능 구현을 넘어서 사용자 경험(UX)까지 고려해야 함을 다시 한 번 깨달았습니다.

  • 폼 로그인 사용 시, 기본 핸들러를 그대로 쓰기보다 커스터마이징하여 실제 요구사항을 반영하자
  • 불필요한 요청이 RequestCache에 저장되지 않도록 주의할 것
  • 특히 인증이 필요한 REST API에서는 RequestCache가 아닌 JWTredirectUri 파라미터 기반 전략이 더 적절할 수 있음

실전 팁: 로그인 이후 리디렉션이 안 되는 문제는 대부분 RequestCache 미설정 혹은 커스터마이징 오류입니다. 꼭 디버깅을 통해 인증 전 요청 URI가 어디에 저장되고 있는지 추적해보세요!

📌 추가적인 경험과 팁

스프링 시큐리티의 로그인 리디렉션 문제는 단순히 설정 한 줄로 해결되지 않을 때가 많아요. 현업에서 마주한 다양한 프로젝트에서 겪은 리디렉션 이슈들을 종합해보면, 정답은 하나가 아니라는 걸 느낍니다. 상황마다 다른 접근이 필요했고, 그때마다 아래와 같은 팁들이 큰 도움이 되었어요.

1. URL 파라미터 기반 리디렉션 전략 활용

기본적인 RequestCache를 활용하는 방식 외에도, 로그인 페이지 진입 시 redirectTo 파라미터를 명시하는 방식도 유용했어요. 예를 들어, 사용자가 /product/123 페이지에 접근하려다 로그인 페이지로 이동하게 됐다면, URL에 /login?redirectTo=/product/123처럼 붙여주는 거죠. 그 후 로그인 성공 핸들러에서 해당 파라미터를 우선적으로 체크하도록 구현하면 예측 가능한 동작을 만들어낼 수 있어요.

2. REST API 기반 서비스에서는 상태 비저장 방식이 유리

전통적인 웹 페이지 외에 SPA나 모바일 API 백엔드를 다룰 때는 RequestCache 방식보다는 상태 비저장(stateless) 방식이 더 안정적이었어요. 이럴 때는 클라이언트가 직접 원래 위치를 기억하고 있다가 인증 후 다시 그 위치로 보내는 게 훨씬 명확하고 간단했습니다.

실제로 JWT 기반 인증을 사용할 때는 리디렉션 문제가 거의 없었고, 프론트엔드와의 협업을 통해 URL 파라미터를 주고받는 구조를 맞추는 게 핵심이었어요.

3. 디버깅을 위한 Filter 로그 남기기

어느 순간부터는 로그를 무작정 보는 게 아니라, ExceptionTranslationFilter, UsernamePasswordAuthenticationFilter, SecurityContextPersistenceFilter 같은 핵심 필터들의 흐름을 따로 로그로 찍어두는 습관을 들였어요. 그러다 보니 어느 시점에 SavedRequest가 null로 설정되는지, 어떤 요청이 캐시되는지를 추적하기 훨씬 쉬워졌죠.

4. 테스트 케이스 작성의 중요성

가장 실용적인 팁은, 이런 리디렉션 흐름도 테스트 케이스로 검증하는 습관을 들이는 거예요. MockMvc를 활용해 인증 전 URL 접근 → 로그인 → 인증 후 리디렉션 경로 검증까지 흐름을 테스트로 만드는 것만으로도 추후 버그 발생 확률이 확 줄어들었습니다.

mockMvc.perform(get("/secure-page"))
       .andExpect(status().is3xxRedirection())
       .andExpect(redirectedUrlPattern("**/login"));

mockMvc.perform(post("/login")
       .param("username", "user")
       .param("password", "pass"))
       .andExpect(redirectedUrl("/secure-page"));

개발자 노트: 리디렉션 문제는 자칫 단순 설정 오류처럼 보이지만, 실제로는 인증 흐름에 대한 깊은 이해가 필요한 이슈입니다. 나만의 디버깅 패턴과 테스트 습관을 만들어두면 비슷한 문제에 더 빠르게 대응할 수 있어요.

이런 경험들을 통해 저는 "문제가 생겼을 땐 리디렉션 핸들러만 고치려 하지 말고, 전체 필터 체인 흐름을 의심하자"는 마인드를 갖게 되었어요. 때로는 우리 코드가 아닌 외부 보안 설정(예: 프록시 서버의 캐싱, 리버스 프록시 설정 등)에서 문제가 시작될 수도 있으니까요.

📌 결론

스프링 시큐리티에서 로그인 후 리디렉션 문제가 발생하면 당황하기 쉽지만, 그 속에는 스프링 시큐리티가 얼마나 체계적인 인증 흐름을 가지고 있는지에 대한 힌트가 숨어 있어요. 단순히 리디렉션 경로를 설정하는 것처럼 보이지만, RequestCache, SavedRequest, AuthenticationSuccessHandler의 작동 방식이 서로 유기적으로 맞물려 있다는 점을 이해하는 것이 정말 중요합니다.

저는 이 문제를 겪으며 설정 한 줄보다 흐름에 대한 이해가 얼마나 중요한지를 절실히 깨달았습니다. 로그인 이후 사용자가 자연스럽게 원래 의도한 페이지로 이동하는 것은 UX 측면에서 매우 중요한 포인트이기도 하니까요.

혹시 비슷한 문제로 고민 중이시라면, 오늘 소개한 경험과 팁이 작은 실마리가 되었기를 바랍니다. 우리가 놓치기 쉬운 부분일수록, 한 번 더 의심하고 로그를 꼼꼼히 보는 습관이 문제 해결의 시작이 될 수 있어요. 그리고 무엇보다 중요한 건, 이런 경험 하나하나가 결국 더 단단한 개발자로 성장하게 해준다는 사실이겠죠.