팀 프로젝트 일정 관리에서의 데이터 일관성 유지 회고
프로젝트를 진행하면서 겪은 가장 큰 도전 중 하나는 팀 프로젝트 기간과 개별 일정 간의 일관성 문제였습니다. 우리 서비스는 단순 피드백을 넘어서 대학생들이 팀 내에서 자유롭게 일정과 할 일을 관리하며 능동적으로 소통할 수 있도록 설계되었는데요, 그만큼 비즈니스 로직에 따른 세밀한 제약 조건을 고려해야 했습니다.
비즈니스 로직의 핵심 요구사항
- 팀 프로젝트 기간 수정: 오직 팀장만 프로젝트 기간을 수정할 수 있습니다.
- 일정 추가: 팀원 누구나 일정을 추가할 수 있습니다.
이러한 제약은 단순한 피드백 시스템이 아니라, 팀원들이 스스로 일정 관리 및 피드백을 주고받음으로써 리텐션을 높이는 핵심 기능을 수행하도록 돕기 위해 고안되었습니다.
하지만 이 요구사항 때문에 두 가지 중요한 문제에 직면했습니다.
- 동시성 문제: 팀장이 프로젝트 기간을 수정하는 순간, 팀원이 일정을 추가하면 일정이 팀 프로젝트 기간 범위를 벗어날 위험이 있습니다.
- 검증의 최신성 문제: 일정 생성 시, 팀 프로젝트 기간을 조회해 검증하는 사이 값이 변경되면, 유효하지 않은 일정이 생성될 수 있습니다.
문제의 본질:
- 일정 생성 중에는 프로젝트 기간이 변경되면 안 된다.
- 프로젝트 기간 변경 시에는 새로운 일정 생성이 없어야 한다.
아키텍쳐 설계: 도메인 모델의 분리
처음에는 일정이 단순히 팀의 하위 도메인처럼 보였지만, 실제로는 일정이 우리 서비스의 핵심 기능임이 분명했습니다. 일정은 단순히 시간 관리가 아니라,
- 할 일 관리,
- 종료 이벤트 발행, 그리고
- 피드백 애그리거트와의 빈번한 이벤트 주고받기등 중요한 역할을 수행합니다.
따라서 일정은 팀과 독립적인 애그리거트 루트로 분리하였습니다.
이렇게 분리함으로써, 각 도메인이 자신만의 불변 조건(예: 일정 종료)을 스스로 책임질 수 있도록 설계했습니다.
데이터 일관성 문제 해결 전략
두 애그리거트 간의 일관성을 유지하기 위해 다양한 전략을 고민했습니다.
1. 도메인 이벤트 방식
- 장점: 여러 엔티티 간의 이벤트 기반 연동 가능
- 단점: 상태 전이, 롤백, 트랜잭션 커밋 후 재검증 등 복잡성이 증가하여 오히려 모놀리식의 장점을 잃게 됨
- 상태 패턴과 도메인 이벤트, Transition 시 이벤트 발행 방식으로 구현하게 될 경우, 구현해야 하는 상태 기계들. 매우 양이 방대하다.
2. 도메인 서비스 도입
결국 TeamPlanOrchestrator라는 도메인 서비스를 도입하여 스프링 트랜잭션으로 간단하게 관리하는 방식을 선택했습니다.
일관성 유지 전략: 다양한 Lock 전략 비교
여러 트랜잭션 격리 수준과 락 전략을 고민한 결과, 최종적으로 select for update
방식을 선택하게 되었습니다.
- Serializable:
- 장점: 팀 정보의 최신성을 보장
- 단점: 성능 저하, 롤백 가능성 증가
- 결론: 폐기
- LockModeType.OPTIMISTIC:
- 장점: 버전 기반 충돌 감지 가능
- 단점: 일정 생성 시 팀의 변경 사항을 반영할 방법이 없음
- 결론: 폐기
- LockModeType.OPTIMISTIC_FORCE_INCREMENT:
- 장점: 일정 생성 사실을 팀 도메인에 알릴 수 있음
- 단점: 불필요한 충돌 및 롤백 발생, 직관성 부족
- 결론: 폐기
- DB 레벨 Check:
- 단점: 특정 DB 기능에 종속되어 이식성 문제 발생
- 결론: 폐기
- select for update:
- 장점:
- 명시적이고 단순한 구현
- 2PL(두 단계 락킹)을 통해 일관성 보장
- 팀 레코드에 Shared Lock(일정 생성 시)과 Exclusive Lock(팀 정보 수정 시)을 통해 필요한 동시성 제어 수행
- 장점:
실행 전략:
- 일정 생성 시: 먼저 팀에 Shared Lock을 걸어 일정 생성 가능 여부를 확인
- 팀 정보 수정 시: 팀에 Exclusive Lock을 걸어 다른 일정 생성과의 충돌을 차단
- TeamPlanOrchestrationService에서 반드시 팀 레코드에 락을 먼저 획득하여 데드락 발생 가능성을 최소화