[cs] feat: 알림톡템플릿관리 페이지 생성

This commit is contained in:
2026-04-08 17:11:52 +09:00
parent f73901c78c
commit 07e842ecb2
8 changed files with 441 additions and 0 deletions
+157
View File
@@ -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` |
@@ -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<NtftalkTempPageList> {
const response = await client.get<NtftalkTempPageList>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}`,
{ params },
);
return response.data;
}
async getNtftalkTemp(templateCode: string): Promise<NtftalkTemp> {
const response = await client.get<NtftalkTemp>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}/${encodeURIComponent(templateCode)}`,
);
return response.data;
}
async saveNtftalkTemp(payload: NtftalkTempSavePayload): Promise<NtftalkTemp> {
const response = await client.post<NtftalkTemp>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}`,
payload,
);
return response.data;
}
async updateNtftalkTemp(
templateCode: string,
payload: NtftalkTempUpdatePayload,
): Promise<NtftalkTemp> {
const response = await client.put<NtftalkTemp>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}/${encodeURIComponent(templateCode)}`,
payload,
);
return response.data;
}
async deleteNtftalkTemp(templateCode: string): Promise<void> {
await client.delete(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_TEMP}/${encodeURIComponent(templateCode)}`,
);
}
}
export const NtftalkTempApi = new NtftalkTempApiService();
@@ -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<NtftalkVarPageList> {
const response = await client.get<NtftalkVarPageList>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}`,
{ params },
);
return response.data;
}
async getNtftalkVar(varCd: string): Promise<NtftalkVar> {
const response = await client.get<NtftalkVar>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}/${encodeURIComponent(varCd)}`,
);
return response.data;
}
async saveNtftalkVar(payload: NtftalkVarSavePayload): Promise<NtftalkVar> {
const response = await client.post<NtftalkVar>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}`,
payload,
);
return response.data;
}
async updateNtftalkVar(
varCd: string,
payload: NtftalkVarUpdatePayload,
): Promise<NtftalkVar> {
const response = await client.put<NtftalkVar>(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}/${encodeURIComponent(varCd)}`,
payload,
);
return response.data;
}
async deleteNtftalkVar(varCd: string): Promise<void> {
await client.delete(
`${ENDPOINT.NTFTALK_SERVICE.NTFTALK_VAR}/${encodeURIComponent(varCd)}`,
);
}
}
export const NtftalkVarApi = new NtftalkVarApiService();
+20
View File
@@ -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";
@@ -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<NtftalkTemp>;
export interface NtftalkTempService {
getNtftalkTempPage(
params: NtftalkTempSearchParams,
): Promise<NtftalkTempPageList>;
getNtftalkTemp(templateCode: string): Promise<NtftalkTemp>;
saveNtftalkTemp(payload: NtftalkTempSavePayload): Promise<NtftalkTemp>;
updateNtftalkTemp(
templateCode: string,
payload: NtftalkTempUpdatePayload,
): Promise<NtftalkTemp>;
deleteNtftalkTemp(templateCode: string): Promise<void>;
}
@@ -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<NtftalkVar>;
export interface NtftalkVarService {
getNtftalkVarPage(params: NtftalkVarSearchParams): Promise<NtftalkVarPageList>;
getNtftalkVar(varCd: string): Promise<NtftalkVar>;
saveNtftalkVar(payload: NtftalkVarSavePayload): Promise<NtftalkVar>;
updateNtftalkVar(
varCd: string,
payload: NtftalkVarUpdatePayload,
): Promise<NtftalkVar>;
deleteNtftalkVar(varCd: string): Promise<void>;
}
+4
View File
@@ -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;