From fedda2550d036780ee9fa86410f8bbc3d5a92b64 Mon Sep 17 00:00:00 2001 From: MJY Date: Mon, 6 Apr 2026 13:05:28 +0900 Subject: [PATCH 1/2] chore: reorganize docs and sharpen readme --- AGENTS.md | 8 ++- PLAN.md | 23 +++---- PROGRESS.md | 65 ++++++++++++------- README.md | 4 +- docs/README.md | 13 ++++ .../cloudflare-account-to-deploy.md | 2 +- docs/{ => deploy}/deploy-cloudflare.md | 2 +- prd.md => docs/product/prd.md | 0 trd.md => docs/product/trd.md | 0 package.json | 1 + 10 files changed, 78 insertions(+), 40 deletions(-) create mode 100644 docs/README.md rename docs/{ => deploy}/cloudflare-account-to-deploy.md (99%) rename docs/{ => deploy}/deploy-cloudflare.md (98%) rename prd.md => docs/product/prd.md (100%) rename trd.md => docs/product/trd.md (100%) diff --git a/AGENTS.md b/AGENTS.md index 4ba1f08..042fce1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ - 목표는 작업이 중간에 끊겨도 다음 세션에서 바로 이어갈 수 있게 만드는 것이다. ## 필수 규칙 -1. 작업을 시작하기 전에 `PLAN.md`와 `PROGRESS.md`를 먼저 확인한다. +1. 작업을 시작하기 전에 `PLAN.md`, `PROGRESS.md`, `docs/product/prd.md`, `docs/product/trd.md`를 먼저 확인한다. 2. 구현 우선순위나 범위가 바뀌면 코드보다 먼저 `PLAN.md`를 갱신한다. 3. 의미 있는 구현, 수정, 검증이 끝나면 `PROGRESS.md`를 즉시 갱신한다. 4. 세션을 마칠 때는 다음 사람이 바로 이어서 작업할 수 있을 정도로 현재 상태를 남긴다. @@ -23,6 +23,12 @@ - 지금까지 무엇을 했는지 - 어디까지 검증했는지 - 다음에 무엇부터 하면 되는지 +- `docs/product/prd.md` + - 제품 목표와 사용자 가치 + - MVP 범위와 우선 기능 +- `docs/product/trd.md` + - 기술 설계와 배포/운영 제약 + - 구현 시 지켜야 할 기술 기준 ## 작업 원칙 - 구현과 문서화는 분리하지 않는다. diff --git a/PLAN.md b/PLAN.md index 33faa9e..25f0267 100644 --- a/PLAN.md +++ b/PLAN.md @@ -4,7 +4,7 @@ 목표: Altteulmap를 `지도 기반 절약 장소 탐색 + 가격 제보` 서비스 기준에서 MVP 완성도와 출시 준비 수준까지 끌어올린다. ## 운영 규칙 -- 세션 시작: `PLAN.md` -> `PROGRESS.md` -> `prd.md` -> `trd.md` 순서로 먼저 확인 +- 세션 시작: `PLAN.md` -> `PROGRESS.md` -> `docs/product/prd.md` -> `docs/product/trd.md` 순서로 먼저 확인 - 작업 시작: 대상 cycle 또는 작업 상태를 `in_progress`로 바꾼 뒤 구현 시작 - 작업 종료: `PROGRESS.md`에 결과/검증/블로커 기록 후 `PLAN.md` 상태 갱신 - cycle 내 모든 작업이 끝나면 제목에 `(완료)` 표시 @@ -29,6 +29,7 @@ ## 최근 완료 메모 - README 포트폴리오 랜딩과 live 스크린샷 세트는 완료했다. 현재 GitHub 첫 화면에서는 제품 개요, 실데이터/배포 포인트, 핵심 화면, AI-native workflow를 짧게 확인할 수 있다. +- 루트 구조 정리도 완료했다. `docs/product`, `docs/deploy`, `docs/README.md`로 제품/배포 문서를 묶었고, 루트에는 운영 문서와 주요 설정 파일만 남기도록 build/test 산출물을 함께 정리했다. ## 현재 제품 상태 @@ -107,7 +108,7 @@ ### Cycle 2: 문서 체계 정비와 지도 검색/URL 상태 반영 (완료) | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| -| `townpet`의 문서 형식을 참고해 `PLAN.md`/`PROGRESS.md`를 운영 문서 형태로 재정리하고, `/map`에 지역/전역 검색과 URL 상태 반영을 추가한다 | Codex | P1 | `done` | 문서는 `운영 규칙/현재 우선순위/Active Plan/실행 로그` 구조를 갖고, 지도는 `q/scope` 기반 검색을 지원하며, `/api/places/map`가 지역/전역 검색을 모두 처리하고, lint/build/runtime 검증 결과가 `PROGRESS.md`에 남는다 | `PLAN.md`, `PROGRESS.md`, `prd.md`, `trd.md`, `src/app/map/page.tsx`, `src/features/places/map-explorer.tsx`, `src/features/places/repository.ts`, `src/app/api/places/map/route.ts` | +| `townpet`의 문서 형식을 참고해 `PLAN.md`/`PROGRESS.md`를 운영 문서 형태로 재정리하고, `/map`에 지역/전역 검색과 URL 상태 반영을 추가한다 | Codex | P1 | `done` | 문서는 `운영 규칙/현재 우선순위/Active Plan/실행 로그` 구조를 갖고, 지도는 `q/scope` 기반 검색을 지원하며, `/api/places/map`가 지역/전역 검색을 모두 처리하고, lint/build/runtime 검증 결과가 `PROGRESS.md`에 남는다 | `PLAN.md`, `PROGRESS.md`, `docs/product/prd.md`, `docs/product/trd.md`, `src/app/map/page.tsx`, `src/features/places/map-explorer.tsx`, `src/features/places/repository.ts`, `src/app/api/places/map/route.ts` | ### Cycle 3: 댓글과 기존 장소 가격 추가 제보 | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | @@ -117,29 +118,29 @@ ### Cycle 4: 운영 품질과 관리자 가격 관리 | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| -| 가격 검증/대표 가격 산정과 관리자 가격 수정 흐름을 정리하고, 쓰기 API에 최소 rate limit을 추가한다 | Codex | P1 | `done` | 가격 대표값 산정 규칙이 코드와 화면에서 일치하고, 운영자가 가격 항목을 수정/비활성화할 수 있으며, 장소 등록/신고/댓글/가격 제보 API에 최소 rate limit이 들어가고, 관련 검증이 `PROGRESS.md`에 남는다 | `trd.md`, `src/features/places/repository.ts`, `src/app/api/places/**`, `src/app/api/reports/**`, 관리자 페이지, DB schema | +| 가격 검증/대표 가격 산정과 관리자 가격 수정 흐름을 정리하고, 쓰기 API에 최소 rate limit을 추가한다 | Codex | P1 | `done` | 가격 대표값 산정 규칙이 코드와 화면에서 일치하고, 운영자가 가격 항목을 수정/비활성화할 수 있으며, 장소 등록/신고/댓글/가격 제보 API에 최소 rate limit이 들어가고, 관련 검증이 `PROGRESS.md`에 남는다 | `docs/product/trd.md`, `src/features/places/repository.ts`, `src/app/api/places/**`, `src/app/api/reports/**`, 관리자 페이지, DB schema | ### Cycle 5: 인증 정리와 출시 준비 | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| -| 로그인 상태 액션과 운영자 대시보드를 운영 가능한 수준으로 확장한다 | Codex | P1 | `done` | 로그인 사용자는 주요 화면 상단에서 계정 라벨과 로그아웃 버튼을 볼 수 있고, 운영자 사용자는 관리자 진입 링크와 `/admin` overview에서 장소 등록/가격 제보/신고 큐, 사용자 수, 현재 세션 수 같은 즉시 계산 가능한 지표를 확인할 수 있으며, 방문 수/활성 사용자 수는 별도 방문 이벤트 적재 기준으로 정의된 2차 작업 계획이 같이 남는다 | `src/app/**`, `src/features/auth/**`, `src/features/**/repository.ts`, `src/db/schema.ts`, `PLAN.md`, `PROGRESS.md`, `prd.md`, `trd.md`, 테스트 | +| 로그인 상태 액션과 운영자 대시보드를 운영 가능한 수준으로 확장한다 | Codex | P1 | `done` | 로그인 사용자는 주요 화면 상단에서 계정 라벨과 로그아웃 버튼을 볼 수 있고, 운영자 사용자는 관리자 진입 링크와 `/admin` overview에서 장소 등록/가격 제보/신고 큐, 사용자 수, 현재 세션 수 같은 즉시 계산 가능한 지표를 확인할 수 있으며, 방문 수/활성 사용자 수는 별도 방문 이벤트 적재 기준으로 정의된 2차 작업 계획이 같이 남는다 | `src/app/**`, `src/features/auth/**`, `src/features/**/repository.ts`, `src/db/schema.ts`, `PLAN.md`, `PROGRESS.md`, `docs/product/prd.md`, `docs/product/trd.md`, 테스트 | | public/admin 공통 헤더에 브랜드 로고와 핵심 이동 액션을 올려 모든 페이지에서 홈 복귀와 주요 이동을 일관되게 제공한다 | Codex | P1 | `done` | public 앱과 별도 admin 앱의 공통 레이아웃에 `알뜰맵` 로고, `장소 등록하기`, `북마크`, `로그인/로그아웃`, `운영자 관리` 액션이 공통 헤더로 제공되고, 기존 페이지별 중복 액션 블록은 제거되며, 현재 경로 기준 로그인/로그아웃 callback과 admin 진입 링크가 유지되고 검증 결과가 `PROGRESS.md`에 남는다 | `src/app/layout.tsx`, `apps/admin/src/app/layout.tsx`, `src/components/**`, `src/features/map/map-page.tsx`, `src/app/**`, `src/features/admin/pages/**`, `PROGRESS.md` | -| visit/activity 이벤트 적재를 추가해 관리자 대시보드에 방문 수와 DAU/WAU를 노출한다 | Codex | P1 | `done` | 방문 이벤트가 visitor/session 기준으로 저장되고, `/admin` overview에서 오늘/7일 방문 수, 고유 방문자 수, DAU/WAU, 재방문율 같은 활동 지표를 확인할 수 있으며, dedupe/rate limit/보존 정책과 검증 로그가 문서에 남는다 | `src/db/schema.ts`, migration, `src/app/**` 또는 middleware, `src/features/admin/**`, `PLAN.md`, `PROGRESS.md`, `trd.md`, 테스트 | +| visit/activity 이벤트 적재를 추가해 관리자 대시보드에 방문 수와 DAU/WAU를 노출한다 | Codex | P1 | `done` | 방문 이벤트가 visitor/session 기준으로 저장되고, `/admin` overview에서 오늘/7일 방문 수, 고유 방문자 수, DAU/WAU, 재방문율 같은 활동 지표를 확인할 수 있으며, dedupe/rate limit/보존 정책과 검증 로그가 문서에 남는다 | `src/db/schema.ts`, migration, `src/app/**` 또는 middleware, `src/features/admin/**`, `PLAN.md`, `PROGRESS.md`, `docs/product/trd.md`, 테스트 | | 배포/점검 스크립트가 쉘·CI env를 로컬 `.env*`보다 우선 사용해 운영 URL과 split 배포 값이 덮어쓰이지 않게 정리한다 | Codex | P1 | `done` | `deploy:check`와 public/admin build 스크립트는 `.env`, `.env.production`, `.env.local`, `.env.production.local`을 읽더라도 이미 주입된 쉘/CI env를 덮어쓰지 않고, split 배포용 `NEXTAUTH_URL`/`ADMIN_APP_URL`/`SITE_URL` 점검 결과와 문서 로그가 `PROGRESS.md`에 남는다 | `scripts/check-cloudflare-deploy.mjs`, `scripts/build-public-worker.mjs`, env loading helper, `README.md`, 배포 문서 | -| `workers.dev` 운영 URL 기준으로 public/admin split 배포를 실제 적용하고, 응답/리다이렉트/SEO 엔드포인트를 재점검한다 | Codex | P1 | `done` | `altteulmap.altteul-lab.workers.dev`와 `altteulmap-admin.altteul-lab.workers.dev` 기준으로 `deploy:check:admin -> deploy:admin -> deploy:check:public -> deploy:public`이 통과하고, `/`, `/admin`, `/api/admin/places`, `/robots.txt`, `/sitemap.xml` 응답 확인 결과가 `PROGRESS.md`에 남는다 | `docs/deploy-cloudflare.md`, `docs/cloudflare-account-to-deploy.md`, `package.json`, `.env.production.local`, Wrangler 인증 | +| `workers.dev` 운영 URL 기준으로 public/admin split 배포를 실제 적용하고, 응답/리다이렉트/SEO 엔드포인트를 재점검한다 | Codex | P1 | `done` | `altteulmap.altteul-lab.workers.dev`와 `altteulmap-admin.altteul-lab.workers.dev` 기준으로 `deploy:check:admin -> deploy:admin -> deploy:check:public -> deploy:public`이 통과하고, `/`, `/admin`, `/api/admin/places`, `/robots.txt`, `/sitemap.xml` 응답 확인 결과가 `PROGRESS.md`에 남는다 | `docs/deploy/deploy-cloudflare.md`, `docs/deploy/cloudflare-account-to-deploy.md`, `package.json`, `.env.production.local`, Wrangler 인증 | | 공개 쓰기 rate limit 정책을 중앙화하고 응답 헤더를 통일해 운영 중 관측과 조정을 쉽게 만든다 | Codex | P1 | `done` | 장소 등록/댓글/가격 제보/신고/반응/회원가입 route가 공통 정책 정의를 사용하고, `429`뿐 아니라 정상 응답에도 남은 횟수·리셋 정보를 헤더로 내려주며, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/lib/rate-limit.ts`, `src/lib/public-write-actor.ts`, `src/app/api/**`, `PLAN.md`, `PROGRESS.md` | | 공개 쓰기와 회원가입 화면이 `429` 응답의 `Retry-After`를 읽어 남은 대기 시간을 사용자에게 일관되게 안내한다 | Codex | P1 | `done` | 장소 등록/댓글/가격 제보/신고/반응/회원가입 UI는 rate limit에 걸렸을 때 단순 실패 문구 대신 남은 대기 시간과 재시도 안내를 같은 형식으로 보여주고, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/**`, `src/app/api/**`, `src/lib/rate-limit.ts`, `PLAN.md`, `PROGRESS.md` | | 관리자 큐 페이지에 공통 운영 네비/요약을 붙이고, 신고 큐는 상태 필터로 바로 좁혀 볼 수 있게 정리한다 | Codex | P1 | `done` | `/admin/places`, `/admin/prices`, `/admin/reports`는 공통 queue nav와 핵심 개수 요약을 공유하고, `/admin/reports`는 `all/open/reviewing/resolved/dismissed` 필터를 지원하며, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/admin/**`, `src/features/reports/**`, `PLAN.md`, `PROGRESS.md`, 테스트 | | 공개 쓰기/회원가입 rate limit 수치를 route 비용에 맞게 다시 조정하고, 응답 헤더에 policy/window 정보를 추가해 운영 중 관측 포인트를 늘린다 | Codex | P1 | `done` | `RATE_LIMIT_POLICIES` 값이 place/comment/price/report/reaction/signup 비용에 맞게 다시 조정되고, 모든 공개 쓰기 응답은 기존 `X-RateLimit-*` 외에 policy/window 헤더도 내려주며, 관련 회귀와 로그가 `PROGRESS.md`에 남는다 | `src/lib/rate-limit.ts`, `src/app/api/**`, `tests/e2e/**`, `PLAN.md`, `PROGRESS.md` | | 관리자 moderation 액션의 성공 피드백을 즉시 반영하고, admin/public 재검증 경로를 공통 helper로 모은다 | Codex | P1 | `done` | 장소 승인/가격 제보 검토/신고 상태 변경/가격 항목 수정 후 카드나 상태가 refresh 전에 즉시 반영되고, admin/public 관련 `revalidatePath` 호출은 공통 helper로 정리되며, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/admin/**`, `src/features/places/**`, `src/features/reports/**`, `src/app/api/admin/**`, `PLAN.md`, `PROGRESS.md` | | 공개 상세 페이지에서 새로 승인된 한글 slug 장소가 404로 보이는 경로를 조사하고 route/detail lookup을 고친다 | Codex | P1 | `done` | 한글 slug를 가진 승인 장소가 홈 검색 노출 직후에도 `/place/[slug]`에서 안정적으로 열리고, 상세 페이지/metadata/API 응답 계약 차이 원인이 제거되며, 재현 케이스와 검증 로그가 `PROGRESS.md`에 남는다 | `src/app/place/[id]/page.tsx`, `src/features/places/repository.ts`, 관련 테스트/런타임 검증, `PLAN.md`, `PROGRESS.md` | -| 공개 지도 가격 필터를 제거하고 지도/검색 흐름을 단순화한다 | Codex | P1 | `done` | 공개 지도에서 가격 필터 UI, `maxPrice` URL/API 계약, 관련 Playwright 회귀와 제품 문서가 함께 제거되고, 카테고리/검색 범위 기반 탐색은 유지되며, 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/map/map-page.tsx`, `src/features/places/map-explorer.tsx`, `src/app/api/places/map/route.ts`, `src/features/places/repository.ts`, `tests/e2e/**`, `PLAN.md`, `PROGRESS.md`, `prd.md`, `trd.md` | +| 공개 지도 가격 필터를 제거하고 지도/검색 흐름을 단순화한다 | Codex | P1 | `done` | 공개 지도에서 가격 필터 UI, `maxPrice` URL/API 계약, 관련 Playwright 회귀와 제품 문서가 함께 제거되고, 카테고리/검색 범위 기반 탐색은 유지되며, 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/map/map-page.tsx`, `src/features/places/map-explorer.tsx`, `src/app/api/places/map/route.ts`, `src/features/places/repository.ts`, `tests/e2e/**`, `PLAN.md`, `PROGRESS.md`, `docs/product/prd.md`, `docs/product/trd.md` | | 공개 지도에 `현재 위치` 이동과 `이 지역 검색` 수동 재조회 액션을 추가해 드래그 탐색 흐름을 보강한다 | Codex | P1 | `done` | 지도 우측 또는 상단 컨트롤에서 현재 위치로 이동할 수 있고, viewport 모드에서는 사용자가 지도를 움직인 뒤 `이 지역 검색` 액션으로 현재 bounds 기준 place/cluster를 다시 가져올 수 있으며, 초기 진입/전역 검색/모바일 시트 흐름을 깨지 않고 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/map/naver-map-panel.tsx`, `src/features/places/map-explorer.tsx`, `tests/e2e/map.spec.ts`, `tests/e2e/map.mobile.spec.ts`, `PLAN.md`, `PROGRESS.md` | | 공개 지도 목록 패널과 상세 시트의 중복 메타/상태 블록을 줄여 모바일 밀도를 한 번 더 정리한다 | Codex | P1 | `done` | 모바일/데스크톱 지도 목록 패널은 상태 안내를 더 압축된 요약 블록으로 보여주고, 상세 시트는 헤더와 본문에 중복되던 장소 메타를 줄여 가격/액션 중심으로 시작하며, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/places/map-explorer.tsx`, `src/features/places/place-detail-sheet.tsx`, `src/features/map/map-page.tsx`, `src/app/globals.css`, `PLAN.md`, `PROGRESS.md` | | mock runtime 기준 데스크톱 지도 상세의 비회원 좋아요 회귀를 조사하고 `map.spec.ts`를 다시 안정화한다 | Codex | P2 | `done` | `USE_MOCK_DATA=true` 기준 `tests/e2e/map.spec.ts`가 검색 가능한 장소를 동적으로 잡더라도 비회원 좋아요 count가 즉시 증가하고, preview/detail fetch 경쟁 또는 mock reaction 저장 경로 원인이 제거되며, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/places/place-detail-sheet.tsx`, `src/features/places/repository.ts`, `tests/e2e/map.spec.ts`, `PLAN.md`, `PROGRESS.md` | | 좋아요 반응 후속으로 홈에 `인기 장소` 추천 섹션을 추가해 반응/최근 갱신 기반의 상위 장소를 노출한다 | Codex | P2 | `done` | 홈 지도 화면에서 상위 6개 추천 장소가 `좋아요 우선 + 최근 갱신` 기준으로 노출되고, 각 카드에서 상세 페이지로 이동할 수 있으며, DB/mock 모두 동작하고 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/places/repository.ts`, `src/features/map/map-page.tsx`, 새 UI 컴포넌트, 테스트/문서 | | 공개 UI polish 후속으로 모바일 헤더를 1줄 기준으로 줄이고, 홈을 map-first 레이아웃으로 다시 정리하며, auth/등록 화면의 상단 chrome과 카드 레이어를 감량한다 | Codex | P1 | `done` | 모바일 전역 헤더는 첫 화면을 과도하게 차지하지 않고, 홈은 모바일/데스크톱 모두 지도 시작 위치가 더 앞당겨지며, 로그인/회원가입/장소 등록 화면은 중복 네비와 설명 없이 입력/액션 중심 레이아웃으로 정리되고, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/components/global-header.tsx`, `src/components/brand-mark.tsx`, `src/features/map/map-page.tsx`, `src/features/auth/**`, `src/app/login/page.tsx`, `src/app/signup/page.tsx`, `src/app/submit/page.tsx`, `src/features/submission/place-submit-form.tsx`, `PROGRESS.md` | -| 로컬 credentials 중심 인증을 실제 로그인/회원가입 경로로 확장하고, 핵심 사용자 흐름 테스트/SEO/배포 체크리스트를 정리한다 | Codex | P2 | `done` | 로컬 credentials 로그인/회원가입과 선택적 소셜 가입 진입면, 지도 탐색/모바일 시트/익명 장소 등록/북마크/신고/관리자 승인/관리자 가격 검토 E2E가 반복 가능하게 정리되고, sitemap/canonical/metadata와 Cloudflare 배포 체크리스트가 최신 상태가 된다 | `src/auth.ts`, `src/app/login/page.tsx`, `src/app/signup/page.tsx`, 테스트 코드, `README.md`, `trd.md` | +| 로컬 credentials 중심 인증을 실제 로그인/회원가입 경로로 확장하고, 핵심 사용자 흐름 테스트/SEO/배포 체크리스트를 정리한다 | Codex | P2 | `done` | 로컬 credentials 로그인/회원가입과 선택적 소셜 가입 진입면, 지도 탐색/모바일 시트/익명 장소 등록/북마크/신고/관리자 승인/관리자 가격 검토 E2E가 반복 가능하게 정리되고, sitemap/canonical/metadata와 Cloudflare 배포 체크리스트가 최신 상태가 된다 | `src/auth.ts`, `src/app/login/page.tsx`, `src/app/signup/page.tsx`, 테스트 코드, `README.md`, `docs/product/trd.md` | | 공개 UI polish 2차로 인증 진입면은 액션 중심으로 다시 단순화하고, 지도 필터/검색 칩은 줄바꿈 대신 가로 레일로 정리한다 | Codex | P1 | `done` | `/login`, `/signup`은 설정성 패널 없이 로그인/가입 액션에만 집중하고, 지도 화면의 모바일/데스크톱 필터 칩과 검색 범위 칩은 과도한 줄바꿈 없이 가로 스크롤 또는 compact 배치로 정리되며, 관련 검증과 문서 로그가 `PROGRESS.md`에 남는다 | `src/app/login/page.tsx`, `src/app/signup/page.tsx`, `src/features/auth/**`, `src/features/map/map-page.tsx`, `src/app/globals.css`, E2E/verify | | 모바일 공개 지도 UX와 인증 진입면을 모바일 우선 기준으로 다시 정리한다 | Codex | P1 | `done` | 모바일에서 검색 외 필터는 접기 가능하고, 목록/상세 시트가 더 위로 올라오며, 로그인/회원가입이 기능 집중형 단일 카드 구조로 정리되고 관련 검증 로그가 `PROGRESS.md`에 남는다 | `src/app/map/page.tsx`, `src/features/places/map-explorer.tsx`, `src/features/places/place-detail-sheet.tsx`, `src/app/login/page.tsx`, `src/app/signup/page.tsx` | | 모바일 지도 UX 후속 정리로 전역 헤더와 시트 레이어 충돌을 없애고, 모바일 시트를 safe-area 기준 bottom sheet로 줄이며, 저줌 개요에서는 cluster/place 혼재를 줄인다 | Codex | P1 | `done` | 모바일 목록/상세 시트는 헤더 위에서 안정적으로 열리고, 닫기 액션이 항상 보이며, 시트는 화면을 과도하게 덮지 않고 safe-area를 고려하며, 모바일 개요 지도에서는 cluster와 place가 동시에 난잡하게 섞여 보이지 않도록 marker 정책 또는 스타일이 분리되고, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/components/global-header.tsx`, `src/features/places/map-explorer.tsx`, `src/features/places/place-detail-sheet.tsx`, `src/features/map/naver-map-panel.tsx`, `src/features/places/repository.ts`, 모바일 E2E | @@ -159,19 +160,19 @@ | public worker에서 넓은 viewport `/api/places/map`가 Workers hang 없이 응답하고, OpenNext preview/deploy가 `middleware-manifest` 동적 require 때문에 500으로 죽지 않도록 런타임 hotfix를 정리한다 | Codex | P1 | `done` | public worker preview와 live에서 넓은 viewport `map` API가 `1101` 없이 JSON을 반환하고, root/API 모두 `middleware-manifest` 동적 require 오류 없이 동작하며, 관련 빌드/배포/검증 로그가 `PROGRESS.md`에 남는다 | `src/features/places/repository.ts`, `scripts/**`, `package.json`, `PROGRESS.md`, Cloudflare preview/deploy | | 공개 지도 bounds 기반 재조회에 짧은 서버 캐시를 넣고, 공개 데이터 쓰기 이후에는 즉시 비워 같은 viewport 연속 조회 비용을 줄인다 | Codex | P1 | `done` | `bounds`가 있는 `/api/places/map` 조회는 짧은 TTL 메모리 캐시를 재사용하고, 반응/가격 대표값/승인 같은 공개 지도 데이터 변경 후에는 캐시가 즉시 비워지며, 검증 로그가 `PROGRESS.md`에 남는다 | `src/features/places/repository.ts`, `src/app/api/places/map/route.ts`, 공개 쓰기 경로, `PROGRESS.md`, 런타임 검증 | | 공개 지도의 플레이스 정렬 기능을 제거하고 검색/필터만 남긴다 | Codex | P1 | `done` | 지도 화면에서 정렬 UI와 `sort` query/API 계약이 제거되고, 공개 플레이스 목록은 고정 순서로 노출되며, 관련 테스트/문서/검증 로그가 같이 갱신된다 | `src/app/map/page.tsx`, `src/features/places/map-explorer.tsx`, `src/app/api/places/map/route.ts`, `src/features/places/**`, `tests/e2e/map.spec.ts`, 문서 | -| 공개 장소 등록을 텍스트 입력 중심으로 단순화하고, 운영자 승인 단계에서 위치를 확정한다 | Codex | P1 | `done` | 공개 등록 폼은 상호명/주소/가격 등 텍스트 입력만 받고, 공개 제출 API는 좌표 없이 접수되며, 위치/지도/링크 처리는 운영자 승인 단계에서 수행하도록 화면/API/문서/검증 로그가 같은 규칙으로 정리된다 | `src/features/submission/**`, `src/features/places/admin-place-review-form.tsx`, `src/app/api/places/route.ts`, `src/app/api/admin/places/[id]/route.ts`, `tests/e2e/submission-admin.spec.ts`, `prd.md`, `trd.md` | +| 공개 장소 등록을 텍스트 입력 중심으로 단순화하고, 운영자 승인 단계에서 위치를 확정한다 | Codex | P1 | `done` | 공개 등록 폼은 상호명/주소/가격 등 텍스트 입력만 받고, 공개 제출 API는 좌표 없이 접수되며, 위치/지도/링크 처리는 운영자 승인 단계에서 수행하도록 화면/API/문서/검증 로그가 같은 규칙으로 정리된다 | `src/features/submission/**`, `src/features/places/admin-place-review-form.tsx`, `src/app/api/places/route.ts`, `src/app/api/admin/places/[id]/route.ts`, `tests/e2e/submission-admin.spec.ts`, `docs/product/prd.md`, `docs/product/trd.md` | | 장소 등록 진입/제출을 로그인 없이도 허용하고, 공개 CTA 문구를 `장소 등록` 기준으로 맞춘다 | Codex | P1 | `done` | 비로그인 사용자가 `/submit`에 바로 진입해 장소 등록 요청을 제출할 수 있고, 등록 API가 익명 제출을 허용하며, 공개 화면 CTA와 검증 로그가 `장소 등록` 기준으로 정리된다 | `src/app/submit/page.tsx`, `src/app/api/places/route.ts`, `src/app/map/page.tsx`, 테스트 코드 | | 공개 쓰기 기능에서 북마크만 로그인 유지하고 댓글/신고/가격 제보는 익명 허용으로 전환하며, `비슷한 장소` UI를 제거한다 | Codex | P1 | `done` | 공개 상세/상세 시트/신고 페이지에서 댓글, 가격 제보, 신고가 로그인 없이 동작하고, 북마크만 로그인 요구를 유지하며, `비슷한 장소` 섹션과 관련 응답/문구/검증 로그가 정리된다 | `src/app/place/[id]/page.tsx`, `src/features/places/place-detail-sheet.tsx`, `src/app/report/page.tsx`, `src/app/api/places/**`, `src/app/api/reports/route.ts`, `src/features/places/repository.ts`, 테스트 코드, DB schema | | 모바일 목록/상세 시트에 스냅 상태와 기본 드래그 제스처를 추가해 바텀시트 감각을 한 번 더 정리한다 | Codex | P1 | `done` | 모바일 목록 시트는 peek/expanded 두 단계 상태를 가지며, 핸들 또는 헤더 드래그로 확장/축소/닫기가 가능하고, 상세 시트도 상단 헤더 드래그로 닫을 수 있으며, 모바일 E2E와 검증 로그가 `PROGRESS.md`에 남는다 | `src/features/places/map-explorer.tsx`, `src/features/places/place-detail-sheet.tsx`, `src/app/globals.css`, `tests/e2e/map.mobile.spec.ts`, `PLAN.md`, `PROGRESS.md` | | 공개 쓰기와 북마크의 public 재검증 경로를 공용 helper로 모아 홈/지도/상세 반영을 일관되게 정리한다 | Codex | P1 | `done` | 댓글/가격 제보/반응/북마크 route는 흩어진 `revalidatePath` 호출 대신 공용 helper를 사용하고, place detail/home/map/bookmarks/admin queue 반영 범위가 액션별로 명시되며, 관련 검증과 로그가 `PROGRESS.md`에 남는다 | `src/features/places/**`, `src/app/api/places/**`, `src/app/api/bookmarks/[id]/route.ts`, `PLAN.md`, `PROGRESS.md` | -| 운영 도메인 기준 read-only smoke 스크립트를 추가해 public/admin URL의 `robots`, `sitemap`, canonical, admin redirect를 한 번에 재검증한다 | Codex | P1 | `done` | `workers.dev` 또는 custom domain 값을 넣으면 public `/`, `/robots.txt`, `/sitemap.xml`, sample place canonical, public `/admin`, public `/api/admin/places`, admin `/admin`, admin `/login`을 읽기 전용으로 점검하는 smoke 스크립트와 문서가 추가되고, 실행 로그가 `PROGRESS.md`에 남는다 | `scripts/**`, `package.json`, `README.md`, `docs/deploy-cloudflare.md`, `PLAN.md`, `PROGRESS.md` | +| 운영 도메인 기준 read-only smoke 스크립트를 추가해 public/admin URL의 `robots`, `sitemap`, canonical, admin redirect를 한 번에 재검증한다 | Codex | P1 | `done` | `workers.dev` 또는 custom domain 값을 넣으면 public `/`, `/robots.txt`, `/sitemap.xml`, sample place canonical, public `/admin`, public `/api/admin/places`, admin `/admin`, admin `/login`을 읽기 전용으로 점검하는 smoke 스크립트와 문서가 추가되고, 실행 로그가 `PROGRESS.md`에 남는다 | `scripts/**`, `package.json`, `README.md`, `docs/deploy/deploy-cloudflare.md`, `PLAN.md`, `PROGRESS.md` | | smoke가 드러낸 Workers runtime hang을 줄이기 위해 public place read 경로의 guest cookie 조회와 DB read fallback을 보강한다 | Codex | P1 | `done` | Cloudflare/OpenNext runtime에서 guest place detail/reaction/comment 경로가 `next/headers` cookie 조회 때문에 hang 되지 않고, public map/detail read는 DB 응답이 지연되더라도 제한 시간 안에 mock fallback으로 응답하며, 관련 smoke/runtime 검증 로그가 `PROGRESS.md`에 남는다 | `src/lib/visitor-id.ts`, `src/lib/public-write-actor.ts`, `src/app/place/[id]/page.tsx`, `src/app/api/places/[id]/route.ts`, `src/features/places/repository.ts`, `PLAN.md`, `PROGRESS.md` | ### Cycle 6: 반복 방문 기능 확장 | 작업명 | 담당 에이전트 | 우선순위 | 상태 | 완료기준(DoD) | 의존성 | |---|---|---|---|---|---| -| 플레이스별 좋아요/싫어요를 먼저 붙여 공개 카운트와 사용자 반응 저장을 제공하고, 이후 공유 후속 활용 범위를 재판단한다 | Codex | P2 | `done` | 공개 상세와 상세 시트에 좋아요/싫어요 UI가 생기고, visitor cookie 또는 로그인 사용자 기준으로 한 플레이스에 한 반응만 저장되며, 공개 읽기에는 카운트가 노출되고, route/schema/repository/DB migration/검증 결과가 `PROGRESS.md`에 남는다 | 벤치마크 분석 메모, `prd.md`, `trd.md`, `src/db/schema.ts`, `src/features/places/repository.ts`, `src/app/api/places/**` | +| 플레이스별 좋아요/싫어요를 먼저 붙여 공개 카운트와 사용자 반응 저장을 제공하고, 이후 공유 후속 활용 범위를 재판단한다 | Codex | P2 | `done` | 공개 상세와 상세 시트에 좋아요/싫어요 UI가 생기고, visitor cookie 또는 로그인 사용자 기준으로 한 플레이스에 한 반응만 저장되며, 공개 읽기에는 카운트가 노출되고, route/schema/repository/DB migration/검증 결과가 `PROGRESS.md`에 남는다 | 벤치마크 분석 메모, `docs/product/prd.md`, `docs/product/trd.md`, `src/db/schema.ts`, `src/features/places/repository.ts`, `src/app/api/places/**` | | 공유 강화 1차로 상세/목록/추천 카드의 링크 payload와 공유 유입 source를 정리한다 | Codex | P2 | `done` | `PlaceShareButton`은 장소명/지역/대표가격 중심 공유 문구와 `ref=share`, `source=detail|detail_sheet|list|trending`가 붙은 URL을 사용하고, 상세 페이지/상세 시트/지도 목록/인기 장소 카드에서 같은 규칙으로 공유할 수 있으며, 관련 회귀와 문서/검증 로그가 `PROGRESS.md`에 남는다 | `src/features/places/place-share-button.tsx`, `src/features/places/map-explorer.tsx`, `src/features/places/trending-places-section.tsx`, `src/app/place/[id]/page.tsx`, `tests/e2e/map.spec.ts`, `PLAN.md`, `PROGRESS.md` | | 공유 링크의 `ref=share`, `source`를 visit telemetry에 반영해 관리자 overview에서 공유 유입을 본다 | Codex | P2 | `done` | public visit tracker와 `/api/telemetry/visit`는 공유 유입 query를 정규화해 저장하고, `visit_activity`는 bucket 단위의 공유 ref/source를 보존하며, `/admin` overview에서 오늘/7일 공유 유입과 source breakdown을 볼 수 있고, migration/검증 로그가 `PROGRESS.md`에 남는다 | `src/features/telemetry/**`, `src/app/api/telemetry/visit/route.ts`, `src/db/schema.ts`, `drizzle/**`, `src/features/admin/**`, `tests/e2e/admin-dashboard.spec.ts`, `PLAN.md`, `PROGRESS.md` | diff --git a/PROGRESS.md b/PROGRESS.md index 49a324f..3155363 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -5,6 +5,7 @@ ## 진행 현황 요약 - Cycle 10: 관리자 실제 구현을 `src/features/admin/**`로 모으고, public 앱 `entrypoints`와 별도 `apps/admin` 빌드를 추가해 관리자 분리 1차 완료. public `cf:build:public`은 `/admin`, `/api/admin`을 external redirect/API stub로 유지한 채 빌드하고, `deploy:check:public`, `deploy:check:admin`, `SITE_URL` 기준, `workers.dev` split 배포와 remote smoke까지 정리했다. custom domain 적용은 필요할 때만 마지막 운영 단계로 남아 있다 - Cycle 10 후속: admin telemetry가 `useSearchParams()` 때문에 standalone admin Cloudflare build를 깨던 경로를 제거했고, admin build는 이제 `ADMIN_APP_URL`을 `NEXTAUTH_URL`로 강제해 shared `.env.production.local`에서도 올바른 callback 기준으로 동작한다. `deploy:check:admin`, `cf:build:admin`, live admin 재배포까지 다시 통과했다 +- 문서/루트 정리 후속: 제품 문서와 배포 문서를 `docs/product`, `docs/deploy`로 재배치했고, `docs/README.md` 인덱스를 추가했다. 루트의 `.next`, `.open-next`, `apps/admin/.next`, `test-results`, `tsconfig.tsbuildinfo` 등 생성 산출물도 `clean:artifacts` 스크립트와 함께 정리했다 - Cycle 0: 프로젝트 로컬 기반, DB 경로, 지도 탐색, 장소 상세, 등록, 신고, 북마크, 관리자 검토, 로컬 인증, 네이버 지도 연동 완료 - Cycle 1: 현재 위치 버튼, viewport 재조회, 모바일 목록 바텀시트, 모바일 상세 시트 기초 정리 완료 - Cycle 2: `PLAN.md`/`PROGRESS.md` 운영 문서 형식 정비, 지역/전역 검색, 검색 URL 상태 반영 완료 @@ -42,6 +43,20 @@ ## 실행 로그 +### 2026-04-06 13:18 KST: 루트 문서 재배치와 build 산출물 정리 +- 완료 내용 + - 루트의 product 문서를 `docs/product/` 하위로 옮겼다. + - 배포 문서는 `docs/deploy/` 하위로 묶었다. + - `/Users/alex/project/altteulmap/docs/README.md`를 추가해 product/deploy/project 문서 인덱스를 만들었다. + - `/Users/alex/project/altteulmap/AGENTS.md`, `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/PROGRESS.md`, `/Users/alex/project/altteulmap/README.md`의 문서 경로 참조를 새 위치로 맞췄다. + - `/Users/alex/project/altteulmap/package.json`에 `clean:artifacts` 스크립트를 추가하고 실제로 root/app build 산출물을 정리했다. + - `/Users/alex/project/altteulmap/README.md` 상단에 이 프로젝트를 만든 배경을 추가했다. `거지맵`에서 받은 영감, 음식점 외 업종 확장, 공공데이터 활용, 고물가/취업난이라는 시대감과의 연결을 짧게 설명하도록 정리했다. +- 검증 결과 + - `npm run clean:artifacts` 통과 + - stale 문서 경로 검색 결과 없음 +- 메모 + - 운영용 문서인 `PLAN.md`, `PROGRESS.md`는 다음 세션 작업 규칙과 접근성을 위해 루트에 유지했다. + ### 2026-04-06 12:52 KST: README 채용 제출용 압축 polish와 admin Cloudflare build 복구 - 완료 내용 - `/Users/alex/project/altteulmap/README.md`를 한 번 더 압축해 제품 소개 페이지 톤으로 정리했다. `At a Glance`, `Highlights`, `Screenshots`, `Why This Repo Is Worth Opening`, `AI-Native Workflow` 중심으로 재구성했고, GitHub에서 바로 렌더되도록 이미지 경로를 `docs/readme/*.png` 상대 경로로 맞췄다. @@ -320,7 +335,7 @@ - `/Users/alex/project/altteulmap/PLAN.md`에서 `좋아요 랭킹`을 미구현 핵심/보류 범위에서 제거하고, 확장 기능 2차 우선순위를 `공유 후속 활용 범위` 기준으로 다시 정리했다. - 같은 문서의 반응 기능/공유 기능 설명과 결정 메모도 현재 기준에 맞게 수정해, 별도 랭킹 화면은 만들지 않고 홈 `인기 장소` 추천 섹션만 유지한다는 정책을 명시했다. - `/Users/alex/project/altteulmap/PROGRESS.md`의 진행 현황 요약과 다음 우선순위도 랭킹 제외 기준으로 갱신했다. - - `/Users/alex/project/altteulmap/prd.md`의 출시 후 빠른 후속 권장 항목은 `랭킹/추천 뷰` 대신 `추천 뷰 고도화`로 바꿨다. + - `/Users/alex/project/altteulmap/docs/product/prd.md`의 출시 후 빠른 후속 권장 항목은 `랭킹/추천 뷰` 대신 `추천 뷰 고도화`로 바꿨다. - 검증 결과 - 문서 범위 조정 작업이라 별도 코드 검증은 실행하지 않았다. - 메모 @@ -364,7 +379,7 @@ - 완료 내용 - `/Users/alex/project/altteulmap/PLAN.md`에서 확장 기능 2차 우선순위를 `공유/랭킹` 기준으로 다시 적고, `미구현 핵심`의 사진 업로드 항목을 제거했다. - 같은 문서의 Cycle 6 설명도 `공유/사진 업로드` 대신 `공유/랭킹` 재판단으로 바꿨고, 결정 메모에 사진 업로드를 저장 용량과 운영 비용 이유로 현재 범위에서 제외한다는 정책을 남겼다. - - `/Users/alex/project/altteulmap/trd.md`의 오픈 기술 이슈도 `이미지 업로드를 MVP에 포함할지 여부`에서 `현재 범위에서 제외 유지`로 정리했다. + - `/Users/alex/project/altteulmap/docs/product/trd.md`의 오픈 기술 이슈도 `이미지 업로드를 MVP에 포함할지 여부`에서 `현재 범위에서 제외 유지`로 정리했다. - `/Users/alex/project/altteulmap/PROGRESS.md`의 다음 우선순위도 `공유/랭킹` 기준으로 맞췄다. - 검증 결과 - 문서 결정 반영 작업이라 별도 코드 검증은 실행하지 않았다. @@ -382,7 +397,7 @@ - 회원가입: 30분 3회 - 같은 helper가 내려주는 헤더에 `X-RateLimit-Policy`, `X-RateLimit-Window`를 추가했다. 기존 `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After`와 함께 보면 어떤 bucket이 어떤 창으로 적용되는지 바로 알 수 있다. - `/Users/alex/project/altteulmap/tests/e2e/comments.spec.ts`의 rate limit 회귀는 하드코딩된 `10회` 대신 첫 응답의 `x-ratelimit-limit`를 읽어 현재 정책 수치에 따라 동적으로 quota를 채우도록 바꿨다. 같은 테스트에서 `x-ratelimit-policy=place_comment_submission`, `x-ratelimit-window=600`도 같이 검증한다. - - `/Users/alex/project/altteulmap/trd.md`에도 현재 운영 기준 정책 수치와 헤더 계약을 반영했다. + - `/Users/alex/project/altteulmap/docs/product/trd.md`에도 현재 운영 기준 정책 수치와 헤더 계약을 반영했다. - 검증 결과 - `npm run verify:quick` 통과 - `npm run verify` 통과 @@ -437,7 +452,7 @@ ### 2026-04-04 16:20 KST: 운영 smoke 스크립트 정리와 Workers public read hang 완화 - 완료 내용 - `/Users/alex/project/altteulmap/scripts/smoke-local.mjs`를 현재 제품 계약에 맞춰 read-only smoke로 다시 정리했다. 이제 홈 canonical, `robots.txt`, `sitemap.xml`, sample place canonical, map API, place detail API, 로그인 화면 렌더만 검증하고, 예전 `credentials -> bookmarks/admin API` 검사는 제거했다. - - `/Users/alex/project/altteulmap/scripts/smoke-remote.mjs`를 추가하고, `/Users/alex/project/altteulmap/package.json`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`에 remote smoke 사용법과 점검 범위를 반영했다. 로그인 matcher도 더 이상 `로컬 로그인` 문구를 기대하지 않고 `login-form` 렌더 기준으로 본다. + - `/Users/alex/project/altteulmap/scripts/smoke-remote.mjs`를 추가하고, `/Users/alex/project/altteulmap/package.json`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`에 remote smoke 사용법과 점검 범위를 반영했다. 로그인 matcher도 더 이상 `로컬 로그인` 문구를 기대하지 않고 `login-form` 렌더 기준으로 본다. - live smoke 조사 과정에서 public `map/detail`과 guest write 경로가 Cloudflare/OpenNext runtime에서 hang 되는 현상을 확인했고, 원인 후보를 둘로 나눠 정리했다. `next/headers` 기반 visitor cookie 조회는 request-driven API 경로에서 hang을 유발했고, DB read는 응답이 지연되거나 schema가 없을 때 fallback 전에 멈출 수 있었다. - `/Users/alex/project/altteulmap/src/lib/visitor-id.ts`는 `next/headers` 의존을 제거하고 raw `cookie` header 파싱 helper로 바꿨다. `/Users/alex/project/altteulmap/src/lib/public-write-actor.ts`와 `/Users/alex/project/altteulmap/src/app/api/places/[id]/route.ts`는 request header 기반 visitor id를 쓰도록 갱신했다. - `/Users/alex/project/altteulmap/src/app/place/[id]/page.tsx`는 guest viewer용 visitor cookie를 서버 페이지에서 더 이상 직접 읽지 않게 조정했다. guest는 place page SSR에서 viewer reaction 초기값을 `null`로 시작하지만, worker hang 없이 page 자체는 계속 렌더된다. @@ -696,7 +711,7 @@ - `/Users/alex/project/altteulmap/src/features/map/map-page.tsx`에서 모바일/데스크톱 가격 필터 UI, `maxPrice` hidden input, 가격 파라미터 URL 조합을 제거했다. 공개 지도 탐색 조건은 이제 카테고리와 검색 범위만 남는다. - `/Users/alex/project/altteulmap/src/features/places/map-explorer.tsx`, `/Users/alex/project/altteulmap/src/app/api/places/map/route.ts`, `/Users/alex/project/altteulmap/src/features/places/repository.ts`, `/Users/alex/project/altteulmap/src/features/places/queries.ts`에서 `maxPrice` prop, query string, API filter payload, repository/mock filtering 경로를 함께 제거했다. - `/Users/alex/project/altteulmap/tests/e2e/map-price-filter.spec.ts`, `/Users/alex/project/altteulmap/tests/e2e/map-price-filter.mobile.spec.ts`는 가격 필터 동작 검증 대신 가격 옵션 비노출과 카테고리 탐색 유지 회귀를 검증하도록 바꿨다. - - `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`를 현재 제품 계약에 맞춰 갱신했다. + - `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`를 현재 제품 계약에 맞춰 갱신했다. - 검증 결과 - `npm run verify:quick` 통과 - `npm run verify` 통과 @@ -814,7 +829,7 @@ - 완료 내용 - `/Users/alex/project/altteulmap/scripts/lib/load-env-files.mjs`를 추가해 `.env`, `.env.production`, `.env.local`, `.env.production.local`을 읽되, 이미 주입된 쉘/CI env는 덮어쓰지 않고 파일끼리만 뒤 순서가 앞 순서를 덮도록 공통 helper를 만들었다. - `/Users/alex/project/altteulmap/scripts/check-cloudflare-deploy.mjs`, `/Users/alex/project/altteulmap/scripts/build-public-worker.mjs`는 이제 이 helper를 써서 로컬 파일보다 셸/CI env를 우선한다. 따라서 운영 워크플로우에서 넘긴 `NEXTAUTH_URL`, `ADMIN_APP_URL`, `SITE_URL`이 개발용 `.env*` 값으로 다시 덮이지 않는다. - - `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/docs/cloudflare-account-to-deploy.md`, `/Users/alex/project/altteulmap/PLAN.md`에 이 precedence 규칙과 split 배포 기준 문구를 반영했고, 예전 `public 번들에서 /admin 제거` 문장도 현재 external stub 동작 기준으로 고쳤다. + - `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/docs/deploy/cloudflare-account-to-deploy.md`, `/Users/alex/project/altteulmap/PLAN.md`에 이 precedence 규칙과 split 배포 기준 문구를 반영했고, 예전 `public 번들에서 /admin 제거` 문장도 현재 external stub 동작 기준으로 고쳤다. - 검증 결과 - `npm run verify:quick` 통과 - `NEXTAUTH_URL=https://override-public.example.workers.dev ADMIN_APP_URL=https://override-admin.example.workers.dev npm run deploy:check:public` 통과 @@ -834,7 +849,7 @@ - `/Users/alex/project/altteulmap/scripts/check-cloudflare-deploy.mjs`는 `--public`, `--admin` 모드를 지원하게 바꿨다. public split은 `ADMIN_APP_URL`, standalone admin worker는 `SITE_URL`까지 확인하고, callback/home link 기준 URL을 같이 출력한다. - `/Users/alex/project/altteulmap/src/lib/env.ts`, `/Users/alex/project/altteulmap/src/lib/site.ts`, `/Users/alex/project/altteulmap/.env.example`에 `SITE_URL`을 반영했다. admin 앱은 `NEXTAUTH_URL`을 자기 자신 기준으로 두고도 헤더의 홈 링크는 public 앱으로 보낼 수 있게 됐다. - `/Users/alex/project/altteulmap/src/lib/admin-app.ts`는 로컬 보호 로직을 `NEXTAUTH_URL` 기준으로 유지해, `SITE_URL`이 별도로 있어도 localhost 개발/테스트에서는 외부 admin 링크가 자동으로 새지 않게 했다. - - `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/docs/cloudflare-account-to-deploy.md`, `/Users/alex/project/altteulmap/PLAN.md`를 업데이트해 split 배포 순서, env 역할, public `/admin` redirect/API stub 동작, admin 앱의 `SITE_URL` 사용 규칙을 남겼다. + - `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/docs/deploy/cloudflare-account-to-deploy.md`, `/Users/alex/project/altteulmap/PLAN.md`를 업데이트해 split 배포 순서, env 역할, public `/admin` redirect/API stub 동작, admin 앱의 `SITE_URL` 사용 규칙을 남겼다. - 검증 결과 - `npm run verify:quick` 통과 - `npm run verify` 통과 @@ -1260,7 +1275,7 @@ - `/Users/alex/project/altteulmap/scripts/build-admin-worker.mjs`, `/Users/alex/project/altteulmap/apps/admin/open-next.config.ts`를 추가해 `apps/admin`이 자기 cwd에서 OpenNext build를 만들도록 정리했다. - `/Users/alex/project/altteulmap/package.json`의 `cf:build:admin`, `preview:admin`, `deploy:admin`을 별도 관리자 앱 기준으로 바꿨고, `/Users/alex/project/altteulmap/wrangler.admin.jsonc`도 `apps/admin/.open-next/**`를 바라보도록 수정했다. - `/Users/alex/project/altteulmap/scripts/build-public-worker.mjs`는 `ADMIN_APP_URL`이 없으면 실패하게 바꿨다. public-only 배포가 `/admin`, `/api/admin`을 제거하므로 외부 관리자 앱 주소 없이 배포되면 링크가 깨지기 때문이다. - - `/Users/alex/project/altteulmap/.env.example`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/docs/cloudflare-account-to-deploy.md`를 새 배포 순서에 맞게 갱신했다. + - `/Users/alex/project/altteulmap/.env.example`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/docs/deploy/cloudflare-account-to-deploy.md`를 새 배포 순서에 맞게 갱신했다. - 검증 결과 - `npm run verify:quick` 통과 - `npm run cf:build:admin` 통과 @@ -1351,7 +1366,7 @@ - `/Users/alex/project/altteulmap/src/features/map/map-page.tsx`로 실제 지도 홈 구현을 분리했다. - `/Users/alex/project/altteulmap/src/app/page.tsx`는 위 구현을 그대로 재사용하는 최소 re-export로 정리했다. - `/Users/alex/project/altteulmap/src/app/map/page.tsx`는 더 이상 전체 지도 화면을 중복 렌더하지 않고, query string을 유지한 채 `/`로 `permanentRedirect` 하도록 바꿨다. - - `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`의 현재 진입 경로 안내도 `/` 기준으로 맞췄다. + - `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`의 현재 진입 경로 안내도 `/` 기준으로 맞췄다. - 검증 결과 - `npm run verify:quick` 통과 - `npm run build` 통과 예정 검증과 함께 route output 확인 @@ -1386,7 +1401,7 @@ - `/Users/alex/project/altteulmap/src/features/admin/repository.ts`를 추가해 관리자 overview 집계를 분리했다. 총 사용자 수, 운영자/일반 사용자 수, 현재 세션 수, 세션 사용자 수, 활성 장소 수, 승인 대기 장소 수, 대기 가격 제보 수, 열린 신고 수와 최근 가입 사용자 목록을 한 번에 조회한다. - `/Users/alex/project/altteulmap/src/app/admin/page.tsx`를 overview 대시보드로 다시 구성했다. KPI 카드, 장소/가격/신고 바로가기, 최근 가입 사용자 목록, 최신 장소 등록 목록, 최신 신고 목록을 넣었고 방문 지표는 아직 미계측이라는 안내를 명시했다. - `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/reports/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/prices/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/prices/places/[id]/page.tsx`에도 같은 세션 액션을 붙여 관리자 하위 화면에서도 로그아웃이 바로 보이게 했다. - - `/Users/alex/project/altteulmap/tests/e2e/admin-dashboard.spec.ts`를 추가했고, `/Users/alex/project/altteulmap/package.json`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`, `/Users/alex/project/altteulmap/PLAN.md`를 새 관리자/인증 동선에 맞게 갱신했다. + - `/Users/alex/project/altteulmap/tests/e2e/admin-dashboard.spec.ts`를 추가했고, `/Users/alex/project/altteulmap/package.json`, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`, `/Users/alex/project/altteulmap/PLAN.md`를 새 관리자/인증 동선에 맞게 갱신했다. - 검증 결과 - `npm run verify:quick` 통과 - `rm -rf .next && npm run verify` 통과 @@ -1447,7 +1462,7 @@ - 완료 내용 - `/Users/alex/project/altteulmap/src/app/map/page.tsx`, `/Users/alex/project/altteulmap/src/features/places/map-explorer.tsx`에서 공개 지도 정렬 UI, `sort` URL 파라미터, 모바일 summary/hidden input을 제거했다. 이제 공개 지도는 검색과 카테고리/가격 필터만 유지하고, 목록은 기본 순서로 고정 노출된다. - `/Users/alex/project/altteulmap/src/app/api/places/map/route.ts`, `/Users/alex/project/altteulmap/src/features/places/types.ts`, `/Users/alex/project/altteulmap/src/features/places/queries.ts`, `/Users/alex/project/altteulmap/src/features/places/repository.ts`에서 공개 지도용 `sort` API 계약과 `likes` 정렬 분기를 제거했다. 내부적으로는 기존 `price`/`recent` 정렬 타입만 남겨 다른 비공개/운영 경로와 충돌하지 않게 정리했다. - - `/Users/alex/project/altteulmap/tests/e2e/map.spec.ts`에서 좋아요순 회귀를 제거했고, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`, `/Users/alex/project/altteulmap/PLAN.md`를 새 공개 지도 계약에 맞게 갱신했다. + - `/Users/alex/project/altteulmap/tests/e2e/map.spec.ts`에서 좋아요순 회귀를 제거했고, `/Users/alex/project/altteulmap/README.md`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`, `/Users/alex/project/altteulmap/PLAN.md`를 새 공개 지도 계약에 맞게 갱신했다. - 검증 결과 - `npm run verify:quick` 통과 - `rm -rf .next && npm run verify` 통과 @@ -1488,7 +1503,7 @@ - `/Users/alex/project/altteulmap/src/features/submission/place-submit-form.tsx`를 전면 단순화했다. 네이버 geocoding import, `주소로 위치 확인` 버튼, hidden 좌표 필드, 제출 전 내부 위치 확인 로직을 모두 제거했고, 운영자가 승인 단계에서 지도 위치와 네이버 지도 검색 결과를 확인한다는 안내 문구로 교체했다. - 공개 제출 흐름에서 더 이상 쓰지 않는 `/Users/alex/project/altteulmap/src/features/submission/place-coordinate-picker.tsx`를 삭제해, 다음 세션에서 공개 등록이 좌표 picker를 쓰는 것으로 오해하지 않게 정리했다. - `/Users/alex/project/altteulmap/src/features/places/repository.ts`는 공개 장소 등록을 항상 `latitude: null`, `longitude: null` 상태의 `pending_review`로 저장하도록 바꿨고, 제출 preview에서도 좌표를 더 이상 노출하지 않게 맞췄다. - - `/Users/alex/project/altteulmap/src/app/submit/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/src/features/places/admin-place-review-form.tsx`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`를 새 정책에 맞게 갱신했다. 공개 제보는 텍스트-only, 운영자는 승인 단계에서 주소와 네이버 지도 검색 결과를 참고해 좌표를 확정하는 흐름으로 설명을 통일했다. + - `/Users/alex/project/altteulmap/src/app/submit/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/src/features/places/admin-place-review-form.tsx`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`를 새 정책에 맞게 갱신했다. 공개 제보는 텍스트-only, 운영자는 승인 단계에서 주소와 네이버 지도 검색 결과를 참고해 좌표를 확정하는 흐름으로 설명을 통일했다. - `/Users/alex/project/altteulmap/tests/e2e/submission-admin.spec.ts`는 회귀 기준을 바꿨다. 공개 폼에 위치 확인 UI가 노출되지 않는지 확인하고, 텍스트-only 등록 후 운영자가 좌표를 입력해 승인하면 홈 검색에 노출되는 시나리오를 유지했다. - 검증 결과 - `npm run verify:quick` 통과 @@ -1523,7 +1538,7 @@ - `/Users/alex/project/altteulmap/src/features/submission/place-submit-form.tsx`에서 제출 직전에 좌표가 비어 있으면 주소 기반 geocode를 한 번 더 시도하고, 그래도 좌표가 없으면 제출을 막도록 수정했다. 주소나 지역 구분이 바뀌면 기존 좌표를 비워 다시 확인하게 연결했다. - 같은 등록 폼에서 입력 UX도 다시 정리했다. `장소 이름`을 `업장/장소 이름`으로 명확히 바꾸고, `업장 주소` 블록 안에 `위치 확인됨/미확인` 상태와 `주소로 위치 확인` 버튼을 넣어 사용자가 plain text만 쓰고 끝내지 않도록 흐름을 앞단에서 보이게 만들었다. - 공개 등록 폼에서는 지도와 위도/경도 숫자 입력 UI를 모두 제거했다. 좌표는 hidden form state로만 유지하고, 사용자에게는 텍스트 주소와 내부 위치 확인 상태만 보이게 바꿨다. 제출 오류 문구도 `좌표 직접 입력`이나 `지도 선택`이 아니라 `주소 재확인` 기준으로 다시 썼다. - - `/Users/alex/project/altteulmap/src/app/submit/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`를 현재 정책에 맞게 갱신했다. 공개 등록은 `텍스트 주소 + 내부 위치 확인`이 필수이고, 운영자는 제출 좌표를 검수/조정한다는 점이 화면과 문서에 같이 반영된다. + - `/Users/alex/project/altteulmap/src/app/submit/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/page.tsx`, `/Users/alex/project/altteulmap/src/app/admin/places/page.tsx`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`를 현재 정책에 맞게 갱신했다. 공개 등록은 `텍스트 주소 + 내부 위치 확인`이 필수이고, 운영자는 제출 좌표를 검수/조정한다는 점이 화면과 문서에 같이 반영된다. - `/Users/alex/project/altteulmap/tests/e2e/submission-admin.spec.ts`에 주소가 바뀌면 기존 좌표가 비워지고 다시 확인해야 한다는 회귀 테스트를 추가했다. - 작업 중 `next dev`와 `next build`/`next start`를 같은 워크트리에서 동시에 돌리며 `.next`를 공유하다가 `ENOENT ... .next/dev/server/pages/_app/build-manifest.json`, `Another write batch or compaction is already active`가 재현됐다. 로컬 `next dev` 프로세스를 종료하고 `/Users/alex/project/altteulmap/.next`를 비운 뒤 다시 기동해 정상 응답을 확인했다. - 검증 결과 @@ -1537,7 +1552,7 @@ ### 2026-04-01 23:59 KST: 북마크만 로그인 유지, 공개 쓰기 익명화, 비슷한 장소 제거 - 완료 내용 - - `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`, `/Users/alex/project/altteulmap/README.md`를 현재 정책에 맞게 갱신했다. 공개 쓰기는 `장소 등록`, `댓글`, `가격 제보`, `신고`까지 익명 허용으로 정리했고, 로그인 유지 기능은 `북마크`만 남겼다. + - `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`, `/Users/alex/project/altteulmap/README.md`를 현재 정책에 맞게 갱신했다. 공개 쓰기는 `장소 등록`, `댓글`, `가격 제보`, `신고`까지 익명 허용으로 정리했고, 로그인 유지 기능은 `북마크`만 남겼다. - `/Users/alex/project/altteulmap/src/lib/public-write-actor.ts`를 추가하고 `/Users/alex/project/altteulmap/src/app/api/places/route.ts`, `/Users/alex/project/altteulmap/src/app/api/places/[id]/comments/route.ts`, `/Users/alex/project/altteulmap/src/app/api/places/[id]/comments/[commentId]/route.ts`, `/Users/alex/project/altteulmap/src/app/api/places/[id]/prices/route.ts`, `/Users/alex/project/altteulmap/src/app/api/reports/route.ts`를 visitor cookie 기반 익명 actor 모델로 통일했다. 로그인 사용자는 기존 user id를 계속 쓰고, 비로그인 사용자는 visitor cookie 또는 forwarded IP fallback 기준으로 rate limit과 본인 삭제 권한을 처리한다. - `/Users/alex/project/altteulmap/src/db/schema.ts`, `/Users/alex/project/altteulmap/drizzle/0005_far_maginty.sql`, `/Users/alex/project/altteulmap/drizzle/meta/0005_snapshot.json`에 댓글 익명 저장을 위한 `comments.user_id nullable`, `comments.visitor_id` 추가를 반영했다. - `/Users/alex/project/altteulmap/src/features/places/repository.ts`, `/Users/alex/project/altteulmap/src/app/api/places/[id]/route.ts`, `/Users/alex/project/altteulmap/src/app/place/[id]/page.tsx`, `/Users/alex/project/altteulmap/src/features/places/place-detail-sheet.tsx`, `/Users/alex/project/altteulmap/src/features/places/map-explorer.tsx`에서 `비슷한 장소` 조회/응답/UI를 제거했다. @@ -1595,7 +1610,7 @@ - `/Users/alex/project/altteulmap/src/features/submission/place-submit-form.tsx`에서 공개 등록 폼의 `사업장 이름` 입력을 제거하고, 단일 `장소 이름` 필드만 받도록 단순화했다. - 같은 폼에 `간판명이나 사용자가 알아보기 쉬운 이름 하나만 입력하면 됩니다.` 안내 문구를 추가하고, 접수 결과 패널 라벨도 `장소 이름` 기준으로 맞췄다. - `/Users/alex/project/altteulmap/src/features/submission/schema.ts`의 검증 메시지를 `장소 이름` 기준으로 정리했다. - - `/Users/alex/project/altteulmap/tests/e2e/submission-admin.spec.ts`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`도 현재 UX에 맞게 갱신했다. + - `/Users/alex/project/altteulmap/tests/e2e/submission-admin.spec.ts`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`도 현재 UX에 맞게 갱신했다. - 검증 결과 - `npm run verify:quick` 통과 - `DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/altteulmap USE_MOCK_DATA=false AUTH_SECRET=altteulmap-local-auth-secret-change-me NEXTAUTH_URL=http://127.0.0.1:3107 AUTH_DEMO_PASSWORD=demo1234 AUTH_ADMIN_PASSWORD=admin1234 npx playwright test tests/e2e/submission-admin.spec.ts` 통과 @@ -1608,7 +1623,7 @@ - `/Users/alex/project/altteulmap/src/app/signup/page.tsx`를 추가해 소셜 계정으로 바로 시작하는 회원가입 진입면을 만들었다. 현재 인증 구조상 일반 이메일 비밀번호 회원가입은 지원하지 않으므로, 소셜 로그인 시 자동으로 계정이 생성된다는 안내를 함께 넣었다. - `/Users/alex/project/altteulmap/src/components/brand-mark.tsx`를 추가하고 `/Users/alex/project/altteulmap/src/app/map/page.tsx`와 인증 화면에 같은 워드마크를 재사용하도록 바꿨다. 홈 상단 로고는 더 이상 장식 박스 없이 typographic lockup 기준으로 통일된다. - `/Users/alex/project/altteulmap/src/lib/session.ts`에 `/signup`용 callback helper를 추가하고, `/login`/`/signup` 자체가 callback 대상으로 다시 들어가지 않도록 정규화 규칙을 보강했다. - - `/Users/alex/project/altteulmap/src/app/globals.css`, `/Users/alex/project/altteulmap/src/app/robots.ts`, `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/trd.md`도 새 auth entry 구조에 맞게 같이 갱신했다. + - `/Users/alex/project/altteulmap/src/app/globals.css`, `/Users/alex/project/altteulmap/src/app/robots.ts`, `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`도 새 auth entry 구조에 맞게 같이 갱신했다. - 검증 결과 - `npm run verify:quick` 통과 - `npm run verify` 통과 @@ -1619,7 +1634,7 @@ ### 2026-04-01: 익명 장소 등록 허용과 공개 CTA 정리 - 완료 내용 - - `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/prd.md`, `/Users/alex/project/altteulmap/trd.md`를 갱신해 현재 정책을 `비회원 읽기 + 비회원 반응 + 비회원/회원 장소 등록 + 회원 전용 북마크/댓글/신고/가격 제보` 기준으로 재정의했다. + - `/Users/alex/project/altteulmap/PLAN.md`, `/Users/alex/project/altteulmap/docs/product/prd.md`, `/Users/alex/project/altteulmap/docs/product/trd.md`를 갱신해 현재 정책을 `비회원 읽기 + 비회원 반응 + 비회원/회원 장소 등록 + 회원 전용 북마크/댓글/신고/가격 제보` 기준으로 재정의했다. - `/Users/alex/project/altteulmap/src/app/submit/page.tsx`에서 비로그인 사용자의 `/submit` 리다이렉트를 제거하고, 로그인 상태가 없을 때도 `로그인 없이도 장소를 등록할 수 있습니다.` 안내가 보이도록 바꿨다. - `/Users/alex/project/altteulmap/src/app/api/places/route.ts`에서 장소 등록 API의 로그인 필수 조건을 제거하고, 로그인 사용자는 user id 기준, 비로그인 사용자는 visitor cookie 또는 forwarded IP fallback 기준으로 rate limit을 적용한 뒤 익명 등록은 `created_by_user_id = null`로 저장되도록 정리했다. - `/Users/alex/project/altteulmap/src/app/map/page.tsx`의 공개 CTA는 항상 `/submit`로 바로 들어가게 바꿔, 로그인 없이도 `장소 등록하기` 흐름으로 진입할 수 있게 했다. @@ -1740,10 +1755,10 @@ - 완료 내용 - `/Users/alex/project/altteulmap/src/features/map/naver-map-panel.tsx`에 네이버 지도 SDK 초기화/마커 렌더링/viewport 이동 구간의 방어 코드를 추가해 `maps.Map`, `maps.LatLng`, `maps.Marker`, `maps.Event`가 비정상 상태일 때 즉시 에러 상태로 전환하도록 보강했다. - 같은 파일에 지도 패널 전용 error boundary와 preview fallback UI를 추가해, 네이버 지도 SDK가 런타임에서 예외를 던져도 페이지 전체가 `This page couldn’t load`로 죽지 않고 장소 목록/상세 시트는 계속 사용할 수 있게 만들었다. - - `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`에 네이버 지도 키는 환경 변수만이 아니라 NAVER Cloud Platform 콘솔의 웹 서비스 URL/허용 도메인에도 실제 배포 주소(`workers.dev` 또는 custom domain)를 등록해야 한다는 점을 추가했다. + - `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`에 네이버 지도 키는 환경 변수만이 아니라 NAVER Cloud Platform 콘솔의 웹 서비스 URL/허용 도메인에도 실제 배포 주소(`workers.dev` 또는 custom domain)를 등록해야 한다는 점을 추가했다. - 변경 파일 - `/Users/alex/project/altteulmap/src/features/map/naver-map-panel.tsx` - - `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md` + - `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md` - `/Users/alex/project/altteulmap/PROGRESS.md` - 검증 결과 - `npm run verify:quick` 통과 @@ -1764,9 +1779,9 @@ ### 2026-04-01: Cloudflare 계정 생성부터 첫 배포까지 총괄 가이드 문서화 - 완료 내용 - - `/Users/alex/project/altteulmap/docs/cloudflare-account-to-deploy.md`를 추가해 Cloudflare 계정 생성, Wrangler 로그인, `workers.dev` 첫 배포, runtime 변수/secret 등록, OAuth callback 연결, 커스텀 도메인 전환까지 전체 흐름을 정리했다. + - `/Users/alex/project/altteulmap/docs/deploy/cloudflare-account-to-deploy.md`를 추가해 Cloudflare 계정 생성, Wrangler 로그인, `workers.dev` 첫 배포, runtime 변수/secret 등록, OAuth callback 연결, 커스텀 도메인 전환까지 전체 흐름을 정리했다. - 현재 저장소의 실제 배포 방식이 `로컬 OpenNext build -> Wrangler deploy`라는 점을 기준으로, 로컬 build 환경 변수와 Cloudflare runtime 변수 둘 다 필요하다는 운영 메모를 문서에 명시했다. - - `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/README.md`에도 새 총괄 가이드 링크를 연결했다. + - `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`, `/Users/alex/project/altteulmap/README.md`에도 새 총괄 가이드 링크를 연결했다. - 검증 결과 - 문서 작업만 수행했고 별도 테스트는 실행하지 않았다. - 메모 @@ -1971,8 +1986,8 @@ - `/Users/alex/project/altteulmap/src/features/auth/repository.ts`, `/Users/alex/project/altteulmap/src/app/login/page.tsx`, `/Users/alex/project/altteulmap/src/features/auth/login-form.tsx`를 수정해 로그인 화면에서 provider 활성 상태와 미설정 이유를 보여주고, 로컬 credentials 로그인은 그대로 유지했다. - 지도용 네이버 키와 로그인용 OAuth 키가 섞이지 않도록 `.env.example`과 `README.md`를 `AUTH_NAVER_*`, `AUTH_KAKAO_*` 기준으로 정리했다. - `/Users/alex/project/altteulmap/scripts/smoke-local.mjs`와 `npm run smoke:local`을 추가해 SEO/지도 API/credentials 로그인/관리자 보호 API를 한 번에 확인할 수 있게 했다. - - `/Users/alex/project/altteulmap/scripts/check-cloudflare-deploy.mjs`, `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md`, `npm run deploy:check`를 추가해 Cloudflare 배포 전 환경 변수와 도메인 설정을 점검할 수 있게 했다. - - `trd.md`의 환경 변수 표기를 현재 코드 기준(`NEXTAUTH_URL`, `AUTH_KAKAO_*`, `AUTH_NAVER_*`, `NEXT_PUBLIC_NAVER_MAP_KEY_ID`)으로 맞췄다. + - `/Users/alex/project/altteulmap/scripts/check-cloudflare-deploy.mjs`, `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md`, `npm run deploy:check`를 추가해 Cloudflare 배포 전 환경 변수와 도메인 설정을 점검할 수 있게 했다. + - `docs/product/trd.md`의 환경 변수 표기를 현재 코드 기준(`NEXTAUTH_URL`, `AUTH_KAKAO_*`, `AUTH_NAVER_*`, `NEXT_PUBLIC_NAVER_MAP_KEY_ID`)으로 맞췄다. - 변경 파일 - `/Users/alex/project/altteulmap/src/lib/site.ts` - `/Users/alex/project/altteulmap/src/app/layout.tsx` @@ -1989,14 +2004,14 @@ - `/Users/alex/project/altteulmap/src/features/auth/constants.ts` - `/Users/alex/project/altteulmap/scripts/smoke-local.mjs` - `/Users/alex/project/altteulmap/scripts/check-cloudflare-deploy.mjs` - - `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md` + - `/Users/alex/project/altteulmap/docs/deploy/deploy-cloudflare.md` - `/Users/alex/project/altteulmap/package.json` - `/Users/alex/project/altteulmap/src/app/robots.ts` - `/Users/alex/project/altteulmap/src/app/sitemap.ts` - `/Users/alex/project/altteulmap/src/app/manifest.ts` - `/Users/alex/project/altteulmap/.env.example` - `/Users/alex/project/altteulmap/README.md` - - `/Users/alex/project/altteulmap/trd.md` + - `/Users/alex/project/altteulmap/docs/product/trd.md` - `/Users/alex/project/altteulmap/PLAN.md` - `/Users/alex/project/altteulmap/PROGRESS.md` - 검증 결과 diff --git a/README.md b/README.md index 40e503b..e9a9f3f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ - Demo: [altteulmap.altteul-lab.workers.dev](https://altteulmap.altteul-lab.workers.dev) - Admin: [altteulmap-admin.altteul-lab.workers.dev](https://altteulmap-admin.altteul-lab.workers.dev) -- Docs: [Cloudflare deploy guide](docs/deploy-cloudflare.md) +- Docs: [Cloudflare deploy guide](docs/deploy/deploy-cloudflare.md) + +최근 유행했던 `거지맵` 서비스에서 아이디어를 얻었습니다. 다만 음식점에만 머무르지 않고, 생활 서비스까지 포함한 더 넓은 절약 지도 형태로 확장하고 싶었습니다. 여기에 공공데이터를 적극적으로 활용해 실제 운영 가능한 데이터 기반 서비스를 만들고 싶었고, 고물가와 취업난이 겹친 지금의 시대감에도 잘 맞는 주제라고 판단해 개발했습니다. ![Altteulmap home](docs/readme/hero-home.png) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..644d8db --- /dev/null +++ b/docs/README.md @@ -0,0 +1,13 @@ +# Docs + +## Product +- [PRD](product/prd.md) +- [TRD](product/trd.md) + +## Deployment +- [Cloudflare deploy guide](deploy/deploy-cloudflare.md) +- [Cloudflare account to deploy](deploy/cloudflare-account-to-deploy.md) + +## Project Workflow +- [PLAN](../PLAN.md) +- [PROGRESS](../PROGRESS.md) diff --git a/docs/cloudflare-account-to-deploy.md b/docs/deploy/cloudflare-account-to-deploy.md similarity index 99% rename from docs/cloudflare-account-to-deploy.md rename to docs/deploy/cloudflare-account-to-deploy.md index 985559f..51e2c72 100644 --- a/docs/cloudflare-account-to-deploy.md +++ b/docs/deploy/cloudflare-account-to-deploy.md @@ -236,7 +236,7 @@ npm run db:seed ``` 이 저장소의 배포 체크 문서는 별도로 아래 파일에 있다. -- `/Users/alex/project/altteulmap/docs/deploy-cloudflare.md` +- `deploy-cloudflare.md` ## 9. 첫 `workers.dev` 배포 diff --git a/docs/deploy-cloudflare.md b/docs/deploy/deploy-cloudflare.md similarity index 98% rename from docs/deploy-cloudflare.md rename to docs/deploy/deploy-cloudflare.md index 7af1cdc..aaa9eb5 100644 --- a/docs/deploy-cloudflare.md +++ b/docs/deploy/deploy-cloudflare.md @@ -6,7 +6,7 @@ - Cloudflare Workers + OpenNext 기준으로 알뜰맵을 배포할 때 빠뜨리기 쉬운 설정을 한 문서에 모은다. - 실제 배포 전 `npm run deploy:check`로 환경 변수를 먼저 점검한다. - 계정 생성부터 첫 배포까지의 전체 절차는 아래 문서를 함께 본다. - - `/Users/alex/project/altteulmap/docs/cloudflare-account-to-deploy.md` + - `cloudflare-account-to-deploy.md` ## 1. 배포 전 필수 확인 1. `npm run verify` diff --git a/prd.md b/docs/product/prd.md similarity index 100% rename from prd.md rename to docs/product/prd.md diff --git a/trd.md b/docs/product/trd.md similarity index 100% rename from trd.md rename to docs/product/trd.md diff --git a/package.json b/package.json index a82c625..cce6b02 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "deploy:check:admin": "node scripts/check-cloudflare-deploy.mjs --admin", "hooks:install": "node scripts/git-hooks/install-hooks.mjs", "cf:clean": "rm -rf .next .open-next", + "clean:artifacts": "rm -rf .next .next-dev .open-next apps/admin/.next apps/admin/.next-dev apps/admin/.open-next test-results playwright-report tsconfig.tsbuildinfo apps/admin/tsconfig.tsbuildinfo", "cf:patch-next-runtime": "node scripts/patch-next-cloudflare-runtime.mjs", "cf:build": "npm run admin:sync && npm run cf:patch-next-runtime && npm run cf:clean && opennextjs-cloudflare build", "cf:build:public": "npm run cf:clean && node scripts/build-public-worker.mjs", From 95d5bd5328a86b3ddad0f8a3a1b2cd79918e7546 Mon Sep 17 00:00:00 2001 From: MJY Date: Mon, 6 Apr 2026 13:11:57 +0900 Subject: [PATCH 2/2] fix: prevent mobile sheet reopen during e2e --- PROGRESS.md | 11 +++++++++++ src/features/places/map-explorer.tsx | 15 +++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/PROGRESS.md b/PROGRESS.md index 3155363..3a9e72f 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -43,6 +43,17 @@ ## 실행 로그 +### 2026-04-06 13:34 KST: PR CI mobile E2E 재오픈 회귀 수정 +- 완료 내용 + - PR #2의 `E2E Full` 실패 로그를 확인한 결과, `tests/e2e/map.mobile.spec.ts`의 `모바일에서 장소 목록 바텀시트를 열고 닫을 수 있다` 케이스만 실패하고 있었다. + - 원인은 `/Users/alex/project/altteulmap/src/features/places/map-explorer.tsx`에서 모바일 목록 시트를 drag로 닫은 직후 같은 포인터 이벤트가 아래 `목록 보기` 버튼까지 전달되어 시트가 즉시 다시 열리는 회귀였다. + - `lastMobileListCloseAtRef` 기반의 짧은 reopen guard를 추가해 닫힘 직후 250ms 동안 모바일 목록 열기 버튼이 같은 이벤트로 재실행되지 않도록 정리했다. +- 검증 결과 + - `npm run verify:quick` 통과 + - `npm run e2e:prepare && npm run build && USE_MOCK_DATA=true npx playwright test tests/e2e/map.mobile.spec.ts --project mobile-chromium` 통과 +- 메모 + - 실패했던 GitHub Actions run은 `24017929325`였고, 실패 job은 `E2E Full` 하나였다. Cloudflare `Workers Builds` 자체는 public/admin 모두 성공 상태였다. + ### 2026-04-06 13:18 KST: 루트 문서 재배치와 build 산출물 정리 - 완료 내용 - 루트의 product 문서를 `docs/product/` 하위로 옮겼다. diff --git a/src/features/places/map-explorer.tsx b/src/features/places/map-explorer.tsx index a1754fb..3aeefc4 100644 --- a/src/features/places/map-explorer.tsx +++ b/src/features/places/map-explorer.tsx @@ -417,6 +417,7 @@ export function MapExplorer({ const [isMobileListOpen, setIsMobileListOpen] = useState(false); const [mobileListSheetMode, setMobileListSheetMode] = useState("peek"); + const lastMobileListCloseAtRef = useRef(0); const [manualRefreshTick, setManualRefreshTick] = useState(0); const shouldSkipInitialFetchRef = useRef(prefetchedOnServer); const activeBounds = @@ -471,6 +472,7 @@ export function MapExplorer({ } : null; const closeMobileList = () => { + lastMobileListCloseAtRef.current = Date.now(); setIsMobileListOpen(false); setMobileListSheetMode("peek"); }; @@ -480,6 +482,14 @@ export function MapExplorer({ const collapseMobileList = () => { setMobileListSheetMode("peek"); }; + const openMobileList = () => { + if (Date.now() - lastMobileListCloseAtRef.current < 250) { + return; + } + + setMobileListSheetMode("peek"); + setIsMobileListOpen(true); + }; const mobileListSheetGesture = useMobileSheetGesture({ enabled: isMobileListOpen, mode: mobileListSheetMode, @@ -631,10 +641,7 @@ export function MapExplorer({