본문으로 건너뛰기

repository

repository는 파일 업로드, 다운로드, 삭제, 파일 메타데이터 관리를 담당하는 모듈입니다. 저장소 정의는 JSON 계약으로 관리하고, 메타데이터 저장/조회는 dbclient 계약과 연계합니다.

책임 범위​

  • 저장소 계약을 읽어 파일 저장 위치와 다운로드 URL 정책을 결정합니다.
  • 단일/다중 파일 업로드, 다운로드, 삭제, 저장소 조회 API를 제공합니다.
  • 파일 메타데이터를 DatabaseContractPath의 dbclient 계약으로 저장하고 조회합니다.
  • 다른 모듈이 MediatR 이벤트로 파일 저장소 기능을 사용할 수 있도록 repository.Events.RepositoryRequest를 제공합니다.
  • 다운로드 응답에 XFrameOptions, ContentSecurityPolicy 같은 보안 헤더를 적용합니다.

주요 API​

메서드경로용도
GET/repository/api/storage/refresh저장소 계약 캐시를 갱신합니다.
GET/repository/api/storage/action-handler저장소 action 요청을 처리합니다.
GET/repository/api/storage/get-repository저장소 정의를 조회합니다.
POST/repository/api/storage/upload-file단일 파일을 업로드합니다.
POST/repository/api/storage/upload-files여러 파일을 업로드합니다.
POST/repository/api/storage/download-file파일을 다운로드합니다.
GET/repository/api/storage/http-download-fileHTTP 경로 기반 다운로드를 처리합니다.
GET/repository/api/storage/virtual-download-file가상 경로 다운로드를 처리합니다.
GET/repository/api/storage/virtual-delete-file가상 경로 파일을 삭제합니다.
POST/repository/api/storage/remove-item단일 항목을 삭제합니다.
POST/repository/api/storage/remove-items여러 항목을 삭제합니다.

핵심 구현​

  • Areas/repository/Controllers/StorageController.cs: 저장소 API 진입점입니다.
  • Extensions: 저장소 provider, 경로 계산, 모듈 API 호출 보조 기능입니다.
  • Services/StorageProviderFactory.cs: 저장소 구현을 선택합니다.
  • Contracts/repository: 저장소 선언 JSON 계약입니다.
  • Contracts/dbclient, Contracts/transact: 파일 메타데이터 조작을 위한 보조 계약입니다.

계약과 설정​

저장소 선언 계약은 기본적으로 다음 위치에 둡니다.

contracts/repository/{ApplicationID}/...

주요 설정은 다음과 같습니다.

설정설명
FileServerUrl외부 다운로드 URL의 기준 주소입니다.
ContractBasePath저장소 JSON 계약 루트입니다.
DatabaseContractPath파일 메타데이터를 저장/조회할 dbclient 계약 루트입니다.
ModuleBasePathrepository 모듈 파일 기준 경로입니다.
SubscribeAction기본 수신 이벤트는 repository.Events.RepositoryRequest입니다.
XFrameOptions다운로드 응답의 frame 허용 정책입니다.
ContentSecurityPolicy다운로드 응답의 frame-ancestors 정책입니다.

저장소 선언 (.rpo)​

JSON 배열이며(주석과 후행 쉼표 허용) 항목 하나가 RepositoryID 하나입니다. 확장자는 .json 또는 .rpo입니다(계약 확장자). 거래 계약이 아니므로 CommandType으로 호출하지 않고 화면의 repositoryID로 연결합니다.

[
{
"ApplicationID": "HDS", "RepositoryID": "ABCATT01", "RepositoryName": "게시글 첨부",
"AccessID": "ABC", "StorageType": "FileSystem", "PhysicalPath": "../storage/HDS/ABCATT01",
"AccessMethod": "public", "IsMultiUpload": true, "IsFileOverWrite": false,
"IsFileNameEncrypt": true, "IsKeepFileExtension": false, "IsAutoPath": true, "PolicyPathID": "1",
"UploadTypeID": "Attachment", "UploadExtensions": "pdf;docx;xlsx;png;jpg",
"UploadCount": 10, "UploadSizeLimit": 10485760,
"TransactionGetItem": "HDS|ABC|FIL010|GD01",
"TransactionGetItems": "HDS|ABC|FIL010|LD01",
"TransactionDeleteItem": "HDS|ABC|FIL010|DD01",
"TransactionUpsertItem": "HDS|ABC|FIL010|MD01",
"TransactionUpdateDependencyID": "HDS|ABC|FIL010|UD01",
"TransactionUpdateFileName": "HDS|ABC|FIL010|UD02"
}
]
항목의미
PhysicalPath파일을 저장하는 기준 경로. 환경별 값이므로 운영 경로를 소스에 새로 쓰지 마세요
IsAutoPath, PolicyPathIDIsAutoPath가 true이면 기준 경로 아래에 날짜 폴더를 만듭니다. PolicyPathID가 1이면 연도(yyyy), 2이면 연월(yyyy-MM), 3이면 연월일(yyyy-MM-dd)이고, 앞에 CustomPath1~3이 붙습니다
IsMultiUploadfalse인 단일 파일 저장소는 새 파일을 올리면 이전 파일을 지웁니다
AccessMethod접근 방식. 다운로드 URL 노출 범위를 최소화하세요
UploadSizeLimit, UploadCount, UploadExtensions, UploadTypeID업로드 제한과 종류. 서버가 강제하는 범위는 아래를 보세요
Transaction*파일 정보를 DB에 기록·조회하는 dbclient 거래 ID(ApplicationID|ProjectID|TransactionID|ServiceID)

