바이브 코딩 시대의 데이터베이스6-5 인덱스의 비용과 함정
테크타니 LEARN — 섹션 06 · 인덱스

인덱스의 비용과 함정 —
많다고 모두 빨라지지 않는다

도서관에 제목별 찾아보기가 있으면 책 찾기는 아주 편합니다. 하지만 새 책이 들어오면 책장에만 꽂는 게 아니라 찾아보기에도 위치를 적어야 하죠. 인덱스도 똑같습니다 — 빠르게 찾도록 도와주지만, 데이터가 바뀔 때마다 함께 관리해야 합니다.

난이도보통소요8분준비물6-4 완료오늘섹션 6 마무리

이번 섹션의 여정

6-2 언제 만들까

완료 ✓

→
6-3 단일 · 복합 인덱스

완료 ✓

→
6-4 EXPLAIN

완료 ✓

→
6-5 비용과 함정

지금 여기

인덱스에는 두 가지 비용이 있습니다

인덱스의 이익
  • 빠른 조회찾을 위치로 바로 이동한다

  • 정렬 절약이미 정리돼 있어 줄을 다시 세우지 않는다

인덱스의 비용
  • 저장 공간원본 데이터 외에 인덱스를 담을 공간이 더 필요하다

  • 쓰기 부담INSERT · UPDATE · DELETE 때 관련 인덱스도 함께 바뀐다

검색을 도와주는 안내판이 많아질수록 — 새 데이터를 넣거나 고칠 때 손볼 안내판도 함께 늘어납니다.

데이터가 적을 때는 차이가 잘 안 느껴집니다. 우리 shop처럼 상품 30개, 주문 40건이면 인덱스를 몇 개 만들든 체감이 없어요. 하지만 데이터가 아주 많고 인덱스도 많아지면 필요한 공간과 쓰기 시간이 함께 커집니다.

많이 만들면 생기는 문제

제목 찾아보기가 이미 있는데 이름만 다른 똑같은 찾아보기를 또 만든다면 어떨까요? 찾는 데 도움은 거의 늘지 않지만, 보관하고 고칠 대상만 늘어납니다.

새 인덱스를 만들기 전에

같은 역할을 하는 인덱스가 이미 있는지 먼저 확인하세요. 6-2에서 봤듯 orders.member_id 처럼 외래키 인덱스가 이미 깔려 있는 컬럼도 있고, 6-3에서 봤듯 (member_id, ordered_at) 복합 인덱스가 있으면 member_id 단독 인덱스는 따로 필요 없습니다.

개수가 기준이 아닙니다중요한 것은 인덱스의 개수가 아니라, 실제 조회에 도움이 되는지입니다.

함정 1 — 컬럼을 가공해서 검색하기

첫 번째 함정은 인덱스가 걸린 값을 다른 모양으로 바꿔서 검색하는 경우입니다.

주문일에서 연도만 꺼내 2026년 주문을 찾아볼게요.

❌ 인덱스를 쓰기 어려운 방식
SELECT order_id, ordered_at
FROM orders
WHERE YEAR(ordered_at) = 2026;

결과는 맞게 나옵니다. 2026년 주문 11건이 정확히 나와요. 하지만 원래 날짜를 함수로 한 번 바꿨기 때문에, 날짜순으로 정리된 인덱스에서 시작 위치를 바로 찾기 어렵습니다.

인덱스는 ordered_at 값 자체를 기준으로 정리돼 있는데, 우리가 찾는 건 YEAR(ordered_at)이라는 가공된 값이니까요. 정리 기준과 찾는 기준이 어긋난 겁니다.

이번에는 날짜를 바꾸지 않고 범위로 표현해 보겠습니다.

