85 Commits

Author SHA1 Message Date
b18a9af195 feat(helper): 입력창 textarea(줄바꿈) + 회사 생활도 답하도록 확장
All checks were successful
CI / backend-test (push) Successful in 2m9s
CI / frontend-build (push) Successful in 45s
CI / backend-dep-scan (push) Successful in 29s
- 입력이 <input> 한 줄뿐이라 줄바꿈이 안 됐다 → <textarea>로. Enter 전송,
  Shift+Enter 줄바꿈. 답변은 이미 pre-wrap이라 줄바꿈 반영됨.
- LlmAssistant 프롬프트에 회사 생활·직장 예절·업무 소통(보고·이메일·질문법·
  온보딩)을 신입 눈높이로 답하도록 추가. mirim 온보딩 교육과 결이 맞음.
2026-07-23 14:26:55 +09:00
4b64d0ac9c feat(dashboard): 주차 체크리스트 → 학습 문서 연결 고리
All checks were successful
CI / backend-test (push) Successful in 1m25s
CI / frontend-build (push) Successful in 45s
CI / backend-dep-scan (push) Successful in 6s
대시보드에도 '이 주차 학습 문서' 링크를 붙였다. 과제 페이지와 같은 패턴.
대시보드가 '오늘 뭐하지'만 보여주고 끝나던 것 → 문서→체크 흐름의 허브로.
2026-07-23 13:42:10 +09:00
6f1c78bc03 fix(docs): 문서 뷰어 :root 변수 소생 — 한글이 배경에 묻히던 문제
All checks were successful
CI / backend-test (push) Successful in 1m36s
CI / frontend-build (push) Successful in 45s
CI / backend-dep-scan (push) Successful in 28s
Shadow DOM 안엔 :root가 없어 문서가 :root에 정의한 CSS 변수(--ink 등)가
통째로 죽었다. var(--ink) 텍스트가 색을 잃고 배경과 같아짐(안드로이드·아이폰
공통). :root→:host로 변수를 그림자 트리에 심고, 기본 배경·글자색 폴백 추가.
2026-07-23 13:35:12 +09:00
c813b99ea3 fix(helper): 학습 도우미 가로 스크롤 제거 + 아이폰 사파리 대응
All checks were successful
CI / backend-test (push) Successful in 1m27s
CI / frontend-build (push) Successful in 46s
CI / backend-dep-scan (push) Successful in 5s
- 긴 코드 줄이 패널을 넘어 가로 스크롤 생기던 것 → overflow-wrap: anywhere
- 사파리 100vh(주소창 포함) 문제 → 100dvh로, 미지원은 vh 폴백
- 아이폰 홈바만큼 띄우기 → env(safe-area-inset-bottom)
2026-07-23 13:26:38 +09:00
4a7e75f92d feat(assignments): 주차별 과제 → 학습 문서(과제집) 연결 고리
All checks were successful
CI / backend-test (push) Successful in 1m21s
CI / frontend-build (push) Successful in 47s
CI / backend-dep-scan (push) Successful in 5s
과제 페이지에 '이 주차 학습 문서' 링크를 붙였다. 학생이 과제만 보고
'뭘 보고 하나' 헷갈리던 문제 해결. 주차별 과제집(week1~4-assignments)이
있는 주차에만 뜨고, 없는 주차(5~8)엔 안 뜬다.
2026-07-23 13:23:13 +09:00
d931281946 fix(nav): 헤더 로고 클릭 시 메인(대시보드)으로 이동
All checks were successful
CI / backend-test (push) Successful in 1m18s
CI / frontend-build (push) Successful in 45s
CI / backend-dep-scan (push) Successful in 5s
2026-07-23 13:05:07 +09:00
8276870252 feat: AI 학습 도우미 위젯 + 피드백 초안 영역 확대
All checks were successful
CI / backend-test (push) Successful in 1m18s
CI / frontend-build (push) Successful in 58s
CI / backend-dep-scan (push) Successful in 28s
- 우측 하단 플로팅 AI 학습 도우미. 학생이 막힐 때 질문하면 로컬 LLM
  (qwen3:8b, 빠른 대화형)이 정답 대신 힌트·설명으로 답한다.
  LlmAssistant + POST /api/assistant/ask + HelperWidget.
