
DailyLog 프로젝트에서 캘린더 기반 조회, 최근 기록 조회 기능을 구현하면서
데이터가 늘어날 경우를 대비해 MySQL 인덱스 최적화 작업을 진행했다.
이번 글에서는
왜 인덱스가 필요한지 → 어떤 인덱스를 추가했는지 → 실제로 인덱스를 타는지
를EXPLAIN기준으로 정리한다.
초기 상태의 daily_log 테이블은 다음과 같았다.
SHOW INDEX FROM daily_log;
PRIMARY KEY (id)만 존재log_date, category 관련 인덱스 없음즉,
모두 풀 테이블 스캔(Full Scan) 상태였다.
데이터가 적을 때는 문제가 없어 보이지만,
데이터가 쌓이면 성능 저하가 바로 발생할 수 있는 구조였다.
DailyLog에서 실제로 발생하는 조회 패턴은 다음과 같다.
log_date BETWEEN)EXERCISE 우선 노출→ 모든 쿼리의 중심은 log_date
log_dateALTER TABLE daily_log
ADD INDEX idx_daily_log_log_date (log_date);
(log_date, category)ALTER TABLE daily_log
ADD INDEX idx_daily_log_log_date_category (log_date, category);
복합 인덱스는 왼쪽 컬럼부터 사용되므로
log_date → category순서가 중요하다.
SHOW INDEX FROM daily_log;
| Index Name | Columns |
|---|---|
| PRIMARY | id |
| idx_daily_log_log_date | log_date |
| idx_daily_log_log_date_category | log_date, category |
👉 의도한 인덱스가 정확히 적용됨.
EXPLAIN)EXPLAIN
SELECT *
FROM daily_log
WHERE log_date >= '2025-12-01'
AND log_date < '2026-01-01'
ORDER BY log_date DESC, created_at DESC;
기대 결과
type = rangekey = idx_daily_log_log_date 또는 복합 인덱스결과값 
EXPLAIN
SELECT id, title, category, log_date
FROM daily_log
WHERE log_date >= CURDATE() - INTERVAL 6 DAY
AND log_date <= CURDATE()
ORDER BY log_date DESC;
결과값

EXPLAIN
SELECT id, title, category, log_date
FROM daily_log
WHERE log_date >= CURDATE() - INTERVAL 6 DAY
AND log_date <= CURDATE()
AND category = 'EXERCISE'
ORDER BY log_date DESC;
기대 결과
key = idx_daily_log_log_date_categorytype = range 또는 ref결과값

EXPLAIN
SELECT id, title, category, log_date
FROM daily_log
WHERE log_date = '2025-12-12'
ORDER BY created_at DESC;
결과값

EXPLAIN 결과에서 아래만 보면 된다.
| 컬럼 | 의미 |
|---|---|
type | ALL이면 풀스캔, range/ref면 정상 |
key | 실제 사용된 인덱스 |
rows | 예상 스캔 row 수 |
Extra | Using where, Using filesort 여부 |
⚠️ 특히 아래 형태는 인덱스를 못 탄다:
WHERE DATE(log_date) = '2025-12-12'
반드시 이렇게 써야 한다:
WHERE log_date = '2025-12-12'
“구조는 이미 맞았고, DB가 그 구조를 제대로 받쳐줄 차례였다.”
이번 작업으로 DailyLog는
데이터가 늘어나도 프론트 구조를 바꾸지 않아도 되는 상태가 되었다.