📚
jchsanGi 만들기
AI랑 협업해서 시험 사이트 + 학습 코치 만드는 풀스택 강의
💡
이 강의는 무엇을 가르치나요?
"AI에게 일을 시켜서 진짜 서비스를 만드는 흐름"을 단계별로 보여드립니다. AI가 코드를 짜준다고 끝이 아니라, 어떤 순서로 무엇을 부탁해야 좋은 결과가 나오는지가 핵심입니다. 실제 jchsanGi 프로젝트의 250개 코드 변경을 6개 챕터로 정리했습니다.
"AI에게 일을 시켜서 진짜 서비스를 만드는 흐름"을 단계별로 보여드립니다. AI가 코드를 짜준다고 끝이 아니라, 어떤 순서로 무엇을 부탁해야 좋은 결과가 나오는지가 핵심입니다. 실제 jchsanGi 프로젝트의 250개 코드 변경을 6개 챕터로 정리했습니다.
🎯 강의 목표
- AI에게 "결정 → 설계 → 구현"을 단계별로 요청하는 법을 익힌다
- 6가지 프롬프트 패턴(브래인스토밍 / 스펙 / 플랜 / 구현 / 디버깅 / 리뷰)을 실전에서 적용한다
- 작은 기능부터 AI 에이전트까지 누적적으로 키우는 법을 본다
📦 사전 준비
- Node.js 18+ 와 npm 설치
- Python 3.11+ 설치 (Chapter 4부터 사용)
- Supabase 무료 계정
- OpenAI API 키 (Chapter 4부터)
- AI 도구 (Claude Code / ChatGPT / Cursor 등)
CHAPTER 01프로젝트 시작 — 시험지를 웹사이트로
이번 챕터에서 만들 것
- Next.js 프로젝트 골격
- 회차별 문제 JSON 데이터
- 회차 선택 + 60문항 풀이 페이지
학습 목표
- 큰 결정을 작게 쪼개서 AI에게 묻는 방법
- 데이터 구조를 먼저 정하는 사고 순서
STEP 1.1
Next.js 프로젝트 만들기
터미널에서 다음을 실행합니다.
npx create-next-app@latest jchsanGi
cd jchsanGi
npm run dev
✅
http://localhost:3000에서 빈 페이지가 떠야 다음으로 갑니다.STEP 1.2
데이터 구조를 AI와 함께 정하기
여기가 강의 전체에서 가장 중요한 패턴입니다. 한 번에 하나씩 객관식으로 물어보세요.
정보처리산업기사 기출문제 PDF를
웹사이트에서 풀 수 있게 만들고 싶어.
지금 갖고 있는 것:
- 회차별 PDF 다섯 개
- 막 만든 Next.js 프로젝트
한꺼번에 정하지 말고, 한 번에 하나씩
객관식으로 물어봐줘.
먼저 이 3가지부터:
1. 문제 JSON 구조는?
(회차 / 문항번호 / 지문 / 보기 4개 /
정답 / 해설 / 과목)
2. 폴더링은?
/datasets/problem2022/{회차}.json vs ...
3. 라우팅은?
/exam/[year]/[round] vs /exam?year=...
각각 추천안 + 다른 후보 + 한 줄 장단점.
📝
AI가 "이렇게 만드세요" 한 번에 답하지 않게 막는 게 핵심입니다.
"한 번에 하나씩 물어봐"라는 문장 하나로 답의 품질이 크게 달라집니다.
STEP 1.3
PDF → JSON 데이터 변환
PDF에서 텍스트를 추출해서 정한 구조로 옮깁니다. 도구는 자유롭게 — pdfplumber, AI에게 OCR 부탁, 수동 옮겨적기 모두 가능.
// datasets/problem2022/1회.json (예시)
{
"round": 1, // 회차 번호 (1회, 2회 ...) — URL과 폴더 구분에 사용
"year": 2022, // 시행 연도 — /exam/[year]/[round] 라우팅 키
"problems": [ // 한 회차의 문항 배열 (보통 80문항)
{
"no": 1, // 문항 번호 — 채점/북마크/오답노트 식별자
"subject": "소프트웨어 설계", // 과목명 — 과목별 통계·필터링에 사용
"question": "...", // 문제 지문 (본문 텍스트)
"options": ["...", "...", "...", "..."], // 4지선다 보기 (index 0~3)
"answer": 2, // 정답 번호 (1~4) — 채점 비교용
"explain": "..." // 해설 — 풀이 후 정답 페이지에서 노출
}
]
}
⚠️
PDF 추출은 글자 깨짐이 자주 발생합니다. 한 회분을 만들면 반드시 끝까지 한 번은 사람 눈으로 검수하세요.
STEP 1.4
회차 선택 + 풀이 페이지 만들기
회차를 선택하면 60문항을 한 화면에서
풀 수 있게 만들고 싶어.
조건:
- 키보드 1~4로 답 고르기
- Enter로 다음 문제, Space로 해설 토글
- 좌측 인덱스에 각 문항 상태(O / X / ?) 표시
- 시험 끝나면 점수와 합격 여부 표시
먼저 페이지 라우팅 / 상태 관리 / 키보드 핸들링
세 부분으로 나눠 어떻게 구성할지 짚어줘.
여기서 자주 막히는 부분
AI가 처음부터 완벽한 컴포넌트를 한 번에 만들려고 하는 경우가 많습니다.
"먼저 라우팅만 잡고, 그다음 상태 관리, 마지막에 키보드" 처럼 순서를 잘라서 지시하면 막힐 일이 줄어듭니다.
CHAPTER 01 정리
- 큰 결정 → 작게 쪼개기 → 객관식으로 묻기
- 데이터 구조부터 결정하기
- 한 step은 가능하면 30분 안에 끝나는 크기로
CHAPTER 02사용자 피드백 받기 — 신고 시스템과 대시보드
이번 챕터에서 만들 것
- Supabase 무료 DB 연결
- 문제 신고 버튼 + Discord 알림
- /admin 운영 대시보드 (방문/완료/합격률)
학습 목표
- "제약 조건을 먼저 명시"하는 프롬프트 패턴
- 운영 데이터를 보고 다음 우선순위를 정하는 습관
STEP 2.1
Supabase 프로젝트 만들기
- supabase.com 가입 → 새 프로젝트 생성
- Project URL과 anon key를
.env.local에 저장 - SQL 에디터에서 테이블 두 개:
analytics_events,reports
STEP 2.2
신고 기능 설계를 AI에게 부탁
문제 신고 기능을 만들고 싶어.
제약:
- Supabase 무료 티어 (DB 한 개)
- Discord Webhook으로 즉시 알림
- /admin에서 한눈에 모아 보기
결정해야 할 것 3개:
A. 같은 문제에 여러 신고가 들어왔을 때
어떻게 묶을지
B. Discord 알림 페이로드 형식
C. /admin에 어떤 지표를 보여줄지
각각 추천안 + 트레이드오프 표.
🔑
"제약"을 먼저 적는 게 포인트입니다.
"공짜로 / 빠르게 / 적게"라는 제약이 없으면 AI는 항상 가장 큰 솔루션을 추천합니다.
STEP 2.3
Discord Webhook 연결
- Discord 서버 설정 → 통합 → Webhook 생성
- Webhook URL을
.env.local에 추가 - 신고 발생 시 fetch로 POST
await fetch(process.env.DISCORD_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
content: `🚨 신고 · ${round}회 ${no}번\n사유: ${reason}\n→ ${adminLink}`
})
});
STEP 2.4
/admin 대시보드
관리자 페이지에서 일자별 방문/완료/합격률 + 신고 그룹을 봅니다.
| 지표 | 의미 | 왜 필요한가 |
|---|---|---|
| 방문 | 들어온 사람 수 | 유입 확인 |
| 완료 | 끝까지 푼 사람 수 | UX 품질 확인 |
| 합격률 | 커트라인 통과 비율 | 난이도 체감 확인 |
| 신고 | 문제 신고 그룹 | 다음 수정 우선순위 |
CHAPTER 02 정리
- 제약을 먼저 적어야 AI가 적절한 규모로 답한다
- 기능보다 측정이 먼저 — 그래야 다음 할 일이 보인다
CHAPTER 03실기 채점 UX — 사용자가 결과를 믿게 만들기
이번 챕터에서 만들 것
- 실기 답 입력 형식 자동 힌트
- 채점 후 내 답 vs 정답 diff 보기
- "이런 차이는 봐줬어요" 채점 근거 표시
학습 목표
- 스펙(설계 문서)을 형식 지정해서 받는 법
- 결정 테이블 + 아키텍처 + 데이터 흐름의 3구조
STEP 3.1
문제 정의
실기는 직접 답을 타이핑합니다. abc, ABC, a, b, c가 다 같은 답인데, 사용자가 못 믿어서 같은 답을 반복 시도합니다. 채점의 관대함을 사용자가 신뢰하게 만들어야 합니다.
STEP 3.2
AI에게 설계서 부탁
실기 채점이 관대하지만
사용자가 "왜 맞았는지" 못 믿어서 답답해해.
원하는 것:
1. 답 입력 전에 "이런 형식이 인정됨" 힌트
2. 채점 후 내 답 vs 정답 비교(diff)
3. "이런 사소한 차이는 봐줬어요" 근거 표시
설계서로 정리해줘. 산문 X.
다음 3구조로:
① 결정 테이블 (결정 / 값 / 이유)
② 아키텍처 (간단한 박스 다이어그램)
③ 데이터 흐름 (순서대로)
스펙 파일은 docs/specs/2026-04-15-practical-ux.md로.
📋
3구조 패턴을 외워두세요. 결정 테이블 + 다이어그램 + 데이터 흐름.
이 셋만 정해지면 다음 단계(구현 계획)로 넘어가도 됩니다.
STEP 3.3
채점 로직 구현
채점 모듈을 따로 빼서 단위 테스트를 쓰기 쉽게 만듭니다.
// app/practical/[sessionId]/_lib/gradePracticalAnswer.js
export function gradePracticalAnswer(userInput, answer, type) {
// type: single / ordered_sequence / unordered_symbol_set /
// multi_blank / textarea
// returns: { correct, reasons, diff }
}
CHAPTER 03 정리
- 스펙은 결정표 + 다이어그램 + 데이터 흐름, 이 셋이면 충분
- "산문 X"라고 명시하면 깔끔한 문서가 나온다
CHAPTER 04AI 해설 추가하기 — "왜 이게 정답이지?"에 답해주기
이번 챕터에서 만들 것
- 채점 후 "AI에게 물어보기" 버튼
- OpenAI API 호출용 Next.js 라우트 (
/api/gpt/objection) - 같은 질문은 두 번 안 부르는 Supabase 캐시
- 👍/👎 피드백 + 신고 연결
학습 목표
- 외부 LLM API를 안전하게 부르는 라우트 패턴
- "AI 호출은 비싸다" 전제로 캐시를 먼저 설계하는 습관
- 프롬프트에 컨텍스트(문제·내 답·정답)를 빠짐없이 넣는 법
💡
해설은 데이터에 이미 들어 있지만, 사용자는 종종 "이 해설이 왜 그렇게 되는지"를 더 묻고 싶어 합니다. 그 한 번의 추가 질문에 AI를 붙이는 게 이 챕터의 목표입니다.
STEP 4.1
문제 정의 — 왜 필요한가
풀이 후 정답을 봐도 "왜 1번이 답이지?"가 풀리지 않는 경우가 많습니다. 신고/이의제기로 들어오는 케이스를 보면 70%가 "해설 보충 요청"이었습니다.
- 해설 텍스트는 짧고 형식적
- 사용자는 본인 답이 왜 틀렸는지를 묻고 싶음
- 같은 문제에 같은 질문이 반복됨 → 캐시 가능
STEP 4.2
AI에게 설계 부탁
사용자가 문제 풀고 나서
"왜 이게 정답이야?" 같은 추가 질문을
AI에게 물을 수 있게 하고 싶어.
제약:
- 별도 서버 없이 Next.js API 라우트로만
- OpenAI API 비용 줄이려면 캐시 필수
- 같은 (문제번호, 내 답, 정답) 조합은 같은 응답 재사용
설계만 단계별로:
A. API 라우트 입출력 스키마
B. 캐시 키를 어떻게 만들지
(sha256? 어떤 필드를 포함?)
C. Supabase 테이블 형태
D. 프론트에서 호출하는 흐름
코드는 아직 쓰지 말고 표 + 다이어그램으로.
🔑
"제약: 캐시 필수"를 미리 적어두지 않으면, AI는 매번 OpenAI를 부르는 코드를 추천합니다. 사용자 100명이 같은 질문을 하면 100번 결제됩니다.
STEP 4.3
AI에게 캐시 저장소 만들어달라고 부탁
같은 질문은 두 번 부르지 않게 하는 "기억 창고"가 필요합니다. Supabase에 테이블 하나만 만들면 됩니다.
Supabase에 GPT 응답을 저장해두는
캐시 테이블 하나 만들어줘.
필요한 칸:
- 캐시 키 (문제 + 내 답 + 정답을 합쳐 만든 고유값)
- 회차 / 문항 번호
- AI가 만들어준 해설 본문
- 재사용된 횟수 (지표용)
- 생성 시각
Supabase SQL 에디터에 그대로 붙여넣을 수 있는
한 덩어리 SQL로 보여줘.
🗂️
캐시 키는 같은 질문인지 판단하는 지문입니다. "ABC"와 "abc "가 같게 처리되도록 "공백 정리 + 소문자" 한 줄을 프롬프트에 꼭 넣어주세요.
STEP 4.4
AI에게 "API 라우트 만들어줘" 부탁
프론트(웹페이지)와 GPT 사이를 중계해주는 작은 함수 하나입니다. 이것도 프롬프트 한 번이면 끝납니다.
Next.js App Router로
POST /api/gpt/objection 라우트 하나 만들어줘.
이런 순서로 동작해야 해:
1) 요청에서 (문제·내 답·정답)을 꺼내
캐시 키를 만든다 (sha256)
2) 캐시 테이블에서 그 키를 찾는다
3) 있으면 → 저장된 응답을 그대로 돌려준다
(cached: true 표시)
4) 없으면 → OpenAI(gpt-4o-mini)에 보내고
응답을 캐시에 저장한 뒤 돌려준다
파일 하나로 만들고, 에러 처리도 같이.
💸
이 라우트가 있으면 사용자 100명이 같은 질문을 해도 OpenAI는 딱 한 번만 결제됩니다. "캐시 먼저 → 없을 때만 호출"이 핵심.
STEP 4.5
핵심 — GPT에게 보낼 "해설 프롬프트" 만들기
이 챕터에서 가장 중요한 부분입니다. 아래 메시지가 사용자가 질문할 때마다 변수만 바꿔서 GPT한테 가는 본문입니다. 글이 곧 결과 품질입니다.
다음 문제에 대해
사용자가 가진 의문을 풀어줘.
[문제] {문제 본문}
[보기] {보기 4개}
[정답] {정답 번호와 텍스트}
[사용자가 고른 답] {사용자 선택}
[기존 해설] {데이터에 있던 해설, 없으면 비워둠}
요청:
- 왜 정답인지 한 문단으로
- 사용자가 고른 답이 왜 틀렸는지 한 문단으로
- 비슷한 함정에 안 걸리는 팁 한 줄
말투: 친근한 강사처럼.
형식: 마크다운(**볼드**, - 리스트) 쓰지 말 것.
줄 바꿈만 써서 정리.
📝
제일 마지막 두 줄 — "마크다운 X · 줄 바꿈만" — 이 빠지면 AI는
**굵게**나 - 리스트를 섞어서 답하고, 그 기호가 화면에 그대로 노출됩니다. 사용자는 "이게 뭐야?" 합니다.
🧪
이 프롬프트는 한 번에 완성되지 않습니다. 실제 응답을 보면서 "더 짧게", "예시 하나만", "한국어 존댓말" 같은 한 줄씩 더해가며 다듬는 게 정석입니다.
STEP 4.6
AI에게 "물어보기 UI 붙여줘" 부탁
마지막은 화면에 챗박스를 띄우는 일입니다. 디자인까지 한 프롬프트로 끝.
풀이 결과 페이지 아래에
"AI에게 물어보기" 챗박스를 붙여줘.
구성:
- 질문 입력칸 + [보내기] 버튼
- 보내고 나면 "응답까지 5초쯤 걸려요" 안내
- 응답 본문 박스
- 응답 아래에 [👍] [👎] 버튼
- 캐시에서 온 응답이면 "재사용" 작은 뱃지
스타일:
- 회색 배경, 둥근 모서리
- 모바일에서도 자연스럽게
- 같은 페이지에서 여러 번 질문 가능
👍
👍/👎 버튼이 사실상 무료 운영 지표입니다. 👎가 많이 찍힌 응답은 캐시에서 지우고, 자주 등장하면 STEP 4.5 프롬프트를 손볼 신호로 씁니다.
왜 LangChain을 안 쓰나요?
해설 한 번 부르는 데 라이브러리 의존성을 깔면, 막혔을 때 디버깅이 두 배로 어려워집니다. OpenAI API는 단일
fetch 호출이라 직접 부르는 게 훨씬 단순합니다. 더 큰 에이전트가 필요해질 때 그때 가서 고민하면 됩니다.
CHAPTER 04 정리
- AI 호출은 비싸다 → 캐시를 제일 먼저 설계
- 프롬프트에는 문제·보기·내 답·정답·기존 해설을 모두 포함
- 응답 아래 👍/👎 한 쌍 → 그게 다음 개선 신호
🚧
실기(주관식 채점 + AI 학습 코치 + 외부 검증) 파트는 나중에 추가하는 방향으로 하겠습니다.
지금까지의 4개 챕터로도 "AI에게 일을 시키는 흐름" — 브래인스토밍 → 스펙 → 구현 → 캐시 + 피드백 — 은 한 번씩 다 거치는 구성입니다.
지금까지의 4개 챕터로도 "AI에게 일을 시키는 흐름" — 브래인스토밍 → 스펙 → 구현 → 캐시 + 피드백 — 은 한 번씩 다 거치는 구성입니다.