18 KiB
피드백 작성 페이지와 관리페이지 데이터 흐름
1. 전체 구조
현재 구조는 피드백 작성 웹과 관리 콘솔이 분리되어 있습니다. 작성 웹은 직접 ABC API나 DB에 접근하지 않고, Next.js 서버 Proxy를 통해 외부 관리 콘솔 API에 요청합니다.
피드백 작성 페이지
/support/{workspaceCode}/new
│
│ POST /api/support/workspaces/{workspaceCode}/submit
▼
apps/web의 Next.js API Proxy
│
│ POST /api/support/workspaces/{workspaceCode}/tickets
▼
외부 관리 콘솔 API
https://feedback.hmac.kr/api/support
│
├─ Secretary DB: support_tickets 저장
├─ ABC API: feedbacks 저장
├─ abc_feedback_mappings: 두 ID 연결
└─ 첨부파일: Secretary Local/R2 저장
▼
관리 콘솔에서 조회·처리
피드백 전용 배포에서는 브라우저 요청을 same-origin으로 유지하고, 외부 관리 콘솔 주소는 서버 환경변수로만 사용합니다.
관련 설정:
NEXT_PUBLIC_FEEDBACK_ONLY=trueSUPPORT_CONSOLE_API_BASE_URL=https://feedback.hmac.kr/api/supportNEXT_PUBLIC_API_BASE_URL은 빈 값으로 설정
1.1 전체 데이터 흐름도
flowchart LR
U[사용자] --> W[피드백 작성 페이지<br/>/support/{workspaceCode}/new]
W -->|FormData<br/>제목·내용·필드·첨부파일| P[Next.js 서버 Proxy]
P -->|POST /api/support/workspaces/{workspaceCode}/tickets| S[외부 관리 콘솔 API<br/>Secretary]
S -->|1. 내부 티켓 저장| T[(Secretary DB<br/>baron_support)]
S -->|2. ABC 피드백 생성| A[ABC API]
A --> F[(ABC MySQL<br/>feedbacks)]
S --> M[(abc_feedback_mappings)]
M -. ticket_id ↔ feedback_id .-> T
M -. ticket_id ↔ feedback_id .-> F
S -->|첨부파일 저장| X[(Secretary Local/R2<br/>+ attachments 메타데이터)]
T --> C[관리 콘솔 목록·상세 화면]
F --> C
X --> C
핵심 저장 위치
flowchart TD
D[피드백 등록 데이터]
D --> D1[ABC MySQL<br/>feedbacks.data JSON<br/>원본 피드백 본문]
D --> D2[Secretary DB<br/>support_tickets<br/>관리용 티켓·상태·권한]
D --> D3[Secretary DB<br/>abc_feedback_mappings<br/>두 시스템 ID 연결]
D --> D4[Secretary Local/R2<br/>첨부파일 바이너리]
D --> D5[Secretary DB<br/>attachments<br/>첨부파일 메타데이터]
D1 -. 검색 활성화 시 .-> D6[OpenSearch<br/>검색용 색인]
ID 연결 구조
flowchart LR
W[workspace_code<br/>예: EGBIM_DEMO]
W --> ST[Secretary ticket_id]
ST --> MAP[abc_feedback_mappings]
MAP --> AF[ABC feedback_id]
AF --> CH[ABC project_id / channel_id]
ticket_id와 feedback_id는 서로 다른 시스템에서 생성되는 별도 ID입니다. 관리 콘솔은 abc_feedback_mappings 또는 응답의 extra_fields.abc_feedback_id를 이용해 두 데이터를 결합합니다.
1.2 네이버웍스 알림 흐름도
피드백 등록 자체는 먼저 ABC와 Secretary에 저장되고, 저장 완료 후 ABC 백엔드 이벤트를 통해 네이버웍스 알림이 발송됩니다.
sequenceDiagram
participant User as 사용자
participant Web as 작성 웹
participant Secretary as Secretary API
participant ABC as ABC API
participant ABCDB as ABC MySQL
participant Notify as 알림 서비스
participant NW as 네이버웍스 API
participant Log as notification_deliveries
User->>Web: 피드백 등록
Web->>Secretary: POST /workspaces/{code}/tickets
Secretary->>Secretary: support_tickets 저장
Secretary->>ABC: feedback 생성 요청
ABC->>ABCDB: feedbacks 저장
ABC-->>Notify: FEEDBACK_CREATION 이벤트
Notify->>Notify: 담당자·프로젝트 관리자·작성자 계산
Notify->>NW: 이메일로 사용자 ID 조회
NW-->>Notify: 네이버웍스 userId
Notify->>NW: Bot 개인 메시지 발송
Notify->>Log: SENT / FAILED / SKIPPED 기록
ABC-->>Secretary: abc_feedback_id 반환
Secretary-->>Web: ticket_id·동기화 상태 반환
2. 피드백 작성 페이지
작성 페이지는 다음 파일입니다.
apps/web/src/pages/support/[workspaceCode]/new.tsx
양식 조회
페이지 진입 시 workspace별 양식을 조회합니다.
GET /api/support/workspaces/{workspaceCode}/form-template
Next.js API Proxy는 이를 외부 관리 콘솔 API로 전달합니다.
GET {SUPPORT_CONSOLE_API_BASE_URL}/workspaces/{workspaceCode}/form-template
양식의 workspace code는 프로젝트와 ABC 프로젝트·채널을 연결하는 식별자입니다.
등록 요청
사용자가 등록하면 브라우저는 FormData를 생성합니다.
주요 필드는 다음과 같습니다.
titledescriptioncategory_codeis_secretticket_typerequires_approvalextra_fieldsattachments
작성 페이지는 다음 내부 API를 호출합니다.
POST /api/support/workspaces/{workspaceCode}/submit
관련 파일:
apps/web/src/pages/api/support/workspaces/[workspaceCode]/submit.ts
이 API는 multipart 요청을 파싱한 후 외부 관리 콘솔로 다시 전달합니다.
POST {SUPPORT_CONSOLE_API_BASE_URL}/workspaces/{workspaceCode}/tickets
인증 정보는 브라우저에서 별도 입력받지 않고, 현재 로그인된 BARON-SSO 세션의 Authorization 헤더를 Proxy가 전달합니다.
3. Secretary API에서의 저장
Secretary API의 티켓 생성 로직은 다음 파일에 있습니다.
apps/secretary-api/app/services/ticket_service.py
티켓 등록 시 다음 순서로 처리됩니다.
support_tickets에 내부 관리 티켓을 생성합니다.- workspace에 매핑된 ABC 프로젝트와 채널을 확인합니다.
- ABC API에 피드백을 생성합니다.
abc_feedback_mappings에 Secretary 티켓과 ABC 피드백의 연결 정보를 저장합니다.- 첨부파일을 Secretary 저장소에 저장합니다.
- 동기화 상태를
SYNCED로 변경합니다. - 티켓 ID와 동기화 상태를 작성 웹에 반환합니다.
API 라우트:
POST /api/workspaces/{workspaceCode}/tickets
구현 파일:
apps/secretary-api/app/api/routes/tickets.py
4. 데이터베이스 구조
Secretary DB
Secretary 쪽에는 관리 업무용 데이터가 저장됩니다.
| 테이블 | 역할 |
|---|---|
support_tickets |
내부 관리 티켓 본체 |
abc_feedback_mappings |
Secretary 티켓 ID와 ABC 피드백 ID 연결 |
ticket_comments |
사용자 댓글 및 내부 메모 |
attachments |
첨부파일 메타데이터 |
user_workspace_access |
사용자별 workspace 권한 |
주요 모델은 다음 파일에 정의되어 있습니다.
apps/secretary-api/app/db/models.py
ABC DB
ABC 쪽에는 기존 피드백 데이터가 저장됩니다.
| 테이블 | 역할 |
|---|---|
feedbacks |
피드백 본문과 동적 필드를 data JSON으로 저장 |
channels |
피드백이 소속된 채널 |
feedbacks_issues_issues |
피드백과 이슈의 다대다 연결 |
ABC 피드백 엔티티:
apps/api/src/domains/admin/feedback/feedback.entity.ts
5. 티켓 ID와 피드백 ID
Secretary 티켓 ID와 ABC 피드백 ID는 서로 다른 식별자입니다.
Secretary ticket_id = 관리 티켓 식별자
ABC feedback_id = ABC 피드백 식별자
abc_feedback_mappings = 두 식별자를 연결하는 매핑
관리 API 응답에서는 보통 다음과 같이 ABC ID가 함께 노출됩니다.
{
"ticket_id": 101,
"extra_fields": {
"abc_feedback_id": "345"
}
}
이 매핑을 이용해 관리 콘솔은 티켓 정보와 ABC 피드백 정보, 이슈 연결 정보를 함께 처리합니다.
6. 관리페이지 조회 흐름
실제 운영 관리 콘솔은 Secretary API를 기준으로 티켓 목록을 조회합니다.
GET /api/support/workspaces/{workspaceCode}/tickets
│
├─ support_tickets 조회
├─ abc_feedback_mappings 확인
├─ 담당자·권한 정보 결합
├─ 댓글·첨부파일 정보 결합
└─ ABC 피드백과 상태 동기화
피드백 전용 모드에서 이 저장소의 목록 Proxy는 외부 관리 콘솔 API를 그대로 호출합니다.
관련 파일:
apps/web/src/pages/api/support/workspaces/[workspaceCode]/tickets.ts
관리 콘솔에서 처리하는 주요 데이터는 다음과 같습니다.
- 피드백 목록과 상세 내용
- 피드백 처리 상태
- 담당자 지정
- 사용자 댓글
- 내부 메모
- 첨부파일
- ABC 이슈 연결 상태
7. 이 저장소에 있는 기존 ABC 관리자 화면
이 저장소에는 기존 ABC 관리자 화면도 남아 있습니다.
/main/project/{projectId}/feedback
페이지:
apps/web/src/pages/main/project/[projectId]/feedback.tsx
이 화면은 Secretary 티켓 목록이 아니라 ABC 관리자 API를 직접 조회합니다.
POST /api/admin/projects/{projectId}/channels/{channelId}/feedbacks/search
ABC API의 검색 요청은 다음 서버 로직으로 처리됩니다.
apps/api/src/domains/admin/feedback/feedback.service.ts
검색 데이터는 설정에 따라 다음 중 하나에서 조회됩니다.
- ABC MySQL의
feedbacks테이블 - OpenSearch의 피드백 인덱스
따라서 이 화면은 실제 외부 관리 콘솔의 Secretary 기반 관리 화면과 데이터 조회 기준이 다를 수 있습니다.
8. 화면별 역할 비교
| 화면 | 주요 데이터 원천 | 역할 |
|---|---|---|
/support/{workspaceCode}/new |
외부 관리 콘솔 등록 API | 피드백 작성 |
외부 관리 콘솔 feedback.hmac.kr |
Secretary + ABC | 실제 운영 관리 |
/main/project/{projectId}/feedback |
ABC API 직접 조회 | 기존 ABC 관리자 화면 |
/admin/issues |
Secretary 티켓 API 또는 스텁 | 데모·보조 관리 화면 |
/ops |
Secretary 티켓 API 또는 스텁 | 승인 큐 데모 화면 |
피드백 전용 전환 문서의 목표는 이 저장소에서 작성 화면만 제공하고, 목록·상세·관리 기능은 외부 관리 콘솔에서 유지하는 것입니다.
9. 첨부파일 흐름
작성 페이지에서 첨부파일을 선택하면 브라우저의 FormData에 포함됩니다.
브라우저
→ Next.js Proxy
→ 외부 관리 콘솔/Secretary API
→ Secretary Local 또는 R2 저장소
→ attachments 테이블에 메타데이터 저장
현재 작성 화면의 제한은 다음과 같습니다.
- 파일당 최대 30MB
- 전체 최대 30MB
- 최대 10개
- 이미지 외 일반 파일도 업로드 가능
첨부파일은 피드백 본문 JSON에 직접 저장되지 않고 별도 저장소와 attachments 메타데이터로 관리됩니다.
10. 네이버웍스 알림 흐름
피드백 작성 웹은 네이버웍스 API를 직접 호출하지 않습니다. 피드백 등록 후 ABC API와 기존 관리 콘솔의 백엔드 알림 파이프라인에서 네이버웍스 알림을 처리합니다.
피드백 작성
→ Secretary가 내부 티켓 저장
→ ABC API가 feedbacks 저장
→ FEEDBACK_CREATION 이벤트 발생
→ 알림 수신자 계산
→ 네이버웍스 사용자 ID 조회
→ 네이버웍스 Bot 메시지 발송
→ notification_deliveries에 결과 저장
10.1 알림 이벤트 발생
ABC의 FeedbackService.create는 피드백 저장 후 FEEDBACK_CREATION 이벤트를 발생시킵니다.
apps/api/src/domains/admin/feedback/feedback.service.ts
│
│ EventTypeEnum.FEEDBACK_CREATION
▼
apps/api/src/domains/admin/project/webhook/notification-internal.listener.ts
트랜잭션 커밋 전에 알림이 조회되지 않도록, 생성 이벤트 처리 시 저장 완료 후 피드백을 다시 조회합니다.
알림 이벤트에는 다음 정보가 포함됩니다.
projectIdchannelIdfeedbackId- 피드백 제목
- 작성자 이메일
- 담당자 이메일
- 현재 피드백 상태
- 비밀글 여부
10.2 알림 대상
수신자 계산은 notification-recipient.router.ts에서 수행합니다.
신규 피드백과 상태 변경 시 대상은 다음과 같습니다.
- 해당 피드백의 담당자
- 해당 프로젝트의
PROJECT_MANAGER또는Admin역할 사용자 - 피드백 작성자
관리자 댓글 등록 시에는 해당 피드백 작성자에게만 보냅니다.
같은 사용자가 여러 역할에 해당하면 이메일 기준으로 중복 제거하여 한 번만 발송합니다. 프로젝트 관리자는 ABC의 프로젝트 멤버와 Secretary의 관리자 조회 결과를 함께 사용합니다.
ABC project/channel
│
├─ ABC DB의 프로젝트 관리자 조회
├─ Secretary API의 프로젝트 관리자 조회
├─ 피드백 담당자 추가
└─ 피드백 작성자 추가
│
▼
이메일 기준 중복 제거
수신자 이메일은 네이버웍스 API에서 실제 사용자 ID로 변환됩니다. 이메일 또는 사용자 매핑이 확인되지 않으면 임의 사용자나 전체 방으로 대체 발송하지 않습니다.
10.3 네이버웍스 발송
실제 발송은 NaverWorksClient에서 수행합니다.
- 네이버웍스 Access Token을 확인합니다.
- 설정된 이메일로 네이버웍스 사용자 ID를 조회합니다.
- Bot ID와 사용자 ID를 사용해 개인 메시지를 발송합니다.
- 발송 성공·실패 결과를 저장합니다.
인증 방식은 두 가지를 지원합니다.
NAVER_WORKS_ACCESS_TOKEN직접 사용- Client ID, Client Secret, Service Account, Private Key를 이용한 JWT Bearer 인증
관련 환경변수는 API 서버에만 설정해야 합니다.
NAVER_WORKS_ENABLED
NAVER_WORKS_API_BASE_URL
NAVER_WORKS_AUTH_URL
NAVER_WORKS_ACCESS_TOKEN
NAVER_WORKS_BOT_ID
NAVER_WORKS_DEFAULT_ROOM_ID
NAVER_WORKS_CLIENT_ID
NAVER_WORKS_CLIENT_SECRET
NAVER_WORKS_SERVICE_ACCOUNT
NAVER_WORKS_PRIVATE_KEY
피드백 전용 작성 웹에는 이 값들을 넣지 않습니다.
10.4 메시지 구성
메시지는 notification-message.builder.ts에서 생성합니다.
기본 메시지에는 다음 정보가 포함됩니다.
- 이벤트 종류
- 피드백 ID
- 피드백 제목
- 상태 변경 전·후 상태
- 관리자 공개 댓글 내용
- 기존 관리자 상세 페이지 링크
비밀글 작성자에게 보내는 메시지는 제목·본문·댓글 원문을 노출하지 않고 피드백 ID 중심으로 최소화합니다. 전화번호, IP 주소, MAC 주소는 기본 알림에 포함하지 않습니다.
10.5 알림 대상 이벤트
| 이벤트 | 기본 수신자 | 조건 |
|---|---|---|
FEEDBACK_CREATION |
담당자, 프로젝트 관리자, 작성자 | 신규 피드백 등록 |
FEEDBACK_STATUS_CHANGE |
담당자, 프로젝트 관리자, 작성자 | 이전 상태와 현재 상태가 다름 |
FEEDBACK_COMMENT_CREATION |
작성자 | 관리자의 공개 댓글 등록 |
다음 이벤트는 현재 네이버웍스 피드백 알림 대상이 아닙니다.
- 내부 메모 등록·수정·삭제
- 관리자 공개 댓글 수정·삭제
- 이슈 상태 변경
- Gitea 상태 변경
- 같은 상태로 다시 저장한 상태 변경
10.6 중복 및 실패 처리
알림 서비스는 notification_deliveries 테이블에 이벤트별 발송 결과를 기록합니다.
주요 상태:
RECEIVED: 발송 처리 대상 등록SENT: 발송 성공SKIPPED: 대상 없음 또는 알림 제외FAILED: 발송 실패
이벤트와 수신자 조합으로 Idempotency Key를 생성하므로 동일 이벤트가 반복 수신되어도 같은 사용자에게 중복 발송하지 않습니다.
네이버웍스 발송은 네트워크 오류, 408, 429, 5xx 응답에 대해 제한적으로 재시도합니다. 400, 403, 사용자 매핑 실패와 같은 영구 오류는 반복하지 않고 실패 이력으로 남깁니다.
발송 실패는 피드백 등록 실패와 분리됩니다. 즉, 피드백이 ABC와 Secretary에 정상 저장된 뒤 네이버웍스 알림만 실패할 수 있습니다.
11. 현재 운영 기준 요약
사용자 입력
→ /support/{workspaceCode}/new
→ /api/support/workspaces/{workspaceCode}/submit
→ 외부 관리 콘솔 API
→ Secretary support_tickets 저장
→ ABC feedbacks 저장
→ abc_feedback_mappings로 연결
→ 외부 관리 콘솔에서 조회·상태변경·댓글·이슈처리
정리하면 작성 페이지와 관리페이지는 프론트엔드 상태를 직접 공유하지 않습니다. 두 시스템은 다음 정보를 기준으로 서버 데이터에서 연결됩니다.
workspace_code
→ Secretary ticket_id
→ abc_feedback_id
→ ABC project/channel
참고 파일
FEEDBACK_ONLY_TASKS.mdapps/web/src/pages/support/[workspaceCode]/new.tsxapps/web/src/pages/api/support/workspaces/[workspaceCode]/submit.tsapps/web/src/pages/api/support/workspaces/[workspaceCode]/tickets.tsapps/web/src/server/support-external.tsapps/web/src/server/support-auth.tsapps/secretary-api/app/api/routes/tickets.pyapps/secretary-api/app/services/ticket_service.pyapps/secretary-api/app/db/models.pyapps/api/src/domains/admin/feedback/feedback.entity.tsapps/api/src/domains/admin/feedback/feedback.service.tsapps/api/src/domains/admin/project/webhook/notification-internal.listener.tsapps/api/src/domains/admin/project/webhook/notification-recipient.router.tsapps/api/src/domains/admin/project/webhook/webhook-notification.service.tsapps/api/src/domains/admin/project/webhook/notification-message.builder.tsapps/api/src/domains/admin/project/webhook/naver-works.client.tsapps/api/src/domains/admin/project/webhook/notification-delivery.entity.ts