동시성 이슈
동시성 이슈 (a.k.a. 따닥 이슈)
개념 및 문제점
동시성 이슈, 흔히 말하는 따닥 이슈라고 한다.
유저가 어떤 버튼을 한 순간에 여러 번 클릭하여 API 호출이 중복으로 일어나게 되면 따닥 거린다해서.....
암튼 이런 경우 비즈니스 로직에서 예외 처리를 해주어도, 여러 요청이 동시에 비즈니스 로직을 타게 되어 예외가 발생하지 않고 통과하게 된다.
따닥 이슈는 아래와 같은 문제를 발생하게 된다.
예로, 개발 중인 혜택 서비스의 경우 유저가 어떠한 액션을 수행했을 때, 이 액션에 대한 보상으로 페이포인트 리워드를 제공한다고 하자. 페이포인트는 실제 현금은 아닌데 그렇지만 현금처럼 사용할 수 있는 디지털 화폐이고, 회사 예산을 사용하기 때문에 중복으로 제공하면 안 된다. 하지만 따닥 이슈가 발생하면, 유저에게 페이포인트가 중복으로 지급되는 문제가 발생할 수 있다.
해결 방안 및 선택 이유
1차적으로는 FE에서 디바운스를 통해 동시성 이슈를 막고 있지만 100% 막을 수는 없어 서버에서도 이를 인지하고 방어해야 할 것이다. 서버에서 해결할 수 있는 방법에는 애플리케이션 단 분산 락 구현, DB 단 베타 락(쓰기 락) 사용 등이 있다.
1. 분산 락 구현
여러 서버가 공유 자원을 동시에 사용하는 경우, Redis 같은 외부 시스템을 이용해 락을 걸어 동시성 문제를 방지할 수 있다.
Redis: SETNX 명령어를 통해 락을 걸고 해제하는 분산 락 구현이 가능하다. Redisson 라이브러리를 사용하면 쉽게 Redis 기반의 락을 적용할 수 있다. 이미 기존에 키가 존재하는 경우에는 작업 수행에 실패하여 false를 반환하도록 할 수 있다.
그리고 레디스는 단일 스레드로 작동되는 키-값 저장소이기 때문에 모든 명령은 순차적으로 처리된다. 이러한 특성으로 인해 분산락을 구현하면 다음 그림과 같다.
2. 데이터베이스 수준에서의 잠금
- 행 잠금 (Row Locking): 특정 데이터베이스의 레코드를 잠그는 방식이다. 특정 트랜잭션이 끝날 때까지 다른 트랜잭션은 잠긴 행에 접근할 수 없다. 주로 금융 등 정합성이 중요한 경우 사용된다.
- 비관적 잠금 (Pessimistic Locking) == 베타 락 : 트랜잭션이 데이터를 수정하려 할 때 데이터를 잠가 다른 트랜잭션이 접근하지 못하게 한다. 주로 SELECT FOR UPDATE 구문을 사용하여 잠금을 걸 수 있다.
- 낙관적 잠금 (Optimistic Locking): 데이터를 갱신하기 전 데이터의 버전을 확인하여, 갱신하려는 동안 다른 트랜잭션이 데이터를 변경하지 않았는지 확인하는 방식이다. version 컬럼을 추가하여 버전을 검사하는 방식이 자주 사용된다.