mirim-app/STUDY.md
AWESOMEDEV 111590104e feat: 수습 학습 플랫폼 최초 구축 (React + Spring Boot + PostgreSQL)
- 로그인/회원가입/아이디찾기/비밀번호재설정 (세션 기반)
- 대시보드(주차별 진도 체크리스트), 학습 문서 뷰어(17종), 과제 제출/멘토 피드백
- 코딩 기초·개발 환경 설치 가이드 학습 페이지
- 전 소스 한국어 학습 주석 — 수습생 교육용 저장소
- Docker 배포 구성 (Caddy + Spring Boot + PG + Gitea)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 08:18:12 +09:00

109 lines
9.3 KiB
Markdown

# STUDY.md — 이 코드로 배우는 법 (수습생용)
> 이 문서는 "코드를 어디서부터 어떻게 읽어야 하는지" 알려주는 지도입니다.
> 처음부터 다 이해하려고 하지 마세요. 하나의 요청이 어디를 거쳐 가는지 **한 줄기**만 따라가면, 나머지는 전부 같은 패턴의 반복입니다.
---
## 1. 요청 하나의 여행 — 로그인 버튼을 눌렀을 때 무슨 일이 일어나나
2주차 수업(웹의 동작 원리, 요청과 응답)에서 배운 내용을 실제 코드로 확인하는 코스입니다.
아래 순서대로 파일을 **직접 열어서** 읽어 보세요. 각 단계마다 "여기서 다음 단계로 어떻게 넘어가는지"를 찾는 게 목표입니다.
```
[브라우저] [서버] [DB]
LoginPage.jsx → api/client.js → (HTTP) → AuthController → AuthService → UserRepository → PostgreSQL
```
### ① `frontend/src/pages/LoginPage.jsx` — 출발점
- 로그인 버튼을 누르면 실행되는 함수(예: `handleSubmit`)를 찾으세요.
- 여기서 화면은 "아이디·비밀번호를 모아서 API 함수를 부르는 것"까지만 합니다. 검증 규칙 같은 진짜 판단은 서버가 합니다.
- **관찰 포인트**: 입력값이 어떻게 state로 관리되는지, 실패했을 때 에러 메시지를 어떻게 보여주는지.
### ② `frontend/src/api/client.js` — 프론트의 우체국
- 모든 페이지가 이 파일을 거쳐 서버와 통신합니다. `fetch``POST /api/auth/login`을 보내는 부분을 찾으세요.
- **관찰 포인트**: 왜 페이지마다 fetch를 직접 쓰지 않고 한 파일에 모았을까? (주소가 바뀌면 한 곳만 고치면 됨, 에러 처리를 한 번만 작성하면 됨.)
- 여기서 브라우저를 떠나 HTTP 요청이 네트워크를 타고 백엔드(8080)로 갑니다. 개발자도구(F12) → Network 탭에서 이 요청을 직접 눈으로 확인해 보세요.
### ③ `backend/.../controller/AuthController.java` — 서버의 현관문
- `@PostMapping("/api/auth/login")`이 붙은 메서드를 찾으세요.
- 컨트롤러는 얇습니다: JSON을 받아서 → 서비스에 넘기고 → 결과를 JSON으로 돌려줄 뿐. 판단하지 않습니다.
- **관찰 포인트**: 요청 JSON이 어떻게 자바 객체로 변하는지(`@RequestBody`).
### ④ `backend/.../service/AuthService.java` — 진짜 일하는 곳
- "이 아이디의 사용자가 있나? 비밀번호가 맞나? 틀리면 어떻게 하나?" — 규칙(비즈니스 로직)은 전부 여기 있습니다.
- **관찰 포인트**: 비밀번호를 평문 비교하지 않고 해시로 비교하는 부분. 왜 DB에 비밀번호 원문을 저장하면 안 되는지 생각해 보세요.
### ⑤ `backend/.../repository/UserRepository.java` — DB로 가는 문
- 파일을 열면 놀랄 만큼 짧습니다. `findByUsername` 같은 메서드 **선언만** 있고 구현이 없습니다.
- **학습 포인트**: Spring Data JPA가 메서드 이름을 읽고 SQL을 대신 만들어 줍니다. `findByUsername``SELECT * FROM users WHERE username = ?`.
### ⑥ `backend/.../domain/User.java` — DB 테이블의 자바 버전
- 엔티티 클래스 하나가 DB 테이블 하나와 짝을 이룹니다. 필드 하나가 컬럼 하나입니다.
- 여기까지 오면 여행 끝. 응답은 왔던 길을 **거꾸로** 타고 브라우저까지 돌아가고, LoginPage는 받은 사용자 정보로 화면을 바꿉니다.
> ✅ **확인 과제**: 같은 방식으로 "체크리스트에 체크했을 때"의 여행 경로를 종이에 그려 보세요.
> 힌트: `POST /api/progress/{itemId}/toggle`에서 출발해서, ChecklistPage → client.js → ProgressController → ProgressService → ProgressRepository 순서로 찾으면 됩니다.
---
## 2. 왜 이렇게 나눠 놨을까?
### 프론트엔드: pages / components / api
- **pages/** — "화면 한 장" 단위. 주소(URL) 하나에 페이지 하나가 붙습니다. (로그인 페이지, 대시보드, 문서 목록…)
- **components/** — 여러 페이지에서 반복해서 쓰는 조각. 버튼, 카드, 헤더 같은 것들. 한 번 만들어 두면 복붙하지 않고 재사용합니다.
- **api/** — 서버와 통신하는 코드만 모은 곳. 화면 코드와 통신 코드를 섞지 않으면, "화면이 이상한 건지 서버 응답이 이상한 건지"를 나눠서 디버깅할 수 있습니다.
한 문장으로: **pages는 조립, components는 부품, api는 배달.**
### 백엔드: controller / service / repository / domain
- **controller/** — HTTP를 아는 유일한 계층. 요청을 받고 응답을 돌려주는 현관문.
- **service/** — 규칙과 판단. "멘토만 피드백을 쓸 수 있다", "같은 과제를 다시 제출하면 덮어쓴다" 같은 우리 서비스의 법.
- **repository/** — DB를 읽고 쓰는 일만. SQL 걱정은 여기(와 JPA)가 다 합니다.
- **domain/** — 데이터의 모양 정의. User, Document, Assignment 같은 "명사"들.
왜 나누냐면: **한 파일에 다 쓰면 처음엔 빠르지만, 고칠 때 지옥이 됩니다.** "비밀번호 규칙을 바꿔라"라는 요청이 오면 service만 보면 되고, "응답 JSON에 필드를 추가해라"면 controller 근처만 보면 됩니다. 계층은 "어디를 고쳐야 하는지"를 알려주는 주소 체계입니다.
---
## 3. 직접 해보기 — 미니 과제 5개
티켓 풀 문서(문서함의 실전 티켓 가이드)에 나오는 것과 같은 형식입니다. 순서대로 안 해도 되고, 막히면 멘토에게 "어디까지 해봤는지"와 함께 물어보세요.
### 과제 1. 대시보드에 오늘 날짜 표시하기 (난이도 ★)
- **목표**: 대시보드 상단에 "2026년 7월 16일 (목)" 형태로 오늘 날짜를 보여준다.
- **건드릴 파일**: `frontend/src/pages/DashboardPage.jsx`
- **힌트**: 자바스크립트 `new Date()``toLocaleDateString('ko-KR', {...})`을 검색해 보세요. 서버는 건드릴 필요가 없습니다.
### 과제 2. 문서 카드에 즐겨찾기 버튼 달기 — 프론트만 (난이도 ★★)
- **목표**: 문서 목록의 각 카드에 ☆ 버튼을 달고, 누르면 ★로 바뀐다. 새로고침하면 사라져도 됩니다(서버 저장 없음).
- **건드릴 파일**: `frontend/src/pages/DocumentsPage.jsx` (카드가 별도 컴포넌트라면 `frontend/src/components/` 안의 카드 파일)
- **힌트**: `useState`로 즐겨찾기된 문서 id 배열을 들고, 버튼 클릭 시 배열에 넣거나 빼면 됩니다. 도전 과제: `localStorage`에 저장해서 새로고침을 버텨 보세요.
### 과제 3. 체크리스트 항목 검색 (난이도 ★★)
- **목표**: 체크리스트 페이지에 검색창을 달아서, 입력한 글자가 포함된 항목만 보여준다.
- **건드릴 파일**: `frontend/src/pages/ChecklistPage.jsx`
- **힌트**: 검색어를 `useState`로 들고, 화면에 그리기 직전에 `items.filter(item => item.label.includes(검색어))`. 서버 API를 바꿀 필요가 없다는 것 자체가 학습 포인트입니다 — 이미 받아 온 데이터는 프론트에서 거를 수 있습니다.
### 과제 4. API 응답에 필드 추가해 보기 (난이도 ★★★, 백엔드 첫 수정)
- **목표**: `GET /api/auth/me` 응답에 `createdAt`(가입 시각)을 추가하고, 화면 어딘가에 "함께한 지 N일째"를 표시한다.
- **건드릴 파일**: `backend/.../controller/AuthController.java`(응답 DTO에 필드 추가) → `frontend/src/api/client.js`는 그대로 → 표시할 페이지 컴포넌트
- **힌트**: User 엔티티에는 `createdAt`이 이미 있습니다. 응답으로 내보내는 record/DTO에 한 필드만 추가하면 됩니다. 백엔드를 재시작한 뒤 브라우저 Network 탭에서 응답 JSON에 필드가 생겼는지 먼저 확인하고, 그다음 화면을 고치세요. **한 번에 한 층씩 확인하는 습관**이 이 과제의 진짜 목표입니다.
### 과제 5. 빈 상태(empty state) 화면 넣기 (난이도 ★★)
- **목표**: 제출한 과제가 하나도 없을 때, 휑한 빈 목록 대신 "아직 제출한 과제가 없어요. 이번 주 과제부터 시작해 볼까요?" 같은 안내를 보여준다.
- **건드릴 파일**: `frontend/src/pages/SubmissionsPage.jsx` (내 제출물 페이지)
- **힌트**: `submissions.length === 0`일 때 다른 JSX를 그리면 됩니다. 좋은 서비스는 "데이터가 없을 때"를 항상 설계합니다 — 실무에서 정말 자주 하는 일입니다.
---
## 4. 이 저장소는 여러분의 연습장입니다
5~6주차 실전 티켓 기간에는 이 앱 자체가 작업 대상이 됩니다. 지금 문서를 보고 과제를 제출하는 데 쓰는 바로 이 코드에, 여러분이 만든 기능이 붙습니다.
- 위 미니 과제 5개는 실전 티켓의 축소판입니다. 티켓도 결국 "어느 파일을, 왜, 어떻게 고칠지"를 찾는 일입니다.
- 브랜치를 파서 마음껏 실험하세요. 망가뜨려도 `git checkout`으로 돌아올 수 있고, 망가뜨려 본 만큼 빨리 늡니다.
- 모르는 게 나오면 정상입니다. "이 파일이 왜 있는지 모르겠어요"는 좋은 질문이고, 멘토는 그런 질문을 기다립니다.
여러분이 고친 코드가 다음 기수 수습생의 교재가 됩니다. 즐겁게 부수고, 정직하게 고치세요. 🚀