로그인/ 회원가입
Auth 와 인연이 있는 것인가?
발생한 문제
로그인, 회원가입을 TEST 하던 중, 문제가 발견되었다.
회원가입을 기준으로, 이미 가입된 ID 가 있다면 회원가입 실패 처리가 되도록 구현했는데
문제는, 찰나에 같은 아이디로 가입을 요청이 여러번 들어올 경우,
모두 저 조건을 건너 뛰고 동일한 ID로 여러개의 계정이 만들어져 버리는 것이었다.
여러개의 요청이 비동기로 실행될 때, 검증 당시에는 모든 요청에서 없는 ID 였었기 때문이다.
LoginId 가 UNIQUE 로 설정되어있는것도 아니었기 때문에 모두 성공 처리 되어버린것.
무슨 문제?
동시성 접근 문제 혹은 레이스 컨디션 문제
하나의 자원에 두개 이상의 프로세스가 접근하면서 예상치 못한 문제가 생기는 것인데,
위를 예로 들면 두 클라이언트가 동시에 회원가입 요청을 하면서,
해당 ID가 DB 에 등록되기 이전에 ID 검증을 둘 다 패스해버리는 현상이 생긴 것이다.
해결 방법 ?
1. 가장 원시적인 방법으로,
LoginId 를 UNIQUE 로 만들면 결국 db에 업데이트 하는 과정에서 에러가 발생하는데,
그 에러를 catch 해서 response 작업을 하는것
=> 일단 검증을 모두 pass 하고 update 에 가서야 문제를 찾는다는 점에서 타이밍이 맞지 않았고,
코드 적으로도 완성도가 떨어진다고 판단이 된다.
2. DB 트랜잭션을 시도
트랜잭션을 통해, 회원가입 기능의 일관성과 무결성을 보장할 수 있는데
아쉬운 점은 역시 있다.
회원가입에 트랜잭션을 걸면 모든 회원가입이, 어떤 ID로 가입하던지 순차적으로 처리되는
방식이 될 것이기 때문에, 동기적인 기능이 되어버린다.
또한 현재 코드와 다르게,
회원가입 검증 과 update 가 트랜잭션을 통해 한번에 이루어져야 하기 때문에
전체적으로 코드의 흐름을 바꾸어야 하는 문제도 있다.
3. lock Key를 사용
redis를 이용한 방법이다.
검증에 앞서서 Redis set으로 키를 등록하는 방법으로,
적절한 key 값을 설정해주고, 'NX' 옵션을 넣어서
return 값으로 'OK' 또는 null 을 반환 받을 수 있다.
이렇게 할 경우, 가입을 시도하려는 ID < 에만 Lock 이 설정되는 형태이기 때문에,
어느정도 비동기성도 유지가 되면서 동시성에 대한 처리가 가능하다.
다만, 역시 redis 에 데이터를 넣는 과정이 추가된다는 점이 아쉽고,
해당 회원가입 처리 이후 lock 키를 제거하는 과정 또한 추가를 해줘야 key 반납을 제대로 받을 수 있다.
Login 의 경우, 중복 로그인 검증을 위한 redis key 를 따로 사용하고 있었기 때문에
이를 적절히 Lock key 의 기능도 할 수 있도록 수정을 하고,
회원가입은 위처럼 lock key 를 만들어주어 동시에 같은 내용으로 성공처리가 되는일이 없도록 구현을 했다.
그 외에도 Queue 를 사용하는 방법도 있지만, 2번과 기능적으로 유사한 방식이라고 생각이 되어 PASS 하도록 했다.