# task-2874 보고서 — 외부 참고 사이트: 국민건강보험공단 추가 + ㄱㄴㄷ 정렬

- 팀: dev2 (오딘)
- 레벨: Lv.1 (webapp-only, merge_policy=none)
- 프로젝트: InsuRo (`insuro.biz/tools/external-sites`)
- PR: https://github.com/Jeon-Jonghyuk/InsuRo/pull/149
- 브랜치: `task/task-2874-dev2` (base=origin/main @477c849)
- worktree: `/home/jay/projects/InsuRo/.worktrees/task-2874-dev2`

## Situation
회장 요청: 외부 참고 사이트 목록에 **국민건강보험공단(https://www.nhis.or.kr/)** 추가 + 목록을 **이름 ㄱㄴㄷ 순 정렬**.

## Complication
데이터=`src/data/external-sites.tsx`의 `sites: SiteItem[]`, 렌더=`src/pages/ExternalSites.tsx`. 정렬은 단일 소스에서만 적용해 이후 항목 추가 시에도 자동 유지되어야 함. 기존 10개 항목·레이아웃 불변 요구.

## Question
2건을 최소 변경으로 구현하고, auth-gated 라우트에서 실동작을 어떻게 검증하는가?

## Answer (변경 내역)

### 1. 국민건강보험공단 추가 — `src/data/external-sites.tsx`
- 1행 import에 `ShieldPlus`(lucide-react, 기존 아이콘과 중복 없음) 추가
- `sites` 배열 끝에 신규 SiteItem 1개 append(원본 순서 불변):
  ```tsx
  { name: "국민건강보험공단", url: "https://www.nhis.or.kr/",
    description: "건강보험 자격·보험료·급여 등 종합 정보",
    icon: <ShieldPlus size={20} />, color: "#2f855a" }
  ```
  - ★ 기존 "국민건강보험 통계연보"(통계 전용)와는 별개 신규 항목

### 2. 이름 ㄱㄴㄷ 정렬 — `src/pages/ExternalSites.tsx`
- 20행: `{sites.map((site) => (` → `{sites.slice().sort((a, b) => a.name.localeCompare(b.name, "ko-KR")).map((site) => (`
- 렌더 시점 slice 정렬(단일 소스). 원본 `sites` 배열 순서는 불변. 이후 사이트 추가돼도 항상 ㄱㄴㄷ 자동 유지.

### 3. 렌더 스모크 테스트(신규) — `src/pages/__tests__/ExternalSites.render.test.tsx`
- `DashboardLayout` passthrough mock 후 `<ExternalSites/>` 렌더
- (1) "국민건강보험공단" 텍스트 존재 + 링크 href == `https://www.nhis.or.kr/`
- (2) 렌더된 h3 제목 순서 == `[...names].sort(localeCompare("ko-KR"))` 와 정확히 일치 + 첫 항목이 원본 배열 첫 항목과 다름(정렬 실제 적용 검증)

## 수정/생성 파일
- 수정: `src/data/external-sites.tsx`
- 수정: `src/pages/ExternalSites.tsx`
- 생성: `src/pages/__tests__/ExternalSites.render.test.tsx`

## 테스트 결과
- `npx tsc --noEmit` → **exit 0, 에러 0건**
- `npx vitest run` → **67 files / 1012 tests passed** (변경 전 66/1010 → 신규 테스트 +1 file/+2 tests, 회귀 0)

## L1 스모크테스트 결과
- 서버 재시작: 성공 (vite dev :5199, `/tools/external-sites` HTTP 200)
- API/브라우저 응답 확인: 실브라우저(Playwright) 접속 시 라우트가 **auth-gated → /login 리다이렉트**(정상 동작). 콘솔 error 0건. 페이지 자체는 인증 필요로 직접 렌더 불가.
- 대체 검증(스크린샷 불가 사유 명시): auth 우회 없이 실페이지 스크린샷 불가하여, **컴포넌트 렌더 테스트**로 L1 대체 — 국민건강보험공단 항목·href 정확, ㄱㄴㄷ 정렬(원본순서와 상이) 실제 렌더 확인(2/2 pass).
- 스크린샷: 해당없음(auth-gated, /login 리다이렉트)

## 발견 이슈 및 해결
- `ExternalSites.tsx:4`의 `type SiteItem` import 미사용 diagnostic(TS6133 hint)은 **원본 파일에 이미 존재하던 pre-existing 항목**(이번 변경과 무관). `tsc --noEmit` 클린 통과(noUnusedLocals 미적용). 범위 최소화 원칙상 미변경.

## 머지 판단
- 머지 필요: Yes (ANU 소유 — merge_policy=none)
- 브랜치: `task/task-2874-dev2`
- 워크트리 경로: `/home/jay/projects/InsuRo/.worktrees/task-2874-dev2`
- 머지 의견: src/(+tests)만 3파일 최소 변경, tsc 클린 + 1012 tests green, 충돌 위험 낮음(단일 데이터/렌더 파일). PR #149 → ANU 독립검증·머지·CF Pages 배포.

## 모델 사용 기록
- 프레이야(프론트, 데이터/렌더 수정): sonnet
- 헤임달(QA, 렌더 테스트): sonnet
- 팀장(오딘, Opus): 설계/검토/통합만, 직접 코딩 없음

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

