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

15 KiB

Content Service 데이터베이스 설계서

데이터설계 요약

설계 개요

  • 서비스: Content Service (이미지 생성 및 콘텐츠 관리)
  • 아키텍처 패턴: Clean Architecture
  • 데이터베이스: PostgreSQL (주 저장소), Redis (캐시 및 Job 상태 관리)
  • 설계 원칙: 데이터독립성원칙 준수, Entity 클래스와 1:1 매핑

주요 엔티티

  1. Content: 이벤트별 콘텐츠 집합 정보
  2. GeneratedImage: AI 생성 이미지 메타데이터 및 CDN URL
  3. Job: 비동기 이미지 생성 작업 상태 추적

캐시 설계 (Redis)

  1. RedisJobData: Job 상태 추적 (TTL: 1시간)
  2. RedisImageData: 이미지 캐싱 (TTL: 7일)
  3. RedisAIEventData: AI 추천 데이터 캐싱 (TTL: 1시간)

주요 특징

  • 이미지 메타데이터 최적화: CDN URL만 저장, 실제 이미지는 Azure Blob Storage
  • 비동기 처리: Kafka 기반 Job 처리, Redis로 상태 관리
  • 캐싱 전략: eventId 기반 캐시 키, TTL 설정으로 자동 만료
  • 외부 연동: Stable Diffusion/DALL-E API, Azure Blob Storage CDN

1. PostgreSQL 데이터베이스 설계

1.1 테이블: content (콘텐츠 집합)

목적: 이벤트별 생성된 콘텐츠 집합 정보 관리

Entity 매핑: com.kt.event.content.biz.domain.Content

컬럼명 데이터 타입 NULL 기본값 설명
id BIGSERIAL NOT NULL AUTO 콘텐츠 ID (PK)
event_id VARCHAR(100) NOT NULL - 이벤트 초안 ID
event_title VARCHAR(200) NOT NULL - 이벤트 제목
event_description TEXT NULL - 이벤트 설명
created_at TIMESTAMP NOT NULL NOW() 생성 시각
updated_at TIMESTAMP NOT NULL NOW() 수정 시각

제약 조건:

  • PRIMARY KEY: id
  • UNIQUE INDEX: event_id (이벤트당 하나의 콘텐츠 집합)
  • INDEX: created_at (시간 기반 조회)

비즈니스 규칙:

  • 하나의 이벤트는 하나의 콘텐츠 집합만 보유
  • 이미지는 별도 테이블에서 1:N 관계로 관리

1.2 테이블: generated_image (생성된 이미지)

목적: AI 생성 이미지 메타데이터 및 CDN URL 관리

Entity 매핑: com.kt.event.content.biz.domain.GeneratedImage

컬럼명 데이터 타입 NULL 기본값 설명
id BIGSERIAL NOT NULL AUTO 이미지 ID (PK)
event_id VARCHAR(100) NOT NULL - 이벤트 초안 ID
style VARCHAR(20) NOT NULL - 이미지 스타일 (FANCY, SIMPLE, TRENDY)
platform VARCHAR(30) NOT NULL - 플랫폼 (INSTAGRAM, FACEBOOK, KAKAO, BLOG)
cdn_url VARCHAR(500) NOT NULL - CDN 이미지 URL (Azure Blob)
prompt TEXT NOT NULL - 생성에 사용된 프롬프트
selected BOOLEAN NOT NULL false 사용자 선택 여부
width INT NOT NULL - 이미지 너비 (픽셀)
height INT NOT NULL - 이미지 높이 (픽셀)
file_size BIGINT NULL - 파일 크기 (bytes)
content_type VARCHAR(50) NOT NULL 'image/png' MIME 타입
created_at TIMESTAMP NOT NULL NOW() 생성 시각
updated_at TIMESTAMP NOT NULL NOW() 수정 시각

제약 조건:

  • PRIMARY KEY: id
  • INDEX: (event_id, style, platform) - 필터링 조회 최적화
  • INDEX: event_id - 이벤트별 이미지 조회
  • INDEX: created_at - 시간 기반 조회
  • CHECK: style IN ('FANCY', 'SIMPLE', 'TRENDY')
  • CHECK: platform IN ('INSTAGRAM', 'FACEBOOK', 'KAKAO', 'BLOG')
  • CHECK: width > 0 AND height > 0

비즈니스 규칙:

  • 이미지 실체는 Azure Blob Storage에 저장, DB는 메타데이터만 보유
  • 동일한 (event_id, style, platform) 조합으로 여러 이미지 생성 가능 (재생성)
  • selected = true인 이미지가 최종 선택 이미지

