[docs] site/scripts: 섹션 22개별 "목업은 무엇으로 만들었고 제품은 무엇으로 만드나"
빌더·백엔드에서 이 시연본을 실제 생성 파이프라인으로 옮기려는 사람이, 화면을 보고 역산해야 하는 상태였다. 시연본은 payload 가 없는 목업이라 "화면에 있는 것"과 "제품이 만들 수 있는 것"이 갈라져 있는데 그 간격이 어디에 얼마나 있는지 적힌 곳이 없었다. 섹션마다 값이 오는 payload 자리 · 계약 유무 · 프롬프트 위치 · 목업이 손으로 한 것을 적고, 계약에 아예 없는 셋(히어로 캐치프레이즈 · 자작곡 플레이어 · 일정 규칙)은 추가할 타입과 프롬프트 초안까지 넣었다. - SECTIONS.md 신설: 22개 섹션표 · 새로 만들어야 할 6가지 · 프롬프트 작성 규칙 · 검증 - README.md: 제품으로 옮기는 사람을 SECTIONS.md 로 보낸다. 감사 항목 수 33 → 34 정정 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f0b5c444bc
commit
a134805b3f
@ -8,9 +8,13 @@
|
||||
> 고치는 것은 구워진 `index.html` 하나이고, 그 파일이 유일본이다.
|
||||
> 2. **렌더러(`solution/site/src`)를 고치지 않는다.** 고치면 번들 해시가 바뀌어
|
||||
> 전 사이트 재굽기 + 전체 재업로드가 따라온다. 목업에만 필요한 것은 `inject.css`/`inject.js` 로 덮는다.
|
||||
> 3. 무엇을 고치든 **`node audit-all.mjs` 가 33개 항목 전부 통과**해야 끝난 것이다.
|
||||
> 3. 무엇을 고치든 **`node audit-all.mjs` 가 34개 항목 전부 통과**해야 끝난 것이다.
|
||||
> "코드에서 빠졌으니 됐다" 로 끝내면 안 된다 — 실제로 그렇게 틀린 적이 있다(§7).
|
||||
|
||||
> **빌더·백엔드에서 이걸 제품으로 옮기는 사람은 [SECTIONS.md](SECTIONS.md) 부터 읽는다.**
|
||||
> 섹션 22개마다 "목업은 무엇으로 만들었고, 제품은 무엇으로 만들어야 하나" 를 적었다 —
|
||||
> 계약(payload 필드) · 프롬프트 · 아직 없는 것 셋(캐치프레이즈 · 자작곡 · 일정 규칙).
|
||||
|
||||
---
|
||||
|
||||
## 0. 먼저 알아야 할 것 — 왜 스크립트인가
|
||||
@ -56,7 +60,7 @@ docker cp build/index.html o2o-web4ai-solution-prerender:/app/solution/site/out/
|
||||
검사:
|
||||
|
||||
```bash
|
||||
node audit-all.mjs # 33개 항목 전수 점검 (로컬)
|
||||
node audit-all.mjs # 34개 항목 전수 점검 (로컬)
|
||||
node audit-all.mjs https://web4ai.o2osolution.ai/s/stay # 킹서버
|
||||
```
|
||||
|
||||
@ -239,7 +243,7 @@ node audit-all.mjs https://web4ai.o2osolution.ai/s/stay # 킹서버
|
||||
**검사가 빌드의 일부가 아니면 검사한 적 없는 것과 같다.**
|
||||
|
||||
`audit-all.mjs` 는 그다음 단계다 — 구워진 HTML 을 **실제 브라우저로 열어**
|
||||
33개 항목을 센다(캐치프레이즈 개수, 링크 개수, 사진 개수, 모바일 레이아웃, 404, 콘솔 오류…).
|
||||
34개 항목을 센다(캐치프레이즈 개수, 링크 개수, 사진 개수, 모바일 레이아웃, 404, 콘솔 오류…).
|
||||
|
||||
---
|
||||
|
||||
@ -327,7 +331,7 @@ for f in audio/*.mp3; do docker cp \"\$f\" \$C:/app/solution/site/out/s/sta
|
||||
| `build_story.py` | 가요 다방 · 인물 열전 |
|
||||
| `patch_stay.py` | 캐치프레이즈 · 객실 · 자작곡 · HTML 조립 |
|
||||
| `inject.css` · `inject.js` | 화면에 덧대는 것(플레이어 · 문구 순환 · 링크 · 크레딧) |
|
||||
| `audit-all.mjs` | 브라우저로 33개 항목 전수 점검 |
|
||||
| `audit-all.mjs` | 브라우저로 34개 항목 전수 점검 |
|
||||
| `verify.mjs` · `player.mjs` · `check2.mjs` · `kingcheck.mjs` · `rail.mjs` · `arrow.mjs` | 부분 점검 |
|
||||
| `stay-payload.json` · `orig/index.html` | **원본. 건드리지 않는다** |
|
||||
| `stay-payload-new.json` | 중간 산출물 |
|
||||
|
||||
279
solution/site/scripts/mockup/SECTIONS.md
Normal file
279
solution/site/scripts/mockup/SECTIONS.md
Normal file
@ -0,0 +1,279 @@
|
||||
# /s/stay 섹션별 — 목업은 무엇으로 만들었고, 제품은 무엇으로 만들어야 하나
|
||||
|
||||
작성 2026-09-11 · 대상 `https://web4ai.o2osolution.ai/s/stay` (스테이,머뭄 · 전북 군산)
|
||||
**읽는 사람**: 빌더·백엔드에서 이 시연본을 실제 생성 파이프라인으로 옮기는 사람.
|
||||
|
||||
시연본은 **payload 가 없는 목업**이다 — 프리렌더가 굽지 않고, `index.html` 한 장을
|
||||
손으로 조립했다(`patch_stay.py`). 그래서 "화면에 있는 것" 과 "제품이 만들 수 있는 것"이
|
||||
지금 **갈라져 있다.** 이 문서는 섹션마다 그 간격을 적는다.
|
||||
|
||||
---
|
||||
|
||||
## 0. 한 줄 결론
|
||||
|
||||
22개 섹션 중 **16개는 이미 계약과 생성 경로가 있다.** 나머지 6개가 할 일이다:
|
||||
|
||||
| | 섹션 | 왜 |
|
||||
|---|---|---|
|
||||
| ① | **히어로 캐치프레이즈** | 계약에 없다. `narrative.tagline` 은 **문자열 하나**인데 목업은 100개를 돌린다 |
|
||||
| ② | **음악 플레이어(자작곡)** | 계약에 없다. `media` 에 오디오 종류가 없다 |
|
||||
| ③ | **추천 일정** | 계약·생성기 둘 다 있는데 **깊이가 다르다**. 서버판은 하루 4칸(관광 2·끼니 2)이고 체류·이동·입퇴실 개념이 없다 |
|
||||
| ④ | **인물·노래 사진** | 프롬프트가 `imageQuery` 만 받는다. 실제 사진을 찾아 붙이고 **라이선스를 표시하는** 경로가 없다 |
|
||||
| ⑤ | **상한(maxItems)** | songs 8·people 10 인데 목업은 25·57 이다 |
|
||||
| ⑥ | **객실 사진** | 목업은 야놀자 등록본에서 가져왔다. 수집 어댑터 정책([DECISIONS 1-1](../../../../docs/DECISIONS.md))에 걸리는지 결론이 필요하다 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 값이 화면까지 가는 길
|
||||
|
||||
```
|
||||
수집·생성(백엔드) → area_contents / places / media → site_payload.py → payload JSON
|
||||
↓
|
||||
프리렌더(Node) → out/s/<slug>/index.html
|
||||
↓
|
||||
window.__SITE_PAYLOAD__ → React 하이드레이션
|
||||
```
|
||||
|
||||
- **읽는 쪽 계약**은 `solution/shared/src/types/site-payload.ts` · `shared/src/lib/section-data.ts` 한 곳이다.
|
||||
- **프롬프트 계약**은 `shared/src/lib/section-prompts.ts` 한 곳이고,
|
||||
백엔드는 `npm run export:prompts` 산출물(`backend/services/prompts/section_prompts.json`)을 읽는다.
|
||||
**파이썬 쪽을 손으로 고치지 않는다.**
|
||||
- 섹션 목록·기본 on/off 는 업종별로 `services/site_payload.py:155` 부근에 있다.
|
||||
|
||||
payload 안에서 섹션이 값을 받는 자리는 셋뿐이다.
|
||||
|
||||
| 자리 | 무엇이 들어가나 | 예 |
|
||||
|---|---|---|
|
||||
| `theme.sections[].data` | JSON 봉투 문자열(`{kind, version, title, items[]}`) | songs · people · itinerary · event · video · planner |
|
||||
| 최상단 필드 | 업소가 적은 사실·사진·문답 | `place` `facts` `units` `media` `faqs` `links` `narrative` |
|
||||
| `local.*` | 지역에서 수집한 것 | `attractions`(12) `restaurants`(8) `festivals`(10) `weather` |
|
||||
|
||||
---
|
||||
|
||||
## 2. 섹션 22개 — 한눈에
|
||||
|
||||
`on` 은 이 목업에서 켜져 있는지다. `story` 안의 6개는 탭이라 최상단은 꺼져 있다.
|
||||
|
||||
| # | id | 이름 | on | 값이 오는 자리 | 계약 | 프롬프트 | 목업에서 한 일 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 1 | `hero` | 히어로 | ● | `narrative` + 대표 사진 | 있음 | **없음** | 캐치프레이즈 100개를 주입분이 돌림 |
|
||||
| 2 | `intro` | 소개 | ● | `narrative.about[]` | 있음 | 있음(빌더) | 원본 그대로 |
|
||||
| 3 | `rooms` | 객실 안내 | ● | `units[]` + `media[]` | 있음 | — | 야놀자 등록본으로 **전면 교체**(A동 12·B동 10) |
|
||||
| 4 | `event` | 소식 | ● | `data(kind=event)` 3건 | 있음 | — | 원본 그대로 |
|
||||
| 5 | `info` | 기본 정보 | ● | `facts[]` 12건 | 있음 | — | 맨 아래 가로선만 CSS 로 제거 |
|
||||
| 6 | `booking` | 실시간 예약 | ○ | — | 있음 | — | 껐다(시연에 결제 흐름 없음) |
|
||||
| 7 | `video` | 영상 | ● | `data(kind=video)` 3건 | 있음 | — | 원본 그대로 |
|
||||
| 8 | `photos` | 사진 갤러리 | ● | `media[]` 37장 | 있음 | — | 원본 그대로 |
|
||||
| 9 | `map` | 오시는 길 | ● | `place` + `routes[]` | 있음 | — | `routes` 비어 있음 |
|
||||
| 10 | `festival` | 계절별 축제 | ● | `local.festivals` 10건 | 있음 | — | 원본 그대로 |
|
||||
| 11 | `local` | 지역 정보 | ● | `local.attractions/restaurants` | 있음 | — | 원본 그대로 |
|
||||
| 12 | `itinerary` | **추천 일정** | ● | `data(kind=itinerary)` 21개 | 있음 | **없음** | **전부 다시 생성**(§4) |
|
||||
| 13 | `story` | 군산 이야기 | ● | 아래 6탭의 껍데기 | 있음 | — | — |
|
||||
| 14 | `songs` | 가요 다방 | 탭 | `data(kind=songs)` 25곡 | 있음 | 있음 `section-prompts.ts:67` | 8 → **25곡**으로 늘림 |
|
||||
| 15 | `people` | 인물 열전 | 탭 | `data(kind=people)` 57명 | 있음 | 있음 `:113` | 10 → **57명** + 사진 18장 |
|
||||
| 16 | `chronicle` | 시간의 골목 | 탭 | `data(kind=chronicle)` 7건 | 있음 | 있음 `:135` | 원본 그대로 |
|
||||
| 17 | `postcard` | 오늘의 엽서 | 탭 | `data(kind=postcard)` 4건 | 있음 | 있음 `:160` | 지도 링크만 추가 |
|
||||
| 18 | `quiz` | 뒤집어 보는 질문 | 탭 | `data(kind=quiz)` 4건 | 있음 | 있음 `:188` | 원본 그대로 |
|
||||
| 19 | `faq` | 자주 묻는 질문 | ● | `faqs[]` 32건 | 있음 | 있음 | 원본 그대로 |
|
||||
| 20 | `rules` | 이용 규정 | ● | `facts[]` | 있음 | — | 원본 그대로 |
|
||||
| 21 | `weather` | 날씨 | ● | `local.weather` | 있음 | — | 애니메이션만 정지 |
|
||||
| 22 | `planner` | 계절별 추천 하루 | ○ | `data(kind=planner)` 4건 | 있음 | **없음** | 껐다 |
|
||||
|
||||
`daily`(오늘의 한 장)는 프롬프트에는 있는데 **이 목업 섹션 목록에 없다.**
|
||||
새 업장에서 켜려면 섹션 목록에 자리를 먼저 만들어야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 계약에 없는 것 — 새로 만들어야 하는 셋
|
||||
|
||||
### ① 히어로 캐치프레이즈 — 계약을 늘려야 한다
|
||||
|
||||
**지금 계약**: `Narrative`(`site-payload.ts:316`)는 `heroHeadline` `heroSubline` `tagline`
|
||||
`about[]` `summary` 뿐이고 전부 **문자열 하나**다. 히어로는
|
||||
`narrative.tagline ?? heroSubline ?? summary` 를 그대로 찍는다(`HeroPension.tsx:125`).
|
||||
|
||||
**목업이 한 것**: `narrative.catchphrases = {version:1, items:[…100]}` 를 payload 에
|
||||
끼워 넣고, 주입분이 7초마다 단어 단위로 갈아 끼운다. **렌더러는 이 필드를 모른다** —
|
||||
주입분(`inject.js`)이 읽는다. 100개 구성은 `patch_stay.py:20`(일반 40) `:62`(계절 24)
|
||||
`:72`(월 24) `:87`(날씨 12).
|
||||
|
||||
**제품에서 해야 할 일**
|
||||
|
||||
```ts
|
||||
// site-payload.ts — Narrative 에 추가
|
||||
catchphrases?: {
|
||||
version: 1;
|
||||
items: {
|
||||
text: string; // 18자 안쪽
|
||||
type: 'general' | 'season' | 'month' | 'weather';
|
||||
season?: '봄' | '여름' | '가을' | '겨울'; // type=season
|
||||
month?: number; // 1–12, type=month
|
||||
weather?: '맑음' | '흐림' | '비' | '눈'; // type=weather
|
||||
}[];
|
||||
};
|
||||
```
|
||||
|
||||
고르는 규칙(목업이 쓰는 것 그대로): **지금 계절·이번 달·현재 날씨에 맞는 것만 후보**로
|
||||
두고 일반과 섞어 돌린다. 계절은 3개월 단위(가을 9–11, 겨울 12–2). 날씨는 `local.weather.condition`.
|
||||
|
||||
**새로 쓸 프롬프트** — `SECTION_PROMPTS` 에 `catchphrase` 종류를 더한다.
|
||||
|
||||
```
|
||||
[해야 할 일]
|
||||
[업소]의 히어로에 돌려 쓸 짧은 문구를 100개 쓴다.
|
||||
일반 40 · 계절 24(계절마다 6) · 월 24(달마다 2) · 날씨 12(맑음/흐림/비/눈 각 3).
|
||||
|
||||
[스키마]
|
||||
{ "kind":"catchphrase", "version":1, "items":[
|
||||
{ "text":"문구", "type":"general|season|month|weather",
|
||||
"season":"봄|여름|가을|겨울", "month":9, "weather":"맑음|흐림|비|눈" } ] }
|
||||
|
||||
[이 아이템만의 규칙]
|
||||
· 18자 안쪽. 두 문장으로 쓰지 않는다.
|
||||
· 느낌표·이모지를 쓰지 않는다. 광고 문구로 들리면 실패다.
|
||||
· **업소에서 실제로 일어나는 일**을 쓴다. 지역 자랑이 아니다.
|
||||
("새소리로 눈이 떠지는 집" ○ / "천년의 역사가 살아 숨쉬는 군산" ×)
|
||||
· 계절·월·날씨 문구는 그 조건에서만 말이 되어야 한다.
|
||||
9월 문구가 1월에 나와도 어색하지 않으면 그건 일반 문구다.
|
||||
· 시설명·가격·전화번호를 넣지 않는다.
|
||||
· source 는 필요 없다. 사실 주장이 아니라 업소가 하는 말이다.
|
||||
```
|
||||
|
||||
### ② 자작곡 플레이어 — `media` 에 오디오가 없다
|
||||
|
||||
**목업**: `narrative.ownSongs = {version:1, items:[…5]}`(`patch_stay.py:109`),
|
||||
파일은 `/s/stay/audio/*.mp3` 로 **직접 넣었다**. nginx `^~ /s/` 가 Range 206 으로 준다.
|
||||
플레이어는 주입분이 헤더에 그린다. **프로토타입이라 파일**이고, 제품에서는 미디어 파이프라인에
|
||||
태워야 한다.
|
||||
|
||||
**해야 할 일**: `MediaItem` 에 `kind: 'audio'` 를 넣고(`url` `title` `artist` `durationSec`),
|
||||
발행 시 다른 미디어와 같이 미러/업로드한다. 렌더러에는 플레이어 컴포넌트가 없다 —
|
||||
헤더에 붙는 작은 플레이어(아이콘 · 곡명 · 재생/멈춤 · 목록)를 새로 만든다.
|
||||
자동재생은 **넣지 않는다**(사장님 지시 2026-09-11). 재생 순서는 **랜덤 시작 후 순차**.
|
||||
|
||||
### ③ 추천 일정 — 생성기가 있는데 깊이가 다르다
|
||||
|
||||
**서버판**(`backend/services/itinerary.py`)은 payload 빌드 때 즉석 계산한다.
|
||||
하루 뼈대가 **관광지 2 + 맛집 2**, 반경 30km, 최근접 이웃 순서다.
|
||||
체류시간·이동시간·입퇴실·끼니 시각 개념이 **없다**.
|
||||
|
||||
**목업판**(`build_itinerary.py`)이 더한 것:
|
||||
|
||||
| | 목업이 넣은 것 | 어디 |
|
||||
|---|---|---|
|
||||
| 장소별 체류 | (최소, 기본, 최대) 분 — '오후' 칸이면 뭐든 90분이던 것을 장소마다 | `:40` |
|
||||
| 이동시간 | 좌표에서 계산. 도보 4km/h(≤1.5km), 그 위는 차 25km/h + 주차 5분 | `move_minutes()` |
|
||||
| 숙소 고정 | **모든 일정이 숙소에서 출발해 숙소로 돌아온다**. 첫 칸·마지막 칸이 숙소 | `:108` `:182` |
|
||||
| 시각 창 | 입실 15:00+ · 퇴실 11:00- · 점심 11:30–13:30 · 늦은 점심 13:00–15:00 · 저녁 17:30–20:00 · 밤 산책 18:30–20:00 | `:156` `:166` |
|
||||
| 테마 | 21개. 테마마다 시작 시각과 체류 배율이 다르다(끝나는 시각이 13:35~21:00 로 퍼진다) | `:368` `:470` |
|
||||
| 제약 | 비 와도 되는 테마는 실내만 · 차 없는 테마는 도보권만 · 아이 동반에 술집 금지 | `:519` |
|
||||
| **감사** | 규칙 14종. 하나라도 어기면 **payload 를 쓰지 않고 빌드가 멈춘다** | `:526` |
|
||||
|
||||
**해야 할 일**: 이 규칙을 `services/itinerary.py` 로 옮긴다. 재료가 모자라면 지어내지 않는
|
||||
원칙은 그대로다 — **감사에 걸리면 그 테마를 빼고, 하루도 못 채우면 섹션을 안 낸다.**
|
||||
|
||||
장소별 체류시간이 관건이다. 지금 `local_contents` 에는 그 값이 없다.
|
||||
LLM 에 물어 채우는 게 현실적이다 — 새 프롬프트:
|
||||
|
||||
```
|
||||
[해야 할 일]
|
||||
아래 장소들에 손님이 실제로 머무는 시간을 분으로 적고,
|
||||
거기서 "무엇을 하는지" 한 문장으로 쓴다.
|
||||
|
||||
[스키마]
|
||||
{ "kind":"stopmeta", "version":1, "items":[
|
||||
{ "name":"장소명", "minMinutes":25, "baseMinutes":35, "maxMinutes":50,
|
||||
"indoor":true, "needsCar":false, "kidFriendly":true,
|
||||
"todo":"대웅전 처마와 대나무 숲을 봅니다. 종각까지 돌면 35분입니다." } ] }
|
||||
|
||||
[이 아이템만의 규칙]
|
||||
· todo 는 **거기서 하는 일**이다. "무엇인지" 설명하지 않는다.
|
||||
("안에 들어가 사진관 세트에서 한 장 찍습니다" ○ / "1950년대 사진관입니다" ×)
|
||||
· 한 바퀴 도는 데 15분이면 15분이라고 적는다. 30분으로 올리지 않는다.
|
||||
· indoor 는 비가 와도 갈 수 있느냐다. 지붕이 있어도 가는 길이 야외면 false.
|
||||
· needsCar 는 숙소에서 걸어서 20분을 넘느냐다.
|
||||
```
|
||||
|
||||
### ④ 인물·노래 사진 — `imageQuery` 다음이 없다
|
||||
|
||||
프롬프트는 **사진 URL 을 금지**하고 `imageQuery` 만 받는다(`section-prompts.ts:128`).
|
||||
초상권·저작권 때문이고 그 판단은 맞다. 그런데 **그 다음 단계가 없어서** 인물 열전이
|
||||
활자만으로 선다.
|
||||
|
||||
목업이 한 것(`build_story.py`): 위키백과 **문서의 `pageimages`** 만 믿는다.
|
||||
이름으로 커먼즈를 검색하면 동명이인이 온다(실측: 이수현→걸그룹, 박성현→골퍼, 이길여→건물).
|
||||
57명 중 **18명**만 사진을 얻었고, 얻은 것은 `imageinfo.extmetadata` 에서 저작자·라이선스를
|
||||
받아 **화면에 표시한다** — CC BY-SA 의 조건이라 안 쓰면 위반이다.
|
||||
|
||||
**해야 할 일**: `services/external/wikimedia.py` 에 "문서 → pageimages → 라이선스" 경로를 붙이고,
|
||||
`imageCredit`(문자열)을 payload 에 넣는다. ★ **문자열이다** — 객체로 넣으면 렌더러가
|
||||
조용히 아무것도 안 그린다(`site/src/sections/items/common.tsx:206`).
|
||||
사진이 붙은 사람을 **앞으로** 정렬한다(빈 카드가 먼저 오면 목록이 비어 보인다).
|
||||
|
||||
### ⑤ 상한 — `maxItems` 를 올려야 한다
|
||||
|
||||
| 종류 | 지금 상한 | 목업 | 왜 |
|
||||
|---|---|---|---|
|
||||
| songs | 8 | 25 | 도시 하나에 그만큼 있다. 8곡이면 '다방'이 아니라 목록이다 |
|
||||
| people | 10 | 57 | 위키백과 '군산시 출신' 분류만으로 57명이 확인된다 |
|
||||
| chronicle | 14 | 7 | 상한은 맞는데 **채우는 쪽이 모자라다** |
|
||||
| postcard | 12 | 4 | 같다 |
|
||||
|
||||
상한을 올릴 때 **한 번에 다 받지 않는다.** 목업은 곡을 8개씩 세 번 나눠 받았다 —
|
||||
한 번에 25곡을 시키면 뒤쪽이 급격히 부실해진다. 나눠 받고 합칠 때 제목으로 중복을 지운다.
|
||||
프롬프트 분할 방법은 [PROMPTS.md](PROMPTS.md) 에 있다.
|
||||
|
||||
### ⑥ 객실 사진 — 출처 정책 결론이 필요하다
|
||||
|
||||
목업은 야놀자 등록본(`nol.yanolja.com/stay/domestic/10068088`)의 Next.js 플라이트 데이터에서
|
||||
`roomTypes[].photos` 를 파싱해 A동 12·B동 10 을 가져왔다(`rooms.json`).
|
||||
**이건 시연용으로 손으로 한 것이고 제품 경로가 아니다.**
|
||||
수집 어댑터를 만들려면 [DECISIONS 1-1](../../../../docs/DECISIONS.md) 과
|
||||
[DATA_SOURCE_RESEARCH.md](../../../../docs/DATA_SOURCE_RESEARCH.md) 를 먼저 읽는다 —
|
||||
**봇 탐지 우회는 결론과 무관하게 영구 금지**다.
|
||||
현실적인 제품 경로는 **사장님이 올리는 것**이거나 **업소가 준 채널의 공개 API** 다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 프롬프트를 새로 쓸 때 — 이 레포의 규칙
|
||||
|
||||
`SECTION_PROMPT_RULES`(`section-prompts.ts:55`)가 공통 7줄이고, 종류별 규칙을 덧붙인다.
|
||||
새 종류를 더할 때 지켜야 하는 것:
|
||||
|
||||
1. **`STORY_KINDS` 에 등록한다.** 빠지면 서버 생성 잡이 그 종류를 아예 모른다 —
|
||||
`daily` 가 그래서 한동안 빈칸이었다(주석에 실측이 적혀 있다).
|
||||
2. **`maxItems` 와 프롬프트 안의 숫자를 같게 둔다.** 어긋나면 받아 놓고 잘라 버린다.
|
||||
3. **빈 값을 지어내지 않는다**(공통 2번). 확실한 게 12개면 12개만 낸다.
|
||||
4. **출처는 실제로 열리는 페이지**여야 한다(공통 3번). 검색 결과 주소는 안 된다.
|
||||
5. **가사·시·소설 원문을 한 줄도 옮기지 않는다**(공통 5번).
|
||||
6. `[지역]` 같은 빈칸을 남기지 않는다 — `buildSectionPrompt` 가 채워서 내보낸다.
|
||||
7. 고친 뒤 **`npm run export:prompts`** 를 돌린다. 백엔드는 그 산출물을 읽는다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 검증 — "됐다"고 말하기 전에
|
||||
|
||||
목업에서 쓰는 방식 그대로 제품에도 필요하다.
|
||||
|
||||
| | 무엇 | 어디 |
|
||||
|---|---|---|
|
||||
| 생성 단계 | 규칙 위반이면 **결과물을 쓰지 않고 멈춘다**. 검사를 생성기 **안**에 둔다 | `build_itinerary.py:526` |
|
||||
| 렌더 단계 | 실제 브라우저로 열어 숫자를 센다. 34종 | `audit-all.mjs` |
|
||||
| 카로셀 | 레일 13개 × 데스크톱·모바일 | `rails-test.mjs` |
|
||||
|
||||
★ 목업에서 같은 실수가 반복된 원인은 "검사를 안 했다"가 아니라 **"검사가 빌드 밖에 있었다"** 였다.
|
||||
그때그때 쓴 일회용 스크립트로 돌리니, 뼈대를 고칠 때마다 이번에 안 본 규칙이 생겼다.
|
||||
경위는 [REVIEW-2026-09-11.md](REVIEW-2026-09-11.md) 2장.
|
||||
|
||||
---
|
||||
|
||||
## 6. 같이 읽을 것
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 목업을 고치고 배포하는 절차 · 밟으면 조용히 틀리는 자리 | [README.md](README.md) |
|
||||
| 실제로 쓴 프롬프트 전문과 나눠 받는 법 | [PROMPTS.md](PROMPTS.md) |
|
||||
| 화면에 나가는 텍스트 전문 | [TEXT.md](TEXT.md) |
|
||||
| 일정이 왜 틀렸고 무엇을 고쳤나 | [REVIEW-2026-09-11.md](REVIEW-2026-09-11.md) |
|
||||
| 값이 DB 에서 페이지까지 가는 길 | [docs/DATA_MODEL.md](../../../../docs/DATA_MODEL.md) |
|
||||
| 미결 사항 · 수집 어댑터 정책 | [docs/DECISIONS.md](../../../../docs/DECISIONS.md) |
|
||||
Loading…
Reference in New Issue
Block a user