Skip to content

feat/#39 공간 등록 글 작성 페이지 API#40

Open
rlagywn wants to merge 2 commits into
mainfrom
feat/#39
Open

feat/#39 공간 등록 글 작성 페이지 API#40
rlagywn wants to merge 2 commits into
mainfrom
feat/#39

Conversation

@rlagywn

@rlagywn rlagywn commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

#️⃣ 연관된 이슈

#39


📝 작업 내용

공간 등록 글 작성 페이지 API

  • 공간 등록 글 작성 페이지 API

💬 리뷰 요청 사항 (선택)

리뷰어가 집중해서 봐줬으면 하는 부분이 있다면 작성해주세요.


Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 공간 등록 화면 재구성: 이미지 업로드(대표 1장 + 추가 최대 4장)와 예약 가능 날짜 선택 포함
    • 공간 등록 폼: 제목/설명/공간 정보 입력, 작성 중 초안 저장·복원
  • 개선 사항

    • 제출 시 입력값 검증 및 저장 진행 상태/성공 결과(공간 ID) 안내 강화
    • 로그아웃 시 저장된 토큰을 더 폭넓게 정리하고 세션 기반 토큰을 우선 사용

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bab5e80a-1f42-44f1-8ec5-d8e908e0b626

📥 Commits

Reviewing files that changed from the base of the PR and between c8b65f3 and c0d4271.

📒 Files selected for processing (4)
  • src/app/Host_post/page.tsx
  • src/app/api/spaces/route.ts
  • src/components/mypage/MyPageShell.tsx
  • src/lib/current-user.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/api/spaces/route.ts
  • src/app/Host_post/page.tsx

📝 Walkthrough

Walkthrough

Host_post/page.tsx를 이미지 업로드(5슬롯)·캘린더 날짜 선택 UI로 재구성하고, 공간 등록 폼 로직을 담은 SpaceCreateForm 컴포넌트를 신규 추가했다. 또한 /api/spaces POST 라우트를 추가해 인증·필드 검증 후 상위 서비스로 프록시한다. 동시에 토큰 저장소 인프라를 확장하고 로그아웃 시 모든 토큰을 정리하도록 개선했다.

Changes

공간 등록 기능 추가

Layer / File(s) 요약
/api/spaces POST 라우트
src/app/api/spaces/route.ts
CreateSpaceBody 타입 및 handleUpstreamError를 정의하고, 환경변수·인증 헤더·필드 타입 검증 후 axios.post로 상위 서비스를 호출한다. 타임아웃은 504, 응답 있는 에러는 상위 status, 그 외는 502로 매핑한다.
HostPostPage 이미지 슬롯·캘린더 UI 재구성
src/app/Host_post/page.tsx
ImageSlot 타입·createEmptySlots·CalendarCell·PhotoSlot·buildCalendarGrid를 신규 구현한다. 대표+추가 사진 4개 슬롯, 월 단위 캘린더 사이드바(날짜 토글·범례)를 구성하고 SpaceCreateForm에 이미지·날짜 데이터를 전달한다.
SpaceCreateForm 컴포넌트
src/app/Host_post/SpaceCreateForm.tsx
infoFields/initialForm 기반 폼 상태, sessionStorage 초안 저장/복원, FieldControl(select/input) 렌더링을 구현한다. handleSubmit에서 인증 토큰 확인 → 단가·위경도·이미지 유효성 검증 → fileToDataUrl 변환 → /api/spaces POST → 성공/실패 처리 흐름을 수행한다.

토큰 저장소 및 로그아웃 인프라 개선

Layer / File(s) 요약
토큰 저장소 키 상수 확장
src/lib/current-user.ts
COMMON_ACCESS_TOKEN_KEY, SESSION_ACCESS_TOKEN_KEY, USER_TOKEN_KEY 상수를 export하고, getAccessToken()이 sessionStorage의 정식 키를 우선 확인한 후 기타 세션/정식 키로 fallback하도록 업데이트한다.
로그아웃 시 토큰 정리 확장
src/components/mypage/MyPageShell.tsx
handleLogout에서 localStorage의 COMMON_ACCESS_TOKEN_KEY/USER_TOKEN_KEY와 sessionStorage의 SESSION_ACCESS_TOKEN_KEY를 추가로 제거하도록 확장한다.

