diff --git a/apps/sn/app/(with-base-layout)/layout.tsx b/apps/sn/app/(with-base-layout)/layout.tsx
index 9cf3614f4..35a09cf33 100644
--- a/apps/sn/app/(with-base-layout)/layout.tsx
+++ b/apps/sn/app/(with-base-layout)/layout.tsx
@@ -58,4 +58,4 @@ export default function WithBaseLayout({
{children}
);
-}
\ No newline at end of file
+}
diff --git a/apps/sn/app/(with-base-layout)/oper/exmn/register/page.tsx b/apps/sn/app/(with-base-layout)/oper/exmn/register/page.tsx
new file mode 100644
index 000000000..bf2fa6ac3
--- /dev/null
+++ b/apps/sn/app/(with-base-layout)/oper/exmn/register/page.tsx
@@ -0,0 +1,6 @@
+import { ExmnRegistrationPage } from "@/features/exmn/ui/ExmnRegistrationPage";
+
+/** SFR-010-004 사업정보·조사결과 등록 화면 라우트다. */
+export default function Page() {
+ return ;
+}
diff --git a/apps/sn/app/(with-base-layout)/oper/exmn/status/page.tsx b/apps/sn/app/(with-base-layout)/oper/exmn/status/page.tsx
new file mode 100644
index 000000000..e99f6eb4e
--- /dev/null
+++ b/apps/sn/app/(with-base-layout)/oper/exmn/status/page.tsx
@@ -0,0 +1,6 @@
+import { ExmnStatusPage } from "@/features/exmn/ui/ExmnStatusPage";
+
+/** SFR-010-005 조사현황과 처리분구별 조사 상세현황 라우트다. */
+export default function Page() {
+ return ;
+}
diff --git a/apps/sn/app/components/lib/utils/fileUploadUtil.tsx b/apps/sn/app/components/lib/utils/fileUploadUtil.tsx
index fdbab1560..39e38c57c 100644
--- a/apps/sn/app/components/lib/utils/fileUploadUtil.tsx
+++ b/apps/sn/app/components/lib/utils/fileUploadUtil.tsx
@@ -21,6 +21,7 @@ type UploadOptions = {
handleProgress?: (percentage: string) => void;
handleSuccess?: (upload: Upload) => void;
handleResponse?: (res: HttpResponse) => void;
+ handleError?: (error: Error) => void;
};
export const handleUpload = (files: FileList, options?: UploadOptions) => {
@@ -43,6 +44,7 @@ export const handleUpload = (files: FileList, options?: UploadOptions) => {
metadata,
onError(error) {
console.error(`Failed because: ${error}`);
+ options?.handleError?.(error);
},
onProgress(bytesUploaded, bytesTotal) {
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
diff --git a/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/001-qa-SFR-010-004~007-조사사업-통합검증계획.md b/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/001-qa-SFR-010-004~007-조사사업-통합검증계획.md
index 53ce2d632..0c60db9bd 100644
--- a/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/001-qa-SFR-010-004~007-조사사업-통합검증계획.md
+++ b/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/001-qa-SFR-010-004~007-조사사업-통합검증계획.md
@@ -41,7 +41,7 @@
- 자동: TypeScript, `git diff --check`, Next 빌드
- 수동: `/oper/trtdst` 진입, 헤더 정렬, 선택 행 유지, 검색조건 간격, 버튼 우측 정렬·글자색, 모달 입력, 행 상태 색, 메시지 표시
- 통합: 실제 처리분구 API와 409 참조 제약, 저장 후 사업등록 후보 즉시 갱신
-- 백엔드 미구현 동안 통합 항목은 보류로 기록하고 성공 데이터 목업으로 대체하지 않는다.
+- 백엔드 미구현 동안 실 API 통합 항목은 보류로 기록한다. 사용자 승인 샘플은 `sample=Y`에만 격리하고 일반 화면 성공 데이터로 대체하지 않는다.
## 6. 다음 검증 순서
@@ -54,3 +54,85 @@
## 7. 흡수한 기존 문서
- 없음
+
+## 8. FE-1 검증 계획
+
+| 구분 | 시나리오 | 기대 결과 |
+| --- | --- | --- |
+| 목록 | 조건 조회·페이지 이동·헤더 정렬 | 적용 조건 유지, 0-base page와 단일 sort 전달 |
+| 선택 | 미선택 상태에서 결과등록/PPL 상세 | 선택 안내 후 popup 진입 차단 |
+| 팝업 체계 | 사업등록·결과등록 클릭 | 공통 `/popup/[popupKey]` 윈도우가 열리고 차단 시 안내 |
+| 사업등록 | 필수값·연도·음수·처리분구 미선택 | 저장 전 항목별 오류 표시 |
+| 처리분구 | 행정구역 선택/변경 | 같은 지역 `useYn=Y` 후보 조회, 이전 선택 초기화 |
+| 양식 | 저장 전/후 `양식 받기` | 저장 전 안내, 저장 후 생성과 다운로드를 한 번에 수행 |
+| 사업등록 입력 | 사업등록 popup 확인 | 첫 줄에 사업년도·시도·시군구·정밀조사연장이 표시되고 연장 라벨은 한 줄 유지 |
+| 처리분구 목록 | 처리분구 후보가 많은 사업등록 popup | 3열 7행 21개까지 표시하고 22개부터 목록 내부 세로 스크롤 |
+| 정밀조사연장 | 입력칸 확인·숫자 입력 | 증감 스피너 없음, 숫자·소수점만 입력, 최대 10자 폭 |
+| 결과등록 요약 | 결과등록 popup 진입 | 사업차수·사업년도·행정구역·사업명 순서로 적정 간격을 두고 한 줄 표시 |
+| 결과등록 파일 목록 | 파일 5종 영역 확인 | 연한 외곽선·헤더·행 구분선만 표시되고 강한 세로 격자 없음 |
+| 결과등록 스크롤 | 최초 popup 진입 | 가로·창 세로 스크롤 없음, 검토결과 초과 시 본문 내부 세로 스크롤만 표시 |
+| 결과등록 행 높이 | 파일 5종 최초 표시 | 파일 선택 영역이 남는 세로 공간을 활용하되 창 스크롤은 만들지 않음 |
+| 결과등록 초기 크기 | 결과등록 버튼으로 신규 popup 진입 | 콘텐츠 설정 `980×340`, Chrome 외곽 포함 약 `980×410` |
+| 파일 열 정렬 | 헤더와 5개 파일 행 비교 | 찾기·데이터 검토 헤더와 버튼 중심선 일치 |
+| 파일 선택 표시 | 파일 선택 전후 비교 | 선택 파일명은 파란색 굵은 글씨와 옅은 파란 배경으로 변경 |
+| Hydration | 사업등록 popup 최초 진입 | Select `aria-controls` 불일치 경고 없이 client mount 후 표시 |
+| 파일 1차 검사 | 빈 파일·200MB 초과·확장자 오류 | 서버 전송 전 차단 |
+| 파일 검토 | PPL/MNHL 미선택·정상·업무 오류 | 최소 1개 요구, 정상 요약 또는 위치 포함 오류 표시 |
+| 최종 저장 | 검토 전/후·Tus 실패·commit 실패 | 검토 전 비활성, 실패를 성공 처리하지 않고 원인 유지 |
+| PPL 집계 | 결함 건수 클릭 | 처리분구·결함 분류로 시설 목록 연결 |
+| PPL 상세 | 시설·단위 선택 | 정규화 unit/detail 표시, STATUS/DEFECT 구분 |
+| 보고서 | 시설 상세 보고서 버튼 | 매핑 미확정 안내와 비활성 유지 |
+| 스타일 | 신규 route·모달 Tailwind | `src/features` 클래스가 생성돼 레이아웃 적용 |
+| 상단 구성 | 검색조건·조회·사업등록·결과등록 | 한 카드에 표시되고 사업명 입력 폭이 과도하지 않음 |
+| 공통 선택 | 사업년도·시도·시군구 | 연도 선택과 I/I 지역 선택 재사용, 지역 기본값 고정 없음 |
+| 목록 열 | 사용자 확정 열 순서·단위 | NO부터 보고서까지 9개 열과 km·개소 단위 일치 |
+| 버튼 | 상단 업무 버튼 | `PPL 집계·상세` 직접 버튼 미표시 |
+| 샘플 격리 | 일반 URL / `?sample=Y` | 일반 URL은 API만 사용, sample URL만 샘플 사업·처리분구·검토결과 표시 |
+
+## 9. FE-1 수동 확인 경로
+
+1. 메뉴 연결 URL을 `/oper/exmn/register`로 등록하거나 주소로 직접 진입한다.
+2. 조회조건과 빈 사업 목록 레이아웃을 확인한다.
+3. 백엔드 데이터가 없으면 개발 환경의 `샘플 데이터 보기`를 눌러 `sample=Y` 샘플 사업목록을 확인한다.
+4. `사업등록` WindowPopup을 열어 행정구역·가나다순 처리분구 체크박스·필수값·양식 버튼을 확인한다.
+5. 사업 행 선택 후 `결과등록` WindowPopup의 파일 5종, 찾기, 데이터 검토, 저장 활성 조건을 확인한다.
+6. 상단에 `PPL 집계·상세` 직접 버튼이 없고 목록 열 순서·단위가 확정안과 일치하는지 확인한다.
+7. 일반 URL의 백엔드 미구현 오류는 화면 오류 메시지로 표시되는지 확인하고 샘플 성공으로 오해하지 않는다.
+
+## 10. 조사현황·집계 1차 검증 계획
+
+| 구분 | 시나리오 | 기대 결과 |
+| --- | --- | --- |
+| 진입 | `/oper/exmn/register`, `?sample=Y` | 기존 통합 화면 유지, 일반 URL은 실 API, sample URL만 샘플 데이터 사용 |
+| 목록 | 조건 조회·페이지·정렬·행 선택 | 등록 화면과 같은 조건 계약, 선택 행 강조 |
+| 상세현황 | 사업 행의 어느 데이터 셀 선택 | 하단 조사 상세현황 표시, 상단과 중복되는 선택 사업 요약 띠는 표시하지 않음 |
+| 선택 전 상태 | 최초 진입·재조회로 선택 해제 | 조사 상세현황 영역 자체가 표시되지 않음 |
+| 명칭 | 조사유형 탭 확인 | `관로조사`, `맨홀조사`로 표시 |
+| 관로 집계 | 관로조사 탭 | 대상연장·주행거리·전체·구조·운영·기타와 합계 표시 |
+| 맨홀 집계 | 맨홀조사 탭 | 전체·구조·운영·기타와 합계 표시 |
+| 버튼 상태 | 미선택/선택 상태 | 미선택 시 상세 패널 미표시, 선택 후 세 버튼 표시·미연결 안내 |
+| 보류 경계 | 상세보기·지도보기 클릭 | 상세 화면·지도 이동 없이 후속/보류 안내 |
+
+- 원본 PPT의 시각 배치 확인 후 실제 브라우저에서 상·하단 카드 높이와 열 줄바꿈을 사용자 재확인한다.
+- 상세보기 실제 화면, 엑셀 파일, 지도 컨텍스트 전달은 이번 1차 완료 기준에서 제외한다.
+- 로그인된 사용자 화면에서 목록 전체 셀 선택, 관로·맨홀 전환, 목록·상세 배치와 집계 표시를 확인해 조사관리 상세현황 1차 수동 검증은 완료했다.
+
+## 11. 조사사업 목록·상세현황 최종 회귀 계획
+
+| 구분 | 확인 방법 | 기대 결과 |
+| --- | --- | --- |
+| 검색조건 | 사업차수·사업연도·시도·시군구를 차례로 변경 후 조회 | 조건 간격이 자연스럽고 기존 연도·행정구역 연동 동작 유지 |
+| 행 선택 | NO부터 보고서까지 각 열의 빈 공간·텍스트 주변 선택 | 어느 위치에서도 같은 행의 상세현황 표시, 선택 행 강조 |
+| 정렬 | 각 정렬 헤더를 3회 연속 선택 | 오름차순→내림차순→기본 순환, 공통 아이콘 상태 일치 |
+| 데이터 원천 | 일반 URL과 `sample=Y`에서 같은 열 정렬 | 서버 결과와 샘플 결과가 같은 값 비교 규칙 사용 |
+| 목록 높이 | 샘플 사업 4건 확인 | 3행은 하단선까지 온전히 표시, 4번째부터 목록 내부 스크롤 |
+| 폭·경계 | 목록과 상세 좌우 끝선 및 스크롤 상태 비교 | 같은 가로 폭·여백, 상·하단 구분선 잘림 없음 |
+| 조사유형 | 관로·맨홀 버튼의 가장자리와 중앙 선택 | 보이는 button 전체가 클릭되고 해당 유형 집계 재조회 |
+| 요청 순서 | 사업·조사유형을 연속으로 빠르게 변경 | 마지막 선택의 응답만 화면에 반영 |
+| 합계 | 관로·맨홀 합계 행 DOM·화면 확인 | NO+처리분구 실제 병합, 중앙 정렬, 배경 `#d2deef` |
+| 상세 스크롤 | 처리분구 10행을 위아래로 스크롤 | 헤더 고정, 다음 행 일부 노출 없음, 경계선 유지 |
+| 페이지 높이 | 상세 표시 전후·Footer 높이 변화·화면 이탈 확인 | 문서 전체 세로 스크롤 없음, 내부 스크롤 사용, 이탈 시 overflow 복원 |
+| 공통 회귀 | 다른 BaseLayout 화면 진입 | 공통 Header·Footer와 레이아웃 동작 변화 없음 |
+| 팝업 회귀 | 사업등록·결과등록 WindowPopup 재진입 | 확정된 팝업 크기·배치·버튼·처리분구·파일행 디자인 유지 |
+
+- 상세보기·다운로드 실제 동작, 지도 이동, 실 API 성공 응답은 이번 화면 회귀의 완료 범위에 포함하지 않는다.
diff --git a/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/002-qa-SFR-010-004~007-조사사업-통합검증결과.md b/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/002-qa-SFR-010-004~007-조사사업-통합검증결과.md
index 18687bc4e..33ade2c2c 100644
--- a/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/002-qa-SFR-010-004~007-조사사업-통합검증결과.md
+++ b/apps/sn/docs/작업검증/SFR-010-004~SFR-010-007-조사사업/002-qa-SFR-010-004~007-조사사업-통합검증결과.md
@@ -21,6 +21,38 @@
| 2026-07-22 | FE-0 UI 1차 사용자 재확인 | 시도·시군구 간격 미적용, 조회 버튼이 저장 버튼보다 약 `0.6rem` 왼쪽임을 캡처로 확인 |
| 2026-07-22 | FE-0 UI 2차 정적 확인 | `.next` 생성 CSS에 `gap-10`·`col-span-12`가 없음을 확인하고 인라인 `2rem` 간격·고정 폭·조회 버튼 `0.6rem` 이동 적용, UTF-8·공백 검사 통과 |
| 2026-07-22 | FE-0 최종 사용자 확인 | 처리분구관리 화면과 2차 UI 보정이 정상임을 사용자가 확인, 화면 단계 완료 판정 |
+| 2026-07-22 | FE-1 import·Hook·API 경로 정적 검사 | 상대 import 누락과 client 경계 누락 없음, 승인된 SFR-010-004 API 경로 확인 |
+| 2026-07-22 | FE-1 공백·한글 검사 | 신규/수정 대상 후행 공백 없음, 한글 본문 UTF-8 재조회 정상 |
+| 2026-07-22 | FE-1 `git diff --check` | 추적 파일 통과, 신규 파일은 별도 후행 공백 검사 통과 |
+| 2026-07-22 | FE-1 TypeScript 실행 | pnpm 비대화형 의존성 정리 중단 및 compiler 파일 샌드박스 `EPERM`으로 결과 미확보 |
+| 2026-07-22 | FE-1 Tailwind 경로 확인 | 기존 content에 `src/features`가 없음을 확인하고 `./src/**/*` 추가 |
+| 2026-07-22 | FE-1 브라우저·실 API | 3002 서버 미실행·백엔드 미구현으로 사용자 화면 확인 및 통합 단계 대기 |
+| 2026-07-22 | FE-1 사용자 1차 화면 확인 | 연도·지역 선택 컴포넌트, 사업명 폭, 상단 영역 분리, 불필요 PPL 버튼, 목록 열 순서 보완 요청 확인 |
+| 2026-07-22 | FE-1 사용자 1차 보정 정적 확인 | 기존 연도 옵션·I/I 지역 선택 재사용, 상단 단일 카드, PPL 버튼 제거, 9개 열 순서·단위 반영 |
+| 2026-07-22 | FE-1 WindowPopup 전환 정적 확인 | popup key·registry·content·부모 메시지, 처리분구 가나다순 체크 선택, 결과 파일 5종 확인 |
+| 2026-07-22 | FE-1 샘플 경로 Next 개발 서버 확인 | `/oper/exmn/register?sample=Y`, 사업등록 popup, 결과등록 popup 모두 HTTP 200 |
+| 2026-07-22 | FE-1 사업등록 2차 보정 | popup `920×620`, 5개 입력, 버튼 구성·green 스타일, client mount hydration 방어 정적 확인 |
+| 2026-07-22 | FE-1 샘플 전환·라우트 재확인 | 일반 목록, 샘플 목록, 사업등록 샘플 popup, 결과등록 샘플 popup 모두 HTTP 200 |
+| 2026-07-22 | FE-1 사업등록 3차 배치 보정 | 정밀조사연장 첫 줄 이동, 처리분구 높이 `20rem` 확장, 샘플 배지 제거 및 popup HTTP 200 확인 |
+| 2026-07-22 | FE-1 사업등록 4차 배치 보정 | 정밀조사연장 한 줄·입력 `7rem`, 처리분구 높이 `21rem`, 샘플 21개 및 초과 스크롤 구조 확인 |
+| 2026-07-22 | FE-1 팝업 5차 보정 | 연장 스피너 제거·최대 10자, 결과 popup `980×620`, 4개 사업요약·경계선 table, 샘플 배지 제거 및 두 popup HTTP 200 |
+| 2026-07-22 | FE-1 결과 popup 6차 보정 | `980×460`, 닫기 제거, 강한 격자 제거, 창 스크롤 차단·본문 조건부 스크롤 및 HTTP 200 확인 |
+| 2026-07-22 | FE-1 결과 popup 7차 보정 | html/body 문서 스크롤 잠금·복원, 업로드 행 높이 확대 및 HTTP 200 정적 확인 |
+| 2026-07-22 | FE-1 결과 popup 최종 크기 보정 | 두 캡처 외곽 높이 차이를 반영해 definition `980×340`으로 조정 |
+| 2026-07-22 | FE-1 결과 popup 최종 UI 보정 | 헤더·행 `gap-3` 통일, 선택 파일명 파란색·굵게 표시 및 HTTP 200 확인 |
+| 2026-07-22 | FE-1 결과 사업요약 순서 보정 | 사업차수→사업년도→행정구역→사업명, 비율 열·`gap-8` 적용 |
+| 2026-07-22 | FE-1 사업·결과 WindowPopup 사용자 확인 | 사업등록과 조사결과 업로드 최종 UI 완료 판정 |
+| 2026-07-23 | 조사현황 목록·상세 스크롤 보정 정적 확인 | 가로 여백 `1.2rem`, 전체 셀 행 선택, 양쪽 내부 스크롤 및 목록 페이지네이션 제거 확인 |
+| 2026-07-23 | 변경 파일 공백·UTF-8 검사 | `git diff --check` 통과, 대상 코드·문서 UTF-8 확인 |
+| 2026-07-23 | 조사현황 브라우저 재검증 | 3002 개발 서버 미실행으로 미수행 |
+| 2026-07-23 | 조사현황 사용자 재확인 | 목록·상세 실제 가로 경계 불일치, 상세 하단 여백, 일부 열만 클릭되는 문제 확인 |
+| 2026-07-23 | 조사현황 2차 구조 보정 | 동일 외부 스크롤 래퍼·stable gutter, 상세 남은 높이 사용, 전체 열 기존 `cellClick` 적용 |
+| 2026-07-23 | 조사현황 사용자 3차 재확인 | 목록 3행에서도 스크롤·하단선 잘림, 행·탭의 시각 영역과 실제 클릭 영역 불일치 확인 |
+| 2026-07-23 | 조사현황 3차 구조 보정 | 목록 `18rem`, 공통 표 `
` 단위 `rowClick`, 80×32px 실제 button 탭, 상세 샘플 10행 적용 |
+| 2026-07-23 | 조사현황 사용자 4차 재확인 | 정밀조사 연장 우측 일부부터 보고서 영역만 선택되고 앞쪽 셀은 클릭되지 않아 공통 `rowClick` 방식 실패 판정 |
+| 2026-07-23 | 조사현황 4차 클릭 구조 교체 | 목록을 공통 Table 스타일의 화면 전용 표로 교체하고 모든 데이터 셀을 전체 면적 button으로 구성, 공통 `rowClick` 제거 |
+| 2026-07-23 | 조사관리 상세현황 사용자 최종 확인 | 전체 행 선택, 관로·맨홀 탭, 목록·상세 배치와 상세 집계 표시 확인 완료. 조사관리 상세현황 화면 단계 완료 판정 |
+| 2026-07-23 | git status·문서 정합성 점검 | 추적 코드 8개·신규 코드 15개와 FE 문서 4개를 대조하고 작업카드·구현·검증 문서의 단계 상태와 실제 파일 목록을 동기화 |
## 3. FE-0 검증 결과
@@ -47,7 +79,11 @@
- FE-0 프로덕션 빌드: 미수행
- FE-0 화면 렌더링·최종 UI: 사용자 확인 통과, 화면 단계 완료
- FE-0 실 API: 백엔드 구현 대기
-- FE-1~FE-4: 미착수
+- FE-1 코드: 사업등록·결과등록 WindowPopup과 조사관리 상세현황 UI 구현·개발 서버 컴파일·사용자 확인 완료, 실 API 검증 대기
+- 조사현황·관로·맨홀 종합집계 화면: 사용자 확인 통과, 화면 단계 완료
+- 상세보기 실제 화면·다운로드·지도보기: 미착수/보류
+- 사용자 확인 기준 화면 1~3단계: 완료
+- 공식 요구사항 기준 FE-3 상세 기능과 FE-4 GIS: 미착수. 현재 맨홀은 종합집계 UI까지만 완료
## 6. 보류/리스크
@@ -55,6 +91,75 @@
- 메뉴 원천 등록과 화면 진입은 해결됐지만 최종 권한별 노출·직접 URL 접근 제어는 통합 검증에서 확인해야 한다.
- FE-0 화면·UI는 사용자 확인으로 완료됐지만 CRUD 실 API의 성공·오류·409 처리는 백엔드 구현 후 확인해야 한다.
- 프로덕션 빌드는 다음 프론트 단계 시작 전 또는 사용자가 허용한 시점에 다시 실행한다.
+- FE-1 multipart 필드명과 응답 DTO는 백엔드 구현 시 최종 대조해야 한다.
+- TypeScript 실행 결과를 확보하지 못했으므로 사용자 개발 서버의 Next 컴파일 결과를 FE-1 단계 종료 전에 확인해야 한다.
+
+## 8. FE-1 정적 검증 결과
+
+- `/oper/exmn/register/page.tsx`는 client UI를 직접 import해 FE-0의 서버/클라이언트 배럴 오류를 반복하지 않는다.
+- 사업 조회조건은 draft/applied로 분리되고 페이지·정렬 요청에도 적용 조건을 유지한다.
+- 사업등록은 활성 처리분구 조회와 필수값·연도·음수·선택 검증을 저장 전에 수행한다.
+- 파일 변경 시 기존 검토 결과를 폐기하고, 정상 검토 전 최종 저장 버튼을 비활성화한다.
+- Tus 업로드 실패 콜백이 Promise reject와 화면 오류로 연결된다.
+- PPL 상세는 `PPL_UNIT_V1` 정규화 계약의 단위·상태·결함 필드를 렌더링하고 보고서 미확정 기능은 활성화하지 않는다.
+- 일반 URL의 런타임 성공 목업과 자동 fallback은 없다. 사용자 승인 샘플은 `sample=Y`에서만 명시적으로 격리한다.
+- 사용자 1차 보정으로 직접 연도 입력과 독립 지역 셀렉트를 제거하고 기존 `getStartYearArray`·`CtpvSggPrcsFcltSelectBox`를 재사용했다.
+- 상단 검색조건과 3개 액션을 하나의 `SearchDiv`에 두고 `PPL 집계·상세` 직접 버튼을 제거했다.
+- 목록은 사용자 확정 9개 열로 재배치했으며 총사업비는 등록 필드로만 유지한다.
+- 사업등록·결과등록은 공통 `windowPopup`의 definition→registry→content 흐름으로 열리고 완료 메시지는 동일 origin 부모창에만 반영한다.
+- 사업등록 처리분구는 한글 가나다순 체크박스이며, 결과등록은 캡처 기준 파일 5종과 행별 찾기·데이터 검토 구조를 사용한다.
+- 샘플 URL 세 경로는 개발 서버에서 HTTP 200을 확인했고, 사업등록·결과등록의 클릭·선택·팝업 크기·실제 시각 배치는 사용자 확인을 통과했다.
+- 일반 목록의 개발 전용 `샘플 데이터 보기`는 `sample=Y`로 재진입하며, 자동 성공 fallback 없이 샘플 사업 선택과 결과등록 진입을 가능하게 한다.
+- popup content의 첫 서버 출력에는 Radix Select를 렌더링하지 않고 client mount 후 화면을 구성해 보고된 hydration ID 불일치를 차단했다.
+
+## 9. 조사현황·집계 1차 정적 검증 결과
+
+- 원본 화면설계서 슬라이드 3·4를 이미지로 렌더링해 상단 사업목록, 행 선택, 하단 조사 상세현황, 두 조사유형 탭과 세 버튼 위치를 대조했다.
+- 신규 route는 client UI를 직접 import해 Hook 포함 feature 배럴이 서버 컴포넌트 import 경계에 들어가지 않게 했다.
+- 일반 모드와 `sample=Y`를 분리했고 샘플은 사업별 처리분구 수에 맞춘 관로·맨홀 집계만 반환한다.
+- `CCTV조사` 사용자 표출은 신규 조사현황 화면에 사용하지 않고 `관로조사`, `맨홀조사`로 구성했다.
+- 세 버튼은 실제 상세·파일·지도 동작을 성공으로 가장하지 않고 미연결 또는 보류 안내만 제공한다.
+- 첫 구현의 별도 조사현황 목록을 제거하고 `ExmnStatusDetailPanel`을 기존 `ExmnRegistrationPage` 목록 아래에 결합해 PPT 3·4의 동일 화면 선택 전·후 구조로 수정했다.
+- 선택 전에는 패널을 렌더링하지 않고 사업 행 선택 후에만 같은 화면 하단에 패널을 추가한다.
+- 사용자 캡처에서 불필요한 선택 사업 요약 띠를 제거하고 조사유형을 공통 `UITab`으로 교체했다. 후속 보정에서는 분석도구의 활성 메뉴 느낌과 맞도록 기본 파란 활성 탭을 컴팩트하게 적용했다.
+- 합계 행은 앞의 두 열을 `colSpan=2`로 병합하고 연한 배경·굵은 글씨를 적용해 일반 처리분구 행과 구분했다.
+- 기본 `Table`의 외부 스크롤과 sticky `thead` 조합에서 스크롤 중 행 일부가 잘려 보이는 브라우저 문제가 사용자 캡처로 확인됐다.
+- 집계표를 공통 `UiTable`로 교체하고 내부 표 래퍼를 유일한 스크롤 컨테이너로 지정했다. 사용자 캡처에서 `16.2rem`은 데이터 3번째 행의 하단선을 자르는 것이 확인되어 `16.7rem`으로 보정했다.
+- 실행 중인 개발 서버의 `/oper/exmn/register?sample=Y` 요청은 변경 후 HTTP 200을 반환했다. 페이지 전체 스크롤 제거와 그리드 내부 스크롤의 최종 시각 확인은 사용자 재확인 대상으로 남긴다.
+- 후속 캡처에서 남은 페이지 스크롤의 원인을 `BaseLayout`의 `calc(100vh - 50px)`와 실제 `6rem` 헤더 높이 차이로 확인했다. 최초 경로 전용 CSS는 실제 SSR DOM에 존재하는 `group/sidebar-wrapper` 중간 래퍼를 건너뛰어 매칭되지 않았고, 선택자를 `.sn-exmn-base-layout > div > .flex-1 > .flex > .flex-1`로 정정했다.
+- 합계 표시는 관로 열 비율 `6%+18%`, 맨홀 열 비율 `8%+28%`를 기준으로 첫 두 열 전체 중앙에 오도록 표시 영역을 확장했다.
+- 보정 후 샘플 경로 HTTP 200, 초기 HTML의 `sn-exmn-base-layout` 및 `group/sidebar-wrapper` DOM 연결, 개발 CSS의 정확한 전용 선택자와 `max-height: 16.7rem` 생성을 확인했다. 실제 브라우저의 우측 스크롤 제거와 3번째 행 하단선 표시는 사용자 최종 확인 대상으로 남긴다.
+- 상세현황 표시 여부와 무관하게 조사사업 본문·Footer 합계가 뷰포트 높이를 넘지 않도록 route 전용 고정 높이와 flex 축소 구조를 적용했다. Next 개발 서버 생성 CSS에서 `height: calc(100dvh - 6rem)`을 확인했다.
+- 공통 `UiTable`의 `rowClick` 코드와 선택 상태가 Next 서버 번들에 생성된 것은 확인했으나 사용자 재확인에서 전체 열 클릭을 보장하지 못했다. 해당 방식은 검증 실패로 판정하고 기존 `cellClick` 전체 열 연결로 교체했다.
+- 3002 포트에 개발 서버 listener가 없어 신규 route의 HTTP 렌더 검증은 확보하지 못했다.
+- 저장소 로컬 TypeScript compiler는 샌드박스 `EPERM` 후 권한 요청이 허용되지 않아 실행 결과를 확보하지 못했다. 대상 소스의 필수 `UiTable` props와 import·client 경계는 정적으로 확인했다.
+- 목록과 상세현황의 동일 폭 보정 과정에서 전역 `sn-exmn-grid-scroll`을 사용했으나, 팝업 디자인 회귀 보고 후 해당 전역 클래스를 제거했다. 최종안은 각 조사 그리드의 인라인 `scrollbarGutter: "stable"`만 사용한다.
+- 목록은 기존 `cellClick`을 전달하고 `clickableHeadIds`를 지정하지 않으므로 공통 `UiTable` 계약상 모든 데이터 셀이 클릭 가능 대상이다. 선택 함수는 표시 행 인덱스로 원본 사업을 찾아 `selectedBusinessId`를 갱신한다.
+- 조사사업 목록은 `page=0`, `size=1000`으로 조회하고 `total={0}`으로 페이지 이동 UI를 숨긴다. 목록만 `16.7rem` 상한을 두고 상세 집계 래퍼는 `flex-1`로 남은 세로 공간을 사용한다.
+- `git diff --check`와 UTF-8 검사는 통과했고 개발 서버 산출물에 신규 스크롤 class와 전체 셀 callback이 포함된 것을 확인했다. 로그인 세션이 없는 검증 브라우저는 `/auth/login`으로 이동하므로 실제 선택 동작과 최종 픽셀 정렬은 사용자 세션에서 재확인이 필요하다.
+- 3차 `UiTable.rowClick` 보정도 사용자 화면에서 전체 행 선택을 보장하지 못해 실패로 판정했다. 4차 보정에서는 목록만 화면 전용 공통 Table 스타일로 교체하고, 생성 번들에서 각 셀의 `min-h-[4rem] w-full` button, `
` 보조 클릭, `18rem` 높이와 기존 정렬 순환을 확인했다.
+- 탭은 생성 번들에서 `tablist`, `aria-selected`, 각 `w-[8rem] h-[3.2rem]` button과 직접 `setJobType` 호출을 확인했다.
+- 당시 샘플 번들의 세 사업 `trtdstCnt: 10`을 확인했다. 이후 목록 내부 스크롤 검증을 위해 네 번째 사업 샘플을 추가했다. 개발 서버 라우트는 빌드 오류 없이 렌더링됐으나 검증 브라우저에 로그인 세션이 없어 실제 샘플 10행과 클릭 동작의 사용자 화면 확인은 남아 있었다.
+- 이후 사용자가 로그인된 실제 화면에서 전체 행 선택, 관로·맨홀 탭, 목록·상세 배치와 집계 표시를 최종 확인해 앞선 브라우저 세션 제한을 보완했다.
+
+## 10. 조사사업 목록·상세현황 최종 검증 결과
+
+- `ExmnRegistrationPage`에서 검색조건 간격과 사업차수·사업연도·시도·시군구 선택 연결을 확인했다.
+- 모든 데이터 셀을 채우는 실제 button과 `
` 보조 선택 함수, `data-state=selected`를 확인해 전체 행 클릭과 선택 행 강조 계약을 확인했다.
+- 정렬 상태 전이가 `asc → desc → undefined`이며 서버 요청과 샘플 배열이 같은 열 키·비교 함수를 사용하는 것을 확인했다.
+- `EXMN_SAMPLE_BUSINESSES`에 `SAMPLE-BIZ-004`가 추가됐고 목록 인라인 높이 제한과 내부 스크롤로 3행 이후 데이터를 확인하는 구조를 확인했다.
+- 목록과 상세에 동일한 인라인 스크롤바 예약과 좌우 기준이 적용됐고 sticky 헤더·하단 보정선이 있는 것을 확인했다.
+- `ExmnStatusDetailPanel`의 관로·맨홀 선택은 각각 전체 `80×32px`가 실제 button이며 선택 변경 시 집계를 재조회한다.
+- `summaryRequestIdRef` 비교로 과거 성공·오류·loading 응답이 최신 사업·탭 선택을 덮어쓰지 않는 것을 확인했다.
+- 합계 행은 실제 `colSpan=2`, 중앙 정렬, 전 셀 `#d2deef` 배경을 사용한다.
+- 상세 헤더는 sticky이고 상세 영역은 인라인 `maxHeight: 23.8rem`과 내부 세로 스크롤을 사용한다. 다음 행 일부가 하단에 노출되지 않도록 목록과 같은 높이 제한 방식을 적용했다.
+- 공통 `layout.tsx`는 `BaseLayout → PageLayout` 구조를 유지한다. 페이지 높이 계산, Footer `ResizeObserver`, 문서 overflow 잠금·복원은 `ExmnRegistrationPage` 내부에만 존재하며 공통 Header·Footer 컴포넌트는 수정하지 않았다.
+- 공통 `UiTable`의 `noOuterMargin`과 선택 행 상태는 소스에 남아 있으나 조사사업 목록·상세현황 최종 표는 병합·클릭·스크롤 제어를 위해 직접 구성한 테이블임을 확인했다.
+- `components.css`의 의미 있는 diff가 제거됐고 조사 그리드 gutter는 두 화면 컴포넌트에만 인라인으로 적용된다.
+- 완료 화면 캡처와 대조해 `tailwind.config.ts`의 `./src/**/*` 누락이 결과등록 grid와 사업등록 임의 폭 클래스 미생성 원인임을 확인했다. 스캔 경로를 복구하고 핵심 grid·폭을 인라인 스타일로 이중 고정했다.
+- 사업등록은 `920×620`, 처리분구 `21rem`, 사업명 최대 `480px`; 결과등록은 `980×340`, 사업요약 4열과 파일행 `180px / 가변 / 80px / 112px` 계약으로 복구했다.
+- 사용자의 로그인된 실제 화면 최종 확인으로 행 전체 선택, 관로·맨홀 전환, 목록·상세 배치와 상세 집계 표시를 완료 판정했다.
+- 남은 검증은 실 API/백엔드 연동과 후속 상세보기·다운로드 구현이다. 지도보기는 보류다.
## 7. 흡수한 기존 문서
diff --git a/apps/sn/docs/작업계획/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-작업카드.md b/apps/sn/docs/작업계획/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-작업카드.md
index b053bd50a..d85d4ca1f 100644
--- a/apps/sn/docs/작업계획/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-작업카드.md
+++ b/apps/sn/docs/작업계획/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-작업카드.md
@@ -4,7 +4,7 @@
- 작업 ID: `SFR-010-004~007-EXMN-NEW-FE-FIRST`
- 작업명: CCTV·맨홀 조사사업 프론트엔드 선행 구현
-- 상태: 진행(`FE-0` 사용자 검증 완료, `FE-1` 시작 대기)
+- 상태: 화면 1~3단계 완료·커밋 대기(상세보기 실제 화면·다운로드·실 API 후속, 지도보기 보류)
- 작성일: 2026-07-22
- 담당 역할: FE
- 관련 역할: BE, QA
@@ -22,11 +22,22 @@
| 단계 | 범위 | 현재 상태 | 다음 단계 진입 조건 |
| --- | --- | --- | --- |
| FE-0 | 공통 타입·API, 처리분구관리 `/oper/trtdst` | 완료(사용자 화면·UI 검증 통과) | 충족 |
-| FE-1 | `SFR-010-004` 사업등록·파일검토·PPL 집계/상세 | 시작 대기 | 사용자 진행 지시 |
-| FE-2 | `SFR-010-005` 조사현황·지도 진입 | 대기 | FE-1 완료 |
-| FE-3 | `SFR-010-006` MNHL 집계·상세 | 대기 | FE-2 완료 |
+| FE-1 | `SFR-010-004` 사업등록·파일검토·PPL 집계/상세 | 부분 완료 | 사업등록·결과등록·종합집계 UI 확인 완료, 실제 상세보기·단계 커밋 대기 |
+| FE-2 | `SFR-010-005` 조사현황·지도 진입 | 부분 완료 | 같은 화면 조사현황 완료, 지도 진입 보류 |
+| FE-3 | `SFR-010-006` MNHL 집계·상세 | 부분 구현 | 맨홀 종합집계 UI 완료, 실제 상세보기 대기 |
| FE-4 | `SFR-010-007` GIS 레이어·등급 필터·상세 | 대기 | FE-3 완료 |
+### 3.1 사용자 확인 기준 화면 단계
+
+| 화면 단계 | 완료 내용 | 판정 |
+| --- | --- | --- |
+| 1단계 | 처리분구관리 조회·추가·수정·삭제, 시도·시군구 조건, 그리드 UI | 완료 |
+| 2단계 | 조사사업 검색·정렬·내부 스크롤, 행 선택·강조, 사업등록·결과등록 WindowPopup, `sample=Y` 데이터 | 완료 |
+| 3단계 | 같은 화면 하단의 관로·맨홀 조사 전환, 처리분구별 종합집계·합계, 상세 내부 스크롤, 요청 순서 보호 | 완료 |
+
+- 위 1~3단계는 화면 구현 체크포인트다. 공식 요구사항 단계는 실제 상세보기·다운로드·지도 및 백엔드 연동 범위를 포함하므로 `FE-1~FE-3`을 모두 완료로 판정하지 않는다.
+- 다음 화면 작업은 `상세보기` 실제 화면이며, `지도보기`는 보류한다.
+
## 4. FE-0 수정 대상
| 구분 | 경로 | 결과 |
@@ -68,3 +79,93 @@
- 신규 `/oper/trtdst` 라우트와 `src/features/exmn`을 제거한다.
- `app/components/lib/api/index.ts`의 `putData` 추가분만 되돌린다.
- 기존 화면·공통 비즈니스 컴포넌트에는 롤백 대상 변경이 없다.
+
+## 9. FE-1 수정 대상
+
+| 구분 | 경로 | 결과 |
+| --- | --- | --- |
+| 라우트 | `app/(with-base-layout)/oper/exmn/register/page.tsx` | 신규 |
+| 화면 | `src/features/exmn/ui/ExmnRegistrationPage.tsx` | 사업 목록·공통 WindowPopup 진입 |
+| 사업등록 | `src/features/exmn/ui/ExmnBusinessRegisterPopupPage.tsx` | 처리분구 체크 선택·가나다순·저장·양식 |
+| 파일검토 | `src/features/exmn/ui/ExmnResultRegisterPopupPage.tsx` | 파일 5종·validate·Tus 첨부·commit |
+| 팝업 공통 | `src/features/windowPopup/{model,ui,registry.tsx}` | popup key·크기·메시지·content 등록 |
+| 샘플 검증 | `src/features/exmn/model/exmn-sample-data.ts` | `sample=Y` 전용 사업·처리분구·검토결과 |
+| PPL | `src/features/exmn/ui/PplSummaryModal.tsx`, `PplFacilityDetailModal.tsx` | 집계·시설·단위구간 상세 |
+| 타입/API/검증 | `src/features/exmn/{model,api,lib}/exmn-business-*` | 승인 계약 구현 |
+| 첨부 오류 | `app/components/lib/utils/fileUploadUtil.tsx` | 실패 콜백 추가 |
+| 스타일 스캔 | `tailwind.config.ts` | `./src/**/*` 추가 |
+
+## 10. FE-1 완료 게이트
+
+- `/oper/exmn/register`의 검색조건과 조회·사업등록·결과등록이 하나의 상단 카드에 표시되는지 확인한다.
+- 사업년도는 단일 연도 선택, 시도·시군구는 I/I 공통 지역 선택으로 동작하며 양평에 고정되지 않는지 확인한다.
+- 사업명 입력 폭과 목록 열 순서·단위가 사용자 확정안과 일치하는지 확인한다.
+- 사업등록 WindowPopup의 행정구역 연동 처리분구 체크 선택·가나다순과 저장 전 검증을 확인한다.
+- 사업등록 WindowPopup은 콘텐츠 창 `920×620` 안에서 5개 입력만 순서대로 표시하고, 별도 닫기·양식 생성 버튼 없이 저장·양식 받기·조사보고서 양식을 제공하는지 확인한다.
+- 결과등록 WindowPopup의 파일 5종, 행별 찾기·데이터 검토, 검토 정상 전 저장 비활성을 확인한다.
+- `/oper/exmn/register?sample=Y`에서만 샘플 사업과 팝업 샘플 상태가 보이고 일반 URL은 실제 API 계약을 유지하는지 확인한다.
+- 상단에 설계되지 않은 `PPL 집계·상세` 직접 버튼이 표시되지 않는지 확인한다.
+- 사업·결과 popup 확인 후 PPT 3·4의 동일 목록 화면에 조사 상세현황을 결합하고 사용자 확인을 받는다.
+
+## 11. 조사현황·집계 1차 구현 범위
+
+| 구분 | 경로 | 결과 |
+| --- | --- | --- |
+| 기존 통합 화면 | `src/features/exmn/ui/ExmnRegistrationPage.tsx` | 사업 목록 행 선택 아래 상세 패널 결합 |
+| 상세 패널 | `src/features/exmn/ui/ExmnStatusDetailPanel.tsx` | 관로·맨홀 조사 상세현황 골격 |
+| 호환 라우트 | `app/(with-base-layout)/oper/exmn/status/page.tsx` | 기존 통합 화면을 그대로 렌더링하는 별칭 |
+| 타입 | `src/features/exmn/model/exmn-business-types.ts` | `ExmnJobType`, `ExmnSectionSummaryRow` 추가 |
+| API | `src/features/exmn/api/exmn-business-api.ts` | `GET /api/v1/exmnBusinesses/{id}/sections?jobType=PPL|MNHL` 추가 |
+| 샘플 | `src/features/exmn/model/exmn-sample-data.ts` | `sample=Y` 전용 관로·맨홀 처리분구 집계 |
+
+- 원본 화면설계서 슬라이드 3·4처럼 기존 사업등록·결과등록 버튼과 사업목록이 있는 화면에서 행 선택 시 바로 아래에 조사 상세현황을 펼친다. 별도 상세 페이지로 이동하지 않는다.
+- 사용자 표출 명칭은 `CCTV조사` 대신 `관로조사`, `맨홀조사`를 사용하고 서버 구분값은 `PPL`, `MNHL`을 유지한다.
+- 사업 행 선택 시 기본 `관로조사` 탭을 열고, 탭별 처리분구 합계와 상세 행을 표시한다.
+- `상세보기`, `다운로드`, `지도보기`는 이번 단계에서 버튼 진입점과 미연결 안내만 제공한다.
+- 상세보기 실제 화면은 사용자의 후속 집계·상세 설명을 받은 뒤 연결하고, 지도보기는 명시적 보류 상태를 유지한다.
+
+## 12. 조사현황·집계 1차 확인 게이트
+
+- `/oper/exmn/register?sample=Y`의 기존 사업목록에서 샘플 사업을 선택하면 같은 화면 하단 상세현황이 표시되는지 확인한다.
+- 관로조사 탭은 대상연장·주행거리·전체·구조·운영·기타 열, 맨홀조사 탭은 전체·구조·운영·기타 열을 표시하는지 확인한다.
+- 탭 이름에 `CCTV조사`가 남지 않고 `관로조사`, `맨홀조사`가 표시되는지 확인한다.
+- 세 기능 버튼은 선택 사업이 없을 때 비활성이고 선택 후 후속 연결 안내를 표시하는지 확인한다.
+- 실제 상세보기·다운로드·지도 페이지가 구현 완료된 것으로 판정하지 않는다.
+
+- 사용자 최종 확인 결과 목록 전체 셀 선택, 관로조사·맨홀조사 전환, 목록·상세 배치와 10행 샘플 집계는 통과했다.
+- 조사관리 상세현황 화면 단계는 완료하며 `상세보기`, 다운로드, 지도보기와 실 API는 후속 범위로 남긴다.
+
+## 13. 2026-07-23 git status 기준 실제 변경 범위
+
+- 프론트 저장소의 현재 코드 변경은 추적 파일 9개, 신규 파일 15개로 총 24개다.
+- 추적 변경: `app/(with-base-layout)/layout.tsx`, `app/components/lib/utils/fileUploadUtil.tsx`, `src/app/styles/components.css`, `src/features/exmn/index.ts`, `src/features/windowPopup/model/{popupDefinitions,types}.ts`, `src/features/windowPopup/registry.tsx`, `tailwind.config.ts`, `packages/common/business_components/src/ui/table/index.tsx`.
+- 신규 라우트: `app/(with-base-layout)/oper/exmn/{register,status}/page.tsx`.
+- 신규 조사사업 모듈: `src/features/exmn/{api,lib,model,ui}/**`의 사업 API·검증·타입·샘플·등록/결과 팝업·상세현황·PPL 상세 파일 13개.
+- 신규 WindowPopup content: `src/features/windowPopup/ui/{ExmnBusinessRegisterPopupContent,ExmnResultRegisterPopupContent}.tsx`.
+- 현재 `layout.tsx` diff는 파일 끝 줄바꿈 정규화뿐이며 공통 `BaseLayout → PageLayout` 구조와 Header·Footer 컴포넌트는 변경하지 않는다. 조사관리 화면 높이·문서 overflow 제어는 `ExmnRegistrationPage`가 담당한다.
+- `components.css`에 추가했던 조사사업 전용 `sn-exmn-grid-scroll`은 팝업 디자인 회귀 차단을 위해 제거했다. 현재 파일의 의미 있는 CSS diff는 없으며 조사 목록·상세 gutter는 각 컴포넌트 인라인 스타일로 유지한다.
+- `tailwind.config.ts`의 `./src/**/*` 스캔 경로를 복구해 `src/features` 팝업의 grid·임의 폭 클래스가 CSS에 생성되도록 했다.
+
+## 14. 조사사업 목록·상세현황 최종 완료 범위
+
+### 14.1 조사사업 목록
+
+- 검색조건 간격을 공통 조회영역에 맞추고 사업차수·사업연도·시도·시군구의 선택 동작을 확인했다.
+- 목록 전체 행 영역에서 사업을 선택할 수 있으며 선택 행을 강조한다.
+- 정렬 헤더는 공통 디자인을 따르고 `오름차순 → 내림차순 → 기본`으로 순환한다. 서버 응답과 `sample=Y` 데이터에 같은 정렬 규칙을 적용한다.
+- 검증용 사업 샘플을 4건으로 늘렸다. 목록에는 3행이 하단선까지 온전히 보이고 4번째부터 내부 세로 스크롤로 확인한다.
+- 상세현황 표와 동일한 좌우 여백·가로 폭·스크롤바 공간을 사용하고 스크롤 중 상·하단선이 잘리지 않게 보정했다. 스크롤바 공간 예약은 두 조사 그리드의 인라인 스타일로 격리해 WindowPopup 디자인에 영향을 주지 않는다.
+
+### 14.2 조사 상세현황
+
+- 관로조사·맨홀조사는 보이는 전체 영역이 실제 클릭 가능한 독립 button이다.
+- 사업이나 조사유형이 바뀌면 해당 집계를 다시 조회하며, 요청 ID로 이전 응답이 최신 선택을 덮어쓰는 것을 차단한다.
+- 합계는 `NO+처리분구` 두 셀을 실제 병합하고 문구를 중앙 정렬했다. 합계 행 배경은 `#d2deef`다.
+- 헤더는 상세 그리드 상단에 고정하고, 다음 행 일부가 하단에 노출되지 않는 인라인 `maxHeight`와 내부 세로 스크롤을 적용했다.
+- 목록과 상세현황은 정교한 셀 전체 클릭·병합·높이 처리를 위해 공통 Table primitive로 직접 구성한 화면 전용 표를 사용한다.
+
+### 14.3 페이지 높이
+
+- 임시 조사관리 전용 공통 레이아웃 래퍼·헤더 CSS는 최종안에서 사용하지 않는다.
+- 페이지 시작 위치와 실제 Footer 높이로 사용 가능 높이를 계산하고 Footer 변화는 `ResizeObserver`로 재계산한다.
+- 조사관리 화면이 열려 있는 동안 문서 전체 스크롤을 잠그고 목록·상세 그리드 내부 스크롤을 사용하며, 화면을 벗어나면 기존 문서 overflow 값을 복원한다.
diff --git a/apps/sn/docs/작업내용/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-통합구현기록.md b/apps/sn/docs/작업내용/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-통합구현기록.md
index ec16b3117..83e7dcba3 100644
--- a/apps/sn/docs/작업내용/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-통합구현기록.md
+++ b/apps/sn/docs/작업내용/SFR-010-004~SFR-010-007-조사사업/001-fe-SFR-010-004~007-조사사업-통합구현기록.md
@@ -20,6 +20,18 @@
| 2026-07-22 | FE-0 후속 | 메뉴 진입 시 발생한 Next 서버/클라이언트 import 경계 오류 수정 |
| 2026-07-22 | FE-0 화면 마무리 | 공통 UiTable 정렬과 검색조건·버튼 배치/색상 보완 |
| 2026-07-22 | FE-0 완료 | 2차 UI 보정 후 사용자 화면 검증 통과, FE-1 시작 대기 |
+| 2026-07-22 | FE-1 | 사업 목록·등록, 파일 검토/확정, PPL 집계·시설·단위상세 구현 |
+| 2026-07-22 | 화면 2단계 완료 | 조사사업 목록과 사업등록·조사결과 업로드 WindowPopup 사용자 확인 완료 |
+| 2026-07-23 | FE-1 조사현황 보정 | 목록·상세 가로 기준 통일, 사업 행 전체 선택, 목록·상세 내부 스크롤 통일 |
+| 2026-07-23 | 화면 3단계 완료 | 같은 화면의 관로·맨홀 종합집계, 합계, 내부 스크롤, 요청 순서 보호 사용자 확인 완료 |
+| 2026-07-23 | 팝업 회귀 복구 | Tailwind `src` 스캔 복구와 핵심 열·폭 인라인 고정 후 완료 디자인 재확인 |
+
+### 3.1 현재 구현 경계
+
+- 화면 1단계 `처리분구관리`, 화면 2단계 `조사사업 관리`, 화면 3단계 `조사 상세현황`까지 프론트 UI와 `sample=Y` 검증 흐름을 완료했다.
+- 화면 3단계 완료 범위는 관로·맨홀의 처리분구별 종합집계까지다.
+- `상세보기` 실제 화면과 다운로드 연동은 다음 프론트 작업이며 `지도보기`는 보류한다.
+- 백엔드 API·서비스·Repository·DB와 실데이터 통합 검증은 아직 시작하지 않았다.
## 4. FE-0 처리분구관리
@@ -84,7 +96,8 @@
## 5. 기능별 현재 상태
- FE-0: 화면 구현·사용자 UI 검증 완료, 실 API 연동은 백엔드 구현 후 검증
-- FE-1~FE-4: 미착수
+- FE-1: 목록과 사업등록·결과등록 WindowPopup 구현·개발 서버 컴파일·사용자 UI 확인 완료, PPL 집계·상세 확인 및 단계 커밋 대기
+- FE-2~FE-4: 미착수
- 백엔드: 사용자 지시대로 프론트 전체 완료 전 미착수
## 6. BE 공유 사항
@@ -104,3 +117,178 @@
## 8. 흡수한 기존 문서
- 없음
+
+## 9. FE-1 사업정보·조사결과 등록
+
+### 9.1 사업 목록과 등록
+
+- 사업차수, 사업년도, 시도, 시군구, 사업명은 입력 상태와 적용 조회조건을 분리했다.
+- 사업년도는 `YearRangeSelectBox` 모듈의 단일 연도 옵션을 사용하는 `SelectBox`로 바꿨다.
+- 시도·시군구는 I/I 분석과 같은 `CtpvSggPrcsFcltSelectBox`를 사용하며 양평 고정값과 하수처리시설 선택 props를 두지 않았다.
+- 검색조건과 조회·사업등록·결과등록을 한 상단 카드에 배치하고 사업명 입력은 `w-52`로 제한했다.
+- 상단의 임의 `PPL 집계·상세` 버튼은 제거했다. PPL 집계·상세 구현은 조사현황의 건수 클릭 등 정식 진입점에 연결할 수 있도록 모듈로 유지한다.
+- 목록 열은 NO, 사업차수, 사업년도, 시도, 시군구, 사업명, 처리구역·분구(개소), 정밀조사 연장(km), 보고서 순서다.
+- 목록은 0-base page, 20건, 허용 정렬 문자열을 `GET /api/v1/exmnBusinesses`에 전달한다.
+- 사업등록은 사업연도·사업명·차수·행정구역·정밀조사연장·총사업비와 활성 처리분구 ID 배열을 저장한다.
+- 사업 저장 후에만 처리분구별 PPL/MNHL 시트 생성과 통합 입력양식 다운로드를 활성화한다.
+
+### 9.2 파일 검토와 최종 저장
+
+- 파일 선택 시 첨부서비스로 즉시 전송하지 않고 파일 구분별 확장자, 빈 파일, 200MB 상한을 먼저 검사한다.
+- PPL 또는 MNHL 파일 1개 이상을 `imports/validate`에 보내고 오류 위치를 그리드로 표시한다.
+- 정상 검토 전에는 최종 저장을 비활성화한다.
+- 최종 저장은 선택 파일 Tus 업로드, 첨부키 PUT, 동일 PPL/MNHL 파일·검토 SHA-256·첨부키 multipart commit 순서다.
+- 기존 파일 업로드 유틸에 `handleError` 콜백을 추가해 실패가 최종 저장 대기 상태로 남지 않고 화면 오류로 귀결되게 했다.
+
+### 9.3 PPL 집계와 상세
+
+- 처리분구별 대상연장, 주행거리, PPL 전체/구조/운영/기타 수를 서버 페이징·정렬 그리드로 표시한다.
+- 결함 건수 클릭은 처리분구와 결함유형을 반영해 `jobType=PPL` 시설 목록을 조회한다.
+- 시설 목록은 처리분구, 보고서번호, GIS 관로관리번호, 상·하류 맨홀, 관종/규격, 연장, 구조·운영등급을 표시한다.
+- 시설 행을 선택하면 서버 정규화 `unitSections`와 선택 단위의 상태·결함 기록을 표시한다.
+- 미확정 보고서 매핑은 비활성 버튼과 안내로 유지한다.
+
+### 9.4 UI 기반 보완
+
+- `sn` Tailwind 설정에 빠져 있던 `./src/**/*`를 content에 추가해 `src/features` 신규 클래스가 생성되게 했다.
+- 기존 `SearchDiv`, `ListDiv`, `UiTable`, Button/Input과 공통 `windowPopup` 등록 패턴을 재사용했다.
+- 라우트는 FE-0에서 확인한 Next 경계 문제를 반복하지 않도록 client UI를 직접 import한다.
+- 사용자 화면 확인에서 분리돼 보이던 상단 액션과 검색조건을 같은 `SearchDiv` 자식 flex 영역으로 합쳤다.
+
+### 9.5 영향과 롤백
+
+- 신규 `/oper/exmn/register`와 `features/exmn` FE-1 파일을 제거하면 화면/API 계약을 롤백할 수 있다.
+- Tus 실패 콜백과 Tailwind `src` 스캔은 `sn` 앱 전체에 적용되지만 기존 호출 계약을 깨지 않는다.
+- 백엔드, DB, 타 앱, 공통 비즈니스 컴포넌트는 변경하지 않았다.
+
+### 9.6 사업등록·결과등록 WindowPopup 전환과 샘플 검증
+
+- 상단 `사업등록`, `결과등록`은 인라인 `UiModal` 대신 `openWindowPopup()`을 사용한다.
+- 공통 popup key는 `exmnBusinessRegister`, `exmnResultRegister`이며 `/popup/[popupKey]` registry에서 각각 업무 화면을 렌더링한다.
+- 사업등록 팝업은 자체 브라우저 컨텍스트에서 시도·시군구 store를 다시 로드하고, 활성 처리분구를 `localeCompare(..., "ko")` 기준 가나다순으로 정렬해 체크박스 다중 선택으로 제공한다.
+- 사업 저장 완료와 결과 저장 완료는 `window.opener.postMessage`로 부모 목록에 전달한다. 팝업 차단 시 부모 화면에 허용 안내를 표시한다.
+- 결과등록 팝업은 조사 결과, CCTV 조사 결과(관로), CCTV 조사 결과(맨홀), 사업보고서, 조사 위치도 5개 행을 표시하며 각 행의 파일명·찾기와 조사 원본의 데이터 검토 진입을 제공한다.
+- 정상 검토 전 저장 버튼은 비활성이고 실제 모드의 Tus 첨부→첨부키 연결→commit 계약은 유지한다.
+- 백엔드 데이터가 없는 사용자 화면 검증을 위해 `/oper/exmn/register?sample=Y`만 샘플 모드로 사용한다. 일반 URL의 API 빈 결과를 자동 샘플로 대체하지 않는다.
+- 샘플 모드는 사업목록, 가나다순 처리분구, 정상 검토 요약을 제공하되 실제 API 저장·첨부·다운로드는 수행하지 않는다.
+
+### 9.7 사업등록 팝업 사용자 확인 보정
+
+- 사업등록 popup definition을 `1180×820`에서 `920×620`으로 축소하고 불필요한 하단 여백을 줄였다.
+- 상단 `닫기`, `양식 생성`을 제거했다. `양식 받기`는 사업 저장 후 양식 생성과 다운로드를 한 번에 수행한다.
+- `양식 받기`, `조사보고서 양식`은 기존 엑셀 다운로드 버튼의 전역 `green` 스타일과 흰색 글자를 사용한다.
+- 화면 입력은 5개만 노출하고 첫 줄에 `사업년도·시도·시군구·정밀조사연장`, 둘째 줄에 `사업명`을 배치한다. API 필수 계약인 사업차수와 총사업비는 신규 기본값 `1`, `0`으로 전달한다.
+- Radix Select의 서버·클라이언트 ID 불일치를 막기 위해 사업등록·결과등록 WindowPopup content를 client mount 이후 렌더링한다.
+- 개발 환경의 목록 헤더에 `샘플 데이터 보기`를 제공한다. 이 버튼은 URL에 `sample=Y`를 명시해 기존 격리 원칙을 유지하며, 사업목록과 처리분구 샘플을 함께 활성화한다.
+- 사용자 재확인에 따라 팝업의 `샘플 화면` 배지를 제거하고 처리분구 목록을 고정 높이 `21rem`으로 확장했다. 3열 7행으로 21개까지 한 번에 표시하고 초과 후보는 목록 내부 세로 스크롤로 확인한다.
+- 정밀조사연장은 브라우저 증감 스피너가 없는 `text`+`inputMode=decimal` 입력으로 변경하고 숫자·소수점만 허용한다. 최대 10자, 입력 폭 `9rem`으로 첫 줄 안에서 표시한다.
+- 결과등록 popup definition은 Chrome 외곽 프레임을 포함한 사용자 목표 크기 약 `980×410`에 맞춰 콘텐츠 창 설정을 `980×340`으로 확정하고 샘플 배지와 닫기 버튼을 제거했다.
+- 결과등록 상단은 `사업차수 → 사업년도 → 행정구역 → 사업명` 순서로 한 줄 표시한다. 짧은 항목은 좁고 사업명은 넓은 `0.7fr/0.8fr/1.2fr/2.3fr` 비율과 `gap-8` 간격을 사용한다.
+- 파일 5종 업로드 영역은 강한 셀 격자 대신 기존 화면처럼 연한 외곽선·헤더 배경·가로 행 구분선만 사용하는 4열 폼 목록으로 구성한다.
+- popup root는 `h-dvh`·`overflow-hidden`으로 브라우저 가로·세로 스크롤을 차단하고, 검토결과가 추가돼 실제 높이가 넘을 때만 본문 내부를 세로 스크롤한다.
+- 상위 popup layout의 `html/body`에서 남는 문서 스크롤을 제거하기 위해 결과 팝업 mount 동안 두 요소의 overflow를 잠그고 종료 시 원래 값을 복원한다.
+- 파일 헤더는 `py-2.5`, 각 업로드 행은 `py-3`으로 늘려 남는 세로 공간을 활용한다.
+- 파일 헤더와 데이터 행에 동일한 `gap-3`을 적용해 `찾기`·`데이터 검토` 열의 중심선을 일치시켰다.
+- 파일 선택 완료 시 파일명 영역을 파란색 굵은 글씨·연한 파란 배경·파란 테두리로 바꿔 미선택 안내와 구분한다.
+- 사용자가 사업등록과 조사결과 업로드 WindowPopup의 최종 크기·배치·스크롤·파일 선택 표시를 확인해 두 팝업 UI를 완료 판정했다.
+
+### 9.8 조사현황·처리분구 집계 1차 골격
+
+- 원본 화면설계서 슬라이드 3·4를 렌더링해 사업 목록의 행 선택 후 같은 화면 하단에 조사 상세현황이 표시되는 구조를 확인했다.
+- 첫 구현에서 조사 상세현황을 별도 `ExmnStatusPage`의 목록에 배치했으나, 사용자 확인으로 PPT 3·4는 같은 화면의 선택 전·후 상태임을 재확인했다.
+- `ExmnStatusDetailPanel`을 분리해 현재 사용 중인 `ExmnRegistrationPage` 사업목록 바로 아래에 결합했다. `/oper/exmn/status`도 별도 화면을 만들지 않고 동일 통합 화면을 렌더링한다.
+- 행 선택 시 기본 조사유형을 `PPL`로 초기화하고 `관로조사`, `맨홀조사` 탭에서 처리분구별 집계와 합계 행을 표시한다.
+- 관로 집계는 대상연장·주행거리·전체 이상·구조·운영·기타를 표시하고, 맨홀 집계는 전체 이상·구조·운영·기타를 표시한다.
+- 사용자 화면 명칭은 `CCTV조사`에서 `관로조사`로 정정하되 API·DB 조사작업 구분은 `PPL|MNHL`을 유지한다.
+- 선택 사업의 집계 API 진입은 `/api/v1/exmnBusinesses/{exmnBizId}/sections?jobType=PPL|MNHL` 계약으로 추가했다.
+- 백엔드 데이터가 없는 화면 확인은 기존 `/oper/exmn/register?sample=Y` 흐름에서 샘플 사업·처리분구 집계를 사용하며 일반 URL은 실제 API 계약을 유지한다.
+- `상세보기`, `다운로드`, `지도보기`는 현재 버튼과 미연결 안내만 제공한다. 상세보기는 집계 확인 후 사용자 설명에 따라 구현하고 지도보기는 보류한다.
+- 선택 사업의 사업명·차수·연도·행정구역을 반복 표시하던 요약 띠는 사용자 요청으로 제거했다.
+- 관로조사·맨홀조사는 별도 버튼을 만들지 않고 기존 공통 `UITab`을 재사용한다. 사용자 후속 확인에 따라 underline 대신 분석도구의 활성 메뉴와 가까운 기본 파란 활성 탭을 컴팩트한 크기로 적용했다.
+- 합계는 일반 데이터 행에 빈 NO 셀을 두지 않고 `NO+처리분구` 두 열을 병합한 전용 연한 배경 행으로 표시한다.
+- 기본 `Table`에 외부 스크롤과 sticky `thead`를 결합했던 1차 보정은 이중 스크롤 컨테이너 때문에 스크롤 중 행 일부가 헤더 위로 잘려 보이는 문제가 확인됐다.
+- 종합집계표를 기존 화면에서 사용하는 공통 `UiTable`로 교체하고, `UiTable` 내부 표 래퍼 하나만 스크롤 컨테이너로 사용한다. 행 패딩과 내부 높이를 `16.7rem`으로 조정해 헤더·합계·처리분구 3행의 하단선까지 온전히 표시하고, 초과 행만 그리드 내부에서 스크롤한다.
+- 우측 페이지 스크롤은 집계표 높이가 아니라 공통 `BaseLayout`이 실제 `6rem` 헤더를 `50px`로 차감하던 최소 높이 불일치가 원인이었다. 1차 전용 선택자는 실제 DOM의 `SidebarProvider` 중간 래퍼를 누락해 적용되지 않았으며, SSR HTML을 기준으로 `.sn-exmn-base-layout > div > .flex-1 > .flex > .flex-1`로 정정해 `/oper/exmn/**` 경로에만 `min-height: calc(100vh - 6rem)`을 적용했다.
+- `UiTable`이 본문 `colSpan`을 지원하지 않는 제약 안에서 합계 첫 두 셀의 경계선을 제거하고 첫 셀의 표시 영역을 `NO+처리분구` 너비만큼 확장해 `합계` 문구를 두 열 중앙에 배치했다.
+- 상세현황 표시 시 카드가 일반 문서 흐름에서 Footer를 밀어 페이지 전체 스크롤을 만들던 구조를 보정했다. 조사사업 경로의 본문 높이를 `100dvh - 6rem`으로 고정하고, PageLayout·조사사업 페이지·상세 패널을 `min-height: 0`인 flex 구조로 연결해 남은 공간을 상세 패널에 배분한다. 상세 데이터 초과분은 종합집계 `UiTable` 내부에서만 스크롤한다.
+- 조사사업 선택에 공통 `UiTable`의 신규 `rowClick` 계약을 적용했으나 사용자 재확인에서 정밀조사 연장·보고서 외 셀의 클릭이 동작하지 않았다. 신규 공통 계약은 철회하고 기존에 사용 중인 `cellClick`을 전체 열에 적용하는 방식으로 교체했다.
+
+### 9.9 조사사업 목록·상세현황 그리드 동작 통일
+
+- 단순히 `1.2rem` 값을 각각 지정하던 구조는 목록의 `UiTable` margin과 상세의 부모 padding이 달라 실제 표 경계가 일치하지 않았다. `UiTable.noOuterMargin` 옵션을 추가하고 두 표 모두 `mx-[1.2rem]`인 동일한 외부 스크롤 래퍼만 가로 여백을 소유하게 했다.
+- 두 스크롤 래퍼에 `scrollbar-gutter: stable`을 적용해 상세 표에 세로 스크롤바가 생겨도 목록과 상세의 표 가로 기준이 달라지지 않게 했다.
+- 행 선택은 검증되지 않은 `rowClick` 확장을 제거하고 기존 `cellClick` 계약을 사용한다. `clickableHeadIds`를 제한하지 않아 NO부터 보고서까지 모든 데이터 셀이 같은 사업 선택 함수를 호출한다.
+- 조사사업 목록의 화면 페이지네이션을 제거하고 최초 페이지의 최대 1,000건을 조회한다. 목록은 최대 높이 `16.7rem` 이후 내부 스크롤하고, 상세 집계는 고정 최대 높이를 제거해 페이지 전체 스크롤이 생기지 않는 범위의 남은 카드 높이를 모두 사용한 뒤 초과 데이터만 내부 스크롤한다.
+- 목록 제목의 전체 건수는 API `totalElements`를 그대로 표시하고, 표 내부 페이지 이동 UI만 숨긴다.
+- 현재 1,000건 상한은 프론트 선행 구현용이다. 실제 운영 데이터가 이를 넘을 가능성이 있으면 백엔드 통합 단계에서 무제한 조회 대신 무한 스크롤 또는 커서 기반 조회 계약을 확정한다.
+
+### 9.10 클릭 영역·목록 높이·샘플 행 최종 보정
+
+- 목록 `16.7rem`은 공통 표의 헤더·3개 행·내부 padding 합계보다 작아 3건만 있어도 세로 스크롤과 마지막 행 하단 잘림이 발생했다. 목록 최대 높이를 `18rem`으로 조정해 3건은 스크롤 없이 하단선까지 표시하고 4건 이상부터 목록 내부 스크롤을 사용한다.
+- 셀별 `cellClick` 이후 공통 `UiTable`에 선택적 `rowClick` 계약을 추가했지만, 사용자 재확인에서 정밀조사 연장 우측 일부 영역만 선택되고 앞쪽 영역은 반응하지 않는 문제가 남았다. 해당 공통 확장도 검증 실패로 판정한다.
+- 관로조사·맨홀조사는 공통 `UITab`의 스타일 선택자에 의존하지 않고 보이는 크기와 실제 클릭 요소가 같은 `80×32px` native button 2개로 구성한다. `role=tablist/tab`, `aria-selected`를 유지한다.
+- 당시 화면 검증용 세 조사사업의 처리분구 수를 모두 10개로 맞췄다. 이후 목록 내부 스크롤 검증을 위해 9.13절에서 네 번째 사업 샘플을 추가했으며, 샘플 상세 집계는 합계 행 외에 관로·맨홀 각각 처리분구 10행을 제공한다.
+
+### 9.11 조사사업 목록 클릭 영역 구조 교체
+
+- 공통 `UiTable`의 `cellClick`·`rowClick`을 반복 확장하는 방식을 철회하고, 조사사업 목록만 공통 `Table` 스타일을 사용하는 화면 전용 표로 교체했다. 상세 집계의 `UiTable` 사용은 유지한다.
+- 각 데이터 셀은 padding을 제거한 뒤 `min-height: 4rem`, `width: 100%`인 실제 `