ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 로그인을 어디서 처리할 것인가? 필터 vs 컨트롤러
    운영 중인 서비스/Coconut. 2024. 5. 2. 23:41

     

    인증 서버에서 로그인을 구현하면서 고민한 내용을 적어봅니다.

    최종적으로 구현은 필터 위에 작업이 되었으나, 다시 한다면 컨트롤러에서 진행할 것 같습니다 ㅎㅎ


     

    아래의 글은 다음의 순서로 진행됩니다.

    1. 필터 기반의 로그인 구현

    2. 필터 체인 구성

    3. 필터를 쓰는 것이 맞는 것인가? 

    4. 다시 만든다면?

     

     

     

    코코넛 서비스의 주요 로그인은 Oauth를 사용할 예정이지만, 특별한 사용자들의 접근을 위해서 아이디/패스워드 형식의 로그인도 기능을 만들어 두었습니다.

     

    Spring Security의 기능을 최대한 사용하는 방향에서 작업을 하고, 인증 서버는 요청량이 많을 것을 감안해서 성능상에 이점을 가져갈 수 있는 방법을 선택해서 구현하였습니다.

     

    1. 필터 기반의 로그인 구현

    Security Conifg 파일을 먼저 살펴보겠습니다.

    목적에 맞는 필터 체인을 구현하는 것을 목표로 하고 있고, 우선 로그인 관련 필터 체인을 추가해보았습니다.

     

     

    간단하게 다이어그램으로 표현해봤습니다.

    로그인 필터 체인은 3가지 필터를 사용하고 있습니다. 

     

    처음에는 CustomLoginFilter 1개를 두고 시작을 했습니다.

    이 필터에서 post로 body에 실려오는 데이터를 파싱하고,  id/pw 검증 로직 트리거도 하고, jwt도 만들어서 반환하려고 하니,

    너무 많은 일을 하게 되고 의존성 주입도 안되다 보니 관련 클래스들을 전부 다 생성자에 넣어야 하는 상황이 됩니다.

     

     

    2. 필터 체인 구성

    그래서 작은 모듈을 선호하는 저는 또 쪼개기로 마음을 먹습니다.

     

    1. post 로 전달되는 body 핸들링 하는 필터

    인풋 관련해서 validation 처리를 진행하고 유효한 경우에만 다음 필터로 넘어갑니다.

     

    2. 인증 로직에 집중하는 필터

    여러가지 방법이 있는 것 같은데, AbstractAtuthencationProcessingFilter가 onSuccess와 onFail 에 대한 핸들러를 제공해주길래 선택해보았습니다. attemptAuthentication에서 스프링 시큐리티가 제공하는 인증 로직을 실행합니다.

    성공하면 다음 필터로 넘어가고, 실패한다면 에러 메세지와 함께 응답을 반환합니다.

     

    3. JWT를 담당하는 필터

    마지막으로 성공시에는 jwt 필터에 와서 필터를 생성하여 반환을 합니다.

    혹여나 유저 데이터가 부적절한 경우에는 401 응답을 반환합니다.

     

     

    3. 필터를 쓰는 것이 맞는 것인가?

    우선 필터를 선택한 이유는 Dispatcher servlet 이전 단계에서 불필요한 요청들을 걸러낼 수 있고, 더 앞단에서 응답을 반환하기 때문이었습니다. 하지만 작업을 할 수록 적절한 선택인가 라는 의문이 생깁니다.

     

     

    1. 스프링에서 제공하는 기능을 다시 구현하고 있는 느낌이 든다?

    LoginInputValidationFilter의 경우 결국 인풋을 핸들링하는 것이 가장 큰 이유인데, 위 로직들은 컨트롤러에서 @Valid @RequestBody 2개의 어노테이션이면 해결될 내용들이었다고 생각이 듭니다.

     

    비슷한 맥락에서 응답에 대한 json을 생성할 때, 컨트롤러에서 적절한 dto를 반환해주면 되는 것인데, 필터에서는 json 생성에 보다 많이 신경을 써야하는 것을 느꼈습니다.

     

     

    2. 비즈니스 로직을 추가하고 싶다면?

    이 부분은 기획 단계에서 놓친 것이 문제이지만, 갑작스럽게 로그인하는 유저에 대한 기록을 데이터베이스에 남기고 싶다는 생각이 들었는데, 이것을 필터에서 하는것이 적절한가? 라는 생각이 또 스쳐지나가서 기능을 다음 기회에 추가하는 것으로 미루었습니다.

     

     

     

    4. 다시 만든다면?

    로그인을 다시 만들 기회가 생기거나, 시간이 지나서 개선을 해야하는 상황을 만나면, 로그인 로직은 컨트롤러로 가지고 갈 것 같습니다.

     

    필터에서 했을 때의 가장 큰 이점은 스프링 시큐리티가 제공하는 로직을 그대로 사용할 수 있다는 부분인데, 분산 환경을 가정했을 때 security context에 데이터를 저장하는 부분은 무의미하기 때문에 굳이 스프링 시큐리티가 제공하는 id/pw 인증 로직을 사용할 필요가 없다고 생각이 됩니다.

     

    또 컨트롤러로 가져오면 input 핸들링과 request, response에 대한 로직은 다시 스프링에게 맞길 수 있고, 필요한 비즈니스 로직을 추가하는 것에 더 자유로울 것 같습니다.

     

    무엇보다 api에 대한 검증은 매 요청마다 진행되야하기 때문에 빈도가 높아서 성능이 중요하지만, login 요청 자체는 상대적으로 빈도가 높지 않을 것이기 때문에 컨트롤러에서 처리해도 괜찮을 것 같다는 생각입니다.

     

     

     

     

    감사합니다.

     

     

Designed by Tistory.