# User Service 데이터베이스 설계서 ## 데이터 설계 요약 ### 📋 설계 개요 - **서비스명**: user-service - **데이터베이스**: PostgreSQL 16 - **캐시 DB**: Redis 7 - **테이블 수**: 2개 (users, stores) - **인덱스 수**: 5개 - **설계 원칙**: 마이크로서비스 데이터 독립성 원칙 준수 ### 🎯 핵심 특징 - **독립 데이터베이스**: user-service만의 독립적인 스키마 - **1:1 Entity 매핑**: 클래스 설계서의 User, Store Entity와 정확히 일치 - **JWT 기반 인증**: Redis를 활용한 세션 및 Blacklist 관리 - **성능 최적화**: 조회 패턴 기반 인덱스 설계 - **보안**: 비밀번호 bcrypt 암호화, 민감 정보 암호화 저장 --- ## 1. 테이블 설계 ### 1.1 users 테이블 **목적**: 사용자(소상공인) 정보 관리 | 컬럼명 | 타입 | 제약조건 | 설명 | |--------|------|---------|------| | id | UUID | PK | 사용자 고유 식별자 | | name | VARCHAR(100) | NOT NULL | 사용자 이름 | | phone_number | VARCHAR(20) | NOT NULL, UNIQUE | 전화번호 (중복 검증) | | email | VARCHAR(255) | NOT NULL, UNIQUE | 이메일 (로그인 ID) | | password_hash | VARCHAR(255) | NOT NULL | bcrypt 암호화된 비밀번호 | | role | VARCHAR(20) | NOT NULL | 사용자 역할 (OWNER, ADMIN) | | status | VARCHAR(20) | NOT NULL, DEFAULT 'ACTIVE' | 계정 상태 | | last_login_at | TIMESTAMP | NULL | 최종 로그인 시각 | | created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 생성 시각 | | updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 수정 시각 | **제약조건**: - PRIMARY KEY: id - UNIQUE: email, phone_number - CHECK: role IN ('OWNER', 'ADMIN') - CHECK: status IN ('ACTIVE', 'INACTIVE', 'LOCKED', 'WITHDRAWN') **인덱스**: - `idx_users_email`: 로그인 조회 최적화 - `idx_users_phone_number`: 전화번호 중복 검증 최적화 - `idx_users_status`: 활성 사용자 필터링 최적화 --- ### 1.2 stores 테이블 **목적**: 가게(매장) 정보 관리 | 컬럼명 | 타입 | 제약조건 | 설명 | |--------|------|---------|------| | id | UUID | PK | 가게 고유 식별자 | | user_id | UUID | NOT NULL, UNIQUE, FK | 사용자 ID (1:1 관계) | | name | VARCHAR(200) | NOT NULL | 가게 이름 | | industry | VARCHAR(100) | NOT NULL | 업종 | | address | VARCHAR(500) | NOT NULL | 주소 | | business_hours | TEXT | NULL | 영업시간 | | created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 생성 시각 | | updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 수정 시각 | **제약조건**: - PRIMARY KEY: id - FOREIGN KEY: user_id REFERENCES users(id) ON DELETE CASCADE - UNIQUE: user_id (1:1 관계 보장) **인덱스**: - `idx_stores_user_id`: User-Store 조인 최적화 --- ## 2. 관계 설계 ### 2.1 User ↔ Store (1:1 양방향) ``` users(1) ---- (1)stores └─ user_id FK ``` **관계 특성**: - **Type**: One-to-One Bidirectional - **Owner**: Store (FK를 소유) - **Cascade**: ALL (User 삭제 시 Store도 삭제) - **Lazy Loading**: User 조회 시 Store는 지연 로딩 **비즈니스 규칙**: - 하나의 User는 최대 하나의 Store만 소유 - Store는 반드시 User에 속해야 함 (NOT NULL FK) - User 삭제 시 Store도 함께 삭제 (CASCADE) --- ## 3. 인덱스 설계 ### 3.1 인덱스 목록 | 인덱스명 | 테이블 | 컬럼 | 목적 | 유형 | |---------|--------|------|------|------| | idx_users_email | users | email | 로그인 조회 | UNIQUE | | idx_users_phone_number | users | phone_number | 중복 검증 | UNIQUE | | idx_users_status | users | status | 활성 사용자 필터링 | B-tree | | idx_stores_user_id | stores | user_id | User-Store 조인 | UNIQUE | ### 3.2 조회 패턴 분석 **빈번한 조회 패턴**: 1. **로그인**: `SELECT * FROM users WHERE email = ?` → idx_users_email 2. **중복 검증**: `SELECT COUNT(*) FROM users WHERE phone_number = ?` → idx_users_phone_number 3. **프로필 조회**: `SELECT u.*, s.* FROM users u LEFT JOIN stores s ON u.id = s.user_id WHERE u.id = ?` 4. **활성 사용자**: `SELECT * FROM users WHERE status = 'ACTIVE'` → idx_users_status --- ## 4. Redis 캐시 설계 ### 4.1 JWT 세션 관리 **키 패턴**: `session:{token}` **데이터 구조**: ```json { "userId": "UUID", "role": "OWNER|ADMIN", "email": "user@example.com", "expiresAt": "timestamp" } ``` **TTL**: JWT 만료 시간과 동일 (예: 7일) **목적**: - JWT 토큰 검증 시 DB 조회 방지 - 빠른 인증 처리 - 로그아웃 시 세션 삭제 --- ### 4.2 JWT Blacklist **키 패턴**: `blacklist:{token}` **데이터 구조**: ```json { "userId": "UUID", "logoutAt": "timestamp" } ``` **TTL**: 토큰 원래 만료 시간까지 **목적**: - 로그아웃된 토큰 재사용 방지 - 유효한 토큰이지만 무효화된 토큰 관리 - 보안 강화 --- ## 5. 데이터 무결성 및 보안 ### 5.1 제약조건 **NOT NULL 제약**: - 필수 필드: name, email, password_hash, role, status - Store 필수 필드: user_id, name, industry, address **UNIQUE 제약**: - email: 로그인 ID 중복 방지 - phone_number: 전화번호 중복 방지 - stores.user_id: 1:1 관계 보장 **CHECK 제약**: - role: OWNER, ADMIN만 허용 - status: ACTIVE, INACTIVE, LOCKED, WITHDRAWN만 허용 **FOREIGN KEY 제약**: - stores.user_id → users.id (ON DELETE CASCADE) ### 5.2 보안 **비밀번호 보안**: - bcrypt 알고리즘 사용 (cost factor 12) - password_hash 컬럼에 저장 - 원본 비밀번호는 저장하지 않음 **민감 정보 보호**: - 전화번호, 이메일: 암호화 고려 (필요시) - 주소: 개인정보이므로 접근 제어 --- ## 6. 성능 최적화 전략 ### 6.1 인덱스 전략 **단일 컬럼 인덱스**: - email, phone_number: UNIQUE 인덱스로 조회 및 중복 검증 - status: 활성 사용자 필터링 **복합 인덱스 검토**: - 현재는 불필요 (단순 조회 패턴) - 추후 복잡한 검색 조건 추가 시 고려 ### 6.2 캐시 전략 **Redis 활용**: - JWT 세션: DB 조회 없이 인증 처리 - Blacklist: 로그아웃 토큰 빠른 검증 **캐시 갱신**: - 프로필 수정 시 세션 캐시 갱신 - 비밀번호 변경 시 모든 세션 무효화 ### 6.3 쿼리 최적화 **N+1 문제 방지**: - User 조회 시 Store LEFT JOIN으로 한 번에 조회 - JPA: `@OneToOne(fetch = FetchType.LAZY)` + 필요시 fetch join **배치 처리**: - 대량 사용자 조회 시 IN 절 활용 - 페이징 처리: LIMIT/OFFSET 또는 커서 기반 --- ## 7. 확장성 고려사항 ### 7.1 수직 확장 (Scale-Up) **현재 설계로 충분**: - 예상 사용자: 10만 명 이하 - 단순한 스키마 구조 - 효율적인 인덱스 ### 7.2 수평 확장 (Scale-Out) **샤딩 전략 (필요 시)**: - 샤딩 키: user_id (UUID 기반) - 읽기 복제본: 조회 성능 향상 - Redis Cluster: 세션 분산 저장 ### 7.3 데이터 증가 대응 **파티셔닝**: - 현재는 불필요 - 수백만 사용자 이상 시 status별 파티셔닝 고려 **아카이빙**: - WITHDRAWN 사용자 데이터 아카이빙 - 1년 이상 비활성 사용자 별도 테이블 이관 --- ## 8. 백업 및 복구 전략 ### 8.1 백업 **PostgreSQL**: - 일일 전체 백업 (pg_dump) - WAL 아카이빙 (Point-in-Time Recovery) - 보관 기간: 30일 **Redis**: - RDB 스냅샷: 1시간마다 - AOF 로그: appendfsync everysec - 보관 기간: 7일 ### 8.2 복구 **재해 복구 목표**: - RPO (Recovery Point Objective): 1시간 - RTO (Recovery Time Objective): 30분 **복구 절차**: 1. PostgreSQL: WAL 기반 특정 시점 복구 2. Redis: RDB + AOF 조합 복구 3. 세션 재생성: 사용자 재로그인 --- ## 9. 모니터링 및 알림 ### 9.1 모니터링 항목 **데이터베이스**: - Connection Pool 사용률 - Slow Query (1초 이상) - 인덱스 사용률 - 테이블 크기 증가율 **캐시**: - Redis 메모리 사용률 - 캐시 히트율 - Eviction 발생 빈도 ### 9.2 알림 임계값 **Critical**: - Connection Pool 사용률 > 90% - Slow Query > 10건/분 - Redis 메모리 사용률 > 90% **Warning**: - Connection Pool 사용률 > 70% - Slow Query > 5건/분 - 캐시 히트율 < 80% --- ## 10. 마이그레이션 및 버전 관리 ### 10.1 스키마 버전 관리 **도구**: Flyway 또는 Liquibase **마이그레이션 파일**: - `V1__create_users_table.sql` - `V2__create_stores_table.sql` - `V3__add_indexes.sql` ### 10.2 무중단 마이그레이션 **컬럼 추가**: 1. 새 컬럼 추가 (NULL 허용) 2. 애플리케이션 배포 3. 데이터 마이그레이션 4. NOT NULL 제약 추가 **컬럼 삭제**: 1. 애플리케이션에서 사용 중단 2. 배포 및 검증 3. 컬럼 삭제 --- ## 11. 참고 자료 - **클래스 설계서**: design/backend/class/user-service.puml - **공통 컴포넌트**: design/backend/class/common-base.puml - **ERD**: design/backend/database/user-service-erd.puml - **스키마**: design/backend/database/user-service-schema.psql