진짜로 돈이 오가는 데이터베이스를 뜯어봤습니다
— DB를 한 번도 안 다뤄본 분도 읽히는 버전

노닥 스터디 2주차 과제 · 챗사피엔스 운영 DB(Supabase/Postgres) → 에어테이블 거울 복제 · 2026-10-14 18:00 KST 스냅샷

표 12개 칸 161개 행 379개(로그는 최신 100건만) 찾은 문제 12건 개선안 표 14개
3줄 요약
  1. 뭘 했나실제로 강의가 팔리고 녹화본이 배포되고 강사료가 정산되는 사이트의 데이터베이스를, 한 칸도 빼지 않고 에어테이블에 그대로 옮겨 적었습니다. 표 12개 · 칸 161개 · 행 379개.
  2. 뭘 찾았나구조 문제 12건. 전부 데이터로 확인한 것만 넣었습니다. 그중 2개 칸은 아무 일도 안 하거나, 적혀 있는 값이 전부 틀려 있었고, 4개 칸은 같은 정보를 두 번 적고 있었고, 1개 이름은 한 DB 안에서 두 가지 뜻으로 쓰이고 있었습니다.
  3. 그래서 뭘 바꾸나표를 다시 그린 개선안(v2_)을 같은 에어테이블 안에 14개 세워서 나란히 비교했습니다. 다만 지금 당장 손댈 건 이름 바꾸기와 안 쓰는 칸 지우기뿐입니다. 큰 수술은 사람이 수백 명 될 때.
먼저, 이 글에 나오는 말 6개 (펼치기)
표 (테이블)
엑셀 시트 한 장. 이 DB에는 students(회원 명단), recording_orders(주문 내역)처럼 시트가 12장 있습니다.
행 (레코드 · 줄)
시트의 한 줄. 사람 한 명, 주문 한 건, 후기 한 개.
칸 (필드 · 컬럼)
줄 안의 한 칸. '이름', '전화번호', '결제금액'. 이 글의 문제 대부분은 칸이 잘못 놓여 있다는 이야기입니다.
연결 (외래키 · FK)
한 표에서 다른 표의 한 줄을 가리키는 것. 주문 줄에 구매자의 이름·번호를 다 베껴 쓰는 대신 "회원 12번"이라고만 적어 둡니다. 그러면 그 사람이 번호를 바꿔도 회원 명단 한 줄만 고치면 끝납니다. 이 DB에는 이런 연결이 19개 있습니다.
링크 칸 (알약 모양)
에어테이블에서 연결이 눈에 보이는 모양. 칸 안에 #12 김** 처럼 둥근 알약으로 상대 줄의 이름이 뜹니다. 이 글의 표 그림에 나오는 알약은 전부 연결입니다.
주문코드
CS-20261002-1531-7HJZ8H 처럼 사람이 읽는 주문번호. 컴퓨터가 내부에서 쓰는 번호(1531)와는 다른 것입니다. 이 둘을 같은 이름으로 불렀다는 게 핵심 3번 문제입니다.

이 문서의 표 그림은 그림이 아니라 실제 에어테이블 베이스에서 API로 꺼내온 레코드입니다(개인정보는 김**·****1234로 마스킹된 상태로 복제했습니다). 에어테이블은 로그인이 필요해 스크린샷을 못 찍으므로, 같은 데이터를 그리드 모양으로 재현했습니다.

1이 DB가 하는 일 — 표 12장으로 사업 하나가 돌아갑니다

문제 이야기를 하기 전에, 무대를 한 번만 봅시다. 어려운 건 없습니다. 시트 12장이고, 그중 두 장(students·recording_orders)에 거의 모든 사건이 모여 있습니다.

