바이브 코딩 시대의 데이터베이스6-1 인덱스의 본질
테크타니 LEARN — 섹션 06 · 인덱스

인덱스의 본질 —
두꺼운 책의 찾아보기

섹션 5에서 원하는 데이터를 꺼내는 법을 익혔습니다. 이번 섹션은 같은 데이터를 어떻게 찾아가는가에 관한 이야기예요. 첫 강의는 비유 하나로 끝까지 갑니다 — 500쪽짜리 책과 맨 뒤의 찾아보기.

난이도쉬움소요7분준비물섹션 5 완료오늘전체 스캔 vs 인덱스

이번 섹션의 여정

섹션 5 SQL 핵심 문법

완료 ✓

6-1 인덱스의 본질

지금 여기

6-2 언제 만들까

WHERE · JOIN · ORDER BY

6-3 ~ 6-5

단일 · 복합 · EXPLAIN · 비용

500쪽짜리 책에서 “향수”를 찾는 두 가지 방법

책상 위에 500쪽짜리 두꺼운 책이 있습니다. 여기서 “향수” 에 대한 설명을 찾아야 해요.

방법은 두 가지죠. 첫 페이지부터 한 장씩 넘겨도 언젠가는 찾습니다 — 시간이 꽤 걸릴 뿐이에요. 아니면 맨 뒤 찾아보기를 펼쳐 향수 … 312쪽을 확인하고, 곧장 312쪽으로 갑니다.

⭐ 오늘의 한 문장

인덱스는 두꺼운 책의 찾아보기처럼, 찾을 값과 원본 데이터를 찾아갈 기본키를 미리 정리해 둔 구조입니다.

이 비유는 강의가 끝날 때까지 바뀌지 않습니다. 책의 본문은 테이블, 찾아보기는 인덱스, 페이지 번호는 기본키예요.

전체 스캔 — 결과 1행이 곧 “1행만 읽었다”는 뜻은 아닙니다

이번엔 진짜 shop DB에서 향수를 찾아보겠습니다.

SELECT product_id, product_name, price
FROM products
WHERE product_name = '향수';
DBeaver — SQL Editor (Ctrl + Enter)
SELECT product_id, product_name, price FROM products WHERE product_name = '향수';
product_idproduct_nameprice
29향수89000
1 row fetched

결과는 딱 한 줄입니다. 그런데 데이터베이스는 향수가 몇 번째 행에 있는지 미리 알고 있었을까요? 아니에요. product_name으로 찾아갈 길이 없으면 상품 행을 처음부터 끝까지 훑어보며 조건에 맞는 걸 골라냈을 수 있습니다.

가장 흔한 착각

결과 1행 ≠ 확인한 행 1개. 결과가 한 줄이라고 해서 한 줄만 들여다봤다는 뜻이 아닙니다. 눈에 보이는 건 골라낸 결과이지 읽은 양이 아니에요.

책을 첫 쪽부터 넘기는 것처럼 테이블 전체를 읽는 이 방법을 전체 스캔(Full Scan) 이라고 부릅니다.

30개일 때와 30만 개일 때

products 30행 — 지금 우리 shop
  • 전체 스캔해도30행 확인은 눈 깜짝할 사이에 끝난다

  • 체감인덱스가 있든 없든 차이를 느끼기 어렵다

상품 30만 행 — 실제 서비스
  • 전체 스캔이면한 번 검색에 30만 행을 다 읽는다

  • 체감여기서부터 인덱스가 있고 없고가 갈린다

그래서 인덱스는 지금 느려서 배우는 게 아니라 — 데이터가 늘어날 때 무엇이 달라지는지 알기 위해 배웁니다.

shop 에는 이미 인덱스가 있습니다 — 다만 이름 컬럼엔 없습니다

init.sql에는 CREATE INDEX 문이 한 줄도 없습니다. 그런데 인덱스가 하나도 없는 건 아니에요. 기본키는 그 자체가 인덱스이고, 외래키를 만들면 InnoDB가 자식 컬럼에 인덱스를 자동으로 만들어 줍니다.

