[FE-34] 서류 계열 상세 (버전 목록 · 다운로드)
작업 내용 (설계 의도)
근거: 지원 관리 확장 FE 웹 설계 S-21 서류 계열 상세 (버전 목록)·컴포넌트 트리·API 연동 > 2단계·Single Writer per File 검증 > 단계 2 wave 2, 지원 관리 확장 TDD 2단계 — 지원 서류 API 계약(GET /api/documents/series/{seriesId}·GET /api/documents/versions/{versionId}/content).
변경 사항
- 서류 계열 상세(S-21)를 구현합니다. 최신 버전을 상단에 두고 과거 버전을 아래로 쌓는 이력 목록이며, 각 항목의 주 액션은 다운로드 하나입니다.
- 버전 카드는
versionNumber·originalFileName·fileSizeBytes(→utils/fileSize.ts로1.2MB포맷)·uploadedAt을 노출하고, 최신 버전에는 “최신” 표시를 붙입니다. - 다운로드는
<a href="/api/documents/versions/{versionId}/content" download>링크입니다. fetch로 받아 Blob URL을 만들지 않습니다 — 동일 오리진이라 세션 쿠키가 자동 전송되고Content-Disposition: attachment를 브라우저가 처리합니다. Blob 경로는 메모리 사용·에러 처리·파일명 복원을 전부 FE가 떠안게 되므로 이득이 없습니다. fetch를 쓰지 않으므로no-direct-fetch규칙과도 무관합니다. storedRelativePath를text-tertiary로 노출합니다. 로컬 저장 도구라 사용자가 파인더에서 원본을 직접 찾는 동선이 실재하고, 저장 경로 규칙(FR-82)이 그 목적으로 설계됐기 때문입니다. 강조하지 않되 숨기지도 않습니다.isCurrentProfileSource === true인 버전에 “현재 프로필 원본” 칩을 붙입니다. 어느 이력서가 지금 추천도 계산의 근거인지 이 화면에서만 알 수 있습니다. 칩 표현은accent-subtle배경 +accent-on-subtle텍스트로 원색을 쓰지 않습니다.- 404(
DOCUMENT_SERIES_NOT_FOUND)는 일반ErrorState가 아니라 “서류를 찾을 수 없어요” +[서류 목록으로]로 처리합니다 — 재시도해도 결과가 같은 상황에서[다시 시도]만 주면 사용자가 갇힙니다. 그 외 실패는ErrorState+ 재시도입니다. - 버전 삭제·계열 제목 수정 UI는 만들지 않습니다 — 계약에 해당 API가 없습니다(설계 Open Question #5). 동작하지 않을 버튼을 미리 두지 않습니다.
- 계열에는 버전이 최소 1개 있는 것이 계약이므로 0건 빈 상태는 정상 경로가 아니지만, 방어적으로 “버전이 없어요” + 새 버전 CTA를 렌더합니다.
- 새 버전 올리기 CTA는 자리만 만들고 시트 배선은 하지 않습니다.
DocumentUploadSheet는 같은 wave의 FE-33이 소유하므로 연결은 wave 3의 FE-38이 수행합니다.
의존
- FE-30 —
DocumentSeriesDetailResponse·DocumentVersionResponse타입, 계열 상세 queryKey, MSW 목,utils/fileSize.ts. - FE-31 —
/documents/series/:seriesId라우트와 스텁 페이지. 이 티켓이 스텁을 대체합니다. - BE-61 —
GET /api/documents/series/{seriesId}·GET /api/documents/versions/{versionId}/content구현. - FE-33 — 업로드 시트 자체. 단 배선은 FE-38이 하므로 이 티켓의 착수를 막지 않습니다.
다이어그램
처리 흐름
sequenceDiagram participant User as 사용자 participant Page as DocumentSeriesDetailPage participant Hook as useDocumentSeriesDetail participant Api as GET series/{id} participant Browser as 브라우저 User->>Page: 계열 카드 탭 Page->>Hook: seriesId로 조회 Hook->>Api: GET 상세 Api-->>Page: versions[] User->>Browser: 다운로드 링크 클릭 Browser-->>User: attachment 저장
컴포넌트 의존
flowchart LR Page[DocumentSeriesDetailPage] --> Hook[useDocumentSeriesDetail] Hook --> Api[fetchDocumentSeriesDetail] Api --> Types[DocumentSeriesDetailResponse] Page --> Chip[DocumentTypeChip] Page --> Card[DocumentVersionCard] Card --> Size[utils/fileSize] Card --> Link[a download 링크] Card --> Source[현재 프로필 원본 칩]
테스트 케이스
- 버전 3건이 내려오면 최신 버전이 상단에 있고 각 카드에 버전 번호·원본 파일명·
1.2MB형식 용량·업로드 일시가 보인다. - 각 버전 카드의 다운로드가
href="/api/documents/versions/{versionId}/content"+download속성을 가진<a>로 렌더되고, 클릭 시fetch호출이 발생하지 않는다. - 버전 카드에
storedRelativePath가text-tertiary계열 스타일로 노출된다. isCurrentProfileSource: true인 버전에만 “현재 프로필 원본” 칩이 보인다.- 계열 상세 조회가
404 DOCUMENT_SERIES_NOT_FOUND이면 “서류를 찾을 수 없어요” +[서류 목록으로]가 보이고[다시 시도]는 보이지 않는다. - 계열 상세 조회가 5xx이면
ErrorState+[다시 시도]가 보인다. - 버전 배열이 0건이면 “버전이 없어요”와 새 버전 CTA가 보이고 앱이 크래시하지 않는다.
- 버전 삭제 버튼과 계열 제목 수정 UI가 화면에 렌더되지 않는다.
- 로딩 중에는 헤더 스켈레톤과 버전 카드 스켈레톤이 보인다.
- 다운로드 링크가 파일명을 링크 텍스트로 가지고
aria-label에 “다운로드”가 명시된다. - 상세 화면과 버전 카드가
.dark클래스 환경에서 렌더되고 하드코딩 색을 0건 사용한다.