시트(표)줄 수무슨 줄이 들어 있나
recording_orders133주문 한 건 = 한 줄. 누가 뭘 얼마에 샀는지
students71로그인한 회원 한 명 = 한 줄
recording_tokens22녹화본 시청용 열쇠 한 개 = 한 줄
products18"어느 강의를 어느 가격으로 파는가" 한 가지 = 한 줄
reviews12후기 한 개 = 한 줄
instructors7강사 한 명 = 한 줄
recordings6강의 한 개 = 한 줄 (이름은 '녹화본'인데 라이브·오프라인도 여기 있습니다)
materials5교보재 파일 한 개 = 한 줄
questions / question_replies2 / 1질문과 답변
payouts2강사 정산 한 건 = 한 줄
event_logs100"누가 어느 페이지를 눌렀다" 기록. 실제로는 1,467줄

행 수는 2026-10-14 18:00 KST 기준. 에어테이블 무료 요금제가 1,000행까지만 담기므로 event_logs는 최신 100건만 복제했습니다.

한 가지만 먼저 느껴두면 좋은 사실

전체 1,747줄 중

빨간 칸 25개가 event_logs = 1,467줄 (84%). 회색 5개가 주문·회원·강의 등 사업에 쓰는 데이터 전부 = 280줄.

그래서 뭐가 중요한가

"DB를 백업하자 / 분석하자 / 옮기자"고 할 때마다 작업량의 8할이 클릭 기록입니다. 정작 사업 데이터는 280줄뿐인데요. 이건 구조 결함은 아니지만, 이번에 에어테이블로 옮기면서 가장 먼저 몸으로 부딪친 벽이었습니다 (1,000행 한도에 걸려 로그를 잘라내야 했습니다).

이 그림의 네모 하나 ≒ 58줄.

2핵심 4건 — 하나씩 끝까지

찾은 문제는 12건인데, 그중 설명할 가치가 큰 4건만 끝까지 보겠습니다. 각 건마다 ① 한 줄 요약 → ② 실제 데이터 → ③ 뭐가 위험한가 → ④ 어떻게 고치나 순서입니다. 나머지 8건은 4장에 한 줄씩 모았습니다.

🔴 지금 위험

① 아무도 쓰지 않는 칸에, 틀린 값이 들어 있습니다

관리자가 4명인데, "관리자입니까?" 칸은 71명 전원 아니오라고 적혀 있습니다.

실제 데이터

회원 표에는 권한을 나타내는 칸이 두 개 있습니다. role(역할)과 is_admin(관리자 여부). 옛날에 is_admin으로 시작했다가, 나중에 강사까지 생기면서 role로 갈아탄 흔적입니다.

students실제 복제된 레코드 · 71줄 중 2줄
AkeyAname#phone⌄role☑is_admin
P458 이**이******4360user☐
P6 김**김******0489admin☐ ← 관리자인데 체크가 비어 있습니다

전수 집계: role='admin' 4명 · is_admin=true 0명 (71명 전원 false).

왜 이게 "빈 칸"보다 나쁜가
위험

비어 있는 칸은 아무도 안 씁니다. 그런데 이 칸은 그럴듯한 이름을 달고 그럴듯한 값(false)을 들고 있습니다. 반년 뒤의 내가, 또는 AI에게 "관리자만 들어오게 해줘"라고 시켰을 때 — is_admin이라는 이름이 role보다 먼저 눈에 들어옵니다. 그 코드를 배포하는 순간 관리자 4명 전원이 관리자 페이지에서 잠깁니다.

비유하면 — 고장난 걸 아는 스위치는 안 누릅니다. 위험한 건 멀쩡해 보이는데 아무 데도 연결 안 된 스위치입니다.

고치는 법
개선안

칸을 지웁니다. 권한은 role 하나만 씁니다(user / instructor / admin). 같은 이유로 강의 표의 content_status도 지웁니다(③에서 설명).

이게 이번 작업에서 가장 먼저 실제 운영 DB에 적용할 항목입니다. 데이터를 옮길 필요가 없고, 이 칸을 읽는 코드가 하나도 없다는 걸 확인했기 때문입니다.

🔴 지금 위험

② 같은 이름이 DB 안에서 두 가지 뜻으로 쓰입니다

order_id라는 이름이 어떤 표에서는 글자, 다른 표에서는 숫자입니다.

실제 데이터

주문 표의 order_id는 사람이 읽는 주문코드입니다. 그런데 후기 표의 order_id는 "몇 번째 주문줄"을 가리키는 내부 번호입니다.

recording_orders 의 order_id
#idAorder_id
1531CS-20261002-1531-7HJZ8H
1532CS-20261002-1532-G954PF

= 글자. 문자·영수증에 찍히는 값.

reviews 의 order_id
#id#order_id
513
281248

= 숫자. 주문 줄을 가리키는 연결.

참고로 같은 연결을 제대로 부른 곳이 한 군데 있습니다 — recording_tokens.recording_order_id. 이름만 보면 뭘 가리키는지 바로 압니다. 이게 원래 모양입니다.

왜 위험한가
위험

두 칸을 헷갈려서 연결하면 에러가 안 납니다. 글자와 숫자를 맞춰보면 그냥 "일치하는 게 없음"이 되어서, 화면에는 "후기 0건"이라고 조용히 뜹니다.

에러는 고칩니다. 0건은 그냥 믿습니다. "아 아직 후기를 안 썼구나" 하고 넘어가는 종류의 버그라서, 몇 달을 모르고 지낼 수 있습니다.

고치는 법
개선안

이름으로 종류가 보이게 바꿉니다. 사람이 읽는 글자는 order_code(→ 개선안에서는 payment_code), 연결은 전부 …_id + 가리키는 표 이름. 즉 order_id라는 애매한 이름을 DB에서 없앱니다.

단, 교재에 나온 함정이 정확히 이 지점입니다 — 칸 이름을 손으로 바꾸면, 그 이름으로 값을 찾던 코드가 조용히 멈춥니다. 그래서 "이름 바꾸기"가 아니라 "이름과 코드를 같은 커밋에서 같이 바꾸기"로 계획을 잡았습니다.

🟡 헷갈림의 근원

③ 공개 여부를 정하는 칸이 3개인데, 실제로 작동하는 건 1개입니다

칸이 3개면 공개 여부를 바꿀 때 어디를 고쳐야 하는지 알 수 없습니다. 지금 강의 6개는 전부 "작성 중"으로 적힌 채 실제로 팔리고 있습니다.

실제 데이터 — 강의 6개 전부
recordings실제 복제된 레코드 · 6줄 전부
Aslug⌄format☑published☑listed⌄content_status실제로 팔리나?
offline-01offline☑☑draft✅ 판매 중 (결제 4건)
v01online☑☑draft✅ 종료 (판매됨)
v02online☑☑draft✅ 종료 (판매됨)
v03online☑☑draft✅ 판매 중 (결제 15건)
v04online☑☐draft🚫 홍보 전 (목록에 안 뜸)
offline-02offline☑☐draft🚫 홍보 전 (목록에 안 뜸)

세로로 읽어보면 바로 보입니다. content_status는 6줄 전부 같은 값(draft)이고, published도 6줄 전부 같은 값(true)입니다. 줄마다 값이 다른 칸은 listed 하나뿐입니다.

그래서 무슨 일이 벌어졌나
진단

칸이 항상 같은 값이면, 그 칸은 아무것도 결정하지 않습니다. 실제로 노출을 조절한 건 listed 하나였고, content_status='draft'는 "작성 중"이라고 써 있으면서 아무것도 막지 않는 장식이었습니다.

현장에서 생기는 문제는 이렇습니다. 강의를 내리고 싶을 때 — 어느 칸을 끄면 내려가는지 아무도 확신할 수 없습니다. 결국 셋 다 끄게 되고, 다시 올릴 때 하나를 빼먹습니다.

고치는 법
개선안

체크박스 3개를 단계가 있는 칸 1개로 접습니다.

visibility = hidden 아무도 못 봄 → link_only 주소 아는 사람만 (결제는 가능, 목록엔 없음) → listed 목록에 뜸

켜고 끄는 칸이 여러 개면 조합이 생깁니다. 3개면 8가지 상태인데, 그중 실제로 쓰는 건 2가지였습니다. 단계 하나면 상태가 딱 3개고, 셋 다 의미가 있습니다.

🟡 셀 때마다 헷갈림

④ "결제 1건"을 가리키는 방법이 두 가지입니다

하나만 사면 1줄, 둘을 같이 사면 2줄. 2줄을 묶는 값으로 첫 줄의 주문코드를 씁니다.

실제 데이터 — 한 사람이 한 번 결제한 것

어떤 분이 지난 특강 녹화본(20,000원)과 교보재(5,000원)를 한 번에 결제했습니다. 카드는 한 번 긁혔고 25,000원이 빠져나갔습니다. DB에는 이렇게 들어갑니다.

recording_orders실제 복제된 레코드 · 묶음 결제 1건 = 2줄
AkeyAorder_id⌄price_track#amountAorder_group_id
#1531 이**CS-20261002-1531-7HJZ8Hvod_nonattendee20,000CS-20261002-1531-7HJZ8H
#1532 이**CS-20261002-1532-G954PFmaterial5,000CS-20261002-1531-7HJZ8H

노란 칸을 보세요. 1531번 줄의 자기 주문코드가, 1532번 줄에서 "묶는 값"으로 쓰이고 있습니다. 두 줄 중 누가 대표인지는 "번호가 작은 쪽"이라는 암묵적 규칙으로만 압니다. 이런 묶음 결제가 14건 있습니다.

왜 헷갈리나
위험
  • "결제가 몇 건?" → 주문 133줄 중 28줄이 실은 14건의 결제입니다. 줄을 세면 133, 결제를 세면 119. 집계할 때마다 어느 쪽인지 매번 생각해야 합니다.
  • 환불할 때 1531만 취소하면 1532는 결제된 채로 남습니다. 카드는 한 번 긁혔는데 DB는 반만 취소된 상태.
  • 단건 주문 105줄은 order_group_id가 비어 있습니다. 그래서 "결제 1건"을 가리키는 값이 — 단건이면 order_id, 묶음이면 order_group_id — 칸 자체가 두 개입니다.
고치는 법
개선안

영수증처럼 2층으로 나눕니다. 위층은 "결제 1건"(총액·결제수단·결제시각), 아래층은 그 안의 "품목"들. 결제는 무조건 1줄이고, 품목은 1개든 5개든 아래에 붙습니다.

그러면 결제 세는 방법이 한 가지가 되고, 환불은 위층 1줄만 건드리면 되고, 품목이 3개로 늘어도 구조가 안 바뀝니다. 다음 장에서 이 변화를 나란히 보겠습니다.

3같은 사건을 두 구조에 넣어보면

위에 나온 그 결제(이** 님, 25,000원, 녹화본+교보재)를 현재 구조와 개선안에 각각 넣어봤습니다. 같은 사건인데 모양이 이렇게 다릅니다. 왼쪽이 지금, 오른쪽이 개선안입니다.

사건 1. 한 사람이 한 번 결제했다 (녹화본 20,000 + 교보재 5,000)
현재 — 주문 표에 2줄
recording_orders
order_idamountgroup
…-1531-7HJZ8H20,000…-1531-7HJZ8H
…-1532-G954PF5,000…-1531-7HJZ8H

총액 25,000원은 어디에도 안 적혀 있습니다. 더해서 알아내야 합니다.

개선안 — 결제 1줄 + 품목 2줄
v2_payments
payment_code⌄statusamount_total
…-1531-7HJZ8Hpaid25,000

↓

v2_order_items
품목paymentamount
녹화본PAY …153120,000
교보재PAY …15315,000

결제는 항상 1줄. 품목이 몇 개든 구조가 같습니다.

사건 2. 그 사람이 "교보재를 받을 수 있는 사람"이 되었다
현재 — 주문줄의 체크박스를 켠다
recording_orders
order_id☑material_granted_at
…-1532-G954PF☑10-02 11:50

결제 기록이 나중에 고쳐집니다. 그러면 이 줄은 "결제했을 때의 기록"이 아니라 "지금 상태"가 돼서, 나중에 보면 그때 무엇을 샀는지 확인할 수 없습니다. 권한 종류가 늘면 칸이 또 늘어납니다(attended_live, material_granted, …).

개선안 — 권한 표에 1줄을 더한다
v2_entitlements
key⌄kindperson왜 생겼나
E material P458materialP458 이**#1532 교보재

기록은 쌓이기만 합니다. 권한 종류가 늘어도 칸이 아니라 줄이 늘어납니다.

사건 3. 오프라인 수업 장소가 "독산역 디지털엠파이어 2층"이다
현재 — 커리큘럼 문단 안에 섞여 있다
13:00~13:15  오프닝 — 에이전트 작동 시연
13:15~13:45  에이전트 개념 — 챗봇과 무엇이…
   …(중략)…
17:40~18:00  체크리스트 + Q&A

[준비물] 노트북 필수 · 해외결제 카드 필수
[별도 비용] 클라우드 서버 연 10달러
[장소] 독산역 디지털엠파이어 빌딩 2층
[운영] 강사 1명 + 코치 2명

장소가 칸이 아니라 글 속 한 줄입니다. "장소가 정해진 수업만 보여줘"가 안 되고, 문자에 장소를 넣으려면 글에서 잘라내야 합니다.

개선안 — 각자 자기 칸으로
v2_courses
AvenueAitems_to_bringAextra_cost_note
독산역 디지털엠파이어 2층노트북 · 해외결제 카드서버 연 10달러

문자 발송·안내 페이지·검색이 전부 칸 하나 읽기로 끝납니다. 커리큘럼은 커리큘럼만 담습니다.

세어보면현재개선안
표 개수1214
한 사람의 정보가 적혀 있는 곳2곳1곳
"결제 1건"을 가리키는 방법2가지1가지
공개 여부를 정하는 칸3개1개
거래 기록을 나중에 수정하는 칸5개0개
값이 틀렸거나 아무도 안 쓰는 칸2개0개
같은 값을 또 적는 칸4개0개 (1개는 의도적 유지)

표 개수가 늘어난 게 핵심입니다. 좋은 구조는 표가 적은 게 아니라, 한 표가 한 가지만 담는 것이었습니다.

4나머지 8건 — 한 줄씩

같은 방식으로 찾은 나머지입니다. 전부 실제 데이터로 숫자를 확인했습니다.

  1. 🟡수강생이 전화번호를 바꾸면, 예전 주문에는 옛 번호가 그대로 남습니다이름·전화·이메일이 주문 줄에도 적혀 있고 회원 표에도 또 적혀 있습니다. 두 곳을 같이 고쳐 주는 장치가 없어서 회원 표만 바뀌고 주문 줄은 그대로입니다. 그러면 어느 쪽이 지금 맞는 번호인지 확인할 방법이 없습니다.회원 71명 중 주문이 있는 사람 49명 · 반대로 계정 없이 수동등록된 구매자 8명은 회원 표에 아예 없습니다
  2. 🟡가격 종류를 두 군데에 적어 둬서, 한쪽만 고치면 값이 어긋납니다주문 줄의 price_track(가격 종류)은 product_id(상품)만 보면 알 수 있는 값입니다. 상품 표에서 꺼내 읽으면 되는데 주문 줄에 또 적어 뒀습니다.주문 133건 전수 대조 — 불일치 0건. 즉 100% 중복이고, 언젠가 한쪽만 고쳐지면 그날이 사고입니다
  3. 🟡문자 종류를 하나 늘릴 때마다 표에 칸을 새로 만들어야 합니다"구매 안내 보냄", "영상 준비됨 보냄", "리마인더 보냄", "녹화본 판매안내 보냄" — 문자 종류가 하나 늘 때마다 칸을 하나 추가해 왔습니다.개선안은 칸이 아니라 줄을 늘립니다(발송 기록 표). 새 문자가 생겨도 구조가 안 바뀝니다
  4. 🟡회원 표의 instructor_id는 "담당 강사"로 읽히는데, 뜻이 정반대입니다이름만 보면 "이 회원의 담당 강사"처럼 읽힙니다. 실제 뜻은 "이 회원이 바로 그 강사"였습니다.71명 중 채워진 건 4명 = 강사 본인들. 이름만 보고 "담당 강사"로 쓰면 틀린 코드가 나옵니다
  5. 🟡장소·준비물이 칸이 아니라 설명 글 안에 있어서, 따로 꺼내 쓸 수 없습니다장소·준비물·별도비용이 커리큘럼 글 안 [태그]로 들어 있습니다(3장 사건 3). 반대로 최소인원 칸은 온라인 강의 4개에도 달려 있어서 의미가 없습니다.칸이 있어야 할 것은 글 안에, 없어도 될 것은 칸으로 — 정확히 반대였습니다
  6. 🟡pending은 "입금 대기"가 아니라 "결제창에서 그냥 나감"입니다이름만 보면 돈이 들어오기를 기다리는 상태로 읽힙니다. 실제로는 결제창까지 갔다가 그냥 나간 흔적이라, 아무리 기다려도 입금은 오지 않습니다.주문 133건 중 42건(32%)이 이 상태. 개선안에서는 initiated(시작됨)로 개명 — 지우지는 않습니다. "살 뻔한 사람" 명단이라서요
  7. 🟡이미 끝난 결제 기록을, 운영하면서 계속 고치고 있습니다"라이브 참석함", "교보재 받음"을 주문 줄에 체크로 표시합니다. 그래서 지나간 결제 기록이 운영 중에 계속 바뀝니다.결제 기록은 한 번 적으면 더 고치지 않는 게 원칙입니다. 개선안은 권한을 별도 표에 쌓습니다(3장 사건 2)
  8. 🟢중복처럼 보이는 연결 3개 중 하나는, 지우면 질문 기능이 깨집니다"어느 강의인가"가 세 표에 또 적혀 있어서 지우려고 봤더니, 질문 표는 주문이 없어도 질문이 생깁니다. 거기서는 그 칸이 유일한 경로였습니다.실제로 질문 2건 모두 주문 연결이 비어 있고 강의 연결만 있었습니다. "중복은 전부 제거"라는 규칙을 그대로 밀면 기능이 깨지는 반례

5그럼 다 고치나 — 아니요, 1단계만

문제를 12개 찾았다고 12개를 고치는 게 아닙니다. 지금 돌아가는 사이트라서, 고치다 멈추면 결제가 멈춥니다. 난이도로 3단계로 나눴습니다.

1단계 · 이름 바꾸기와 안 쓰는 칸 지우기 — 지금 합니다
데이터 이동 없음 · 30분 규모
  • is_admin 삭제 (아무도 안 읽는 것 확인됨)
  • content_status 삭제
  • order_id → 글자는 order_code, 연결은 recording_order_id
  • instructor_id → linked_instructor_id
  • pending → initiated
2단계 · 중복 칸 제거 — 다음 기수 준비할 때
데이터 옮기기 필요 · 줄 구조는 그대로
  • price_track을 상품 표에서 읽어오기로 전환
  • 공개 칸 3개 → visibility 1개
  • 장소·준비물·별도비용을 글에서 칸으로 꺼내기
