대니
danny's blog@dannywon_dev
발행 95 · 대기 163

1인 개발자. 클로드 코드를 팀처럼 굴린다. 매일 겪은 것만 쓴다.

제품 만들기

Supabase 인덱스 하나로 접었던 앱 합치기

「제약이 있으니 못 얹는다」고 문서에 적어뒀는데, 마이그레이션을 열어보니 그 제약은 얹으려는 테이블에 닿지도 않았다.

4일 전의 내가 "이건 안 된다"고 적어둔 문서를 다시 열 때가 있다. 나는 그 문장을 안 의심했다. 내가 쓴 거니까.

앱을 두 개 만들고 있다. 하나는 1년에 한 번 열리는 행사용 플랫폼(Next 15), 하나는 새로 짜는 출결·기록 앱(Next 16). 둘 다 Supabase를 쓴다. 2026-08-08에 "두 앱은 합치지 않는다"고 결정하고 문서에 적었다. 근거는 둘이었다.

  1. 재사용할 코드가 100줄 남짓밖에 안 된다
  2. 행사 플랫폼 DB에 「활성 행사는 항상 하나」 제약이 박혀 있어서, 학교 4곳이 동시에 도는 출결 앱을 얹으면 깨진다

8/12에 방향이 바뀌어서 다시 봤다. 이번엔 문서를 믿지 않고 실제 마이그레이션 파일을 열었다.

그 제약은 테이블 하나에만 걸려 있었다

create unique index if not exists events_single_active_idx
  on events (is_active) where is_active;

events 테이블 하나에 걸린 partial unique index다. 얹으려던 12개 테이블(schools, classes, students …)과는 아무 상관이 없었다. events 를 재사용해서 학교를 거기 욱여넣을 때만 걸리는 얘기인데, 애초에 그럴 계획도 아니었다. 표 이름도 하나도 안 겹쳤고(8개 ↔ 12개), 주소도 안 겹쳤다.

처음엔 "제약이 있다"는 사실만 보고 "그러니까 못 얹는다" 로 건너뛰었다. 그 사이에 있어야 할 질문 — "그 제약이 내가 얹으려는 것에 실제로 닿나?" — 을 안 했다.

진짜 장벽은 그날 문서에 한 줄도 없었다

합치면서 시간을 먹은 항목은 딱 하나였다. Next 버전이 15와 16으로 갈려서 await cookies() 부터 안 맞았다. 접을 때는 이게 안 보였고, DB 제약이라는 그럴듯한 이유 하나로 결론이 났다.

그리고 접은 근거가 문서에 박히면 그건 다음에 여는 사람에게 사실로 읽힌다. 내가 8/12에 그 문장을 안 의심했으면 그날도 안 합쳤을 것이다.

뭘 바꿨나

  • 결정을 뒤집고 합치기로 했다. 문서에서 지우지 않고 ~~접었던 근거~~ → 왜 틀렸나 로 남겼다. 지우면 같은 제안이 또 올라온다.
  • 진짜 장벽부터 해결했다. Next 15 → 16, React 18 → 19. 30분 걸렸다.
  • 접은 근거를 적을 때 "무엇이 무엇에 닿아서 깨지는지"까지 적기로 했다. "제약이 있다"가 아니라 "events.is_active 인덱스가 schools 를 막는다" 수준으로 적었으면, 적는 그 자리에서 틀린 걸 알았을 것이다.

덤으로, 같이 열어본 설정 파일에서 규칙 두 개가 더 낡아 있었다. "--turbopack 붙이면 SVG import가 깨진다" 는 Turbopack이 기본이 되면서 무의미해졌고, "next build 를 돌리지 마라 — dev와 .next 를 공유해서 CSS가 깨진다" 는 Next 16부터 출력이 .next/dev 로 갈리면서 없어진 얘기다.

접은 근거만 낡는 게 아니라 「하지 마라」도 같이 낡는다.

← 목록으로