2️⃣ 투표 시스템
| 종류 |
제약 |
URL |
| 팀 투표 |
1인 1팀 |
POST /api/v1/votes/teams |
| 파트장 후보 투표 |
FE/BE 파트별 각 1표 |
POST /api/v1/votes/candidates |
📌 엔티티 설계
⇒ TeamVote (팀 투표) + CandidateVote (파트장 투표) 두 엔티티로 분리
단일 Vote + VoteType |
TeamVote + CandidateVote |
| team_id / candidate_id ⇒ nullable |
NOT NULL FK |
| XOR 검증 필요 |
스키마 자체가 무결성 강제 |
동시성
[1차 방어] 사전 체크 (existsByUserId / existsByUserIdAndPart)
└─ 일반 케이스 대부분 처리, 명확한 ALREADY_VOTED 응답
[2차 방어] DB UNIQUE 제약
├─ UNIQUE(user_id) — 팀 투표 1인 1표
└─ UNIQUE(user_id, part) — 파트별 1표
└─ race condition 시 DataIntegrityViolationException 발생
-> Service의 try/catch에서 ALREADY_VOTED로 변환
-> 어떤 경로든 일관된 응답 보장
// 투표 저장
try {
teamVoteRepository.save(TeamVote.of(user, team));
} catch (DataIntegrityViolationException e) {
throw new GeneralException(VoteErrorCode.ALREADY_VOTED);
}