🗄️ 데이터베이스 설계
설계 원칙: 실제 업무 플로우를 완벽하게 지원하는 데이터 구조
핵심 테이블 구조
📋 4개 핵심 테이블
- gift_orders: 선물 주문 (1:N 구조의 중심)
- gift_recipients: 수신자 상세 정보 + 상태 관리
- recipient_messages: 소통 이력 (카카오톡, SMS, 전화)
- recipient_change_logs: 변경 이력 (모든 수정사항 추적)
12단계 수신자 상태 관리
| 단계 |
상태 |
설명 |
아이콘 |
| 연락 단계 |
| 1 | PENDING | 연락 대기 | 📋 |
| 2 | FIRST_CONTACT | 1차 연락 발송 | 📤 |
| 3 | SECOND_CONTACT | 2차 연락 발송 | 📤📤 |
| 4 | THIRD_CONTACT | 3차 연락 발송 | 📤📤📤 |
| 응답 단계 |
| 5 | RESPONDED | 응답 받음 | 💬 |
| 6 | CONFIRMED | 배송 확정 | ✅ |
| 7 | DECLINED | 수령 거부 | ❌ |
| 8 | POSTPONED | 연기 요청 | ⏰ |
| 9 | UNREACHABLE | 연락 불가 | 📵 |
| 배송 단계 |
| 10 | DELIVERED | 배송 완료 | 🚚 |
| 11 | RECEIVED | 수령 확인 | ✅ |
| 12 | FAILED_DELIVERY | 배송 실패 | ❌ |
개인정보 암호화 필드
🔐 암호화 대상 필드
- gift_orders.sender_phone: 발신자 전화번호
- gift_recipients.name: 수신자 이름
- gift_recipients.phone: 수신자 전화번호
- gift_recipients.address: 수신자 주소
- gift_recipients.delivery_address: 실제 배송지
암호화 방식: Fernet (AES-256) + Base64 인코딩
데이터 볼륨 예상
# 일반적인 주문 (50명 수신자)
GiftOrder: 1건
GiftRecipient: 50건
RecipientMessage: 250건 (수신자당 평균 5회 소통)
RecipientChangeLog: 150건 (수신자당 평균 3회 변경)
# 연간 예상 (100개 주문)
총 데이터: 약 45,100건
저장 용량: 약 500MB (암호화 포함)
성능 최적화
✅ 인덱스 전략
- 주문 조회: store_id, user_id, status 복합 인덱스
- 수신자 조회: order_id, status 복합 인덱스
- 이력 조회: recipient_id, created_at 복합 인덱스
- 통계 캐시: gift_orders 테이블에 집계 필드 저장
🏗️ 기술 스택
선택 기준: 빠른 개발 + 안정성 + 확장성
최종 기술 구성
| 영역 |
기술 |
버전 |
선택 이유 |
| 백엔드 |
FastAPI + SQLAlchemy |
0.104.1 |
빠른 API 개발, 자동 문서화, 타입 힌트 |
| 데이터베이스 |
PostgreSQL |
15 |
ACID 보장, JSON 지원, 암호화 호환 |
| 캐시/세션 |
Redis |
7 |
JWT 세션 관리, TTL 지원, 고성능 |
| 프론트엔드 |
Vue.js 3 + Bootstrap 5 |
3.x |
반응형 UI, 컴포넌트 재사용 |
| 컨테이너 |
Docker + Docker Compose |
latest |
일관된 개발 환경, 쉬운 배포 |
| 메시징 |
카카오톡 비즈니스 API |
v2 |
높은 사용자 선호도, 합리적 비용 |
개발 환경 구성
# docker-compose.dev.yml 핵심 구성
services:
postgres:
image: postgres:15
ports: ["5433:5432"] # 충돌 방지
environment:
POSTGRES_DB: giftcall_dev
POSTGRES_USER: giftcall_dev
POSTGRES_PASSWORD: dev_password_123
redis:
image: redis:7-alpine
ports: ["6380:6379"] # 충돌 방지
backend:
build: ./backend
ports: ["8001:8000"]
volumes:
- ./backend/app:/app/app
- ./config:/config # 암호화 키 마운트
frontend:
build: ./frontend
ports: ["3001:3000"]
프로젝트 구조
giftcall/
├── backend/app/
│ ├── models/
│ │ ├── enums.py # RecipientStatus 등 Enum 정의
│ │ ├── gift_models.py # 새로운 1:N 구조 모델
│ │ └── models.py # 기존 모델 (Store, User)
│ ├── routers/
│ │ ├── auth.py # 공통 인증 모듈
│ │ ├── orders.py # 기존 주문 API (호환성)
│ │ └── gift_orders.py # 새로운 GiftCall API
│ ├── services/
│ │ └── gift_service.py # 비즈니스 로직
│ └── utils/
│ ├── common_auth.py # 공통 인증 헬퍼
│ └── encryption.py # 개인정보 암호화
├── frontend/src/
│ ├── views/ # 페이지 컴포넌트
│ ├── components/ # 재사용 컴포넌트
│ └── stores/ # Pinia 상태 관리
├── scripts/
│ ├── manage-db.sh # DB 관리 도구
│ └── migrate_to_gift_system.py # 마이그레이션
└── docs/
└── 05-giftcall-database-schema.md # DB 문서
🔐 보안 구현
핵심 원칙: 개인정보 보호 최우선 + 다층 보안
개인정보 암호화 시스템
🚨 Fernet (AES-256) 암호화
# app/utils/encryption.py
from cryptography.fernet import Fernet
import base64
class EncryptionManager:
def __init__(self):
self.key = self._load_or_generate_key()
self.cipher = Fernet(self.key)
def encrypt_name(self, name: str) -> str:
"""이름 암호화"""
encrypted = self.cipher.encrypt(name.encode())
return base64.b64encode(encrypted).decode()
def decrypt_name(self, encrypted_name: str) -> str:
"""이름 복호화"""
encrypted_data = base64.b64decode(encrypted_name.encode())
decrypted = self.cipher.decrypt(encrypted_data)
return decrypted.decode()
# 전역 인스턴스
encryption_manager = EncryptionManager()
JWT + Redis 하이브리드 인증
📋 인증 시스템 구조
- JWT 토큰: 사용자 정보 + 권한 + 만료시간
- Redis 세션: 24시간 TTL, 즉시 무효화 가능
- 공통 모듈: common_auth.py로 일관된 인증 처리
- 권한 제어: SUPER_ADMIN, STORE_ADMIN, STORE_USER
공통 인증 모듈
# app/utils/common_auth.py
class CommonAuth:
@staticmethod
def login(db: Session, username: str, password: str) -> Dict[str, Any]:
"""통합 로그인 처리"""
user = authenticate_user(db, username, password)
if not user:
raise HTTPException(status_code=401, detail="인증 실패")
# JWT 토큰 생성
access_token = create_access_token(
data={"sub": user.username},
expires_delta=timedelta(minutes=30)
)
return {
"access_token": access_token,
"token_type": "bearer",
"user": {
"id": user.id,
"username": user.username,
"role": user.role.value,
"store_id": user.store_id
}
}
# 전역 인스턴스
common_auth = CommonAuth()
권한 기반 접근 제어
| 권한 |
접근 범위 |
주요 기능 |
| SUPER_ADMIN |
전체 점포 |
모든 주문 조회/수정, 시스템 관리 |
| STORE_ADMIN |
해당 점포만 |
점포 내 모든 주문 관리 |
| STORE_USER |
자신의 주문만 |
본인이 생성한 주문만 관리 |
환경별 보안 설정
🔧 개발 환경
로컬 Fernet 키, 기본 보안 수준
☁️ 운영 환경
AWS KMS CMK, 엔터프라이즈 보안
⚙️ 개발 프로세스
방법론: AI 협업 + 점진적 개발 + 철저한 문서화
DB 마이그레이션 과정
✅ 마이그레이션 완료 (2025.11.19)
# 마이그레이션 실행
./scripts/manage-db.sh migrate
# 실행 내용
1. 기존 테이블 정리 (orders, recipients, sms_logs)
2. 새로운 테이블 생성 (gift_orders, gift_recipients, ...)
3. 기본 데이터 초기화 (점포, 사용자)
4. 결과 검증
공통 인증 모듈 개발
📋 일관성 보장 전략
- test_auth_helper.py: 공통 인증 테스트 도구
- common_auth.py: 통합 로그인 처리
- 표준화된 토큰: 모든 API에서 동일한 인증 방식
- 테스트 계정: eunbong/admin123!, admin_e001/store123!
테스트 전략
| 테스트 유형 |
도구/방법 |
상태 |
| 인증 테스트 |
test_auth_helper.py |
✅ 완료 |
| DB 마이그레이션 |
migrate_to_gift_system.py |
✅ 완료 |
| API 테스트 |
FastAPI TestClient |
🔄 진행중 |
| 현장 테스트 |
2025년 설날 베타 |
📅 예정 |
개발 도구 및 스크립트
# 주요 관리 도구
./scripts/manage-db.sh # DB 관리 (check, init, migrate)
python test_auth_helper.py # 인증 테스트
docker-compose -f docker-compose.dev.yml up # 개발 환경
# 사용 예시
./scripts/manage-db.sh check # DB 상태 확인
./scripts/manage-db.sh migrate # 새 시스템으로 마이그레이션
python test_auth_helper.py # 인증 시스템 테스트
문서화 전략
📋 기술 문서
docs/05-giftcall-database-schema.md - 완전한 DB 스키마
🌐 웹 문서
edu 프로젝트 연동 - 접근성 높은 HTML 문서
🔄 변경 이력
Git 커밋 + README.md 진행률 추적
🧪 실행 기록
실제 명령어와 결과를 포함한 상세 기록