데이터베이스 설계 기초 - 주의사항과 아키텍처
데이터베이스 설계 주의사항
데이터베이스를 설계할 때 확신이 없다면 다음 사항들을 반드시 체크해야 합니다.
체크리스트
1. Index (인덱스)
- 자주 검색되는 컬럼에 인덱스를 설정했는지 확인
- 복합 인덱스의 순서가 쿼리 패턴에 맞는지 확인
- 불필요한 인덱스가 성능을 저하시키지 않는지 검토
2. 텍스트 사이즈
- VARCHAR, TEXT 등의 문자열 컬럼 크기가 적절한지 확인
- 저장될 데이터의 최대 크기를 고려하여 설정
- 너무 크게 설정하면 메모리 낭비, 너무 작으면 데이터 손실 발생
3. Nullable 여부
- 해당 컬럼이 NULL 값을 허용해야 하는지 명확히 정의
- NOT NULL 제약조건이 필요한 경우 설정
- NULL 허용 시 애플리케이션에서의 NULL 처리 로직 고려
추가로 고려해야 할 사항
- Primary Key: 고유 식별자 설계
- Foreign Key: 테이블 간 관계 정의
- 기본값(Default): 필요한 경우 기본값 설정
- 유니크 제약조건: 중복 방지가 필요한 컬럼 식별
데이터베이스 아키텍처 참고 자료
다양한 도메인에 대한 데이터베이스 아키텍처 샘플을 참고하면 설계에 도움이 됩니다.
추천 리소스
- Database Answers - Data Models - 수백 개의 산업별 데이터 모델 샘플 제공
이 사이트에서는 다음과 같은 다양한 도메인의 데이터 모델을 확인할 수 있습니다:
- E-Commerce
- Healthcare
- Finance
- Education
- Manufacturing
- 그 외 다수
실제 프로젝트에서 데이터베이스를 설계할 때, 유사한 도메인의 모델을 참고하여 시작하면 효율적입니다.
정규화 (Normalization)
데이터베이스 정규화는 데이터 중복을 최소화하고 무결성을 유지하기 위한 설계 기법입니다.
1정규형 (1NF)
- 각 컬럼은 원자적 값(Atomic Value)만 포함
- 반복 그룹 제거
잘못된 예시: phone 컬럼에 “010-1234-5678, 010-9876-5432” 저장
올바른 설계: 별도의 phone_numbers 테이블을 생성하여 각 전화번호를 독립된 행으로 저장
2정규형 (2NF)
- 1NF를 만족하면서
- 부분적 함수 종속 제거 (복합 키의 일부에만 종속되는 컬럼 분리)
3정규형 (3NF)
- 2NF를 만족하면서
- 이행적 함수 종속 제거 (A -> B -> C에서 A -> C 관계 제거)
반정규화 (Denormalization)
성능 최적화를 위해 의도적으로 정규화를 위반하는 경우도 있습니다. 자주 조인되는 테이블의 컬럼을 중복 저장하여 조회 성능을 향상시킵니다.
| 상황 | 정규화 | 반정규화 |
|---|---|---|
| 데이터 무결성 중시 | 적합 | 부적합 |
| 읽기 성능 중시 | 조인 비용 발생 | 적합 |
| 쓰기 성능 중시 | 적합 | 데이터 동기화 비용 |
| 데이터 변경 빈도 높음 | 적합 | 부적합 |
데이터 타입 선택 가이드
적절한 데이터 타입을 선택하면 저장 공간을 절약하고 쿼리 성능을 높일 수 있습니다.
숫자 타입
| 타입 | 범위 | 바이트 | 용도 |
|---|---|---|---|
| TINYINT | -128 ~ 127 | 1 | 상태값, 플래그 |
| SMALLINT | -32,768 ~ 32,767 | 2 | 작은 범위의 코드값 |
| INT | -21억 ~ 21억 | 4 | 일반적인 ID, 수량 |
| BIGINT | 매우 큰 범위 | 8 | 큰 범위의 ID, 금액 |
| DECIMAL(M,D) | 정밀 소수 | 가변 | 금액 (부동소수점 오류 방지) |
금액 관련 데이터는 반드시
DECIMAL타입을 사용해야 합니다.FLOAT이나DOUBLE은 부동소수점 오류가 발생할 수 있습니다.
문자열 타입
| 타입 | 최대 크기 | 특징 |
|---|---|---|
| CHAR(n) | 255바이트 | 고정 길이, 패딩 |
| VARCHAR(n) | 65,535바이트 | 가변 길이 |
| TEXT | 65,535바이트 | 긴 텍스트 |
| MEDIUMTEXT | 16MB | 중간 크기 텍스트 |
| LONGTEXT | 4GB | 매우 큰 텍스트 |
길이가 항상 일정한 데이터(예: 국가 코드 ‘KR’, ‘US’)는 CHAR이 더 효율적이고, 가변 길이 데이터는 VARCHAR을 사용합니다.
인덱스 설계 원칙
인덱스는 데이터베이스 성능에 가장 큰 영향을 미치는 요소 중 하나입니다.
인덱스를 생성해야 하는 경우
- WHERE 절에서 자주 사용되는 컬럼
- JOIN 조건에 사용되는 컬럼
- ORDER BY, GROUP BY에 사용되는 컬럼
- 높은 선택도(Cardinality)를 가진 컬럼
인덱스를 생성하지 말아야 하는 경우
- 데이터가 적은 테이블 (풀 스캔이 더 빠름)
- INSERT, UPDATE, DELETE가 빈번한 컬럼
- 선택도가 낮은 컬럼 (예: 성별, boolean 플래그)
복합 인덱스 순서
복합 인덱스의 컬럼 순서는 매우 중요합니다. 선택도가 높은 컬럼을 앞에 배치하는 것이 일반적입니다.
-- user_id의 선택도가 높으므로 앞에 배치
CREATE INDEX idx_orders ON orders (user_id, status, created_at);
이 인덱스는 WHERE user_id = ?, WHERE user_id = ? AND status = ?, WHERE user_id = ? AND status = ? AND created_at > ? 쿼리 모두에서 활용됩니다. 하지만 WHERE status = ?만 있는 쿼리에서는 이 인덱스를 사용할 수 없습니다.
트랜잭션 격리 수준
동시에 여러 트랜잭션이 실행될 때 데이터 일관성을 어느 수준까지 보장할지 결정하는 설정입니다.
| 격리 수준 | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
| READ UNCOMMITTED | 가능 | 가능 | 가능 |
| READ COMMITTED | 불가 | 가능 | 가능 |
| REPEATABLE READ | 불가 | 불가 | 가능 |
| SERIALIZABLE | 불가 | 불가 | 불가 |
MySQL InnoDB의 기본 격리 수준은 REPEATABLE READ이며, 대부분의 애플리케이션에서 적합합니다. 격리 수준이 높을수록 데이터 일관성은 보장되지만 동시성 처리 성능이 저하되므로, 서비스 요구사항에 맞게 적절한 수준을 선택해야 합니다.
Comments