충돌은 이 브랜치의 주석 정리 커밋(11d30bb)과 main 의 기능 변경이 겹친 백엔드 9개 파일 —
main 쪽을 그대로 썼다(기능이 main 에만 있다). AGENTS.md 는 양쪽 링크를 모두 두고,
DEVLOG 는 요약본 위에 main 의 09-28 에이전트 항목 셋을 붙였다.
site eslint 통과 · vitest 123 passed · frontend tsc·eslint 통과 · 백엔드 py_compile 통과
(백엔드 pytest 는 이 기계에 테스트 DB 계정이 없어 실행하지 못함)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
255 lines
21 KiB
Markdown
255 lines
21 KiB
Markdown
# 개발 일지
|
|
|
|
무엇을 왜 바꿨는지 날짜순(새 것이 위)으로 요약한다. 결론·배경은 각 문서가 단일 출처고, 여기에는
|
|
**나중에 같은 실수를 막아 주는 것**(결정의 이유·밟은 함정·실측값)만 남긴다.
|
|
2026-09-29에 요약본으로 다시 썼다. 원문 전체는 git 히스토리(이 파일의 09-29 이전 버전)에 있다.
|
|
|
|
## 2026-09-30 — 템플릿 검수 · 코랄 · 미니멀 · 솔숲 추가
|
|
|
|
- 한국 펜션 사이트(코랄트리 · 바다동화)를 참고해 `coral` · `minimal`, 디자인 스킬 시안에서 `pine`(솔숲).
|
|
- 화면 규칙을 [TEMPLATE_DESIGN.md](TEMPLATE_DESIGN.md) 로 뽑았다 — 섹션은 **제목 위 · 내용 아래**,
|
|
‘가능’은 ✓ 목록, 같은 탭을 다시 눌러도 맨 위로, 제목↔설명↔내용 간격.
|
|
- ★ 좌우 분할(제목 왼쪽 · 내용 오른쪽)은 내용이 한두 줄이면 왼쪽이 텅 비어 버렸다.
|
|
- ★ 템플릿 넷이 같은 Noto Sans KR 이라 다 비슷해 보였다 — 한글 제목 글꼴을 템플릿마다 다르게 배정.
|
|
- 간격 검사는 제목 위 여백만 보고 있어서 **설명↔내용 0px** 을 놓쳤다. 형제 요소 사이 간격을 전부 재도록 바꿨다.
|
|
|
|
## 2026-09-28 — 한 발화에 여러 가지 (+ 실배포에서 잡은 인자 버그)
|
|
|
|
**① 인자가 모델에 닿지 않던 것** — 배포 후 실모델로 찍어 보고 잡았다. 도구 선택은 6/6
|
|
정확했는데 `move_section` 이 `{name,to}` 를 받는데 응답 스키마에 그 칸이 없어 `{key,value}` 로
|
|
왔다. **새 도구 다섯이 전부 "못 찾았어요" 로 끝나는 상태**였고, 단위 테스트는 `_choose` 를
|
|
monkeypatch 해서 그 층을 건너뛰니 전부 초록이었다.
|
|
→ 스키마에 `name·to·enabled` 추가 + `test_도구가_선언한_인자는_응답_스키마에_있다` 로 소스 대조.
|
|
→ **교훈: 도구를 늘리면 실모델로 한 번 찍어 봐야 한다.** 단위 테스트가 초록인 것과 실제로
|
|
도는 것은 다르다(DEVLOG 2026-09-17 의 죽은 import 건과 같은 종류다).
|
|
|
|
**② 한 발화에 여러 가지** — 응답을 `actions` 배열로 바꾸고 순서대로 실행한다.
|
|
- `publish`(SEMI)가 섞이면 **그 앞까지만** 하고 확인을 받는다 — 확인이 필요한 행위를
|
|
다른 일에 묻어 실행하면 확인의 의미가 없다.
|
|
- 중간에 실패하면 **앞의 것을 되돌리지 않는다**(사장님 결정). 되돌리는 것도 시키지 않은
|
|
변경이다. 대신 무엇이 됐고 무엇이 안 됐는지 그대로 말한다.
|
|
- 재발행 안내는 `Tool.republish` 플래그로 옮겨 **런타임이 한 번만** 붙인다. 도구 문장에
|
|
박아 두면 셋을 고쳤을 때 같은 말이 세 번 나왔다.
|
|
- 상한 5개 — 무한정이면 "다 지워줘" 한 마디에 연쇄 실행된다.
|
|
|
|
**검증** — 실모델 4/4 정확히 쪼갬(킹서버 기준 1.8~2.8초):
|
|
`"체크인 3시로 바꾸고 후기도 빼줘"` → `[set_fact, toggle_section]`,
|
|
`"소개 맨 위로 올리고 발행까지"` → `[move_section, publish]`.
|
|
`test_agent_runtime`·`test_kakao_webhook` 63 passed. 전체 `878 passed / 53 failed`(기존과 동일).
|
|
|
|
## 2026-09-28 — 에이전트가 사진을 내리고 대표를 지정한다
|
|
|
|
사진 쪽은 `MediaService` 에 `list_media` 하나뿐이었다 — **쓰기 경로가 아예 없었다.**
|
|
빌더 화면에서도 보기만 됐다. 그래서 서비스·라우터부터 열고 도구를 붙였다.
|
|
|
|
- `crud/media_crud.set_sort_order` · `services/media_service.hide_media`·`set_primary`
|
|
- `POST .../media/{id}/hide` · `/primary` — ★ 에이전트 전용 뒷문을 만들지 않는다.
|
|
그러면 빌더 화면이 그 기능을 못 쓰고 나중에 붙일 때 로직이 두 벌이 된다
|
|
- 도구 셋: `list_photos`(READ) · `hide_photo` · `set_primary_photo`(REVERSIBLE)
|
|
|
|
**★ 대표 사진에 별도 칸을 두지 않았다.** `site_payload.primary_media` 가 '첫 장' 을 쓰고
|
|
목록이 `ORDER BY sort_order, created_at` 이라, 지정은 `sort_order` 를 가장 작게 내리는 일이다.
|
|
칸을 따로 두면 규칙이 둘이 되어 **검색 결과에 뜨는 그림과 화면 첫 장이 갈린다.**
|
|
|
|
**★ 내려도 지우지 않는다**(`REJECTED`). `origin_url`·`source_type` 이 남아야 재게시 권리
|
|
(DECISIONS 1-2) 결론이 났을 때 되짚을 수 있다.
|
|
|
|
**★★ 업로드·교체는 만들지 않았다.** 미결 사항(1-2)을 코드가 먼저 푸는 자리다 —
|
|
테스트가 레지스트리에 `upload`·`replace` 가 없는지 실제로 검사한다.
|
|
|
|
**검증** — `test_agent_runtime` 32 passed(사진 6건 추가). 전체 `871 passed / 53 failed` 이고
|
|
그 53 은 이번 변경 전과 같다. `npm run lint` 통과.
|
|
|
|
## 2026-09-28 — 에이전트가 페이지 구성을 바꾼다 (섹션 on/off · 순서)
|
|
|
|
"문구 변경밖에 안 된다" 는 지적에서 시작했다. 페이지 구성은 `sites.theme.sections` 배열
|
|
하나이고 **배열 순서가 곧 발행본의 순서**라, 그 JSON 을 만지는 도구 셋을 붙였다.
|
|
|
|
- `list_sections`(READ) · `toggle_section`(REVERSIBLE) · `move_section`(REVERSIBLE)
|
|
- 목록은 `site_payload._sections` 를 그대로 쓴다 — 발행본이 쓰는 그 함수다.
|
|
표를 따로 만들면 에디터·발행본·대화 셋이 갈라진다.
|
|
- 잠긴 섹션은 못 끈다. `_sections` 가 어차피 켜서 내보내므로 끌 수 있게 두면 화면만 거짓말한다.
|
|
- 이름이 둘 이상 걸리면 고르지 않는다. 추측으로 고르면 발행하고 나서야 안다.
|
|
- `sections` 만 갈아끼운다 — theme 을 통째로 쓰면 고른 색·서체가 말없이 사라진다.
|
|
|
|
**★ 템플릿·색은 넣지 않았다(요청 범위였으나 선행 작업이 필요하다).**
|
|
템플릿 목록은 `frontend/src/data/industryData.ts` 의 `templatesFor()` 가 **생성**하고,
|
|
색·`look`·`defaultSectionTypes`·`defaultVariants` 를 함께 계산한다(`stores/builder.selectTemplate`
|
|
가 섹션 on/off 와 배리에이션까지 바꾼다). 백엔드가 `template_id` 만 바꾸면 색은 옛것이 남아
|
|
**"레이아웃은 새것, 색은 옛것"** 이 된다 — 이 레포가 반복해 경고하는 '두 곳에 같은 표' 함정이다.
|
|
하려면 그 레지스트리를 공유 단일 출처로 옮기는 작업이 먼저다.
|
|
|
|
**검증** — `test_agent_runtime` 26 passed(구성 7건 추가). 전체 `864 passed / 53 failed` 이고
|
|
그 53 은 이번 변경 전과 같다.
|
|
|
|
---
|
|
|
|
## 2026-09-28 — 숙박 템플릿 다섯 개 추가 (라운드 · 시네마 · 빅타이포 · 부티크 · 일러스트)
|
|
|
|
국내 펜션 사이트 46곳을 모바일에서 재 보니 첫 화면 제목 14~24px, 본문 11~14px였다. 기존 템플릿도
|
|
전부 작은 글씨 쪽이라, 토스·카카오뱅크·당근·해든스테이·스테이인터뷰를 390px에서 실측해 뼈대를 새로 만들었다.
|
|
|
|
- `site/src/layouts/` 에 `round` `cinema` `bigtype` `boutique` `graphic`. 섹션·탭 네 개는 고택과 같고
|
|
예약 시트·폼·캐러셀은 고택 부품을 쓴다. 공통 구조 CSS는 `layouts/kit/kit.css`.
|
|
- ★ kit.css 에 고택의 글꼴 규칙을 넣지 않는다 — 넣으면 다른 레이아웃의 워드마크가 17.5px로 눌린다(실측).
|
|
- `graphic` 은 사진이 거의 없는 집용. 사진 0장이면 기존 발행 게이트("고유 콘텐츠 0건")에 걸린다.
|
|
|
|
## 2026-09-28 — 템플릿 정의를 한 파일로 모았다
|
|
|
|
빌더·렌더러·백엔드가 템플릿을 따로 적어 서로 어긋나 있었다(없는 기본 id, 강조색 오타, 섹션 간격 차이).
|
|
|
|
- 템플릿 목록은 `shared/src/data/templates.json` 하나. TS와 파이썬이 같은 파일을 읽는다.
|
|
- id에서 업종을 뗐다(`stay-retro` → `retro`, 마이그레이션 `0023`, 운영 미적용).
|
|
- 모르는 템플릿 id는 저장·미리보기·발행 모두 거절한다. 기본값으로 슬쩍 굽지 않는다.
|
|
- 고택(`paper`)을 `/s/stay2` 시안과 같게 다시 만들었다. 하위 페이지는 한 HTML 안의 탭이다.
|
|
- 구조와 추가 방법: [TEMPLATES.md](TEMPLATES.md).
|
|
|
|
## 2026-09-23 — 개발자용 사이트·유저 관리를 solution 앱에 얹었다
|
|
|
|
admin 앱을 키우기엔 이르다(대표 지시). `UserRole.DEVELOPER` 게이트로 `/ops/sites`·`/ops/users`(읽기 전용).
|
|
★ 메뉴 문자열은 사장님 번들에도 실린다(런타임 조건부 렌더) — 데이터는 백엔드 게이트가 막는다.
|
|
|
|
## 2026-09-21 ~ 22 — 사장님 에이전트 (빌더 대화창 → 카카오톡 채널)
|
|
|
|
설계와 함정은 [AGENT.md](AGENT.md)와 AGENTS.md "에이전트에서 조용히 틀리는 것"이 단일 출처다.
|
|
|
|
- 순서: 신원 연결(`owner_kakao_links`) → 도구 레지스트리·런타임·빌더 채팅창 → 카카오 웹훅.
|
|
런타임이 채널을 모르게 만들어 두어, 웹훅을 붙일 때 런타임은 한 줄도 안 바뀌었다.
|
|
- 모델에게 맡기지 않은 셋: 확인 등급, 결과 문구, fact key. 확인(SEMI)은 서버가 인자를 다시 검증한다.
|
|
- ★ 오픈빌더는 서명이 없다 — 공유 시크릿이 유일한 문이고, 없으면 엔드포인트가 404.
|
|
- ★ 카톡 5초 벽: 개발 중 잰 1.3~2.4초는 장난감 프롬프트였고 실사용 첫날 타임아웃이 났다.
|
|
콜백(`useCallback`)으로 즉답 후 따로 보낸다. 오픈빌더 스킬 설정에서 콜백을 켜야 이 경로가 열린다.
|
|
- 카톡 대화에는 홈페이지 목록·발행 여부·가게 바꾸기를 LLM 없이 보여 준다(대화가 막혔을 때 늘 통해야 한다).
|
|
- 밟은 것: `execute_lambda` 는 람다 반환값을 그대로 준다 — 객체만 돌려주면 언패킹 TypeError 가 나는데
|
|
라우터가 예외를 삼켜 "지금은 처리할 수 없어요"만 보였다.
|
|
|
|
## 2026-09-17 — 미니 블로그: 팀 검수 폐지 · 배정일 · 달력 화면 · 메일 승인
|
|
|
|
상세는 [MINI_BLOG.md](MINI_BLOG.md).
|
|
|
|
- 팀 사전검수를 없애고 최종 판단을 사장님에게 넘겼다(팀 단계가 병목이었다). 업장당 하루 한 통.
|
|
- `place_posts.scheduled_date` 로 글마다 날짜를 정했다. 빌더는 달력 + 그 위 일주일치 카로셀, 생성 이력 탭.
|
|
- 메일 승인 링크는 GET 즉시 승인(프리페치 위험을 알고 사장님이 택했다), 링크는 그날 자정 만료.
|
|
- 잡은 버그들:
|
|
- 승인이 BUILD 잡에 `owner_user_id` 를 안 실어 **메일 승인이 재발행을 못 하고 있었다**.
|
|
- `generate_one` 의 죽은 import 로 "지금 생성하기"가 500. 테스트는 그 함수를 monkeypatch 해서 초록이었다 —
|
|
단위 테스트 초록과 실제로 도는 것은 다르다.
|
|
- `scheduled_date` 를 추가하자 값이 NULL인 기존 글 13건이 조회에서 조용히 빠졌다 → 백필.
|
|
새 컬럼 마이그레이션은 "기존 행이 조회에서 빠지는지"부터 본다.
|
|
- raw `text()` 로 timestamptz 에 naive datetime 을 넣으면 드라이버 로컬 시간대(KST)로 9시간 밀린다.
|
|
- 세션 복구보다 늦게 자동 로그인하면 `RequireAuth` 가 이미 `/login` 으로 튕긴다 → 복구 단계로 옮겼다.
|
|
- ORM 객체를 commit 뒤까지 들고 있으면 detached 로 깨진다 → flush 직후 dict 로 뽑는다.
|
|
|
|
## 2026-09-16 — 생성·수집 실패를 잡이 견디게
|
|
|
|
- Gemini 호출 실패(429 등)가 온보딩 COPY 잡을 DEAD 로 보내지 않는다. fact 만으로 계속한다 —
|
|
키가 없을 때와 같은 동작. 실측: 사진분석 배치가 분당 쿼터를 다 써서 같은 키의 COPY 가 죽었다.
|
|
- 재수집 때 나는 유니크 충돌 로그를 ERROR → WARN(정상 경로인데 오류처럼 보였다).
|
|
- 크롤링 실패를 `jobs.result` 에 구조화해 남긴다(`common/collect_diagnostics.py`).
|
|
- Teams 웹훅: 플로우 수신자가 예약값(`48:notes`)이라 계속 실패 → 플로우 재생성으로 해결.
|
|
|
|
## 2026-09-15 — 발행 버전 전환 · 장애 알림 · 보안 · 서치콘솔 · 생성 진행 복구
|
|
|
|
- **워커가 렌더하고 버전별로 보관, 게이트 통과 뒤 공개 링크를 바꾼다.** 상시 프리렌더를 없앴다.
|
|
배포는 기존 HTML과 목업을 다시 굽지 않는다 → [PUBLISH_VERSION.md](PUBLISH_VERSION.md).
|
|
- 장애 알림: `alert_outbox` + 재시도·중복 억제·복구 알림, `/readyz`(DB까지 확인) → [ALERTS.md](ALERTS.md).
|
|
★ 앱·DB 시계가 수십 ms만 어긋나도 방금 넣은 알림이 안 잡혔다 → 비교는 DB 시계(`func.now()`).
|
|
★ HTTP 202 는 워크플로 접수일 뿐 채널 게시 성공이 아니다.
|
|
- 운영 번들에서 자동 로그인 자격증명 제거(build arg 삭제 + `import.meta.env.DEV` 가드).
|
|
`users.token_version` 으로 비밀번호 변경 시 기존 refresh 토큰을 무효화한다(전에는 7일간 계속 통했다).
|
|
- Google 사이트맵 자동 제출·색인 관측 → [SEARCH_CONSOLE.md](SEARCH_CONSOLE.md).
|
|
- 콘텐츠 생성 단계 상태를 DB에 기록, URL의 jobId로 새로고침 복구 → [GENERATION_FLOW.md](GENERATION_FLOW.md).
|
|
|
|
## 2026-09-14 — SNS 게재 · 엽서 · FAQ 20개 · SEO 키워드
|
|
|
|
- **SNS(스레드) 게재**: 사장님 클릭 → fact로 초안 → 승인 → 사장님 계정으로 게시. 이 레포가 처음으로
|
|
외부에 쓰고, 남의 자격증명을 보관하고, 되돌릴 수 없는 일을 한다. 설계는 [SOCIAL.md](SOCIAL.md).
|
|
승인은 POST만, 주소가 확정된(`domain`) 사이트만, 사진은 올리지 않는다, 기본 꺼짐.
|
|
X는 URL 글 요청당 $0.20이라 뺐다.
|
|
★ `server_default=text("'[]',")` 쉼표가 CREATE TABLE 을 통째로 실패시켰다(테스트 DB에서만 드러난다).
|
|
- **엽서 쓰기**를 발행본에 넣었다. ★ 남의 도메인 사진을 캔버스에 그리면 오염돼 저장·공유가 막힌다
|
|
(네이버 CDN은 CORS를 안 준다) → 발행 때 사진을 우리 오리진으로 내려받는 미러로 해결(AGENTS.md).
|
|
- **FAQ를 20개까지**: fact로 쓰면 4~8개에서 끝나서, 펜션 공통 질문 카탈로그로 "문의 안내" 답을 채운다.
|
|
★ 공통 답에 값을 적지 않고, 이 답은 JSON-LD·llms.txt·고유 콘텐츠 계수에서 뺀다.
|
|
- **SiteOntology 키워드**를 제목·keywords 메타에 싣는다. ★ 추천 10건 중 사실이 아닌 것(마당·복층)이
|
|
섞여 와서 "모든 낱말이 이 가게 자료에 있어야" 싣는다(10건 → 4건). 모르는 regionId 는 저쪽이 500을 준다.
|
|
|
|
## 2026-09-11 — 발행하면 이 숙소의 노래가 생긴다 (가사 Gemini → 작곡 Suno)
|
|
|
|
- 발행이 노래를 기다린다(첫 화면에 기능이 빠져 보이지 않게). 실패해도 발행은 막지 않는다.
|
|
- 가사는 소개문과 같은 재료로 우리가 쓴다 — Suno에 맡기면 없는 시설을 노래한다.
|
|
- ★ Suno 주소는 만료된다 → mp3를 받아 우리 경로로만 내보낸다. 콜백이 아니라 폴링(우리 서버에 닿을 주소가 없다).
|
|
- 미리보기 빌드에는 만들지 않는다(유료 호출).
|
|
|
|
## 2026-09-10 — 소개문 승인 단계 제거 · 렌더러 이식 · 일력
|
|
|
|
- **생성된 소개문이 영영 안 나가던 것**: 소개문이 수집 확인 화면보다 2분 늦게 도착해 승인할 화면이 없었다.
|
|
LLM 출력은 확인된 fact로만 쓰므로 승인 없이 노출값으로 둔다. 사장님이 고친 문장은 LLM이 못 덮는다
|
|
→ [DECISIONS.md 7절](DECISIONS.md).
|
|
★ ORM의 timestamptz 기본값 `(now() AT TIME ZONE 'utc')` 가 값을 서버 시간대만큼 미래로 밀어
|
|
테스트 DB에서 잡이 영영 안 집혔다 → init.sql과 같은 `now()`.
|
|
- `/s/stay` 시안이 다른 워크트리의 **커밋 안 된 작업본**에만 있어 렌더러가 갈렸다 → 시안 payload를 현재
|
|
렌더러로 다시 구워 태그 단위 diff(129줄 → 4줄). 카카오 길찾기가 상호의 쉼표 때문에 목적지를 버리던 것도 고쳤다.
|
|
- 일력을 서버 생성에 붙였다. 종류 목록이 두 벌이라 새 종류가 서버에 안 갔고, "한 건이라도 있으면 안 부른다"
|
|
가드가 기존 지역에 새 종류를 영영 막았다 → 없는 종류만 부른다.
|
|
|
|
## 2026-09-09 — 지역 이야기 서버 생성 · 예약 목업
|
|
|
|
- 가요·인물·연표·엽서·퀴즈를 지역 단위로 한 번 생성해 같은 지역 사이트가 나눠 쓴다(Perplexity, 출처 필수)
|
|
→ [DECISIONS.md 6절](DECISIONS.md).
|
|
- 예약 흐름 목업(`StayBookingDemo`). 연동 없음 — "마감/잔여"를 지어내지 않고, 시간 후보는 체크인 fact에서만.
|
|
★ 날짜는 브라우저에서 만든다 — 서버에서 구우면 발행일 날짜가 HTML에 박혀 크롤러가 지난 날을 읽는다.
|
|
|
|
## 2026-09-08 — 가짜 발행 제거 · `/s` 정본 주소 · 회사(테넌트) 제거 · 네이버 예약
|
|
|
|
- **가짜 발행**: 사업장이 없으면 서버를 안 부르고 [사이트 열기]를 그렸다(주소는 404). 분기를 지우고
|
|
발행 불가 사유를 모달 안에서 말한다.
|
|
- **`/s` 가 빌더 셸을 200으로 주고 있었다** → nginx `location = /s` + `/s/` 301, `absolute_redirect off`.
|
|
- **회사 스코프를 걷어냈다** — 스코프 키는 `places.owner_user_id`, 주인은 토큰이 정한다(body로 받지 않는다).
|
|
워커의 `UserInfo.user_id` 는 사업장 주인이어야 한다(랜덤 uuid면 fact가 0건이 된다).
|
|
- **예약 버튼이 검색 화면을 열었다** → 플레이스 응답의 `naverBookingUrl` 을 수집해 쓴다. 주소를 조립하지 않는다.
|
|
- 내 사이트 목록에 썸네일·주소·시각. 썸네일 주소에 `?v=<버전>` 을 붙여 재발행하면 그림이 바뀌게 했다.
|
|
|
|
## 2026-09-07 — 자산 보관과 두 번의 사고 · 로컬 설정 함정 · 숙박 예약 안내
|
|
|
|
- **옛 해시 자산을 30일 남긴다**(대장 `.builds.json`, mtime 을 쓰지 않는다). 배포와 전체 재굽기를 뗐다.
|
|
- **사고 1**: 대장이 없는 첫 실행에서 기존 자산이 전부 "대장에 없음"으로 지워져 운영 CSS가 끊겼다.
|
|
→ 기록이 없으면 입양한다. "기록이 없다"와 "만료됐다"를 같이 묶지 않는다.
|
|
검증은 빈 디렉토리가 아니라 **배포 직전 서버 모습**으로 재현해야 했다.
|
|
- **사고 2**: payload 없는 목업(`stay`·`stay2`·`stay3`)의 자산이 지워져 영영 복구 불가가 됐다.
|
|
→ HTML이 참조하는 자산은 기간과 무관하게 남긴다(`referencedAssets`). 목업의 존재를 AGENTS.md 맨 위에 적었다.
|
|
- 사이트맵 lastmod 를 파일 mtime 에서 뗐다 — 배포마다 전 사이트가 "오늘 갱신"으로 통보되어 구글이 필드를 무시하게 된다.
|
|
- `.env.example` 함정: 컨테이너 안의 `DB_HOST=127.0.0.1`(워커만 조용히 재시작), 값 뒤 주석이 값이 됨,
|
|
API 기본 주소가 크로스 오리진을 만들어 로그인만 실패. → 같은 오리진 기본값, 주석은 윗줄로.
|
|
- 숙박 "실시간 예약" 섹션이 전화번호 한 줄이었다(읽는 fact가 숙박 스키마에 없었다) → "예약 안내"로 이름을 바꾸고
|
|
요금·인원·규정·창구를 모았다. 예약을 처리하지는 않는다. `availability` 는 넣지 않는다.
|
|
|
|
## 2026-09-03 — 레포·호스트 교체 · 랜딩 · 로그인 전 검색 · 썸네일
|
|
|
|
- 레포 `Web4ai/o2o-site-AEO`, 호스트 `web4ai.o2osolution.ai`.
|
|
★ `origin` 은 payload에 구워진다 → 재발행이 필요하다. ★ `init.sql` 은 최초 생성 때만 돈다 —
|
|
기존 DB에 컬럼이 없어 로그인이 죽었는데 HTTP는 200이었다.
|
|
- 로그인 전 랜딩·요금(월 70만원 한 플랜)·쇼케이스(진짜 발행본만). 상호 검색을 로그인 앞으로(인증 없음, IP 제한).
|
|
업종은 카카오 카테고리로 정하고 LLM을 부르지 않는다.
|
|
- 썸네일은 스크린샷이 아니라 대표 사진이다(헤드리스 브라우저는 영구 금지).
|
|
|
|
## 2026-09-02 — 가입·구글 로그인 · 내 사이트 홈 · 아이템 · 템플릿 모양(look)
|
|
|
|
- 계정 생성 API가 아예 없었다(손으로 INSERT). ★ `GOOGLE_CLIENT_ID` 는 백엔드·프론트가 같아야 하고
|
|
`aud` 대조가 남의 앱 토큰을 막는다. 같은 이메일이라도 계정을 자동으로 잇지 않는다([DECISIONS 1-5](DECISIONS.md)).
|
|
- 로그인한 사장님의 홈(`/sites`·`/account`). 위저드에서 사이드바를 뺐다. 삭제 대신 [발행 내리기]만 둔다.
|
|
- 붙여넣기 아이템이 발행본에 **하나도 안 나가고 있었다**(`SectionSetting.data` 계약이 없었다).
|
|
직접 쓴 소개문도 같은 이유로 사라졌다(`body`). 계약에 넣고 고유 콘텐츠로 센다.
|
|
- `SiteTheme.look`(서체·모서리·그림자 등)이 발행본까지 가게 했다. 웹폰트는 템플릿이 쓰는 것만.
|
|
- 계절 추천은 HTML에 전 계절을 굽고 브라우저에서 지금 계절만 보인다(구운 시점의 계절이 박히지 않게).
|
|
|
|
## 2026-09-01 — 설정을 `.env` 하나로
|
|
|
|
toml → `pydantic-settings`. 키마다 손으로 덮던 구조에서 `client_url` 이 빠져 배포 주소 API가 전부 CORS로 막혔다.
|
|
★ `.env` 경로가 없는 파일을 보고 있었다. 환경변수 이름은 `validation_alias` 로 못 박는다(`PORT` 를 주워 먹는다).
|
|
|
|
## 2026-08-31 — 킹서버 최초 배포
|
|
|
|
포트를 전부 `.env` 로 뺐다(`:80` 은 호스트 nginx가 쓴다) → [SERVERS.md](SERVERS.md).
|
|
★ `PUBLIC_API_BASE_URL` 은 브라우저가 부르는 주소다. ★ `deploy.sh api` 는 worker·api-admin 도 같이 갈아 끼운다.
|