플랫폼별 기본 해상도:

  • INSTAGRAM: 1080x1080
  • FACEBOOK: 1200x628
  • KAKAO: 800x800
  • BLOG: 800x600

1.3 테이블: job (비동기 작업 추적)

목적: 이미지 생성 비동기 작업 상태 추적

Entity 매핑: com.kt.event.content.biz.domain.Job

컬럼명 데이터 타입 NULL 기본값 설명
id VARCHAR(100) NOT NULL - Job ID (PK)
event_id VARCHAR(100) NOT NULL - 이벤트 초안 ID
job_type VARCHAR(50) NOT NULL - 작업 타입 (IMAGE_GENERATION, IMAGE_REGENERATION)
status VARCHAR(20) NOT NULL 'PENDING' 작업 상태 (PENDING, PROCESSING, COMPLETED, FAILED)
progress INT NOT NULL 0 진행률 (0-100)
result_message TEXT NULL - 완료 메시지
error_message TEXT NULL - 에러 메시지
created_at TIMESTAMP NOT NULL NOW() 생성 시각
updated_at TIMESTAMP NOT NULL NOW() 수정 시각
completed_at TIMESTAMP NULL - 완료 시각

제약 조건:

  • PRIMARY KEY: id
  • INDEX: event_id - 이벤트별 작업 조회
  • INDEX: (status, created_at) - 상태별 작업 조회
  • CHECK: status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED')
  • CHECK: job_type IN ('IMAGE_GENERATION', 'IMAGE_REGENERATION')
  • CHECK: progress >= 0 AND progress <= 100

비즈니스 규칙:

  • Job ID는 "job-img-{uuid}" 형식 (외부에서 생성)
  • 상태 전이: PENDING → PROCESSING → COMPLETED/FAILED
  • COMPLETED/FAILED 상태에서 completed_at 자동 설정
  • Redis에도 동일한 Job 정보 저장 (TTL 1시간, 폴링 조회 최적화)

2. Redis 캐시 설계

2.1 RedisJobData (Job 상태 캐싱)

목적: 비동기 작업 상태 폴링 조회 성능 최적화

DTO 매핑: com.kt.event.content.biz.dto.RedisJobData

Redis 키 패턴: job:{jobId}

TTL: 1시간 (3600초)

데이터 구조 (Hash):

job:job-img-abc123 = {
  "id": "job-img-abc123",
  "eventId": "evt-draft-12345",
  "jobType": "IMAGE_GENERATION",
  "status": "PROCESSING",
  "progress": 50,
  "resultMessage": null,
  "errorMessage": null,
  "createdAt": "2025-10-29T10:00:00Z",
  "updatedAt": "2025-10-29T10:00:05Z"
}

사용 시나리오:

  1. 이미지 생성 요청 시 Job 생성 → Redis 저장
  2. 클라이언트 폴링 조회 → Redis에서 빠르게 조회
  3. Job 완료 후 1시간 뒤 자동 삭제
  4. PostgreSQL의 job 테이블과 동기화 (영구 이력)

2.2 RedisImageData (이미지 캐싱)

목적: 동일 이벤트 재요청 시 즉시 반환

DTO 매핑: com.kt.event.content.biz.dto.RedisImageData

Redis 키 패턴: image:{eventId}:{style}:{platform}

TTL: 7일 (604800초)

데이터 구조 (Hash):

image:evt-draft-12345:SIMPLE:INSTAGRAM = {
  "eventId": "evt-draft-12345",
  "style": "SIMPLE",
  "platform": "INSTAGRAM",
  "imageUrl": "https://cdn.kt-event.com/images/evt-draft-12345-simple.png",
  "prompt": "Clean and simple event poster with coffee theme",
  "createdAt": "2025-10-29T10:00:10Z"
}

사용 시나리오:

  1. 이미지 생성 완료 → Redis 저장
  2. 동일 이벤트 재요청 → Redis 캐시 확인 → 즉시 반환
  3. 7일 후 자동 삭제 (오래된 캐시 정리)

2.3 RedisAIEventData (AI 추천 데이터 캐싱)

목적: AI Service 이벤트 데이터 캐싱

DTO 매핑: com.kt.event.content.biz.dto.RedisAIEventData

Redis 키 패턴: ai:event:{eventId}

TTL: 1시간 (3600초)

데이터 구조 (Hash):

ai:event:evt-draft-12345 = {
  "eventId": "evt-draft-12345",
  "recommendedStyles": ["SIMPLE", "TRENDY"],
  "recommendedKeywords": ["coffee", "spring", "discount"],
  "cachedAt": "2025-10-29T10:00:00Z"
}

