Skip to content

ProcessCenter 첨부파일 연동 ​

책임 원칙 ​

ProcessCenter는 프로세스 노드와 첨부파일의 관계, 업무 권한, 화면 표시만 관리한다. 파일 원본, 저장 Backend, 저장 정책, 파일 상태, 다운로드 locator, 삭제 lifecycle, 감사 이벤트의 주된 소유자는 FileStore다.

PortalConsole의 media API와 Asset 관계 API는 ProcessCenter 첨부파일 처리에 사용하지 않는다. PortalConsole는 향후 사용자·조직 권한 확인을 담당할 수 있지만, 첨부파일 원본과 저장소 라우팅을 소유하지 않는다.

요청 흐름 ​

text
ProcessCenter 서버
  → FileStore upload-session
  → FileStore가 consumerId 정책과 Backend 검증
  → FileStore가 반환한 uploadUrl로 파일 업로드
  → FileStore complete
  → ProcessCenter는 processNodeId ↔ assetId 관계만 기록

다운로드는 ProcessCenter 서버가 FileStore read-url을 요청한 뒤, 원본 바이트를 서버에서 받아 브라우저에 흘려보낸다(GET /api/process-nodes/:processNodeId/attachments/:attachmentId/download, 기본 ATTACHMENT_DOWNLOAD_MODE=relay). 서비스 토큰과 서명 URL은 브라우저에 전달하지 않는다.

왜 중계하는가: FileStore read-url 의 locator 는 FileStore(또는 R2/S3) 오리진이라 앱 오리진과 다르다. 브라우저가 그 주소를 직접 fetch 하면 CORS 에 막힌다 — 업로드는 서버를 경유하므로 되는데 다운로드만 실패하는 증상이 된다. 중계는 저장 backend(로컬·S3/R2·OAuth)와 배포 형태에 상관없이 동작한다.

브라우저가 서명 URL 을 직접 받는 구성이 필요하면(대용량 파일의 포탈 서버 경유 회피) ATTACHMENT_DOWNLOAD_MODE=direct 로 두고 FileStore 에 FILE_STORE_ALLOWED_ORIGINS(이 포탈의 공개 origin, 쉼표 구분)를 설정한다. FileStore 는 기본적으로 CORS 헤더를 내지 않고, 켜도 *·쿠키 자격증명은 쓰지 않는다(서명 URL 에 쿠키가 필요 없고 SSO 세션 노출을 막는다).

여러 파일은 일괄 다운로드로 받는다 — GET /api/process-nodes/:processNodeId/attachments.zip(노드 전체), ?ids=1,2(선택한 첨부만). 서버가 FileStore 원본을 받아 ZIP 으로 스트리밍하며(server/attachmentArchive.ts — 공지 압축 다운로드와 같은 코드), 요청한 id 는 이 노드의 소유 행과 교차 확인한 뒤 묶는다. 스트리밍 ZIP 은 Content-Length 가 없으므로 진행률은 브라우저 내려받기 관리자가 보여 준다(클라이언트는 URL 을 브라우저에 넘긴다).

파일명 클릭은 미리보기다 — 같은 라우트에 ?disposition=inline 을 붙여 새 탭에서 바로 열고(previewUrl), 다운로드 버튼은 attachment 로 저장한다. 인라인은 브라우저가 스크립트를 실행하지 않는 형식(PDF·이미지·평문)만 허용하고(isPreviewableAttachment), X-Content-Type-Options: nosniff·Cross-Origin-Resource-Policy: same-origin 을 함께 붙인다 — 업로드된 HTML·SVG·문서는 인라인 요청이 와도 저장으로 떨어뜨린다.

삭제는 ProcessCenter가 업무 권한을 확인한 뒤 FileStore delete API를 호출한다. FileStore가 보존 기간, legal hold, soft delete, 감사 로그를 최종 처리한다.

서버 환경변수 ​

env
FILE_STORE_BASE_URL=https://filestore.example
FILE_STORE_SERVICE_TOKEN=<server-only-token>
FILE_STORE_CONSUMER_ID=process-center
FILE_STORE_MAPPING_ID=<FileStore consumer policy UUID>
FILE_STORE_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000

FILE_STORE_MAPPING_ID는 FileStore 관리자 UI에서 ProcessCenter 정책을 등록한 뒤 확인한다. R2 Bucket, Endpoint, Access Key, Secret Key는 ProcessCenter에 설정하지 않는다.

구현 규칙 ​

  • 파일 Base64는 ProcessCenter 서버에서만 FileStore upload-session으로 변환한다.
  • 브라우저는 FileStore 서비스 토큰을 받지 않는다.
  • ProcessCenter DB의 assetId는 FileStore 자산을 참조하는 관계값이다.
  • storageKey, 파일명, MIME type, 크기, 다운로드 URL은 FileStore 응답을 기준으로 기록하거나 표시한다.
  • 다운로드 URL은 만료되므로 ProcessCenter가 영구 저장하지 않고 조회 시 새로 발급한다.
  • FileStore가 오류를 반환하면 ProcessCenter는 내부 자격증명이나 저장소 정보를 노출하지 않고 일반 오류를 반환한다.
  • 기존 첨부파일은 없는 개발 상태이므로 PortalConsole media 데이터 마이그레이션은 수행하지 않는다.

1단계 완료 기준 ​

ProcessCenter에서 다음 흐름을 확인한다.

text
프로세스 권한 확인
  → FileStore upload-session
  → 객체 업로드
  → FileStore complete
  → 첨부파일 목록 조회 시 read-url 발급
  → ProcessCenter 권한 확인
  → FileStore delete

API 테스트 시나리오 ​

시나리오검증 내용기대 결과
정상 업로드upload-session, signed PUT, complete 순서FileStore 자산이 ACTIVE가 되고 ProcessCenter 관계가 생성된다.
정책 불일치잘못된 mappingId 또는 consumerIdFileStore 요청이 거부되고 ProcessCenter 관계가 생성되지 않는다.
파일 형식 거부정책에 없는 MIME typeupload-session 단계에서 거부된다.
파일 크기 거부정책의 maxBytes 초과upload-session 또는 complete 단계에서 거부된다.
다운로드첨부파일 목록 조회목록은 same-origin 릴레이 주소를 돌려주고, 다운로드 시점에 ProcessCenter 가 FileStore read-url 을 새로 발급해 서버에서 원본을 중계한다.
일괄 다운로드여러 첨부를 ZIP 하나로 요청서버가 원본을 받아 압축해 스트리밍한다(선택 id 는 이 노드의 소유 행과 교차 확인). 선택이 없으면 빈 ZIP 대신 404.
삭제ProcessCenter 업무 권한 통과 후 삭제FileStore lifecycle 삭제 후 ProcessCenter 관계가 제거된다.
권한 거부R 담당자 또는 관리자 이외의 삭제·업로드FileStore 호출 없이 ProcessCenter가 FORBIDDEN을 반환한다.
연동 미설정필수 환경변수 누락저장소 정보 없이 설정 오류를 반환한다.
FileStore 장애4xx·5xx 응답내부 토큰이나 Backend 정보 없이 일반 연동 오류를 반환한다.
서비스 토큰 보호브라우저 첨부파일 흐름 확인FILE_STORE_SERVICE_TOKEN이 브라우저 응답에 포함되지 않는다.

자동화 테스트는 server/fileStoreClient.test.ts에 두며, 외부 R2나 S3에 실제 요청하지 않고 FileStore API와 signed URL 응답을 모킹한다. 실제 저장소 연결은 별도의 통합 테스트 환경에서 수행한다.