shop DB — 지금 이미 존재하는 인덱스
테이블있는 인덱스어디서 생겼나
membersPRIMARY (member_id)기본키
categoriesPRIMARY (category_id)기본키
productsPRIMARY (product_id) · category_id기본키 + 외래키 생성 시 자동
ordersPRIMARY (order_id) · member_id기본키 + 외래키 생성 시 자동
order_itemsPRIMARY (order_id, product_id) · product_id복합 기본키 + 외래키 생성 시 자동
order_items.order_id 는 복합 기본키의 맨 왼쪽 컬럼이라 기본키 인덱스가 그대로 커버합니다 — 따로 만들 필요가 없습니다

반대로 말하면 — members.member_name, members.age, products.product_name, products.price, orders.ordered_at 에는 아직 인덱스가 없습니다. 방금 우리가 향수를 찾은 product_name이 바로 그중 하나예요.

상품 이름에 찾아보기 하나 만들기

그럼 상품 테이블에도 찾아보기를 하나 붙여 보겠습니다.

CREATE INDEX idx_products_name
ON products (product_name);

“상품 이름으로 더 쉽게 찾을 수 있도록, 상품 이름 컬럼에 인덱스를 만든다” — 딱 그 뜻입니다. idx_products_name은 그냥 이름표예요. 문자 그대로 읽지 말고 “상품 이름 인덱스” 라고 부르면 됩니다.

테이블을 복사하는 게 아닙니다

인덱스를 만들어도 products 30행이 통째로 복사되지 않습니다. 책 뒤 찾아보기가 본문을 베껴 적지 않는 것처럼 — 이름과 상품 번호만 따로 정리해 둘 뿐이에요.

찾아보기에 적히는 것 — 값과 기본키

책의 찾아보기에는 두 가지가 나란히 적혀 있었죠. 단어페이지 번호. 인덱스도 똑같이 두 가지를 담습니다.

products에서 상품 번호 product_id는 상품마다 하나씩 붙는 기본키입니다. 그래서 상품 이름 인덱스에는 향수라는 값과 기본키 값 29가 함께 정리돼요.

같은 구조, 다른 이름
두꺼운 책products 테이블무엇을 뜻하나
본문 500쪽상품 30행실제 데이터가 있는 원본
1쪽부터 한 장씩 넘기기행을 처음부터 끝까지 확인전체 스캔
맨 뒤 찾아보기product_name 인덱스검색용으로 따로 만든 구조
찾아보기의 단어 "향수"인덱스에 저장된 값 "향수"찾을 값
페이지 번호 312쪽기본키 product_id 29원본으로 찾아가는 길
책: 향수 → 312쪽 → 본문 / DB: 향수 → 기본키 29 → 원본 상품 행

그리고 하나 더. 책의 찾아보기는 가나다순으로 정렬돼 있습니다. 그래서 첫 항목부터 훑지 않고 “ㅎ” 근처를 바로 펼칠 수 있어요.

인덱스도 마찬가지입니다. 값이 정렬된 상태로 유지되고, 상품이 추가되거나 이름이 바뀌어도 그 정렬은 계속 지켜집니다. 인덱스가 빠른 이유는 사본이 하나 더 있어서가 아니라 — 정렬돼 있어서 찾아볼 범위를 확 줄일 수 있기 때문입니다.

만들었다고 항상 쓰는 것은 아닙니다

여기서 많이들 놀랍니다. 찾아보기가 있다고 항상 펼쳐보는 건 아니거든요.

500쪽짜리 책이면 찾아보기가 훨씬 편하죠. 그런데 30쪽짜리 얇은 책이라면? 그냥 처음부터 넘기는 편이 빠릅니다. 우리 products도 30개뿐이라, 인덱스를 만든 뒤에도 데이터베이스가 전체 스캔을 고를 수 있어요.

