# 대시보드 → n8n → Slack 정기 리포트 연동 설계 작성일: 2026-06-23 ## 1. 목적 / 범위 문자ON 관리자 대시보드(`/cmm/main/mainPage.do`)의 주요 운영 지표를 **매일(또는 매주) 정해진 시각에 Slack 채널로 요약 발송**한다. - 연동 방식: 앱에 **신규 JSON API 엔드포인트**를 추가하고, **n8n이 스케줄로 호출**하여 Slack으로 게시(pull 방식). - 포함 지표(1차): **매출(일/월), 회원 현황, 문자 발송 현황**. - 인증: **공유 토큰**(헤더) 단독. (회사 내부망 사용 전제로 IP 허용목록은 제외) - 비범위(추후): 발신번호/환불 대기 지표, 실시간 이벤트 알림, IP 허용목록. ## 2. 현황 (재사용 자산) `itn.let.main.web.EgovMainController`가 대시보드 데이터를 아래 서비스 메서드로 조회하고 있으며, **그대로 재사용**한다(신규 SQL 없음). - 매출: `MjonPayService.selectPayDayChartDashboard`, `selectPayMonthChartDashboard`, `selectPayYearChart` - 회원: `EgovConectStatsService.selectMberTotStats`, `selectMberCmpHstStatsDashboard`, `selectTodaySttCountString` - 문자: `MjonMsgService.selectAgentSmsCountStatMainList`, `selectMemDayChartDashboard` ## 3. 엔드포인트 계약 (Contract) **요청** ``` GET /api/dashboard/summary.do Header: X-DASHBOARD-TOKEN: <공유 토큰> (필수) Query : ?date=YYYY-MM-DD (선택, 미지정 시 오늘) ``` **응답** — 앱은 Slack 포맷이 아닌 **순수 도메인 JSON**만 반환(Slack 표현은 n8n 담당, 앱-Slack 결합도 제거). ```jsonc { "isSuccess": true, "msg": "", "baseDate": "2026-06-23", "sales": { // 매출(VAT 포함, 단위: 원, 숫자) "todayAmt": 1234500, // 당일 매출액 "dayChart": [ {"date":"06-20","amt":900000}, ... ], // 최근 4일 "monthChart": [ {"month":"6월","amt":25000000}, ... ], // 최근 4개월 "yearAmtSum": 310000000 // 연간 누적 }, "members": { // 회원 현황 "userNorCnt": 0, "userComCnt": 0, // 개인/기업 정상회원수 "mberNorTot": 0, // 정상회원 합계 "mberBlockTot": 0, "mberSecessTot": 0,// 정지/탈퇴 누적 "cmpApply": 0, "cmpApprove": 0, "cmpReject": 0, // 기업회원 신청/승인/반려 "todayExpireCnt": 0, "todayStopCnt": 0 // 금일 탈퇴/정지 }, "sms": { // 문자 발송 현황(전송사별) "agents": [ {"agentCode":"ITN019","succCnt":12000,"failCnt":30,"succRate":99}, ... ], "todaySignupCnt": 0 // 일 회원가입자수(참고) } } ``` - 원본 서비스가 문자열로 반환하는 금액/건수는 서비스 계층에서 **숫자로 정규화**한다. - 천단위 콤마/원화 표기 등 **표현 포맷은 n8n**에서 처리한다(앱은 raw 숫자만 제공). - 누락 지표는 1차 범위에서 제외하며, 향후 `sales`/`members`/`sms` 블록에 필드를 추가하는 식으로 확장한다. ## 4. 인증 / 보호 - `globals.properties`에 `Globals.dashboard.api.token` 추가, 환경별 `globals_*.properties`에서 override. - 신규 인터셉터 `DashboardApiAuthInterceptor`를 **`/api/dashboard/**` 경로에만** 매핑(`egov-interceptor-servlet.xml`). - 요청 헤더 `X-DASHBOARD-TOKEN` 값이 설정 토큰과 불일치/누락 → **401** JSON(`{"isSuccess":false,"msg":"unauthorized"}`)을 직접 작성하고 컨트롤러 진입 차단. - 엔드포인트가 `*.do`이므로 Spring Security의 DB 기반 URL 권한(`context-security.xml`)에 걸릴 수 있다 → `/api/dashboard/**`를 `security="none"`으로 예외 추가(토큰으로 자체 보호, 세션 불필요). - 토큰 값은 응답/로그에 노출 금지. ## 5. Java 컴포넌트 구성 (4종 패턴) 신규 모듈 `itn.let.mjo.dashboardapi`: | 파일 | 역할 | |---|---| | `web/DashboardApiController.java` | **`@RestController`**. `GET /api/dashboard/summary.do` → `getDashboardSummary(...)`가 `DashboardSummaryVO`를 직접 반환(Jackson 직렬화, jsonView 미사용). 얇게 유지 | | `service/DashboardSummaryService.java` | 인터페이스 | | `service/DashboardSummaryVO.java` | 응답 DTO(sales/members/sms 중첩 + isSuccess/msg/baseDate). Jackson 직렬화용 getter 포함 | | `service/impl/DashboardSummaryServiceImpl.java` | `@Service`. 기존 `mjonPayService`·`conectStatsService`·`mjonMsgService`를 주입해 조합·숫자 정규화 | | `web/DashboardApiAuthInterceptor.java` | 토큰 헤더 검증 인터셉터 | 설계 근거 / 규칙: - **DAO 신규 없음** — 기존 검증된 서비스 메서드만 호출. - `@RestController`는 `@Controller`를 메타포함 → 디스패처 컨텍스트의 `@Controller` 스캔에 잡힘. Jackson 컨버터는 `` + `jackson-databind` 의존성 + 기존 `@ResponseBody` 선례로 동작 검증됨. - 빈 스캔 분리 유지: 컨트롤러/인터셉터=디스패처 컨텍스트, 서비스=루트 컨텍스트. - 관리자 로그 AOP가 `itn..*Controller.select*`에 걸리므로, 메서드명을 `getDashboardSummary`로(=`select*` 아님) 지어 불필요한 잡로그를 피한다. - 지표 블록별 try/catch — 개별 지표 실패가 전체 응답을 막지 않도록 하고 실패 블록은 null/0으로 둔다(기존 대시보드 Ajax 관례). ## 6. n8n 워크플로우 / Slack ``` [Schedule Trigger] 매일 09:00 (cron) ↓ [HTTP Request] GET https:///api/dashboard/summary.do Header X-DASHBOARD-TOKEN = {{ $env.MJON_DASH_TOKEN }} ↓ [Code (Function)] 도메인 JSON → Slack Block Kit 변환(콤마/원화 포맷 여기서) ↓ [Slack node] 지정 채널 게시(OAuth 또는 Incoming Webhook) ``` - Slack 메시지: 제목(`📊 문자ON 일일 리포트 — {baseDate}`) + 매출/회원/문자 섹션을 Block Kit `section` 블록으로 구성. - 토큰은 n8n 환경변수 `MJON_DASH_TOKEN`에 보관(워크플로우 JSON에 평문 미포함). - 산출물: import 가능한 `n8n-workflow.json` 예시 + Code 노드 포맷 스니펫. ## 7. 에러처리 / 테스트 / 배포 - 앱 서비스: 지표 블록별 try/catch, 전역 실패 시 `isSuccess:false` + `msg`. 인증 실패는 인터셉터에서 401 JSON. - 테스트: 본 저장소는 단위테스트 인프라가 없음(surefire `skipTests=true`) → **수동 검증**. - `curl -H "X-DASHBOARD-TOKEN: " http://localhost/api/dashboard/summary.do` (토큰 유/무 케이스 모두). - n8n 워크플로우 Execute → Slack 채널 수신 확인. - 설정: `globals.properties`에 `Globals.dashboard.api.token` 추가, 환경별 override. 운영 토큰은 별도 공유. - 배포: WAR 재빌드 후 WAS 반영. 신규 엔드포인트는 추가 전용이라 기존 기능 영향 없음. ## 8. 결정 사항 요약 - 연동: pull(신규 JSON API + n8n 스케줄) ✅ - 집계 위치: 신규 Service(접근안 A) ✅ - 컨트롤러: `@RestController` ✅ - 인증: 공유 토큰 단독(IP 허용목록 제외) ✅ - 지표: 매출(일/월) / 회원 현황 / 문자 발송 현황 ✅