그래빗CS 학습 서비스
개발자 혹은 개발자 취업을 준비하는 취업 준비생들이 CS 개념 학습을 간편하게 할 수 있도록 퀴즈와 개념 노트를 제공하는 서비스입니다.

메인위젯별 로딩·오류 분리 
학습 홈legacy 화면 FSD 이전 
유닛 상세북마크·오답노트 진입 
문제 풀이마크다운 지연 로딩 대상
개요
- 팀 구성: 웹 2명 / 안드로이드 2명 / 백엔드 3명 / 디자이너 3명 / PM 1명 (총 11인)
- 개발 기간: 2025.06 — 현재 (1년 4개월)
- 출시 결과: MAU 100명
핵심 담당 범위
legacy 화면을 FSD 구조로 이전하고, 웹·모바일 시안에 대응하는 반응형 UI와 API 상태 처리를 구현했습니다. AI를 활용한 개발 과정에서 구조와 동작을 검증할 수 있도록 작업 규칙·승인 절차·자동 검사로 구성된 개발 하네스도 설계하였습니다.
- Legacy React 화면의 동작을 분석해 기능 단위 모듈 구조로 단계적 이전
- 웹·모바일 반응형 UI와 로딩·오류·빈 상태를 포함한 API 상태 처리
- Storybook·Vitest·RTL 기반 컴포넌트 문서화 및 동작·회귀 검증
- 프로젝트 규칙과 구현·검증 절차를 연결한 AI 개발 하네스 구축
Legacy 화면을 동작 계약으로 분해해 단계적으로 이전
레거시 화면의 분기 · 요청 흐름을 기준선으로 기록하고, 새 시안 · API와 달라진 점을 정리했습니다. 로그인부터 학습 · 복습까지 제가 맡은 8개 화면을 단계적으로 구현하며 검증 기준(AC) 89개를 문서화했습니다. 메인의 부분 오류 처리와 복습 풀이의 문제별 제출처럼, 화면마다 달라진 동작을 따로 검증했습니다.
반복된 문제
- 기존 화면의 분기·문구·요청 흐름은 유지해야 했지만, 새 시안과 API 계약도 반영해야 했습니다.
- 예전 코드는 공통 컨벤션 없이 작성돼 그대로 옮기면 새 구조에 맞춰 다시 손봐야 했습니다.
- 화면을 통째로 복사하면 무엇을 유지하고 무엇을 바꿀지 구분하기 어려웠습니다.
설계 원칙
기존 동작을 Markdown 기준선으로 작성하고 기획·Figma 시안·API를 대조했습니다. 시안에 필요한 데이터가 API에 없는 경우 백엔드와, 시안에서 누락된 빈·오류·비활성 상태는 디자이너와 조율했습니다. 예를 들어, 메인 화면의 경우 단일 API로 모든 섹션 데이터를 내려줘 조회에 실패하면 화면 전체가 막히는 구조였는데, 이를 섹션별 API로 나눠 달라고 요청했습니다.
구조
| 단계 | 작업 내용 | 산출물 |
|---|---|---|
| 1. 기존 동작 기록 | 화면별 분기·상태·문구·API 요청 순서를 문서화 | 기존 웹 동작 기준선 |
| 2. 시안·API 대조 | Figma 데스크톱·모바일 시안, API 스키마와 비교 | 차이 판정(변경·유지·별도 작업 분리)과 누락 항목 정리 |
| 3. AC 확정 · 테스트 | Given-When-Then AC → Vitest · RTL · MSW로 로직 고정 | 검증 기준 89개 |
| 4. 단계적 이전 | 기능 단위 모듈 구조로 화면을 구현하고 AC와 대조 | 이전한 담당 화면 8개에서 사용되는 전체 모듈들 |
적용 결과
- 담당한 8개 화면을 새 웹에 구현하고 관련 자동 테스트가 통과함을 확인했습니다.
- 메인 화면은 섹션별로 조회·오류·재시도를 처리해 한 섹션의 조회가 실패해도 나머지 영역은 사용할 수 있게 개선했습니다.
- 디자인에서는 챕터별 유닛 번호를 01부터 표시하지만, 유닛 상세 API는 챕터 내 순번 대신 unitId를 제공하고 있었습니다. 기획자에게 번호 표기 기준을 확인한 뒤 백엔드에 챕터 내에서의 유닛 순번 정보를 요청했습니다. API 반영 후 목록과 상세 화면에 같은 번호가 표시되도록 수정했습니다.
프론트엔드 규칙을 AI 작업 절차와 자동 검사에 연결한 개발 하네스 구축
AI는 시안과 구현 중 무엇이 맞는지, 범위를 키울지를 직접 결정할 수 없었습니다. 작업 종류별 경로를 나누고 사람이 판단할 승인 지점 4곳을 고정했습니다. 이 흐름으로 작업 26건을 같은 절차로 완료했습니다.
반복된 문제
- 긴 프로젝트 규칙을 AI에 한 번에 전달하면 FSD 의존 방향이나 생성 파일 수정 금지 같은 제약이 작업 도중 누락되었습니다.
- 모든 규칙을 매 세션에 불러와 현재 작업과 관련 없는 내용까지 컨텍스트에 포함되었습니다.
설계 원칙
- 규칙을 역할과 작동 시점에 따라 파일을 쪼개어 작성했습니다.
- 어기면 안 되는 룰(상향 의존, 생성물·legacy 편집)만 저장 전에 강제하고, 나머지는 권고로 두었습니다.
- 규칙·스킬·훅은 .claude/ 한 곳을 기준(SoT)으로 두고, Claude Code와 Codex가 같은 파일을 참조하도록 했습니다. 처음에 도구별로 스킬 사본을 두었더니 이틀 만에 8개 중 4개가 서로 달라졌습니다.
- AI가 정할 수 없는 판단에는 GATE를 두어 사용자가 결정할 수 있게 했습니다. 예를 들어 시안과 구현이 다른 경우 뭘 따를지와 작업 범위가 커지는 경우 한 번에 처리할 지 별도 이슈로 나눌지도 사용자가 결정합니다.
구조
CLAUDE.md 진입점 · 규칙 목차
AGENTS.md CLAUDE.md를 가리키기만 한다
.claude/
├ rules/ 19 상시 8 · 파일별 11
├ skills/ 9 그중 4개는 사람 승인 단계
├ hooks/ 1 편집 직전 차단 · Claude·Codex 공용
└ agents/ 1 AC 독립 검증
work/ 작업 하나 = 폴더 하나
├ to-do/ 상태가 바뀌면 폴더를 통째로 옮긴다
├ in-progress/ 한 번에 하나만
└ done/ 26 MIG 11 · FIX 6 · 그 외 9| 구성 요소 | 역할 | 작동 시점 |
|---|---|---|
| rules | 구조·코드 규칙. 레이어 규칙은 paths로 해당 파일을 열 때만 로드 | 세션 시작 · 파일을 열 때 |
| skills | 계획 → 승인 → 구현 → 검증 | 요청을 받을 때 → 계획~검증 |
| PreToolUse hook | 상향 의존 · cross-slice · 생성물 · legacy 편집을 저장 전에 거부 | 저장 직전 |
| ac-verifier agent | 구현과 다른 컨텍스트에서 AC → 테스트 → 구현을 따라 읽고 판정 | 완료 |
작업 종류별 경로 분리
요청의 크기와 종류에 따라 스킬 경로를 선택합니다. 큰 작업은 모두 ai-plan 승인을 거쳐야 코드 구현 단계로 넘어갑니다. 작업 종류마다 경로 분리 및 구현 전 개발자 승인 GATE 추가했습니다.
- 간단한 수정: ai-quick → 변경 파일만 검증 · 커밋 메시지 제안
- MIG 이전: refactor-baseline → design-diff(승인) → refactor-planner(승인 ×4) → 공통 단계
- REF 구조: refactor-planner(승인 ×4) → 공통 단계
- FEAT·NAT 신규: feature-planner(승인 ×3) → issue-reviewer → 공통 단계
- 공통 단계(ai-quick 제외): ai-plan(승인) → ai-orchestrate → ai-validate → ac-verifier → 사용자가 커밋
승인은 사용자가 확인해야 다음 단계로 넘어가는 GATE이고, ×N은 스킬 안의 승인 횟수입니다. refactor-planner와 ac-verifier는 규모가 클 때(파일 10개 초과·여러 슬라이스)나 필요할 때만 거칩니다.
하네스 검사 플로우
AI 편집 혹은 새로 쓰는 코드만 보고, 사람이 직접 고친 코드는 보지 못했습니다. 따라서 커밋 훅 · pnpm verify · PR CI를 더해 사람의 변경과 기존 코드까지 검사 범위를 넓혔습니다. 이를 통해 포맷이 어긋난 파일을 25개에서 0개로 수정하였고, 검증에 실패한 PR은 55초 만에 실패로 표시됨을 확인했습니다.
| 시점 | 검사 | 하는 일 | 적용 범위 |
|---|---|---|---|
| 작업 전 | rules | 규칙 19개로 안내 (강제하지 않음) | AI가 새로 쓰는 작업 |
| 편집 직전 | PreToolUse 훅 | legacy·openAPI 생성물 편집 차단, FSD 위반 import 차단 | AI가 새로 쓰는 작업 |
| 커밋 | 커밋 훅 | lint-staged로 스테이징 파일 포맷·ESLint, commitlint로 메시지 형식 제한 | 사람과 AI 작업 모두 · 바뀐 파일 |
| 마무리 · PR | pnpm verify | 포맷·lint·타입·테스트·빌드 전체 검사. 로컬에서는 직접 실행, PR 작성 시 자동 실행 | 사람과 AI 작업 모두 · 저장소 전체 |
적용 결과
- 작업 경로에 맞는 규칙만 불러오도록 paths를 적용해 세션마다 항상 포함되는 규칙을 105KB→41KB로 줄였습니다.
- 구축한 하네스로 담당한 로그인·온보딩·메인·학습 영역의 화면 8개를 이전했습니다.
번들 분석과 측정 설계로 첫 로딩 JS 27% 축소
라우트 설정 하나가 마크다운 라이브러리를 첫 JS에 끌고 옴 → FSD 배럴은 두고 마크다운 모듈에 지연 로딩 입구를 둠 → 첫 JS 841 → 615 KiB(−27%), 메인 LCP 0.55초 단축
문제 지표
- 메인 화면의 LCP를 측정해 보니 LCP 요소인 히어로 이미지는 13.7KB로 작았지만, LCP 시간의 78%(4.6초)가 이미지 요청이 시작되기 전의 대기였습니다.
- 이미지가 아니라 첫 번들의 JS 실행이 끝나야 이미지 요청이 시작되는 구조가 병목이었습니다.
원인 분석
- 번들 지도에서 퀴즈·개념 노트에서만 쓰는 마크다운 라이브러리가 첫 번들의 33%(압축 전 739KB)를 차지했습니다.
- import 경로를 따라가 보니 북마크·오답 라우트 설정이 쿼리 키 하나를 쓰려고 entities/problem 배럴을 불러왔습니다. 라우트 설정은 코드 분할 대상이 아니어서 첫 번들에 남았고, 쓰지 않는 마크다운 코드까지 함께 포함되었습니다.
해결 방안
- 배럴은 FSD 공개 API라 그대로 두고, 무거운 마크다운 모듈에 지연 로딩 입구(lazy-markdown.tsx)를 두었습니다.
- 퀴즈·개념 노트 라우트에서는 loader에서 청크를 미리 받아 API 요청과 함께 마크다운 컴포넌트를 내려받게 했습니다.
| 방법 | 영향 범위 | 판단 |
|---|---|---|
| A. sideEffects 선언 | 앱 전체 — 안 쓰는 export를 버릴 수 있음 | △ 보류 — 앱 전체를 검증해야 함 |
| B. 마크다운 지연 로딩 (선택) | 마크다운 모듈 한 곳 — 변경범위가 작고 확실 | ○ 선택 — FSD 배럴은 그대로 유지 |
| C. 해당 라우트에서만 쿼리 키 직접 import | 북마크·오답 라우트 두 곳만 고침 | ✕ 기각 — 다른 파일이 배럴을 불러오면 다시 생김 |
결과
| 지표 (도구 · 조건) | 이전 → 이후 | 판단 |
|---|---|---|
| 첫 JS (vite build) | 841 → 615 KiB (gzip 270 → 195 KiB) | −27% — 마크다운은 226 KiB 별도 청크로 이동 |
| 메인 LCP (녹화 재생 · 5회 중앙값) | 4.50 → 3.95초 (−0.55) | 개선 — 흔들림 0.00초보다 큼 |
| 개념 노트 LCP | 7.42 → 7.04초 (−0.38) | 개선 — 흔들림 0.09초보다 큼 |
측정 방법
공용 와이파이에서 같은 빌드를 5번 재니 LCP가 5.38~6.57초로 최대 1.2초 차이 났습니다. 그래서 API 응답을 녹화해 두고 재생하는 방식으로 서버 응답 속도와 와이파이 상태의 영향을 없앴습니다.
- 녹화: 측정 전용 Chrome 프로필로 실제 dev 서버에 접속해 API 응답 저장 (Puppeteer)
- 재생: API 요청을 가로채 녹화본으로 응답, 지연 약 180ms 고정 (CDP Fetch)
- 조건 고정: 412×823 모바일 · CPU 4배 · Fast 4G · 캐시 끔 (CDP, DevTools와 같은 방식)
- 번갈아 5회: 이전 → 이후 → 이후(같은 빌드), LCP·FCP·CLS 수집 (PerformanceObserver)
지연 로딩 후 개념 노트가 오히려 느려진 문제
- 증상: 지연 로딩 적용 후 개념 노트 LCP +2.07초, CLS 0.35 → 0.63 (배포 전 측정에서 발견)
- 원인: React.lazy는 이미 받아 둔 모듈도 첫 렌더에서 한 번 멈춰 스켈레톤을 다시 그림
- 해결: 받아 둔 모듈은 Suspense 없이 바로 그리도록 수정
- 검증: 코드 블록·표가 있는 개념 노트 3개의 렌더 결과가 이전과 픽셀 단위로 같은지 확인
다음 작업
- 첫 청크의 미사용 아이콘(약 11%)과 퀴즈 코드(약 5%)는 REF-063에서 줄이는 중