From 139d46083920f0238687a47d550636a28e55162a Mon Sep 17 00:00:00 2001 From: Mina Choi Date: Mon, 31 Aug 2026 15:11:37 +0900 Subject: [PATCH] =?UTF-8?q?postgres-init:=20=EB=88=84=EC=A0=81=20ALTER=20?= =?UTF-8?q?=EB=A5=BC=20=EC=97=86=EC=95=A0=EA=B3=A0=20init.sql=20=ED=95=9C?= =?UTF-8?q?=20=EB=B2=8C=EB=A1=9C=20=EA=B0=84=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alters/*.sql 은 **기존 DB 를 보정**하는 파일이다. 그런데 이 프로젝트는 아직 git 에도 서버에도 올라가지 않아 보정할 기존 DB 가 없다. 파일마다 주석이 "신규 DB 는 init-data/init.sql 에 반영돼 있어 이 파일이 필요 없다"고 이미 적고 있었다. 지우기 전에 init.sql 이 여섯 개를 전부 담고 있는지 확인했다 — local_contents 발행 컬럼 4개, site template_id·theme, external_place_id/external_source, 그리고 allow-duplicate-places 가 지우는 두 인덱스가 애초에 없다는 것까지. 운영 DB 가 생기는 순간 이 디렉토리는 다시 필요해진다. 그때 되살린다. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_019uYhHQdssRubirPirrdJJC --- .../alters/2026-08-27-external-place-id.sql | 41 ------------------- .../2026-08-27-fact-candidate-model.sql | 33 --------------- .../2026-08-27-local-content-publishing.sql | 10 ----- .../alters/2026-08-27-site-template.sql | 12 ------ .../2026-08-28-allow-duplicate-places.sql | 3 -- .../alters/2026-08-28-site-theme.sql | 23 ----------- 6 files changed, 122 deletions(-) delete mode 100644 postgres-init/alters/2026-08-27-external-place-id.sql delete mode 100644 postgres-init/alters/2026-08-27-fact-candidate-model.sql delete mode 100644 postgres-init/alters/2026-08-27-local-content-publishing.sql delete mode 100644 postgres-init/alters/2026-08-27-site-template.sql delete mode 100644 postgres-init/alters/2026-08-28-allow-duplicate-places.sql delete mode 100644 postgres-init/alters/2026-08-28-site-theme.sql diff --git a/postgres-init/alters/2026-08-27-external-place-id.sql b/postgres-init/alters/2026-08-27-external-place-id.sql deleted file mode 100644 index a7acd9a..0000000 --- a/postgres-init/alters/2026-08-27-external-place-id.sql +++ /dev/null @@ -1,41 +0,0 @@ --- 사업장의 외부 장소 식별자를 카카오 전용에서 **소스 중립**으로 바꾼다. --- 신규 DB 는 init-data/init.sql 에 반영돼 있어 이 파일이 필요 없다(멱등이라 실행해도 무해). --- 적용: psql -h -p -U -f postgres-init/alters/2026-08-27-external-place-id.sql --- --- 왜 바꾸나 --- 카카오 REST 키가 없어 네이버 지역검색으로 동일 업소를 검증하는데, **네이버에는 카카오의 --- kakao_place_id 같은 안정적 고유 키가 없다**(응답의 link 는 업체 홈페이지지 플레이스 URL 이 아니다). --- 그대로 두면 네이버로 검증한 사업장은 external id 가 NULL 이라 중복 등록 방지 유니크가 걸리지 않고, --- 같은 가게를 두 번 등록해도 막히지 않는다. --- --- → 식별자 컬럼을 소스 중립으로 바꾸고(external_source + external_place_id), --- 고유 키가 없는 소스를 위해 **(회사, 상호명, 도로명주소)** 대체 유니크를 둔다. --- 도로명주소만으로는 안 된다 — 한 건물에 여러 가게가 있다. - -\connect web4ai_db - -ALTER TABLE place.places - RENAME COLUMN kakao_place_id TO external_place_id; - -ALTER TABLE place.places - ADD COLUMN IF NOT EXISTS external_source SMALLINT NULL; -- ExternalPlaceSource: 1=kakao 2=naver - --- 기존 값은 전부 카카오에서 온 것이다. -UPDATE place.places SET external_source = 1 - WHERE external_place_id IS NOT NULL AND external_source IS NULL; - -DROP INDEX IF EXISTS place.uq_places_company_kakao; - --- 고유 키가 있는 소스(카카오): 소스 + 외부 id 로 중복을 막는다. -CREATE UNIQUE INDEX IF NOT EXISTS uq_places_company_external - ON place.places (company_id, external_source, external_place_id) - WHERE deleted = FALSE AND external_place_id IS NOT NULL; - --- 고유 키가 없는 소스(네이버): 상호명 + 도로명주소로 막는다. --- ★ 도로명주소만 쓰면 안 된다 — 한 건물에 카페와 식당이 같이 있다. -CREATE UNIQUE INDEX IF NOT EXISTS uq_places_company_name_address - ON place.places (company_id, name, road_address) - WHERE deleted = FALSE AND road_address IS NOT NULL AND external_place_id IS NULL; - -CREATE INDEX IF NOT EXISTS idx_places_external ON place.places (external_source, external_place_id) - WHERE deleted = FALSE AND external_place_id IS NOT NULL; diff --git a/postgres-init/alters/2026-08-27-fact-candidate-model.sql b/postgres-init/alters/2026-08-27-fact-candidate-model.sql deleted file mode 100644 index 6aa9a06..0000000 --- a/postgres-init/alters/2026-08-27-fact-candidate-model.sql +++ /dev/null @@ -1,33 +0,0 @@ --- fact 를 '노출값 1건 + 후보 N건' 모델로 바꾼다 — 재수집(업데이트) 프로세스를 담기 위함. --- 신규 DB 는 init-data/init.sql 에 이미 반영돼 있어 이 파일이 필요 없다(멱등이라 실행해도 무해). --- 적용: psql -h -p -U -f postgres-init/alters/2026-08-27-fact-candidate-model.sql --- --- 왜 바꾸나 --- 기존: 활성 유니크가 status IN (1,2,3,4) 라 (사업장,단위,key) 당 살아있는 fact 가 1건뿐이었다. --- → 재수집이 오면 확인된 노출값을 반드시 밀어내야 했고, 그 순간 사이트에서 사실이 사라졌다. --- → 값이 그대로여도 검증(VERIFIED)이 초기화됐다. --- 변경: 유니크를 **노출 상태(3=VERIFIED, 4=CORRECTED)에만** 건다. --- → 사이트에 나가는 값은 여전히 1건(두 값으로 갈라지지 않는다) --- → 후보(1=UNVERIFIED, 2=PENDING_OWNER)는 여러 건 공존 가능 --- → 재수집은 노출값을 건드리지 않고 후보로 쌓이고, 사람이 승인할 때 교체된다 - -\connect web4ai_db - -DROP INDEX IF EXISTS fact.uq_facts_place_key; -DROP INDEX IF EXISTS fact.uq_facts_unit_key; - --- 노출값은 (사업장, 단위, key) 당 1건. 후보·이력은 제외한다. -CREATE UNIQUE INDEX IF NOT EXISTS uq_facts_published_place_key ON fact.facts (place_id, key) - WHERE deleted = FALSE AND unit_id IS NULL AND status IN (3, 4); -CREATE UNIQUE INDEX IF NOT EXISTS uq_facts_published_unit_key ON fact.facts (place_id, unit_id, key) - WHERE deleted = FALSE AND unit_id IS NOT NULL AND status IN (3, 4); - --- 후보 조회 경로(사람 확인 큐) — 재수집이 올려놓은 대기 항목을 훑는다. -CREATE INDEX IF NOT EXISTS idx_facts_candidate ON fact.facts (place_id, key, source_type) - WHERE deleted = FALSE AND status IN (1, 2); - --- 노출값이 실제로 바뀐 시각. ★ 개별 재빌드 대상 판별용 — --- site_versions.built_at < places.content_updated_at 인 사이트만 다시 빌드하면 된다. --- (사이트 1,000개에서 전체 재빌드는 못 쓴다.) -ALTER TABLE place.places - ADD COLUMN IF NOT EXISTS content_updated_at TIMESTAMPTZ NULL; diff --git a/postgres-init/alters/2026-08-27-local-content-publishing.sql b/postgres-init/alters/2026-08-27-local-content-publishing.sql deleted file mode 100644 index af6bc4d..0000000 --- a/postgres-init/alters/2026-08-27-local-content-publishing.sql +++ /dev/null @@ -1,10 +0,0 @@ -ALTER TABLE local.local_contents - ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 1, - ADD COLUMN IF NOT EXISTS published_at TIMESTAMPTZ NULL, - ADD COLUMN IF NOT EXISTS published_by uuid NULL, - ADD COLUMN IF NOT EXISTS display_start_at TIMESTAMPTZ NULL, - ADD COLUMN IF NOT EXISTS display_end_at TIMESTAMPTZ NULL; - -CREATE INDEX IF NOT EXISTS idx_local_contents_status - ON local.local_contents (status, collected_at DESC) - WHERE deleted = false; diff --git a/postgres-init/alters/2026-08-27-site-template.sql b/postgres-init/alters/2026-08-27-site-template.sql deleted file mode 100644 index 46d422f..0000000 --- a/postgres-init/alters/2026-08-27-site-template.sql +++ /dev/null @@ -1,12 +0,0 @@ --- 사이트 템플릿 저장. --- --- 위저드 4단계에서 사장님이 고른 템플릿이 브라우저 상태로만 남아 있었다. --- 그래서 발행 잡(services/site_payload)이 그 값을 읽을 곳이 없어 업종 기본 템플릿으로 --- 굽고 있었고, 사장님이 고른 디자인과 실제 발행본이 달랐다. --- --- template_id 는 프론트의 배리에이션 레지스트리 키다(예: 'stay-o2o-editorial'). --- 서버는 값을 해석하지 않고 그대로 보관·반환만 한다 — 템플릿 목록은 프론트가 소유한다. -ALTER TABLE site.sites ADD COLUMN IF NOT EXISTS template_id varchar(100); - -COMMENT ON COLUMN site.sites.template_id IS - '사장님이 고른 템플릿 키. NULL 이면 업종 기본 템플릿으로 굽는다.'; diff --git a/postgres-init/alters/2026-08-28-allow-duplicate-places.sql b/postgres-init/alters/2026-08-28-allow-duplicate-places.sql deleted file mode 100644 index 3b11f7d..0000000 --- a/postgres-init/alters/2026-08-28-allow-duplicate-places.sql +++ /dev/null @@ -1,3 +0,0 @@ --- 한 사용자가 같은 실제 업장을 여러 사업장 프로젝트로 등록할 수 있게 한다. -DROP INDEX IF EXISTS place.uq_places_company_external; -DROP INDEX IF EXISTS place.uq_places_company_name_address; diff --git a/postgres-init/alters/2026-08-28-site-theme.sql b/postgres-init/alters/2026-08-28-site-theme.sql deleted file mode 100644 index bdf39f1..0000000 --- a/postgres-init/alters/2026-08-28-site-theme.sql +++ /dev/null @@ -1,23 +0,0 @@ --- 사이트 테마(색·서체·섹션) 저장. --- --- 관리자 에디터에서 사장님이 바꿀 수 있는 디자인은 6가지다: --- 템플릿 · 섹션 on/off · 섹션 순서 · 섹션별 배리에이션 · 색 · 서체 --- 그중 저장되는 자리가 있던 건 template_id 하나뿐이었다. 나머지 다섯은 브라우저 메모리에만 있다가 --- 새로고침하면 사라졌고, 발행 잡(services/site_payload)은 읽을 곳이 없어 업종 기본 표를 그대로 구웠다. --- 그래서 사장님이 섹션을 끄고 순서를 바꿔도 발행본은 언제나 업종 기본 모양으로 나갔다 — --- template_id 컬럼이 생긴 것과 정확히 같은 이유이고, 남은 다섯을 담을 자리가 이 컬럼이다. --- --- ★ 왜 컬럼 5개가 아니라 jsonb 1개인가 --- 섹션 목록·배리에이션 키·색 토큰 이름은 전부 프론트(배리에이션 레지스트리)가 소유한다. --- 관계형 컬럼으로 펼치면 프론트가 섹션이나 색 토큰을 하나 늘릴 때마다 마이그레이션이 따라와야 한다. --- 서버는 이 값을 해석하지 않고 보관·반환만 하므로 통째로 담는 것이 맞다. --- --- ★ templateId 는 여기 넣지 않는다. --- 이미 sites.template_id 컬럼이 있다. 두 곳에 두면 어느 쪽이 진짜인지 갈리고, --- uq/조회가 걸린 쪽(컬럼)과 payload 가 읽는 쪽(jsonb)이 어긋나는 순간 화면과 발행본이 달라진다. -ALTER TABLE site.sites ADD COLUMN IF NOT EXISTS theme jsonb NULL; - -COMMENT ON COLUMN site.sites.theme IS - '에디터가 정한 디자인. {"colors":{...},"fontStyle":"...","sections":[{"id","name","enabled","locked","variantId"}]} ' - '— 서버는 해석하지 않고 그대로 보관·반환한다(목록은 프론트가 소유). ' - 'NULL 이면 발행 잡이 업종 기본 색·서체·섹션으로 굽는다. templateId 는 sites.template_id 가 소유한다.';