| name | faq-guide |
| description | 초보자 FAQ - "이건 어디서 설정해?" 모든 질문 해결 |
| triggers | faq, 어디서, 설정, 질문, where, 찾기 |
초보자 FAQ 가이드
"이건 어디서 설정해?" 모든 질문에 대한 답변
빠른 찾기 목차
| 질문 | 파일 위치 |
|---|---|
| API 키 설정 | .env.local |
| 색상 변경 | tailwind.config.ts |
| 폰트 변경 | src/app/layout.tsx |
| 로고 변경 | public/logo.png |
| 페이지 제목 변경 | src/app/layout.tsx |
| 결제 설정 | .env.local + src/lib/stripe.ts |
| 데이터베이스 연결 | .env.local + prisma/schema.prisma |
| 로그인 설정 | src/lib/auth.ts |
| 메뉴 추가 | src/components/Navigation.tsx |
| 새 페이지 추가 | src/app/[페이지명]/page.tsx |
1. 환경변수 / API 키
API 키 설정
질문: "OpenAI API 키는 어디에 넣어?"
위치: .env.local (프로젝트 루트)
# .env.local
# AI 서비스
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx
# 결제
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxx
STRIPE_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx
# 데이터베이스
DATABASE_URL=postgresql://user:password@host:5432/db
# 인증
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-here
# 소셜 로그인
GOOGLE_CLIENT_ID=xxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxxxxxxxxxxxxxx
# 스토리지
CLOUDFLARE_R2_ACCESS_KEY=xxxxxxxxxxxxx
CLOUDFLARE_R2_SECRET_KEY=xxxxxxxxxxxxx
CLOUDFLARE_R2_BUCKET=my-bucket
주의사항:
.env.local은 git에 올리면 안 됨 (.gitignore에 포함되어 있음)NEXT_PUBLIC_접두사가 붙은 것만 브라우저에서 접근 가능- 배포 시 Vercel/Railway 대시보드에서 환경변수 설정 필요
2. 디자인 / 스타일
색상 변경
질문: "메인 색상을 파란색에서 초록색으로 바꾸고 싶어"
위치: tailwind.config.ts
// tailwind.config.ts
import type { Config } from 'tailwindcss';
const config: Config = {
theme: {
extend: {
colors: {
// 여기서 색상 변경!
primary: {
50: '#f0fdf4', // 가장 밝은
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e', // 메인 색상
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d', // 가장 어두운
},
},
},
},
};
export default config;
색상 팔레트 생성 도구:
- https://uicolors.app/create (Tailwind 색상 생성)
- https://coolors.co (색상 조합)
- https://colorhunt.co (트렌디한 색상)
폰트 변경
질문: "글꼴을 바꾸고 싶어"
위치: src/app/layout.tsx
// src/app/layout.tsx
// 방법 1: Google Fonts 사용 (권장)
import { Noto_Sans_KR, Inter } from 'next/font/google';
const notoSansKR = Noto_Sans_KR({
subsets: ['latin'],
weight: ['400', '500', '700'],
variable: '--font-noto',
});
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
<html lang="ko" className={`${notoSansKR.variable} ${inter.variable}`}>
<body className="font-noto">
{children}
</body>
</html>
);
}
// tailwind.config.ts
module.exports = {
theme: {
extend: {
fontFamily: {
noto: ['var(--font-noto)', 'sans-serif'],
inter: ['var(--font-inter)', 'sans-serif'],
},
},
},
};
인기 한글 폰트:
Noto_Sans_KR- 가독성 좋음Pretendard- 모던함Spoqa_Han_Sans_Neo- 깔끔함
로고 변경
질문: "로고를 바꾸고 싶어"
위치:
public/logo.png- 메인 로고public/logo-dark.png- 다크모드 로고public/favicon.ico- 브라우저 탭 아이콘
// src/components/Logo.tsx
import Image from 'next/image';
export function Logo() {
return (
<Image
src="/logo.png" // ← 이 파일 교체
alt="MySaaS Logo"
width={120}
height={40}
priority
/>
);
}
파비콘 생성:
- 512x512 PNG 로고 준비
- https://realfavicongenerator.net 에서 생성
- 생성된 파일들을
public/폴더에 복사
3. 페이지 / 라우팅
페이지 제목 변경
질문: "브라우저 탭에 뜨는 제목을 바꾸고 싶어"
위치: src/app/layout.tsx (전체) 또는 각 페이지의 page.tsx
// src/app/layout.tsx - 전체 사이트 기본값
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: {
default: 'MySaaS - 서비스 설명', // 기본 제목
template: '%s | MySaaS', // 페이지별 제목 템플릿
},
description: '서비스 설명 150자 이내',
};
// src/app/pricing/page.tsx - 개별 페이지
export const metadata: Metadata = {
title: '요금제', // → "요금제 | MySaaS" 로 표시됨
description: '다양한 요금제를 확인하세요',
};
새 페이지 추가
질문: "새 페이지를 만들고 싶어"
위치: src/app/[페이지명]/page.tsx
# 예: /about 페이지 만들기
mkdir src/app/about
touch src/app/about/page.tsx
// src/app/about/page.tsx
export const metadata = {
title: '소개',
description: '회사 소개 페이지',
};
export default function AboutPage() {
return (
<div className="container mx-auto py-8">
<h1 className="text-3xl font-bold">회사 소개</h1>
<p>내용...</p>
</div>
);
}
URL 구조:
src/app/page.tsx → /
src/app/about/page.tsx → /about
src/app/blog/page.tsx → /blog
src/app/blog/[slug]/page.tsx → /blog/hello-world (동적 라우트)
메뉴 추가
질문: "상단 네비게이션에 메뉴를 추가하고 싶어"
위치: src/components/Navigation.tsx 또는 src/components/Header.tsx
// src/components/Navigation.tsx
const menuItems = [
{ href: '/', label: '홈' },
{ href: '/features', label: '기능' },
{ href: '/pricing', label: '요금제' },
{ href: '/about', label: '소개' }, // ← 새 메뉴 추가
{ href: '/contact', label: '문의' }, // ← 새 메뉴 추가
];
export function Navigation() {
return (
<nav>
{menuItems.map((item) => (
<Link key={item.href} href={item.href}>
{item.label}
</Link>
))}
</nav>
);
}
4. 인증 / 로그인
로그인 설정
질문: "구글 로그인을 추가하고 싶어"
위치:
.env.local- API 키src/lib/auth.ts- 설정
Step 1: Google Cloud Console 설정
- https://console.cloud.google.com 접속
- 새 프로젝트 생성
- APIs & Services → Credentials → Create Credentials → OAuth 2.0
- Authorized redirect URIs:
http://localhost:3000/api/auth/callback/google
Step 2: 환경변수 설정
# .env.local
GOOGLE_CLIENT_ID=xxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxxxxxxxxxxx
Step 3: auth.ts 설정
// src/lib/auth.ts
import GoogleProvider from 'next-auth/providers/google';
export const authOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
// 다른 프로바이더 추가...
],
};
로그인 페이지 커스터마이징
질문: "로그인 페이지 디자인을 바꾸고 싶어"
위치: src/app/(auth)/login/page.tsx
// src/app/(auth)/login/page.tsx
'use client';
import { signIn } from 'next-auth/react';
export default function LoginPage() {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="bg-white p-8 rounded-xl shadow-lg max-w-md w-full">
<h1 className="text-2xl font-bold text-center mb-6">로그인</h1>
<button
onClick={() => signIn('google', { callbackUrl: '/dashboard' })}
className="w-full bg-white border py-3 rounded-lg flex items-center justify-center gap-2"
>
<GoogleIcon />
Google로 로그인
</button>
<div className="my-4 text-center text-gray-500">또는</div>
<form>
<input
type="email"
placeholder="이메일"
className="w-full p-3 border rounded-lg mb-3"
/>
<input
type="password"
placeholder="비밀번호"
className="w-full p-3 border rounded-lg mb-4"
/>
<button className="w-full bg-primary-500 text-white py-3 rounded-lg">
이메일로 로그인
</button>
</form>
</div>
</div>
);
}
5. 결제
결제 설정
질문: "Stripe 결제를 연동하고 싶어"
위치:
.env.local- API 키src/lib/stripe.ts- Stripe 설정src/app/api/webhooks/stripe/route.ts- 웹훅
Step 1: Stripe 대시보드 설정
- https://dashboard.stripe.com 가입
- Developers → API keys에서 키 복사
- Developers → Webhooks → Add endpoint
Step 2: 환경변수
# .env.local
STRIPE_SECRET_KEY=sk_test_xxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
Step 3: 가격 설정
// src/config/pricing.ts
export const PLANS = {
free: {
name: '무료',
price: 0,
priceId: null,
features: ['기본 기능', '월 100회 사용'],
},
pro: {
name: '프로',
price: 9900,
priceId: 'price_xxxxxxxxxxxxx', // Stripe에서 생성한 Price ID
features: ['모든 기능', '무제한 사용', '우선 지원'],
},
};
가격 변경
질문: "구독 가격을 바꾸고 싶어"
위치:
- Stripe Dashboard → Products
src/config/pricing.ts
// src/config/pricing.ts
export const PLANS = {
starter: {
name: '스타터',
price: 4900, // ← 가격 변경
monthlyPriceId: 'price_xxx',
yearlyPriceId: 'price_yyy',
yearlyPrice: 49000, // 연간 (2개월 할인)
},
pro: {
name: '프로',
price: 14900, // ← 가격 변경
monthlyPriceId: 'price_xxx',
yearlyPriceId: 'price_yyy',
yearlyPrice: 149000,
},
};
6. 데이터베이스
데이터베이스 연결
질문: "데이터베이스 연결은 어떻게 해?"
위치:
.env.local- 연결 URLprisma/schema.prisma- 스키마 정의
Step 1: 데이터베이스 생성 (예: Supabase)
- https://supabase.com 가입
- 새 프로젝트 생성
- Settings → Database → Connection string 복사
Step 2: 환경변수 설정
# .env.local
DATABASE_URL="postgresql://postgres:password@db.xxxxx.supabase.co:5432/postgres"
Step 3: Prisma 설정
# 마이그레이션 생성 및 적용
npx prisma migrate dev --name init
# DB에서 타입 생성
npx prisma generate
# DB 확인
npx prisma studio
테이블 추가
질문: "새 테이블을 추가하고 싶어"
위치: prisma/schema.prisma
// prisma/schema.prisma
// 새 모델 추가
model Product {
id String @id @default(cuid())
name String
description String?
price Float
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String
user User @relation(fields: [userId], references: [id])
}
// User 모델에 관계 추가
model User {
// ... 기존 필드
products Product[]
}
# 마이그레이션 실행
npx prisma migrate dev --name add_products
# 타입 생성
npx prisma generate
7. 이메일
이메일 발송 설정
질문: "이메일 발송은 어떻게 해?"
위치:
.env.local- API 키src/lib/email.ts- 이메일 설정
Resend 사용 예시:
# .env.local
RESEND_API_KEY=re_xxxxxxxxxxxxx
// src/lib/email.ts
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendWelcomeEmail(email: string, name: string) {
await resend.emails.send({
from: 'MySaaS <noreply@mysaas.com>',
to: email,
subject: '환영합니다!',
html: `<p>안녕하세요 ${name}님, 가입을 환영합니다!</p>`,
});
}
이메일 템플릿 위치: src/emails/ 또는 src/lib/email-templates/
8. 파일 업로드
이미지 업로드 설정
질문: "이미지 업로드는 어떻게 해?"
위치:
.env.local- 스토리지 키src/lib/storage.ts- 업로드 설정
Cloudflare R2 예시:
# .env.local
CLOUDFLARE_R2_ACCESS_KEY=xxxxx
CLOUDFLARE_R2_SECRET_KEY=xxxxx
CLOUDFLARE_R2_BUCKET=my-bucket
CLOUDFLARE_R2_ENDPOINT=https://xxxxx.r2.cloudflarestorage.com
// src/lib/storage.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({
region: 'auto',
endpoint: process.env.CLOUDFLARE_R2_ENDPOINT,
credentials: {
accessKeyId: process.env.CLOUDFLARE_R2_ACCESS_KEY!,
secretAccessKey: process.env.CLOUDFLARE_R2_SECRET_KEY!,
},
});
export async function uploadFile(file: File): Promise<string> {
const key = `uploads/${Date.now()}-${file.name}`;
await s3.send(new PutObjectCommand({
Bucket: process.env.CLOUDFLARE_R2_BUCKET,
Key: key,
Body: Buffer.from(await file.arrayBuffer()),
ContentType: file.type,
}));
return `${process.env.CLOUDFLARE_R2_PUBLIC_URL}/${key}`;
}
9. 자주 하는 실수
환경변수가 안 읽혀요
원인:
.env.local파일명이 틀림 (.env아님!)- 서버를 재시작 안 함
- 클라이언트에서
NEXT_PUBLIC_없이 접근
해결:
# 서버 재시작
npm run dev
페이지가 404 에러
원인:
- 파일명이
page.tsx가 아님 - 폴더 위치가
src/app/안이 아님 - 대소문자 오류
확인:
✅ src/app/about/page.tsx → /about
❌ src/app/about/About.tsx → 404
❌ src/pages/about.tsx → 404 (App Router에서)
데이터베이스 연결 실패
원인:
DATABASE_URL형식 오류- 데이터베이스 서버가 꺼져있음
- IP 화이트리스트 미설정
확인:
# 연결 테스트
npx prisma db push
Stripe 웹훅 실패
원인:
- 웹훅 시크릿 오류
- 로컬에서 테스트 시 ngrok 미사용
- 웹훅 URL 오류
로컬 테스트:
# Stripe CLI 설치 후
stripe listen --forward-to localhost:3000/api/webhooks/stripe
10. 빠른 검색 테이블
| 하고 싶은 것 | 파일 위치 |
|---|---|
| 메인 색상 변경 | tailwind.config.ts |
| 폰트 변경 | src/app/layout.tsx |
| 로고 변경 | public/logo.png |
| 파비콘 변경 | public/favicon.ico |
| 사이트 제목 변경 | src/app/layout.tsx |
| API 키 설정 | .env.local |
| 메뉴 추가 | src/components/Navigation.tsx |
| 새 페이지 추가 | src/app/[이름]/page.tsx |
| 푸터 수정 | src/components/Footer.tsx |
| 소셜 로그인 추가 | src/lib/auth.ts |
| 결제 연동 | src/lib/stripe.ts |
| 이메일 설정 | src/lib/email.ts |
| 데이터베이스 스키마 | prisma/schema.prisma |
| 환경변수 | .env.local |
| 빌드 설정 | next.config.js |
| 라우트 리다이렉트 | next.config.js → redirects() |
| 404 페이지 | src/app/not-found.tsx |
| 에러 페이지 | src/app/error.tsx |
| 로딩 UI | src/app/loading.tsx |
| SEO 메타태그 | 각 page.tsx의 metadata |
| sitemap | src/app/sitemap.ts |
| robots.txt | src/app/robots.ts |