데이터베이스
제가 데이터베이스를 다룰 때 따르는 원칙과 쿼리 작성 규칙입니다.
핵심 원칙
1. Audit Trail (필수 5가지 필드)
모든 테이블은 다음 5가지 필드를 필수로 포함해야 합니다:
| |
2. Soft Delete (물리적 삭제 금지)
절대 금지: DELETE 쿼리 사용 금지
| |
3. JPA 엔티티 설계
Audit Trail과 Soft Delete를 포함한 Base Entity
| |
| |
Spring Data JPA Repository
기본 조회
| |
INSERT
| |
UPDATE
| |
Soft DELETE
| |
QueryDSL 사용
설정
| |
기본 조회
| |
JOIN 쿼리
| |
페이징
| |
집계 함수
| |
서브쿼리
| |
EXISTS 쿼리
| |
동적 쿼리
| |
프로젝션 (DTO 직접 조회)
| |
트랜잭션
| |
벌크 연산
| |
인덱스 전략
권장 인덱스
| |
N+1 문제 해결
| |
동시성 제어 (Concurrency Control)
Optimistic Locking (낙관적 잠금)
충돌이 거의 발생하지 않는 환경에서 사용합니다. 조회가 많고 수정이 적은 경우에 적합합니다.
| |
사용 시나리오:
- 조회가 많고 수정이 적은 경우
- 충돌 확률이 낮은 경우
- 데이터베이스 잠금 오버헤드를 줄이고 싶은 경우
Pessimistic Locking (비관적 잠금)
충돌이 빈번하게 발생하는 환경에서 사용합니다. 재고 관리, 예약 시스템 등에 적합합니다.
| |
사용 시나리오:
- 재고 관리 (재고 차감)
- 좌석 예약 시스템
- 금융 거래
- 충돌이 빈번하게 발생하는 경우
데드락 방지 전략:
| |
성능 최적화 (Performance Optimization)
배치 처리 (Batch Processing)
대용량 데이터를 처리할 때 메모리 효율적으로 처리하는 방법입니다.
| |
application.yml 배치 설정:
| |
쿼리 최적화
| |
Native Query 사용 시 주의사항:
| |
캐싱 전략
| |
트랜잭션 관리 강화
격리 수준 (Isolation Level)
| |
격리 수준별 특징:
| 격리 수준 | Dirty Read | Non-Repeatable Read | Phantom Read | 성능 | 사용 시나리오 |
|---|---|---|---|---|---|
| READ_UNCOMMITTED | 발생 | 발생 | 발생 | 최고 | 거의 사용 안 함 |
| READ_COMMITTED | 방지 | 발생 | 발생 | 높음 | 대부분의 경우 (기본값) |
| REPEATABLE_READ | 방지 | 방지 | 발생 | 중간 | 트랜잭션 내 일관성 필요 |
| SERIALIZABLE | 방지 | 방지 | 방지 | 낮음 | 금융 거래 등 중요한 경우 |
전파 옵션 (Propagation)
| |
전파 옵션 실무 가이드:
- REQUIRED (기본값): 대부분의 경우 사용
- REQUIRES_NEW: 감사 로그, 이벤트 기록 등 독립적으로 저장해야 하는 경우
- SUPPORTS: 트랜잭션이 필요 없는 조회 작업
- NOT_SUPPORTED: 트랜잭션 없이 실행 (성능 최적화)
- MANDATORY: 반드시 트랜잭션 내에서 호출되어야 함 (검증용)
- NEVER: 트랜잭션 내에서 호출되면 예외 발생 (검증용)
인덱스 전략 강화
복합 인덱스 순서
| |
인덱스 순서 결정 원칙:
- 카디널리티가 높은 컬럼을 앞에 (이메일, 고유 ID 등)
- WHERE 절에 자주 사용되는 컬럼을 앞에
- 등호(=) 비교 컬럼을 범위 검색 컬럼보다 앞에
- deleted_at은 항상 마지막에
인덱스 안티패턴
| |
인덱스 생성 가이드:
- 테이블당 인덱스는 5-7개 이내로 제한
- WHERE, JOIN, ORDER BY에 자주 사용되는 컬럼에만 생성
- 복합 인덱스로 여러 쿼리 패턴 커버
- 주기적으로 사용하지 않는 인덱스 삭제
- 인덱스 크기 모니터링 (테이블 크기의 20% 이내 권장)
보안 (Security)
Soft Delete 보안
| |
민감 데이터 암호화
| |
SQL Injection 방지
| |
체크리스트
쿼리 작성 시
- 모든 WHERE 절에
deletedAt IS NULL조건이 포함되어 있는가? - DELETE 쿼리 대신 Soft Delete를 사용하는가?
- INSERT/UPDATE 시 Audit Trail 필드를 업데이트하는가?
- 프로젝션(DTO)을 사용하여 필요한 필드만 조회하는가?
- SELECT * 대신 명시적인 컬럼을 지정하는가?
- SQL Injection 방지를 위해 파라미터 바인딩을 사용하는가?
테이블 설계 시
- Audit Trail 5가지 필드가 모두 포함되어 있는가?
- deleted_at 컬럼이 존재하는가?
- 적절한 인덱스가 생성되어 있는가?
- 복합 인덱스의 컬럼 순서가 올바른가? (카디널리티 높은 순)
- Foreign Key 제약조건이 설정되어 있는가?
- 민감 데이터에 암호화가 적용되어 있는가?
성능 최적화
- N+1 문제가 없는가? (Fetch Join 또는 Batch Size 설정)
- 프로젝션을 사용하여 불필요한 데이터를 조회하지 않는가?
- 페이징을 적용했는가?
- 인덱스를 활용하는 쿼리인가?
- 대용량 처리 시 배치 처리를 사용하는가?
- EntityManager flush/clear 패턴으로 메모리를 관리하는가?
- 조회 빈도가 높은 데이터에 캐싱을 적용했는가?
동시성 제어
- 동시 수정이 발생할 수 있는 데이터에 잠금을 적용했는가?
- 낙관적 잠금 vs 비관적 잠금 중 적절한 방식을 선택했는가?
- OptimisticLockException 발생 시 재시도 로직이 있는가?
- 비관적 잠금 사용 시 타임아웃을 설정했는가?
- 데드락 방지를 위해 잠금 순서를 정렬했는가?
트랜잭션 관리
- @Transactional 어노테이션이 적절히 적용되어 있는가?
- 격리 수준(Isolation Level)을 명시적으로 설정했는가?
- 전파 옵션(Propagation)이 비즈니스 요구사항에 맞는가?
- REQUIRED vs REQUIRES_NEW를 올바르게 사용하는가?
- 읽기 전용 트랜잭션에 readOnly = true를 설정했는가?
보안
- 삭제된 데이터가 조회되지 않도록 방어했는가?
- 민감 데이터가 암호화되어 저장되는가?
- SQL Injection 공격에 대한 방어가 되어 있는가?
- 사용자 권한을 검증하는가?
- 개인정보는 최소한으로만 수집하고 있는가?