git 저장소가 없어 히스토리·협업 기반이 아예 없던 상태를 연다.
함께 문서를 재편했다. 그동안 문서가 있어도 "이 제품이 뭘 푸는가"와
"어떻게 도는가"를 담은 문서가 없어서, 목표 문장이 backend/frontend
README 두 곳에 복붙돼 있었다 — 상위 문서가 없어 아래로 샌 것이다.
신설
README.md 레포 진입점 + 문서 지도 + 문서 규칙 4가지
AGENTS.md 에이전트·신규 합류자용 함정 목록과 규약
(CLAUDE.md 는 여기로 걸린 심볼릭 링크)
docs/PRODUCT.md 제품 정의 — 문제·사용자·원칙·**non-goals**·성공 기준
docs/ARCHITECTURE.md payload 경계·발행 파이프라인·서빙 결정·앱 분리 설계
이동
backend/docs/DECISIONS.md → docs/DECISIONS.md
백엔드만의 결정이 아니다. 게다가 코드 주석 ~25곳이 이미
`docs/DECISIONS.md` 로 적고 있어 레포 루트 기준으로는 그게 맞다.
갱신
docs/DEPLOY.md 서빙 결정 반영 — nginx 정적 서빙이 지금 경로(3절),
Azure 는 나중에 켤 때(4절)로 분리
docs/ARCHITECTURE.md 사이트 = 한 장(2026-08-31) 구조 반영
docs/COLLECTION_SEO_AEO_FLOW.md
robots.txt·sitemap.xml 은 오리진 루트에만 굽는다는 점 명시
frontend/site/scripts/prerender.ts
헤더 주석의 렌더 보고서 경로가 실제(422줄)와 달라 수정
.gitignore
★ CLAUDE.md 를 더 이상 무시하지 않는다. 에이전트 지침은 팀과 모든
에이전트가 공유하는 규약이라 커밋해야 한다 — 무시하면 클론한 사람이
"배포 후 republish_all.py 필수" 같은 함정을 전달받지 못한다.
개인용 오버라이드는 ~/.claude/CLAUDE.md 에 둔다.
84 lines
4.9 KiB
Plaintext
84 lines
4.9 KiB
Plaintext
# o2o-web4ai 환경변수 템플릿.
|
|
# cp .env.example .env 후 값을 채운다. .env 는 커밋되지 않는다(.gitignore).
|
|
#
|
|
# 우선순위: 실제 환경변수(docker-compose 등) > .env > config/config.{APP_ENV}.toml
|
|
# DB 접속·JWT 는 config.local.toml 이 기본값이다. 여기 값을 채우면 그쪽을 덮어쓴다.
|
|
# 외부 API 키는 toml 을 비워두고 여기서만 관리하는 것을 권장한다.
|
|
|
|
# ── 실행 ─────────────────────────────────────────────────
|
|
APP_ENV=local
|
|
# RELOAD=1 # uvicorn --reload (개발 컨테이너)
|
|
# SCHEDULER_ENABLED=1 # 배치 스케줄러 기동. 다중 워커면 1개 프로세스에서만 1
|
|
|
|
# ── DB (비우면 config.local.toml 의 [MainDBConfig] 사용) ──
|
|
# DB_HOST=127.0.0.1
|
|
# DB_PORT=5432
|
|
# DB_USER=postgres
|
|
# DB_PASSWORD=
|
|
# DB_NAME=o2o_site_db
|
|
|
|
# ── JWT 서명 키 ───────────────────────────────────────────
|
|
# ★ 도커 이미지는 시크릿을 굽지 않으므로(config.local.toml 이 플레이스홀더 사본)
|
|
# 컨테이너로 띄울 땐 반드시 여기서 주입해야 한다. 비우면 공개된 플레이스홀더가 서명 키가 된다.
|
|
# 생성: python -c "import secrets; print(secrets.token_urlsafe(48))"
|
|
JWT_ACCESS_SECRET=
|
|
JWT_REFRESH_SECRET=
|
|
|
|
# ── 외부 API 키 ───────────────────────────────────────────
|
|
# 비어 있으면 해당 어댑터만 비활성된다. 서버는 그대로 뜬다.
|
|
|
|
# 채널 URL 발견 (api.perplexity.ai, 모델 sonar / sonar-pro)
|
|
# ★ 지금은 **기본으로 꺼져 있다**(COLLECT_USE_PERPLEXITY=0). 키가 있어도 호출하지 않는다.
|
|
# 실측(2026-08-27 도플로·버터브루·힐튼 가든 인 서울 강남): 야놀자만 물어오고 네이버
|
|
# 플레이스는 0건, 필터를 넓히면 네이버 도움말 페이지를 채널로 등록했다. 게다가
|
|
# 검색 호출 요금이 토큰 요금과 별도로 붙는다(생성 1건당 15회).
|
|
# ★ 답변을 사실로 쓰지 않는다. URL 발견 전용 — 동명 업소가 섞이고 환각이 있다.
|
|
PERPLEXITY_API_KEY=
|
|
|
|
# Perplexity 채널 URL 발견 스위치. 0=끔(기본) / 1=켬.
|
|
# 코드는 지우지 않고 여기서만 끈다 — 야놀자·여기어때 어댑터가 붙으면 배포 없이 되켠다.
|
|
# 끈 상태에서도 상호 → 네이버 place id 직접 해석은 계속 돈다(유일한 자동 발견 경로).
|
|
COLLECT_USE_PERPLEXITY=0
|
|
|
|
# 동일 업소 검증 · 주변 정보 (dapi.kakao.com)
|
|
# https://developers.kakao.com > 내 애플리케이션 > 앱 키 > REST API 키
|
|
# ★ 무료 쿼터는 개발자 계정의 "첫 번째 활성 앱" 에만 붙는다.
|
|
# dev/stage/prod 앱을 따로 파면 하나만 무료다 — 앱을 나누기 전에 확인할 것.
|
|
# 초과 단가: 키워드/카테고리 검색 2원, 좌표 변환 0.5원 (키워드가 4배 비싸다)
|
|
# 동일 업소 검증 — 네이버 지역검색 (카카오 키 미발급이라 이쪽을 쓴다)
|
|
# https://developers.naver.com/apps 검색 API
|
|
# ★ 제약: display 최대 5건 · telephone 이 빈 값으로 온다 · 행정구역 코드 없음
|
|
# → 카카오보다 동일 업소 판정 근거가 약하다(AMBIGUOUS 가 늘어난다)
|
|
NAVER_CLIENT_ID=
|
|
NAVER_CLIENT_SECRET=
|
|
|
|
KAKAO_REST_API_KEY=
|
|
|
|
# 사진 분류 + 카피 작성 (Google AI Studio)
|
|
# https://aistudio.google.com/apikey
|
|
GEMINI_API_KEY=
|
|
|
|
# 축제 · 관광지 (한국관광공사 TourAPI, data.go.kr)
|
|
# ★ TourAPI 는 자체 areaCode/sigunguCode 체계를 쓴다 — 카카오 행정구역 코드와 다르므로 매핑이 필요하다.
|
|
# 디코딩된 서비스키를 넣는다(인코딩 키를 넣으면 %2B 등이 이중 인코딩된다).
|
|
TOUR_API_KEY=
|
|
|
|
# Open-Meteo(날씨)는 API 키가 필요 없다. 좌표만 있으면 된다.
|
|
# ── 발행 호스트 ──
|
|
# 커스텀 도메인이 없는 사이트의 공개 주소(`https://<이 값>/s/<slug>`).
|
|
# canonical·og:url·sitemap·IndexNow 가 전부 이 값을 쓴다. 비우면 w4ai.o2o.kr.
|
|
# ★ 프론트(admin)의 VITE_PUBLISH_HOST 와 **같은 값**이어야 화면과 발행본이 갈리지 않는다.
|
|
SITE_PUBLIC_HOST=w4ai.o2o.kr
|
|
|
|
# Azure Blob 정적 사이트 발행. 비우면 기존 로컬 out/ 발행만 사용한다.
|
|
AZURE_STORAGE_CONNECTION_STRING=
|
|
AZURE_STORAGE_CONTAINER=$web
|
|
AZURE_STORAGE_PREFIX=ai-for-web
|
|
|
|
# ── 색인 통보(IndexNow) ──
|
|
# 발행 즉시 네이버·Bing·Yandex 에 URL 을 알린다. 비우면 통보를 건너뛴다(발행은 정상).
|
|
# 구글은 IndexNow 를 지원하지 않는다 — 구글 쪽은 Search Console 사이트맵 제출이 별도 경로다.
|
|
# 값은 8~128자의 영문·숫자·하이픈 아무 문자열이면 된다(비밀이 아니다. 공개되어야 작동한다):
|
|
# python3 -c "import uuid; print(uuid.uuid4().hex)"
|
|
INDEXNOW_KEY=
|