1๋ถ ๋ง์ ์์๋ณด๋ ๋์ ํ์ ์คํ์ผ โ ์ฌ๋ฏธ์๋ ๊ณต์ ํ ์ฑํฅ ํ ์คํธ
TEAMITAKA ํ์ ํ ์คํธ๋ MBTI์ ๊ฐ๋ฒผ์ด ํ์ ์ฑํฅ ํ ์คํธ๋ก, 15๊ฐ ์ง๋ฌธ์ ํตํด ์ฌ์ฉ์์ ํ์ํฌ ์คํ์ผ์ ๋ถ์ํ๊ณ ๋ฐ์ด๋ด ๊ฐ๋ฅํ ๊ฒฐ๊ณผ ์นด๋๋ฅผ ์์ฑํฉ๋๋ค.
- โก 1๋ถ ์๋ฃ: 15๋ฌธํญ์ผ๋ก ๋น ๋ฅธ ํ ์คํธ
- ๐จ ๊ฒฐ๊ณผ ์นด๋: ๋ผ์ดํธ/๋คํฌ ํ ๋ง ์ง์ํ๋ ๊ณต์ ๊ฐ๋ฅํ ์นด๋
- ๐ฑ ๋ชจ๋ฐ์ผ ์ต์ ํ: ๋ฐ์ํ ๋์์ธ๊ณผ ํฐ์น ์นํ์ UX
- โฟ ์ ๊ทผ์ฑ ์ง์: ํค๋ณด๋ ๋ค๋น๊ฒ์ด์ , ์คํฌ๋ฆฐ ๋ฆฌ๋ ์ง์
- ๐ ๊ฐ์ธ์ ๋ณด ์์ : ๊ฐ์ธ์ ๋ณด ์์ง ์๋ ๋ก์ปฌ ์ฒ๋ฆฌ
- ์๋ฃ์จ: ํด์ฆ ์์ โ ๊ฒฐ๊ณผ ํ์ด์ง ๋๋ฌ
- ์นด๋ ์ ์ฅ๋ฅ : ๊ฒฐ๊ณผ ์นด๋ ์ด๋ฏธ์ง ๋ค์ด๋ก๋
- ๊ณต์ ํด๋ฆญ๋ฅ : ์นด๋ ๊ณต์ /๋งํฌ ๋ณต์ฌ
- Framework: Next.js 15 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS
- Testing: Vitest (Unit), Playwright (E2E)
- Image Capture: html2canvas
- Deployment: Vercel (๊ถ์ฅ)
teamitaka-type/
โโโ app/ # Next.js App Router
โ โโโ layout.tsx # ๊ธ๋ก๋ฒ ๋ ์ด์์
โ โโโ page.tsx # ์ธํธ๋ก ํ์ด์ง (/)
โ โโโ quiz/
โ โ โโโ page.tsx # ํด์ฆ ์งํ (/quiz)
โ โโโ result/[type]/
โ โโโ page.tsx # ๊ฒฐ๊ณผ ํ์ด์ง (/result/[type])
โโโ components/ # React ์ปดํฌ๋ํธ
โ โโโ ProgressBar.tsx
โ โโโ QuestionCard.tsx
โ โโโ ChoiceButton.tsx
โ โโโ ResultCard.tsx # ์บก์ณ์ฉ ๊ฒฐ๊ณผ ์นด๋
โ โโโ ShareBar.tsx # ๊ณต์ /์ ์ฅ ์ก์
๋ฐ
โโโ lib/ # ๋น์ฆ๋์ค ๋ก์ง
โ โโโ questions.ts # 15๋ฌธํญ ๋ฐ์ดํฐ
โ โโโ scoring.ts # MBTI ์ค์ฝ์ด๋ง ๋ก์ง
โ โโโ types.ts # 16๊ฐ ํ์
๋ฉํ๋ฐ์ดํฐ
โ โโโ theme.ts # ๋์์ธ ํ ํฐ
โ โโโ og.ts # OG ๋ฉํ๋ฐ์ดํฐ
โโโ public/
โ โโโ og/ # OG ์ด๋ฏธ์ง
โโโ e2e/ # E2E ํ
์คํธ
git clone https://github.com/your-org/teamitaka-type.git
cd teamitaka-type
# ํจํค์ง ์ค์น
npm install
# ๋๋ yarn
yarn installnpm run dev๋ธ๋ผ์ฐ์ ์์ http://localhost:3000์ผ๋ก ์ ์
# ๋จ์ ํ
์คํธ
npm run test
# ๋จ์ ํ
์คํธ (watch ๋ชจ๋)
npm run test:watch
# E2E ํ
์คํธ
npm run test:e2e
# ํ์
์ฒดํฌ
npm run type-check# ํ๋ก๋์
๋น๋
npm run build
# ๋น๋ ๊ฒฐ๊ณผ ์คํ
npm run start-
Vercel ํ๋ก์ ํธ ์ฐ๊ฒฐ
npm install -g vercel vercel login vercel --prod
-
ํ๊ฒฝ ๋ณ์ ์ค์ (ํ์์)
- Vercel ๋์๋ณด๋์์ ์ค์
- Analytics, SEO ๋๊ตฌ ์ฐ๋
-
์ปค์คํ ๋๋ฉ์ธ ์ฐ๊ฒฐ
# type.teamitaka.com CNAME ์ค์ vercel domains add type.teamitaka.com
Netlify
npm run build
# dist ํด๋๋ฅผ Netlify์ ์
๋ก๋AWS S3 + CloudFront
npm run build
aws s3 sync out/ s3://your-bucket --deletelib/questions.ts์์ ์ง๋ฌธ ํ
์คํธ์ ๊ฐ์ค์น๋ฅผ ์์ ํ ์ ์์ต๋๋ค:
export const questions: Question[] = [
{
id: 1,
text: "์๋ก์ด ์ง๋ฌธ ๋ด์ฉ",
choices: ["์", "์๋์ค"]
},
// ...
];lib/types.ts์์ ๊ฐ MBTI ํ์
์ ์ค๋ช
, ๊ฐ์ , ํ์ ์์ :
export const TYPE_METADATA: Record<string, TypeMeta> = {
ENFP: {
nickname: '์ด์ ํฐ๋ฏธ',
oneLiner: '๋์น๋ ์๋์ง๋ก ํ์ ์ด๋๋ ์์ด๋์ด ๋ฐ์ ์',
description: '์ฐฝ์์ ์ธ ํด๊ฒฐ์ฑ
์ ์ ์ํ๊ณ ...',
strengths: ['์ฐฝ์์ ์์ด๋์ด ๋ฐ๊ตด', 'ํ ๋๊ธฐ๋ถ์ฌ', '์ ์ฐํ ์ฌ๊ณ '],
bestMatches: ['์ฒด๊ณํฐ๋ฏธ (INTJ)', '์คํํฐ๋ฏธ (ESTJ)'],
tips: ['๊ตฌ์ฒด์ ์ธ ์คํ ๊ณํ ์๋ฆฝํ๊ธฐ', '...']
}
};lib/theme.ts์ tailwind.config.js์์ ์์, ํฐํธ, ๊ฐ๊ฒฉ ๋ฑ์ ์กฐ์ :
export const theme = {
colors: {
primary: '#F76241', // TEAMITAKA ์ค๋ ์ง
// ...
}
};lib/scoring.ts์ SCORING_WEIGHTS์์ ๊ฐ ์ง๋ฌธ์ ์ํฅ๋ ์กฐ์ :
export const SCORING_WEIGHTS = {
EI: {
1: { axis: 'E', weight: 1.0 }, // ๊ฐ์ค์น ์กฐ์ ๊ฐ๋ฅ
}
};// app/layout.tsx์ ์ถ๊ฐ
<script defer data-domain="type.teamitaka.com" src="https://plausible.io/js/script.js"></script>Test Started: ํ ์คํธ ์์Quiz Completed: ํด์ฆ ์๋ฃResult Generated: ๊ฒฐ๊ณผ ์์ฑ (ํ์ ๋ณ)Result Viewed: ๊ฒฐ๊ณผ ํ์ด์ง ์กฐํCard Saved: ์นด๋ ์ด๋ฏธ์ง ์ ์ฅCard Shared: ์นด๋ ๊ณต์Retest Started: ์ฌํ ์คํธ
// gtag ์ค์ ๋ฐ ์ปค์คํ
์ด๋ฒคํธ ์ ์ก
gtag('event', 'quiz_completed', {
custom_parameter: 'value'
});- ํค๋ณด๋ ๋ค๋น๊ฒ์ด์ : ๋ชจ๋ ์ธํฐ๋์ ์์ Tab/Enter ์ง์
- ํฐ์น ๋์: ์ต์ 44pxร44px ํฐ์น ์์ญ
- ์์ ๋๋น: WCAG AA ๊ธฐ์ค 4.5:1 ์ด์
- ํฌ์ปค์ค ํ์: ๋ช ํํ ํฌ์ปค์ค ๋ง ํ์
- ์คํฌ๋ฆฐ ๋ฆฌ๋: semantic HTML๊ณผ ARIA ๋ ์ด๋ธ
- ๋์ฒด ํ ์คํธ: ์๋ฏธ์๋ ์ด๋ฏธ์ง alt ์์ฑ
- ํ ์คํธ ํฌ๊ธฐ: ๋ธ๋ผ์ฐ์ ํ๋/์ถ์ ์ง์
- ๊ณ ๋๋น ๋ชจ๋ ์ง์
- ๋ชจ์ ๊ฐ์ ์ต์ (prefers-reduced-motion)
- ๋ค๊ตญ์ด ์ง์ (์์ด, ์ผ๋ณธ์ด)
- ์์ฑ ์๋ด ๊ธฐ๋ฅ
- Mobile: 360px - 430px (๊ธฐ๋ณธ ์ต์ ํ)
- Tablet: 431px - 768px (์ค์ ์ ๋ ฌ ์นด๋)
- Desktop: 769px+ (์ต๋ ๋๋น ์ ํ)
- ํด์ฆ ์นด๋: ๋ชจ๋ฐ์ผ์์ ์ ์ฒด ๋๋น, ๋ฐ์คํฌํฑ์์ ๊ณ ์ ๋๋น
- ๊ฒฐ๊ณผ ์นด๋: ํ๋ฉด ํฌ๊ธฐ์ ๋ฐ๋ฅธ ํจ๋ฉ๊ณผ ํฐํธ ํฌ๊ธฐ ์กฐ์
- ๊ณต์ ๋ฐ: ๋ชจ๋ฐ์ผ์์ ํ๋จ ๊ณ ์ , ๋ฐ์คํฌํฑ์์ ์ธ๋ผ์ธ
- ํฐ์น ์ ์ค์ฒ: ์ค์์ดํ์ ํญ ์ต์ ํ
- LCP (Largest Contentful Paint): < 2.5s
- FID (First Input Delay): < 100ms
- CLS (Cumulative Layout Shift): < 0.1
- ํฐํธ ์ต์ ํ:
font-display: swap์ผ๋ก FOUT ๋ฐฉ์ง - ์ด๋ฏธ์ง ์ต์ ํ: Next.js Image ์ปดํฌ๋ํธ ์ฌ์ฉ
- ์ฝ๋ ๋ถํ : ํ์ด์ง๋ณ ์๋ ์ฝ๋ ๋ถํ
- ์บ์ฑ: ์ ์ assets CDN ์บ์ฑ
- ์์ถ: Gzip/Brotli ์์ถ
- Initial Bundle: < 150KB (gzipped)
- Total Bundle: < 500KB (gzipped)
- ์ด๋ฏธ์ง ์บก์ณ: html2canvas ๋์ ๋ก๋ฉ
- ๋ฐ์ดํฐ ์์ง ์์: ์ด๋ฆ, ์ด๋ฉ์ผ ๋ฑ ๊ฐ์ธ์ ๋ณด ๋ฏธ์์ง
- ๋ก์ปฌ ์ฒ๋ฆฌ: ๋ชจ๋ ๋ต๋ณ๊ณผ ๊ฒฐ๊ณผ๋ ๋ธ๋ผ์ฐ์ ์์๋ง ์ฒ๋ฆฌ
- ์ธ์ ๊ด๋ฆฌ: ์๋ก๊ณ ์นจ ์ ๋ฐ์ดํฐ ์ด๊ธฐํ
- ์ฟ ํค ์ฌ์ฉ ์ํจ: ๋ถ์ ๋๊ตฌ ์ ์ธ ์ฟ ํค ๋ฏธ์ฌ์ฉ
// next.config.js
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
}
]1. ์ด๋ฏธ์ง ์บก์ณ๊ฐ ์๋๋ ๊ฒฝ์ฐ
# html2canvas ๊ด๋ จ ์ด์
npm install html2canvas@latest
# ํฐํธ ๋ก๋ฉ ์๋ฃ ํ ์บก์ณ๋๋๋ก ์์ ๋จ
await document.fonts.ready;2. ๋ชจ๋ฐ์ผ์์ ๊ณต์ ๋ฒํผ์ด ์๋๋ ๊ฒฝ์ฐ
// Web Share API ์ง์ ์ฌ๋ถ ํ์ธ ํ ํด๋ฐฑ
if (navigator.share && navigator.canShare) {
await navigator.share(shareData);
} else {
// ํด๋ฆฝ๋ณด๋ ๋ณต์ฌ ํด๋ฐฑ
await navigator.clipboard.writeText(url);
}3. ๊ฒฐ๊ณผ ํ์ด์ง ์ง์ ์ ๊ทผ ์ 404
// [type] ๋ค์ด๋๋ฏน ๋ผ์ฐํ
ํ์ธ
// ์ ํจํ MBTI ํ์
(16๊ฐ์ง)๋ง ํ์ฉ
if (!TYPE_METADATA[typeCode?.toUpperCase()]) {
notFound();
}# Next.js ๋ฒ๋ค ๋ถ์
npm run build
npx @next/bundle-analyzer
# ๋ผ์ดํธํ์ฐ์ค ์ฑ๋ฅ ์ธก์
npm run build && npm run start
# Chrome DevTools > Lighthouse ์คํ
# ์ ๊ทผ์ฑ ํ
์คํธ
npm install -g @axe-core/cli
axe http://localhost:3000- ์ด์ ์์ฑ: ์๋ก์ด ๊ธฐ๋ฅ์ด๋ ๋ฒ๊ทธ ๋ฆฌํฌํธ
- ๋ธ๋์น ์์ฑ:
feature/feature-name๋๋fix/bug-name - ๊ฐ๋ฐ ์งํ: ์ฝ๋ ์์ฑ ๋ฐ ํ ์คํธ
- ํ
์คํธ ์คํ:
npm run test && npm run test:e2e - PR ์์ฑ: ๋ช ํํ ์ค๋ช ๊ณผ ํจ๊ป Pull Request
- ์ฝ๋ ๋ฆฌ๋ทฐ: ํ ๋ฆฌ๋ทฐ ํ ๋จธ์ง
feat: ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
fix: ๋ฒ๊ทธ ์์
docs: ๋ฌธ์ ์์
style: ์ฝ๋ ์คํ์ผ ๋ณ๊ฒฝ
refactor: ์ฝ๋ ๋ฆฌํฉํ ๋ง
test: ํ
์คํธ ์ถ๊ฐ/์์
chore: ๋น๋ ํ๋ก์ธ์ค ๋๋ ๋ณด์กฐ ๋๊ตฌ ๋ณ๊ฒฝ- ESLint: Next.js ๊ถ์ฅ ์ค์ ์ฌ์ฉ
- Prettier: ์ฝ๋ ํฌ๋งทํ ์๋ํ
- TypeScript: ์๊ฒฉํ ํ์ ์ฒดํฌ ํ์ฑํ
- ์ ๊ทผ์ฑ: WCAG 2.1 AA ๊ธฐ์ค ์ค์
MIT License - ์์ธํ ๋ด์ฉ์ LICENSE ํ์ผ์ ์ฐธ์กฐํ์ธ์.
- ํ๋ก์ ํธ ๋ฌธ์: contact@teamitaka.com
- ๋ฒ๊ทธ ๋ฆฌํฌํธ: GitHub Issues
- ๊ธฐ๋ฅ ์์ฒญ: GitHub Discussions
