Skip to content

챌린지 생성 규칙 관리 논의 필요 #36

@Kimgyuilli

Description

@Kimgyuilli

문제 설명

현재 챌린지 생성 로직에서 user당 활성 챌린지는 1개만 가능해야 하는 비즈니스 규칙이 있습니다.

현재 구현 상태 ✅

  • 애플리케이션 레벨 검증으로 구현 완료
  • 위치: ChallengeService.validateNoDuplicateActiveChallenge()
  • 정상적인 API 요청에 대해서는 중복 생성 방지 동작

기술 부채 🔶

  • DB 레벨 제약이 없어 동시성 문제(Race Condition)에 취약
  • 추후 팀 논의를 통해 DB 제약 추가 여부 결정 필요

현재 구현

검증 방식

// ChallengeCreationFacade.java:49-50
challengeService.validateNoDuplicateActiveChallenge(userId);

// ChallengeService.java:28-32                                                                                                                                                                         
public void validateNoDuplicateActiveChallenge(Long userId) {
    if (challengeRepository.existsByUserIdAndIsActiveTrue(userId)) {
        throw new ChallengeException(ChallengeErrorCode.DUPLICATE_ACTIVE_CHALLENGE);
    }
}

잠재적 문제: Race Condition

동시에 2개의 챌린지 생성 요청이 들어올 경우:

Time    Request A                          Request B
----    ---------                          ---------
T1      existsByUserId(1) → false
T2                                         existsByUserId(1) → false
T3      save(Challenge) ✓
T4                                         save(Challenge) ✓

결과: User 1에게 활성 챌린지 2 생성됨발생 확률:
- 일반적인 사용: 매우 낮음 (사용자가 동시에 2 클릭할 가능성)
- 악의적 공격: 높음 (동시 요청 의도적으로 발생 )

해결 방안 (추후 논의 필요)

Option 1: DB Unique Partial Index

장점:
- 데이터 무결성 강력 보장 (모든 경로에서)
- 성능 우수 (인덱스 검색 O(log n))
- 비활성 챌린지는 여러  가능

구현:
CREATE UNIQUE INDEX CONCURRENTLY idx_user_active_challenge
ON challenges(user_id) WHERE is_active = true;

단점:
- SQL 스크립트 수동 실행 필요 (또는 Flyway 도입)

난이도: ⭐️ (낮음)

---
Option 2: Flyway 마이그레이션 도구 도입

장점:
- 스키마 변경 자동화
- 버전 관리   협업 용이
- 프로덕션 배포 안전
- DB Unique Index와 함께 사용

단점:
- 추가 의존성 필요
- 초기 설정  학습 필요
- 로컬 개발 환경 구성 변경

난이도: ⭐️⭐️⭐️ (중간)

---
Option 3: 애플리케이션 레벨 

장점:
- 추가 의존성 없음
- 코드만으로 해결

단점:
- DB 제약 없어 데이터 정합성 약함
- 성능 영향 (비관적 : row lock, 낙관적 : 재시도)
- 직접 DB 접근  보호  
- 복잡도 증가

난이도: ⭐️⭐️ (낮음-중간)

---
Option 4: 현상 유지 (기술 부채 수용)

장점:
- 추가 작업 불필요
- 현재 동작 유지

단점:
- 동시성 이슈 발생 가능성 존재
- 추후 문제 발생  데이터 정리 필요

비교표

| 항목          | 현재 상태 | +DB Index    | +Flyway     | +       |
|---------------|-----------|--------------|-------------|-----------|
| 데이터 무결성 | ⚠️ 약함   | ✅ 강함      | ✅ 강함     | ⚠️ 약함   |
| 성능          | ✅ 빠름   | ✅ 빠름      | ✅ 빠름     | ❌ 느림   |
| 복잡도        | ✅ 낮음   | ✅ 낮음      | ⚠️ 보통     | ❌ 높음   |
| 자동화        | ✅ 자동   | ❌ 수동      | ✅ 자동     | ✅ 자동   |
| 추가 작업     | -         | SQL 1 실행 | 의존성 추가 | 코드 수정 |

참고 자료

관련 파일:
- ChallengeCreationFacade.java:49-50 - 검증 호출
- ChallengeService.java:28-32 - 검증 로직
- ChallengeRepository.java:17 - 존재 여부 확인 쿼리
- Challenge.java:43-44 - isActive 필드

작성된 SQL 스크립트 (필요시 사용):
- src/main/resources/db/20260105_add_unique_active_challenge_constraint.sql                                                                                                                            

 논의 필요 사항

1. 우선순위 결정
  - 동시성 이슈의 비즈니스 영향도는?
  - 언제까지 해결해야 하는가?
2. 해결 방안 선택
  - DB Index만 추가? (빠른 해결)
  - Flyway 도입? (장기적 이득, 초기 비용)
  - 현상 유지? (기술 부채 수용)
3. 개발 환경 전략
  - 로컬 vs 배포 환경 스키마 관리 방법
  - 팀원  DB 동기화 방법
4. 마이그레이션 계획
  - 기존 중복 데이터 확인  정리
  - 배포 일정  롤백 계획

제안

단계적 접근:
1. 1단계 (즉시): 현재 애플리케이션 레벨 검증 유지완료
2. 2단계 (논의 ): DB Unique Index 추가 (SQL 스크립트 준비됨)
3. 3단계 (선택): Flyway 도입 검토 ( 역량  필요성에 따라)

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions