타입체크 통과했는데 column does not exist — Supabase select의 함정
에이전트도 마이그레이션도 타입체크도 컬럼이 있다고 했다. 실제로는 없었고 화면이 통째로 죽었다.
타입체크가 세 앱 모두 0건으로 통과했다. 그런데 dev 서버를 열어 페이지에 들어가니 화면이 통째로 죽어 있었다.
column exhibitors_1.city does not exist
2026-08-10, 전시 상세 화면에 갤러리 홈페이지·인스타그램·오시는 길 링크를 아이콘으로 붙이는 작업을 하던 중이었다.
「있다」쪽을 가리킨 근거 셋
작업 전에 탐색 담당 에이전트에게 준비 상태를 물었다. 갤러리 테이블에 website, instagram, address, city 컬럼이 다 있다는 답이 왔다. 컬럼 목록을 표로 정리해서 줬다.
그래서 「오시는 길」을 만들 때 주소 앞에 도시를 붙이기로 했다. 서울 서울 종로구...처럼 겹치지 않도록, 주소에 도시 이름이 없을 때만 붙이는 함수까지 짰다.
의심스러워서 마이그레이션 파일도 직접 grep해 봤다. city를 다루는 SQL이 여러 개 나왔다. 컬럼을 업데이트하는 구문도 있고, city_edited_at 같은 짝 컬럼도 있었다.
그리고 타입체크가 0건이었다.
셋 다 「있다」쪽을 가리켰는데 셋 다 근거가 아니었다.
마이그레이션의 city SQL은 테이블 이름이 바뀌기 전 옛 테이블(galleries)의 것이었다. 지금 테이블에는 그 컬럼이 안 넘어왔다. 마이그레이션 파일은 지워지지 않으니 grep에는 영원히 걸린다.
타입체크가 통과한 이유가 더 고약했다. 이 프로젝트의 타입 정의는 손으로 쓴 파일이지 DB에서 만들어낸 게 아니다. 그 파일에도 city는 없었는데, Supabase 클라이언트의 select 문자열이 타입 수준에서 엄격하게 검사되지 않아서 그냥 지나갔다. 타입체크가 확인한 건 「내가 쓴 타입 정의와 내 코드가 맞나」였지 「DB에 그 컬럼이 있나」가 아니었다.
조용해서 위험했다
이 조합의 문제는 에러가 빌드에서 안 난다는 점이다. 화면을 실제로 열어야만 나온다.
그리고 그 화면은 통째로 안 뜬다. 아이콘 하나가 빠지는 게 아니라 전시 페이지 전체가 404가 된다.
그날 dev 서버를 안 열어봤으면, 커밋하고 배포한 뒤 관람객이 QR을 찍었을 때 전시 페이지가 안 뜨는 걸로 알게 됐을 것이다.
뭘 바꿨나
city를 select에서 뺐다. 지도 검색은 주소 하나로만 만든다. 실제 값을 열어 보니 서울 특별시 종로구 인사동 10길 10번지 인사1010 갤러리라서 도시를 붙일 이유도 없었다. 겹침 방지 함수도 필요 없었다.
코드에는 주석을 남겼다.
"city를 같이 읽으려다 뺐다 — 그런 컬럼이 없다. 타입 정의에도 없는데 마이그레이션에는 city를 다루는 SQL이 남아 있어 있는 것처럼 보인다(옛 테이블 것)."
다음에 같은 grep을 하는 사람이 또 속지 않게.
작업 방식도 둘 바뀌었다.
타입체크 통과를 「됐다」의 신호로 쓰지 않는다. DB 컬럼을 새로 읽는 코드는 dev 서버에서 그 화면을 실제로 열어 보기 전까지 끝난 게 아니다.
남이 준 조사 결과를 그대로 쓰지 않는다. 컬럼 목록처럼 실물이 답을 갖고 있는 것은 실물에 물어야 한다. 표로 정리돼서 오면 이미 확인된 사실처럼 보이는데, 그게 함정이었다.