7 Commits

Author SHA1 Message Date
5c9959ce08 feat(design): 디자인 시스템 — Pretendard 실적용·토큰·Lucide 아이콘 132개·/design
All checks were successful
CI / backend-test (push) Successful in 3m25s
CI / frontend-build (push) Successful in 48s
CI / backend-dep-scan (push) Successful in 31s
이모지 아이콘을 SVG로 바꾸다가, 더 큰 원인을 발견해 함께 고쳤다.

■ 폰트: 이름만 부르고 파일은 안 받아오고 있었다
  global.css에 font-family: "Pretendard"가 적혀 있었지만 @font-face도 CDN
  링크도 없었다. 그래서 이 글꼴이 따로 깔린 사람 외에는 전원 맑은 고딕으로
  보고 있었다 — 앱과 학습 문서 19개 전부. 에러가 안 나서 조용히 넘어간 케이스.
  - PretendardStdVariable.woff2(285KB)를 public/fonts에 self-host
    · CDN 의존 없음 → 외부 서비스가 죽어도, 오프라인에서도 안 깨진다
    · variable font → 파일 하나가 굵기 45~920을 전부 담는다
    · font-display: swap → 받는 동안 흰 화면(FOIT) 방지
  - 전체판(2MB) 대신 Std판(상용 한글 2,350자)을 골랐다. 앱이 쓰는 한글
    1,276자가 Std에 전부 있는지 브라우저에서 실측 확인했다(폴백 0자).
  - 학습 문서 19개에도 같은 @font-face 적용

■ 아이콘: 이모지 95종 → Lucide 132개
  이모지는 OS마다 다른 그림으로 렌더링되고, 굵기·색을 통제할 수 없고,
  스크린리더가 "bust in silhouette"처럼 읽는다.
  - 카탈로그의 icon: '📜'(문자) → Icon: ScrollText(컴포넌트)로 전환
  - strokeWidth={1.5}로 굵기 통일, aria-hidden으로 장식임을 명시
  - 개별 import만 사용(트리셰이킹) → gzip +14KB
  - slug: null인 '개발 환경 설치 가이드' 항목이 일괄 변환에서 누락돼
    Icon이 undefined가 될 뻔한 것을 정합성 검사(항목 수 vs Icon 수)로 잡았다

■ 토큰: 여백·글자 크기·굵기를 :root로 승격
  --space-1~7(4의 배수), --text-xs~2xl, --weight-*, --radius-sm/lg/full.
  값을 직접 박으면 다크 모드에서 깨지고 화면마다 미묘하게 어긋난다.
  CSS 프레임워크는 도입하지 않았다 — 손으로 쓴 global.css와 충돌하고,
  교재로서 "왜 이렇게 썼는지"가 사라지기 때문이다.

■ /design: 재료를 고르는 화면
  색·글자·여백·모서리를 실제 CSS 변수로 렌더링(다크 모드 자동 반영) +
  Lucide 아이콘 1,993개 검색·복사. lucide-react는 아이콘 하나를 이름 3개로
  내보내(Rocket/RocketIcon/LucideRocket) 5,981개처럼 보이므로 별칭을 걸렀다.
  아이콘 전체를 훑어야 해 무거운 화면이라 lazy로 분리했다
  → 첫 화면 번들은 1.9KB만 증가(575.57 → 577.44KB).

검증: ESLint 통과, Vitest 30개 통과, 프로덕션 빌드 성공, 콘솔 에러 0.
브라우저에서 수습 7카테고리 + 초급~특급 4과정 = 11개 화면 전수 확인
(각 12카드/12아이콘/이모지 0).

문서: DESIGN.md(폰트·토큰·비용), ICONS.md(규약·대응표 132행)
2026-07-22 21:10:28 +09:00
a5deecf519 docs: SEC-02·03·04 구현 완료 표기 + CI 의존성 스캔 문서화
All checks were successful
CI / backend-test (push) Successful in 1m16s
CI / frontend-build (push) Successful in 23s
CI / backend-dep-scan (push) Successful in 5s
- 티켓 풀: 감사로그(SEC-02)·레이트리밋(SEC-03)·접근로그(SEC-04) '구현됨' 표기
- CI.md: npm audit·OSV-Scanner(비차단) 구성과 Spring Boot 3.4.2 누적 CVE 발견 기록

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 19:01:46 +09:00
92e65760bc ci: 러너를 compose로 관리 + CI.md 문서 + OP-02 티켓 '구현됨'으로 갱신
All checks were successful
CI / backend-test (push) Successful in 2m6s
CI / frontend-build (push) Successful in 1m4s
- act_runner를 docker-compose.prod.yml에 편입(등록 정보는 /opt/mirim-runner에 영속)
  → compose down/up에도 관리되고, 재등록 없이 이어짐. 재연결 확인
