# 화면설계서: 알림이력 ## 1. 기본 정보 | 항목 | 내용 | | ----------- | -------------------------------------------- | | 버전 | 1.4 | | 프로세스명 | UI설계 | | 화면명 | 알림이력 | | 화면 ID | AS_SC0205011 | | 요구사항 ID | NSUR-001-001 | | 화면 유형 | [v] 조회 [ ] 입력 [ ] 수정 [ ] 삭제 [ ] 출력 | | 메뉴 경로 | 메인 > 행정지원서비스 > 알림이력 | --- ## 2. 화면 목적 로그인한 사용자가 **본인에게 발송된 행정지원(ADSS) 알림**을 기간·구분으로 조회한다. 목록의 처리 버튼으로 해당 알림을 읽음 처리한 뒤, 연계된 업무 화면으로 이동한다. --- ## 3. 화면 구성 ### 3-1. 전체 레이아웃 ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 브레드크럼: 메인 > 행정지원서비스 > 알림이력 │ │ 페이지 타이틀: 알림이력 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ [영역 A] 검색 필터 │ │ 기간 [시작일] ~ [종료일] 구분 ▼ [검색] │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ [영역 B] 알림 이력 목록 │ │ 타이틀: 알림 이력 목록 N개 ▼ │ │ ┌────┬──────────────┬────────────────┬──────────┬────────┬────────┐ │ │ │ No │ 알림일시 │ 알림내용 │ 연계업무 │ 상태 │ 처리 │ │ │ ├────┼──────────────┼────────────────┼──────────┼────────┼────────┤ │ │ │ … │ regDt │ notiTtl │ docSeComNm│ Badge │ 버튼 │ │ │ └────┴──────────────┴────────────────┴──────────┴────────┴────────┘ │ │ [페이지네이션] │ └─────────────────────────────────────────────────────────────────────────────────┘ ``` ### 3-2. 화면 데이터 흐름 ``` BEGIN // 최초 진입 // 기간 기본값: 오늘 기준 최근 1개월 (시작일 = today-1M, 종료일 = today) CALL findMyAsNotiList(NotiSearchDto + pageable) DISPLAY 알림 이력 목록 그리드 (영역 B) ON [검색] 버튼 CLICK DO CALL findMyAsNotiList(영역 A 조건 + page=0) DISPLAY 알림 이력 목록 그리드 (영역 B) END ON ON 페이지네이션 CHANGE DO CALL findMyAsNotiList(영역 A 조건 + page/size) DISPLAY 알림 이력 목록 그리드 (영역 B) END ON ON 영역 B [처리] 버튼 CLICK DO CALL readNoti(notiId) // UI설계: 해당 알림에 연계된 URL로 이동 NAVIGATE notiUrl END ON END ``` --- ## 4. 영역 A — 검색 필터 구성: **기간 · 구분** (+ 검색). 서버 `findMyAsNotiList` 고정 정책 (클라이언트 미전달): - `recvUnqId` = 로그인 사용자 UUID - `delYn` = `N` - `notiType` = `ADSS` (행정지원 전용 API) ### 4-1. 필드 정의 | 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 | | ---------- | ---------- | ---- | --------------------------- | ----------------- | ------------------------------------------------------------------------------------------------ | | 기간(시작) | DatePicker | N | 오늘 − 1개월 (`YYYY-MM-DD`) | `startDt` | `NotiSearchDto.startDt` | | 기간(종료) | DatePicker | N | 오늘 (`YYYY-MM-DD`) | `endDt` | `NotiSearchDto.endDt` | | 구분 | Select | N | 전체(미선택) | `docSeComCd` | `INSTL` / `FNRS` / `SEWER` / `CNFM` / `PWRK` (또는 단축 `I`/`F`/`S`/`C`/`P`). 공통코드 `ASDOCSE` | ### 4-2. 버튼 | 버튼명 | 동작 | | ------ | -------------------------------------------------------------- | | 검색 | 영역 A 조건으로 `findMyAsNotiList` 호출 → 영역 B 갱신 (page=0) | ### 4-3. 콤보·기본값 주의 | 항목 | 내용 | | -------------- | ---------------------------------------------------------- | | 구분 = 전체 | `docSeComCd` 미전달. `notiType`은 서버가 `ADSS`로 고정 | | 구분 옵션 소스 | 공통코드 `ASDOCSE` (`docSeComCd` ↔ `docSeComNm`) | | 기간 | UI설계 상세설명: 기본 최근 1개월. `startDt`/`endDt`로 전달 | --- ## 5. 영역 B — 알림 이력 목록 ### 5-1. 그리드 컬럼 | 표시명 | Field | 비고 | | -------- | ------------ | --------------------------------------------------------------- | | No | (row index) | 페이지 오프셋 반영 | | 알림일시 | `regDt` | 표시 `YYYY-MM-DD HH:mm` (응답 `yyyy-MM-dd HH:mm:ss`) | | 알림내용 | `notiTtl` | 제목 문자열. UI 샘플은 `[YYYY-MM-DD] …` 접두 | | 연계업무 | `docSeComNm` | 문서구분 공통코드명 (`docSeComCd` + `@CodeName` `ASDOCSE`) | | 상태 | (derived) | `Badge` 렌더링. 판정 규칙은 §5-1-1 | | 처리 | (action) | 상태별 버튼 라벨. 클릭 시 `readNoti` 후 `notiUrl`로 이동 (§5-3) | #### 5-1-1. 상태 UI 케이스 (`@common/components` `Badge`) 판정에 사용: `notiPrcsCmptnYn`, `notReadYn`, `regDt` (오늘 기준 경과일). | 조건 | 라벨 | Badge | | ------------------------------------------------------------------------------ | -------- | ----------------------------- | | `notiPrcsCmptnYn` = `Y` | 처리완료 | `variant="default"` (primary) | | `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `Y` 이고 `regDt`로부터 **7일 초과** | 처리지연 | `color="red"` | | `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `Y` 이고 `regDt`로부터 **7일 이내** | 처리중 | `color="green"` | | `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `N` 이고 `regDt`로부터 **3일 초과** | 지연 | `color="orange"` | | `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `N` 이고 `regDt`로부터 **3일 이내** | 미조회 | `color="yellow"` | ``` IF notiPrcsCmptnYn = Y THEN DISPLAY Badge "처리완료" (primary / variant=default) ELSE IF notReadYn = Y THEN IF daysSince(regDt) > 7 THEN DISPLAY Badge "처리지연" (red) ELSE DISPLAY Badge "처리중" (green) END IF ELSE // notReadYn = N IF daysSince(regDt) > 3 THEN DISPLAY Badge "지연" (orange) ELSE DISPLAY Badge "미조회" (yellow) END IF END IF ``` - `daysSince(regDt)`: 오늘 날짜 − `regDt` 일자 (일 단위) - `regDt`가 없으면 상태 Badge 표시 전략 TBD (미표시 또는 기본값) 응답에 있으나 그리드에 **표시하지 않음**: | Field | 비고 | | ----------------- | ---------------------------------------------------------------------- | | `notiId` | 알림 ID — 행 키 | | `recvUnqId` | 수신 회원 UUID (서버가 로그인 사용자로 고정) | | `senderUnqId` | 발신 회원 UUID | | `notiType` | 알림 종류 (`ADSS`) | | `notiCn` | 알림 내용 (목록에서는 서버가 제외할 수 있음) | | `notReadYn` | 확인 여부 `Y`/`N`. 상태 Badge 판정에 사용 (§5-1-1) | | `notiUrl` | 처리 버튼 이동 URL. 컬럼 미표시 | | `notiLnkgUnqNo` | 알림연결고유번호 | | `docSeComCd` | 문서구분 공통코드. 연계업무 표시는 `docSeComNm` 사용 | | `notiIdntyDt` | 알림 확인 일시 | | `notiIdntyYn` | 알림 확인 여부 (`notReadYn`과 동일 의미). 상태 판정은 `notReadYn` 사용 | | `notiPrcsCmptnYn` | 알림 처리 완료 여부. 상태 Badge 판정에 사용 (§5-1-1) | | `notiPrcsCmptnDt` | 알림 처리 완료 일시 | ### 5-2. 목록 메타·페이지네이션 | 항목 | 매핑 | | ------------- | ------------------------------------------- | | 건수 / size ▼ | 상단 우측 `N개` 셀렉트 → `Pageable.size` | | page / size | `NotiSearchDto`의 `PageableParams` | | 페이지 컨트롤 | Spring `Page` 표준 (`number`, `totalPages`) | ### 5-3. 행 상호작용 | 동작 | 설명 | | -------------- | --------------------------------------------------------------------------- | | 처리 버튼 클릭 | `readNoti(notiId)`로 읽음 처리 후, 해당 알림에 연계된 URL(`notiUrl`)로 이동 | | 행 클릭 | UI설계에 없음. 처리 버튼만 명시 | **처리 버튼 라벨** (상태 Badge §5-1-1 기준): | 상태 | 버튼명 | | -------- | -------- | | 미조회 | 처리하기 | | 지연 | 처리하기 | | 처리중 | 처리하기 | | 처리지연 | 처리하기 | | 처리완료 | 상세보기 | - `notiUrl`이 비어 있으면 이동하지 않음 (no-op). 읽음 처리는 `notiUrl` 유무와 관계없이 호출 - 읽음 처리 성공 후 목록 쿼리(`notiBaseKey`)를 invalidate 하여 영역 B 상태를 갱신 --- ## 6. API/DTO 명세 ### 6-1. 공통 | 항목 | 값 | | --------------- | ---------------------------------------------------------------------------------------- | | Base prefix | `/cms-service/api/v1/noti` (`ENDPOINT.CMS_SERVICE.NOTI`) | | 인증 | 로그인 사용자 UUID — 서버 `SecurityUtils.getLoggedInUserUnqIdUUID()` (클라이언트 미전달) | | 클라이언트 모듈 | `@/entities/noti` — `notiService` / `useFindMyAsNotiList` / `useReadNoti` | ### 6-2. 로그인한 사용자 행정지원 알림 목록 — `findMyAsNotiList` | 기능 | Method | URL | Request DTO | Response DTO | API | | ---------------------------------- | ------ | ------------------- | --------------------------------- | ------------------------------------- | ------------------------------------------------------ | | 로그인한 사용자 행정지원 알림 목록 | GET | `{BASE}/as/my/list` | Query: `NotiSearchDto` + Pageable | `PageResponse` | `notiService.findMyAsNotiList` / `useFindMyAsNotiList` | **Request (`NotiSearchDto` + Pageable)** | 필드 | 필수 | 설명 | | ------------------------ | ---- | --------------------------------------------------------------------------- | | `recvUnqId` | N | 수신 회원 UUID. 서버가 로그인 사용자로 덮어씀 | | `senderUnqId` | N | 발신 회원 UUID. 본 화면 필터에 없음 | | `notiType` | N | 알림 종류. 본 API는 서버가 `ADSS`로 고정 | | `notiTtlKeyword` | N | 알림 제목 키워드. 본 화면 필터에 없음 | | `notiCnKeyword` | N | 알림 내용 키워드. 본 화면 필터에 없음 | | `notReadYn` | N | 알림 확인 여부 (`Y`/`N`). 본 화면 검색 필터에 없음 | | `delYn` | N | 삭제 여부. 서버가 `N`으로 고정 | | `startDt` | N | 시작일자 `YYYY-MM-DD` — 영역 A 기간(시작) | | `endDt` | N | 종료일자 `YYYY-MM-DD` — 영역 A 기간(종료) | | `docSeComCd` | N | 문서 구분 공통 코드 (`INSTL`/`FNRS`/`SEWER`/`CNFM`/`PWRK` 등) — 영역 A 구분 | | `page` / `size` / `sort` | N | Spring Pageable | **Response (`NotiAsUserResponseDto`)** | 필드 | 타입 | 설명 | | ----------------- | ------- | ------------------------------------------------------- | | `notiId` | number | 알림 ID | | `recvUnqId` | string | 수신 회원 UUID | | `senderUnqId` | string | 발신 회원 UUID | | `notiType` | string | 알림 종류 (`ADSS`) | | `notiTtl` | string | 알림 제목 | | `notiCn` | string? | 알림 내용 (목록에서 제외될 수 있음) | | `notReadYn` | string? | 알림 확인 여부 (`Y`/`N`). 상태 Badge 판정 | | `regDt` | string? | 등록 일시 `yyyy-MM-dd HH:mm:ss`. 상태 Badge 경과일 기준 | | `notiUrl` | string? | 알림 URL 주소 | | `notiLnkgUnqNo` | string? | 알림연결고유번호 | | `docSeComCd` | string? | 문서구분 공통코드 | | `docSeComNm` | string? | 문서구분 공통코드명 (`ASDOCSE`) | | `notiIdntyDt` | string? | 알림 확인 일시 `yyyy-MM-dd HH:mm:ss` | | `notiIdntyYn` | string? | 알림 확인 여부 (`Y`/`N`) | | `notiPrcsCmptnYn` | string? | 알림 처리 완료 여부 (`Y`/`N`). 상태 Badge 판정 | | `notiPrcsCmptnDt` | string? | 알림 처리 완료 일시 `yyyy-MM-dd HH:mm:ss` | ### 6-3. 알림 읽음 처리 — `readNoti` | 기능 | Method | URL | Request DTO | Response DTO | API | | -------------- | ------ | ---------------------- | ----------------------- | ---------------- | -------------------------------------- | | 알림 읽음 처리 | PUT | `{BASE}/{notiId}/read` | Path: `notiId` (`Long`) | `Long` (알림 ID) | `notiService.readNoti` / `useReadNoti` | **Request** | 필드 | 위치 | 필수 | 설명 | | -------- | ---- | ---- | ------- | | `notiId` | Path | Y | 알림 ID | Body 없음. **Response** | 타입 | 설명 | | ------ | ------------------------------ | | number | 읽음 처리된 알림 ID (`notiId`) | 화면 매핑: 영역 B [처리] 버튼 클릭 시 호출. 성공 시 `notiBaseKey` invalidate → `findMyAsNotiList` 재조회. --- ## 7. UI 구현 가이드 ### 7-1. 기술 스택·원칙 - UI: `@common/components` — DatePicker, Select, Button, DataTable, Pagination, **Badge** - 목록: `@tanstack/react-table` + `useDataTable` (기존 `my-pbadms-sprt-list` 패턴) - 데이터: React Query `useFindMyAsNotiList(params)`, `useReadNoti()` - 검색 조건·페이지: URL `searchParams` 동기화 권장 (`useSearchParamsState` 스킬) - 처리 컬럼: 행 클릭이 아니라 **버튼 클릭**으로 `readNoti` 후 `notiUrl` 이동 - 상태 컬럼: §5-1-1 규칙으로 `Badge` 렌더링 (`notiPrcsCmptnYn` / `notReadYn` / `regDt`) - 연계업무: `docSeComNm` 표시 ### 7-2. 앱·파일 배치 | 구분 | 경로 | | ------------ | --------------------------------------------------------------------- | | 라우트 | `app/(main)/(with-base-layout)/noti/page.tsx` | | 페이지 | `pages/noti/ui/noti-list-page.tsx` | | 목록 그리드 | `pages/noti/ui/noti-list.tsx` | | 테이블 컬럼 | `pages/noti/model/noti-table-columns.tsx` | | 상태 판정 | `pages/noti/lib/resolve-noti-status.ts` | | Entity API | `entities/noti/api/noti-service.ts` | | Entity Hook | `entities/noti/api/queries.ts` → `useFindMyAsNotiList`, `useReadNoti` | | Entity Model | `entities/noti/model/noti-interface.ts` | 화면 레이어(`pages/noti`)·Entity(`findMyAsNotiList`, `readNoti`) 구현 완료. ### 7-3. 영역별 UI 매핑 | 영역 | 컴포넌트 | 데이터 | | ---- | ------------------- | -------------------------------------------------------------- | | A | `noti-list-page` | 로컬 state → `NotiSearchDto` | | B | DataTable + columns | `useFindMyAsNotiList` → `content[]`. 처리 버튼 → `useReadNoti` | ### 7-4. 다이얼로그·라우팅 | 항목 | 내용 | | ----------- | --------------------------------------------------------------------- | | 다이얼로그 | 없음 (목록 전용) | | 목록 라우트 | `/noti` | | 상세 이동 | 처리 버튼 → `readNoti(notiId)` 후 `notiUrl` (절대/상대 경로 형식 TBD) | ### 7-5. 데이터 흐름 ``` BEGIN // 최초 진입 SET 기간 = 최근 1개월 // startDt / endDt 전달 CALL findMyAsNotiList DISPLAY 영역 B ON [검색] CLICK DO CALL findMyAsNotiList // → 영역 B 갱신 END ON ON 페이지 CHANGE DO CALL findMyAsNotiList END ON ON 영역 B [처리] 버튼 CLICK DO CALL readNoti(notiId) // → 영역 B 상태 갱신 (invalidate) IF NOT notiUrl THEN // no-op (읽음 처리는 수행됨) ELSE NAVIGATE notiUrl END IF END ON END ```