- 멘토 피드백 textarea min-height 200px — AI 초안 여러 문장이 한눈에.
2026-07-23 12:55:50 +09:00
a209bb1d80 fix(docs): Shadow DOM 목차 앵커 클릭 시 스크롤 이동
All checks were successful
CI / backend-test (push) Successful in 1m11s
CI / frontend-build (push) Successful in 47s
CI / backend-dep-scan (push) Successful in 5s
브라우저의 #프래그먼트 이동은 Shadow DOM 안의 id를 못 찾아 목차 순번
클릭이 안 먹혔다. 클릭을 가로채 그림자 안에서 대상을 찾아 scrollIntoView.
2026-07-23 12:44:26 +09:00
cd61f06ba5 refactor(docs): 문서 뷰어 iframe 제거, Shadow DOM 네이티브 렌더
All checks were successful
CI / backend-test (push) Successful in 1m12s
CI / frontend-build (push) Successful in 50s
CI / backend-dep-scan (push) Successful in 6s
iframe의 이중 스크롤 문제를 없애고, 문서 HTML/스타일을 Shadow DOM에
격리해 렌더한다. 문서 HTML(public/docs/*.html)은 한 글자도 안 고치고 재사용.
2026-07-23 12:35:31 +09:00
0a7f91ba8c deploy: 멘토 AI 초안용 ollama-proxy 추가 + backend LLM 설정
Some checks failed
CI / backend-test (push) Failing after 2m22s
CI / frontend-build (push) Successful in 1m5s
CI / backend-dep-scan (push) Successful in 29s
edu(EC2)가 워크스테이션 tailnet의 Ollama를 쓰도록 프록시를 둔다.
Ollama는 DNS-rebinding 방어로 Host를 검증해 tailnet 이름 요청을 403으로
막는데, 이 프록시가 Host를 localhost로 재작성해 통과시킨다.
backend는 JAVA_TOOL_OPTIONS(-Dllm.base-url)로 프록시를 가리킨다.
2026-07-23 12:23:50 +09:00
c26207ee48 feat: 멘토 코딩 피드백 AI 초안 기능
All checks were successful
CI / backend-test (push) Successful in 1m16s
CI / frontend-build (push) Successful in 51s
CI / backend-dep-scan (push) Successful in 28s
멘토가 코딩 제출물에 피드백을 쓸 때, 로컬 LLM(gpt-oss:20b)이 초안을
만들어 준다. 최종 판정은 멘토가 한다 — LLM은 초안 작성자일 뿐이다.

- LlmFeedbackDrafter: Ollama /api/chat 호출 (TelegramNotifier 패턴 —
  비활성 시 조용히, 예외 삼킴, 학습 포인트 주석)
- POST /api/mentor/coding-submissions/{id}/feedback-draft (초안만, 저장 안 함)
- 멘토 화면에 "AI 초안 받기" 버튼 → textarea 채움 → 멘토가 고쳐 저장
- llm.base-url 미설정 시 기능 자동 비활성 (모델 없어도 앱 정상 동작)

호출 규칙(/api/chat, num_ctx 16384, gpt-oss:20b)은 E:/llm 평가 근거:
qwen 계열은 async 버그를 "정상"이라 놓쳐 피드백에 위험, gpt-oss는 6/6.
2026-07-23 11:36:19 +09:00
169dcfba38 docs(README): 실행 안내를 실제와 맞춤 — Maven 준비물 추가, 없는 mvnw 안내 제거
All checks were successful
CI / backend-test (push) Successful in 2m9s
CI / frontend-build (push) Successful in 46s
CI / backend-dep-scan (push) Successful in 6s
수습생이 README대로 따라 하면 백엔드 실행 첫 줄에서 막히는 상태였다.

- `./mvnw spring-boot:run` → `mvn spring-boot:run`
  저장소에 mvnw 래퍼 파일이 없다. 없는 파일을 안내하고 있었다.
  (CI도 같은 이유로 Maven을 직접 내려받아 쓴다 — ci.yml 주석 참고)
- 준비물에 Maven 3.9 이상 추가
  Docker·Java·Node만 적혀 있고 Maven이 빠져 있었다. 실제로 이것 때문에
  막혔다 — "java는 있는데 왜 안 되지?"의 원인이 여기였다.
- 시작 전 확인 명령 4줄 추가(docker -v / java -version / mvn -v / node -v)
  준비물이 갖춰졌는지 먼저 확인하는 습관을 들이게 한다.
- 자주 겪는 문제 2건 추가
  · mvn: command not found — 래퍼가 없어 직접 설치해야 함을 명시
  · mvn package가 커버리지로 실패 — -DskipTests로 돌리면 JaCoCo 라인 30%
    게이트에 걸린다. 서버만 띄울 땐 spring-boot:run을 쓰면 된다.
2026-07-22 21:42:04 +09:00
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
1b559f5da2 fix: 전면 결함 스윕 — 4관점 감사 확정 결함 일괄 수정
All checks were successful
CI / backend-test (push) Successful in 1m10s
CI / frontend-build (push) Successful in 40s
CI / backend-dep-scan (push) Successful in 28s
4개 전문 감사관(프론트·백엔드·일관성·UX) 병렬 검증 결과 반영.

버그/깨짐(HIGH·MED):
- HtmlCssPage: 존재하지 않는 /learn/computer → /learn/hardware(깨진 링크, 대시보드로 튕김)
- MentorAnalyticsService: 체크리스트 활동(Progress.checkedAt)이 '마지막 활동'에 누락돼
  체크리스트만 한 학생이 '관심 필요'로 오분류되던 버그 수정(+회귀 테스트)
- 퀴즈 핫스팟 평균 정수절삭 → Math.round로 통일

접근성/UX(MED):
- CodeEditor: 코드 입력창에 aria-label 추가(WCAG 4.1.2, 스크린리더 이름 부여)
- 멘토 제출물 표: tr role=button → 셀 안 button으로(표 의미구조 보존 + 키보드)
- 대시보드 체크박스 토글 실패 시 무피드백 → try/catch + 에러 안내

콘텐츠/문서 드리프트:
- 초급 미니프로젝트 JS 예제 var→let(let/const 가르치며 var 쓰던 자기모순)
- README 강좌 수(71→83+48), BACKUP.md 복원검증 카운트(퀴즈655·문서19·코딩25)
- 브랜드/코스수 주석 정리(수습 플랫폼→학습 플랫폼, 72개→비수치화), 퀴즈 결과 '점'→'%'

테스트 백엔드 51→52, 프론트 30 유지. 운영 배포·스모크 11/11.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:01:05 +09:00
bc2807db79 feat(mentor): 코호트(기수) 관리 + 기수 분석 대시보드
All checks were successful
CI / backend-test (push) Successful in 1m13s
CI / frontend-build (push) Successful in 40s
CI / backend-dep-scan (push) Successful in 29s
멘토 화면의 '분석·기수관리'를 개인 카드 나열에서 '묶어서 본 그림'으로 끌어올림.

기수(코호트):
- Flyway V2 마이그레이션으로 student_profile.cohort 추가(기존 학생 '2026' 백필)
- 가입 시 현재 기수(app.current-cohort, .env로 매년 변경) 자동 배정
- 명단에 기수 컬럼 + 배지 클릭으로 변경(POST /mentor/students/{id}/cohort)
- CSV 내보내기·StudentProfileResponse에 기수 포함

분석 대시보드(GET /mentor/analytics?cohort=):
- MentorAnalyticsService: 코호트별 KPI(진도·퀴즈·코딩·리뷰대기),
  학생별 마지막 활동 계산(4개 테이블 타임스탬프 max), 관심필요(7일+ 미활동),
  어려운 지점(평균 낮은 퀴즈·통과율 낮은 코딩 top5)
- MentorAnalytics 컴포넌트: 기수 선택·KPI 카드·관심필요·핫스팟, 슬러그→제목 해석

테스트: 백엔드 47→51(분석 4), 프론트 27→30(분석 3).
운영 검증: V2 빈DB→운영 무손실 적용(3명 백필), 분석 API·대시보드 브라우저 확인, 스모크 11/11.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 13:29:49 +09:00
9045303a7a fix(content): 전문가 검증 지적 2건 반영 (티어 강좌 정밀성)
All checks were successful
CI / backend-test (push) Successful in 1m1s
CI / frontend-build (push) Successful in 38s
CI / backend-dep-scan (push) Successful in 29s
4개 트랙 전문 리뷰(강좌 48·퀴즈 240) 결과 CRITICAL 0·정답키 오류 0.
확정된 정밀성 MINOR 2건만 수정:

- Lv2 테스트 강좌: 'at>0→at>=0으로 바꾸면 빨간불' 실습이 제시된 테스트로는
  안 깨지던 문제(살아남는 뮤턴트) — 선두 @ 케이스 테스트 추가로 실제 성립하게
- Lv3 인덱스 강좌+퀴즈 해설: 전방일치 LIKE('보안%')가 Postgres 기본 로케일에선
  일반 B-Tree를 못 탄다는 조건(text_pattern_ops 필요) 명시 — 운영 DB 해설도 갱신

정답키·사실오류·버그코드는 240문항·48강좌에서 발견되지 않음.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:51:55 +09:00
f571462b95 feat(ui): 브랜드 '수습 플랫폼'→'학습 플랫폼', 비밀번호 눈 토글 추가
All checks were successful
CI / backend-test (push) Successful in 59s
CI / frontend-build (push) Successful in 39s
CI / backend-dep-scan (push) Successful in 6s
- 로고·타이틀·강좌 예시의 '수습(학습) 플랫폼' 표기를 '학습 플랫폼'으로 통일
- PasswordInput 컴포넌트: 비밀번호 칸 끝에 눈 버튼(👁️/🙈)으로 마스킹 토글
  (오타 확인용) — type=button으로 폼 제출 방지, ...props로 기존 input 대체
- 로그인·비밀번호 변경(3칸)·회원가입(Field password 타입 2칸)에 일괄 적용

운영 검증: 타이틀 '학습 플랫폼' 확인, 눈 클릭 시 password→text 전환·아이콘 토글 브라우저 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:24:43 +09:00
d8708caca5 fix(learn): 과정 셀렉트를 학습 센터(/learn)에서만 노출
All checks were successful
CI / backend-test (push) Successful in 1m0s
CI / frontend-build (push) Successful in 39s
CI / backend-dep-scan (push) Successful in 6s
대시보드·과제·코딩 등 학습과 무관한 화면에서는 과정 셀렉트를 숨긴다.
useLocation으로 /learn 경로에서만 렌더 — 도구는 쓰이는 자리에서만 보인다.

운영 검증: 대시보드에 셀렉트 없음, 학습 센터에서 노출·선택 유지 브라우저 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:19:50 +09:00
c5f875d990 feat(learn): 수준별 학습 과정 4종 — 초급·중급·고급·특급 48강좌 + 퀴즈 240문항
All checks were successful
CI / backend-test (push) Successful in 1m1s
CI / frontend-build (push) Successful in 42s
CI / backend-dep-scan (push) Successful in 30s
기존 84강좌는 수습용으로 유지하고, 헤더 셀렉트 박스로 과정을 전환한다.

과정 구성 (각 12강좌, 배우는 순서대로):
- 초급 — 코딩 첫걸음: 프로그램의 개념→HTML→JS→변수·조건·반복·함수→에러읽기→첫 게임
- 중급 — 실전 웹 개발: 비동기·배열→API설계→Express CRUD→DB·트랜잭션→인증→React 실전→테스트→배포
- 고급 — 아키텍처·성능·보안: 레이어드·패턴→인덱스·격리→캐싱·최적화→OWASP·인증설계→관측·무중단배포
- 특급 — 대규모 시스템: 분산기초·CAP→메시지큐→MSA→샤딩→대용량 설계→k8s→SRE→시스템설계 3연작→ADR

구현:
- trackContext(localStorage 유지) + 헤더 TrackSelect + 테스트 5개
- levelCatalog(lv1-~lv4- 슬러그, 기존과 충돌 없음) + 라우트·퀴즈 자동 연결
- LearnHubPage 과정 인식(과정별 인사말·진도), CertificatePage 분모 버그 선제 수정
- 퀴즈 240문항: quizzes.json 415→655 + 운영 DB 삽입(655/131 확인)

생성 파이프라인: 설계 4 → 작성 48(병렬) → 검수 4 에이전트.
검수 BLOCKER 2건(백틱 충돌·OpenAPI 표기) 수정 후 빌드 통과.
운영 검증: 4개 과정 전환·강좌 렌더·퀴즈 로드·선택 유지 브라우저 확인, 스모크 11/11.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:13:25 +09:00
8058a89304 test(component): React 컴포넌트 테스트 도입 (Testing Library, 13개)
All checks were successful
CI / backend-test (push) Successful in 1m0s
CI / frontend-build (push) Successful in 35s
CI / backend-dep-scan (push) Successful in 5s
단위(순수함수)와 E2E(전체 흐름) 사이 '부품 렌더링' 층을 메운다(jsdom).
- Badge(3): 코드값→한국어 라벨/색 매핑, 미지값 폴백(방어적 기본값)
- ProgressBar(4): 백분율 계산·반올림, 0 나눗셈 방어(NaN 방지), 막대 너비
- CodeEditor(6): 제어 입력 왕복, 줄번호 minRows 보장/증가, Tab 공백2칸 삽입,
  텍스트 선택 후 Tab 커서 위치(selectionStart+2 회귀 방지 — 과거 버그 못 박기)

세팅: @testing-library/react+jest-dom+user-event+jsdom, 파일별 @vitest-environment,
매 테스트 후 cleanup(globals 미사용 시 DOM 누적 방지).

프론트 테스트 9→22. coverage 게이트·lint·build 통과.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 08:39:01 +09:00
48bab9b2dd fix(test): vitest 대상을 src로 한정 (e2e Playwright 스펙 제외)
All checks were successful
CI / backend-test (push) Successful in 59s
CI / frontend-build (push) Successful in 30s
CI / backend-dep-scan (push) Successful in 28s
vitest가 기본으로 *.spec.js를 잡아 e2e/의 Playwright 스펙까지 실행하려다
frontend-build CI가 실패했다. test.include를 src/**로 좁혀 단위(vitest)와
E2E(Playwright)를 분리한다. (CI가 잡아준 회귀)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 08:30:07 +09:00
1661591d8b test(e2e): Playwright E2E 도입 — 로그인→강좌→퀴즈 + 멘토/감사로그
Some checks failed
CI / backend-test (push) Successful in 1m3s
CI / frontend-build (push) Failing after 27s
CI / backend-dep-scan (push) Successful in 6s
실제 브라우저로 '다 조립된 앱'(프론트+백엔드+DB)을 태우는 통합 테스트 6개:
- 인증 가드(비로그인→로그인), 로그인 실패 안내, 학생 로그인→대시보드
- 학습 센터→강좌 열기, 퀴즈 풀기→제출→서버 채점 결과
- 멘토 로그인→멘토 화면+개인정보 접근 기록(SEC-02 감사로그까지 검증)

운영 무오염 설계:
- deploy/docker-compose.e2e.yml — 별도 프로젝트(mirime2e)·네트워크·휘발성 DB(tmpfs)
- deploy/Caddyfile.e2e — 평문 HTTP로 SPA+/api 프록시(운영과 동일 구조)
- deploy/e2e.sh — 스택 기동→테스트 학생 API 생성→퀴즈 slug 자동조회→
  Playwright(브라우저 내장 이미지) 실행→down -v(정리)
- 백엔드 힙 256MB 제한(작은 인스턴스에서 운영과 공존)

EC2에서 6/6 통과 확인. 러너가 운영과 같은 인스턴스라 push마다가 아닌
온디맨드(릴리스 전)로 운영 — 전용 러너 확보 시 CI 게이트로 승격.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 08:27:42 +09:00
db54b9bc19 feat(db): Flyway 마이그레이션 도입 (ddl-auto update → validate)
All checks were successful
CI / backend-test (push) Successful in 1m1s
CI / frontend-build (push) Successful in 23s
CI / backend-dep-scan (push) Successful in 29s
스키마를 '앱이 자동 변경'에서 '버전 관리되는 마이그레이션'으로 전환:
- flyway-core + flyway-database-postgresql 추가
- V1__baseline.sql: 현재 운영 스키마(테이블 13·제약 31·인덱스 1)를 그대로 박제
- ddl-auto: update → validate (Hibernate는 이제 검증만, 스키마는 Flyway 소유)
- baseline-on-migrate: 기존 운영 DB는 V1 미실행·baseline 표시, 신규 DB는 V1 실행

안전 절차로 진행:
- 도입 전 운영 DB 백업(pg_dump -Fc)
- 신규 DB 경로 검증(빈 DB→V1 실행→validate 통과→기동) 후 운영 배포
- 운영: 기존 스키마 baseline(무실행)→validate 통과→기동, 데이터 무손실(users=4·profile=3) 확인
- 테스트 잔재 pgcrypto 확장 제거

향후 스키마 변경은 V2, V3...로 의도를 남긴다. 엔티티만 바꾸고 마이그레이션을
안 쓰면 validate가 기동 시 실패해 알려 준다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 07:59:20 +09:00
0e3ab14b14 fix(security): Spring Boot 3.4.2 → 3.5.16 (OSV 발견 CVE 전부 해결)
All checks were successful
CI / backend-test (push) Successful in 1m2s
CI / frontend-build (push) Successful in 23s
CI / backend-dep-scan (push) Successful in 27s
OSV-Scanner가 찾은 누적 CVE를 관리 의존성 버전 상향으로 일괄 해결:
- tomcat-embed-core 10.1.34 → 10.1.55 (CVSS 9.8 포함 다수)
- spring-security-* 6.4.2 → 6.5.11 (CVSS 9.1 포함)
- spring-web/webmvc/core/context 6.2.2 → 6.2.19
- assertj-core 3.26.3 → 3.27.7
취약 의존성 전부 OSV 수정버전 이상으로 확인(dependency:tree 대조).

현재 GA 라인(3.5.x) 최신 패치라 지원 수명도 길다. 표준 스타터만 써서
마이너 상향에도 회귀 없음: 전체 47개 테스트 통과.
운영 검증: 세션 로그인 200·멘토 권한 200·감사쓰기(실 IP)·비로그인 401·스모크 11/11.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 07:27:35 +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
e04e93cf18 feat(security): 보안 3종 — PII 감사로그·공개쓰기 레이트리밋·CI 의존성 스캔
All checks were successful
CI / backend-test (push) Successful in 1m22s
CI / frontend-build (push) Successful in 24s
CI / backend-dep-scan (push) Successful in 31s
SEC-02 PII 감사로그(누가·언제·어디서 개인정보를 조회/내보냈/파기했나):
- AuditLog 엔티티 + AuditLogRepository + AuditService(DB 영속 + slf4j 이중기록)
- MentorController: 명단조회·CSV내보내기·비번재설정·학생파기 4개 민감지점에 기록
- GET /api/mentor/audit-logs + MentorPage에 '개인정보 접근 기록' 표(남기기+보기 짝)
- 미성년 수습생 PII라 개인정보보호법상 접근기록 의무 대응

SEC-03 공개쓰기 레이트리밋(무인증 남용 방어):
- RateLimiterService(범용 고정창, Clock 주입, 메모리) — LoginAttemptService와 별도(단일책임)
- signup(30건/시간/IP: 대량가입·알림폭탄 차단), find-id(15건/10분/IP: 이름열거 차단)
- 초과 시 429. ClientIp 유틸로 rightmost XFF(Caddy 뒤 실제 IP) 공용화(AuthController 중복 제거)

CI 의존성 스캔:
- 프론트 npm audit(--omit=dev --audit-level=high, 비차단), 백엔드 OSV-Scanner v2.4.0(비차단)
- 둘 다 continue-on-error로 '보고 전용' 시작 → 무관한 신규 권고로 CI가 붉어지지 않게

적대적 보안 리뷰(4관점→검증) 확정 4건(전부 LOW) 반영:
- 파기 감사에 대상 아이디/이름 박제(파기 후 id는 dangling → '누구'를 못 알던 문제)
- 가입 한도 10→30(기수 동시 온보딩 대비), 감사 저장 전 로그 선기록(DB실패 시 흔적 유지)
- 고정창 경계 2배 특성 문서화, npm audit 비차단 전환

테스트 29→47(RateLimiter 5·Audit 3·SecurityContract 2 추가), 전부 통과.
운영 검증: audit_log 테이블 생성·감사쓰기(실IP 포함)·find-id 429·스모크 11/11 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:52:44 +09:00
daf3e9ac09 docs(CI): 품질 게이트·브랜치 보호 PR 워크플로 문서화
All checks were successful
CI / backend-test (push) Successful in 1m28s
CI / frontend-build (push) Successful in 21s
Spotless/JaCoCo/ESLint/커버리지 게이트와 로컬 교정법, main 보호 규칙과
학생 PR 실습 흐름을 CI.md에 정리.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:06:03 +09:00
63f34aa025 chore: 생성물 frontend/coverage/ 추적 제외 (.gitignore)
All checks were successful
CI / backend-test (push) Successful in 2m4s
CI / frontend-build (push) Successful in 23s
커버리지 리포트는 빌드 산출물이라 저장소에 두지 않는다(직전 커밋에서 실수로 포함).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:02:42 +09:00
6ba8776e04 ci: 10점 향한 빠른 승리 3종 — 브랜치 보호·커버리지·린트/포맷
Some checks failed
CI / frontend-build (push) Has been cancelled
CI / backend-test (push) Has been cancelled
1) 브랜치 보호(main): CI 두 개 초록불 + 승인 1 없이는 merge 불가,
   학생 직접 push 차단(PR 워크플로), 관리자만 운영·배포용 직접 push (Gitea 설정)
2) 커버리지 측정:
   - 백엔드 JaCoCo — 현재 라인 36.7%, 바닥 30% ratchet(테스트 지우며 후퇴하면 빌드 실패)
   - 프론트 vitest 커버리지 — 채점 로직 runSql.js 라인 40% 바닥
3) 린트/포맷 CI 강제:
   - 프론트 ESLint 9(flat) 신규 — 버그류 에러, 훅 오용 검출 (현재 위반 0)
   - 백엔드 Spotless — 안 쓰는 import·줄끝 공백·파일끝 개행 (기존 코드 이미 clean, 재정렬 없음)
   - ci.yml에 포맷검사·린트·커버리지 단계 추가

백엔드 37개 테스트 + 커버리지 게이트 통과, 프론트 ESLint·커버리지 통과 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:02:08 +09:00
c723cba92c test: 배포 스모크 테스트 (deploy/smoke-test.sh) — 인프라·설정 회귀 감지
All checks were successful
CI / backend-test (push) Successful in 51s
CI / frontend-build (push) Successful in 17s
CI가 못 잡는 '설정 회귀'(예: 보안헤더로 문서 iframe 깨짐)를 배포 후 잡는 그물.
살아있는 사이트를 실제 사용자처럼 두드려 11개 확인:
- 홈 200, HTTP→HTTPS 리다이렉트, 보안헤더 3종
- 학습 문서 200 + X-Frame-Options=SAMEORIGIN + no-cache + 한국어 본문
  (그 회귀를 정확히 재현·감지)
- 백엔드 API 401(Caddy→백엔드→DB 경로), SPA 라우트 폴백
하나라도 실패 시 exit 1. CI.md에 사용법·권장 배포 흐름 문서화.

운영 검증: 11/11 통과, exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:39:27 +09:00
edafae9f86 test(SEC-01): 보안·권한 계약 통합테스트 8개 (@WebMvcTest)
All checks were successful
CI / backend-test (push) Successful in 50s
CI / frontend-build (push) Successful in 17s
SecurityConfig의 URL→권한 규칙을 실제로 검증 — 규칙이 깨지면 CI에서 빨간불:
- /api/mentor/**: 비로그인→401, STUDENT→403, MENTOR→200
- /api/**(코딩): 비로그인→401, 로그인→200
- /api/auth/login·signup·find-id: permitAll(잘못된 본문 400으로 도달 증명)
- 인증은 실제 앱과 동일하게 세션에 SecurityContext를 넣어 흉내(가짜 우회 아님)
- @WebMvcTest 슬라이스 + @MockitoBean — DB 없이 웹계층+보안필터만 로드
- spring-security-test 불필요(세션 직접 주입이 더 충실) → 의존성 추가 안 함

전체 37개 테스트 통과(백엔드 29→37). 권한 규칙이 이제 자동 회귀 방지된다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:33:37 +09:00
7788509651 test(QA-05): 프론트 테스트 하네스(Vitest) + gradeSql 단위테스트 9개 + CI 연결
All checks were successful
CI / backend-test (push) Successful in 1m5s
CI / frontend-build (push) Successful in 17s
- Vitest 도입(devDep) + 'npm test' 스크립트
- src/lib/runSql.test.js: SQL 채점 순수함수 gradeSql 검증 9케이스 —
  행수 불일치·값 일치·orderMatters(순서 채점)·컬럼별칭 무시·타입 구분·빈결과
  (gradeSql은 학생 통과/실패를 결정하고 서버가 그 판정을 믿으므로 값어치 큼)
- CI(.gitea/workflows/ci.yml)에 'npm test' 단계 추가 — 회귀 시 배포 전 빨간불
- runSql/runJs는 Worker·WASM 의존이라 이번 범위 밖(후속 티켓)

로컬 검증: vitest 9 passed, 빌드 정상.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:15:20 +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
740b533921 ci: Maven 다운로드를 archive.apache.org로 (dlcdn 404 → CI 복구)
All checks were successful
CI / backend-test (push) Successful in 39s
CI / frontend-build (push) Successful in 16s
dlcdn 미러가 옛 버전(3.9.9)을 지워 백엔드 CI가 'Maven 설치'에서 실패했다.
모든 버전을 영구 보관하는 archive.apache.org로 변경 — 미래에도 안 깨진다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:59:53 +09:00
bf4b6955e3 ci: Gitea Actions로 push 시 자동 테스트 게이트 추가 (OP-02)
Some checks failed
CI / backend-test (push) Failing after 2m9s
CI / frontend-build (push) Successful in 1m6s
- .gitea/workflows/ci.yml: push/PR마다 백엔드 mvn test + 프론트 npm build 자동 실행
- Gitea Actions 활성화(compose GITEA__actions__ENABLED) + act_runner 등록(별도 컨테이너,
  deploy_default 네트워크, host-gateway로 job이 Gitea clone 가능)
- 방금 같은 회귀(보안헤더로 문서 깨짐 등)를 배포 전 자동으로 잡는 안전망.
  학생 티켓 OP-02를 실물로 구현 — 'CI가 왜 필요한가'의 살아있는 예제

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:54:12 +09:00
9ae3cf2709 fix(cache): 문서 iframe ?v= 캐시버스터 + SPA 표준 캐시 정책
옛 X-Frame-Options:DENY 응답이 브라우저 캐시에 박혀 서버 수정 후에도 계속
'연결 거부'가 나던 문제의 근본 대응:
- DocViewerPage iframe src에 ?v=2 — 캐시 키를 바꿔 새 응답을 받게(캐시 우회)
- Caddy 캐시 정책 정리: /assets/*(해시파일) 영구 immutable, 그 외 HTML·문서 no-cache
  → 앞으로 헤더/내용 변경이 캐시에 눌어붙지 않고 즉시 반영

검증: 문서=no-cache+SAMEORIGIN, index=no-cache, assets=immutable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:38:15 +09:00
91a6c2a9a9 fix(cache): 학습 문서에 Cache-Control:no-cache — 옛 보안헤더가 캐시에 눌어붙는 문제 방지
X-Frame-Options를 SAMEORIGIN으로 고쳤는데도, 예전에 DENY로 받은 문서가
브라우저 캐시에 남아 일반 새로고침으로는 계속 '연결 거부'가 났다.
/docs/* 응답에 no-cache를 걸어 브라우저가 매번 서버에 재검증하게 함 —
이후로는 헤더가 바뀌면 즉시 반영된다. Caddy 재기동만으로 적용.

검증: 문서 응답에 Cache-Control:no-cache + X-Frame-Options:SAMEORIGIN 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:33:15 +09:00
0cbfe666d7 fix(security): X-Frame-Options DENY→SAMEORIGIN — 학습 문서 iframe 로딩 복구
보안 헤더 추가(0a4c769) 때 X-Frame-Options를 DENY로 걸었는데,
DENY는 '어떤 사이트에서도 iframe 금지'라 우리 자신의 iframe(DocViewerPage가
학습 문서를 표시하는 방식)까지 막아 '연결 거부'로 문서가 안 떴다.
SAMEORIGIN(같은 출처끼리는 허용)으로 변경 — 클릭재킹 방어는 그대로 유지.

원인·교훈을 Caddyfile 주석에 기록. Caddy 재기동만으로 반영(재빌드 불필요).
검증: 문서 헤더 SAMEORIGIN, SPA도 헤더 유지, intern-guide 문서 iframe 정상 로딩.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:23:22 +09:00
f8dd3f683d feat: 수료증 + 개발 용어 사전 98개 + 학습 경로 안내
취업준비생 대상 동기·유틸리티 3종:
- 수료증(/certificate): 완료강좌·만점퀴즈·코딩통과·과제 집계를 인쇄/PDF 가능한
  증명서로. 기존 진도 API 4개를 프론트에서 합산(새 백엔드 0), 대표 서명 형식.
  인쇄 시 증명서만 나오게 @media print
- 개발 용어 사전(/glossary): 다중 에이전트로 7카테고리 98개 용어 생성(카테고리당 14),
  검색+카테고리 필터. 데이터(lib/glossary.js)와 화면 분리
- 학습 경로 안내: 대시보드 위젯에 '다음에 들을 강좌'(카탈로그 순 첫 미완료) 추가
- 대시보드에 용어사전·증명서 빠른이동 카드

검증: 빌드 통과, 운영 E2E — 용어 98개·해시검색 2건 필터·수료증 집계(1/83)·
다음강좌 추천(history→hardware)·콘솔에러0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:17:01 +09:00
1867c050c4 feat(ux): 학습센터를 카테고리 선택 방식으로 (83강좌 한번에 → 카테고리별 12개)
강좌 83개가 한 화면에 다 펼쳐져 압도적이던 문제.
- 카테고리 탭(7개, 각 '이름 완료/전체' 표시) 추가, 선택한 카테고리 강좌만 렌더
- 처음엔 첫 카테고리(컴퓨터 기초)를 열어 둠, .tabs/.tab 기존 스타일 재사용
- 전체 진도율 바는 히어로에 그대로 유지

검증: 초기 컴퓨터기초 12개만 노출, '앱 개발' 탭 클릭 시 앱 12강좌로 전환, 콘솔에러0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:55:42 +09:00
d9bd1a55c6 fix(ux): 페이지 이동 시 스크롤 맨 위로 (ScrollToTop)
SPA 특성상 라우트가 바뀌어도 스크롤 위치가 유지돼, 강좌 목록을 내려가
강좌를 클릭하면 강좌 페이지가 중간부터 보이던 불편.
useLocation의 pathname 변화 시 window.scrollTo(0,0). App 최상단에 배치.

검증: 학습센터 3000px 스크롤 후 React Native 클릭 → 강좌 페이지 스크롤 0 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:43:22 +09:00
4d5c6d2e2b feat: 학습센터 '앱 개발' 카테고리 12강좌 추가 (안드로이드·iOS·크로스플랫폼)
다중 에이전트로 하우스 스타일(ReactIntroPage) 그대로 12개 강좌 페이지 생성:
앱의 지형 → 안드로이드 구조 → Kotlin → iOS 구조 → Swift → React Native →
Flutter → 앱 내비게이션 → 상태·데이터 → PWA → 앱 보안 → 스토어 배포

- 프레이밍: '웹 개발자가 보는 모바일' — 학생 React 지식을 다리로(RN·PWA),
  안드로이드·iOS 둘 다 정확히 다루되 인식·literacy 수준
- 각 페이지: hero·pill-nav·6~7섹션·SVG 다이어그램·tip/warn·다음코스 링크,
  색은 전부 테마 변수(하드코딩 0), 386~489줄
- courseCatalog에 lazy import 12 + '앱 개발' 카테고리(라우팅·허브·완료바 자동)
- 확인 퀴즈 60문항(코스당 5) — quizzes.json 병합 + 운영 DB 적재
- 총 코스 71→83

검증: 빌드 통과, 하우스스타일 일괄 점검(12/12 준수), 운영 E2E —
학습센터 앱개발 카테고리 노출·React Native 렌더·PWA 퀴즈 채점(정답 서버보관)·콘솔에러0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:36:28 +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
0a4c769d00 feat: 고도화 배치 7건 — 보안헤더·다운감시·로그회전·개인정보파기·문서정합성
[보안]
- Caddy 보안 응답헤더(HSTS·X-Frame-Options:DENY·X-Content-Type-Options·Referrer-Policy) +
  Server 헤더 제거. :80 catch-all 제거로 HTTP→HTTPS 자동 리다이렉트 복원(평문 표면 제거)
[운영 안정성]
- 헬스체크 스크립트 + systemd 타이머(2분): 공개 URL 두드려 UP/DOWN 변화 시에만 텔레그램 알림
- Docker 로그 회전(json-file 10m×3, YAML 앵커 공통적용) — 디스크 풀로 인한 조용한 다운 방지
[개인정보]
- 학생 파기: DELETE /api/mentor/students/{id} (멘토 권한) → 자식6테이블 먼저·계정 나중,
  @Transactional. 동의화면이 약속한 '파기' 이행. 멘토 화면에 이름입력 확인 파기 버튼
  StudentPurgeService + 테스트3(삭제순서·멘토거부·404)
[문서 정합성 — 지금 쓰는 학생 대상]
- STUDY.md: 없는 파일 가리키던 코드지도 정정(controller/→web/, ChecklistPage→DashboardPage,
  DocumentsPage→DocsPage, SubmissionsPage→AssignmentsPage)
- README: '6주'→8주, 학습센터/퀴즈/코딩 소개 추가, 폴더구조·초기계정(멘토만 시드·랜덤비번) 현행화
- 퀴즈 만점 안내: 없는 '코스 완료' 버튼 대신 '[← 코스로] 돌아가 완료' 로 정정

검증: 테스트29 통과, 운영에서 헤더4종·308리다이렉트·헬스체크·파기 E2E(무인증401·멘토200·
자식전체0건) 확인. 나머지 13건은 티켓 풀 확장감으로 남김.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 14:58:00 +09:00
bb12d072b1 docs: 코드리뷰 실습 가이드 + 캡스톤·데모데이 문서 추가
- code-review.html: 5~8주차 티켓 흐름에 상시 코드리뷰 도입.
  왜/리뷰 흐름(PR→동료+멘토→반영→merge)/리뷰어 체크리스트/
  좋은·나쁜 코멘트 예시/받는 자세. 우리 실제 버그를 예시로.
- capstone-demoday.html: 7~8주차 캡스톤 = 1인 1기능(플랫폼 위) +
  8주차 금 데모데이. 밤샘 해커톤 아닌 미니 스프린트+발표. 일정·발표법·평가 연결.
- 평가 루브릭 ③ 협업·소통에 코드리뷰 항목 명시(주는 것/받는 것) + dim-note 스타일
- DocsPage에 'PRACTICE(실습)' 카테고리 추가, documents.json + 운영 DB 시드(학생 노출)

검증: 학생 계정에 두 문서 노출·렌더 확인. 커리큘럼 확장 없이 기존 티켓·PR·루브릭에 얹음.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:56:38 +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
57a334545a feat: 주소 입력을 카카오 우편번호 검색으로 — 주소 오타·누락 원천 차단
- lib/postcode.js: 다음(카카오) 우편번호 서비스 지연 로딩 + embed 방식
  (팝업은 모바일에서 차단될 수 있어 폼 안에 펼치는 embed 채택, 학습 주석 포함)
- 가입·내 정보 페이지: 기본주소는 [주소찾기] 검색으로만 채워지는 읽기 전용,
  상세주소(동·호수)만 직접 입력 → 저장 시 '[우편번호] 도로명주소, 상세' 로 합침
- 검색 결과: 우편번호 + 표준 도로명주소 + 건물명 자동 입력
- 배경: 학생 2명의 가입 주소가 시/군/구 누락 등 불완전했음 —
  '사용자의 실수는 실수할 수 없는 UI로 막는다'

운영 검증: 버튼 클릭 → 카카오 위젯(postcode.map.kakao.com) embed 로드 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 12:01:36 +09:00
f557d7a189 feat: 학생용 '내 정보' 확인·수정 페이지 (/profile)
- GET/PUT /api/profile/me — 경로에 id 없이 세션에서 본인 식별 (IDOR 원천 차단, 주석화)
- 수정 가능: 이름·회사이메일·생년월일·연락처·주소·학교·전공·비상연락처
  불가: 아이디(불변)·트랙(멘토 소관)·동의시각(증빙은 본인도 수정 불가)
- 이메일 변경 시에만 중복 검사(내 값 유지는 통과), 검증 규칙은 가입과 동일
- 수정할 엔티티는 트랜잭션 안 재조회(managed) — 비밀번호 변경 버그의 교훈 적용
- DTO 변환을 트랜잭션 안에서 — open-in-view:false의 LAZY 함정 회피 (주석화)
- 수정 시 멘토 텔레그램 알림 (경리 CSV 재발급 필요 알림)
- 네비 👤 아이콘 (학생만 표시), Field 컴포넌트는 밖에 정의(키보드 버그 교훈 참조)
- 테스트 3개 추가 → 전체 25개 통과

운영 E2E 검증: 조회→이메일·주소 수정→재조회 반영 확인.
배경: 가입한 학생 2명의 주소가 불완전하고 이메일 오타 의심 — 본인이 직접 교정 가능하게.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:52:04 +09:00
0336213ca2 fix: 기본기 전수 감사(38 에이전트, 172파일)에서 확정된 버그 10건 수정
[major]
- client.js 401 인터셉터: /auth/** 전체를 예외로 — 비밀번호 변경에서 현재 비번
  틀리면 세션 만료로 오인해 /login으로 튕기던 버그 (에러 안내 도달 불가였음)
- Submission (assignment_id,user_id) 유니크 제약 추가(엔티티+운영DB) —
  동시 제출 시 중복 행 → 이후 해당 과제 재제출 영구 500 방지. 자매 엔티티와 일관화

[minor]
- AuthContext: 마운트 시 /auth/me의 늦은 401이 로그인 성공을 덮는 경쟁 → 함수형 업데이트로 보존
- AssignmentsPage: key에 제출 id 포함 → 첫 제출 직후 카드 리마운트로 성공 메시지 소실
  → key를 과제 id로 고정, 메시지는 접히는 영역 밖으로
- CodeEditor: 텍스트 선택 후 Tab 시 커서가 선택 길이만큼 밀림 (selectionEnd+2 → selectionStart+2)
- div.olist>ol 마크업 오류로 목록 번호 이중 표시 — 7개 강좌 10곳 전부 ol.olist로 통일
  (감사가 3파일 발견, 전역 grep으로 4파일 추가 확보)
- SignupPage 검증을 서버 규칙과 일치: 아이디 4자+영숫자, 생년월일 과거만
- LoginRequest @NotBlank + @Valid: username 누락 시 NPE 500 → 400
- server.error.include-message: always — 서버의 한국어 에러 사유가 화면까지 전달
  ('이미 사용 중인 아이디입니다' 등, 그동안 폴백 문구만 보였음)

검증: 테스트 22개 통과, 운영에서 400/409 message/비번변경 에러표시 확인.
각 버그의 원리를 소스 주석에 학습 포인트로 기록.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:42:05 +09:00