Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c8a0de1
chore : ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ์žฌํŽธ
guen9310 Mar 24, 2026
ae8de09
chore : prisma.utils.ts ๋ถ„ํ•ด
guen9310 Mar 25, 2026
7c01685
chore: service.utils.ts ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ
guen9310 Mar 25, 2026
f30e633
chore: ๋ถˆํ•„์š”ํ•œ ํŒŒ์ผ ๋ฐ ํŒจํ‚ค์ง€ ์ •๋ฆฌ
guen9310 Mar 26, 2026
c162dc8
refactor: GlobalStateSync ์ œ๊ฑฐ ํ›„ useSession์œผ๋กœ ์ „ํ™˜
guen9310 Mar 26, 2026
d56bb8d
perf : jsodom + dompurify ์ œ๊ฑฐ, sanitizeHtml ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ต์ฒด
guen9310 Mar 26, 2026
6af815d
refactor: core-request ๋‹จ์ˆœํ™”
guen9310 Mar 28, 2026
65339ae
refactor: ํŒŒํŽธํ™”๋œ Zustand ์Šคํ† ์–ด๋ฅผ ๋„๋ฉ”์ธ ๊ธฐ์ค€์œผ๋กœ ํ†ตํ•ฉ
guen9310 Mar 28, 2026
891254b
chore: ๋ถˆํ•„์š”ํ•œ ๋ฒˆ๋“ค ์ตœ์†Œํ™”
guen9310 Mar 28, 2026
a850fe2
test: drift ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ
guen9310 Mar 29, 2026
e6a4f82
refactor: ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ œ๊ฑฐ ๋ฐ ckeditor -> tiptap์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
guen9310 Mar 30, 2026
9c47877
feat:ํ•„ํ„ฐ ๋กœ์ง ์ˆ˜์ •
guen9310 Mar 31, 2026
a343b24
feat: ์œ„์น˜ ๊ธฐ๋ฐ˜ ๋งค์žฅ ์ฐพ๊ธฐ ๋„ค์ด๋ฒ„ -> ์นด์นด์˜ค ์ง€๋„๋กœ ๋ณ€๊ฒฝ
guen9310 Apr 6, 2026
1a7afcf
chore: useUserStore -> useCurrentUser๋กœ ์ด๋ฆ„ ๋ณ€๊ฒฝ
guen9310 Apr 8, 2026
f07ed7b
refactor: community/me/review/point ์„œ๋น„์Šค ํด๋” ์Šน๊ฒฉ ๋ฐ ์ฑ…์ž„ ๋ถ„๋ฆฌ
guen9310 Apr 8, 2026
d71dae3
chore: ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ์ œ๊ฑฐ
guen9310 Apr 9, 2026
2c1f755
feat: ๊ณ„์ • ํƒˆํ‡ด ๊ธฐ๋Šฅ ๊ตฌํ˜„
guen9310 Apr 9, 2026
af12492
refactor: next-auth ์žฌ์„ค๊ณ„ ๋ฐ CurrentUser ์ƒํƒœ ๋ถ„๋ฆฌ
guen9310 Apr 10, 2026
2093c4b
feat: ์†Œ์…œ ๊ณ„์ • ์ด๋ฉ”์ผ ๊ธฐ๋ฐ˜ ๊ณต์œ  ๋ฐ ๊ณ„์ • ์—ฐ๊ฒฐ ์•Œ๋ฆผ
guen9310 Apr 10, 2026
274b076
refactor: Prisma ํƒ€์ž…์„ ์Šคํ‚ค๋งˆ ํŒŒ์ผ re-export๋กœ ์ผ์›ํ™”
guen9310 Apr 11, 2026
e35a0f2
feat: ํ–ฅ์ˆ˜ ์ƒ์„ธ ํŽ˜์ด์ง€ ์Šค์ผˆ๋ ˆํ†ค UI ๊ตฌํ˜„
guen9310 Apr 11, 2026
0a7955a
feat: ํ–ฅ์ˆ˜ ์ƒ์„ธ ํŽ˜์ด์ง€ ์‹ค๋ฐ์ดํ„ฐ ์—ฐ๋™ ๋ฐ ๋ชฉ๋ฐ์ดํ„ฐ ์ œ๊ฑฐ
guen9310 Apr 11, 2026
20ec4f9
fix: ๋ฆฌ๋ทฐ ๋กœ๊ทธ์ธ ๊ฐ€๋“œ๋ฅผ ๋ฒ„ํŠผ ํ•ธ๋“ค๋Ÿฌ๋กœ ์ด๋™ ๋ฐ PostForm initialPerfumes prop ์ถ”๊ฐ€
guen9310 Apr 11, 2026
96af146
feat: ๋กœ๊ทธ์ธ ์—๋Ÿฌ ํ•ธ๋“ค๋ง ์ถ”๊ฐ€ ๋ฐ ๊ตฌ์กฐ ๊ฐœ์„ 
guen9310 Apr 14, 2026
27ea575
fix: cache ๋ฌด๋ ฅํ™” ์˜ค๋ฒ„๋ผ์ด๋“œ ๋ฌธ์ œ ํ•ด๊ฒฐ
guen9310 Apr 14, 2026
abc0710
feat: ๊ฒ€์ƒ‰ ์—๋Ÿฌ ํ•ธ๋“ค๋ง ์ถ”๊ฐ€ ๋ฐ ๊ตฌ์กฐ ๊ฐœ์„ 
guen9310 Apr 16, 2026
22014fa
feat: ์„ธ์…˜ ์ฟ ํ‚ค ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ์ž๋™ ์‚ญ์ œ
guen9310 Apr 16, 2026
f1c851c
fix: fill + object-contain ์กฐํ•ฉ์—์„œ placeholder blur๋กœ ์ธํ•œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๋ณ€๋™ ํ˜„์ƒ ์ œ๊ฑฐ
guen9310 Apr 16, 2026
57ce029
refactor: ๊ฒŒ์‹œ๊ธ€ ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ฐœ์„ 
guen9310 Apr 20, 2026
36ba399
refactor: Draft API ๋‹จ์ผ ์กฐํšŒ ๋ฆฌํŒฉํ† ๋ง
guen9310 Apr 20, 2026
fd7c0eb
fix: Session SSR ์ดˆ๊ธฐํ™”
guen9310 Apr 20, 2026
04e8257
fixed: ๋นŒ๋“œ ์—๋Ÿฌ ๋ฐ ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ ๊นจ์ง ํ˜„์ƒ ์ˆ˜์ •
guen9310 Apr 21, 2026
3b982bf
docs: README.md ์ตœ์‹ ํ™”
guen9310 Apr 21, 2026
3902616
fix: Kakao ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ NEXT_PUBLIC_KAKAO_APP_KEY๋กœ ํ†ต์ผ
guen9310 Apr 23, 2026
17cb1bf
feat: ๊ฐ€์ž… ์‹œ OAuth ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ ์ œ๊ฑฐ ๋ฐ ์„œ๋น„์Šค ๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ์ ์šฉ
guen9310 Apr 23, 2026
d36d00c
feat: OAuth ์ด๋ฉ”์ผ ์ค‘๋ณต ์‹œ ๋ช…์‹œ์  ๊ณ„์ • ์—ฐ๊ฒฐ ํ™•์ธ ํ”Œ๋กœ์šฐ ์ถ”๊ฐ€
guen9310 Apr 23, 2026
aa2fa4c
fix: draft ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ ํƒ€์ž… ์ขํž˜ && CI ์Šคํฌ๋ฆฝํŠธ ์ˆ˜์ •
guen9310 Apr 23, 2026
b6ee4b9
refactor: zod-prisma-types โ†’ prisma-zod-generator ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
guen9310 Apr 24, 2026
5b49bdd
Refactor: ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„  ๋ฐ ๋นŒ๋“œ ์ด์Šˆ ์ˆ˜์ •(#129)
guen9310 Apr 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 8 additions & 11 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:
- name: Enable Corepack
run: corepack enable

- name: Use Node.js 20.9.0
- name: Use Node.js 22
uses: actions/setup-node@v4
with:
node-version: "20.9.0"
node-version: "22"
cache: "yarn"
cache-dependency-path: "yarn.lock"

Expand All @@ -32,11 +32,6 @@ jobs:
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-nextjs-
- name: Install system dependencies for Sharp
run: |
sudo apt-get update
sudo apt-get install -y libvips-dev

- name: Install dependencies
run: yarn install --immutable

Expand All @@ -50,14 +45,16 @@ jobs:
run: yarn build
env:
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
INTERNAL_API_SECRET: ${{ secrets.INTERNAL_API_SECRET }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
KAKAO_CLIENT_ID: ${{ secrets.KAKAO_CLIENT_ID }}
NEXT_PUBLIC_KAKAO_APP_KEY: ${{ secrets.NEXT_PUBLIC_KAKAO_APP_KEY }}
KAKAO_CLIENT_SECRET: ${{ secrets.KAKAO_CLIENT_SECRET }}
KAKAO_REST_KEY: ${{ secrets.KAKAO_REST_KEY }}
NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }}
DATABASE_URL: ${{secrets.DATABASE_URL}}
DIRECT_URL: ${{secrets.DIRECT_URL}}
NAVER_CLIENT_SECRET: ${{ secrets.NAVER_CLIENT_SECRET }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
DIRECT_URL: ${{ secrets.DIRECT_URL }}
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
295 changes: 126 additions & 169 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,95 @@
# Perscent ๐ŸŒธ
# ChickChick ๐ŸŒธ

**Perscent**๋Š” ํ–ฅ์ˆ˜ ์• ํ˜ธ๊ฐ€๋“ค์„ ์œ„ํ•œ ์ข…ํ•ฉ ์ปค๋ฎค๋‹ˆํ‹ฐ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. ํ–ฅ์ˆ˜ ๋ฆฌ๋ทฐ, ์ถ”์ฒœ, ์ปฌ๋ ‰์…˜ ๊ด€๋ฆฌ ๋ฐ ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
ํ–ฅ์ˆ˜ ์ •๋ณด ์กฐํšŒ, ๋ฆฌ๋ทฐ, ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜.
Next.js 15 App Router์™€ Hono๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•œ ํ’€์Šคํƒ ํ”„๋กœ์ ํŠธ.

## โœจ ์ฃผ์š” ๊ธฐ๋Šฅ
---

### ๐Ÿ” ํ–ฅ์ˆ˜ ํƒ์ƒ‰ ๋ฐ ๊ฒ€์ƒ‰
## ๐Ÿ–ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ

<table>
<tr>
<td><img src="public/images/docs/image.png" alt="๋ฏธ๋ฆฌ๋ณด๊ธฐ 1" style="width:600px;height:500px;object-fit:cover;" /></td>
<td><img src="public/images/docs/image2.png" alt="๋ฏธ๋ฆฌ๋ณด๊ธฐ 2" style="width:600px;height:500px;object-fit:cover;" /></td>
</tr>
</table>

---

## โœจ ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ

### ์‚ฌ์šฉ์ž

- Google, Kakao ์†Œ์…œ ๋กœ๊ทธ์ธ (NextAuth.js v5)
- ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ๋ฐ ํ™œ๋™ ๋‚ด์—ญ

### ํ–ฅ์ˆ˜

- ๋ธŒ๋žœ๋“œ๋ณ„ ํ–ฅ์ˆ˜ ๋ชฉ๋ก ์กฐํšŒ
- ํ–ฅ์ˆ˜ ์ƒ์„ธ ์ •๋ณด (๋…ธํŠธ, ์–ด์ฝ”๋“œ, ์ง€์†์„ฑ, ์‹ค๋ผ์ฃผ ๋“ฑ)
- ๊ณ ๊ธ‰ ํ•„ํ„ฐ๋ง ๋ฐ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ
- ํ–ฅ์ˆ˜ ๊ฒ€์ƒ‰ ๋ฐ ๋‹ค์ค‘ ํ•„ํ„ฐ๋ง

### ๐Ÿ“ ๋ฆฌ๋ทฐ ์‹œ์Šคํ…œ
### ๋ฆฌ๋ทฐ

- ํ–ฅ์ˆ˜ ์‚ฌ์šฉ ํ›„๊ธฐ ์ž‘์„ฑ
- 5์  ํ‰์  ์‹œ์Šคํ…œ
- ์ง€์†์„ฑ, ์‹ค๋ผ์ฃผ, ๊ณ„์ ˆ๊ฐ, ๊ฐ€๊ฒฉ ๋Œ€๋น„ ๋งŒ์กฑ๋„ ํ‰๊ฐ€
- ์„ฑ๋ณ„ ํ”„๋กœํ•„ ๋ฐ ์‚ฌ์šฉ ์‹œ๊ฐ„๋Œ€ ์ •๋ณด
- 5์  ํ‰์  ๋ฐ ์„ธ๋ถ€ ํ‰๊ฐ€ (์ง€์†์„ฑ, ์‹ค๋ผ์ฃผ, ๊ณ„์ ˆ๊ฐ, ๊ฐ€๊ฒฉ ๋Œ€๋น„ ๋งŒ์กฑ๋„)
- ์„ฑ๋ณ„ ๋ฐ ์‚ฌ์šฉ ์‹œ๊ฐ„๋Œ€ ์ •๋ณด ํฌํ•จ
- Tiptap ๊ธฐ๋ฐ˜ ์—๋””ํ„ฐ

### ๐Ÿ‘ฅ ์ปค๋ฎค๋‹ˆํ‹ฐ
### ์ปค๋ฎค๋‹ˆํ‹ฐ

- ์ž์œ ๊ฒŒ์‹œํŒ, ์งˆ๋ฌธ, ์ถ”์ฒœ ๊ฒŒ์‹œํŒ
- ํ–ฅ์ˆ˜ ๊ด€๋ จ ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ ๋ฐ ๋Œ“๊ธ€
- ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š” ๋ฐ ๋ถ๋งˆํฌ ๊ธฐ๋Šฅ
- ๊ฒŒ์‹œํŒ (์ž์œ /์งˆ๋ฌธ/์ถ”์ฒœ)
- ๋Œ“๊ธ€ ๋ฐ ์ข‹์•„์š”
- ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ (Supabase Storage)

### ๐Ÿ“š ๊ฐœ์ธ ์ปฌ๋ ‰์…˜
### ๊ฐœ์ธ ํ™œ๋™

- ๋ณด์œ  ํ–ฅ์ˆ˜ ์ปฌ๋ ‰์…˜ ๊ด€๋ฆฌ
- ๋ถ๋งˆํฌ ๊ธฐ๋Šฅ
- ์‚ฌ์šฉ์ž ํ™œ๋™ ๊ธฐ๋ก
- ํ–ฅ์ˆ˜/๊ฒŒ์‹œ๊ธ€ ๋ถ๋งˆํฌ
- ๊ฐœ์ธ ํ–ฅ์ˆ˜ ์ปฌ๋ ‰์…˜ ๊ด€๋ฆฌ
- ์ž„์‹œ์ €์žฅ

### ๐Ÿ” ์†Œ์…œ ๋กœ๊ทธ์ธ

- Google, Naver, Kakao ๋กœ๊ทธ์ธ ์ง€์›
- NextAuth.js ๊ธฐ๋ฐ˜ ์ธ์ฆ ์‹œ์Šคํ…œ
---

## ๐Ÿ›  ๊ธฐ์ˆ  ์Šคํƒ

### Frontend
**Frontend**
Next.js 15 (App Router) ยท TypeScript ยท Tailwind CSS ยท Zustand ยท TanStack Query ยท React Hook Form ยท Zod ยท Tiptap

**Backend**
Hono ยท Prisma ยท PostgreSQL ยท NextAuth.js v5 ยท Supabase Storage

- **Framework**: Next.js 15 (App Router)
- **Language**: TypeScript
- **Styling**: Tailwind CSS
- **UI Components**: Custom components with Tailwind
- **State Management**: Zustand
- **Forms**: React Hook Form + Zod validation
- **Rich Text Editor**: CKEditor 5
**Infra**
Vercel ยท Yarn

### Backend
---

- **API Framework**: Hono
- **Database**: PostgreSQL
- **ORM**: Prisma
- **Authentication**: NextAuth.js
- **File Storage**: Supabase
## ๐ŸŽฏ ์ฃผ์š” ๊ธฐ์ˆ ์  ์„ ํƒ

### Development Tools
### Hono on Next.js

- **Package Manager**: Yarn
- **Linting**: ESLint
- **Component Development**: Storybook
- **API Documentation**: OpenAPI/Swagger
Next.js API Routes ๋Œ€์‹  Hono๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. App Router์˜ ํด๋” ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ๋ฐฉ์‹์€
์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋Š˜์–ด๋‚ ์ˆ˜๋ก ํŒŒ์ผ๊ณผ ํด๋”๊ฐ€ ๋ถ„์‚ฐ๋˜์–ด ์ „์ฒด API ๊ตฌ์กฐ๋ฅผ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š”
ํ•œ๊ณ„๋ฅผ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค. ์บ์น˜์˜ฌ ์„ธ๊ทธ๋จผํŠธ(`/api/[[...route]]`)๋ฅผ ํ™œ์šฉํ•ด Hono๋กœ ๋ชจ๋“  ์š”์ฒญ์„
์ค‘์•™์—์„œ ๋ผ์šฐํŒ…ํ•˜๋„๋ก ๊ตฌ์„ฑํ•˜์—ฌ API ๊ตฌ์กฐ์˜ ์‘์ง‘๋„๋ฅผ ๋†’์˜€์Šต๋‹ˆ๋‹ค.

## ๐Ÿ“ฆ ์„ค์น˜ ๋ฐ ์‹คํ–‰
### ์ƒํƒœ ๊ด€๋ฆฌ ๋ถ„๋ฆฌ

### ํ•„์ˆ˜ ์š”๊ตฌ์‚ฌํ•ญ
- **Zustand**: ์„ธ์…˜ ์ •๋ณด, UI ์ƒํƒœ(๋ชจ๋‹ฌ, ํ† ๊ธ€ ๋“ฑ)
- **TanStack Query**: ์„œ๋ฒ„ ์ƒํƒœ (API ์‘๋‹ต ์บ์‹ฑ ๋ฐ ์žฌ๊ฒ€์ฆ)

- Node.js 18+
์„œ๋ฒ„ ์ƒํƒœ์™€ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ์˜ ์ฑ…์ž„์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

---

## ๐Ÿ“ฆ ๋กœ์ปฌ ์‹คํ–‰

### ์š”๊ตฌ์‚ฌํ•ญ

- Node.js 22+
- PostgreSQL
- Yarn

### ์„ค์น˜

### 1. ์ €์žฅ์†Œ ํด๋ก 

```bash
Expand All @@ -87,13 +108,10 @@ yarn install
`.env.local` ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค์Œ ๋ณ€์ˆ˜๋“ค์„ ์„ค์ •ํ•˜์„ธ์š”:

```env
# Database
DATABASE_URL="postgresql://..."
DIRECT_URL="postgresql://..."

# NextAuth.js
NEXTAUTH_SECRET="your-secret"
NEXTAUTH_URL="http://localhost:3000"
# Supabase
NEXT_PUBLIC_SUPABASE_URL="your-supabase-url"
NEXT_PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key"
SUPABASE_SERVICE_ROLE_KEY="your-supabase-service-role-key"

# OAuth Providers
GOOGLE_CLIENT_ID="your-google-client-id"
Expand All @@ -102,129 +120,68 @@ NAVER_CLIENT_ID="your-naver-client-id"
NAVER_CLIENT_SECRET="your-naver-client-secret"
KAKAO_CLIENT_ID="your-kakao-client-id"
KAKAO_CLIENT_SECRET="your-kakao-client-secret"

# Supabase
NEXT_PUBLIC_SUPABASE_URL="your-supabase-url"
NEXT_PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key"
```

### 4. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •

```bash
# Prisma ์Šคํ‚ค๋งˆ ์ƒ์„ฑ
npx prisma generate

# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
npx prisma migrate deploy
```

### 5. ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰

```bash
yarn dev
NEXT_PUBLIC_KAKAO_JAVASCRIPT_KEY="your-kakao-javascript-key"
KAKAO_REST_KEY="your-kakao-rest-key"

# Auth (NextAuth.js v5)
AUTH_SECRET="your-auth-secret"
AUTH_TRUST_HOST=true
AUTH_URL="http://localhost:3000"

# Internal
# NextAuth๊ฐ€ OAuth ๋กœ๊ทธ์ธ ์‹œ ์œ ์ € ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•ด ๋‚ด๋ถ€ API(/api/v1/auth/sync)๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ
# ์™ธ๋ถ€ ์š”์ฒญ๊ณผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด x-internal-secret ํ—ค๋”์— ์‹ค์–ด ๋ณด๋‚ด๋Š” ์‹œํฌ๋ฆฟ ํ‚ค์ž…๋‹ˆ๋‹ค.
INTERNAL_API_SECRET="your-internal-api-secret"
```

์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜๋ฉด [http://localhost:3000](http://localhost:3000)์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

## ๐Ÿ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์Šคํฌ๋ฆฝํŠธ

```bash
# ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰
yarn dev

# ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ
yarn build

# ํ”„๋กœ๋•์…˜ ์„œ๋ฒ„ ์‹คํ–‰
yarn start
---

# Storybook ์‹คํ–‰
yarn storybook

# Prisma Zod ์Šคํ‚ค๋งˆ ์ƒ์„ฑ
yarn zod
```

## ๐Ÿ— ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ
## ๐Ÿ—‚ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

```
src/
โ”œโ”€โ”€ app/ # Next.js App Router
โ”‚ โ”œโ”€โ”€ (main)/ # ๋ฉ”์ธ ํŽ˜์ด์ง€ ๊ทธ๋ฃน
โ”‚ โ”œโ”€โ”€ api/ # API ๋ผ์šฐํŠธ
โ”‚ โ”œโ”€โ”€ brand/ # ๋ธŒ๋žœ๋“œ ํŽ˜์ด์ง€
โ”‚ โ”œโ”€โ”€ community/ # ์ปค๋ฎค๋‹ˆํ‹ฐ ํŽ˜์ด์ง€
โ”‚ โ”œโ”€โ”€ perfumes/ # ํ–ฅ์ˆ˜ ์ƒ์„ธ ํŽ˜์ด์ง€
โ”‚ โ””โ”€โ”€ user/ # ์‚ฌ์šฉ์ž ํŽ˜์ด์ง€
โ”œโ”€โ”€ components/ # ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ
โ”‚ โ”œโ”€โ”€ commons/ # ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ
โ”‚ โ”œโ”€โ”€ domains/ # ๋„๋ฉ”์ธ๋ณ„ ์ปดํฌ๋„ŒํŠธ
โ”‚ โ””โ”€โ”€ modal/ # ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ
โ”œโ”€โ”€ lib/ # ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฐ ์„ค์ •
โ”‚ โ”œโ”€โ”€ hono/ # Hono API ์„ค์ •
โ”‚ โ”œโ”€โ”€ prisma/ # Prisma ํด๋ผ์ด์–ธํŠธ
โ”‚ โ”œโ”€โ”€ queries/ # ๋ฐ์ดํ„ฐ ์กฐํšŒ ํ•จ์ˆ˜
โ”‚ โ”œโ”€โ”€ stores/ # Zustand ์Šคํ† ์–ด
โ”‚ โ””โ”€โ”€ utils/ # ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜
โ””โ”€โ”€ stories/ # Storybook ์Šคํ† ๋ฆฌ
โ”œโ”€โ”€ app/ # Next.js App Router
โ”‚ โ”œโ”€โ”€ (main)/ # ๋ฉ”์ธ ํŽ˜์ด์ง€
โ”‚ โ”œโ”€โ”€ api/[[...route]]/ # Hono ์บ์น˜์˜ฌ API ๋ผ์šฐํŠธ
โ”‚ โ”œโ”€โ”€ api/auth/ # NextAuth.js ์ธ์ฆ ๋ผ์šฐํŠธ
โ”‚ โ”œโ”€โ”€ api-doc/ # OpenAPI ๋ฌธ์„œ ํŽ˜์ด์ง€
โ”‚ โ”œโ”€โ”€ brand/[name]/ # ๋ธŒ๋žœ๋“œ ์ƒ์„ธ ํŽ˜์ด์ง€
โ”‚ โ”œโ”€โ”€ community/ # ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ชฉ๋ก
โ”‚ โ”œโ”€โ”€ community/post/ # ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑยท์ƒ์„ธยท์ˆ˜์ •
โ”‚ โ”œโ”€โ”€ perfumes/ # ํ–ฅ์ˆ˜ ๋ชฉ๋ก ๋ฐ ์ƒ์„ธ
โ”‚ โ””โ”€โ”€ user/[id]/ # ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ยทํ™œ๋™ยท๋ถ๋งˆํฌยท์ปฌ๋ ‰์…˜
โ”œโ”€โ”€ components/ # UI ์ปดํฌ๋„ŒํŠธ
โ”‚ โ”œโ”€โ”€ commons/ # ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ (๋ฒ„ํŠผ, ์นด๋“œ, ํƒญ ๋“ฑ)
โ”‚ โ”œโ”€โ”€ domains/ # ๋„๋ฉ”์ธ๋ณ„ ์ปดํฌ๋„ŒํŠธ
โ”‚ โ”‚ โ”œโ”€โ”€ brandDetail/
โ”‚ โ”‚ โ”œโ”€โ”€ community/
โ”‚ โ”‚ โ”œโ”€โ”€ main/
โ”‚ โ”‚ โ”œโ”€โ”€ perfumeDetail/
โ”‚ โ”‚ โ”œโ”€โ”€ perfumes/
โ”‚ โ”‚ โ”œโ”€โ”€ post/
โ”‚ โ”‚ โ”œโ”€โ”€ postDetail/
โ”‚ โ”‚ โ””โ”€โ”€ user/
โ”‚ โ””โ”€โ”€ modal/ # ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ
โ”œโ”€โ”€ server/ # ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์ฝ”๋“œ
โ”‚ โ”œโ”€โ”€ hono/ # Hono API ๋ ˆ์ด์–ด
โ”‚ โ”‚ โ”œโ”€โ”€ middleware/ # ์ธ์ฆ ๋ฏธ๋“ค์›จ์–ด
โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ # DB ์ ‘๊ทผ ๋ ˆ์ด์–ด
โ”‚ โ”‚ โ”œโ”€โ”€ routes/v1/ # ํ•ธ๋“ค๋Ÿฌ (๋„๋ฉ”์ธ๋ณ„ ๋ผ์šฐํŠธ)
โ”‚ โ”‚ โ”œโ”€โ”€ schemas/ # Zod ์š”์ฒญ/์‘๋‹ต ์Šคํ‚ค๋งˆ
โ”‚ โ”‚ โ”œโ”€โ”€ services/ # ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
โ”‚ โ”‚ โ””โ”€โ”€ utils/ # ์„œ๋ฒ„ ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ”‚ โ”œโ”€โ”€ database/ # ์„œ๋ฒ„ ์•ก์…˜ ๋ฐ ์„ธ์…˜ ์กฐํšŒ
โ”‚ โ”œโ”€โ”€ prisma/ # Prisma ํด๋ผ์ด์–ธํŠธ
โ”‚ โ””โ”€โ”€ supabase/ # Supabase ํด๋ผ์ด์–ธํŠธ
โ”œโ”€โ”€ client/ # ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์ฝ”๋“œ
โ”‚ โ”œโ”€โ”€ hooks/query/ # TanStack Query ํ›…
โ”‚ โ”œโ”€โ”€ stores/ # Zustand ์Šคํ† ์–ด
โ”‚ โ”œโ”€โ”€ tiptap/ # Tiptap ์—๋””ํ„ฐ ์„ค์ •
โ”‚ โ””โ”€โ”€ utils/ # ํด๋ผ์ด์–ธํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ””โ”€โ”€ shared/ # ์„œ๋ฒ„ยทํด๋ผ์ด์–ธํŠธ ๊ณต์šฉ
โ”œโ”€โ”€ constants/ # ๊ณตํ†ต ์ƒ์ˆ˜
โ”œโ”€โ”€ types/ # ๊ณตํ†ต ํƒ€์ž… ์ •์˜
โ””โ”€โ”€ utils/ # ๊ณตํ†ต ์œ ํ‹ธ๋ฆฌํ‹ฐ
```

## ๐ŸŽจ ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ

ํ”„๋กœ์ ํŠธ๋Š” ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ์„ ๊ฐ–์ถ”๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

- **Commons**: ๋ฒ„ํŠผ, ์ž…๋ ฅ, ์นด๋“œ ๋“ฑ ๊ธฐ๋ณธ UI ์ปดํฌ๋„ŒํŠธ
- **Domains**: ํŠน์ • ๋„๋ฉ”์ธ(ํ–ฅ์ˆ˜, ์ปค๋ฎค๋‹ˆํ‹ฐ, ์‚ฌ์šฉ์ž)์— ํŠนํ™”๋œ ์ปดํฌ๋„ŒํŠธ
- **Modal**: ๋กœ๊ทธ์ธ, ๋ฆฌ๋ทฐ ๋“ฑ ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ

๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” Storybook์œผ๋กœ ๋ฌธ์„œํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, `yarn storybook`์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

## ๐Ÿ“Š ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ

์ฃผ์š” ์—”ํ‹ฐํ‹ฐ:

- **User**: ์‚ฌ์šฉ์ž ์ •๋ณด
- **Perfume**: ํ–ฅ์ˆ˜ ์ •๋ณด
- **Brand**: ๋ธŒ๋žœ๋“œ ์ •๋ณด
- **Review**: ํ–ฅ์ˆ˜ ๋ฆฌ๋ทฐ
- **Post**: ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ฒŒ์‹œ๊ธ€
- **Comment**: ๋Œ“๊ธ€
- **Bookmark**: ๋ถ๋งˆํฌ (ํ–ฅ์ˆ˜/๊ฒŒ์‹œ๊ธ€)
- **Collection**: ์‚ฌ์šฉ์ž ํ–ฅ์ˆ˜ ์ปฌ๋ ‰์…˜

์ž์„ธํ•œ ์Šคํ‚ค๋งˆ๋Š” `prisma/schema.prisma` ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

## ๐Ÿ”ง API ๋ฌธ์„œ

๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ API ๋ฌธ์„œ๋Š” ๋‹ค์Œ URL์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

- OpenAPI ์ŠคํŽ™: `http://localhost:3000/api/doc`

API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

- `/api/v1/perfumes` - ํ–ฅ์ˆ˜ ๊ด€๋ จ API
- `/api/v1/community` - ์ปค๋ฎค๋‹ˆํ‹ฐ API
- `/api/v1/reviews` - ๋ฆฌ๋ทฐ API
- `/api/v1/users` - ์‚ฌ์šฉ์ž API
- `/api/v1/search` - ๊ฒ€์ƒ‰ API

## ๐Ÿ“ธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ

![๋ฉ”์ธ ํŽ˜์ด์ง€](./public/images/Preview.png)

## ๐Ÿ’ก ๊ธฐ์ˆ ์  ๋„์ „ ํฌ์ธํŠธ

โ€ข App Router ์ „ํ™˜: ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… ๋ฐ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์„ฑ๋Šฅ ๊ฐœ์„ .
โ€ข ์ƒํƒœ ๊ด€๋ฆฌ ์ „๋žต: Zustand + React Query ๊ธฐ๋ฐ˜ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ.
โ€ข ์ปดํฌ๋„ŒํŠธ ๋ฌธ์„œํ™”: Storybook์œผ๋กœ UI ํ…Œ์ŠคํŠธ ๋ฐ ๊ด€๋ฆฌ.

## ๐Ÿ“† ๊ฐœ๋ฐœ ๋กœ๋“œ๋งต

โ€ข ํ–ฅ์ˆ˜ ์ถ”์ฒœ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ถ”๊ฐ€
โ€ข ๋‹ค๊ตญ์–ด ์ง€์› (i18n)
โ€ข ๋ชจ๋ฐ”์ผ ์ตœ์ ํ™” ๊ฐœ์„ 

## ๐Ÿค ํŒ€ ํ˜‘์—… & ์ฝ”๋“œ ์ปจ๋ฒค์…˜

โ€ข GitHub Actions + PR ๋ฆฌ๋ทฐ ํ”„๋กœ์„ธ์Šค.
โ€ข ESLint + Prettier๋ฅผ ํ†ตํ•œ ์ผ๊ด€์„ฑ ์œ ์ง€.
---
Loading
Loading