내가 Redisson을 활용해 개발하면서 더블탭 이슈에서 자유로워진 방법

개발해규스·2025년 1월 22일
0

Java-Spring

목록 보기
3/5
post-thumbnail

들어가며

회사에서 MySQL -> MongoDB로 운영 DB마이그레이션 진행 후 서버 애플리케이션 개발을하던 중 해결하기 어려웠던 부분중 하나는 유저가 액션을 한 번 했는데, 실제 클라이언트에서는 두번 누른 것으로 판단해 매우 짧은 텀을 갖고 요청이 들어오는 경우였습니다.
독립요청으로 간주해도 크게 상관 없는 도메인이라면 그렇게 중요하지 않을 수 있지만, 저의 경우 인앱 결제를 통한 재화를 다루는 부분이었기 때문에 이런 이슈에 상당히 민감한 도메인이었습니다. 사실 기존에 RDB를 사용했었더라면 Transaction에서 데이터 Read시 Exclusive Lock을 걸어(SELECT ~~~ FOR UPDATE) 데이터를 Read하는 부분에서 부터 막고 부가적인 처리를 해준다면 비교적 간단하게 이를 방지할 수 있겠지만, MongoDB를 사용하는 이상 Read시 Exclusive Lock을 걸 수 있는 방법이 딱히 없기 때문에 이를 해결한 방법을 공유하려고합니다.

시작

MongoDB를 활용해 개발시 더블탭 이슈에서 해결해야할 사항은 2가지가 있습니다.

1. 정상 처리가 됐는데도 WriteConflict 발생으로 인해 예외가 발생함

2. 구매를 한 번만 했는데, 2번, 3번 구매하게되는 경우

해결에 앞서 이전에 로컬에 구축했던 MongoDB Cluster와 Redis를 이용하겠습니다.(MongoDB 클러스터 구축하기, Redis 구축하기)
제가 개발하는 환경과 툴은 아래와 같습니다
OS: MacOS
Tool: Intellij, Docker, DockerCompose

먼저 그냥 트랜잭션만 걸었을때의 코드를 만들어 보겠습니다.
레이어 구성은 아래와 같습니다

소스 코드 구성은 아래와 같습니다.

Wallet(Entity)

WalletService

CustomWalletRepository(Wallet의 Balance를 차감하는 DAO)

테스트할 테스트코드도 작성해서 실제 테스트를 해보겠습니다.

여기서 한번의 요청을 했지만 10번의 중복 탭 이슈가 발생한 것을 가정해서 병렬로 1번 아이템을 10번 구매해서 로그가 어떻게 찍히는지 확인해보겠습니다. (WriteConflict가 발생할 것을 예상해서 retry를 시켜줬습니다)

생각보다 WriteConflict에 의한 재시도 현상이 꽤 많이 발생하는 것을 볼 수 있습니다.

WriteConflict 해결

여기서 Redisson을 활용해 분산락을 이용해 프로세스 동시성제어를 한 다음 다시 테스트 해보겠습니다.

먼저 Redisson 의존성을 프로젝트에 추가합니다.

그 후 Redisson으로 분산락을 적용하기 위해 service 상위 메소드를 생성합니다. 구조와 코드, 테스트코드 및 실행 결과는 아래와 같습니다.

재시도 없이 잘 처리가 된 것을 볼 수 있습니다. 이 것으로 우선 WriteConflict는 해결했습니다.

2번, 3번 중복 처리 해결

위에서 WriteConflict는 해결 했지만, 아직 중복 처리에 대해서는 해결 되지 않았습니다.
이를 해결 하기 위해 저는 클라이언트와 협업하여 클라이언트에서 API를 호출할때 id를 발급해서 처리하도록 구성했었는데, 이 개념을 추가해서 로직 변경을 해보겠습니다. 클라이언트에서 받은 id를 저장하는 로그 엔티티를 하나 추가로 생성하고 분산 락 획득을 시도하는 퍼사드 서비스의 코드를 조금 수정하겠습니다. 코드와 실행결과는 아래와 같습니다.


처음 의도한대로 10번의 병렬 구매시도를 했지만 1번만 구매처리가 되었고, 해결해야할 상황 2가지를 모두 해결 했습니다.

마치며

위에서 적용한 방법으로도 중복 처리 이슈를 해결 할 수 있지만, 조금 더 나아가자면 로그 추가로 인한 클라이언트 발급 id 중복 가능성이슈를 간편하게 줄일 수 있는 방법으로는 MongoDB의 ttl Index를 적용해 일정 시간 뒤에 자동으로 삭제되게끔 적용과 프로세스가 정상 처리가 됐는지 여부등의 체크를 추가로 해주면 좋을듯 싶습니다.
실제 회사에서 이걸 해결하려고 정말 많은 고민과 시도를 해보면서 해결했던 경험을 글로 쓰게됐는데요, 여러분께 도움이 되셨으면 좋겠습니다. 긴 글 봐주셔서 감사합니다.

참 가시기 전에 꼭 한번 Redisson 공식 문서를 확인해보세요. 분산 아키텍쳐에서 사용해 봄 직한 기능이 많습니다.

profile
개발, 기타 좋아하는 뒷단 개발자

0개의 댓글