[이룸] 240219 카카오 로그인 refresh token 관련 500 에러
[문제 상황]
같은 이메일로 카카오 로그인과 일반 로그인을 실행했을 때 500 에러가 터진다.
[원인]
카카오 로그인의 refresh-token은 Bearer이 붙어서 저장되고, 아닌 경우 Bearer이 떨어져서 저장된다. 따라서 같은 이메일이 두 개의 refresh-token을 가지게 되어 500에러가 터지는 것으로 가정하고 로직을 수정해 보겠다.
[해결 방안]
먼저, 왜 카카오 refresh token에는 bearer prefix가 붙는지 확인하였다. refresh token은 JwtUtil 클래스의 createRefreshToken 메서드에서 만들고 저장한다. 이후 KakaoService 클래스의 kakaoLogin 메서드에서 refresh token을 한번 더! 저장하는 중복되는 로직이 있는 것을 발견하였다.
여기서 ‘refreshTokenRepository.save’ 할 때는 Bearer이 없었다. 이후 Bearer이 붙은 토큰값을 반환한다.
그런데 여기서 existingToken이 없으면, 즉 카카오 로그인을 시도하면 위에 Bearer이 붙은 String refreshToken을 사용하여 다시 한번 RefreshToken을 만든다.
따라서, 중복으로 refresh token을 저장하는 부분을 지워 주었다. 그러자 같은 로그인으로 일반 로그인을 하든, 카카오 로그인을 하든 refresh token이 중복 저장되지 않고 새로 토큰이 발급되어 날아갔다.
[문제의 진짜 진짜 원인]
- 본래 3-layer 아키텍처를 선택했을 때는, controller와 service, repository의 목적을 명확히하기 위함이었다. 따라서 repository의 의존성은 service 단에서만 가지는 게 맞다.
- 그런데 JwtUtil 클래스를 살펴보면, 토큰을 생성할 뿐만 아니라 저장까지 한다. 이 저장 과정에서 repository의 의존성을 주입받는다. 다시 말해, 토큰을 저장할 수 있는 포인트가 util 클래스와 service 클래스, 두 군데가 되어 버린 것.
- 이 점을 인식하지 못한 채 평소처럼 service에서 repository를 저장하는 로직을 짜게 되었고, 토큰 저장은 중복이 되었다.
- JwtUtil 클래스의 목적은 무엇일까? JwtUtil 클래스는 jjwt 의존성을 이곳에 몰아넣고, 다른 곳에서 jjwt를 사용할 때 JwtUtil을 거치도록 하는 것이 그 목적이라고 생각한다. 따라서 본래의 목적을 고려한다면 토큰 저장 메서드는 service 단에 존재해야 한다.
- 나는 db에 Bearer이라는 반복되는 문구를 계속 저장하고 싶지 않았고, 그것 또한 비용이라고 생각하여 Bearer이 떨어진 순수 토큰값을 저장하고자 하였다. 따라서 Bearer이 붙은 토큰을 저장하는 service단의 로직을 결국 지우자는 결정을 내렸다.
- 이후 리팩토링할 여유가 생겨, 위의 이유를 근거로 util과 login 관련 부분을 수정하였다.
[수정 전]
[수정 후]
[새로 생긴 refreshTokenService class]