바이브 코딩: 프로젝트 시작 화면과 라우팅
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function PlayPage() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen items-center bg-white px-6 py-12 text-slate-950">
|
||||||
|
<section className="mx-auto flex w-full max-w-3xl flex-col gap-8">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<p className="text-sm font-semibold text-emerald-700">
|
||||||
|
괄호 음수 분배
|
||||||
|
</p>
|
||||||
|
<h1 className="text-4xl font-bold tracking-normal sm:text-5xl">
|
||||||
|
문제 풀이
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-2xl text-lg leading-8 text-slate-700">
|
||||||
|
다음 단계에서 괄호 앞 음수 분배 문제가 이 화면에 표시됩니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="inline-flex h-11 items-center justify-center rounded-md border border-slate-300 px-5 text-base font-semibold text-slate-800 transition hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2"
|
||||||
|
>
|
||||||
|
처음으로
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: #ffffff;
|
||||||
|
--foreground: #171717;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background: #0a0a0a;
|
||||||
|
--foreground: #ededed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--foreground);
|
||||||
|
background: var(--background);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.text-balance {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import localFont from "next/font/local";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
const geistSans = localFont({
|
||||||
|
src: "./fonts/GeistVF.woff",
|
||||||
|
variable: "--font-geist-sans",
|
||||||
|
weight: "100 900",
|
||||||
|
});
|
||||||
|
const geistMono = localFont({
|
||||||
|
src: "./fonts/GeistMonoVF.woff",
|
||||||
|
variable: "--font-geist-mono",
|
||||||
|
weight: "100 900",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "괄호 음수 분배 연습",
|
||||||
|
description: "중학교 2학년 수학 실수 유형 반복 연습",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="ko">
|
||||||
|
<body
|
||||||
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen items-center bg-slate-50 px-6 py-12 text-slate-950">
|
||||||
|
<section className="mx-auto flex w-full max-w-3xl flex-col gap-8">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<p className="text-sm font-semibold text-emerald-700">
|
||||||
|
중학교 2학년 수학 실수 유형 연습
|
||||||
|
</p>
|
||||||
|
<h1 className="text-4xl font-bold tracking-normal sm:text-5xl">
|
||||||
|
괄호 음수 분배 연습
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-2xl text-lg leading-8 text-slate-700">
|
||||||
|
괄호 앞의 음수를 분배할 때 부호가 어떻게 바뀌는지 차근차근
|
||||||
|
연습합니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
href="/play"
|
||||||
|
className="inline-flex h-12 items-center justify-center rounded-md bg-emerald-700 px-6 text-base font-semibold text-white transition hover:bg-emerald-800 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-2"
|
||||||
|
>
|
||||||
|
연습 시작
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-l-4 border-emerald-700 pl-4 text-sm leading-6 text-slate-600">
|
||||||
|
<p>이번 단계에서는 화면 이동만 확인합니다.</p>
|
||||||
|
<p>문제 생성과 채점은 다음 이터레이션에서 추가합니다.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# 01. 프로젝트 시작 화면과 라우팅
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
Next.js 14 프로젝트를 초기화하고, 시작 화면에서 문제 풀이 화면(`/play`)으로 이동할 수 있는 가장 작은 앱 흐름을 만든다.
|
||||||
|
|
||||||
|
## 이번 단계에서 할 것
|
||||||
|
|
||||||
|
- Next.js 14 App Router 기반 프로젝트를 초기화한다.
|
||||||
|
- TypeScript, Tailwind CSS, ESLint를 함께 설정한다.
|
||||||
|
- 루트 시작 화면(`/`)을 만든다.
|
||||||
|
- 문제 풀이 화면(`/play`)을 만든다.
|
||||||
|
- 시작 화면의 버튼 또는 링크를 통해 `/play`로 이동할 수 있게 한다.
|
||||||
|
- `pnpm dev`로 앱 실행을 확인한다.
|
||||||
|
|
||||||
|
## 이번 단계에서 하지 않을 것
|
||||||
|
|
||||||
|
- 문제 생성 엔진 구현
|
||||||
|
- KaTeX 수식 렌더링
|
||||||
|
- 답 입력 및 채점 UI
|
||||||
|
- Zustand 상태 관리
|
||||||
|
- Framer Motion 애니메이션
|
||||||
|
- DB, Prisma, Redis, NextAuth 설정
|
||||||
|
- 유형 선택 화면
|
||||||
|
- 모드 선택 화면
|
||||||
|
- 테스트 환경 구성
|
||||||
|
|
||||||
|
## 수정 또는 생성 예상 파일
|
||||||
|
|
||||||
|
프로젝트 초기화 후 다음 파일들이 핵심 확인 대상이 된다.
|
||||||
|
|
||||||
|
- `app/layout.tsx`
|
||||||
|
- `app/page.tsx`
|
||||||
|
- `app/(game)/play/page.tsx`
|
||||||
|
- `app/globals.css`
|
||||||
|
- `package.json`
|
||||||
|
- `tailwind.config.ts`
|
||||||
|
- `tsconfig.json`
|
||||||
|
|
||||||
|
## 작업 순서
|
||||||
|
|
||||||
|
1. `pnpm create next-app@14 . --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*"`로 프로젝트를 초기화한다.
|
||||||
|
2. 생성된 기본 파일 구조를 확인한다.
|
||||||
|
3. `app/page.tsx`를 시작 화면으로 정리한다.
|
||||||
|
4. `app/(game)/play/page.tsx`를 추가해 빈 풀이 화면을 만든다.
|
||||||
|
5. 시작 화면에서 `/play`로 이동하는 링크를 추가한다.
|
||||||
|
6. `pnpm dev`를 실행해 브라우저에서 화면 이동을 확인한다.
|
||||||
|
|
||||||
|
## 화면 흐름
|
||||||
|
|
||||||
|
```text
|
||||||
|
/ 시작 화면
|
||||||
|
- 괄호 음수 분배 연습 시작
|
||||||
|
- 시작 버튼 클릭
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
/play 풀이 화면
|
||||||
|
- 아직 실제 문제는 표시하지 않음
|
||||||
|
- 다음 단계에서 문제 생성 엔진과 연결 예정
|
||||||
|
```
|
||||||
|
|
||||||
|
## 시작 화면 초안
|
||||||
|
|
||||||
|
시작 화면은 기능 소개보다 바로 연습을 시작할 수 있는 간단한 화면으로 만든다.
|
||||||
|
|
||||||
|
- 제목: 괄호 음수 분배 연습
|
||||||
|
- 보조 문구: 괄호 앞의 음수를 분배할 때 부호가 어떻게 바뀌는지 연습한다.
|
||||||
|
- 주요 액션: 연습 시작
|
||||||
|
|
||||||
|
## 풀이 화면 초안
|
||||||
|
|
||||||
|
`/play` 화면은 아직 실제 문제 풀이 기능을 넣지 않는다.
|
||||||
|
|
||||||
|
- 제목: 문제 풀이
|
||||||
|
- 보조 문구: 다음 단계에서 괄호 음수 분배 문제가 표시된다.
|
||||||
|
- 돌아가기 링크: 처음으로
|
||||||
|
|
||||||
|
## 완료 기준
|
||||||
|
|
||||||
|
- `pnpm dev`가 정상 실행된다.
|
||||||
|
- 브라우저에서 `/` 페이지가 열린다.
|
||||||
|
- 시작 화면에서 “연습 시작”을 누르면 `/play`로 이동한다.
|
||||||
|
- `/play`에서 “처음으로”를 누르면 `/`로 돌아온다.
|
||||||
|
- 콘솔에 명백한 런타임 오류가 없다.
|
||||||
|
|
||||||
|
## 확인 명령
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
필요하면 초기화 직후 다음 명령으로 정적 검사를 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm lint
|
||||||
|
```
|
||||||
|
|
||||||
|
## 작업 결과
|
||||||
|
|
||||||
|
- Next.js 14 App Router 프로젝트를 초기화했다.
|
||||||
|
- TypeScript, Tailwind CSS, ESLint 설정을 추가했다.
|
||||||
|
- `/` 시작 화면을 만들었다.
|
||||||
|
- `/play` 풀이 화면을 만들었다.
|
||||||
|
- 시작 화면의 “연습 시작” 링크로 `/play` 이동을 확인했다.
|
||||||
|
- `/play` 화면의 “처음으로” 링크로 `/` 복귀를 확인했다.
|
||||||
|
- `pnpm lint` 통과를 확인했다.
|
||||||
|
- `pnpm dev` 실행 후 브라우저에서 화면 이동을 확인했다.
|
||||||
|
|
||||||
|
## 다음 단계
|
||||||
|
|
||||||
|
`02-parenthesis-sign-engine.md`에서 괄호 앞 음수 분배 문제 생성 엔진을 만든다.
|
||||||
|
|
||||||
|
다음 단계에서는 UI를 확장하지 않고, `lib/engine/` 아래에 순수 TypeScript 함수와 단위 테스트를 먼저 추가한다.
|
||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
## 진행 단계
|
## 진행 단계
|
||||||
|
|
||||||
- [ ] 01. 프로젝트 시작 화면과 라우팅
|
- [x] [01. 프로젝트 시작 화면과 라우팅](./01-project-start.md)
|
||||||
- [ ] 02. 괄호 앞 음수 분배 문제 생성 엔진
|
- [ ] 02. 괄호 앞 음수 분배 문제 생성 엔진
|
||||||
- [ ] 03. KaTeX 수식 렌더링
|
- [ ] 03. KaTeX 수식 렌더링
|
||||||
- [ ] 04. 한 문제 풀이 UI
|
- [ ] 04. 한 문제 풀이 UI
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "math-quiz",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18",
|
||||||
|
"next": "14.2.35"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5",
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"postcss": "^8",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-next": "14.2.35"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+3738
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('postcss-load-config').Config} */
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
content: [
|
||||||
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
background: "var(--background)",
|
||||||
|
foreground: "var(--foreground)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user