FileStore 개발자 인수인계 문서
작성일: 2026-09-05
대상 저장소: hohyun94/FileStore
현재 브랜치: main
현재 마지막 커밋: 2ab4887 Log safe R2 object operation failures
배포 서비스: 관리형 클라우드 H Project / production / FileStore
서비스 ID: 19d16fe6-0797-4b90-868b-5f75d610a946
1. 현재 결론
FileStore의 Stage 1 구현은 대부분 완료되었습니다. backend 설정, 포탈별 policy, PostgreSQL 저장, 관리자 인증, 업로드 세션, presigned upload URL 생성은 동작합니다. 실제 R2 통합 테스트에서 upload-session과 signed PUT까지 성공했습니다.
현재 남은 장애는 signed PUT 이후 FileStore 서버가 R2에 보내는 HeadObject 요청과 실패 시 정리 목적으로 보내는 DeleteObject 요청입니다. 테스트는 complete 단계에서 중단됩니다.
현재 테스트는 R2에 객체를 업로드하는 단계까지 성공했지만, FileStore 서버가 객체의 크기를 확인하지 못해
ACTIVE상태로 전환하지 못하고 있습니다.
관리형 클라우드 dashboard의 CLI 인증과 로그 조회는 마지막 작업 시점에 완료되지 않았습니다. 다음 개발자는 Windows 관리형 클라우드 CLI 또는 관리형 클라우드 Dashboard의 FileStore Deployments 상세 로그에서 구체적인 AWS 오류명과 HTTP status를 먼저 확인해야 합니다.
2. 시스템 상태
| 항목 | 상태 | 확인 내용 |
|---|---|---|
| FileStore health | 정상 | /healthz가 HTTP 200 반환 |
| PostgreSQL persistence | 정상 | health 응답에 persistence: postgresql 표시 |
| PostgreSQL backend/policy 조회 | 정상 | storageBackends: 1, consumerPolicies: 1 |
| process-center policy | 정상 | 활성화, read/write 활성화 |
| PortalConsole 조직 ID | API 검증 통과 | 현재 API route가 dept_... 문자열을 허용 |
| upload-session | 성공 | 실제 파일 UUID 발급 |
| presigned PUT | 성공 | Cloudflare R2에 signed upload 완료 |
| complete / HeadObject | 통과 | HTTPS endpoint 정규화 후 객체 크기 확인 성공 |
| read-url download | 통과 | 실제 다운로드 바이트가 테스트 원문과 일치 |
| DeleteObject cleanup | 통과 | 관리자 cleanup 실행으로 R2 객체 2개 삭제, objectDeletedAt 및 CLEANUP_DELETED 감사 로그 확인 |
| legal hold cleanup 보류 | 통과 | legalHold=true 파일에서 skipped=1, deleted=0, objectDeletedAt=null 및 LEGAL_HOLD_UPDATED 감사 로그 확인 |
| 관리형 클라우드 global logs | 확인 실패 | 최근 15분에 로그가 표시되지 않음 |
| 관리형 클라우드 CLI | 설치만 완료 | 인증은 미완료 |
현재 health 응답의 기준 형태는 다음과 같습니다.
{
"service": "file-store",
"status": "ok",
"persistence": "postgresql",
"storageBackends": 1,
"consumerPolicies": 1,
"configurationPath": "filestore-config.json"
}3. PostgreSQL 전환 내용
FileStore는 DATABASE_URL이 설정되면 자체 PostgreSQL repository를 사용합니다. PortalConsole 또는 ProcessCenter의 기존 application table은 수정하지 않습니다.
FileStore가 사용하는 테이블은 다음과 같습니다.
| 테이블 | 용도 |
|---|---|
file_store_backends | R2/S3/local backend 메타데이터 |
file_store_policies | 소비 포탈별 저장 정책 |
file_store_assets | 첨부파일 메타데이터와 lifecycle 상태 |
file_store_audits | 감사 로그 |
file_store_admin_accounts | 관리자 계정, 역할, scrypt 비밀번호 해시, 활성화 상태 |
PostgreSQL 환경변수는 다음과 같습니다.
DATABASE_URL=postgresql://...
DATABASE_DIALECT=postgresql
DB_POOL_MAX=5
DATABASE_SSL=requireDATABASE_URL이 없으면 로컬 JSON repository와 파일 기반 관리자 인증을 사용합니다. DATABASE_URL이 있으면 관리자 인증도 PostgreSQL 기반으로 전환되며 FILE_STORE_ADMIN_AUTH_PATH는 읽지 않습니다.
PostgreSQL 관리자 계정 테이블이 비어 있는 최초 기동 시 다음 환경변수로 OWNER 계정을 생성합니다.
FILE_STORE_ADMIN_USERNAME=admin
FILE_STORE_ADMIN_PASSWORD=<12자 이상>초기 계정 생성 후 비밀번호 환경변수는 삭제하거나 교체하는 것이 안전합니다. 기존 admin-auth.json을 DB로 자동 import하는 기능은 구현하지 않았습니다.
관련 구현 파일은 다음과 같습니다.
src/postgres.ts
src/postgresRepositories.ts
src/adminAuth.ts
docs/SHARED_POSTGRES_DATABASE.md4. 현재 backend와 policy
배포된 process-center policy 조회 결과는 다음과 같습니다.
{
"id": "c44b65bd-bdc4-4cdc-b6eb-cb9dfcb61707",
"consumerId": "process-center",
"backendId": "rs-primary",
"enabled": true,
"writeEnabled": true,
"readEnabled": true,
"allowedContentTypes": [
"application/pdf",
"image/png",
"image/jpeg"
]
}R2 테스트에서 사용하는 policy UUID는 다음입니다.
c44b65bd-bdc4-4cdc-b6eb-cb9dfcb61707조직 ID는 PortalConsole에서 전달된 식별자이며 UUID가 아닙니다.
dept_58016148e5504c1c98deb7182d02e224따라서 organizationIds는 UUID 전용으로 검증하면 안 됩니다. 현재 다음 두 HTTP route가 문자열 식별자를 허용하도록 수정되어 있습니다.
POST /api/file-store/v1/assets/upload-session
POST /api/file-store/v1/assets현재 validation 형태는 다음과 같습니다.
organizationIds: z.array(z.string().trim().min(1).max(160))5. R2 테스트 실행 방법
로컬의 FileStore/.env.r2.integration.local에는 서버 URL과 서비스 토큰이 있으며, R2 secret은 저장하지 않습니다. 테스트 실행 시 환경변수 파일을 불러오고 policy 및 조직 ID를 지정합니다.
cd /home/ubuntu/FileStore
set -a
. .env.r2.integration.local
set +a
export FILE_STORE_TEST_MAPPING_ID='c44b65bd-bdc4-4cdc-b6eb-cb9dfcb61707'
export FILE_STORE_TEST_ORGANIZATION_ID='dept_58016148e5504c1c98deb7182d02e224'
export FILE_STORE_TEST_CONTENT_TYPE='application/pdf'
node scripts/verify-r2-flow.mjsWindows PowerShell에서는 다음처럼 실행합니다.
$env:FILE_STORE_TEST_MAPPING_ID = "c44b65bd-bdc4-4cdc-b6eb-cb9dfcb61707"
$env:FILE_STORE_TEST_ORGANIZATION_ID = "dept_58016148e5504c1c98deb7182d02e224"
$env:FILE_STORE_TEST_CONTENT_TYPE = "application/pdf"
node scripts/verify-r2-flow.mjs테스트의 의도된 흐름은 다음과 같습니다.
upload-session
→ presigned PUT
→ complete and HeadObject verification
→ read-url issuance
→ downloaded content checksum comparison
→ DELETE / soft delete
→ deleted asset read-url rejection6. 최근 테스트 결과
최근 성공 로그:
PASS upload-session assetId=bc1ca949-2a37-4e57-a61f-465bca6ebf00
PASS signed upload그 다음 단계의 실패:
POST /api/file-store/v1/assets/{assetId}/complete failed: HTTP 400
업로드된 객체의 크기를 확인할 수 없습니다.정리 요청의 실패:
DELETE /api/file-store/v1/assets/{assetId} failed: HTTP 400
@aws-sdk XML parse error: mismatched tags <hr> and </body>.
Deserialization error실패한 테스트에서 생성된 파일은 complete 이전 상태로 DB에 남아 있을 수 있습니다. 실패한 파일 ID 예시는 다음과 같습니다.
ac866231-ed3f-43cb-adfc-0c5a192a6894
bc1ca949-2a37-4e57-a61f-465bca6ebf00
b29e5fdd-a75a-4eba-a27f-22365a4951d0
0eb6e263-0303-4626-8ecb-9502c5292ef8이 파일들은 운영 데이터가 아닌 통합 테스트 잔여 데이터로 분류하고, R2 객체와 PostgreSQL metadata를 확인한 뒤 정리해야 합니다.
7. 진단 코드
src/storage.ts에 R2 object operation 실패 시 credential 또는 signed URL을 출력하지 않는 안전한 로그를 추가했습니다.
로그 형식은 다음과 같습니다.
[file-store] object HEAD failed {
operation: "HeadObject",
bucket: "...",
error: "...",
status: 403
}
[file-store] object DELETE failed {
operation: "DeleteObject",
bucket: "...",
error: "...",
status: 403
}진단 로그를 추가한 마지막 커밋은 다음입니다.
2ab4887 Log safe R2 object operation failures다음 개발자는 해당 커밋이 실제 관리형 클라우드 FileStore deployment에 배포되었는지 먼저 확인해야 합니다. 공개 health 응답에 persistence: postgresql이 있더라도 진단 로그 코드는 별도 최신 배포 여부를 확인해야 합니다.
8. 우선 확인할 원인
signed PUT이 성공하고 서버-side HEAD 및 DELETE가 실패하므로 다음 항목을 우선 확인합니다.
| 확인 항목 | 기준 |
|---|---|
| Cloudflare API Token 권한 | Object Read & Write |
| Token bucket scope | 실제 테스트 bucket 포함 |
| R2 endpoint | https://<ACCOUNT_ID>.r2.cloudflarestorage.com |
| endpoint path | bucket 이름을 URL path에 붙이지 않음 |
| backend provider | CLOUDFLARE_R2 |
| backend region | auto |
| backend bucket | 실제 R2 bucket 이름과 일치 |
| secretRef | 예: R2_PRIMARY |
| 관리형 클라우드 variable 1 | <secretRef>_ACCESS_KEY_ID |
| 관리형 클라우드 variable 2 | <secretRef>_SECRET_ACCESS_KEY |
예를 들어 backend의 secretRef가 R2_PRIMARY이면 관리형 클라우드 환경변수는 정확히 다음 이름이어야 합니다.
R2_PRIMARY_ACCESS_KEY_ID=...
R2_PRIMARY_SECRET_ACCESS_KEY=...Cloudflare R2 endpoint는 다음 형식이어야 합니다.
https://<CLOUDFLARE_ACCOUNT_ID>.r2.cloudflarestorage.com잘못된 형식은 다음과 같습니다.
https://<ACCOUNT_ID>.r2.cloudflarestorage.com/<BUCKET_NAME>9. Windows 관리형 클라우드 CLI 로그 조회 절차
다음 개발자가 Windows에서 로그를 조회하려면 PowerShell에서 실행합니다.
FileStore 프로젝트 디렉터리에서 프로젝트와 서비스를 연결합니다.
cd C:\path\to\FileStore선택값은 다음과 같습니다.
Project: H Project
Environment: production
Service: FileStore서비스 상태와 로그는 다음 명령으로 확인합니다.
서비스 ID를 직접 지정할 수도 있습니다.
PowerShell에서 R2 진단 로그만 찾으려면 다음을 사용합니다.
Select-String -Pattern "HEAD failed|DELETE failed|HeadObject|DeleteObject|error|status"bucket, error, status만 인수인계 문서에 기록하고 Access Key, Secret Access Key, signed URL은 기록하지 않습니다.
10. 커밋 이력
주요 작업 커밋은 다음과 같습니다.
| 커밋 | 내용 |
|---|---|
e680582 | 관리자 UI에서 policy UUID 표시 |
4656168 | PortalConsole 조직 식별자 지원 |
1586f54 | R2 verifier MIME type 선택 지원 |
7250e62 | backend/policy/asset/audit PostgreSQL 저장 |
9b3c3cd | 관리자 인증 정보 PostgreSQL 저장 |
ca326e1 | health 응답에 persistence mode 표시 |
1043a7c | 실제 upload route의 조직 ID UUID 검증 수정 |
686b908 | Node fetch signed PUT body를 Blob으로 수정 |
2ab4887 | R2 HEAD/DELETE 안전 진단 로그 추가 |
원격 저장소와 로컬 main은 마지막 확인 시 일치했습니다.
main...origin/main11. 다음 개발자의 작업 순서
첫째, Windows 관리형 클라우드 CLI로 인증한 뒤 FileStore 서비스와 production 환경을 연결합니다.
둘째, 최신 deployment commit이 2ab4887 이상인지 확인합니다. 최신 배포가 아니면 main의 최신 커밋으로 재배포합니다.
셋째, 배포 로그에서 HeadObject 및 DeleteObject 진단 행을 확인합니다. 실제 status와 error 값을 기록합니다.
넷째, status가 403이면 R2 API Token의 Object Read & Write 권한과 bucket scope를 수정합니다. status가 404이면 endpoint, bucket 이름, path-style 요청 문제를 확인합니다. status가 301 또는 3xx이면 endpoint 또는 jurisdiction endpoint를 확인합니다. AWS XML parse error가 계속되면 응답 HTML의 원인이 되는 endpoint 또는 Cloudflare gateway 응답을 확인합니다.
다섯째, R2 설정을 수정한 뒤 다음 명령으로 테스트를 재실행합니다.
$env:FILE_STORE_TEST_MAPPING_ID = "c44b65bd-bdc4-4cdc-b6eb-cb9dfcb61707"
$env:FILE_STORE_TEST_ORGANIZATION_ID = "dept_58016148e5504c1c98deb7182d02e224"
$env:FILE_STORE_TEST_CONTENT_TYPE = "application/pdf"
node scripts/verify-r2-flow.mjs여섯째, 최종적으로 다음 출력이 모두 나오는지 확인합니다.
PASS upload-session
PASS signed upload
PASS complete
PASS read-url download content matches
PASS soft delete
PASS deleted asset read-url
R2 integration flow passed12. 보안 주의사항
R2 Secret Access Key와 관리형 클라우드 service token은 채팅, Git, 문서, 로그에 기록하지 않습니다. 지금까지 테스트 과정에서 노출된 것으로 의심되는 service token 또는 R2 credential은 검증 완료 후 교체해야 합니다.
테스트 파일과 실패한 pending 파일을 정리한 뒤에도 R2 bucket과 PostgreSQL audit log에 잔여 데이터가 남아 있는지 확인해야 합니다.
References
13. 2026-09-06 운영 검증 업데이트
관리형 클라우드 H Project / production / FileStore 서비스에 main의 cleanup job 커밋 898cbee가 자동 배포되었고, 관리형 클라우드 화면에서 Deployment successful 및 ACTIVE 상태를 확인했다. FileStore production 환경에는 FILE_STORE_CLEANUP_INTERVAL_MS=3600000이 추가되어 1시간 주기로 DELETED 객체 cleanup이 실행된다.
배포 후 공개 endpoint 검증 결과는 다음과 같다.
| Endpoint | 결과 |
|---|---|
/healthz | HTTP 200, PostgreSQL persistence, backend 1개, policy 1개 |
/admin | HTTP 200, FileStore 관리자 HTML 정상 반환 |
배포 서비스는 Southeast Asia, 1 Replica, Online 상태이며 최신 배포 설명은 feat: add deleted asset cleanup job이다.