3단계 · 표 나누기 — 아직 안 합니다
큰 수술 · 사람·결제·권한·발송을 각각 독립 표로
  • 지금 사업 데이터가 280줄입니다. 이 규모에서는 과설계입니다
  • 수강생이 수백 명 단위가 되거나, 환불·부분취소가 잦아지면 그때
왜 1단계부터인가. 이름 문제는 시간이 갈수록 고치는 비용만 오릅니다. 그 이름을 쓰는 코드와 사람의 습관이 계속 늘어나니까요. 반대로 표를 나누는 건 지금 해도 이득이 거의 없습니다 — 280줄은 어떻게 담아도 잘 돌아갑니다.

그리고 1단계에는 교재가 경고한 함정이 정확히 들어 있습니다 — "필드 이름을 손으로 바꾸면 사이트가 멈춘다." 코드가 그 이름으로 값을 찾고 있기 때문입니다. 그래서 이름과 코드를 같은 커밋에서 같이 바꿉니다.

6에어테이블로 옮겨서 알게 된 것

솔직한 질문을 스스로 던졌습니다 — 이 12건, 에어테이블 없이 그냥 들여다봐도 알 수 있었나? 답은 반반입니다.

12건 중 8건은 — 네, 쿼리 몇 줄로 찾을 수 있었습니다

"is_admin이 true인 사람 몇 명?" "price_track과 상품이 다른 줄 있나?" 같은 질문은 원래 DB에서도 1분이면 세어 봅니다. 도구가 알려준 게 아닙니다.

그런데 4건은 — 옮기는 행위 자체가 드러냈습니다

① 칸 161개 전부에 설명을 써야 했습니다. 그러니까 칸마다 "이거 왜 있지?"를 한 번씩 물은 셈입니다. 그러다 설명이 안 써지는 칸이 나왔습니다 — is_admin, content_status. 설명을 쓸 수 없는 칸이 곧 죽은 칸이었습니다.

② 연결 19개를 손으로 하나씩 걸어야 했습니다. "이게 뭘 가리키나"를 적다 보니 글자 order_id와 숫자 order_id가 나란히 놓였습니다. 평소 쿼리를 쓸 때는 쓰는 쪽만 보니까 둘을 같이 볼 일이 없었습니다.

③ 에어테이블은 연결 칸에 상대 표의 첫 칸 값을 보여줍니다. 제 DB의 첫 칸은 전부 1, 133 같은 번호라서, 연결을 걸어 놓으니 아무것도 읽을 수 없었습니다. 그래서 표마다 key 칸을 새로 만들었습니다. 기계가 식별하는 값과 사람이 식별하는 값은 다른 것 — 이 질문을 해 본 적이 없었습니다.

④ 무료 1,000행 한도에 부딪혀서 알았습니다 — 전체 1,747줄 중 84%가 클릭 기록이었습니다. 한도라는 제약이 비율을 보게 만들었습니다.

정리하면 — 도구가 답을 준 게 아니라, 질문을 강제했습니다

쿼리는 의심이 가는 곳만 들여다봅니다. 옮기기는 161칸 전부를 지나가야 끝납니다. 평소에 코드를 읽을 때는 쓰는 칸만 봅니다. 안 쓰는 칸은 영원히 안 보입니다. 그 차이에서 4건이 나왔습니다.

교재가 "필드마다 설명 쓰기"를 유난히 강조한 이유를 여기서 알았습니다. 설명은 남을 위한 친절이 아니라, 내 구조를 검사하는 도구였습니다.

그리고 가장 크게 배운 것

"잘 돌아가는 것"과 "잘 설계된 것"은 다릅니다. 이 사이트는 결제도 되고 문자도 가고 정산도 됩니다. 그런데 안에는 값이 전부 틀린 칸과, 두 가지 뜻으로 쓰이는 이름이 있었습니다. 돌아가니까 안 봤던 것입니다.