- deploy/CI.md: 구성요소·동작흐름·재구축 절차·함정 메모(Maven archive·clone add-host·checkout node)
- 티켓 풀 OP-02를 ' 구현됨 — 확장(프론트 테스트·캐시·브랜치 보호)은 학생 몫'으로 갱신

CI 상태: run 2 백엔드·프론트 둘 다 초록. run 1 빨강→run 2 초록으로 게이트 동작 실증.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:04:36 +09:00
24ff3320a3 docs: 티켓 풀에 고도화 점검 추가 티켓 14건 (20→34)
고도화 점검(다중 에이전트)에서 생존했으나 배치로 즉시 구현하지 않은 항목을
학생 실습 티켓으로 정식 등록. 기존 20개는 그대로, 새 섹션으로 추가:

- OP-01/02: 컨테이너 헬스체크, push 자동 테스트 게이트(Gitea Actions CI)
- SEC-01~05: 보안·권한 통합테스트(@WebMvcTest), PII 감사로그, 공개 쓰기 레이트리밋,
  개인정보 접근 로그, 의존성 취약점 수동 점검
- QA-05/06: 프론트 첫 테스트(Vitest+gradeSql), 죽은 퀴즈 사본(seed/quiz 71개) 정리
- FE-08~10: 학습센터 진도율 로딩 가드, 멘토 '리뷰 대기' 카운트·정렬, 설치 가이드 진입점
- EDU-01/02: '웹 보안·인증 기초' 코스 신설, 코딩문제·캡스톤 문서 주차별 연결

각 티켓에 무엇을/찾는법/완료조건/멘토준비를 실코드 근거로 기입. 현황판 34행,
헤더 개수 표기 갱신. 배치에서 이미 구현한 4건(다운알림·로그회전·개인정보파기·퀴즈버튼)은 제외.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:03:10 +09:00
84144a1608 fix(security+a11y): 종합 감사(PMD+5관점, 29에이전트) 확정 12건 수정
[HIGH]
- 로그인 잠금 XFF 스푸핑 우회: X-Forwarded-For leftmost(위조 가능) → rightmost(Caddy가 붙인 실제 IP) 사용
- 멘토 기본 비밀번호 하드코딩 제거: env 미설정 시 SecureRandom 생성 후 로그 1회 출력.
  compose에 MENTOR_INITIAL_PASSWORD 추가
- 클릭 카드 키보드 접근 불가(학습문서 전체): Card에 role=button·tabIndex·Enter/Space·aria-label

[MEDIUM]
- CSV 수식 인젝션: =+-@ 로 시작하는 학생 자유입력 앞에 ' 붙여 엑셀 수식화 차단 (+회귀 테스트)
- 학습문서 17개 <html lang=ko> 골격 주입 (스크린리더 한국어 발음)
- 과제 제출 폼 label htmlFor/id 연결
- 상단 아이콘 링크(👤·🔑) aria-label + 이모지 aria-hidden
- 멘토 제출물 표 행 키보드 접근(role/tabIndex/Enter·Space)
- 배지 amber/teal 글자 대비 4.5:1 확보(테마별 --amber-text/--teal-text)

[LOW]
- 코드에디터 포커스 표시(:focus-within), 문제번호 색 대비(--muted), 카드 :focus-visible
- 폼 오류 메시지 role=alert (스크린리더 즉시 안내)

검증: 테스트 26개 통과, 운영에서 XFF 우회 차단(6회째 429)·카드 role/aria·문서 lang=ko 확인.
PMD 29건은 전부 저심각 스멜로 실질 결함 없음. 각 수정에 원리 학습 주석.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:38:30 +09:00
2c8bbf3574 docs: 실전 티켓 풀 20개 전부 구체화 — mirim-app 실코드 기준
- 20개 패턴 각각에 '🎯 우리 건' 블록 추가: 대상 파일 경로, 현재 상태(검증된 사실),
  구체적 할 일, 티켓 간 연계(FE-01↔FE-04, DZ-01↔FE-05 등)
- 전 항목을 실제 코드로 검증 후 작성: '불러오는 중' 9곳 하드코딩(grep),
  hero compact 7페이지 복붙, ProblemSummary에 attempts 부재, SubmitRequest 길이 제한 부재,
  ControllerAdvice 부재, SubmissionService·CourseProgressService 테스트 0개,
  README 최근 기능 미반영, DocViewer notFound 잠재 결함(감사 기각 건 활용) 등
- 이미 구현돼 있던 후보(과제 빈 상태, 퀴즈 성적 API)는 검증 과정에서 다른 실존 대상으로 교체
- 현황판 20행 제목·구체화 체크 채움, 헤더를 '구체화 완료 · 배정 대기'로 갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 12:50:37 +09:00
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