본문 바로가기
Spring 7기 프로젝트/일정관리 프로젝트

Spring [트러블슈팅] 로그인 기능

by JuNo_12 2025. 5. 20.

로그인 기능을 구현한 후에 기존의 기능들을 싹 다 수정했다.

그 이유는 로그인 요청, 유저 생성 (회원가입) 빼고는 로그인이 되었을 때만 기능을 사용할 수 있도록 설정을 해놓는게 맞다고 생각했기에 수정을 진행했다.

 

예시 코드 :

private Long getLoginUserId(HttpServletRequest request) {
	// HTTP 요청 객체를 받아 로그인한 유저의 ID를 구한다는 의미

   return (Long) request.getSession(false).getAttribute("userId");
    // 현재 요청에 대한 세션 객체를 가져와주고
    // false를 넣었으니 세션이 없으면 새로 만들지 않고 null을 반환한다.
    // 세션이 존재하면, 그 세션에서 userId 라는 이름의 저장된 값을 꺼낸다.
    // (로그인 시점에 저장된 사용자 ID)
    // 즉, 로그인한 사용자의 ID를 반환해주는 메서드.
}



@GetMapping
public ResponseEntity<List<FindAllSchedulesWithUserIdResponseDto>> getMySchedules(
        HttpServletRequest request){
    // 파라미터로 `HttpServletRequest request`를 받아 현재 요청 정보를 활용할 수 있다.

    Long userId = getLoginUserId(request);

    List<FindAllSchedulesWithUserIdResponseDto> foundSchedules = scheduleService.findSchedules(userId);
    // 일정 서비스(scheduleService)에게 로그인한 사용자의 ID(userId)를 전달하여, 그 사용자가 등록한 모든 일정을 조회
    // 조회된 일정 데이터들은 FindAllSchedulesWithUserIdResponseDto 형태의 리스트로 반환된다.

    return ResponseEntity.ok(foundSchedules);

}

 


그리고 필터를 만들어줄 때 문제점들이 발견되었다.

1. 문제 상황

  • 인증(로그인) 체크용 필터() 가 있는데, 
  • 화이트리스트에 없는 URL에서도 필터가 동작하지 않고 그냥 통과되는 현상이 발생

2. 원인 분석

  • 원인은 필터 등록 시 URL 패턴 지정이 너무 좁게 설정되어 있었기 때문!
      registrationBean.addUrlPatterns("/users/*");
  • 이러면 /users/로 시작하는 경로에만 필터가 동작함
  • 다른 경로에서는 인증 체크로직이 아예 실행되지 않음

3. 해결 방법

  • 필터가 모든 경로에서 동작하도록 URL 패턴을 변경
    • /users/*     ->     /*
    • 코드 예시:
      registrationBean.addUrlPatterns("/*");
  • 이제 필터가 모든 요청을 가로채고,
    • 화이트리스트(로그인, 회원가입 등)는 필터 내부에서 pass
    • 나머지 경로는 인증 확인 후 처리

4. 결과

  • 모든 요청에 대한 인증 체크가 정상적으로 작동
  • 화이트리스트 경로는 예외 처리,
    인증 필요한 경로는 세션 체크 등이 제대로 적용됨

그리고 추가적으로 모든 예외 처리를 exceptionHandler를 통해 관리해주고 싶어서 Filter 내부의 예외에 대해서도 이를 통해 관리를 해주려다가 지속에서 에러가 발생했다.

Filter의 동작 원리

  • Spring의 @RestControllerAdvice는 DispatcherServlet 레벨에서 발생한 예외만 처리
  • 하지만 Filter는 DispatcherServlet 이전에 동작하기 때문에,
    • 필터나 서블릿에서 발생한 예외는 글로벌 예외 핸들러(@ExceptionHandler)로 처리되지 않는다.

 

일반적인 해결 방법 2가지

1. 필터에서 예외 객체로 직접 응답을 만들어준다 (현재 방식)

  • 예외를 던지는 대신, 바로 상태코드와 메시지 등을 직접 response에 작성하는 것
  • 하지만, 이렇게 하면 API 응답 포맷이 일관성 없을 수 있음

2. Spring Filter 대신 HandlerInterceptor 사용?

  • HandlerInterceptor는 DispatcherServlet 안쪽(Spring 영역)이기 때문에,
    GlobalExceptionHandler가 처리할 수 있다고 한다.

 

Filter를 사용하는 이유

1. 더 앞단에서 동작

Filter는 서블릿 컨테이너 단계에서 작동
즉, Spring DispatcherServlet이 요청을 처리하기 전,
스프링 프레임워크 바깥의 모든 요청이 먼저 Filter를 통과

2. Spring MVC 처리 대상이 아니더라도 적용 가능

Filter는 정적 리소스(/static/* · js · css · 이미지 등),
또는 Spring Controller와 무관한 요청,
서블릿, JSP, REST API, 모든 HTTP 요청에 작동할 수 있다.

3. 범용 공통 처리에 적합

  • 인코딩 처리, 로깅, CORS, 보안 체크, 인증 등 모든 웹 요청의 공통 선처리
  • Spring 프레임워크에 종속적이지 않으므로 프레임워크가 달라도 동작

4. 프레임워크 독립성

비즈니스 요구사항이 Spring에 종속적이지 않고
다양한 기술 스택(서블릿, JSP, Spring 등)에서
동일하게 인증/로깅 정책을 적용하고 싶다면 Filter가 더 적합하다.

5. 단점도 있음

  • Spring의 ExceptionResolver, @RestControllerAdvice 등과 연동이 어렵다.
  • Spring보다는 다소 복잡
  • 보통은 (API나 MVC 컨트롤러 기준의 인증/인가에는) HandlerInterceptor 가 더 편하고 유연 

정리: 필터가 적합한 경우

  • App 전체(정적 리소스까지) 보안 정책을 적용해야 함
  • Spring 이외의 기술과 함께 써야 함
  • 요청의 초입에서 무조건 체크해야 하는 로직이 있을 때

 

추후에 인터셉터와 필터에서 화이트리스트 대신 상수로 빼는 방법(enum으로 관리)에 대해 공부를 해보고 적용을 해봐야겠다.