회원 기능 챌린지 분석 및 설계
회원 기능 챌린지에서 복잡하거나 조금 생각이 필요할 것 같은 부분만 골라서 설계를 진행해봤다.
이메일 인증
일반적으로 회원가입을 진행할 때, 이메일 인증을 했던 방식을 떠올리면 크게 두 가지였던 것 같다. 바로 코드 기반 인증과 링크 기반 인증이다. 코드를 입력받아서 인증하거나 링크를 클릭하는 것만으로 인증되는 차이인데. 설계를 한다면 어떻게 할 수 있을까?
1. 코드 기반 인증
코드 기반 인증은 회원 가입시, 이메일을 입력하고 인증을 누르면 해당 이메일로 인증 코드가 발송된다. 이제 이 인증 코드를 회원 가입창에 제출하면 인증이 된다. 설계 자체는 어렵지 않다고 생각한다. 인증을 위한 이메일과 코드를 저장소에 저장하고 일치하면 회원가입을 허락하면 된다.
설계해보기
가입 전이라 장기적으로 보관할 데이터는 아니기 때문에 저장소는 DB보다는 캐시를 사용하는 게 나을 것 같다. 캐시는 Redis로 키/값 자료구조를 사용하며 TTL 설정을 통해 일정 기간만 보관하도록 한다. 다만 회원 가입 요청을 할 때, 인증이 완료됐다는 것을 어떻게 처리해야 할지가 고민이었다.
두 가지가 떠올랐는데. 이메일과 인증 여부도 캐시에 담아 저장하는 것과 토큰을 사용하는 방법이다. 그럴 일이 없겠지만, 전자는 A 사용자가 인증에 성공하고 바로 B 사용자가 동일한 이메일로 가입 신청하면 성공하게 된다. 그래서 후자가 보안 측면에서 나은 선택일 것 같다.
API 설계
2. 링크 기반 인증 설계
링크 기반 인증은 인증 요청한 이메일에 링크를 담아서 보낸다. 그리고 해당 링크를 클릭하면 인증이 완료된다.
설계해보기
링크 기반 인증은 클릭시 인증이 되도록 하는 것인데. 생각나는 방법은 인증 코드와 인증 여부를 같이 저장하는 것이다. 그리고 링크를 클릭하면 GET 요청을 통해 서버에 인증 코드와 함께 요청을 하게 되고 서버에서는 인증을 완료한다.
API 설계
중복 로그인 방지
중복 로그인 방지를 위한 방법은 여러가지가 있다. 당장 구글에 검색만 해도 코드와 함께 좋은 예시들이 수십개가 나온다. 대부분이 세션을 이용한 방법인데. JWT를 사용하면서 가능하면 상태를 저장하지 않고 중복 로그인 방지를 할 수 있을까 고민해봤는데. 방법이 딱히 떠오르지 않는다. 왜냐하면 이전 로그인을 했다는 상태를 기록해야 이후 로그인을 검증할 수 있기 때문이다.
가능하면 MSA처럼 서버를 확장할 수 있는 방식으로 설계하고 싶다. 세션을 사용하면 세션 클러스터링을 하거나 공통 저장소를 이용하면 되는데. 세션 클러스터링은 세션 복사, 서버 설정 등, scale out시 복잡해진다. 그래서 캐시같은 공통 저장소를 선택하는게 나을 것 같다.
Token Id
로그인시 JWT 토큰에 고유한 토큰 ID를 할당해준다. 그리고 캐시 메모리 혹은 DB에 ID를 저장하는데. 이 ID는 화이트리스트로 관리한다.
이 화이트리스트에 포함되어 있으면 로그인 가능한 사용자라 정의한다. 새로 로그인한 사용자는 새로운 토큰 ID를 발급받고 화이트리스트에 저장한다. 만약 이전 토큰이 있다면 화이트 리스트에서 제거한다. Refresh 토큰 재발급에도 동일하게 적용한다.
설계해보기
모든 접근은 화이트리스트에 등록된 토큰만 허용한다. User ID와 Token ID는 Redis의 String을 사용하고 TTL 설정을 통해 토큰 만료기간까지만 보관하도록 한다. 화이트리스트는 Set을 사용하는게 나을 것 같다.
회원 테이블 설계
회원 테이블을 설계하려고 고민하던 중 좋은 자료를 발견했고 한 번 그대로 적용해보고자 한다. 정말 제대로 정규화한 회원 테이블이라 생각되고 배울 부분이 많았다. (RDMS 테이블 설계)
권한 부분에서 고민이 있었는데. DB를 수정하는 것으로 권한과 역할을 나누도록 설계하는 것과 단순 역할만 추가하도록 설계하는 것에서 고민이었다.
전자는 DB 설계의 복잡도는 늘어날 수 있지만 한 번 잘 설계하면 DB로 모든 추가, 수정이 가능하다는 장점이있다. 후자는 DB 설계는 간단하지만 로직 상에서 수정해야 할 부분이 많아진다. 쉽게말해서 전자는 DB를 통해서 해당 권한에 역할이 가지는 페이지, URL 등에 Read, Write, Delete 권한을 설정할 수 있다. 후자는 DB에는 역할만 구분해 놓고 로직 상에서 Spring Security가 제공해주는 애너테이션을 사용해서 역할별 권한을 구분할 수 있다.
각자 장단점이 있겠지만 Spring Security의 연습을 위해 후자를 선택했다.
설계해보기
다음은 임시로 설계한 DB이다. OAuth 2.0 과 ID/PW 인증을 사용하도록 설계했다. 각 설계는 위에 링크를 참고했으며, 실제 구현을 해보면서 수정을 거칠 예정이다.
설계는 대충 완료했으니 다음은 구현으로.!