1. 개요
트레이너(ADMIN 또는 VIP 자격 보유자)와 피코칭 일반 회원(NORMAL) 간의 유기적인 코칭 관계를 맺고 안정적으로 이력을 모니터링하기 위한 핵심 비즈니스 도메인의 설계 회고입니다.
“회원은 반드시 오직 한 명의 트레이너만 지정할 수 있다”는 강력한 비즈니스 제약 조건을 보장하는 구조적 설계와 함께, 실제 프로덕션 서비스 환경에서 가장 골머리를 썩이게 만드는 **“한쪽 당사자가 서비스를 탈퇴할 때, 기존의 데이터 정합성과 히스토리를 어떻게 파괴하지 않고 영속적으로 보존할 것인가”**에 대한 현명한 Soft Delete 변형 전략 노하우를 담고 있습니다.
2. 핵심 내용
2-1. 트레이너와 회원의 1:N 비즈니스 코칭 관계 구축과 패키지 레이아웃
회원(User) 도메인의 의존적 하위 개념으로 설계한 domain.user.relationship 패키지 안에는 API 컨트롤러, DTO, 리포지토리, 서비스, 엔티티 그리고 상태 정의 Enum들이 응집도 높게 분할 배치되었습니다.
- 데이터베이스 복합 유니크 제약:
UserRelationshipEntity테이블에trainer_id+client_id복합 유니크 키(Unique Constraint)를 DB 인프라 레벨에서 강하게 결착했습니다. 이 조치 덕분에 네트워크 레이턴시나 비동기 중복 요청 클릭 오류로 인해 동일한 두 사용자 간에 엉뚱한 관계 데이터가 중복으로 생성되는 시나리오를 철저히 차단했습니다. - 철저한 비즈니스 1:N 제약: 일반 회원은 무조건 단 한 명의 트레이너에게만 매칭(ACCEPTED)될 수 있어야 합니다. 이를 위해 요청 생성 시점에 서비스 단에서
existsByClientUserIdAndStatus조회를 동반한 선행 검증을 통과하도록 엄격한 게이팅을 부여했습니다.
2-2. 회원 탈퇴 시나리오와 Soft Delete 변형 대응 전략 (원인과 해결책)
- 문제 상황: 만약 회원이 탈퇴할 때 관계 테이블(
USER_RELATIONSHIP)의 해당 행을 물리적으로 통째로DELETE(Hard Delete) 처리해 버리면 심각한 부작용이 터집니다. 트레이너의 CMS 화면에서 “내가 이전에 이 회원에게 어떤 식단 코칭 피드백과 운동 지도를 해 주었는지” 과거의 정밀했던 모든 일지 기록과 대시보드 히스토리가 몽땅 유실되어 트레이너 입장에서 불합리한 UX를 초래하게 됩니다. - 원인 분석: 관계형 데이터베이스 외래 키(FK) 제약 조건으로 인해 회원을 물리 삭제하려면 관계 테이블 데이터도 함께 캐스케이드(Cascade) 삭제되거나 에러를 뿜어야 합니다. 그렇다고 탈퇴 유저 데이터를 그대로 DB에 방치해 두는 것은 GDPR 및 개인정보보호법에 강하게 위배되며, 탈퇴한 유저의 이메일로 다른 신규 가입자가 회원가입을 하려 할 때 중복 계정 충돌을 야기합니다.
- 해결 방안: 물리 삭제 대신 외래 키 참조 끊기 및 Soft Delete 변형 방식을 적용했습니다.
UserRelationshipEntity내부의trainer및client필드(FK)를Nullable구조로 설계했습니다.- 회원이 최종 탈퇴 절차를 밟을 때, 연동된 모든 관계를 찾아서 관계 상태 필드(
status)를DISCONNECTED(연결 끊김) 상태로 안전하게 업데이트시킵니다. - 이어서 엔티티 내부의
updateClient(null)혹은updateTrainer(null)를 가동시켜 실제 탈퇴 회원과 연결되어 있던 FK 값을null로 세련되게 치워 줍니다. - 이를 통해 탈퇴한 유저의 정보는 법적으로 완벽하게 소멸시키면서도, 관계 테이블에 존재하던 “과거에 누군가와 코칭 관계가 성사되어 일지를 기록했었다”는 메타 데이터와 이력은 깨끗하게 보존되도록 절충안을 구현했습니다.
2-3. 안전 장치 수립 결과와 맞춤 추천 라이브러리 검토
조회 시점(getMyClients)에 만약 상대방 회원이 탈퇴하여 client 값이 null로 유입되는 조건이라면, 프론트엔드 뷰단이 NPE(Null Pointer Exception)로 인해 붕괴하지 않도록 Mapper 레이어에서 “탈퇴한 회원”이라는 아름다운 자리표 더미 데이터(Dummy Data)를 채워 넣어 렌더링 안정성을 완결했습니다.
- 추천 오픈소스 라이브러리:
Spring StateMachine: 코칭 관계가PENDING→ACCEPTED→DISCONNECTED또는REJECTED→PENDING등으로 전이되는 과정이 향후 훨씬 비대해질 때 활용하기 좋은 고도화 상태 엔진입니다. 복잡해지는 상태 전이 전후에 알림 발송이나 FK 초기화 비즈니스 액션을 트리거하도록 선언적으로 설정할 수 있어 중장기적 확장에 유리합니다.Querydsl: 트레이너의 회원 목록을 페이징 조회할 때,null값 방어 조건이나 탈퇴 필터링 등 다이내믹한 쿼리 분기를 컴파일 타임 타입 세이프하게 작성할 수 있게 돕는 JPA 짝꿍 도구입니다. 복잡도가 높아지는 1:N 관계 쿼리의 가독성을 보장해 주므로 반드시 추천해 드립니다.