대표 지적: "벽난로와 따뜻한 물이 있는 날씨입니다" 처럼 말이 성립하지 않는 문장.
확인해 보니 벽난로는 사진 설명이 전부 "벽난로 조명" 이라 장식이고, 테라스는
발행본 어디에도 근거가 없었다. 쓰지 않는 표현("실내로 도는", "묶어 도는",
"국물이 도는")과 펜션과 무관한 일반 안전 안내도 섞여 있었다.
- local.weather.noteSets 45줄 · tempNoteSets 25줄 전면 교체
- notes · tempNotes(한 줄짜리 기본값)도 같은 묶음 첫 줄로 맞춤
- 근거 없는 시설(벽난로 · 테라스 · 대문 안쪽 우산) 삭제
- 시각을 못 박던 줄("흐릿하게 보이는 아침입니다") 정리 — 문구는 하루 중 아무 때나 뜬다
- .gitignore: backup/ · king-stay2/ · build6p*/ · siann6/ · _sub*.mjs
(발행본 원본은 도커 볼륨이라 레포에 사본을 두면 원본이 둘이 된다)
검증: 70줄 중복 0 · 쓴 낱말 31개 전부 발행본에 존재 · 크롬 렌더 오류 0
킹서버 반영 완료(/s/stay e1c0d8ff, 이전본 index.html.bak-20260923 보관)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .serena | ||
| admin | ||
| docs | ||
| nginx | ||
| ontology | ||
| postgres-init | ||
| solution | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| deploy.sh | ||
| docker-compose.search-console.yml | ||
| docker-compose.yml | ||
| log.sh | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.base.json | ||
o2o-web4ai
상호명 하나로 소상공인 홈페이지를 만들어 발행하는 서비스.
목표는 예쁜 사이트가 아니다 — AI 검색과 검색엔진이 이 가게를 "공식 홈페이지 기준"으로 설명하게 만드는 것이다. 그래서 산출물은 SPA 가 아니라 크롤러가 그대로 읽는 정적 HTML 이고, 발행 게이트가 통과시키지 않은 값은 사이트에 나가지 않는다.
- 무엇을 · 누구를 위해 · 무엇을 안 하는가 → docs/PRODUCT.md
- 어떻게 도는가 (파이프라인·앱 경계) → docs/ARCHITECTURE.md
- 에이전트·신규 합류자가 먼저 읽을 규약 → AGENTS.md
실행
cp .env.example .env # DB_*, JWT_*, 외부 API 키
cp nginx/site.conf.example nginx/site.conf # 빼먹으면 nginx 가 설정 없이 뜬다
docker compose up -d
docker compose logs -f solution-worker
| 주소 | 공개 | |
|---|---|---|
| 사장님 앱 (빌더) | http://localhost:3000 | 외부 |
| 발행된 사이트 | http://localhost:3000/s/<slug> · 운영은 :80 |
외부 |
| 솔루션 API 문서 | http://localhost:9800/docs | 외부 |
| 내부 운영 화면 | http://localhost:3002 | 127.0.0.1 만 |
| 어드민 API 문서 | http://localhost:9801/docs | 127.0.0.1 만 |
내부 두 개를 0.0.0.0 으로 열면 앱을 가른 의미가 없다 (ADMIN_BIND · ADMIN_API_BIND).
- DB 는 compose 밖이다 (호스트 PostgreSQL,
host.docker.internal). 스키마는postgres-init/init-data/init.sql한 벌 — 누적 ALTER 파일은 없다. - npm 워크스페이스 루트는 레포 루트다.
npm install은 여기서 한 번.npm run dev:frontend/dev:admin/dev:site - 백엔드 스크립트는
solution/backend/에서.venv/bin/python scripts/<name>.py - 테스트는
solution/backend/에서.venv/bin/pytest
레포 구조
solution/ 사장님 — 사이트 만들기·관리
backend/ FastAPI(:9800) + 워커. HTML 은 만들지 않는다 — payload JSON 만 떨어뜨린다
frontend/ 빌더 (위저드 + 에디터 + 발행 게이트)
site/ 발행 사이트. SSR 엔트리 + 프리렌더 + 정적 서버
shared/ frontend·site·백엔드 계약 (SitePayload · slug · 디자인 토큰)
admin/ 우리 — 전체 사이트 운영
backend/ 진입점만(:9801). 도메인 코드는 solution/backend 를 PYTHONPATH 로 쓴다
frontend/ 운영 화면. `@` 별칭이 solution/frontend/src 를 가리킨다
docs/ 아래 표
nginx/ 발행 사이트 정적 서빙 (site.conf 는 .example 만 커밋)
postgres-init/ 스키마 DDL
의존 방향은 admin → solution 한 쪽뿐이다. 반대가 생기면 번들을 가른 의미가 사라진다. 근거는 ARCHITECTURE.md 4절.
문서 지도
| 문서 | 언제 읽나 |
|---|---|
| docs/PRODUCT.md | 이 제품이 뭘 푸는지 · 안 하기로 한 것이 뭔지 |
| docs/ARCHITECTURE.md | 발행 파이프라인 전체 · 두 앱과 한 백엔드의 경계 |
| docs/DEVELOPMENT_DIRECTION.md | v19 설계서 대비 격차 · 개발 우선순위(P0~P4) |
| docs/DECISIONS.md | 미결 사항과, 코드가 그걸 어떻게 격리해 뒀는지 |
| docs/DEPLOY.md | 서버에 올릴 때 · 배포 후 재발행 절차 |
| docs/DATA_SOURCE_RESEARCH.md | 어디서 콘텐츠를 가져올 수 있나 (실측 근거) |
| docs/COLLECTION_SEO_AEO_FLOW.md | 수집→LLM→SEO/AEO 현재 구현 |
| docs/API_USAGE.md | 외부 API 원가 — 사이트 1건당 $1 상한을 어디서 강제하나 |
문서 규칙
- 한 사실은 한 곳에. 중복된 문서는 썩고, 썩은 문서는 사람과 에이전트를 적극적으로 오도한다. 다른 문서의 내용은 복사하지 말고 링크한다.
- 코드가 말해주는 건 쓰지 않는다. 문서에 적을 값어치가 있는 건 왜 이렇게 했는지, 왜 저건 안 했는지, 그리고 실측값(날짜와 함께)이다.
- 결론이 나면
DECISIONS.md에 날짜와 함께 적고, 격리해 둔 플래그를 제거한다. - 문서는 코드와 같은 커밋·같은 리뷰에서 고친다. 동작을 바꾸는 PR 이 문서를 안 고쳤으면 미완이다.