# Next.js App Rules (Supabase + ShadCN + TailwindCSS)
## Next.js: Route Handler 우선 사용
- 모든 API 엔드포인트는 Route Handler를 사용하여 구현하세요.
- 데이터베이스 작업, 외부 API 호출, 인증 등 복잡한 서버 작업은 반드시 Route Handler를 사용하세요.
- Server Action은 단순 폼 제출 또는 간단한 데이터 처리에만 사용하세요.
---
## Next.js 라우팅: App Router 사용
- 프로젝트 내 라우팅은 Pages Router 대신 App Router를 사용하세요.
---
## 가급적 서버 컴포넌트 사용
- 프로젝트 내 가급적 서버 컴포넌트 사용하세요.
- 필요에 서버 컴포넌트에서 할 수 없는 경우 컴포넌트를 분리 하여 사용하세요
## 코드 긴거 방지
- 한 페이지 내 코드가 길어짐을 방지하기 위하여, 가급적 컴포넌트를 분리하여 유지 보수 및 재활용 가치를 높이도록 사용하세요.
## TypeScript 사용: TS 사용 권장
- 프로젝트 전반에 TypeScript를 사용하세요.
- 타입 안정성을 위해 모든 컴포넌트와 서버 로직에 TypeScript를 적용하세요.
---
## TypeScript 인터페이스 정의 규칙: 'I' 접두사 사용
- 인터페이스 정의 시 이름 앞에 'I'를 접두사로 추가하세요.
- 예시:
```typescript
export interface IComment {
id: string
text: string
author: string
}
export interface IUser {
id: string
email: string
name: string
created_at: string
updated_at: string
}
```
- 인터페이스 생성은 types/index.ts 파일에 작성하세요.
---
## 컴포넌트 생성: ShadCN 우선 사용
- 모든 UI 컴포넌트는 ShadCN을 우선으로 생성하세요.
- ShadCN 컴포넌트 생성 CLI 명령어는 `npx shadcn@latest add`입니다.
- Toast 관련 컴포넌트는 다음 위치에 있습니다:
```
components/ui/toast.tsx # Toast 기본 컴포넌트
components/ui/toaster.tsx # Toast 컨테이너 컴포넌트
hooks/use-toast.ts # Toast 커스텀 훅
```
---
## 스타일링: TailwindCSS 사용
- 모든 스타일링은 TailwindCSS를 사용하세요.
- ShadCN과 TailwindCSS의 호환성을 위해 기본 설정을 유지하세요.
- CSS 모듈이나 styled-components는 사용하지 않습니다.
- 커스텀 컴포넌트 생성 시 TailwindCSS 클래스를 사용하세요.
---
## 데이터베이스: Supabase 사용
- 모든 데이터베이스 작업은 Supabase를 사용하세요.
- Supabase Client 설정:
```typescript
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
export const supabase = createClient(supabaseUrl, supabaseKey)
```
- 서버 컴포넌트에서 Supabase 사용:
```typescript
// utils/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export const createClient = async () => {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options)
})
} catch (error) {
// SSR에서는 쿠키 설정이 제한될 수 있습니다
}
},
}
}
)
}
```
---
## 인증: Supabase Auth 사용
- 모든 인증은 Supabase Auth를 사용하세요.
- 미들웨어 설정:
```typescript
// middleware.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
export async function middleware(request: NextRequest) {
let supabaseResponse = NextResponse.next({
request,
})
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
request.cookies.set(name, value)
supabaseResponse.cookies.set(name, value, options)
})
},
}
}
)
// 사용자 인증 확인
const { data: { user } } = await supabase.auth.getUser()
// 보호된 라우트 처리
if (!user && request.nextUrl.pathname.startsWith('/protected')) {
return NextResponse.redirect(new URL('/auth/signin', request.url))
}
return supabaseResponse
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
}
```
---
## 환경 변수 설정
- 필수 환경 변수 (.env.local):
```env
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
```
---
## 프로젝트 구조
```
├── app/
│ ├── (auth)/
│ │ ├── signin/
│ │ └── signup/
│ ├── api/
│ │ └── auth/
│ └── protected/
├── components/
│ ├── ui/ # ShadCN 컴포넌트
│ └── custom/ # 커스텀 컴포넌트
├── hooks/
├── lib/
│ └── supabase.ts # Supabase 클라이언트 설정
├── types/
│ └── index.ts # TypeScript 인터페이스
└── utils/
└── supabase/
├── client.ts
└── server.ts
```
---
## 데이터 페칭 패턴
- 클라이언트 컴포넌트에서 데이터 페칭:
```typescript
'use client'
import { useEffect, useState } from 'react'
import { supabase } from '@/lib/supabase'
export default function ClientComponent() {
const [data, setData] = useState(null)
useEffect(() => {
const fetchData = async () => {
const { data, error } = await supabase
.from('table_name')
.select('*')
if (error) {
console.error('Error:', error)
} else {
setData(data)
}
}
fetchData()
}, [])
return <div>{/* 컴포넌트 렌더링 */}</div>
}
```
- 서버 컴포넌트에서 데이터 페칭:
```typescript
import { createClient } from '@/utils/supabase/server'
export default async function ServerComponent() {
const supabase = await createClient()
const { data, error } = await supabase
.from('table_name')
.select('*')
if (error) {
console.error('Error:', error)
return <div>Error loading data</div>
}
return <div>{/* 데이터 렌더링 */}</div>
}
```
---
## 에러 핸들링
- 모든 Supabase 쿼리에서 에러 핸들링을 구현하세요.
- Toast 컴포넌트를 사용하여 사용자에게 에러 메시지를 표시하세요.
- 로딩 상태를 적절히 관리하세요.
---
## 보안 규칙
- Supabase Row Level Security (RLS)를 활성화하세요.
- 민감한 데이터는 서버 사이드에서만 접근하세요.
- 환경 변수를 통해 API 키를 안전하게 관리하세요.
## 서버 활성화 금지
- dev / build 등 기본적으로 사용자가 직접 활성화 합니다.
- 시스템 내에서 해당 명령어를 사용하지 마세요
댓글 1개
게시글 목록
| 번호 | 제목 |
|---|---|
| 31 | |
| 30 | |
| 29 | |
| 28 | |
| 27 | |
| 26 | |
| 25 | |
| 24 | |
| 22 | |
| 20 | |
| 19 | |
| 18 | |
| 17 | |
| 16 | |
| 14 | |
| 13 | |
| 11 | |
| 10 | |
| 9 | |
| 7 | |
| 6 | |
| 5 | |
| 4 | |
| 3 | |
| 1 |
댓글 작성
댓글을 작성하시려면 로그인이 필요합니다.
로그인하기