데이터베이스에서 대용량 문자열과 이진 데이터를 저장할 때 사용하는 TEXT와 BLOB 타입에 대해 알아보겠습니다. 이들은 일반적인 VARCHAR, CHAR와는 다른 저장 방식과 성능 특성을 가지고 있어 적절한 사용법을 이해하는 것이 중요합니다.
TEXT 타입 - 대용량 문자열 저장
TEXT는 CHAR, VARCHAR와 다르게 행 내부가 아닌 디스크에 저장됩니다.
일반적인 저장 방식 (VARCHAR):
┌──────────────────────────────────────┐
│ Row Data │
├─────┬─────┬─────────────────────────┤
│ ID │Name │ Description(VARCHAR) │
│ 1 │김철수│ 짧은 설명 텍스트 │
└─────┴─────┴─────────────────────────┘
모든 데이터가 행 내부에 저장 (메모리)
TEXT 저장 방식:
┌──────────────────────────┐ ┌─────────────────────┐
│ Row Data │ │ Disk Storage │
├─────┬─────┬─────────────┤ ├─────────────────────┤
│ ID │Name │ TEXT Pointer │───→│ 매우 긴 텍스트 내용 │
│ 1 │김철수 │ 8byte 주소 │ │ 저장... │
└─────┴─────┴─────────────┘ └─────────────────────┘
행에는 8바이트 포인터만, 실제 데이터는 디스크에
TEXT 타입 종류 (MySQL 기준)
| 타입 | 최대 길이 | 저장 공간 | 용도 |
| TINYTEXT | 255자 | L + 1바이트 | 짧은 메모 |
| TEXT | 65,535자 | L + 2바이트 | 일반적인 긴 텍스트 |
| MEDIUMTEXT | 16,777,215자 | L + 3바이트 | 중간 크기 문서 |
| LONGTEXT | 4,294,967,295자 | L + 4바이트 | 대용량 문서 |
L = 실제 데이터 길이
VARCHAR vs TEXT 성능 비교
성능 테스트 시나리오: 1만 개 레코드에서 검색
VARCHAR(1000) - 평균 500자 저장:
┌─────────────────┬──────────────┐
│ 저장 위치 │ 메모리 (행내부) │
│ 읽기 속도 │ 0.1초 │
│ 검색 속도 │ 0.05초 │
│ 메모리 사용량 │ 높음 │
│ 인덱스 효율성 │ 높음 │
└─────────────────┴──────────────┘
TEXT - 평균 500자 저장:
┌─────────────────┬──────────────┐
│ 저장 위치 │ 디스크 │
│ 읽기 속도 │ 0.3초 │
│ 검색 속도 │ 0.15초 │
│ 메모리 사용량 │ 낮음 │
│ 인덱스 효율성 │ 낮음 (제한적) │
└─────────────────┴──────────────┘
TEXT 사용 권장 상황
1. 매우 큰 문자열 데이터
-- 블로그 포스트 내용
post_content LONGTEXT
-- 상품 상세 설명
product_description TEXT
-- 로그 메시지
error_log MEDIUMTEXT
-- 사용자 약관
terms_of_service LONGTEXT
2. 검색과 수정이 빈번하지 않은 데이터
-- 게시글 내용 (작성 후 수정 빈도 낮음)
article_body TEXT
-- 이메일 내용 (보관용)
email_content TEXT
-- 백업 데이터
backup_data LONGTEXT
TEXT 사용 비권장 상황
1. 자주 검색되는 데이터
-- 자주 검색되는 사용자명
username TEXT
-- VARCHAR 사용 권장
username VARCHAR(50)
2. WHERE 절에 자주 사용되는 컬럼
-- 상태값을 TEXT로
status TEXT
-- 효율적인 검색을 위해
status VARCHAR(20)
BLOB 타입 - 이진 데이터 저장
BLOB(Binary Large Object)은 이진 데이터를 저장하는 데이터 타입입니다.
BLOB 타입 종류
| 타입 | 최대 크기 | 용도 예시 |
| TINYBLOB | 255바이트 | 작은 아이콘 |
| BLOB | 65KB | 썸네일 이미지 |
| MEDIUMBLOB | 16MB | 일반 이미지, 문서 |
| LONGBLOB | 4GB | 비디오, 대용량 파일 |
BLOB 사용 예시
-- 이미지 저장 테이블
CREATE TABLE user_profiles (
user_id INT PRIMARY KEY,
username VARCHAR(50),
profile_image MEDIUMBLOB, -- 프로필 사진
signature BLOB -- 서명 이미지
);
-- 문서 저장 테이블
CREATE TABLE documents (
doc_id INT PRIMARY KEY,
title VARCHAR(200),
file_data LONGBLOB, -- PDF, Word 파일
file_type VARCHAR(50) -- MIME 타입
);
BLOB의 문제점
1. 성능 저하
시나리오: 사용자 목록 조회
BLOB 포함 쿼리:
SELECT user_id, username, profile_image FROM users;
문제점:
- 각 행마다 MB 단위 이미지 데이터 전송
- 네트워크 대역폭 과도한 사용
- 메모리 사용량 급증
- 쿼리 응답 시간 지연
2. 백업 및 복제 문제
데이터베이스 크기 비교:
BLOB 사용:
- 테이블 크기: 10GB (이미지 포함)
- 백업 시간: 2시간
- 복제 지연: 심각
URL 방식:
- 테이블 크기: 100MB (URL만 저장)
- 백업 시간: 5분
- 복제 지연: 최소
3. 확장성 문제
- 데이터베이스 서버 저장 공간 부족
- 캐싱 효율성 저하
- 수평 확장의 어려움
현대적 해결책: 이미지 호스팅 서비스
권장 아키텍처
기존 방식 (BLOB 사용):
[클라이언트] ──→ [웹서버] ──→ [데이터베이스(BLOB)]
↑
이미지도 함께 저장
현대 방식 (호스팅 서비스):
[클라이언트] ──→ [웹서버] ──→ [데이터베이스(URL)]
│ ↑
└──────→ [S3/CDN] ─────────────┘
(실제 파일 저장)
호스팅 서비스 사용 시 데이터베이스 설계
-- 사용자 프로필 테이블
CREATE TABLE user_profiles (
user_id INT PRIMARY KEY,
username VARCHAR(50),
profile_image_url VARCHAR(500), -- S3 URL 저장
cover_image_url VARCHAR(500), -- 커버 이미지 URL
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 상품 이미지 테이블
CREATE TABLE product_images (
image_id INT PRIMARY KEY AUTO_INCREMENT,
product_id INT,
image_url VARCHAR(500), -- 원본 이미지 URL
thumbnail_url VARCHAR(500), -- 썸네일 URL
alt_text VARCHAR(200), -- 접근성을 위한 대체 텍스트
sort_order INT DEFAULT 0
);
성능 최적화 전략
TEXT 최적화
1. 적절한 타입 선택
-- 과도한 크기 X
short_description LONGTEXT -- 실제로는 200자 내외
-- 적절한 크기 O
short_description VARCHAR(500)
long_description TEXT
2. 분할 테이블 설계
-- 기본 정보 테이블 (자주 조회)
CREATE TABLE articles (
article_id INT PRIMARY KEY,
title VARCHAR(200),
author VARCHAR(100),
created_at TIMESTAMP,
view_count INT
);
-- 내용 테이블 (필요시에만 조회)
CREATE TABLE article_contents (
article_id INT PRIMARY KEY,
content LONGTEXT,
FOREIGN KEY (article_id) REFERENCES articles(article_id)
);
BLOB 대체 전략
1. 하이브리드 접근
-- 작은 이미지는 BLOB, 큰 이미지는 URL
CREATE TABLE user_avatars (
user_id INT PRIMARY KEY,
small_avatar BLOB, -- 64x64 썸네일 (< 10KB)
large_avatar_url VARCHAR(500) -- 고해상도는 S3 URL
);
2. 지연 로딩
// 목록 조회 시에는 URL만
const users = await db.query(
'SELECT user_id, username, avatar_url FROM users'
);
// 상세 조회 시에만 이미지 로드
const userDetail = await db.query(
'SELECT * FROM users WHERE user_id = ?', [userId]
);
'ZeroBase > CS' 카테고리의 다른 글
| Map과 Set (0) | 2025.09.13 |
|---|---|
| Apache Kafka - 분산 스트리밍 플랫폼 (0) | 2025.09.11 |
| CHAR vs VARCHAR - 문자열 타입 (0) | 2025.09.11 |
| 데이터베이스 기초 - 엔터티, 릴레이션, 속성 (0) | 2025.09.11 |
| Convoy Effect, Starvation, Busy Wait (0) | 2025.09.08 |