Files

131 lines
7.0 KiB
Markdown

# 피드백 작성 페이지 서버
이 저장소는 BARON-SSO로 로그인한 사용자가 피드백을 작성하는 보안형 웹 서버입니다. 피드백 목록·상세 관리와 데이터 저장은 기존 관리 콘솔인 [`feedback.hmac.kr`](https://feedback.hmac.kr/)에서 담당합니다.
이 저장소를 관리 콘솔의 복사본이나 독립적인 피드백 DB로 운영하지 않습니다. 작성 페이지는 브라우저의 요청을 같은 출처(same-origin)로 유지하고, Next.js 서버가 관리 콘솔의 Support API로 요청을 중계합니다.
## 운영 구조
```text
사용자 브라우저
│ BARON-SSO 로그인 / 피드백 작성
피드백 작성 웹 (Next.js, 이 저장소)
│ 서버사이드 proxy
│ https://feedback.hmac.kr/api/support
관리 콘솔의 Secretary/ABC API
├─ 관리 티켓·권한·첨부파일 저장
├─ ABC 피드백 원본 저장
└─ 관리 콘솔에서 목록·상세·댓글·상태 처리
```
운영 Compose에는 `web` 서비스만 포함됩니다.
| 항목 | 값 |
| --- | --- |
| 작성 페이지 | `/support/{workspaceCode}/new` |
| 기본 예시 workspace | `EGBIM_DEMO` |
| 외부 API | `https://feedback.hmac.kr/api/support` |
| 기본 스테이징 주소 | `http://10.13.10.4:8864` |
| 외부 공개 컨테이너 | `web` 하나, 기본 host port `8864` |
| health check | `/api/health` |
## 동기화와 보안 경계
피드백 등록 요청은 다음 순서로 처리됩니다.
1. 사용자가 이 서버의 `/api/auth/baron-sso/login`에서 BARON-SSO에 로그인합니다.
2. callback에서 이 서버가 authorization code를 서버 간 통신으로 교환하고 사용자 정보를 조회합니다.
3. 이 서버가 자체 세션 JWT를 `HttpOnly` 쿠키에 저장합니다. `SSO_CLIENT_SECRET``JWT_SECRET`은 서버에서만 사용합니다.
4. 브라우저는 `/api/support/access`, `/form-template`, `/submit` 같은 same-origin API를 호출합니다.
5. Next.js proxy가 현재 세션의 `Authorization`을 관리 콘솔 API로 전달합니다.
6. 관리 콘솔이 Secretary와 ABC에 저장하고, 관리 콘솔에서 조회할 수 있게 합니다.
브라우저가 ABC API, Secretary API, DB, 첨부파일 저장소에 직접 접근하지 않도록 하는 것이 핵심입니다. `NEXT_PUBLIC_API_BASE_URL`은 운영에서 빈 값이어야 하며, `SUPPORT_CONSOLE_API_BASE_URL`은 서버 runtime 변수로만 주입합니다.
첨부파일은 파일당·전체 합계 최대 30MB, 최대 10개입니다. 작성 서버가 파일을 임시 파싱한 뒤 관리 콘솔 API로 multipart 요청을 전달하고, 실제 영구 저장은 관리 콘솔/Secretary가 담당합니다.
## 로컬 개발
필요 조건:
- Node.js `>=24.14.1`
- `pnpm@10.32.1`
- Docker 및 Docker Compose
전체 플랫폼을 로컬에서 실행할 때:
```bash
pnpm install
./start-local.sh
```
작성 웹만 기존 관리 콘솔에 연결해 확인하려면 `apps/web/.env.local`에 실제 테스트용 값을 넣고 다음을 실행합니다.
```bash
pnpm dev:local
```
실제 secret, API key, 개인키, 운영용 JWT secret은 `.env` 파일이나 저장소에 넣지 않습니다. 로컬에서는 별도 테스트용 값과 테스트용 BARON-SSO RP를 사용합니다.
## 검증 명령
```bash
pnpm lint
pnpm typecheck
pnpm build
```
E2E는 DB를 초기화할 수 있으므로 개발 데이터와 분리된 환경에서만 실행합니다.
```bash
pnpm test:e2e
```
## 스테이징 배포
`main` 브랜치 push 또는 Gitea Actions의 수동 실행으로 [`.gitea/workflows/deploy-staging.yml`](./.gitea/workflows/deploy-staging.yml)이 실행됩니다.
workflow는 다음 작업만 수행합니다.
1. 저장소를 checkout합니다.
2. 필수 Gitea Variables/Secrets가 있는지 검사합니다.
3. SSH로 스테이징 서버에 소스를 전송합니다.
4. 원격에서 `docker compose -f docker/docker-compose.prod.yml config --quiet`를 실행합니다.
5. `web` 이미지를 빌드하고 컨테이너를 재기동합니다.
6. `http://127.0.0.1:${WEB_PORT}/api/health`가 응답할 때까지 확인합니다.
환경값은 스테이징 서버의 `.env`를 자동으로 갱신하는 방식이 아니라 workflow에서 원격 `docker compose` 명령의 runtime 환경으로 전달됩니다. 수동 배포 시에도 같은 값을 명시해야 합니다.
Gitea 등록 절차와 키의 위치는 [`GITEA_VARIABLES.md`](./GITEA_VARIABLES.md), 배포 후 점검 순서는 [`STAGING_DEPLOYMENT_CHECKLIST.md`](./STAGING_DEPLOYMENT_CHECKLIST.md), 데이터 흐름은 [`FEEDBACK_DATA_FLOW.md`](./FEEDBACK_DATA_FLOW.md), 전체 동기화 운영 절차는 [`FEEDBACK_SYNC_GUIDE.md`](./FEEDBACK_SYNC_GUIDE.md)를 참고합니다.
## 가장 중요한 유의사항
- `NEXT_PUBLIC_API_BASE_URL``localhost`, `127.0.0.1`, 내부 Docker 주소를 넣지 않습니다. 운영 브라우저 요청은 빈 값으로 same-origin을 사용합니다.
- `SUPPORT_CONSOLE_API_BASE_URL``https://feedback.hmac.kr/api/support`여야 합니다. 끝의 `/api/support`를 생략하면 proxy가 `/api`를 보완하지만, 운영에서는 명시된 기본값을 유지합니다.
- 작성 서버와 관리 콘솔이 검증하는 JWT의 `JWT_SECRET`은 같은 값이어야 합니다. 임의로 서로 다른 값을 만들면 로그인은 성공해도 `/api/support/access``401`이 됩니다.
- BARON-SSO에 등록한 redirect URI의 scheme, host, port, path가 실제 접속 주소와 정확히 같아야 합니다: `/api/auth/baron-sso/callback`.
- `10.13.10.4:8864`는 내부 스테이징 기준 주소입니다. 외부 공개 시에는 TLS reverse proxy 뒤에 두고 `X-Forwarded-Proto`와 redirect URI를 HTTPS 기준으로 맞춥니다.
- 운영 Compose는 API·Secretary·MySQL을 띄우지 않습니다. 이 서버에 DB migration, `MASTER_API_KEY`, `SECRETARY_ABC_API_KEY`를 추가하는 것으로 동기화가 해결되지 않습니다.
- 기존 데이터가 있는 서버에서 `docker compose down -v`를 실행하지 않습니다. DB를 직접 관리하는 콘솔 서버와 이 작성 서버를 혼동하지 않습니다.
- Gitea Actions 로그에 secret을 출력하거나 `docker compose config` 결과를 공개 로그에 남기지 않습니다.
## 문제 해결 순서
1. 브라우저 주소와 BARON-SSO redirect URI를 확인합니다.
2. 브라우저 Network에서 `401`, `404`, `413`, `502` 응답을 확인합니다.
3. `401`이면 `SSO_CLIENT_ID`, `SSO_CLIENT_SECRET`, `JWT_SECRET`, 관리 콘솔의 JWT 검증 설정을 확인합니다.
4. `404`이면 `SUPPORT_CONSOLE_API_BASE_URL`과 workspace code를 확인합니다.
5. `413`이면 파일당 30MB·전체 30MB·최대 10개 제한을 확인합니다.
6. `502`이면 작성 서버에서 관리 콘솔로 나가는 네트워크와 외부 API 로그를 확인합니다.
7. 컨테이너 상태와 로그를 확인합니다.
```bash
docker compose -f docker/docker-compose.prod.yml ps
docker compose -f docker/docker-compose.prod.yml logs --tail=200 web
curl -fsS http://127.0.0.1:8864/api/health
```
상세 운영 절차는 [`FEEDBACK_SYNC_GUIDE.md`](./FEEDBACK_SYNC_GUIDE.md)를 기준으로 합니다.