๐จ 3๋จ๊ณ : ์ปค๋ฎค๋ํฐ ๋๋ฉ์ธ ํ ํฌ์คํ - 100-hours-a-week/7-team-ddb-wiki GitHub Wiki
์์ฑ์ผ | 2024-04-27 |
---|---|
๋ฒ์ | 1.0 |
์์ฑ์ | suzy.kang (๊ฐ์์ง) |
๊ฒํ ์ | kevin |
์ํ | ์ด์ |
- ์ฌ์ฉ์ ๊ฐ ๊ธฐ๋ก ๊ณต์ ๋ฐ ์ํต ๊ธฐ๋ฅ ์ ๊ณต
- ์ง๊ด์ ์ธ ๊ธฐ๋ก ์์ฑ ๋ฐ ์กฐํ ์ธํฐํ์ด์ค ๊ตฌํ
- ํจ์จ์ ์ธ ์ด๋ฏธ์ง ์ ๋ก๋ ๋ฐ ๊ด๋ฆฌ ์์คํ ๊ตฌ์ถ
- ์ค์๊ฐ ์ฑํ ๊ธฐ๋ฅ
- ๊ทธ๋ฃน ์ฑํ ๋ฐฉ
- ์ค์๊ฐ ์๋ฆผ
-
Next.js (v15.3.0) + React (v19.1.0)
- App Router ๊ธฐ๋ฐ ๋ผ์ฐํ
- SSR/ISR ํ์ฉ์ผ๋ก SEO ์ต์ ํ
- React 19์ use() ํ ํ์ฉํ ๋ฐ์ดํฐ ํ์นญ
-
Zustand (v5.0.3)
- ์์ ๋ฒ๋ค ์ฌ์ด์ฆ๋ก ๋ชจ๋ฐ์ผ ์น ์ต์ ํ
- ์๋ฒ ์ปดํฌ๋ํธ ํธํ์ฑ
- ์ง๊ด์ ์ธ API๋ก ๋น ๋ฅธ ๊ฐ๋ฐ ๊ฐ๋ฅ
-
Tailwind CSS (v4.1)
- ์ ํธ๋ฆฌํฐ ํผ์คํธ ์ ๊ทผ์ผ๋ก ๋น ๋ฅธ ๊ฐ๋ฐ
- ์ปค์คํ ํ ๋ง๋ก ๋ธ๋๋ ๋์์ธ ์์คํ ๊ตฌ์ถ
-
shadcn/ui
- Tailwind ๊ธฐ๋ฐ ์ปดํฌ๋ํธ
- ํ์ํ ์ปดํฌ๋ํธ๋ง ๊ฐ์ ธ์ ๋ฒ๋ค ํฌ๊ธฐ ์ต์ ํ
-
React use() + TanStack Query (v5)
- ์๋ฒ ์ปดํฌ๋ํธ: use() ํ ์ผ๋ก SEO ์ต์ ํ
- ํด๋ผ์ด์ธํธ: TanStack Query๋ก ๋ณต์กํ ์ํ ๊ด๋ฆฌ
-
Zod (v3.24.3)
- ํ์ ์คํฌ๋ฆฝํธ ์ฐ๋
- ์๋ฒ-ํด๋ผ์ด์ธํธ ์คํค๋ง ๊ณต์
-
react-hook-form (v7.51.2)
- ๊ฐ๋ฒผ์ด ๋ฒ๋ค ์ฌ์ด์ฆ๋ก ๋ชจ๋ฐ์ผ ์นํ์
- Zod๊ณผ ํตํฉํด ์ ๊ตํ ์ ํจ์ฑ ๊ฒ์ฌ ๊ฐ๋ฅ
- Server Action๊ณผ ๋ณํ ์ฌ์ฉ ๊ฐ๋ฅ (ํ์ ์ SSR + ๋น ๋ฅธ UX ๋ชจ๋ ํ๋ณด)
-
Kakao Maps SDK (v2.7.5)
- ๊ตญ๋ด ์ฌ์ฉ์ ์นํ์ UI
- ์ปค์คํ ๋ง์ปค/ํด๋ฌ์คํฐ๋ง ์ง์
-
Turbopack(v1.5.3) + webpack (v5)
- Next.js 15 ๋ด์ฅ ๋น๋ ๋๊ตฌ ( Turbopack : ๊ฐ๋ฐ ํ๊ฒฝ, Webpack : ํ๋ก๋์ ํ๊ฒฝ )
- ๋น ๋ฅธ ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ HMR ์ง์
-
/moments
: SSR + CSR ํผํฉ-
SSR ์ ์ฉ ๋ถ๋ถ:
- ์ด๊ธฐ ๊ธฐ๋ก ๋ชฉ๋ก ๋ก๋ (10๊ฐ)
- ๋ฉํ๋ฐ์ดํฐ ๋ฐ SEO ์ต์ ํ
-
CSR ์ ์ฉ ๋ถ๋ถ:
- ๋ฌดํ ์คํฌ๋กค (์ถ๊ฐ 10๊ฐ์ฉ)
- ์ค์๊ฐ ์ํธ์์ฉ
-
SSR ์ ์ฉ ๋ถ๋ถ:
-
/moments/[id]
: SSR + CSR ํผํฉ-
SSR ์ ์ฉ ๋ถ๋ถ:
- ๊ธฐ๋ก ๊ธฐ๋ณธ ์ ๋ณด
- ๋ฉํ๋ฐ์ดํฐ ๋ฐ SEO ์ต์ ํ
-
CSR ์ ์ฉ ๋ถ๋ถ:
- ๋๊ธ ๋ฌดํ ์คํฌ๋กค (10๊ฐ์ฉ)
- ์ค์๊ฐ ์ํธ์์ฉ
-
SSR ์ ์ฉ ๋ถ๋ถ:
-
/moments/new
: CSR ์ฌ์ฉ- ์ด๋ฏธ์ง ์ ๋ก๋
- ์ค์๊ฐ ํผ ์ ํจ์ฑ ๊ฒ์ฆ
- ์ฌ์ฉ์ ์ธํฐ๋์ ์ค์ฌ
-
/moments/[id]/edit
: CSR ์ฌ์ฉ- ์ด๋ฏธ์ง ์์
- ์ค์๊ฐ ํผ ์ ํจ์ฑ ๊ฒ์ฆ
- ์ฌ์ฉ์ ์ธํฐ๋์ ์ค์ฌ
-
/users/[id]
: SSR + CSR ํผํฉ-
SSR ์ ์ฉ ๋ถ๋ถ:
- ์ฌ์ฉ์ ํ๋กํ ์ ๋ณด
- ๋ฉํ๋ฐ์ดํฐ ๋ฐ SEO ์ต์ ํ
-
CSR ์ ์ฉ ๋ถ๋ถ:
- ๊ธฐ๋ก ๋ชฉ๋ก ๋ฌดํ ์คํฌ๋กค
- ์ค์๊ฐ ์ํธ์์ฉ
-
SSR ์ ์ฉ ๋ถ๋ถ:
-
์ฃผ์๊ธฐ๋ฅ:
- ๊ธฐ๋ก ๋ชฉ๋ก ํ์ (๋ฌดํ ์คํฌ๋กค, 10๊ฐ์ฉ)
- ๊ธฐ๋ก ๊ณต๊ฐ/๋น๊ณต๊ฐ ํ์ (์๋ฌผ์ ์์ด์ฝ)
- ๋งจ ์๋ก ๋ฒํผ (500px ์คํฌ๋กค ์ ํ์)
-
์ฌ์ฉ ์ปดํฌ๋ํธ:
-
MomentList
(๊ธฐ๋ก ๋ชฉ๋ก) -
MomentCard
(๊ธฐ๋ก ์นด๋) -
ScrollToTopButton
(๋งจ ์๋ก ๋ฒํผ)
-
-
๋ฐ์ดํฐ ๋ก๋ฉ ์์ :
- ํ์ด์ง ์ง์
์:
GET /api/v1/users/me/moments?limit=10
- ์คํฌ๋กค ์:
GET /api/v1/users/me/moments?limit=10&cursor={cursor}
- ํ์ด์ง ์ง์
์:
-
UI ์๊ตฌ์ฌํญ:
- ์ฌ์ง ์ ๋ฌด์ ๋ฐ๋ฅธ ๋ ์ด์์ ์ฐจ๋ณํ
- ์ ๋ชฉ: ์ต๋ 15์, ์ดํ ๋ง์ค์ํ
- ๋ณธ๋ฌธ: ์ฌ์ง ์์ ๋ 50์, ์์ ๋ 80์๊น์ง ํ์
- ์ฒซ ๋ฒ์งธ ์ฌ์ง๋ง ํ์
- ๋ ์ง ๊ธฐ์ค ์ต์ ์ ์ ๋ ฌ
-
์ฃผ์๊ธฐ๋ฅ:
- ๊ธฐ๋ก ์์ธ ์ ๋ณด ํ์
- ์ด๋ฏธ์ง ์ฌ๋ผ์ด๋ (์ต๋ 3์ฅ, ์ค์์ดํ/๋ฒํผ)
- ๋๊ธ ๋ชฉ๋ก ๋ฐ ์์ฑ
- ์์ /์ญ์ ๊ธฐ๋ฅ (๋ชจ๋ฌ ํ์ธ)
-
์ฌ์ฉ ์ปดํฌ๋ํธ:
-
MomentDetail
(๊ธฐ๋ก ์์ธ) -
ImageSlider
(์ด๋ฏธ์ง ์ฌ๋ผ์ด๋) -
CommentList
(๋๊ธ ๋ชฉ๋ก) -
CommentForm
(๋๊ธ ์์ฑ)
-
-
๋ฐ์ดํฐ ๋ก๋ฉ ์์ :
- ํ์ด์ง ์ง์ ์: ๊ธฐ๋ก ์์ธ ์ ๋ณด
- ์คํฌ๋กค ์:
GET /api/v1/moments/{momentId}/comments?limit=10&cursor={cursor}
-
UI ์๊ตฌ์ฌํญ:
- ์์ฑ์ ํ๋กํ ์ด๋ฏธ์ง
- ์ ๋ชฉ, ๋ ์ง ํ์
- ์์ /์ญ์ ๋ฉ๋ด (์ 3๊ฐ)
- ์ด๋ฏธ์ง ์ธ๋์ผ์ดํฐ (2์ฅ ์ด์)
- ์ฅ์ ์ด๋ฆ (ํด๋ฆญ ์ ์ฅ์ ์์ธ)
- ๋๊ธ ์ ๋ ฅ์ฐฝ (ํ๋กํ ์ด๋ฏธ์ง ํฌํจ)
- ๋๊ธ ๋ชฉ๋ก (ํ๋กํ, ์์ฑ์ผ, ๋ณธ๋ฌธ)
- ์์ ์ ๋๊ธ ์ญ์ ์์ด์ฝ
-
์ฃผ์๊ธฐ๋ฅ:
- ๊ธฐ๋ก ์์ฑ ํผ
- ์ด๋ฏธ์ง ์ ๋ก๋
- ๊ณต๊ฐ/๋น๊ณต๊ฐ ์ค์
-
์ฌ์ฉ ์ปดํฌ๋ํธ:
-
MomentForm
(๊ธฐ๋ก ์์ฑ ํผ) -
ImageUploader
(์ด๋ฏธ์ง ์ ๋ก๋) -
VisibilityToggle
(๊ณต๊ฐ/๋น๊ณต๊ฐ ํ ๊ธ)
-
-
๋ฐ์ดํฐ ๋ก๋ฉ ์์ :
- ์ด๋ฏธ์ง ์
๋ก๋ ์:
POST /api/v1/gcs/signed-urls
- ํผ ์ ์ถ ์:
POST /api/v1/users/moments
- ์ด๋ฏธ์ง ์
๋ก๋ ์:
-
UI ์๊ตฌ์ฌํญ:
- ๊ณต๊ฐ/๋น๊ณต๊ฐ ํ ๊ธ
- ์์ฑ ์๋ฃ ๋ฒํผ
- ์ ๋ชฉ ์ ๋ ฅ ํ๋
- ๋ณธ๋ฌธ ์ ๋ ฅ ํ๋
- ์ฅ์ ํ์ (์์ ๋ถ๊ฐ)
-
์ฃผ์๊ธฐ๋ฅ:
- ๊ธฐ๋ก ์์ ํผ
- ์ด๋ฏธ์ง ์์
- ๊ณต๊ฐ/๋น๊ณต๊ฐ ์ค์
-
์ฌ์ฉ ์ปดํฌ๋ํธ:
-
MomentForm
(๊ธฐ๋ก ์์ ํผ) -
ImageUploader
(์ด๋ฏธ์ง ์์ ) -
VisibilityToggle
(๊ณต๊ฐ/๋น๊ณต๊ฐ ํ ๊ธ)
-
-
๋ฐ์ดํฐ ๋ก๋ฉ ์์ :
- ํ์ด์ง ์ง์ ์: ๊ธฐ์กด ๊ธฐ๋ก ๋ฐ์ดํฐ
- ์ด๋ฏธ์ง ์
๋ก๋ ์:
POST /api/v1/gcs/signed-urls
- ํผ ์ ์ถ ์:
PATCH /api/v1/users/moments/{moment_id}
-
UI ์๊ตฌ์ฌํญ:
- ๊ณต๊ฐ/๋น๊ณต๊ฐ ํ ๊ธ
- ์์ฑ ์๋ฃ ๋ฒํผ
- ์ ๋ชฉ ์์ ํ๋
- ๋ณธ๋ฌธ ์์ ํ๋
- ์ฅ์ ํ์ (์์ ๋ถ๊ฐ)
-
์ฃผ์๊ธฐ๋ฅ:
- ์ ์ ํ๋กํ ์ ๋ณด
- ์ ์ ๊ธฐ๋ก ๋ชฉ๋ก
-
์ฌ์ฉ ์ปดํฌ๋ํธ:
-
UserProfile
(์ ์ ํ๋กํ) -
MomentList
(๊ธฐ๋ก ๋ชฉ๋ก)
-
-
๋ฐ์ดํฐ ๋ก๋ฉ ์์ :
- ํ์ด์ง ์ง์
์:
GET /api/v1/users/{user_id}
- ์คํฌ๋กค ์:
GET /api/v1/users/{user_id}/moments?limit=10&cursor={cursor}
- ํ์ด์ง ์ง์
์:
-
UI ์๊ตฌ์ฌํญ:
- ํ๋กํ ์ด๋ฏธ์ง
- ๋๋ค์
- ์๊ฐ๊ธ
- ๊ธฐ๋ก ๋ชฉ๋ก (๋ด ๊ธฐ๋ก ๋ชฉ๋ก๊ณผ ๋์ผ UI)
-
๋ฐ์ดํฐ ์ฒ๋ฆฌ:
- ํ๋กํ ์ด๋ฏธ์ง ์ต์ ํ (Next.js Image)
- ๋ ์ง ํฌ๋งทํ (created_at, updated_at)
-
๊ฐ์
- ์ญํ : ๊ธฐ๋ก ๋ชฉ๋ก์ ๋ฌดํ ์คํฌ๋กค๋ก ํ์
-
Props & Interface
interface MomentListProps { moments: Array<{ id: number; title: string; thumbnail?: string; images_count: number; is_public: boolean; created_at: string; place: { id: string; name: string; }; location: string; }>; isLoading?: boolean; onLoadMore: () => Promise<void>; hasMore: boolean; }
-
์คํ์ผ๋ง
- ๋ฆฌ์คํธ ๋ ์ด์์
- ๋ฌดํ ์คํฌ๋กค ์ธ๋์ผ์ดํฐ
- ๋ ์ง ํฌ๋งทํ (yyyy.MM.dd)
-
Storybook
- Default: ๊ธฐ๋ณธ ๋ชฉ๋ก
- Loading: ๋ก๋ฉ ์ํ
- Empty: ๋น ๋ชฉ๋ก
- With Items: ๊ธฐ๋ก์ด ์๋ ์ํ
-
๊ฐ์
- ์ญํ : ๊ฐ๋ณ ๊ธฐ๋ก ์ ๋ณด๋ฅผ ์นด๋ ํํ๋ก ํ์
-
Props & Interface
interface MomentCardProps { id: number; title: string; thumbnail?: string; images_count: number; is_public: boolean; created_at: string; place: { id: string; name: string; }; location: string; onCardClick: (momentId: number) => void; }
-
์คํ์ผ๋ง
- ์นด๋ ๋ ์ด์์
- ์ด๋ฏธ์ง ๋น์จ
- ๊ณต๊ฐ/๋น๊ณต๊ฐ ์์ด์ฝ
-
Storybook
- Default: ๊ธฐ๋ณธ ์นด๋
- With Image: ์ด๋ฏธ์ง๊ฐ ์๋ ์ํ
- Private: ๋น๊ณต๊ฐ ์ํ
- Long Title: ๊ธด ์ ๋ชฉ
-
๊ฐ์
- ์ญํ : ์ฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ผ์ด๋๋ก ํ์
-
Props & Interface
interface ImageSliderProps { images: string[]; showIndicators?: boolean; }
-
์คํ์ผ๋ง
- ์ฌ๋ผ์ด๋ ๋ ์ด์์
- ์ธ๋์ผ์ดํฐ
- ์ค์์ดํ ์ ๋๋ฉ์ด์
-
Storybook
- Single Image: ๋จ์ผ ์ด๋ฏธ์ง
- Multiple Images: ์ฌ๋ฌ ์ด๋ฏธ์ง
- With Indicators: ์ธ๋์ผ์ดํฐ ํ์
-
๊ฐ์
- ์ญํ : ๋๊ธ ๋ชฉ๋ก์ ๋ฌดํ ์คํฌ๋กค๋ก ํ์
-
Props & Interface
interface CommentListProps { comments: Array<{ id: number; user: { id: number; nickname: string; profile_image?: string; }; content: string; created_at: string; is_owner: boolean; }>; isLoading?: boolean; onLoadMore: () => Promise<void>; hasMore: boolean; onDelete: (commentId: number) => Promise<void>; }
-
์คํ์ผ๋ง
- ๋ฆฌ์คํธ ๋ ์ด์์
- ๋ฌดํ ์คํฌ๋กค ์ธ๋์ผ์ดํฐ
- ๋ ์ง ํฌ๋งทํ
-
Storybook
- Default: ๊ธฐ๋ณธ ๋ชฉ๋ก
- Loading: ๋ก๋ฉ ์ํ
- Empty: ๋น ๋ชฉ๋ก
- With Items: ๋๊ธ์ด ์๋ ์ํ
-
๊ฐ์
- ์ญํ : ๋๊ธ ์์ฑ ํผ
-
Props & Interface
interface CommentFormProps { onSubmit: (content: string) => Promise<void>; userProfileImage?: string; }
-
์คํ์ผ๋ง
- ํผ ๋ ์ด์์
- ์ ๋ ฅ ํ๋
- ์ ์ถ ๋ฒํผ
-
Storybook
- Default: ๊ธฐ๋ณธ ํผ
- With Profile: ํ๋กํ ์ด๋ฏธ์ง๊ฐ ์๋ ์ํ
- Loading: ์ ์ถ ์ค ์ํ
-
๊ฐ์
- ์ญํ : ํ์ด์ง ์ต์๋จ์ผ๋ก ์คํฌ๋กคํ๋ ๋ฒํผ
-
Props & Interface
interface ScrollToTopButtonProps { threshold?: number; }
-
์คํ์ผ๋ง
- ๋ฒํผ ๋์์ธ
- ์ ๋๋ฉ์ด์ ํจ๊ณผ
-
Storybook
- Default: ๊ธฐ๋ณธ ๋ฒํผ
- Visible: ํ์ ์ํ
- Hidden: ์จ๊น ์ํ
-
๊ฐ์
- ์ญํ : ๊ธฐ๋ก ์ ๋ชฉ์ ์ ๋ ฅํ๋ ์ปดํฌ๋ํธ
-
Props & Interface
interface TitleInputProps { value: string; onChange: (value: string) => void; placeholder?: string; maxLength?: number; error?: string; }
-
์คํ์ผ๋ง
- ์ ๋ ฅ ํ๋ ๋ ์ด์์
- ์๋ฌ ๋ฉ์์ง ์คํ์ผ
- ๊ธ์ ์ ์ ํ ํ์
-
Storybook
- Default: ๊ธฐ๋ณธ ์ ๋ ฅ ํ๋
- With Error: ์๋ฌ ์ํ
- With Max Length: ์ต๋ ๊ธธ์ด ์ ํ
- With Placeholder: ํ๋ ์ด์คํ๋
-
๊ฐ์
- ์ญํ : ๊ธฐ๋ก ๋ณธ๋ฌธ์ ์ ๋ ฅํ๋ ์ปดํฌ๋ํธ
-
Props & Interface
interface ContentInputProps { value: string; onChange: (value: string) => void; placeholder?: string; maxLength?: number; error?: string; rows?: number; }
-
์คํ์ผ๋ง
- ํ ์คํธ ์์ญ ๋ ์ด์์
- ์๋ฌ ๋ฉ์์ง ์คํ์ผ
- ๊ธ์ ์ ์ ํ ํ์
-
Storybook
- Default: ๊ธฐ๋ณธ ํ ์คํธ ์์ญ
- With Error: ์๋ฌ ์ํ
- With Max Length: ์ต๋ ๊ธธ์ด ์ ํ
- With Placeholder: ํ๋ ์ด์คํ๋
- With Custom Rows: ์ปค์คํ ํ ์
-
๊ฐ์
- ์ญํ : ํน์ ์ฅ์์ ๋ํ ๊ธฐ๋ก ๋ชฉ๋ก์ ํ์
-
Props & Interface
interface PlaceMomentListProps { placeId: string; moments: Array<{ id: number; title: string; thumbnail?: string; images_count: number; is_public: boolean; created_at: string; user: { id: number; nickname: string; profile_image?: string; }; location: string; }>; isLoading?: boolean; onLoadMore: () => Promise<void>; hasMore: boolean; }
-
์คํ์ผ๋ง
- ๋ฆฌ์คํธ ๋ ์ด์์
- ๋ฌดํ ์คํฌ๋กค ์ธ๋์ผ์ดํฐ
- ๋ ์ง ํฌ๋งทํ
-
Storybook
- Default: ๊ธฐ๋ณธ ๋ชฉ๋ก
- Loading: ๋ก๋ฉ ์ํ
- Empty: ๋น ๋ชฉ๋ก
- With Items: ๊ธฐ๋ก์ด ์๋ ์ํ
-
MomentList
:- ์ญํ : ๊ธฐ๋ก ๋ชฉ๋ก ํ์
-
๊ด๊ณ:
MomentCard
๋ฅผ ์์ ์ปดํฌ๋ํธ๋ก ํฌํจ
-
MomentDetail
:- ์ญํ : ๊ธฐ๋ก ์์ธ ์ ๋ณด ํ์
-
๊ด๊ณ:
ImageSlider
,CommentList
,CommentForm
๊ณผ ์ฐ๋
-
MomentForm
:- ์ญํ : ๊ธฐ๋ก ์์ฑ/์์ ํผ
-
๊ด๊ณ:
-
TitleInput
,ContentInput
์ ์์ ์ปดํฌ๋ํธ๋ก ํฌํจ -
ImageUploader
,VisibilityToggle
๊ณผ ์ฐ๋
-
-
PlaceMomentList
:- ์ญํ : ์ฅ์๋ณ ๊ธฐ๋ก ๋ชฉ๋ก ํ์
-
๊ด๊ณ:
MomentCard
๋ฅผ ์์ ์ปดํฌ๋ํธ๋ก ํฌํจ
-
Local State (React useState):
- MomentList: ๋ฌดํ ์คํฌ๋กค ์ํ
- ImageSlider: ํ์ฌ ์ด๋ฏธ์ง ์ธ๋ฑ์ค
- CommentForm: ์ ๋ ฅ๊ฐ ์ํ
-
Global State (Zustand):
-
momentStore:
-
์ํ:
moments
,currentMoment
,isLoading
-
์ก์
:
fetchMoments
,createMoment
,updateMoment
,deleteMoment
-
์ํ:
-
commentStore:
-
์ํ:
comments
,isLoading
-
์ก์
:
fetchComments
,createComment
,deleteComment
-
์ํ:
-
placeStore:
-
์ํ:
placeMoments
,isLoading
-
์ก์
:
fetchPlaceMoments
,loadMorePlaceMoments
-
์ํ:
-
momentStore:
-
Server Cache State (TanStack Query):
- ๊ธฐ๋ก ๋ชฉ๋ก ์บ์ฑ
- ๊ธฐ๋ก ์์ธ ์ ๋ณด ์บ์ฑ
- ๋๊ธ ๋ชฉ๋ก ์บ์ฑ
-
ํธ์ถํ ๋ฐฑ์๋ API ๋ชฉ๋ก
-
GET /api/v1/users/me/moments
: ๋ณธ์ธ ๊ธฐ๋ก ๋ชฉ๋ก ์กฐํ -
GET /api/v1/users/{user_id}/moments
: ๋ค๋ฅธ ์ฌ์ฉ์ ๊ธฐ๋ก ๋ชฉ๋ก ์กฐํ -
GET /api/v1/users/{user_id}
: ๋ค๋ฅธ ์ฌ์ฉ์ ํ๋กํ ์กฐํ -
POST /api/v1/users/moments
: ๊ธฐ๋ก ์์ฑ -
PATCH /api/v1/users/moments/{moment_id}
: ๊ธฐ๋ก ์์ -
DELETE /api/v1/users/moments/{moment_id}
: ๊ธฐ๋ก ์ญ์ -
POST /api/v1/gcs/signed-urls
: ์ด๋ฏธ์ง ์ ๋ก๋ URL ๋ฐ๊ธ -
GET /api/v1/moments/{momentId}/comments
: ๋๊ธ ์กฐํ -
POST /api/v1/moments/{moment_id}/comments
: ๋๊ธ ์์ฑ -
DELETE /api/v1/moments/{moment_id}/comments/{comment_id}
: ๋๊ธ ์ญ์
-
-
API ํธ์ถ ์ฒ๋ฆฌ
- ๋ฌดํ ์คํฌ๋กค ๊ตฌํ
- ์ด๋ฏธ์ง ์ ๋ก๋ ์ต์ ํ
- ์๋ฌ ์ฒ๋ฆฌ ๋ฐ ์ฌ์๋
- ์บ์ ๊ด๋ฆฌ
- ์ด๋ฏธ์ง ์ต์ ํ
- Next.js Image ์ปดํฌ๋ํธ ์ฌ์ฉ
- WebP ํฌ๋งท ์ฌ์ฉ
- ์ ์ ํ ์ด๋ฏธ์ง ํฌ๊ธฐ ์ค์
- ๋ฌดํ ์คํฌ๋กค ์ต์ ํ
- ๊ฐ์ํ ์คํฌ๋กค ์ ์ฉ
- ๋ฐ์ดํฐ ํ๋ฆฌํ์นญ
- ๋ฒ๋ค ํฌ๊ธฐ ์ต์ ํ
- ์ฝ๋ ์คํ๋ฆฌํ
- ๋์ ์ํฌํธ
- ์ต์ ๊ธธ์ด: 2์
- ์ต๋ ๊ธธ์ด: 50์
- ํ์ ์ ๋ ฅ: true
- ๊ธฐ๋ณธ ๊ฐ: (์ ์ ์ด๋ฆ)์ ๊ธฐ๋ก
-
์ ํ ์ฌํญ:
- ๊ณต๋ฐฑ๋ง์ผ๋ก ๊ตฌ์ฑ ๋ถ๊ฐ
- ํน์๋ฌธ์ ์ฌ์ฉ ์ ํ (์ด๋ชจ์ง, HTML ํ๊ทธ ๋ฑ)
-
์๋ฌ ๋ฉ์์ง:
- ํ์ ์ ๋ ฅ: "์ ๋ชฉ์ ์ ๋ ฅํด์ฃผ์ธ์"
- ์ต์ ๊ธธ์ด ๋ฏธ๋ฌ: "์ ๋ชฉ์ ์ต์ 2์ ์ด์์ด์ด์ผ ํฉ๋๋ค"
- ์ต๋ ๊ธธ์ด ์ด๊ณผ: "์ ๋ชฉ์ 50์๋ฅผ ์ด๊ณผํ ์ ์์ต๋๋ค"
- ๊ณต๋ฐฑ๋ง ์ ๋ ฅ: "์ ๋ชฉ์ ์ ๋ ฅํด์ฃผ์ธ์"
- ํน์๋ฌธ์ ์ฌ์ฉ: "ํน์๋ฌธ์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค"
- ์ต์ ๊ธธ์ด: 1์
- ์ต๋ ๊ธธ์ด: 2200์
- ํ์ ์ ๋ ฅ: true
-
์ ํ ์ฌํญ:
- ๊ณต๋ฐฑ๋ง์ผ๋ก ๊ตฌ์ฑ ๋ถ๊ฐ
- XSS ๋ฐฉ์ง๋ฅผ ์ํ HTML ํ๊ทธ ํํฐ๋ง
-
์๋ฌ ๋ฉ์์ง:
- ํ์ ์ ๋ ฅ: "๋ณธ๋ฌธ์ ์ ๋ ฅํด์ฃผ์ธ์"
- ์ต์ ๊ธธ์ด ๋ฏธ๋ฌ: "๋ณธ๋ฌธ์ ์ต์ 1์ ์ด์์ด์ด์ผ ํฉ๋๋ค"
- ์ต๋ ๊ธธ์ด ์ด๊ณผ: "๋ณธ๋ฌธ์ 2200์๋ฅผ ์ด๊ณผํ ์ ์์ต๋๋ค"
- ๊ณต๋ฐฑ๋ง ์ ๋ ฅ: "๋ณธ๋ฌธ์ ์ ๋ ฅํด์ฃผ์ธ์"
- ์ต๋ ๊ธธ์ด: 200์
- ํ์ ์ ๋ ฅ: true
-
์ ํ ์ฌํญ:
- ๊ณต๋ฐฑ๋ง์ผ๋ก ๊ตฌ์ฑ ๋ถ๊ฐ
- XSS ๋ฐฉ์ง๋ฅผ ์ํ HTML ํ๊ทธ ํํฐ๋ง
-
์๋ฌ ๋ฉ์์ง:
- ํ์ ์ ๋ ฅ: "๋๊ธ์ ์ ๋ ฅํด์ฃผ์ธ์"
- ์ต๋ ๊ธธ์ด ์ด๊ณผ: "๋๊ธ์ 200์๋ฅผ ์ด๊ณผํ ์ ์์ต๋๋ค"
- ๊ณต๋ฐฑ๋ง ์ ๋ ฅ: "๋๊ธ์ ์ ๋ ฅํด์ฃผ์ธ์"
- ์ต๋ ๊ฐ์: 3๊ฐ
- ํ์ผ ํ์: jpg, jpeg, png, webp
- ์ต๋ ํฌ๊ธฐ: 5MB
-
์ ํ ์ฌํญ:
- ์ด๋ฏธ์ง ๋น์จ: 1:1
-
์๋ฌ ๋ฉ์์ง:
- ์ต๋ ๊ฐ์ ์ด๊ณผ: "์ด๋ฏธ์ง๋ ์ต๋ 3๊ฐ๊น์ง ์ ๋ก๋ํ ์ ์์ต๋๋ค"
- ์ง์ํ์ง ์๋ ํ์: "์ง์ํ์ง ์๋ ์ด๋ฏธ์ง ํ์์ ๋๋ค"
- ์ต๋ ํฌ๊ธฐ ์ด๊ณผ: "์ด๋ฏธ์ง ํฌ๊ธฐ๋ 5MB๋ฅผ ์ด๊ณผํ ์ ์์ต๋๋ค"