From e1790423e4ebe1d97656cb485c93eeb16cc14631 Mon Sep 17 00:00:00 2001 From: art Date: Mon, 29 Jun 2026 15:06:27 +0900 Subject: [PATCH] =?UTF-8?q?docs(dev):=20local-dev-setup=20=EA=B0=80?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=EC=B6=94=EA=B0=80=20(93525cb=20=EB=B3=B4?= =?UTF-8?q?=EC=99=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 선행 커밋(93525cb)에서 add 중단으로 누락된 문서 보완. - docs/development/local-dev-setup.md 신설: 업로드 저장루트(~/.bibimbap/uploads) 경로 규약 · @Value 기본값 유지 근거 · 자산 이전 이력 표 · SSRF DNS 캐시 운영 참고 - docs/development/index.md 목록 링크 추가 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD --- docs/development/index.md | 1 + docs/development/local-dev-setup.md | 44 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 docs/development/local-dev-setup.md diff --git a/docs/development/index.md b/docs/development/index.md index 3560e4f..73c4c88 100644 --- a/docs/development/index.md +++ b/docs/development/index.md @@ -8,5 +8,6 @@ - [document-category-classification.md](./document-category-classification.md) — 카테고리 분류 기준 (불필요한 카테고리는 프로젝트에 맞게 정리) - [agent-output-conventions.md](./agent-output-conventions.md) — 에이전트 출력 규약. 사용자 대면 의사결정 제시문엔 압축 비적용(배경+선택지+권장 풀어쓰기), 압축은 내부 산출물 한정 - [git-workflow.md](./git-workflow.md) — 브랜치 분류(메인스트림 vs 비-메인스트림) · 비-메인스트림 브랜치 커밋 표준 승인 · push 명시 요청 한정 · Conventional Commits + `Co-Authored-By` 트레일러 규약. CLAUDE.md '작업 원칙' 커밋 정책의 정본. +- [local-dev-setup.md](./local-dev-setup.md) — 로컬 구동 환경 설정. 업로드 저장 루트(`~/.bibimbap/uploads`, static 트리 밖) 경로 규약 · @Value 기본값 유지 근거 · 자산 이전 이력 · SSRF DNS 캐시 운영 참고. > atp 플러그인 번들 레퍼런스(`agent-team-protocol.md`, `agent-catalog.md`, `documentation-guidelines.md`, `search-tool-matrix.md`)는 플러그인 캐시에 있으며 이 프로젝트로 복사되지 않는다. 에이전트가 `${CLAUDE_PLUGIN_ROOT}/docs/...` 로 직접 참조한다. diff --git a/docs/development/local-dev-setup.md b/docs/development/local-dev-setup.md new file mode 100644 index 0000000..58c562b --- /dev/null +++ b/docs/development/local-dev-setup.md @@ -0,0 +1,44 @@ +# Local Dev Setup — 로컬 개발 환경 설정 + +로컬에서 앱을 구동할 때 필요한 환경/경로 설정을 둔다. + +## 업로드 저장 루트 (static 트리 밖) + +업로드물(프로필 이미지·게임 WebGL asset)은 **웹서버 정적 서빙 트리(`src/main/resources/static/`) 밖**에 저장한다. 직접 서빙을 차단하고 컨트롤러 권한 게이트를 강제하기 위함이다(보안 하드닝, commit `9041bb7`). + +### 경로 규약 + +| 설정 키 | 값 | 비고 | +| --- | --- | --- | +| `app.upload.game-storage-path` | `${user.home}/.bibimbap/uploads` | `application.properties` 에 명시. `${user.home}` 는 Spring 런타임 placeholder | + +- 프로필 이미지: `~/.bibimbap/uploads/profile/{userId}/*.{png,jpg}` — `UploadResourceConfig` 의 `/profile/**` ResourceHandler 가 이 위치를 서빙. +- 게임 WebGL asset: `~/.bibimbap/uploads/game/{gameUuid}/**` — `GameAssetController @GetMapping("/game/{gameUuid}/**")` 컨트롤러가 경계 검증 후 서빙(정적 핸들러 아님). + +### 디렉토리 생성 + +런타임에 `Files.createDirectories` 로 자동 생성된다(`GameUploadController`·`UserController`). 첫 업로드 시 `~/.bibimbap/uploads/{game,profile}` 가 만들어지므로 수동 생성 불필요. + +### @Value 기본값 주의 + +5개 클래스(`UploadResourceConfig`·`GameUploadController`·`GameAssetController`·`UserController`·`GameAssetCleanupService`)의 `@Value("${app.upload.game-storage-path:src/main/resources/static}")` **기본값(fallback)은 `src/main/resources/static` 으로 유지**한다. 이유: + +- `application.properties` 의 명시 설정이 항상 오버라이드하므로 실제 동작 경로는 `~/.bibimbap/uploads`. +- 기본값을 홈 경로로 바꾸면 IDE/CI 가 명시 설정 없이 홈 디렉토리에 파일을 생성하는 부작용이 생긴다. +- 업로드 테스트(`GameUploadControllerSecurityTest`)는 `@TempDir` + `ReflectionTestUtils.setField` 로 경로를 격리하므로 기본값과 무관. + +## 자산 이전 이력 + +저장 루트가 static 트리 밖으로 이전되면서, **이전부터 `static/profile/` 에 커밋돼 있던 자산은 자동 이동되지 않는다.** 신규 루트로 수동 이전이 필요하다. + +| 일자 | 이전 대상 | 조치 | +| --- | --- | --- | +| 2026-06-29 | `src/main/resources/static/profile/8/*` (user 8 프로필 2파일) | `~/.bibimbap/uploads/profile/8/` 로 이동(체크섬 대조 검증) + tracked 원본 git rm. 이후 user 8 프로필은 신규 루트에서 서빙됨. | + +> `static/game/` 은 이전 시점 비어 있어(커밋된 게임 자산 0) 이전 대상 없음. + +향후 저장 루트를 다시 옮기거나 자산을 마이그레이션할 때 이 표에 한 줄씩 기록한다. + +## SSRF DNS 캐시 (운영 참고) + +`SsrfSafeFetcher` 가 `@PostConstruct` 로 `networkaddress.cache.ttl=30` 을 best-effort 설정한다(DNS rebinding TOCTOU 완화). 런타임 반영은 `InetAddressCachePolicy` lazy init 경합에 의존하므로 **결정적 보장이 필요하면 JVM 레벨**(`$JAVA_HOME/conf/security/java.security` 의 `networkaddress.cache.ttl=30`)이 정본이다. 앱 기동 후 `java.security.Security.getProperty("networkaddress.cache.ttl")` 로 반영 여부를 확인할 수 있다(배포 후 체크리스트 참조: `../maintenance/post-deploy-verification-checklist.md`).