설정: 어느 폴더에 .env 와 toml 이 들어가는지 한 표로 못 박는다
세팅할 때마다 "이 값이 어디 가는 거지" 를 코드에서 되짚고 있었다. AGENTS.md 에 설정 파일 지도를 넣고, 흩어져 있던 cp 명령을 거기 한 곳으로 모았다. 두는 곳 — 템플릿이 전부 짝을 갖는다(7개 확인). 루트 .env ← 백엔드가 읽는 유일한 .env solution/backend/config/ config.local.toml DB·JWT·포트·CORS solution/backend/config/ config.test.toml 없으면 pytest 가 import 단계에서 죽는다 solution/frontend/ .env VITE_* (API :9800) admin/frontend/ .env VITE_* (API :9801) nginx/ site.conf 없으면 Docker 가 디렉토리를 만든다 두지 않는 곳과 그 이유도 같이 적었다 — solution/backend(dotenv 가 루트를 본다), admin/backend(PYTHONPATH 로 solution 것을 쓴다), solution/site, solution/shared. solution/site/.env.example 을 지웠다. "여기엔 아무것도 없다" 는 걸 알리려고 둔 파일인데, .env.example 은 이 레포에서 "복사해서 채워라" 라는 뜻이라 정반대 신호를 준다. 그 설명은 지도의 '두지 않는 곳' 으로 옮겼다. 그 밖에 - config.test.toml.example 의 server_name 이 O2oSiteServerTest 로 남아 있었다. - config.local.toml.example 에 생략 가능한 [ExternalApiConfig] 필드 셋을 주석으로 적었다. - .gitignore 의 *.toml 이 레포 전체를 덮는다는 것과, 그래서 pyproject.toml 같은 걸 추가하면 조용히 무시된다는 것을 규칙 옆에 적었다.
This commit is contained in:
parent
b9d598e214
commit
fc47946d3f
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,6 +4,9 @@
|
||||
!.env.example
|
||||
|
||||
# 설정: 시크릿 포함이라 커밋하지 않는다. *.example 만 커밋한다.
|
||||
# ★ 레포 전체의 *.toml 이 대상이다. toml 은 DB 비밀번호·JWT 키가 들어가는 파일이라
|
||||
# 안전한 쪽으로 넓게 잡았다 — 대신 pyproject.toml 같은 걸 새로 추가하면 조용히
|
||||
# 무시되므로, 그때는 여기에 예외(!)를 명시적으로 적는다.
|
||||
*.toml
|
||||
!*.toml.example
|
||||
|
||||
|
||||
59
AGENTS.md
59
AGENTS.md
@ -96,8 +96,7 @@ docker compose logs -f worker
|
||||
|
||||
- 사장님 앱 `:3000`(API :9800) · 내부 운영 `:3002`(API :9801, 로컬호스트에만 열림)
|
||||
- 발행 사이트: `http://localhost:3000/s/<slug>` (빌더 Vite 가 :3001 정적서버로 프록시)
|
||||
- **클론 직후 1회**: `cp .env.example .env` · `cp nginx/site.conf.example nginx/site.conf`
|
||||
(후자를 빼먹으면 Docker 가 그 자리에 디렉토리를 만들어 nginx 가 설정 없이 뜬다)
|
||||
- **클론 직후 1회**: 설정 파일 복사 — 아래 "설정 파일은 어느 폴더에 두나"
|
||||
- DB 는 compose 밖이다 (호스트 PostgreSQL, `host.docker.internal`).
|
||||
스키마는 `postgres-init/init-data/init.sql` **한 벌**이다 — 누적 ALTER 파일은 없다
|
||||
- npm 워크스페이스 루트는 **레포 루트**다. `npm install` 은 루트에서 한 번.
|
||||
@ -106,19 +105,55 @@ docker compose logs -f worker
|
||||
- 테스트: `solution/backend/` 에서 `.venv/bin/pytest`. **`APP_ENV=test` 면 `.env` 를 읽지 않는다** —
|
||||
실키가 테스트로 새어 외부 API 요금이 나가는 경로를 막아 뒀다
|
||||
|
||||
## .env 는 어디에 두나
|
||||
## 설정 파일은 어느 폴더에 두나
|
||||
|
||||
**루트 `.env` 가 단일 출처다.** compose 가 이 파일을 읽고(`${...}` 치환 + `env_file`),
|
||||
**백엔드도 compose 밖에서 띄우면 이 파일을 직접 읽는다**(`config/server_configs.py` 의
|
||||
python-dotenv). Vite 앱만 구조상 **자기 디렉토리의 `.env`** 를 읽는다 — 그래서 나뉘어 있는
|
||||
것이지 취향이 아니다.
|
||||
**실제 값은 하나도 커밋되지 않는다.** 커밋되는 건 `*.example` 뿐이고, 클론 직후 그걸 복사해
|
||||
값을 채우는 게 세팅의 전부다.
|
||||
|
||||
| 파일 | 담는 것 |
|
||||
| 폴더 | 파일 | 담는 것 | 템플릿 |
|
||||
|---|---|---|---|
|
||||
| **레포 루트** | `.env` | DB · JWT · 외부 API 키 · 수집 어댑터 · 발행/색인 · 포트 바인딩 | `.env.example` |
|
||||
| `solution/backend/config/` | `config.local.toml` | DB 접속 · JWT · 포트 · CORS(`client_url`) | `config.local.toml.example` |
|
||||
| `solution/backend/config/` | `config.test.toml` | 테스트 DB(`web4ai_test_db`) | `config.test.toml.example` |
|
||||
| `solution/frontend/` | `.env` | 이 앱의 `VITE_*` (API :9800 · 발행 호스트 · 개발 자동로그인) | `.env.example` |
|
||||
| `admin/frontend/` | `.env` | 이 앱의 `VITE_*` (API **:9801**) | `.env.example` |
|
||||
| `nginx/` | `site.conf` | 발행 사이트 정적 서빙 규칙 | `site.conf.example` |
|
||||
|
||||
**두지 않는 곳** — 두면 조용히 무시된다.
|
||||
|
||||
| 폴더 | 왜 없나 |
|
||||
|---|---|
|
||||
| `.env` | DB · JWT · 외부 API 키 · 수집 어댑터 · 발행/색인 · 포트 바인딩 — **백엔드가 쓰는 전부** |
|
||||
| `solution/frontend/.env` · `admin/frontend/.env` | 그 앱에만 있는 `VITE_*`. admin 은 API 가 :9801 이다 |
|
||||
| `solution/site` | **없다.** 렌더러는 런타임 env 를 안 쓴다 — 사이트별 값은 전부 `SitePayload` 로 들어온다 |
|
||||
| `solution/backend` | **없다.** 여기 `.env` 를 두면 아무도 읽지 않는다 (루트를 본다) |
|
||||
| `solution/backend/` | `.env` 를 여기 두면 아무도 안 읽는다. dotenv 가 **레포 루트**를 본다 (`config/server_configs.py`) |
|
||||
| `admin/backend/` | 진입점 두 파일뿐이다. `config/` 는 `PYTHONPATH` 로 `solution/backend` 것을 그대로 쓴다 |
|
||||
| `solution/site/` | 렌더러는 런타임 env 를 안 쓴다 — 사이트별 값은 전부 `SitePayload` 로 들어온다. 그래야 payload 하나로 같은 HTML 이 재현된다. 프리렌더 옵션은 CLI 인자(`--payload` `--out`)이고, `INDEXNOW_KEY`·`PORT` 는 compose 가 프로세스에 넣는다 |
|
||||
| `solution/shared/` | 코드만 있다 |
|
||||
|
||||
### 클론 직후 한 번
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
cp nginx/site.conf.example nginx/site.conf
|
||||
cp solution/backend/config/config.local.toml.example solution/backend/config/config.local.toml
|
||||
cp solution/backend/config/config.test.toml.example solution/backend/config/config.test.toml
|
||||
cp solution/frontend/.env.example solution/frontend/.env # compose 로만 쓰면 생략 가능
|
||||
cp admin/frontend/.env.example admin/frontend/.env # 〃
|
||||
```
|
||||
|
||||
`site.conf` 를 빼먹으면 Docker 가 그 자리에 **디렉토리**를 만들어 nginx 가 설정 없이 뜬다.
|
||||
`config.test.toml` 이 없으면 `pytest` 가 conftest import 단계에서 죽는다.
|
||||
|
||||
### 누가 무엇을 읽나 — 우선순위
|
||||
|
||||
**실제 환경변수(compose) > 루트 `.env` > `config.{APP_ENV}.toml`**
|
||||
|
||||
- compose 는 루트 `.env` 를 `env_file` 로 통째로 넣고 `${...}` 치환에도 쓴다.
|
||||
- 백엔드는 **compose 밖에서 띄울 때** 루트 `.env` 를 python-dotenv 로 직접 읽는다.
|
||||
- Vite 앱만 구조상 **자기 디렉토리의 `.env`** 를 읽는다 — 그래서 나뉜 것이지 취향이 아니다.
|
||||
- 도커 이미지는 시크릿을 굽지 않는다. `config.local.toml` 을 **example 사본(플레이스홀더)으로
|
||||
덮어** 넣으므로(`Dockerfile:24`), 컨테이너로 띄울 땐 `JWT_*` 를 env 로 반드시 주입해야 한다.
|
||||
안 하면 공개된 플레이스홀더가 서명 키가 된다.
|
||||
- **`APP_ENV=test` 면 `.env` 를 읽지 않는다.** 실키가 테스트로 새어 외부 API 요금이 나가는
|
||||
경로를 막아 뒀다 — 그래서 `config.test.toml` 의 키는 전부 빈 값이어야 한다.
|
||||
|
||||
★ **두 곳에 같은 값을 적지 않는다.** 발행 호스트는 compose 가 루트의 `SITE_PUBLIC_HOST` 를
|
||||
`VITE_PUBLISH_HOST` 로 흘려보낸다. 각자 적으면 canonical 과 화면 주소가 조용히 갈라진다.
|
||||
|
||||
@ -13,8 +13,11 @@
|
||||
## 실행
|
||||
|
||||
```bash
|
||||
cp .env.example .env # DB_*, JWT_*, 외부 API 키
|
||||
cp nginx/site.conf.example nginx/site.conf # 빼먹으면 nginx 가 설정 없이 뜬다
|
||||
# 클론 직후 1회 — 설정 파일 복사 목록은 AGENTS.md "설정 파일은 어느 폴더에 두나"
|
||||
cp .env.example .env
|
||||
cp nginx/site.conf.example nginx/site.conf
|
||||
cp solution/backend/config/config.local.toml.example solution/backend/config/config.local.toml
|
||||
|
||||
docker compose up -d
|
||||
docker compose logs -f worker
|
||||
```
|
||||
|
||||
@ -85,7 +85,7 @@ UNVERIFIED ──┬─> PENDING_OWNER ──┬─> VERIFIED ──┬─> CORR
|
||||
|
||||
```
|
||||
o2o-web4ai/
|
||||
├── .env.example # 외부 API 키·DB 접속 주입 템플릿 (cp .env.example .env)
|
||||
├── .env.example # ★ 루트다. 백엔드가 읽는 유일한 .env (cp .env.example .env)
|
||||
├── postgres-init/
|
||||
│ └── init-data/init.sql # 스키마 DDL 한 벌 (재실행 안전. 누적 ALTER 파일은 없다)
|
||||
├── admin/backend/ # 내부 API 진입점(:9801). 도메인 코드는 아래를 그대로 import 한다
|
||||
@ -196,9 +196,13 @@ POST /v1/place/{id}/site/slug {slug} → sites.domain 에 저장(사
|
||||
|
||||
| 무엇 | 어디 | 커밋 |
|
||||
|---|---|---|
|
||||
| DB 접속 · JWT · 포트 | `solution/backend/config/config.local.toml` | ✗ (`*.toml` ignore) |
|
||||
| 외부 API 키 | 레포 최상위 `.env` | ✗ |
|
||||
| 템플릿 | `config/config.local.toml.example` · `.env.example` | ✓ |
|
||||
| DB 접속 · JWT · 포트 · CORS | `solution/backend/config/config.local.toml` | ✗ (`*.toml` ignore) |
|
||||
| 테스트 DB | `solution/backend/config/config.test.toml` | ✗ |
|
||||
| 외부 API 키 · 수집/발행 스위치 | **레포 루트 `.env`** | ✗ |
|
||||
| 템플릿 | 위 셋의 `*.example` | ✓ |
|
||||
|
||||
★ `solution/backend/.env` 는 **아무도 읽지 않는다** — dotenv 가 레포 루트를 본다.
|
||||
어느 폴더에 무엇을 두는지는 [../../AGENTS.md](../../AGENTS.md) 가 단일 출처다.
|
||||
|
||||
우선순위: **실제 환경변수(docker-compose) > `.env` > `config.{APP_ENV}.toml`**
|
||||
|
||||
|
||||
@ -49,4 +49,8 @@ kakao_rest_api_key = "" # 동일 업소 검증(미발급)
|
||||
naver_client_id = "" # 동일 업소 검증 — 네이버 지역검색
|
||||
naver_client_secret = ""
|
||||
gemini_api_key = "" # 사진 분류 · 카피 작성
|
||||
tour_api_key = "" # 공공데이터포털 전국문화축제표준데이터 일반 인증키
|
||||
tour_api_key = "" # 공공데이터포털 일반 인증키. fact 의 주력 공급원
|
||||
# 아래 셋은 생략하면 코드 기본값을 쓴다(config_models.ExternalApiConfig).
|
||||
# gemini_vision_model = "gemini-3.7-flash"
|
||||
# gemini_text_model = "gemini-3.7-flash"
|
||||
# vision_confidence_threshold = 0.7 # 미만이면 사람 확인 큐에 남긴다
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
# ★ DB 이름을 dev 와 절대 같게 두지 말 것 — 픽스처가 TRUNCATE 를 돌린다.
|
||||
# conftest 의 안전가드가 이름을 확인하지만, 여기서부터 갈라 두는 게 먼저다.
|
||||
[WebServerConfig]
|
||||
server_name = "O2oSiteServerTest"
|
||||
server_name = "Web4aiServerTest"
|
||||
port = 9800
|
||||
process_count = 1
|
||||
is_ssl = false
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
# 발행 사이트 렌더러는 런타임 환경변수를 쓰지 않는다 — 여기 채울 것이 없다.
|
||||
#
|
||||
# 사이트마다 다른 값(도메인·색·내용)은 전부 SitePayload 로 들어온다.
|
||||
# 그래야 payload 하나로 같은 HTML 이 재현된다 — 렌더 결과가 환경에 따라 달라지면
|
||||
# "구워진 HTML 기준" 2차 발행 게이트가 의미를 잃는다.
|
||||
#
|
||||
# 프리렌더 옵션은 CLI 인자로 준다:
|
||||
# npm run prerender -- --payload=./payloads --out=/var/www
|
||||
#
|
||||
# 예외로 프로세스 환경에서 읽는 둘은 compose 가 넣는다(이 파일 아님):
|
||||
# INDEXNOW_KEY 루트 <key>.txt 를 굽는다. 백엔드와 같은 값이어야 한다
|
||||
# PORT serve-sites.mjs 개발용 정적 서버 포트(:3001)
|
||||
Loading…
Reference in New Issue
Block a user