kt-event-marketing/design/backend/database/participation-service.md
jhbkjh 3075a5d49f 물리아키텍처 설계 완료
 주요 기능
- Azure 기반 물리아키텍처 설계 (개발환경/운영환경)
- 7개 마이크로서비스 물리 구조 설계
- 네트워크 아키텍처 다이어그램 작성 (Mermaid)
- 환경별 비교 분석 및 마스터 인덱스 문서

📁 생성 파일
- design/backend/physical/physical-architecture.md (마스터)
- design/backend/physical/physical-architecture-dev.md (개발환경)
- design/backend/physical/physical-architecture-prod.md (운영환경)
- design/backend/physical/*.mmd (4개 Mermaid 다이어그램)

🎯 핵심 성과
- 비용 최적화: 개발환경 월 $143, 운영환경 월 $2,860
- 확장성: 개발환경 100명 → 운영환경 10,000명 (100배)
- 가용성: 개발환경 95% → 운영환경 99.9%
- 보안: 다층 보안 아키텍처 (L1~L4)

🛠️ 기술 스택
- Azure Kubernetes Service (AKS)
- Azure Database for PostgreSQL Flexible
- Azure Cache for Redis Premium
- Azure Service Bus Premium
- Application Gateway + WAF

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 15:13:01 +09:00

11 KiB

Participation Service 데이터 설계서

📋 데이터 설계 요약

목적

  • 이벤트 참여자 관리 및 당첨자 추첨 시스템 지원
  • 중복 참여 방지 및 매장 방문 보너스 관리
  • 공정한 추첨 이력 관리

설계 범위

  • 서비스명: participation-service
  • 아키텍처 패턴: Layered Architecture
  • 데이터베이스: PostgreSQL (관계형 데이터)
  • 캐시: Redis (참여 세션 정보, 추첨 결과 임시 저장)

Entity 목록

  1. Participant: 이벤트 참여자 정보
  2. DrawLog: 당첨자 추첨 이력

1. 데이터베이스 구조

1.1 데이터베이스 정보

  • Database Name: participation_db
  • Schema: public (기본 스키마)
  • Character Set: UTF8
  • Collation: ko_KR.UTF-8

1.2 테이블 목록

테이블명 설명 주요 특징
participants 이벤트 참여자 중복 참여 방지, 보너스 응모권 관리
draw_logs 당첨자 추첨 이력 재추첨 방지, 추첨 이력 관리

2. 테이블 상세 설계

2.1 participants (참여자)

테이블 설명

  • 이벤트 참여자 정보 및 당첨 상태 관리
  • 동일 이벤트에 동일 전화번호로 중복 참여 방지
  • 매장 방문 시 보너스 응모권 부여

컬럼 정의

컬럼명 데이터 타입 NULL 기본값 설명
id BIGSERIAL NOT NULL AUTO 내부 식별자 (PK)
participant_id VARCHAR(50) NOT NULL - 참여자 고유 ID (형식: EVT{eventId}-{YYYYMMDD}-{SEQ})
event_id VARCHAR(50) NOT NULL - 이벤트 ID (외부 참조)
name VARCHAR(100) NOT NULL - 참여자 이름
phone_number VARCHAR(20) NOT NULL - 전화번호 (중복 참여 검증용)
email VARCHAR(100) NULL - 이메일 주소
channel VARCHAR(50) NOT NULL - 참여 채널 (WEB, MOBILE, INSTORE)
store_visited BOOLEAN NOT NULL false 매장 방문 여부
bonus_entries INTEGER NOT NULL 1 보너스 응모권 개수 (매장 방문 시 +2)
agree_marketing BOOLEAN NOT NULL false 마케팅 수신 동의
agree_privacy BOOLEAN NOT NULL true 개인정보 수집 동의 (필수)
is_winner BOOLEAN NOT NULL false 당첨 여부
winner_rank INTEGER NULL - 당첨 순위 (1등, 2등 등)
won_at TIMESTAMP NULL - 당첨 일시
created_at TIMESTAMP NOT NULL NOW() 참여 일시
updated_at TIMESTAMP NOT NULL NOW() 수정 일시

제약 조건

Primary Key

PRIMARY KEY (id)

Unique Constraints

CONSTRAINT uk_participant_id UNIQUE (participant_id)
CONSTRAINT uk_event_phone UNIQUE (event_id, phone_number)

Check Constraints

CONSTRAINT chk_bonus_entries CHECK (bonus_entries >= 1 AND bonus_entries <= 3)
CONSTRAINT chk_channel CHECK (channel IN ('WEB', 'MOBILE', 'INSTORE'))
CONSTRAINT chk_winner_rank CHECK (winner_rank IS NULL OR winner_rank > 0)

인덱스

-- 이벤트별 참여자 조회 (최신순)
CREATE INDEX idx_participants_event_created ON participants(event_id, created_at DESC);

-- 이벤트별 당첨자 조회
CREATE INDEX idx_participants_event_winner ON participants(event_id, is_winner, winner_rank);

-- 매장 방문자 필터링
CREATE INDEX idx_participants_event_store ON participants(event_id, store_visited);

-- 전화번호 중복 검증 (복합 유니크 인덱스로 커버)
-- uk_event_phone 인덱스 활용

2.2 draw_logs (당첨자 추첨 이력)

테이블 설명

  • 당첨자 추첨 이력 기록
  • 재추첨 방지 및 감사 추적
  • 추첨 알고리즘 및 설정 정보 저장

컬럼 정의

컬럼명 데이터 타입 NULL 기본값 설명
id BIGSERIAL NOT NULL AUTO 내부 식별자 (PK)
event_id VARCHAR(50) NOT NULL - 이벤트 ID (외부 참조)
total_participants INTEGER NOT NULL - 총 참여자 수
winner_count INTEGER NOT NULL - 당첨자 수
apply_store_visit_bonus BOOLEAN NOT NULL false 매장 방문 보너스 적용 여부
algorithm VARCHAR(50) NOT NULL 'RANDOM' 추첨 알고리즘 (RANDOM, WEIGHTED)
drawn_at TIMESTAMP NOT NULL NOW() 추첨 실행 일시
drawn_by VARCHAR(100) NOT NULL 'SYSTEM' 추첨 실행자
created_at TIMESTAMP NOT NULL NOW() 생성 일시
updated_at TIMESTAMP NOT NULL NOW() 수정 일시

제약 조건

Primary Key

PRIMARY KEY (id)

Unique Constraints

CONSTRAINT uk_draw_event UNIQUE (event_id)

Check Constraints

CONSTRAINT chk_winner_count CHECK (winner_count > 0)
CONSTRAINT chk_total_participants CHECK (total_participants >= winner_count)
CONSTRAINT chk_algorithm CHECK (algorithm IN ('RANDOM', 'WEIGHTED'))

인덱스

-- 이벤트별 추첨 이력 조회
CREATE INDEX idx_draw_logs_event ON draw_logs(event_id);

-- 추첨 일시별 조회
CREATE INDEX idx_draw_logs_drawn_at ON draw_logs(drawn_at DESC);

3. 캐시 설계 (Redis)

3.1 캐시 키 구조

참여 세션 정보

  • Key: participation:session:{eventId}:{phoneNumber}
  • Type: String (JSON)
  • TTL: 10분
  • 용도: 중복 참여 방지, 빠른 검증
  • 데이터:
    {
      "participantId": "EVT123-20251029-001",
      "eventId": "EVT123",
      "phoneNumber": "010-1234-5678",
      "participatedAt": "2025-10-29T10:00:00"
    }
    

추첨 결과 임시 저장

  • Key: participation:draw:{eventId}
  • Type: String (JSON)
  • TTL: 1시간
  • 용도: 추첨 결과 임시 캐싱, 빠른 조회
  • 데이터:
    {
      "eventId": "EVT123",
      "winners": [
        {
          "participantId": "EVT123-20251029-001",
          "rank": 1,
          "name": "홍길동",
          "phoneNumber": "010-****-5678"
        }
      ],
      "drawnAt": "2025-10-29T15:00:00"
    }
    

이벤트별 참여자 카운트

  • Key: participation:count:{eventId}
  • Type: String (숫자)
  • TTL: 5분
  • 용도: 빠른 참여자 수 조회
  • 데이터: "123" (참여자 수)

3.2 캐시 전략

캐시 갱신 정책

  • Write-Through: 참여 등록 시 DB 저장 후 캐시 갱신
  • Cache-Aside: 조회 시 캐시 미스 시 DB 조회 후 캐시 저장

캐시 무효화

  • 이벤트 종료 시: participation:*:{eventId} 패턴 삭제
  • 추첨 완료 시: participation:count:{eventId} 삭제 (재조회 유도)

4. 데이터 무결성 설계

4.1 중복 참여 방지

1차 검증: Redis 캐시

1. 참여 요청 수신
2. Redis 키 확인: participation:session:{eventId}:{phoneNumber}
3. 키 존재 시 → DuplicateParticipationException 발생
4. 키 미존재 시 → 2차 검증 진행

2차 검증: PostgreSQL 유니크 제약

1. DB 삽입 시도
2. uk_event_phone 제약 위반 시 → DuplicateParticipationException 발생
3. 정상 삽입 시 → Redis 캐시 생성 (TTL: 10분)

4.2 재추첨 방지

추첨 이력 검증

-- 추첨 전 검증 쿼리
SELECT COUNT(*) FROM draw_logs WHERE event_id = ?;
-- 결과 > 0 → AlreadyDrawnException 발생

유니크 제약

-- uk_draw_event: 이벤트당 1회만 추첨 가능
CONSTRAINT uk_draw_event UNIQUE (event_id)

4.3 당첨자 수 검증

최소 참여자 수 검증

-- 추첨 전 참여자 수 확인
SELECT COUNT(*) FROM participants
WHERE event_id = ? AND is_winner = false;

-- 참여자 수 < 당첨자 수 → InsufficientParticipantsException 발생

5. 성능 최적화

5.1 인덱스 전략

쿼리 패턴별 인덱스

  1. 참여자 목록 조회 (페이징, 최신순)

    • 인덱스: idx_participants_event_created
    • 커버: (event_id, created_at DESC)
  2. 당첨자 목록 조회 (순위 오름차순)

    • 인덱스: idx_participants_event_winner
    • 커버: (event_id, is_winner, winner_rank)
  3. 매장 방문자 필터링

    • 인덱스: idx_participants_event_store
    • 커버: (event_id, store_visited)

5.2 캐시 활용

읽기 성능 최적화

  • 참여자 수 조회: Redis 캐시 우선 (TTL: 5분)
  • 추첨 결과 조회: Redis 캐시 (TTL: 1시간)
  • 중복 참여 검증: Redis 캐시 (TTL: 10분)

캐시 히트율 목표

  • 중복 참여 검증: 95% 이상
  • 추첨 결과 조회: 90% 이상
  • 참여자 수 조회: 85% 이상

6. 보안 고려사항

6.1 개인정보 보호

전화번호 마스킹

  • 저장: 원본 저장 (중복 검증용)
  • 조회: 마스킹 처리 (010-****-5678)
  • 로그: 마스킹 처리 (감사 추적용)

이메일 마스킹

6.2 데이터 암호화

저장 시 암호화 (향후 적용 권장)

  • 민감 정보: 전화번호, 이메일
  • 암호화 알고리즘: AES-256
  • 키 관리: AWS KMS 또는 HashiCorp Vault

7. 백업 및 복구

7.1 백업 정책

  • Full Backup: 매일 02:00 (KST)
  • Incremental Backup: 6시간마다
  • 보관 기간: 30일

7.2 복구 목표

  • RPO (Recovery Point Objective): 6시간 이내
  • RTO (Recovery Time Objective): 1시간 이내

8. 모니터링 지표

8.1 성능 지표

  • 참여 등록 응답 시간: 평균 < 200ms
  • 당첨자 조회 응답 시간: 평균 < 100ms
  • 캐시 히트율: > 85%

8.2 비즈니스 지표

  • 총 참여자 수: 이벤트별 실시간 집계
  • 매장 방문자 비율: 보너스 응모권 적용률
  • 중복 참여 시도 횟수: 비정상 접근 탐지

9. 데이터 마이그레이션 전략

9.1 초기 데이터 로드

  • 참조 데이터: 없음 (참여자 데이터는 실시간 생성)
  • 테스트 데이터: 샘플 참여자 100명, 추첨 이력 10건

9.2 데이터 정합성 검증

-- 중복 참여자 확인
SELECT event_id, phone_number, COUNT(*)
FROM participants
GROUP BY event_id, phone_number
HAVING COUNT(*) > 1;

-- 당첨자 순위 중복 확인
SELECT event_id, winner_rank, COUNT(*)
FROM participants
WHERE is_winner = true
GROUP BY event_id, winner_rank
HAVING COUNT(*) > 1;

-- 추첨 이력 정합성 확인
SELECT d.event_id, d.winner_count, COUNT(p.id) as actual_winners
FROM draw_logs d
LEFT JOIN participants p ON d.event_id = p.event_id AND p.is_winner = true
GROUP BY d.event_id, d.winner_count
HAVING d.winner_count != COUNT(p.id);

10. 참조 및 의존성

10.1 외부 서비스 참조

  • event-id: Event Service에서 생성한 이벤트 ID 참조 (캐시 기반)
  • user-id: User Service의 사용자 ID 참조 없음 (비회원 참여 가능)

10.2 이벤트 발행

  • Topic: participant-registered
  • Event: ParticipantRegisteredEvent
  • Consumer: Analytics Service

설계자: Backend Developer (최수연 "아키텍처") 설계일: 2025-10-29 문서 버전: v1.0