동시성 제어는 여러 요청이 동시에 들어와도 데이터 일관성을 유지하고 안정성을 확보하기 위한 핵심 전략임
클라이언트가 대량의 요청을 일괄로 보낼 때, 이를 순차적으로 처리하여 예기치 않은 오류나 데이터 불일치를 예방하는 것이 목적임
이 과정에서 TDD(Test-Driven Development)를 활용해 비즈니스 로직을 단위별로 검증하며, Clean Architecture 원칙에 따라 Controller-Service-Database 레이어를 명확히 분리함
Jest Mocking을 통한 단위(Unit) 테스트, E2E(End-To-End) 테스트 등을 점진적으로 수행하고 최종적으로 동시성 제어 로직을 원하는 레벨에 명확히 위치함
효율적인 동시성 제어를 위해 async-mutex 라이브러리를 이용한 Mutex(뮤텍스) 기반 잠금 방식을 채택함
한 유저에 대한 포인트 변경 요청이 들어올 때마다 Mutex를 통해 Lock을 획득하고, 해당 임계 구역당 요청당 작업 1개를 초과하지 않도록 보장함
-
장점
- 구현이 간단하고 직관적임
- 스레드 안정성을 확보하기 위한 별도 스레드 관리 없이도 순차적 로직 실행이 가능함
- TDD 사이클 내에서 비즈니스 로직 테스트 시 Mutex Lock 획득 여부를 Mock하거나 별도 테스트 전략으로 검증 가능함
-
한계
- Mutex는 한 임계 구역 처리에 대기 시간이 생길 수 있음
- 큰 트래픽에서 순차 처리가 병목으로 작용할 수 있음
- 더 복잡한 동시성 전략(예: R/W Lock, Lock-free 알고리즘)을 적용하기 어렵고 단순한 순차 처리에 치중함
- 최대한 베이스 코드의 컨벤션을 따르도록 eslint 및 prettier 설정값을 조정함
- 빌드 속도 향상과 작업 효율 개선을 위해 NestJS와의 호환도가 높은 SWC 컴파일러(Rust 기반)로 교체하고 Jest Transform으로도 적용함
- 처음에는 Controller 레벨에서 바로 기본 기능을 구현해보았으나, 리팩토링 과정에서 Service 레이어로 분리함
- HttpException(400 Bad Request)을 통해 예외 처리를 개선함
- 통합 테스트를 추가하는 과정에서 단위 테스트에서는 발견하지 못했던 문제들까지 수정함
- 그 외에도 TS 문법 문제, 절대경로 문제, supertest namespace import 문제, Providers 미포함 문제 등의 자잘한 버그를 해결함
- TDD의 효율성: 이번 기회에 TDD를 적극 활용해보니 변경에 안전한 구조를 확보할 수 있었고 괜히 사용하는 게 아니라는 걸 깨닫게 됨
- 아키텍처의 안전성: Clean Architecture 원칙에 따라 레이어를 구분하니 정책 변경, 로직 분리, Mocking 및 테스트가 한층 수월해짐
- 확장성 확보: 동시성 제어 로직을 Service 레벨과 Lock Manager로 분리하면서 추후 정책 변경이나 Lock 방식 변경에 용이하게 됨
- 디버깅 숙련: 처음 접하는 다양한 이슈를 해결하며 테스트 환경 구축 및 디버깅 노하우를 습득하게 됨
Author: 이선민 @LilMGenius
Update:2024-12-20 9AM