Sequence Diagram(s)

sequenceDiagram
  actor User as 사용자
  participant HostPostPage as HostPostPage
  participant SpaceCreateForm as SpaceCreateForm
  participant NextRoute as /api/spaces (Next.js)
  participant Upstream as 상위 공간 서비스

  User->>HostPostPage: 이미지 파일 선택 (PhotoSlot)
  HostPostPage->>HostPostPage: URL.createObjectURL → previewUrl 생성
  User->>HostPostPage: 캘린더 날짜 클릭
  HostPostPage->>HostPostPage: selectedDates 토글
  HostPostPage->>SpaceCreateForm: imageFiles, selectedDates 전달
  User->>SpaceCreateForm: 폼 입력 후 임시저장
  SpaceCreateForm->>SpaceCreateForm: sessionStorage에 draft 저장
  User->>SpaceCreateForm: 등록 제출
  SpaceCreateForm->>SpaceCreateForm: getAccessToken() / 유효성 검증 / fileToDataUrl()
  SpaceCreateForm->>NextRoute: POST /api/spaces (Authorization + body)
  NextRoute->>NextRoute: 환경변수·헤더·필드 타입 검증
  NextRoute->>Upstream: axios.post /spaces
  Upstream-->>NextRoute: 응답 (space_id 등)
  NextRoute-->>SpaceCreateForm: 상위 status + 데이터
  SpaceCreateForm->>SpaceCreateForm: sessionStorage draft 삭제
  SpaceCreateForm-->>User: 성공 메시지 (space_id 포함)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • MomentLit/FE-V1#5: src/app/Host_post/page.tsx의 동일한 기본 컴포넌트를 Section/카드 기반 가이드 UI로 추가했으며, 본 PR의 폼 기반 재구성과 직접 코드 수준에서 충돌한다.
  • MomentLit/FE-V1#25: Host_post/page.tsxgetCalendar 로직과 레이아웃을 크게 재작성한 PR로, 본 PR의 캘린더 그리드 및 UI 재구성과 동일 파일을 수정한다.
  • MomentLit/FE-V1#14: 본 PR이 확장한 src/lib/current-user.tsgetAccessToken() 토큰 조회 로직과 인증 헤더 setup을 공유하며, 검색 PR의 /api/users/me 핸들러가 동일한 Authorization 토큰 체계에 의존한다.

Suggested reviewers

  • justhumannn
  • y-ehddjs

Poem

🐇 토끼가 공간을 등록하러 왔어요~
사진 다섯 장, 날짜도 콕콕 찍고,
제목·주소·단가까지 꼼꼼히 채워,
sessionStorage에 초안도 안전하게 보관!
/api/spaces로 폴짝 제출하면,
space_id 와 함께 성공 메시지가 짠! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive 필수 섹션은 포함되어 있으나 작업 내용이 매우 간단하고 체크박스가 미완료 상태이며 상세한 설명이 부족합니다. 작업 내용 섹션에서 구체적인 변경사항(SpaceCreateForm 컴포넌트, API 엔드포인트, 토큰 관리 로직 등)을 상세히 기술하고 체크박스를 완료 처리하거나 제거해주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 공간 등록 글 작성 페이지 API 구현이라는 주요 변경사항을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#39

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/app/api/spaces/route.ts (1)

4-14: 💤 Low value

CreateSpaceBody 타입 정의가 실제 검증 로직과 불일치합니다.

모든 필드가 optional(?)로 선언되어 있지만, Line 78-92에서 필수 필드로 검증하고 있습니다. 타입 정의를 실제 요구사항에 맞게 수정하면 코드 가독성과 타입 안전성이 향상됩니다.

