κ°μμ§ μ€μ’ /λ°κ²¬ κ²μλ¬Ό κ΄λ¦¬ μμ€ν μ κ΄λ¦¬μ λμ보λμ λλ€. Next.js 15μ React 19λ₯Ό κΈ°λ°μΌλ‘ ꡬμΆλμμ΅λλ€.
- νλ‘μ νΈ κ°μ
- μ£Όμ κΈ°λ₯
- κΈ°μ μ€ν
- νλ‘μ νΈ κ΅¬μ‘°
- μμνκΈ°
- νκ²½ μ€μ
- μ£Όμ νμ΄μ§
- API ꡬ쑰
- μ£Όμ μ»΄ν¬λνΈ
κ°μμ§ν΄μ΄ κ΄λ¦¬μ λμ보λλ μ€μ’ /λ°κ²¬ κ²μλ¬Ό νλ«νΌμ κ΄λ¦¬μμ© μΉ μ ν리μΌμ΄μ μ λλ€. κ΄λ¦¬μλ μ΄ λμ보λλ₯Ό ν΅ν΄ μ¬μ©μ, κ²μλ¬Ό, μ κ³ λ΄μμ κ΄λ¦¬ν μ μμ΅λλ€.
- π JWT κΈ°λ° μΈμ¦ μμ€ν
- π μ€μκ° λ°μ΄ν° μ‘°ν λ° κ΄λ¦¬
- π¨ λ°μν λμμΈ (λͺ¨λ°μΌ/λ°μ€ν¬ν± μ§μ)
- π κ²μ λ° νν°λ§ κΈ°λ₯
- π νμ΄μ§λ€μ΄μ μ§μ
- ποΈ κ²μλ¬Ό λ° μ¬μ©μ μμ κΈ°λ₯
β οΈ μ κ³ λ΄μ κ΄λ¦¬ λ° μ²λ¦¬
- μ¬μ©μ λͺ©λ‘ μ‘°ν: μ 체 μ¬μ©μ λͺ©λ‘μ νμ΄μ§λ€μ΄μ κ³Ό ν¨κ» μ‘°ν
- κ²μ κΈ°λ₯: μ¬μ©μλͺ λλ μ΄λ©μΌλ‘ κ²μ
- μν κ΄λ¦¬: κ³μ νμ±ν/λΉνμ±ν μ²λ¦¬
- κ³μ μμ : μ¬μ©μ κ³μ μμ κΈ°λ₯
- μμΈ μ 보: μ¬μ©μ νλ μμΈ μ 보 λͺ¨λ¬
- κ²μλ¬Ό λͺ©λ‘ μ‘°ν: μ 체/λ°κ²¬νμ΄μ/μμ΄λ²λ Έμ΄μ κ²μλ¬Ό νν°λ§
- AI μ΄λ―Έμ§ νν°: AI μμ± μ΄λ―Έμ§ κ²μλ¬Όλ§ μ‘°ν
- κ²μλ¬Ό μμ : λΆμ μ ν κ²μλ¬Ό μμ κΈ°λ₯
- μμΈ μ 보: κ²μλ¬Ό μμΈ μ 보 λͺ¨λ¬ (κ°μμ§ μ 보, μμΉ μ 보 ν¬ν¨)
- μ§λ μ°λ: Google Mapsλ₯Ό ν΅ν μμΉ μ 보 νμ
- μ κ³ λͺ©λ‘ μ‘°ν: μ 체 μ κ³ λ΄μ μ‘°ν
- μ κ³ μμΈλ³΄κΈ°: μ κ³ μ¬μ λ° μμΈ μ 보 νμΈ
- κ²μλ¬Ό μ°λ: μ κ³ λ κ²μλ¬Ό λ°λ‘ νμΈ
- μ²λ¦¬ κΈ°λ₯:
- μ κ³ λ¬΄μ μ²λ¦¬
- μ κ³ λ κ²μλ¬Ό μμ
- λ‘κ·ΈμΈ: μ΄λ©μΌ/λΉλ°λ²νΈ κΈ°λ° λ‘κ·ΈμΈ
- ν ν° κ΄λ¦¬: JWT ν ν° κΈ°λ° μΈμ¦
- μλ 리λ€μ΄λ νΈ: λ―ΈμΈμ¦ μ λ‘κ·ΈμΈ νμ΄μ§λ‘ μλ μ΄λ
- μΈμ κ΄λ¦¬: λ‘컬 μ€ν 리μ§λ₯Ό ν΅ν μΈμ μ μ§
- Framework: Next.js 15.5.6 (App Router)
- UI Library: React 19.1.0
- Language: TypeScript 5
- Styling: Tailwind CSS 3.4.0
- HTTP Client: Axios 1.12.2
- UI Components:
- @headlessui/react 2.2.9
- @heroicons/react 2.2.0
- Maps: @react-google-maps/api 2.20.7
- Linting: ESLint 9
- Build Tool: Turbopack (Next.js λ΄μ₯)
- Package Manager: npm/yarn
fe-admin/
βββ src/
β βββ app/ # Next.js App Router νμ΄μ§
β β βββ admin/ # κ΄λ¦¬μ νμ΄μ§
β β β βββ members/ # μ¬μ©μ κ΄λ¦¬
β β β βββ posts/ # κ²μλ¬Ό κ΄λ¦¬
β β β βββ reports/ # μ κ³ λ΄μ κ΄λ¦¬
β β β βββ layout.tsx # κ΄λ¦¬μ λ μ΄μμ
β β β βββ page.tsx # κ΄λ¦¬μ λ©μΈ (리λ€μ΄λ νΈ)
β β βββ api/ # API λΌμ°νΈ (νλ‘μ)
β β β βββ admin/ # κ΄λ¦¬μ API
β β β βββ auth/ # μΈμ¦ API
β β βββ login/ # λ‘κ·ΈμΈ νμ΄μ§
β β βββ layout.tsx # λ£¨νΈ λ μ΄μμ
β β βββ page.tsx # ν νμ΄μ§
β βββ components/ # μ¬μ¬μ© κ°λ₯ν μ»΄ν¬λνΈ
β β βββ badge/ # μν λ°°μ§ μ»΄ν¬λνΈ
β β βββ filters/ # νν° μ»΄ν¬λνΈ
β β βββ layout/ # λ μ΄μμ μ»΄ν¬λνΈ
β β βββ tables/ # ν
μ΄λΈ μ»΄ν¬λνΈ
β β βββ tabs/ # ν μ»΄ν¬λνΈ
β β βββ ui/ # UI μ»΄ν¬λνΈ
β β βββ AiToggle.tsx # AI νν° ν κΈ
β β βββ FilterButtons.tsx # κ²μλ¬Ό νμ
νν°
β β βββ MembersDetailModal.tsx
β β βββ PostDetailModal.tsx
β β βββ PostsTable.tsx
β β βββ ReportDetailModal.tsx
β β βββ Sidebar.tsx
β βββ lib/ # μ νΈλ¦¬ν° λ° μ€μ
β βββ api-client.ts # API ν΄λΌμ΄μΈνΈ
β βββ url-utils.ts # URL μ νΈλ¦¬ν°
β βββ mock/ # λͺ©μ
λ°μ΄ν°
βββ public/ # μ μ νμΌ
βββ next.config.js # Next.js μ€μ
βββ tailwind.config.js # Tailwind CSS μ€μ
βββ package.json # νλ‘μ νΈ μμ‘΄μ±
- Node.js 18 μ΄μ
- npm λλ yarn
# μμ‘΄μ± μ€μΉ
npm install
# λλ
yarn install# κ°λ° μλ² μμ (Turbopack μ¬μ©)
npm run dev
# λλ
yarn devλΈλΌμ°μ μμ http://localhost:3000μ μ΄μ΄ νμΈνμΈμ.
# νλ‘λμ
λΉλ
npm run build
# λλ
yarn build
# νλ‘λμ
μλ² μμ
npm start
# λλ
yarn startνλ‘μ νΈλ μΈλΆ λ°±μλ μλ²(http://54.180.54.51:8080)μ ν΅μ ν©λλ€.
next.config.jsμμ API νλ‘μ μ€μ μ νμΈν μ μμ΅λλ€:
async rewrites() {
return [
{
source: "/api/:path*",
destination: "http://54.180.54.51:8080/api/:path*",
},
];
}κ°λ° νκ²½μμ λͺ©μ λ°μ΄ν°λ₯Ό μ¬μ©νλ €λ©΄ νκ²½ λ³μλ₯Ό μ€μ νμΈμ:
# .env.local
NEXT_PUBLIC_USE_MOCK=trueS3 μ΄λ―Έμ§λ₯Ό νμνκΈ° μν΄ next.config.jsμ μ΄λ―Έμ§ λλ©μΈμ΄ μ€μ λμ΄ μμ΅λλ€:
images: {
remotePatterns: [
{
protocol: "https",
hostname: "gangajikimi-server.s3.ap-northeast-2.amazonaws.com",
pathname: "/**",
},
],
}- μ΄λ©μΌ/λΉλ°λ²νΈ μ λ ₯
- JWT ν ν° λ°κΈ λ° μ μ₯
- λ‘κ·ΈμΈ μ±κ³΅ μ
/admin/membersλ‘ λ¦¬λ€μ΄λ νΈ
- μ¬μ©μ λͺ©λ‘ ν μ΄λΈ
- κ²μ κΈ°λ₯ (μ¬μ©μλͺ /μ΄λ©μΌ)
- νμ΄μ§λ€μ΄μ (κΈ°λ³Έ 20κ°/νμ΄μ§)
- κ³μ μν λ³κ²½ (νμ±ν/λΉνμ±ν)
- κ³μ μμ
- μ¬μ©μ μμΈ μ 보 λͺ¨λ¬
- κ²μλ¬Ό λͺ©λ‘ ν μ΄λΈ
- νν°: μ 체/λ°κ²¬νμ΄μ/μμ΄λ²λ Έμ΄μ
- AI μ΄λ―Έμ§ νν° ν κΈ
- κ²μλ¬Ό μμ
- κ²μλ¬Ό μμΈ μ 보 λͺ¨λ¬:
- κ²μλ¬Ό μ 보 ν
- κ°μμ§ μ 보 ν
- μμΉ μ 보 ν (Google Maps)
- μ κ³ λͺ©λ‘ ν μ΄λΈ
- μ κ³ μ¬μ , μ κ³ μ, μ κ³ μΌ νμ
- μ κ³ μν λ°°μ§
- μ κ³ μμΈλ³΄κΈ° λͺ¨λ¬
- μ κ³ λ κ²μλ¬Ό λ°λ‘κ°κΈ°
- κ΄λ¦¬μ μμ
:
- μ κ³ λ¬΄μ μ²λ¦¬
- μ κ³ λ κ²μλ¬Ό μμ
POST /api/auth/login- λ‘κ·ΈμΈ
GET /api/admin/members- μ¬μ©μ λͺ©λ‘ μ‘°νGET /api/admin/members/[id]- μ¬μ©μ μμΈ μ‘°νPATCH /api/admin/members/[id]/status- μ¬μ©μ μν λ³κ²½DELETE /api/admin/members/[id]- μ¬μ©μ μμ
GET /api/admin/posts- κ²μλ¬Ό λͺ©λ‘ μ‘°νGET /api/admin/posts/[type]/[id]- κ²μλ¬Ό μμΈ μ‘°νDELETE /api/admin/posts/[type]/[id]/delete- κ²μλ¬Ό μμ
GET /api/admin/reports- μ κ³ λͺ©λ‘ μ‘°νGET /api/admin/reports/[type]/[reportId]- μ κ³ μμΈ μ‘°νPATCH /api/admin/reports/[type]/[reportId]/ignore- μ κ³ λ¬΄μ μ²λ¦¬DELETE /api/admin/reports/[type]/[reportId]/delete- μ κ³ λ κ²μλ¬Ό μμ
src/lib/api-client.tsμμ API ν΄λΌμ΄μΈνΈλ₯Ό κ΄λ¦¬ν©λλ€:
apiClient: μ€μ μλ²μ ν΅μ (κ²μλ¬Ό, μ κ³ κ΄λ¦¬)mockApiClient: λͺ©μ λ°μ΄ν° μ¬μ© (μ¬μ©μ κ΄λ¦¬)
- Sidebar: μ¬μ΄λλ° λ€λΉκ²μ΄μ λ©λ΄
- AdminHeader: μλ¨ ν€λ (μ¬μ΄λλ° ν κΈ, μ¬μ©μ μ 보, λ‘κ·Έμμ)
- AdminTable: λ²μ© κ΄λ¦¬μ ν μ΄λΈ
- PostsTable: κ²μλ¬Ό μ μ© ν μ΄λΈ
- TablePagination: νμ΄μ§λ€μ΄μ μ»΄ν¬λνΈ
- MembersDetailModal: μ¬μ©μ μμΈ μ 보 λͺ¨λ¬
- PostDetailModal: κ²μλ¬Ό μμΈ μ 보 λͺ¨λ¬ (ν ꡬ쑰)
- ReportDetailModal: μ κ³ μμΈ μ 보 λͺ¨λ¬
- FilterButtons: κ²μλ¬Ό νμ νν° (μ 체/λ°κ²¬/μ€μ’ )
- SearchFilter: κ²μ μ λ ₯ νν°
- AiToggle: AI μ΄λ―Έμ§ νν° ν κΈ
- ActivityBadge: μ¬μ©μ νλ μν λ°°μ§
- StatusBadge: μΌλ° μν λ°°μ§
- ReportStatusBadge: μ κ³ μν λ°°μ§
- JWT ν ν° κΈ°λ° μΈμ¦
- λ‘컬 μ€ν 리μ§μ ν ν° μ μ₯
- API μμ² μ Authorization ν€λμ ν ν° ν¬ν¨
- ν ν° λ§λ£ μ μλ λ‘κ·Έμμ λ° λ‘κ·ΈμΈ νμ΄μ§ 리λ€μ΄λ νΈ
- κ΄λ¦¬μ νμ΄μ§ μ κ·Ό μ μΈμ¦ νμΈ
- Tailwind CSSλ₯Ό μ¬μ©ν μ νΈλ¦¬ν° κΈ°λ° μ€νμΌλ§
- λ°μν λμμΈ (λͺ¨λ°μΌ/νλΈλ¦Ώ/λ°μ€ν¬ν±)
- λ€ν¬ λͺ¨λλ λ―Έμ§μ (ν₯ν μΆκ° κ°λ₯)
κ°λ° μ€ λ°±μλ μλ²κ° μμ λ λͺ©μ λ°μ΄ν°λ₯Ό μ¬μ©ν μ μμ΅λλ€:
.env.localνμΌ μμ±NEXT_PUBLIC_USE_MOCK=trueμ€μ - κ°λ° μλ² μ¬μμ
- λΈλΌμ°μ μ½μμμ API μμ²/μλ΅ λ‘κ·Έ νμΈ
- λ€νΈμν¬ νμμ μ€μ HTTP μμ² νμΈ
- Next.js μλ² μ½μμμ μλ² μ¬μ΄λ λ‘κ·Έ νμΈ
μ΄ νλ‘μ νΈλ λΉκ³΅κ° νλ‘μ νΈμ λλ€.
νλ‘μ νΈ κ΄λ ¨ λ¬Έμμ¬νμ΄ μμΌμλ©΄ κ°λ°νμ μ°λ½ν΄μ£ΌμΈμ.