S-HOOK 서비스의 핵심은 사용자들이 노래의 킬링파트를 빠르게 접할 수 있다는 것입니다. 이는 서비스 회원(로그인을 한 사용자) 뿐만아니라 비회원 모두 이용할 수 있는 기능이어야 합니다.
이 부분에서 발생했던 문제점이 있었습니다. 이는 같은 요청(노래 상세페이지)에 대하어 회원과 비회원의 api 응답값이 다르게 나타나야한다는 것이었습니다.
위의 사진과 같이 회원의 경우 좋아요를 누르면 채워진 하트를 나타내주어야 하고 비회원의 경우에는 채워지지 않은 하트를 화면에 나타내야 했습니다.
사용자의 요청이 넘어오는 경우에 백엔드에서는 회원인지 비회원인지에 대해서 어떻게 알 수 있을까요? 이를 해결하기에 앞서서 S-HOOK의 Interceptor 구조를 먼저 살펴보겠습니다. (현재 S-HOOK은 Token 인증 방식을 이용하고 있습니다.)
기존의 S-HOOK의 인증 과정은 위의 그림과 같은 플로우로 이루어져 있습니다.
위의 흐름에서는 비회원의 경우 회원들과 공통으로 이용하는 서비스 자체를 접근하지 못하는 상황이 발생합니다.
회원인증을 하는 것에 앞서서 비회원 인증을 하는 LoginChecker Interceptor
를 구성하였습니다. 인증이라는 과정이 회원에만 적용된다고 생각해서 이 아이디어를 떠올리는데 어려움이 있었습니다.
위의 그림과 같이 LoginChecker Interceptor
를 두어서 Header에 토큰 정보가 없는 경우에는 비회원으로 처리하게 했고 Header의 토큰 정보가 있다면 Token Interceptor를 거치게 하여 올바른 토큰이 넘어왔는지 검증합니다. 위와 같은 방식으로 요청 처리가 이루어지면 회원과 비회원 모두 서비스를 이용할 수 있게 됩니다.
조금 더 쉽게 알아보기 위해서 구현코드를 보면서 알아보겠습니다.
위의 사진과 사용자의 요청이 넘어오게 되면 Login Interceptor
는 현재 요청에 Token이 있는지 없는지만 파악을 하고 토큰 정보가 있는 경우에는 TokenInterceptor
에서 토큰을 검증하도록 처리했습니다.
여기서 이제 의문이 생길 수 있습니다.
사용자 요청을 처리하는 곳에서는 어떻게 비회원과 회원인 것을 구분할 수 있을까요?
이를 위해서 요청마다 생성이 되는 AuthContext
를 만들어 주었습니다.
AuthContext는 기본적으로 비회원을 의미하는 값으로 설정이 됩니다. 현재 DB 설정 상 memberId는 1부터 저장이 되므로 memberId는 0
을 가지게 했고 비회원이라는 것을 의미하는 ANONYMOUS
enum을 가지게 했습니다.
이 정보를 Controller에 넘겨주기 위해서 ArgumentResolver를 이용했습니다. 처음에는 회원인지 비회원인지 검증이 필요한 Controller에 AuthContext를 필드로 가지게 할지 고민을 했었는데요. 그럼에도 AgrumentResolver를 이용한 이유는 Controller에서 AuthContext를 필드로 가지게 되면 AuthContext의 기능이 추가되었을 때 Controller단에서 잘못된 사용을 할 수 있다고 판단했습니다. 지금도 setAuthenticatedMember
메소드의 경우 TokenInterceptor에서 이용되지만 이를 controller에서 AuthContext를 필드로 가지면 회원 비회원 정보를 조작할 수 있습니다.
ArgumentResolver는 AuthContext에서 memberId와 authority만 가지는 MemberInfo Dto로 변환해주는 작업을 처리했습니다.
최종적으로 위의 사진과 같이 Controller에서는 MemberInfo를 활용해 회원인지 비회원인지 판단을 하고 그에 알맞은 응답 값을 제시할 수 있게 개선할 수 있었습니다.