사용 시나리오:

  1. AI Service에서 이벤트 분석 완료 → Redis 저장
  2. Content Service에서 이미지 생성 시 AI 추천 데이터 참조
  3. 1시간 후 자동 삭제

3. 인덱스 전략

3.1 성능 최적화 인덱스

generated_image 테이블:

-- 이벤트별 이미지 조회 (가장 빈번)
CREATE INDEX idx_generated_image_event_id ON generated_image(event_id);

-- 필터링 조회 (스타일, 플랫폼)
CREATE INDEX idx_generated_image_filter ON generated_image(event_id, style, platform);

-- 선택된 이미지 조회
CREATE INDEX idx_generated_image_selected ON generated_image(event_id, selected) WHERE selected = true;

-- 시간 기반 조회 (최근 생성 이미지)
CREATE INDEX idx_generated_image_created ON generated_image(created_at DESC);

job 테이블:

-- 이벤트별 작업 조회
CREATE INDEX idx_job_event_id ON job(event_id);

-- 상태별 작업 조회 (모니터링)
CREATE INDEX idx_job_status ON job(status, created_at DESC);

content 테이블:

-- 이벤트 ID 기반 조회 (UNIQUE)
CREATE UNIQUE INDEX idx_content_event_id ON content(event_id);

4. 데이터 정합성 규칙

4.1 데이터 일관성 보장

PostgreSQL ↔ Redis 동기화:

  • Write-Through: Job 생성 시 PostgreSQL + Redis 동시 저장
  • Cache-Aside: 이미지 조회 시 Redis 먼저 확인 → 없으면 PostgreSQL
  • TTL 기반 자동 만료: Redis 데이터는 TTL로 자동 정리

4.2 트랜잭션 범위

이미지 생성 트랜잭션:

BEGIN TRANSACTION
  1. Job 상태 업데이트 (PROCESSING)
  2. 외부 API 호출 (Stable Diffusion)
  3. CDN 업로드 (Azure Blob)
  4. generated_image INSERT
  5. Job 상태 업데이트 (COMPLETED)
  6. Redis 캐시 저장
COMMIT

실패 시 롤백:

  • 외부 API 실패 → Job 상태 FAILED, error_message 저장
  • CDN 업로드 실패 → 재시도 (3회), 최종 실패 시 FAILED
  • Circuit Breaker OPEN → Fallback 템플릿 이미지 사용

5. 백업 및 보존 정책

5.1 백업 전략

PostgreSQL:

  • Full Backup: 매일 오전 2시 (Cron Job)
  • Incremental Backup: 6시간마다
  • 보관 기간: 30일

Redis:

  • RDB Snapshot: 1시간마다
  • AOF (Append-Only File): 실시간 로깅
  • 보관 기간: 7일

5.2 데이터 보존 정책

generated_image:

  • 보존 기간: 90일
  • 정리 방식: created_at 기준 90일 초과 데이터 자동 삭제 (Batch Job)

job:

  • 보존 기간: 30일
  • 정리 방식: created_at 기준 30일 초과 데이터 자동 삭제

content:

  • 보존 기간: 영구 (이미지 삭제 시에만 CASCADE 삭제)

6. 확장성 고려사항

6.1 수평 확장

Read Replica:

  • PostgreSQL Read Replica 구성 (조회 성능 향상)
  • 쓰기: Master, 읽기: Replica

Sharding 전략 (미래 대비):

  • Shard Key: event_id (이벤트 ID 기반 분산)
  • 예상 임계점: 1억 건 이미지 이상

6.2 캐시 전략

Redis Cluster:

  • 3 Master + 3 Replica 구성
  • 데이터 파티셔닝: event_id 기반 Hash Slot

Cache Warming:

  • 자주 조회되는 이미지는 Redis에 영구 보관 (별도 TTL 없음)

7. 모니터링 지표

7.1 성능 지표

PostgreSQL:

  • QPS (Queries Per Second): 이미지 조회 빈도
  • Slow Query: 100ms 이상 쿼리 모니터링
  • Connection Pool: 사용률 70% 이하 유지

Redis:

  • Cache Hit Ratio: 90% 이상 목표
  • Memory Usage: 80% 이하 유지
  • Eviction Rate: 최소화

7.2 비즈니스 지표

이미지 생성:

  • 성공률: 95% 이상
  • 평균 생성 시간: 10초 이내
  • Circuit Breaker OPEN 빈도: 월 5회 이하

