wwwroot
wwwroot는 HandStack 화면과 정적 리소스를 제공하는 모듈입니다. 계약 기반 화면 리소스는 ContractRequestPath 아래에 노출하고, 실제 정적 자산은 WWWRootBasePath에서 서비스하며, 공통 파일 카탈로그에 등록한 호스트 파일도 지정 URL로 제공합니다.
책임 범위
contracts/wwwroot의 애플리케이션별 화면 리소스를 기본/view경로로 노출합니다.modules/wwwroot/wwwroot아래의 실제 정적 파일을 서비스합니다.shared_files.json카탈로그의 요청 경로를 기존 정적 파일 루트 밖의 호스트 파일에 연결합니다.- 정적 파일 요 청에 대해 대소문자 무시 탐색과 캐시 정책을 적용합니다.
- Basic token 기반 파일 동기화 API를 제공합니다.
- 계약 파일 업로드 후 대상 모듈의 refresh API를 호출해 런타임 계약 캐시를 갱신합니다.
주요 API
| 메서드 | 경로 | 용도 |
|---|---|---|
POST | /wwwroot/api/sync/upload | 계약 또는 정적 파일을 업로드 동기화합니다. |
GET | /wwwroot/api/sync/refresh | 대상 모듈 refresh API를 호출합니다. |
GET | /wwwroot/api/htmx/* | HTMX 샘플/보조 API입니다. |
GET | /wwwroot/api/index/* | 화면 보조 API와 거래 직접 호출 래퍼입니다. |
GET | /shared-files/manifest | 공통 파일의 공개 요청 경로 목록을 반환합니다. |
핵심 구현
ModuleInitializer.cs: 정적 파일 provider, 계약 리소스 경로, CORS/필터 처리를 구성합니다.Extensions/SharedFileServingMiddleware.cs: 공통 파일 요청 경로를 호스트 파일에 연결합니다.Entity/SharedFileCatalog.cs:items[].requestPath/hostFilePath카탈로그 스키마입니다.wwwroot/js/syn.loader.js: manifest의 공통 CSS/JS와 기타 텍스트 자산을 브라우저 로딩 과정에 추가합니다.Areas/wwwroot/Controllers/SyncController.cs: 파일 업로드와 refresh 동기화 API입니다.Areas/wwwroot/Controllers/IndexController.cs: 화면 보조 API와transact직접 호출을 제공합니다.Extensions/ModuleApiClient.cs: 화면에서 업무 거래를 직접 호출하는 래퍼입니다.Contracts/wwwroot: 계약 기반 화면/자산 매핑입니다.
주요 설정
| 설정 | 설명 |
|---|---|
ContractRequestPath | 계약 기반 정적 리소스 요청 경로입니다. 기본값은 view입니다. |
ContractBasePath | 계약 기반 화면 리소스 루트입니다. |
WWWRootBasePath | 실제 정적 파일 루트입니다. |
SharedFileConfigPath | 공통 파일 카탈로그 JSON 경로입니다. 빈 값이면 사용하지 않습니다. |
FileSyncTokens | /wwwroot/api/sync/* 호출을 허용할 Basic token 목록입니다. |
BusinessServerUrl | 화면 보조 API가 거래를 실행할 transact URL입니다. |
ModuleLogFilePath | wwwroot 모듈 로그 파일 경로입니다. |
공통 파일 카탈로그
module.json에서 카탈로그 경로를 지정합니다.
"SharedFileConfigPath": "../config/shared_files.json"
shared_files.json은 다음 형식입니다.
{
"items": [
{
"requestPath": "/assets/common.css",
"hostFilePath": "../shared-files/common.css"
},
{
"requestPath": "/assets/common.js",
"hostFilePath": "../shared-files/common.js"
},
{
"requestPath": "/assets/settings.json",
"hostFilePath": "../shared-files/settings.json"
},
{
"requestPath": "/assets/fragment.html",
"hostFilePath": "../shared-files/fragment.html"
}
]
}
예제 파일은 shared_files.json, common.css, common.js, settings.json과 /sample/wwwroot/shared-files/fragment.html에서 확인할 수 있습니다. 다음처럼 배치하면 위 상대경로가 일치합니다.
HANDSTACK_HOME/
├─ app/
│ └─ ack
├─ config/
│ └─ shared_files.json
└─ shared-files/
├─ common.css
├─ common.js
├─ settings.json
└─ fragment.html
SharedFileConfigPath와hostFilePath의 상대경로는 모두 카탈로그 디렉터리가 아니라 ack 실행 기준 경로인GlobalConfiguration.EntryBasePath에서 해석됩니다. 환경 변수와 절대경로도 사용할 수 있습니다.requestPath는HttpContext.Request.Path와 대소문자를 무시하고 정확히 비교하므로/로 시작해야 합니다. 쿼리 문자열은 비교 대상이 아닙니다.requestPath또는hostFilePath가 비어 있는 항목은 적재하지 않습니다.- 중복 요청 경로는 첫 번째 항목이 우선하며, 대상 파일이 없으면 다음 미들웨어로 넘어갑니다.
- 파일 확장자로 MIME 형식을 판단하고 알 수 없는 형식은
application/octet-stream으로 응답합니다. - 카탈로그는 모듈 초기화 때 한 번 적재되므로 파일을 수정한 뒤 ack를 다시 시작해야 합니 다.
GET /shared-files/manifest는 hostFilePath를 제외한 다음 형식의 요청 경로 목록을 반환합니다.
[
{ "requestPath": "/assets/common.css" },
{ "requestPath": "/assets/common.js" },
{ "requestPath": "/assets/settings.json" },
{ "requestPath": "/assets/fragment.html" }
]
syn.loader.js는 화면 시작 시 manifest를 no-cache로 조회합니다.
| 확장자 | 브라우저 처리 |
|---|---|
.css | 기존 스타일 로드 목록에 추가합니다. |
.js | 기존 스크립트 로드 목록에 추가합니다. |
.html, .htm | type="text/html" 텍스트 자산으로 <head>에 추가합니다. |
.json | type="application/json" 텍스트 자산으로 <head>에 추가합니다. |
.xml | type="application/xml" 텍스트 자산으로 <head>에 추가합니다. |
.md | type="text/markdown" 텍스트 자산으로 <head>에 추가합니다. |
.txt | type="text/plain" 텍스트 자산으로 <head>에 추가합니다. |
.csv | type="text/csv" 텍스트 자산으로 <head>에 추가합니다. |
| 그 외 | type="text/plain" 텍스트 자산으로 <head>에 추가합니다. |
텍스트 자산 ID는 마지막 확장자를 제거한 파일명에서 만들고 영문·숫자·밑줄 이외의 문자는 _로 바꿉니다. 다음과 같이 JSON이나 HTML 내용을 읽을 수 있습니다.
const settings = JSON.parse(document.getElementById('shared_settings').textContent);
const fragment = document.getElementById('shared_fragment').innerHTML;
CSS/JS 분기 비교는 대소문자를 구분하므로 파일 확장자는 소문자로 작성합니다. 서로 다른 경로라도 확장자를 제외한 파일명이 같으면 DOM ID가 중복될 수 있으므로 고유한 파일명을 사용합니다. manifest 또는 텍스트 파일 로딩 예외는 로더 로그에 남고 나머지 화면 초기화는 계속됩니다.
공통 파일 미들웨어와 manifest는 세션·인증 미들웨어보다 먼저 실행하고 자체 인증·권한 검사를 하지 않습니다. manifest가 요청 경로 목록도 공개하므로 카탈로그와 대상 파일은 공개 가능한 자산만 등록하고 카탈로그 쓰기 권한을 제한해야 합니다.
동기화 대상
SyncController는 계약 동기화 시 허용된 모듈만 처리합니다. 현재 소스 기준으로 dbclient, graphclient, transact, function, wwwroot 계약을 대상으로 합니다.
파일 업로드 후 refresh가 필요한 경우 대상 모듈의 refresh API가 호출됩니다. 예를 들어 DB 계약을 업로드하면 dbclient의 query 계약 캐시를 갱신해야 합니다.
실행 흐름
- 모듈 초기화 시
SharedFileConfigPath의 카탈로그를 읽어 공통 파일 목록을 메모리에 적재합니다. - 공통 파일 미들웨어가 manifest를 제공하고 정확히 일치하는 요청 경로를 호스트 파일에서 먼저 찾습니다.
- 브라우저의
syn.loader.js가 manifest의 CSS/JS와 기타 텍스트 자산을 화면 로딩 과정에 추가합니다. ContractBasePath/{ApplicationID}를ContractRequestPath아래 정적 파일 provider로 등록합니다.WWWRootBasePath는 실제 정적 파일 루트로 등록됩니다.- 화면 요청은 공통 파일, 계약 경로 또는 실제 정적 경로에서 파일을 찾습니다.
- 동기화 API는 Basic token을 검증한 뒤 파일을 저장하고 대상 모듈 refresh를 수행합니다.