✓ 인덱스가 시작점을 찾을 수 있는 방식
SELECT order_id, ordered_at
FROM orders
WHERE ordered_at >= '2026-01-01'
AND ordered_at < '2027-01-01';
같은 결과, 다른 경로
조건 쓰는 법인덱스에서 시작점을 찾을 수 있나
YEAR(ordered_at) = 2026어렵다 — 정리 기준과 찾는 기준이 다르다
ordered_at >= '2026-01-01' AND < '2027-01-01'쉽다 — 시작 날짜로 바로 이동한다
컬럼을 그대로 두고 값 쪽을 바꾸세요
우리 shop 에서 확인하려면

6-3에서 만든 건 (member_id, ordered_at) 복합 인덱스라, ordered_at 단독으로는 쓰기 어렵습니다 — 맨 왼쪽 컬럼이 아니니까요(6-3의 왼쪽 우선 규칙). 이 함정을 직접 확인하려면 CREATE INDEX idx_orders_date ON orders (ordered_at); 로 단독 인덱스를 하나 만들고 두 쿼리에 EXPLAIN 을 붙여 비교해 보세요.

함정 2 — 첫 글자를 모르는 검색

두 번째 함정은 LIKE 검색에서 첫 글자를 모르는 경우입니다.

6-1에서 products.product_name에 인덱스를 만들었죠. 그 인덱스는 상품 이름이 가나다순으로 정리돼 있습니다.

같은 LIKE 인데 결과가 갈립니다
조건찾는 상품인덱스를 쓸 수 있나
product_name LIKE '면%'면 셔츠 · 면바지○ '면' 으로 시작하는 위치로 이동
product_name LIKE '%셔츠'라운드 티셔츠 · 면 셔츠✕ 어디서부터 볼지 정할 수 없다
앞부분을 알면 시작점을 찾고, 모르면 넓게 살펴봅니다

'면%'은 첫 글자를 알고 있으니 가나다순 목록에서 면으로 시작하는 자리로 바로 갈 수 있습니다. 하지만 '%셔츠'는 앞에 무엇이 오는지 모릅니다 — 라운드 티셔츠일 수도 있고 면 셔츠일 수도 있죠. 시작점을 정할 수 없으니 일반적인 인덱스로는 넓게 살펴보게 됩니다.

두 함정의 뿌리는 같습니다

인덱스는 정렬된 목록입니다. 정렬된 목록이 힘을 쓰려면 어디서부터 볼지 정할 수 있어야 해요. 컬럼을 가공하거나 앞부분을 모르면 — 그 시작점을 잃습니다.

인덱스를 만들기 전 세 가지 질문

새 인덱스를 만들기 전에
① 자주 사용하는 조회인가?
② 같은 역할을 하는 인덱스가 이미 있는가?
③ 관리 비용보다 조회 이익이 큰가?

이 세 가지에 답하면 필요 없는 인덱스를 줄이고, 꼭 필요한 인덱스를 고르기 쉬워집니다.

⭐ 섹션 6 의 결론

좋은 인덱스는 많이 만든 인덱스가 아니라, 자주 사용하는 조회에 꼭 필요한 인덱스입니다.

한눈에 정리

주제핵심
비용 ①저장 공간 — 원본 외에 인덱스를 담을 자리
비용 ②쓰기 부담 — INSERT · UPDATE · DELETE 때 함께 갱신
함정 ①컬럼을 가공하면(YEAR(...)) 시작점을 잃는다
함정 ②앞부분을 모르는 LIKE '%...' 는 시작점이 없다
판단 기준조회 이익과 저장 · 쓰기 · 관리 비용의 균형

여기까지가 섹션 6입니다. 데이터를 빠르게 찾는 구조를 봤어요 — 인덱스가 무엇인지, 어디에 만들지, 컬럼 순서를 어떻게 정할지, EXPLAIN으로 확인하는 법, 그리고 오늘의 비용과 함정까지.

다음 섹션에서는 여러 SQL을 하나의 업무로 묶는 트랜잭션을 배웁니다.

NEXT LESSON7-1 트랜잭션의 본질 — 전부 성공하거나 전부 실패한다

주문은 됐는데 재고가 안 줄었다면? 반쪽짜리 결과를 막는 약속을 배웁니다.

GO ▸ 섹션 7 시작하기