메타데이터 거래: Transaction*이 가리키는 거래 계약과 SQL 계약이 모두 있어야 합니다. 파일을 물리적으로 복사하는 것만으로는 메타데이터가 생기지 않으므로 삭제·이관은 repository API로 하세요.

메타데이터 관리 방식: IsFileUploadDownloadOnly가 true이면 메타데이터 없이 파일만 저장·다운로드합니다. IsLocalDbFileManaged가 true이면 모듈 로컬 SQLite 메타 SQL로, 아니면 Transaction* dbclient 거래로 목록과 건수를 조회합니다.

화면 연결: <syn_fileclient syn-options="{repositoryID: '…'}">, <syn_filedropzone … {repositoryID, applicationID, dependencyID}>, <syn_htmleditor … {repositoryID}>로 연결합니다. dependencyID는 파일을 업무 키에 묶는 값입니다. 신규 작성 중에는 임시 키로 올리고, 저장에 성공한 뒤 updateDependencyID로 업무 키로 옮기세요.

서버가 강제하는 것:

  • 파일 크기(UploadSizeLimit): 초과하면 업로드를 거부합니다.
  • 경로 안전성: CustomPath1~3의 .., 절대경로, 줄바꿈과 파일명 변경 시 경로 문자를 거부합니다.
  • 업로드 건수(UploadCount): 다중 업로드에서 이미 등록된 건수가 한도를 넘는 경우에 거부합니다(메타데이터를 관리하는 저장소일 때만).
  • 확장자(UploadExtensions)는 서버가 검사하지 않습니다. 값은 클라이언트 컨트롤에 전달되어 업로드 전에 걸러질 뿐이므로, 서버 측 확장자 차단이 필요하면 업로드 후 검증 로직을 따로 두세요.

실행 흐름​

  1. 모듈 초기화 시 ContractBasePath의 저장소 JSON 계약을 로드합니다.
  2. 업로드 요청이 들어오면 저장소 ID와 업무 키를 기준으로 실제 파일 경로를 계산합니다.
  3. 파일 저장 후 DatabaseContractPath의 dbclient 계약으로 메타데이터를 기록합니다.
  4. 다운로드 요청은 저장소 계약과 메타데이터를 확인한 뒤 보안 헤더를 포함해 파일을 반환합니다.
  5. 계약 변경 시 /repository/api/storage/refresh 또는 파일 감시로 매핑을 갱신합니다.

운영 주의사항​

  • 업로드 가능한 경로와 다운로드 URL은 저장소 계약에서 명확히 제한합니다.
  • 업로드 파일은 PhysicalPath/{CustomPath1}/{CustomPath2}/{CustomPath3}/{정책 경로}에 저장됩니다. upload-file, upload-files는 CustomPath1~3에 .. 구간, 절대경로, 줄바꿈이 있으면 Result: false로 거부합니다. action-handler의 UpdateFileName도 변경할 FileName에 경로 구분자(/, \), ./.., 파일명에 쓸 수 없는 문자가 있으면 400으로 거부합니다. download-file, http-download-file은 항목에 기록된 CustomPath1~3으로 계산한 디렉터리(지정하지 않았으면 저장소 PhysicalPath) 아래의 파일만 반환하며, 벗어나는 경로는 파일 경로 확인 필요(400)로 거부합니다.
  • 파일 메타데이터와 실제 파일 저장소가 불일치하지 않도록 삭제/이관 작업은 repository API를 통해 수행합니다.
  • XFrameOptions, ContentSecurityPolicy는 화면 내 iframe 다운로드나 미리보기 요구사항과 보안 정책을 함께 고려해 설정합니다.
  • LoadPassAssemblyPath에 OS별 SkiaSharp native library가 포함되므로 배포 대상 OS에서 파일이 누락되지 않게 확인합니다.