Skip to content

현재 사용법 (2026-09-10, 6fbaee7f): 불편·기능 신고·첨부·답변 확인 · 기능 마스터. 아래는 작성 당시의 변경 설명이며 현재 전체 동작·재측정 결과를 뜻하지 않습니다.

CS 리포트 로그 조회를 S3 직접 fetch에서 API 경유로 전환 ​

한 줄 요약 ​

시스템 관리 콘솔에서 CS 리포트의 클라이언트 로그를 열 때, 브라우저가 S3 주소로 직접 접속하다 차단되어 로그가 표시되지 않던 것을, 서버를 거쳐 조회하도록 바꿔 항상 표시되게 합니다.

여는 한 줄 ​

근본 원인은 첨부 세 종류 가운데 로그만 브라우저의 교차 출처 fetch()라는 점입니다. CORS 정책은 fetch()에만 적용되고 <img>·링크에는 적용되지 않으므로, 스크린샷은 보이는데 로그만 막혔습니다.

배경 / 지금 동작 ​

CS 리포트는 직원이 앱에서 접수하는 불편·오류 신고로, 접수 시 화면 스크린샷·클라이언트 로그(JSON)·사용자 파일이 S3에 첨부로 올라갑니다. 운영자는 시스템 관리 콘솔(system-admin)의 CS 리포트 탭에서 신고를 열람합니다.

콘솔의 상세 화면은 엔드포인트 API의 csReport.admin.get을 호출하고, 이 프로시저는 각 첨부에 대해 짧은 만료의 서명된 다운로드 주소(presigned URL — S3가 서명을 보고 인증 없이 열어 주는 임시 주소)를 만들어 응답에 담았습니다. 콘솔 브라우저는:

  • 스크린샷 — <img src="presigned URL">로 표시합니다. 이미지 로드는 CORS 검사 대상이 아니라 정상 동작했습니다.
  • 로그 — fetch(presigned URL)로 JSON을 내려받아 화면에 그렸습니다. fetch()는 CORS 검사 대상이라, 운영 S3 버킷이 콘솔 origin(okbest-sysadm.hereby.me)을 허용하지 않아 차단됐습니다(2026-09-08 프로드 리포트). 버킷 CORS를 여는 임시 조치로 복구했지만, 버킷·도메인이 늘 때마다 같은 설정을 반복해야 하고, 인증 없이 유효한 presigned URL이 브라우저에 그대로 노출되는 문제가 남았습니다.

개념 다이어그램 ​

변경 전

변경 후

개념 흐름별 변경 ​

1. 로그 첨부에는 presigned URL을 내주지 않습니다 ​

csReport.admin.get(packages/api/src/routers/cs-report.router.ts)이 로그 첨부(kind === LOG)의 downloadUrl을 null로 돌려줍니다. 프런트가 더 이상 이 URL을 쓰지 않으므로, 브라우저에 S3 주소가 노출되는 경로 자체가 사라집니다. 스크린샷·사용자 파일은 <img>·다운로드 링크(문서 로드)로 소비되어 CORS와 무관하므로 presigned URL을 유지합니다.

2. 서버가 스토리지에서 로그를 읽어 주는 프로시저를 추가합니다 ​

새 systemProcedure csReport.admin.attachmentContent가 reportId·attachmentId를 받아, 첨부가 로그 종류인지 확인한 뒤 스토리지 객체를 서버에서 읽어 텍스트로 돌려줍니다. 로그는 접수 시 정제(redact)된 JSON 텍스트(스키마상 최대 10MB, 실측 수 KB)라 응답 본문으로 돌려주기에 적합합니다. 이를 위해 스토리지 서비스 인터페이스(packages/api/src/services/storage.service.ts)에 readObject(key)를 추가했습니다 — S3 구현은 GetObjectCommand로, 로컬 폴백 구현은 기존 readFile로 읽습니다.

3. 콘솔은 same-origin 경유로 조회합니다 ​

콘솔 서버(tools/system-admin/server/routes/cs-reports.ts)에 GET /api/cs-reports/logs 라우트를 추가해 위 프로시저를 엔드포인트 세션으로 호출합니다. 콘솔 프런트(tools/system-admin/src/app.ts)의 loadCsrLogs()는 fetch(downloadUrl) 대신 이 라우트를 부릅니다. 브라우저 입장에서는 자기 origin으로의 요청뿐이라 CORS가 개입할 여지가 없고, 버킷 CORS 설정과도 무관해집니다.

검증 ​

  • 로컬 라이브 검증: 콘솔 로그인 → CS 리포트 상세 → 「로그 확인」 → 로그 19건 렌더 확인. 브라우저 네트워크에서 스토리지의 로그 파일로 향하는 요청 0건(스크린샷 <img> 제외).
  • admin.get 응답에서 로그 첨부 downloadUrl === null, 스크린샷은 URL 유지 확인.
  • pnpm lint:check && pnpm build 그린.