이유는 하나 더 있습니다. 찾는 값이 너무 흔할 때도 그렇습니다.

찾는 값이 흔하면 찾아보기가 손해입니다
조건해당하는 행값이 얼마나 흔한가
product_name = '향수'30개 중 1개드문 값 — 찾아보기가 제 몫을 하는 조건
members.gender = 'F'20명 중 10명절반 — 찾아보기를 펴도 결국 거의 다 읽는다
찾아보기를 폈다가 원본으로 되돌아가는 왕복을 열 번 할 바에는 — 스무 줄을 그냥 읽는 편이 쌉니다
⭐ 꼭 기억할 것

인덱스 생성 ≠ 인덱스 사용. 인덱스는 찾아갈 길을 하나 더 놓아줄 뿐이고, 실제로 어느 길로 갈지는 데이터베이스가 그때그때 판단합니다.

그러니 인덱스를 만든 뒤에 전체 스캔이 선택되더라도 실패가 아닙니다. 데이터가 적거나 값이 흔할 때 나올 수 있는 정상적인 판단이에요.

그럼 어느 길로 갔는지는 어떻게 확인할까요. 이때 쓰는 도구가 EXPLAIN입니다.

EXPLAIN — 오늘은 이름만

EXPLAIN(익스플레인)은 데이터베이스가 예상한 검색 방법을 보여주는 도구입니다. 실제 실행 시간을 재는 도구도, 인덱스 사용을 보장하는 명령도 아니에요. 결과표를 읽는 방법은 6-4 에서 천천히 다룹니다.

찾아보기도 공짜가 아닙니다

책의 모든 단어를 찾아보기에 넣으면 어떻게 될까요. 찾아보기 자체가 본문만큼 두꺼워지고, 본문이 한 줄 바뀔 때마다 찾아보기도 같이 고쳐야 합니다.

데이터베이스도 똑같습니다. 인덱스는 저장 공간을 쓰고, 상품이 추가되거나 상품 이름이 바뀌면 인덱스도 함께 정리해야 해요.

그래서 인덱스 후보는 이렇게 고릅니다
① 데이터가 많은가 — 얇은 책에는 찾아보기가 필요 없다
② 같은 조건으로 자주 검색하는가 — 아무도 안 찾는 단어는 찾아보기에 넣지 않는다

두 조건을 모두 만족하는 컬럼부터 인덱스 후보로 올려 두세요. 어떤 컬럼이 여기에 해당하는지는 다음 6-2 강의에서 하나씩 가려냅니다.

한눈에 정리

개념책으로 말하면데이터베이스에서는
원본 데이터500쪽 본문products 테이블의 행
전체 스캔1쪽부터 한 장씩 넘기기행을 처음부터 끝까지 확인
인덱스맨 뒤 찾아보기찾을 값 + 기본키를 정렬해 둔 구조
인덱스로 찾기향수 → 312쪽 → 본문향수 → product_id 29 → 원본 상품 행
만들어도 안 쓸 수 있다얇은 책은 그냥 넘긴다데이터가 적거나 값이 흔하면 전체 스캔
확인 도구EXPLAIN — 예상 검색 방법 (6-4)

순서로 기억하면 간단합니다. 전체를 읽고 있는지 살펴보고 → 필요한 인덱스를 만들고 → EXPLAIN으로 실제 선택을 확인한다.

이제 인덱스가 무엇인지는 알았습니다. 그런데 우리 shop의 수많은 컬럼 중 어디에 찾아보기를 붙여야 할까요? 다음 강의에서 그 후보를 고르는 기준을 잡습니다.

NEXT LESSON6-2 언제 인덱스를 만들까 — 필요한 곳을 고르는 법

WHERE · JOIN · ORDER BY 세 단서로 인덱스 후보를 찾고, 효과가 작은 경우도 함께 봅니다.

GO ▸ 6-2 시작하기