증상 네 개, 이름 네 개
이 연재는 모바일 청첩장 서비스를 예로 ERD를 읽어 왔다. ERD는 데이터베이스의 표와 표 사이 관계를 한 장에 그린 설계도다. 01편에서 관리 대상인 엔티티와 칸인 컬럼을, 02편에서 하나에 여러 개가 딸리는 1:N 관계를, 03편에서 여러 개끼리 서로 얽히는 N:M 관계와 조인을 봤다.
강의는 마지막에 고급 주제 네 가지를 소개만 하고 끝난다. N+1 문제, 트랜잭션, 인덱스, 마이그레이션이다. 이름만 들으면 개발자의 영역 같지만 강의가 설명하는 방식은 코드가 아니라 증상이다. 어떤 일이 벌어지면 무엇을 의심하라는 식이다.
바이브코더에게는 이 방식이 맞다. AI에게 코드를 맡긴 사람이 직접 볼 수 있는 것은 화면과 동작이다. 코드 속 원인은 못 찾아도 「목록이 느리다」「포인트가 안 들어왔다」는 알아챌 수 있다. 그 증상에 이름을 붙일 줄 알면 AI에게 무엇을 확인해 달라고 할지가 정해진다.
아래 표가 이 글 전체의 요약이다. 왼쪽 열의 증상을 만나면 가운데 열의 이름을 넣어 오른쪽 열처럼 AI에게 확인을 요청한다.
| 증상 | 의심할 것 | AI에게 할 요청 |
|---|---|---|
| 목록 화면이 너무 느리다 | N+1 문제, 인덱스 | 이 목록에 N+1 문제가 있는지, 인덱스가 빠졌는지 둘 다 확인해 줘 |
| 검색이 너무 느리다 | 인덱스 | 이 검색에 쓰는 칸에 인덱스가 설정돼 있는지 확인해 줘 |
| 가입은 됐는데 300포인트가 안 들어왔다 | 트랜잭션 | 가입과 포인트 지급이 한 트랜잭션으로 묶여 있는지 확인해 줘 |
| 칸을 별도 표로 바꾸는 등 기존 데이터를 옮겨야 한다 | 마이그레이션 | 실행하기 전에 단계별 계획을 먼저 보여 줘 |
목록이 느리다 — N+1 문제, 그리고 인덱스

청첩장 목록 화면을 떠올려 보자. 청첩장마다 어떤 템플릿을 쓰는지 썸네일과 템플릿 주소(URL)를 같이 보여 준다. 그런데 청첩장 표에는 템플릿 정보가 없고 템플릿의 id(template_id)만 있다. 03편에서 본 대로 템플릿 표에서 그 id의 줄을 찾아 주소를 끌어와 붙인다. 이것이 조인이다.
목록 화면은 이 데이터를 API로 받아 온다. API는 화면이 데이터를 달라고 부르는 창구다. 제대로 짠 목록 API는 청첩장과 템플릿을 조인해 한 번에 가져온다. N+1 문제는 조인하지 않고 청첩장 목록을 한 번 가져온 다음, 한 줄마다 템플릿을 다시 조회하는 것이다. 목록 조회 한 번(1)에 줄 수만큼(N)의 조회가 붙어서 N+1이라고 부른다.
청첩장이 몇 개 없을 때는 차이가 안 보이고, 줄이 늘수록 조회 횟수가 같이 는다. 그래서 강의는 증상으로 기억하라고 한다. 목록 API를 불렀는데 너무 느리면 한번 의심해 보라는 것이다. 그때 AI에게 「이 목록 API에 N+1 문제가 있는지 확인해 줘」라고 요청하면 된다.
인덱스도 증상이 비슷하다. 인덱스는 데이터베이스를 위해 목차를 미리 만들어 두는 것이다. 목차 없는 책에서 원하는 대목을 찾으려면 처음부터 넘겨야 하듯, 인덱스가 없는 칸으로 검색하면 표 전체를 훑는다. 검색이 너무 느려졌다면 인덱스를 의심한다.
강의가 강조하는 대목은 둘을 함께 의심하라는 것이다. 목록 같은 API를 호출했는데 너무 느리다면 N+1 문제인지, 인덱스가 제대로 설정되지 않았는지 두 가지를 다 물어봐야 한다. 증상이 같아서 한쪽만 확인하면 원인을 놓칠 수 있다.
청첩장 20개짜리 목록을 그릴 때
N+1 (느린 쪽)
① 청첩장 20개를 조회 → 1번
② 청첩장마다 템플릿을 따로 조회 → 20번
합계 21번. 청첩장이 늘면 같이 는다
조인 (빠른 쪽)
① 청첩장과 템플릿을 template_id 로 붙여 한 번에 조회 → 1번가입은 됐는데 포인트가 없다 — 트랜잭션
강의의 예시는 회원 가입이다. 신규 가입자에게 300포인트를 주는 기능이 있다고 하자. 데이터베이스에는 유저 표와 포인트 표가 따로 있다. 가입 한 번에 두 표 모두에 기록이 생겨야 한다.
직접 가입해 보면 유저는 생성됐는데 300포인트는 추가되지 않은 경우가 생길 수 있다. 같이 일어나야 할 일인데 표가 나뉘어 있다는 이유로 한쪽만 되는 것이다. 포인트 없는 반쪽짜리 계정이 남는다.
트랜잭션은 여러 동작을 한 묶음으로 처리하는 것이다. 묶인 동작은 같이 성공하거나 같이 실패한다. 은행 이체에서 내 계좌에서 돈이 빠지는 일과 상대 계좌에 돈이 들어가는 일이 따로 놀면 안 되는 것과 같다. 포인트 지급이 실패하면 가입도 없던 일로 돌린다.
증상은 「같이 일어나야 할 일 중 하나만 일어나 있다」이다. 확인도 강의 예시처럼 직접 가입해 보고 포인트가 들어왔는지 보면 된다. 안 들어왔으면 AI에게 「회원 가입과 300포인트 지급이 한 트랜잭션으로 묶여 있는지 확인해 줘. 둘 중 하나가 실패하면 둘 다 취소돼야 해」라고 요청한다.
| 가입 | 포인트 지급 | 트랜잭션이 없으면 | 트랜잭션이 있으면 |
|---|---|---|---|
| 성공 | 성공 | 정상 | 정상 |
| 성공 | 실패 | 포인트 없는 유저만 남는다 | 둘 다 취소된다 |
템플릿을 표로 옮길 때 — 마이그레이션

