> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MySQL 데이터 유형 선택하기

> 컬럼에 맞는 MySQL 데이터 유형을 선택하는 방법: 정수, 문자열, 날짜, JSON — 저장 비용과 성능 트레이드오프 포함.

> [card] [VillageSQL is a drop-in replacement for MySQL with extensions.](https://villagesql.com/docs/en/8.4/stable/install-server.md)
>
> All examples on this page work on VillageSQL. Install Now →

컬럼에 지정하는 데이터 유형은 저장 크기, 인덱스 효율성, 쿼리 동작에 영향을 줍니다. 필요 이상으로 큰 유형을 선택하면 모든 행과 그 컬럼을 포함하는 모든 인덱스에서 공간이 낭비됩니다. 너무 작은 유형을 선택하면 잘림 오류나 조용한 데이터 손실이 발생합니다.

## 정수 유형

MySQL에는 다섯 가지 정수 유형이 있습니다. 예상 범위에 맞는 가장 작은 것을 고르세요.

| 유형 | 저장 공간 | 범위(부호 있음) | 사용 시기 |
| :- | :- | :- | :- |
| `TINYINT` | 1바이트 | -128 \~ 127 | 플래그, 상태 코드, 작은 열거형 |
| `SMALLINT` | 2바이트 | -32,768 \~ 32,767 | 약 32K를 넘지 않는 개수 또는 ID |
| `MEDIUMINT` | 3바이트 | -8.3M \~ 8.3M | 중간 범위 ID 또는 카운터 |
| `INT` | 4바이트 | -2.1B \~ 2.1B | 대부분의 ID와 카운터 |
| `BIGINT` | 8바이트 | ±920경 | 큰 ID, 밀리초 단위 Unix 타임스탬프 |

`INT UNSIGNED`는 양수 범위를 약 4.3B로 두 배 늘립니다. 항상 양수인 기본 키와 외래 키에는 `UNSIGNED`를 사용하세요 — 공짜로 얻는 추가 범위입니다.

`BIGINT`는 `INT`의 두 배 저장 공간을 차지합니다. 모든 ID에 기본으로 사용하지 마세요 — `INT`는 20억 행 이상을 처리하며, 이는 대부분의 테이블이 도달하지 못하는 규모입니다.

## 문자열 유형

### VARCHAR 대 CHAR

`VARCHAR(n)`은 문자열이 담은 만큼의 바이트만 저장합니다(길이를 위한 1-2바이트 추가). `CHAR(n)`은 항상 정확히 `n`바이트를 저장하며, 더 짧은 문자열은 공백으로 채웁니다.

| 유형 | 저장 공간 | 사용 시기 |
| :- | :- | :- |
| `CHAR(n)` | 항상 n바이트 | 고정 길이 문자열(국가 코드, 'Y'/'N' 같은 상태 플래그, MD5 해시) |
| `VARCHAR(n)` | 실제 길이 + 1-2바이트 | 가변 길이 문자열(이름, 이메일, URL) |

`CHAR`는 길이 접두사를 파싱할 필요가 없으므로 고정 길이 값에서 약간 더 빠를 수 있습니다. 가변적인 것에는 모두 `VARCHAR`를 사용하세요.

### VARCHAR 대 TEXT

```sql theme={null}
-- VARCHAR stores inline in the row (up to row size limit)
name VARCHAR(255)

-- TEXT stores off-page once it exceeds a threshold; inline storage is limited
description TEXT
```

행 크기 제한까지의 `VARCHAR` 값은 클러스터형 인덱스 페이지에 인라인으로 저장됩니다. `TEXT`(와 `BLOB`) 값은 충분히 커지면 오버플로 페이지에 저장됩니다. 이는 다음을 의미합니다:

* `VARCHAR` 컬럼은 인덱스에 직접 포함됩니다
* `TEXT` 컬럼은 완전히 인덱싱할 수 없습니다 — 접두사 길이가 필요합니다: `INDEX (description(100))`

수백 자 미만임을 아는 문자열에는 `VARCHAR`를 사용하세요. 인라인 저장이 적합하지 않은 긴 콘텐츠(기사, 댓글, HTML)에는 `TEXT`를 사용하세요.

## 날짜와 시간 유형

| 유형 | 저장 공간 | 범위 | 사용 시기 |
| :- | :- | :- | :- |
| `DATE` | 3바이트 | 1000-01-01 \~ 9999-12-31 | 날짜만, 시간 없음 |
| `TIME` | 3바이트 | -838:59:59 \~ 838:59:59 | 기간 또는 하루 중 시각 |
| `DATETIME` | 8바이트 | 1000-01-01 \~ 9999-12-31 | 시간대 변환이 없는 타임스탬프 |
| `TIMESTAMP` | 4바이트 | 1970-01-01 \~ 2038-01-19 | 자동 갱신 타임스탬프; UTC로 저장, 세션 시간대로 표시 |
| `YEAR` | 1바이트 | 1901 \~ 2155 | 연도만 |

`DATETIME`과 `TIMESTAMP`의 핵심 차이:

* `TIMESTAMP`는 값을 UTC로 저장하고 조회 시 세션의 `time_zone`으로 변환합니다. 애플리케이션이 여러 시간대에서 동작한다면 `TIMESTAMP`가 변환을 자동으로 처리합니다.
* `DATETIME`은 시간대 변환 없이 리터럴 값을 저장합니다 — 넣은 그대로 나옵니다.
* `TIMESTAMP`에는 2038년 제한(Unix 타임스탬프 오버플로)이 있습니다. 2038년 이후의 날짜에는 `DATETIME`을 사용하세요.

```sql theme={null}
-- TIMESTAMP auto-updates on insert and update
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
```

시간대 처리에 대한 전체 논의는 [MySQL의 타임스탬프와 시간대](https://villagesql.com/docs/ko/guides/timestamps-timezones.md)를 참조하세요.

## 십진수와 부동 소수점

| 유형 | 저장 공간 | 정밀도 | 사용 시기 |
| :- | :- | :- | :- |
| `DECIMAL(p,s)` | 가변 | 정확 | 금액, 금융 계산 — 통화에는 절대 FLOAT를 사용하지 마세요 |
| `FLOAT` | 4바이트 | 약 7자리 유효 숫자 | 근사값이 허용되는 과학 데이터 |
| `DOUBLE` | 8바이트 | 약 15자리 유효 숫자 | 더 높은 정밀도의 근사값 |

`FLOAT`와 `DOUBLE`은 십진 소수를 정확히 표현할 수 없는 이진 부동 소수점을 사용합니다. 부동 소수점에서 `0.1 + 0.2`는 `0.30000000000000004`입니다. 금액에는 항상 `DECIMAL`을 사용하세요.

```sql theme={null}
-- Correct: exact decimal arithmetic
price DECIMAL(10, 2)   -- up to 99,999,999.99

-- Wrong: floating-point rounding errors will appear in financial calculations
price FLOAT
```

## JSON

`JSON` 컬럼은 검증된 JSON을 저장하고 경로 기반 쿼리 연산자를 제공합니다.

```sql theme={null}
-- Store structured data without a fixed schema
CREATE TABLE events (
    id      INT PRIMARY KEY AUTO_INCREMENT,
    payload JSON NOT NULL
);

-- Extract a field
SELECT payload->>'$.user_id' FROM events;

-- Filter on a nested field
SELECT * FROM events WHERE payload->>'$.event_type' = 'purchase';
```

`JSON` 컬럼은 직접 인덱싱할 수 없습니다. 자주 쿼리하는 JSON 필드를 인덱싱하려면 생성 컬럼을 사용하세요:

```sql theme={null}
ALTER TABLE events
    ADD COLUMN event_type VARCHAR(50) AS (payload->>'$.event_type') STORED,
    ADD INDEX idx_event_type (event_type);
```

## 자주 묻는 질문

#### 기본 키에 INT와 BIGINT 중 무엇을 사용해야 하나요?

`INT`는 부호 있는 정수로 21억 행, 부호 없는 정수로 43억 행을 처리합니다. 대부분의 테이블에서는 `INT UNSIGNED`로 충분합니다. 실제로 그 한계를 넘을 것으로 예상하거나 4바이트에 들어가지 않는 UUID 스타일 ID를 사용한다면 `BIGINT`를 사용하세요.

#### 고민하지 않으려고 모든 곳에 VARCHAR(255)를 사용해도 되나요?

가능하지만, 더 긴 `VARCHAR` 선언은 더 넓은 인덱스 키를 만들어 인덱스 메모리 사용량을 늘리고 페이지당 들어가는 인덱스 항목 수를 줄입니다. 현실적인 최대값을 선언하세요 — 이름 필드에는 `VARCHAR(255)`가 아니라 `VARCHAR(100)`. 실제 저장 비용은 데이터 길이와 같지만, 인덱스 키 길이는 선언된 크기로 고정됩니다.

#### NULL과 기본값의 실질적인 차이는 무엇인가요?

`NULL`은 "알 수 없음"을 의미합니다 — 표현식을 통해 전파되고 집계 함수에서 제외됩니다. `0`이나 `''` 같은 기본값은 "명시적으로 비어 있음으로 설정됨"을 의미합니다. 실제로 없는 데이터에는 `NULL`을 고르고, 컬럼이 즉시 합리적인 값을 갖기를 원하는 "아직 설정되지 않음" 시나리오에는 기본값을 고르세요. 둘을 섞으면 쿼리에 불일치가 생깁니다.

## 문제 해결

| 문제 | 해결 방법 |
| :- | :- |
| `Data too long for column` | 컬럼 유형이 너무 좁음 — `VARCHAR` 길이를 늘리거나 `TEXT`로 전환 |
| 금융 계산의 반올림 오류 | `FLOAT`/`DOUBLE`을 `DECIMAL(p,s)`로 교체 |
| `TIMESTAMP`가 잘못된 시간을 표시함 | 세션 `time_zone`이 예상과 다름 — `SELECT @@session.time_zone` 확인 |
| TEXT 컬럼에 인덱스를 만들 수 없음 | 접두사 인덱스 사용: `INDEX (col(100))`, 또는 생성 컬럼 추가 |
| INT 기본 키가 한계에 근접함 | 한계에 도달하기 전에 `BIGINT` 또는 `INT UNSIGNED`로 변경 |

## 참고

* [MySQL의 정규화](https://villagesql.com/docs/ko/guides/normalization.md) — 유형 결정을 위한 스키마 설계 맥락
* [MySQL의 JSON](https://villagesql.com/docs/ko/guides/json-in-mysql.md) — 경직된 스키마의 유연한 대안으로서의 JSON 컬럼 유형
* [MySQL 인덱스의 동작 방식](https://villagesql.com/docs/ko/guides/mysql-indexes.md) — 데이터 유형 선택은 인덱스 크기와 효율성에 영향을 줍니다
* [다차원 범위 쿼리](https://villagesql.com/docs/ko/guides/cube-queries.md) — n차원 기하 데이터를 저장하고 쿼리하기 위한 cube 유형
* [MySQL 대 PostgreSQL: 무엇을 사용해야 할까?](https://villagesql.com/docs/ko/guides/mysql-vs-postgresql.md) — 두 데이터베이스가 유형 등에서 어떻게 다른지
* [MySQL의 utf8mb4: 이모지와 완전한 유니코드 저장하기](https://villagesql.com/docs/ko/guides/utf8mb4.md) — 컬럼 문자 집합과 4바이트의 현실
* [MySQL의 진짜 불리언](https://villagesql.com/docs/ko/guides/boolean-columns.md) — true와 false 외의 모든 것을 거부하는 컬럼 유형
* [MySQL SELECT](https://villagesql.com/docs/ko/tutorial/select.md) — 이 컬럼들을 다시 읽는 튜토리얼 레슨


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.