💡 제안: 필수/선택 필드 명시
 type CreateSpaceBody = {
-  name?: string;
-  description?: string | null;
-  address?: string;
-  thumbnail_url?: string;
-  image_urls?: string[];
-  price_per_hour?: number;
-  category?: string;
-  lat?: number;
-  lng?: number;
+  name: string;
+  description?: string | null;
+  address: string;
+  thumbnail_url: string;
+  image_urls: string[];
+  price_per_hour: number;
+  category: string;
+  lat: number;
+  lng: number;
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/spaces/route.ts` around lines 4 - 14, The CreateSpaceBody type
definition marks all fields as optional with the `?` modifier, but the
validation logic at lines 78-92 treats certain fields as required. Update the
CreateSpaceBody type definition to remove the optional marker from fields that
are validated as mandatory (such as name, address, lat, lng, etc.), keeping only
truly optional fields marked with `?`. This will align the type definition with
the actual validation requirements and improve type safety.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/spaces/route.ts`:
- Around line 78-92: The validation check for the `image_urls` field in the
request body validation section only verifies that it is an array using
`Array.isArray(body.image_urls)`, but does not validate that all elements within
the array are strings. Add an additional check within the same conditional block
to ensure that every element in the `body.image_urls` array is of type string.
This can be done by chaining a check that uses `every()` method to verify all
array elements are strings, preventing invalid data types from being passed to
downstream services.

In `@src/app/Host_post/page.tsx`:
- Around line 168-179: The handleImageSlotChange function creates new Blob URLs
via URL.createObjectURL when replacing images but never revokes the previous
ones, causing memory leaks when images are replaced multiple times in the same
slot. Before assigning a new file and preview URL to a slot, check if the
current slot already has a previewUrl value and revoke it using
URL.revokeObjectURL() before proceeding with the update. This ensures old Blob
URLs are properly cleaned up during image replacement.
- Around line 128-136: The useEffect cleanup function is running every time
imageSlots changes because imageSlots is included in the dependency array, which
immediately revokes newly created Blob URLs before they can be displayed. To fix
this, change the dependency array to an empty array [] so the cleanup function
only runs on component unmount, allowing Blob URL previews to persist during the
component's lifetime.

In `@src/app/Host_post/SpaceCreateForm.tsx`:
- Around line 276-297: The current implementation converts image files to
base64-encoded Data URLs and includes them directly in the JSON request body,
which exceeds the 1MB request size limit for large or multiple images. Instead
of using fileToDataUrl to convert files and embedding them as Data URLs in the
JSON body sent to the "/api/spaces" endpoint, refactor the image upload flow to
either use FormData with multipart/form-data encoding or implement a separate
image upload endpoint that returns URLs to be referenced in the space creation
request. This approach keeps the request body size manageable and follows
standard file upload patterns.

---

Nitpick comments:
In `@src/app/api/spaces/route.ts`:
- Around line 4-14: The CreateSpaceBody type definition marks all fields as
optional with the `?` modifier, but the validation logic at lines 78-92 treats
certain fields as required. Update the CreateSpaceBody type definition to remove
the optional marker from fields that are validated as mandatory (such as name,
address, lat, lng, etc.), keeping only truly optional fields marked with `?`.
This will align the type definition with the actual validation requirements and
improve type safety.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d6742069-979d-4d9b-a9fc-7bd244d40fcc

📥 Commits

Reviewing files that changed from the base of the PR and between 892384d and c8b65f3.

📒 Files selected for processing (3)
  • src/app/Host_post/SpaceCreateForm.tsx
  • src/app/Host_post/page.tsx
  • src/app/api/spaces/route.ts

Comment thread src/app/api/spaces/route.ts
Comment thread src/app/Host_post/page.tsx Outdated
Comment thread src/app/Host_post/page.tsx
Comment on lines +276 to +297
try {
const imageDataUrls = await Promise.all(selectedFiles.map(fileToDataUrl));

const response = await fetch("/api/spaces", {
body: JSON.stringify({
name: form.title.trim(),
description: form.description.trim() || null,
address: form.region.trim(),
thumbnail_url: imageDataUrls[0],
image_urls: imageDataUrls,
price_per_hour: pricePerHour,
category: form.category,
lat,
lng,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
method: "POST",
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Next.js API route body size limit default

💡 Result:

The default request body size limit for Next.js API routes and Server Actions is 1MB [1][2]. The method for configuring or changing this limit depends on which router you are using: Pages Router (API Routes) You can modify the body size limit by exporting a config object within your API route file [1][3]: export const config = { api: { bodyParser: { sizeLimit: '1mb', }, }, } App Router (Route Handlers) There is currently no direct equivalent to the pages router config object for Route Handlers in the App Router to increase the body size limit. If you need to handle larger payloads, you may need to stream the request body or use an alternative approach [4][5][6]. Server Actions For Server Actions, the limit can be configured in your next.config.js file [2]: module.exports = { experimental: { serverActions: { bodySizeLimit: '2mb', // Set your desired limit here }, }, }

Citations:


🏁 Script executed:

# First, let's understand the project structure - check if it's Next.js and which router is used
git ls-files | head -20

Repository: MomentLit/FE-V1

Length of output: 470


🏁 Script executed:

# Check for next.config.js or next.config.ts
find . -maxdepth 2 -name "next.config.*" -o -name "package.json" | head -10

Repository: MomentLit/FE-V1

Length of output: 91


🏁 Script executed:

# Look for the API route being called (/api/spaces)
fd -t f "spaces" --search-path . | grep -i "api\|route"

Repository: MomentLit/FE-V1

Length of output: 41


🏁 Script executed:

# Check the actual SpaceCreateForm.tsx file to understand context better
wc -l src/app/Host_post/SpaceCreateForm.tsx

Repository: MomentLit/FE-V1

Length of output: 101


🏁 Script executed:

# Check next.config.ts for any body size configuration
cat next.config.ts

Repository: MomentLit/FE-V1

Length of output: 295


🏁 Script executed:

# Check the specific lines 276-297 in SpaceCreateForm.tsx
sed -n '276,297p' src/app/Host_post/SpaceCreateForm.tsx

Repository: MomentLit/FE-V1

Length of output: 759


🏁 Script executed:

# Search for the /api/spaces endpoint - might be in src/app/api
find ./src -type f -name "*.ts" -o -name "*.tsx" | xargs grep -l "api/spaces\|/spaces" 2>/dev/null | head -10

Repository: MomentLit/FE-V1

Length of output: 280


🏁 Script executed:

# Check if there's an app/api directory structure
fd -t f . src/app/api 2>/dev/null | head -20

Repository: MomentLit/FE-V1

Length of output: 191


🏁 Script executed:

# Check the API route handler
cat src/app/api/spaces/route.ts

Repository: MomentLit/FE-V1

Length of output: 2636


🏁 Script executed:

# Search for fileToDataUrl function
rg "fileToDataUrl" -A 5 -B 2 src/app/Host_post/SpaceCreateForm.tsx

Repository: MomentLit/FE-V1

Length of output: 534


🏁 Script executed:

# Look for any file size/image handling utilities
fd -t f "util\|helper\|constant" src/app/Host_post/ src/lib/ src/utils/ 2>/dev/null | xargs cat 2>/dev/null | head -50

Repository: MomentLit/FE-V1

Length of output: 41


🏁 Script executed:

# Check if there's a base64 or file conversion utility
rg "toDataUrl|DataUrl|base64" -l src/

Repository: MomentLit/FE-V1

Length of output: 97


Data URL로 인한 요청 본문 크기 초과 문제

Base64로 인코딩된 Data URL은 원본 파일 대비 약 33% 더 크며, 고해상도 이미지 5장이면 수십 MB가 될 수 있습니다. Next.js 기본 요청 본문 제한이 1MB이므로, 현재 코드에서는 다수의 이미지를 업로드할 때 요청이 실패할 가능성이 높습니다.

이미지는 multipart/form-data로 업로드하거나 별도의 업로드 엔드포인트에서 URL을 받아 사용하는 방식이 표준입니다. 또한 상위 서비스가 Data URL 형식을 지원하는지 확인이 필요합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/Host_post/SpaceCreateForm.tsx` around lines 276 - 297, The current
implementation converts image files to base64-encoded Data URLs and includes
them directly in the JSON request body, which exceeds the 1MB request size limit
for large or multiple images. Instead of using fileToDataUrl to convert files
and embedding them as Data URLs in the JSON body sent to the "/api/spaces"
endpoint, refactor the image upload flow to either use FormData with
multipart/form-data encoding or implement a separate image upload endpoint that
returns URLs to be referenced in the space creation request. This approach keeps
the request body size manageable and follows standard file upload patterns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant