DB 를 잘 모르거나 지식이 없는 채로 서비스를 개발할 때 누군가 이 글을 보고 이렇게 하면 안되는구나! 라고 생각했으면 하고 글을 작성해본다.
문제 상황
동시에 회원가입 요청이 들어왔을 때 중복되는 ID 값을 갖게 돼 2명의 사용자의 데이터가 하나의 ID 를 갖게 되는 상황이 발생했다.
그리고 다른 테이블에서는 해당 키를 외래키로 사용을 하는데 A 란 사용자로 인해 생긴 데이터인지, B 라는 사용자로 인해 생긴 데이터인지 판단할 수 없게 되었다. 결국 해당 데이터들은 모두 제거하게 되었다.
그렇다면 왜 이런 상황이 발생했을까?
원인
서버에서는 해당 ID 값을 직접 생성하고 있었다.
가장 마지막 ID 값을 가져와 + 1 을 하여 값을 넣어줬고, 아마 요청이 동시에 일어나거나, 트래픽이 많지 않기 때문에 테스트 시 별 문제가 없었을 것이다.
그리고 데이터 생성 쪽이라 @Transactional 도 메소드에 잘 걸려있는 상태이다.
그러면 서버에서 분명 로직 상 문제가 없는데 왜 문제가 발생하는 걸까?
N 명의 사용자가 우연히 동시에 회원가입 요청을 했다고 가정하자.
위에서 "동시에" 라는 말을 사용했지만 완벽히 동시에는 없다. 서버에서 먼저 처리하게 되는 요청들이 있을 것이다.
어쨌든 각 요청들은 동시에 @Transactional 이 걸려있는 회원가입 메소드로 진입할 것이다.
그리고 서버에서는 DB 에서 가장 마지막 ID 를 가지고 올 것이고 해당 ID 값에 + 1 을 하여 ID 값을 세팅 후 insert 를 했다.
그리고 트랜잭션 전파가 따로 되지 않은 이상 해당 메소드가 종료되면서 커밋이 되고 이 커밋이 되는 시점에 DB 단에 데이터가 생성될 것이다.
이 때 커밋이 되기 전까지는 마지막 ID 값을 가져오더라도 똑같은 값만을 가져올 것이다.
마지막 ID 값을 가져오니 50이었고 실제 커밋이 되지 않았기 때문에 insert 했더라도 마지막 값을 가져오면 50 일 것이다.
그렇기 우연찮게 각 요청들이 커밋되기 전에 ID 를 세팅 함으로 써 같은 ID 를 가진 값들이 DB 에 생성이 됐을 것이다.
수정
그렇다면 이제 수정을 해야 한다. 사실 해당 ID 값은 온전히 숫자로만 이루어져 있지 않고 (문자 + 숫자) 로 이루어져 있는 상태이다.
문자는 항상 고정이고 숫자만 + 1 씩 되면서 생성이 되고 있던 것이었다.
이럴 때 어떻게 기존 유저들에게 문제 없도록 수정 가능할까?
우선 해당 ID 값은 온전히 DB 에서 처리하도록 auto increment 를 설정해야 하며 그렇기 때문에 온전히 숫자로만 이루어지도록 해야 한다.
1. 서버에서 해당 (문자+숫자) 를 이용하는 코드 제거
- 해당 문자를 통해 어떤 로직을 수행하거나 하고 있는데 데이터들을 수정하면 문제가 분명 생길 것이기 때문
2. 테이블에 새로운 필드를 생성 후 기존 ID 값에서 문자만 제거해서 세팅되도록 함
- 이 때 필드는 auto increment 설정을 하도록 함
- auto increment 값은 처음에 기존 ID 값에서 숫자 이상의 값으로 설정하도록 함
3. 해당 ID 값을 이용하는 테이블에서 새로 필드를 만들어 새로 만든 필드에 새로운 ID 값을 넣음
- 서버에서도 관련 데이터 생성 될 시 새로운 ID 값을 넣도록 수정
4. ID 생성하는 코드를 제거
5. 배포
6. 기존 (문자+숫자) 로 이루어진 ID 값 필드 제거
7. 끝