마이그레이션은 데이터베이스의 엔티티를 추가하거나 고치는 일이다. 강의에 따르면 표를 새로 하나 더하는 정도는 큰 상관이 없다. 조심할 것은 이미 쌓인 데이터를 옮겨야 하는 변경이다.
대표적인 경우가 02편에서 본 템플릿의 승격이다. 처음에는 청첩장 표의 템플릿 칸에 1·2·3 중 하나만 넣게 정해 두었다. 정해 둔 값만 들어가게 하는 이 방식을 enum이라고 한다. 템플릿을 관리해야 할 때가 오자 템플릿을 별도 표로 뺐다. 설계도에서는 선 하나 바꾸는 일이지만, 이미 만들어진 청첩장들이 있다면 이야기가 달라진다.
순서는 이렇다. 먼저 템플릿 표를 만들고 1·2·3을 거기에 넣는다. 각 줄에 그 줄을 가리키는 id가 생긴다. 다음으로 기존 청첩장의 템플릿 값 1·2·3을 새 id에 하나씩 짝지어 template_id에 채운다. 두 표가 이미 관계를 맺고 있으니 이 짝이 맞아야 에러가 나지 않는다. 마지막으로 더는 필요 없어진 옛 템플릿 칸을 지운다.
이런 대규모 변경을 AI에게 맡기고 아무것도 보지 않으면 사고가 굉장히 많이 난다는 것이 강의의 경고다. 데이터베이스를 잘못 조작해 데이터가 싹 날아갔다는 이야기는 대체로 마이그레이션을 신경 쓰지 않고 AI에게 맡긴 경우라고 한다. 그래서 이런 작업은 AI에게만 맡기지 말고 직접 한 번씩 확인하는 습관을 들이라고 권한다.
확인이 코드를 읽는 일일 필요는 없다. 실행 전에 단계별 계획을 먼저 보여 달라고 하고, 옮기기 전과 후의 청첩장 수가 같은지, 모든 청첩장에 template_id가 채워졌는지 확인한 다음에 옛 칸을 지우게 한다. 지우는 단계가 되돌리기 가장 어렵기 때문이다.
전: invitations
id title template
1 청첩장 A 1
2 청첩장 B 3
① templates 표를 만들고 1·2·3 을 넣는다
id url thumbnail
t-01 ... ...
t-02 ... ...
t-03 ... ...
② invitations.template_id 를 채운다
template 1 → templates.id t-01
template 3 → templates.id t-03
③ 옛 invitations.template 칸을 지운다 ← 되돌리기 가장 어렵다템플릿 칸을 templates 표로 옮기는 마이그레이션을 하려고 해.
실행하기 전에 단계별 계획을 먼저 보여 줘.
옮기기 전과 후의 청첩장 수가 같은지,
모든 청첩장에 template_id 가 채워졌는지 확인하는 방법도 같이 알려 줘.
옛 template 칸은 내가 확인한 다음에 지울게.완성된 ERD가 보여 주는 것
강의자가 가장 중요하게 꼽은 것은 네 가지 사고가 아니라 완성된 ERD 자체다. 완성된 ERD를 보면 무엇을 구현했고 무엇을 구현하지 않았는지가 눈에 보인다.
추가 기능을 개발해야 할 때 어느 부분에 무엇을 더하면 되는지가 보이고, 지금의 연관 관계로 어떤 기능은 가능하고 어떤 기능은 불가능한지를 스스로 빠르게 파악할 수 있다. 01편에서 말한, 몇 달 뒤 「내가 뭘 만들었나」를 복원하는 지도가 이것이다.
네 가지 사고도 그 지도 위에 자리가 있다. N+1 문제는 표와 표를 잇는 선을 따라 데이터를 가져오는 방식의 문제이고, 인덱스는 표 안에서 자주 찾는 칸에 붙는 목차다. 트랜잭션은 한 동작이 건드리는 두 표 사이의 문제이고, 마이그레이션은 선을 바꾸는 순간의 문제다. ERD를 읽을 줄 알면 증상이 났을 때 AI에게 어디를 보라고 할지도 보인다.
여기까지가 ERD 강의를 바탕으로 한 네 편이다. 다음 편부터는 웹 페이지의 뼈대를 짜는 HTML 강의로 넘어가, 상담 신청 폼을 예로 화면 구성(UI)이 왜 정답이 있는 문제인지 본다.
