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

9.3 KiB

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 — 프론트의 우체국

  • 모든 페이지가 이 파일을 거쳐 서버와 통신합니다. fetchPOST /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을 대신 만들어 줍니다. findByUsernameSELECT * 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으로 돌아올 수 있고, 망가뜨려 본 만큼 빨리 늡니다.
  • 모르는 게 나오면 정상입니다. "이 파일이 왜 있는지 모르겠어요"는 좋은 질문이고, 멘토는 그런 질문을 기다립니다.

여러분이 고친 코드가 다음 기수 수습생의 교재가 됩니다. 즐겁게 부수고, 정직하게 고치세요. 🚀