From 07e842ecb210716931c5aa2296819c699af6d95f Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 8 Apr 2026 17:11:52 +0900 Subject: [PATCH] =?UTF-8?q?[cs]=20feat:=20=EC=95=8C=EB=A6=BC=ED=86=A1?= =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF=EA=B4=80=EB=A6=AC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/docs/spec/ntftalk/SPEC.md | 157 ++++++++++++++++++ .../ntftalk/api/ntftalk-temp-service.ts | 56 +++++++ .../ntftalk/api/ntftalk-var-service.ts | 56 +++++++ apps/cs/src/entities/ntftalk/index.ts | 20 +++ .../ntftalk/model/ntftalk-temp-interface.ts | 106 ++++++++++++ .../ntftalk/model/ntftalk-var-interface.ts | 42 +++++ apps/cs/src/pages/ntftalk-temp/page.tsx | 0 apps/cs/src/shared/config/api.ts | 4 + 8 files changed, 441 insertions(+) create mode 100644 apps/cs/docs/spec/ntftalk/SPEC.md create mode 100644 apps/cs/src/entities/ntftalk/api/ntftalk-temp-service.ts create mode 100644 apps/cs/src/entities/ntftalk/api/ntftalk-var-service.ts create mode 100644 apps/cs/src/entities/ntftalk/index.ts create mode 100644 apps/cs/src/entities/ntftalk/model/ntftalk-temp-interface.ts create mode 100644 apps/cs/src/entities/ntftalk/model/ntftalk-var-interface.ts create mode 100644 apps/cs/src/pages/ntftalk-temp/page.tsx diff --git a/apps/cs/docs/spec/ntftalk/SPEC.md b/apps/cs/docs/spec/ntftalk/SPEC.md new file mode 100644 index 000000000..ec8a7455e --- /dev/null +++ b/apps/cs/docs/spec/ntftalk/SPEC.md @@ -0,0 +1,157 @@ +# 화면설계서: 알림톡템플릿관리 + +## 1. 기본 정보 + +| 항목 | 내용 | +| ---------- | -------------------------------------------- | +| 버전 | 1.0 | +| 프로세스명 | UI설계 | +| 화면명 | 알림톡템플릿관리 | +| 화면 유형 | [x] 조회 [x] 입력 [x] 삭제 [ ] 수정 [ ] 출력 | +| 메뉴 경로 | home > 시스템관리 > 알림톡템플릿관리 | + +--- + +## 2. 화면 목적 + +카카오 알림톡(또는 동일 채널) **템플릿 목록을 조회·관리**하고, 선택한 템플릿에 연결된 **치환 변수 목록**을 확인·관리한다. + +--- + +## 3. 화면 구성 + +### 3-1. 전체 레이아웃 + +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 페이지 제목: 알림톡템플릿관리 브레드크럼 (우측 상단) │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ [영역 A] 검색 필터 │ +│ · 1행: 템플릿코드, 템플릿명, 템플릿내용 (텍스트) │ +│ · 2행: 채널, 검수상태, 메시지유형 (드롭다운, 기본값: 전체) | 조회, 초기화 │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ [영역 B] 알림톡 템플릿 관리 │ +│ · 섹션 제목 + 툴바(템플릿갱신, 템플릿등록, 선택 검수요청, 선택삭제, 저장) │ +│ · 템플릿 그리드 (체크박스 다중 선택) │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ [영역 C] 변수관리 │ +│ · 섹션 제목 + 툴바(변수추가, 선택삭제, 저장) │ +│ · 변수 그리드 │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. 영역 A: 검색 필터 + +### 4-1. 필드 정의 + +| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 | +| ---------- | --------- | ---- | ------ | ------------------ | --------------------------------------------------------- | +| 템플릿코드 | 텍스트 | N | 공백 | `templateCode` | 부분/완전일치 여부는 백엔드 정책 확인 필요 | +| 템플릿명 | 텍스트 | N | 공백 | `templateName` | 부분/완전일치 여부는 백엔드 정책 확인 필요 | +| 템플릿내용 | 텍스트 | N | 공백 | - | 현재 `NtftalkTempSearchParams`에 미정의(백엔드 확장 필요) | +| 채널 | 드롭다운 | N | 전체 | `senderKey` | 화면명 `@채널`과 `senderKey` 매핑 | +| 검수상태 | 드롭다운 | N | 전체 | `inspectionStatus` | 예: 승인/반려/요청 등 코드값 | +| 메시지유형 | 드롭다운 | N | 전체 | - | 현재 `NtftalkTempSearchParams`에 미정의(백엔드 확장 필요) | + +### 4-2. 버튼 + +| 버튼명 | 동작 | +| ------ | ----------------------------------------------------------------------- | +| 조회 | 현재 필터 조건으로 영역 B 목록을 재조회한다. 페이지는 1페이지로 초기화. | +| 초기화 | 검색 필드를 기본값(공백·전체)으로 되돌린다. | + +--- + +## 5. 영역 B: 알림톡 템플릿 관리 + +### 5-1. 툴바 버튼 + +| 버튼명 | 동작(요구사항) | +| ------------- | ------------------------------------------------------- | +| 템플릿갱신 | API를 재조회하여 템플릿 목록을 갱신한다. | +| 템플릿등록 | 신규 템플릿 등록 다이얼로그를 오픈한다. | +| 선택 검수요청 | 체크된 행에 대해 검수 요청 API를 호출한다. | +| 선택삭제 | 체크된 행을 삭제한다. 데이터베이스에는 삭제되지 않는다. | +| 저장 | 그리드에서 변경된 내용을 일괄 저장한다. | + +※ 실제 API·권한은 백엔드 정책에 따른다. + +### 5-2. 그리드 컬럼 + +| 표시명 | Field | 비고 | +| ---------- | ----------------------- | ----------------------------------------- | +| (선택) | — | 체크박스, 다중 선택 | +| @채널 | `senderKey` | | +| 템플릿코드 | `templateCode` | | +| 템플릿명 | `templateName` | | +| 메시지유형 | `templateMessageType` | | +| 템플릿내용 | `templateContent` | 긴 텍스트, 말줄임 처리 | +| 강조유형 | `templateEmphasizeType` | | +| 버튼정보 | `buttons` | | +| 검수상태 | `inspectionStatus` | | +| 상태 | `status` | | +| 등록일자 | `createdAt` | ISO datetime 문자열 (YYYY-MM-DD HH:mm:ss) | + +### 5-3. 행 상호작용 + +- 행 **클릭** 시: 해당 템플릿의 상세 다이얼로그를 오픈한다. +- 다중 체크는 **삭제·검수요청** 등 일괄 작업용으로 사용한다. + +--- + +## 6. 영역 C: 변수관리 + +알림톡 템플릿에 사용되는 공통 변수 목록을 관리한다. + +### 6-1. 툴바 버튼 + +| 버튼명 | 동작 | +| -------- | ----------------------------- | +| 변수추가 | 변수 행 추가(인라인). | +| 선택삭제 | 선택된 변수 행 삭제. | +| 저장 | 추가·수정·삭제 반영 API 호출. | + +### 6-2. 그리드 컬럼 + +| 표시명 | Field | 비고 | +| -------- | --------- | ----------------------------------------- | +| 순번 | — | 화면 표시용 row index(서버 필드 없음) | +| 변수코드 | `varCd` | | +| 변수명 | `varNm` | | +| 설명 | `varDesc` | | +| 등록일자 | `regDt` | ISO datetime 문자열 (YYYY-MM-DD HH:mm:ss) | +| 등록자 | `regr` | | + +--- + +## 7. API/DTO 명세 + +### 7-1. 공통 + +- Base Endpoint Prefix: `/ntftalk-service/api/v1` + +### 7-2. 템플릿 API + +Base: `ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP`(`/ntftalk-service/api/v1/ntftalk-temp`) + +| 기능 | Method | URL | Request DTO | Response DTO | API | +| ---------------- | ------ | ------------------------------ | ------------------------------------------------------ | --------------------- | -------------------- | +| 템플릿 목록 조회 | GET | `/ntftalk-temp` | `NtftalkTempSearchParams` | `NtftalkTempPageList` | `getNtftalkTempPage` | +| 템플릿 상세 조회 | GET | `/ntftalk-temp/{templateCode}` | Path: `templateCode` | `NtftalkTemp` | `getNtftalkTemp` | +| 템플릿 등록 | POST | `/ntftalk-temp` | `NtftalkTempSavePayload` | `NtftalkTemp` | `saveNtftalkTemp` | +| 템플릿 수정 | PUT | `/ntftalk-temp/{templateCode}` | Path: `templateCode`, Body: `NtftalkTempUpdatePayload` | `NtftalkTemp` | `updateNtftalkTemp` | +| 템플릿 삭제 | DELETE | `/ntftalk-temp/{templateCode}` | Path: `templateCode` | 없음 | `deleteNtftalkTemp` | + +### 7-3. 변수 API + +Base: `ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR`(`/ntftalk-service/api/v1/ntftalk-var`) + +| 기능 | Method | URL | Request DTO | Response DTO | API | +| -------------- | ------ | ---------------------- | ---------------------------------------------- | -------------------- | ------------------- | +| 변수 목록 조회 | GET | `/ntftalk-var` | `NtftalkVarSearchParams` | `NtftalkVarPageList` | `getNtftalkVarPage` | +| 변수 상세 조회 | GET | `/ntftalk-var/{varCd}` | Path: `varCd` | `NtftalkVar` | `getNtftalkVar` | +| 변수 등록 | POST | `/ntftalk-var` | `NtftalkVarSavePayload` | `NtftalkVar` | `saveNtftalkVar` | +| 변수 수정 | PUT | `/ntftalk-var/{varCd}` | Path: `varCd`, Body: `NtftalkVarUpdatePayload` | `NtftalkVar` | `updateNtftalkVar` | +| 변수 삭제 | DELETE | `/ntftalk-var/{varCd}` | Path: `varCd` | 없음 | `deleteNtftalkVar` | diff --git a/apps/cs/src/entities/ntftalk/api/ntftalk-temp-service.ts b/apps/cs/src/entities/ntftalk/api/ntftalk-temp-service.ts new file mode 100644 index 000000000..190e9964a --- /dev/null +++ b/apps/cs/src/entities/ntftalk/api/ntftalk-temp-service.ts @@ -0,0 +1,56 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import { + type NtftalkTemp, + type NtftalkTempPageList, + type NtftalkTempSavePayload, + type NtftalkTempSearchParams, + type NtftalkTempService, + type NtftalkTempUpdatePayload, +} from "../model/ntftalk-temp-interface"; + +class NtftalkTempApiService implements NtftalkTempService { + async getNtftalkTempPage( + params: NtftalkTempSearchParams, + ): Promise { + const response = await client.get( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}`, + { params }, + ); + return response.data; + } + + async getNtftalkTemp(templateCode: string): Promise { + const response = await client.get( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}/${encodeURIComponent(templateCode)}`, + ); + return response.data; + } + + async saveNtftalkTemp(payload: NtftalkTempSavePayload): Promise { + const response = await client.post( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}`, + payload, + ); + return response.data; + } + + async updateNtftalkTemp( + templateCode: string, + payload: NtftalkTempUpdatePayload, + ): Promise { + const response = await client.put( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}/${encodeURIComponent(templateCode)}`, + payload, + ); + return response.data; + } + + async deleteNtftalkTemp(templateCode: string): Promise { + await client.delete( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}/${encodeURIComponent(templateCode)}`, + ); + } +} + +export const NtftalkTempApi = new NtftalkTempApiService(); diff --git a/apps/cs/src/entities/ntftalk/api/ntftalk-var-service.ts b/apps/cs/src/entities/ntftalk/api/ntftalk-var-service.ts new file mode 100644 index 000000000..972ca0208 --- /dev/null +++ b/apps/cs/src/entities/ntftalk/api/ntftalk-var-service.ts @@ -0,0 +1,56 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import { + type NtftalkVar, + type NtftalkVarPageList, + type NtftalkVarSavePayload, + type NtftalkVarSearchParams, + type NtftalkVarService, + type NtftalkVarUpdatePayload, +} from "../model/ntftalk-var-interface"; + +class NtftalkVarApiService implements NtftalkVarService { + async getNtftalkVarPage( + params: NtftalkVarSearchParams, + ): Promise { + const response = await client.get( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}`, + { params }, + ); + return response.data; + } + + async getNtftalkVar(varCd: string): Promise { + const response = await client.get( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}/${encodeURIComponent(varCd)}`, + ); + return response.data; + } + + async saveNtftalkVar(payload: NtftalkVarSavePayload): Promise { + const response = await client.post( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}`, + payload, + ); + return response.data; + } + + async updateNtftalkVar( + varCd: string, + payload: NtftalkVarUpdatePayload, + ): Promise { + const response = await client.put( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}/${encodeURIComponent(varCd)}`, + payload, + ); + return response.data; + } + + async deleteNtftalkVar(varCd: string): Promise { + await client.delete( + `${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}/${encodeURIComponent(varCd)}`, + ); + } +} + +export const NtftalkVarApi = new NtftalkVarApiService(); diff --git a/apps/cs/src/entities/ntftalk/index.ts b/apps/cs/src/entities/ntftalk/index.ts new file mode 100644 index 000000000..87c3dfe2c --- /dev/null +++ b/apps/cs/src/entities/ntftalk/index.ts @@ -0,0 +1,20 @@ +export { NtftalkVarApi } from "./api/ntftalk-var-service"; +export { NtftalkTempApi } from "./api/ntftalk-temp-service"; +export type { + NtftalkVar, + NtftalkVarPageList, + NtftalkVarSavePayload, + NtftalkVarSearchParams, + NtftalkVarUpdatePayload, +} from "./model/ntftalk-var-interface"; +export type { + NtftalkBtnTemp, + NtftalkBtnTempSavePayload, + NtftalkQuickTemp, + NtftalkQuickTempSavePayload, + NtftalkTemp, + NtftalkTempPageList, + NtftalkTempSavePayload, + NtftalkTempSearchParams, + NtftalkTempUpdatePayload, +} from "./model/ntftalk-temp-interface"; diff --git a/apps/cs/src/entities/ntftalk/model/ntftalk-temp-interface.ts b/apps/cs/src/entities/ntftalk/model/ntftalk-temp-interface.ts new file mode 100644 index 000000000..79d537b28 --- /dev/null +++ b/apps/cs/src/entities/ntftalk/model/ntftalk-temp-interface.ts @@ -0,0 +1,106 @@ +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface NtftalkTempSearchParams extends PageableParams { + templateCode?: string; + templateName?: string; + inspectionStatus?: string; + senderKey?: string; + status?: string; +} + +export interface NtftalkBtnTemp { + templateCode: string; + name: string; + ordering: number; + linkType: string; + linkMo: string; + linkPc: string; + linkAnd: string; + linkIos: string; +} + +export interface NtftalkQuickTemp { + templateCode: string; + name: string; + linkType: string; + linkMo: string; + linkPc: string; + linkAnd: string; + linkIos: string; +} + +export interface NtftalkTemp { + templateCode: string; + senderKey: string; + senderKeyType: string; + templateName: string; + templateContent: string; + templateMessageType: string; + templateEmphasizeType: string; + templateImageUrl: string; + inspectionStatus: string; + status: string; + createdAt: string; + modifiedAt: string; + buttons?: NtftalkBtnTemp[]; + quickReplies?: NtftalkQuickTemp[]; +} + +export interface NtftalkBtnTempSavePayload { + templateCode?: string; + name: string; + ordering: number; + linkType: string; + linkMo?: string; + linkPc?: string; + linkAnd?: string; + linkIos?: string; +} + +export interface NtftalkQuickTempSavePayload { + templateCode?: string; + name: string; + linkType: string; + linkMo?: string; + linkPc?: string; + linkAnd?: string; + linkIos?: string; +} + +export interface NtftalkTempSavePayload { + templateCode: string; + senderKey: string; + senderKeyType: string; + templateName: string; + templateContent: string; + templateMessageType: string; + templateEmphasizeType: string; + templateImageUrl?: string; + inspectionStatus?: string; + buttons?: NtftalkBtnTempSavePayload[]; + quickReplies?: NtftalkQuickTempSavePayload[]; +} + +export interface NtftalkTempUpdatePayload { + templateName: string; + templateContent: string; + inspectionStatus?: string; + status?: string; + buttons?: NtftalkBtnTempSavePayload[]; + quickReplies?: NtftalkQuickTempSavePayload[]; +} + +export type NtftalkTempPageList = PageableList; + +export interface NtftalkTempService { + getNtftalkTempPage( + params: NtftalkTempSearchParams, + ): Promise; + getNtftalkTemp(templateCode: string): Promise; + saveNtftalkTemp(payload: NtftalkTempSavePayload): Promise; + updateNtftalkTemp( + templateCode: string, + payload: NtftalkTempUpdatePayload, + ): Promise; + deleteNtftalkTemp(templateCode: string): Promise; +} diff --git a/apps/cs/src/entities/ntftalk/model/ntftalk-var-interface.ts b/apps/cs/src/entities/ntftalk/model/ntftalk-var-interface.ts new file mode 100644 index 000000000..7a52a7a89 --- /dev/null +++ b/apps/cs/src/entities/ntftalk/model/ntftalk-var-interface.ts @@ -0,0 +1,42 @@ +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface NtftalkVarSearchParams extends PageableParams { + varCd?: string; + varNm?: string; + regr?: string; +} + +export interface NtftalkVar { + varCd: string; + sender: string; + varNm: string; + varDesc: string; + regDt: string; + regr: string; +} + +export interface NtftalkVarSavePayload { + varCd: string; + sender?: string; + varNm: string; + varDesc?: string; + regr?: string; +} + +export interface NtftalkVarUpdatePayload { + varNm: string; + varDesc?: string; +} + +export type NtftalkVarPageList = PageableList; + +export interface NtftalkVarService { + getNtftalkVarPage(params: NtftalkVarSearchParams): Promise; + getNtftalkVar(varCd: string): Promise; + saveNtftalkVar(payload: NtftalkVarSavePayload): Promise; + updateNtftalkVar( + varCd: string, + payload: NtftalkVarUpdatePayload, + ): Promise; + deleteNtftalkVar(varCd: string): Promise; +} diff --git a/apps/cs/src/pages/ntftalk-temp/page.tsx b/apps/cs/src/pages/ntftalk-temp/page.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index db8eda72c..5d1cb6bcd 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -66,6 +66,10 @@ export const ENDPOINT = { POST_EVALUATION: "/board-service/api/v1/pstEvl", STATISTICS: "/board-service/api/v1/stat/bbs", }, + NTFTALK_SERVICE: { + NTFTALK_VAR: "/ntftalk-service/api/v1/ntftalk-var", + NTFTALK_TEMP: "/ntftalk-service/api/v1/ntftalk-temp", + }, }; export const JWT_EXPIRATION_TIME = 1800000;