캐시 효율:

  • 재사용률: 동일 이벤트 재요청 비율
  • TTL 만료율: 7일 이내 재조회 비율

8. 데이터독립성 검증

8.1 서비스 경계

Content Service 소유 데이터:

  • content, generated_image, job 테이블 완전 소유
  • 다른 서비스는 API를 통해서만 접근

외부 의존성 최소화:

  • Event Service 데이터: event_id만 참조 (FK 없음)
  • User Service 데이터: 참조하지 않음
  • AI Service 데이터: Redis 캐시로만 참조

8.2 크로스 서비스 조인 금지

허용되지 않는 패턴:

-- ❌ 금지: Event Service DB와 조인
SELECT * FROM event_service.event e
JOIN content_service.generated_image i ON e.id = i.event_id;

올바른 패턴:

// ✅ 허용: API 호출 또는 캐시 참조
String eventTitle = eventServiceClient.getEvent(eventId).getTitle();

9. 보안 고려사항

9.1 접근 제어

PostgreSQL:

  • 계정: content_service_user (최소 권한)
  • 권한: content, generated_image, job 테이블만 SELECT, INSERT, UPDATE, DELETE
  • 스키마 변경: DBA 계정만 가능

Redis:

  • 계정: content_service_redis (별도 패스워드)
  • ACL: 특정 키 패턴만 접근 (job:*, image:*, ai:event:*)

9.2 데이터 암호화

전송 암호화:

  • PostgreSQL: SSL/TLS 연결 강제
  • Redis: TLS 연결 강제

저장 암호화:

  • PostgreSQL: AES-256 암호화 (pgcrypto 확장)
  • CDN URL은 공개 데이터 (암호화 불필요)

10. 클래스 설계와의 매핑 검증

10.1 Entity 클래스 매핑

Entity 클래스 PostgreSQL 테이블 필드 매핑 일치
Content content 완전 일치
GeneratedImage generated_image 완전 일치 (width, height 추가)
Job job 완전 일치

10.2 DTO 매핑

DTO 클래스 Redis 키 패턴 필드 매핑 일치
RedisJobData job:{jobId} 완전 일치
RedisImageData image:{eventId}:{style}:{platform} 완전 일치
RedisAIEventData ai:event:{eventId} 완전 일치

10.3 Enum 매핑

Enum 클래스 데이터베이스 값 일치
ImageStyle VARCHAR CHECK FANCY, SIMPLE, TRENDY
Platform VARCHAR CHECK INSTAGRAM, FACEBOOK, KAKAO, BLOG
JobStatus VARCHAR CHECK PENDING, PROCESSING, COMPLETED, FAILED

11. 마이그레이션 전략

11.1 초기 배포

데이터베이스 생성:

CREATE DATABASE content_service_db;
CREATE SCHEMA content;

테이블 생성 순서:

  1. content (부모 테이블)
  2. generated_image (자식 테이블)
  3. job (독립 테이블)

11.2 버전 관리

도구: Flyway (Spring Boot 통합)

마이그레이션 파일 위치: src/main/resources/db/migration/

명명 규칙: V{version}__{description}.sql

예시:

  • V1__create_content_tables.sql
  • V2__add_image_size_columns.sql
  • V3__create_job_status_index.sql

12. 테스트 데이터

12.1 샘플 데이터

Content:

INSERT INTO content (event_id, event_title, event_description)
VALUES ('evt-draft-12345', '봄맞이 커피 할인 이벤트', '신메뉴 아메리카노 1+1 이벤트');

GeneratedImage:

INSERT INTO generated_image (event_id, style, platform, cdn_url, prompt, width, height)
VALUES
  ('evt-draft-12345', 'SIMPLE', 'INSTAGRAM',
   'https://cdn.kt-event.com/images/evt-draft-12345-simple.png',
   'Clean and simple coffee event poster', 1080, 1080),
  ('evt-draft-12345', 'FANCY', 'INSTAGRAM',
   'https://cdn.kt-event.com/images/evt-draft-12345-fancy.png',
   'Vibrant and colorful coffee event poster', 1080, 1080);

Job:

INSERT INTO job (id, event_id, job_type, status, progress)
VALUES ('job-img-abc123', 'evt-draft-12345', 'IMAGE_GENERATION', 'COMPLETED', 100);

13. 참조 문서

  • 클래스 설계서: design/backend/class/content-service.puml
  • API 명세서: design/backend/api/content-service-api.yaml
  • 통합 검증: design/backend/class/integration-verification.md
  • 데이터설계 가이드: claude/data-design.md

작성자: Backend Developer (아키텍트) 작성일: 2025-10-29 버전: v1.0