[api] 파일업로드, tus파일 주소변경, md 파일 변경

This commit is contained in:
2026-05-08 17:21:19 +09:00
parent 0fc91fddac
commit 13e41f6e3c
23 changed files with 981 additions and 268 deletions
+6
View File
@@ -6,3 +6,9 @@
- 테스트 코드가 충분하지 않을 수 있으므로 기능 변경 시 필요한 테스트를 보강한다.
- 일부 Java 소스와 주석, Swagger 설명에 인코딩이 깨진 문자열이 있으므로 표시 문자열 수정은 영향 범위를 보고 묶어서 처리한다.
- `application-local.yaml` 은 로컬 실행값이 직접 들어가 있으므로 공유하거나 커밋할 때 민감정보 노출 여부를 한 번 더 확인한다.
- 파일 업로드는 두 흐름으로 분리되어 있다. `file` 모듈의 단순 업로드는 DB에 기록하지 않고 `uploadPath`, 파일명, 확장자, contentType, size, image width/height 정도만 반환한다. 각 업무 테이블 저장은 호출 측에서 처리한다.
- SunEditor 이미지 업로드는 API로 파일을 저장하되 응답 URL은 `file.upload.view.file-domain + uploadPath` 로 만든다. 에디터 본문에 저장되는 URL은 admin/user 토큰에 의존하지 않는 file-domain URL이어야 한다.
- TUS 업로드는 `tusFile` 모듈에서 DB master/detail 기록, upload token 발급, tusd hook 반영, 상태 조회, view/download/delete를 담당한다.
- TUS 인증 경로는 `/tusFiles/uploadAuth`, hook 경로는 `/tusFiles/tusHook` 이다. nginx `/_upload_auth` 와 Spring Security 공개 경로, JWT filter 제외 경로를 함께 맞춘다.
- file-domain nginx 는 `/uploads/` 를 공개 정적 파일로 열고 `/uploads/tmp/` 는 404로 막는다. TUS 임시 디렉터리는 파일 시스템에서는 사용하지만 URL로 직접 노출하지 않는다.
- Admin 로그인 상태 확인은 `/admin/auth/loginChecked` 를 사용한다. `/admin/auth/refresh` 는 토큰을 재발급하므로 새로고침 상태 확인용으로 쓰지 않는다.
+3 -1
View File
@@ -30,4 +30,6 @@
- MyBatis는 인터페이스와 XML을 함께 사용한다.
- Mapper 인터페이스는 `src/main/java/.../mapper`, SQL XML은 `src/main/resources/mapper/...` 경로를 짝으로 맞춘다.
- 공통 응답은 `common/response`, 보안은 `config/security, jwt`, 전역 예외 처리는 `config/exception` 아래에 둔다.
- 모듈 패키지는 현재 `auth`, `file`, `main`, `user` 형태로 구성되어 있고, 필요한 모듈만 `dto`, `form`, `mapper`, `service`, `vo`를 둔다.
- 모듈 패키지는 현재 `admin`, `auth`, `file`, `main`, `tusFile`, `user` 형태로 구성되어 있고, 필요한 모듈만 `dto`, `form`, `mapper`, `service`, `vo`를 둔다.
- `file` 모듈은 DB 기록 없는 단순 업로드, SunEditor 이미지 업로드, uploadPath 기반 view/download를 담당한다.
- `tusFile` 모듈은 DB 기록이 필요한 TUS 기반 대용량 업로드 초기화, 업로드 토큰 검증, tusd hook, 상태 조회, 파일 목록/view/download/delete 흐름을 담당한다.
+19
View File
@@ -17,6 +17,25 @@
- URL: `http://localhost:8106/swagger-ui.html`
- 인증: `swagger.login.id` / `swagger.login.password` (환경별 yaml에 설정)
## 파일 업로드 설정
- `tus-file.upload.final-root` 는 TUS 완료 파일과 단순 업로드 파일이 공유하는 최종 저장 루트다.
- `tus-file.upload.tmp-root` 는 tusd 임시 업로드 루트다. file-domain nginx 에서는 `/uploads/tmp/` 접근을 404로 막는다.
- `file.upload.root-path` 는 보통 `${tus-file.upload.final-root}` 를 사용해 단순 업로드와 TUS 완료 파일의 마운트 루트를 맞춘다.
- `file.upload.view.file-domain` 은 SunEditor 이미지 응답 URL 생성에 사용한다. 예: `https://file-alist.pjt.kr`.
- `file.upload.max-size` 는 단순 업로드 전역 최대 용량이다. `file.upload.types.{folder}.max-size` 가 있으면 폴더별 설정이 우선한다.
- `file.upload.allowed-extensions` 는 단순 업로드 전역 확장자 허용 목록이다.
- `file.upload.types.{key}.folder` 는 실제 저장 폴더명이다. 설정되지 않은 folder 값도 전역 정책을 통과하면 동적 폴더로 저장할 수 있다.
- `file.upload.types.{key}.image-only``true` 이면 이미지 확장자만 허용한다.
- `file.upload.types.{key}.resize.enabled``true` 이고 업로드 파일이 이미지이면 resize 함수를 거친다. `width``height` 가 모두 있으면 중앙 crop 후 고정 크기로 저장하고, `max-width` 만 있으면 비율을 유지해 축소한다.
- `spring.servlet.multipart.max-file-size``max-request-size``-1` 로 두고, 실제 제한은 업로드 서비스 정책에서 처리한다.
## file-domain nginx 기준
- `/tus/files/` 는 tusd 로 프록시하며 `auth_request /_upload_auth``/tusFiles/uploadAuth` 를 호출한다.
- `/_upload_auth` 는 내부 location 으로만 열고 `Authorization`, `X-File-Uuid`, 필요 시 `Upload-Metadata`, `Upload-Length` 헤더를 API 로 전달한다.
- `/uploads/``/srv/project/alist/uploads/` 를 정적 파일로 제공한다.
- `/uploads/tmp/` 는 tusd 임시 파일 노출을 막기 위해 404 처리한다.
- 그 외 경로는 `location /` fallback 에서 차단한다.
## 실행 명령
- 로컬 실행: `./gradlew bootRun`
- 테스트 실행: `./gradlew test`
+4 -1
View File
@@ -10,7 +10,10 @@
- Swagger: `/v3/api-docs/**`, `/swagger-ui/**` → HTTP Basic 인증 (InMemory)
- API: JWT Bearer 토큰 인증 (Stateless)
- 세션/쿠키: Redis Session 저장소 사용, 쿠키 속성은 프로파일별 `cookie.*` 설정으로 제어
- 공개 경로: `/`, `/actuator/health`, `/sso/**`, `/auth/**`, `/user/signup`, `/files/tusHook`
- Admin API: `/admin/**` 는 별도 `SecurityFilterChain` 으로 분리하며 `/admin/auth/**` 만 공개하고 나머지는 `ADMIN` 권한을 요구한다.
- 공개 경로: `/`, `/actuator/health`, `/sso/**`, `/auth/**`, `/user/signup`, `/user/migrationUserList`, `/tusFiles/tusHook`, `/tusFiles/uploadAuth`
- TUS 업로드 토큰은 일반 access token 이 아니므로 `/tusFiles/uploadAuth`, `/tusFiles/tusHook``JwtAuthenticationFilter.shouldNotFilter(...)` 에서도 제외한다.
- `/admin/auth/loginChecked` 는 admin access token 쿠키의 현재 로그인 상태 확인용이다. 토큰을 재발급하지 않으며 `isAdminAccessToken`, `isAdminRefreshToken`, `loggedIn`, `userId`, `userIdx`, `userTokenIdx`, `userRole` 형태의 값을 반환한다.
- Swagger 인증과 API 인증은 `SecurityFilterChain` 을 분리해서 관리한다.
## 응답 코드 규칙
+5
View File
@@ -12,9 +12,14 @@
- Mapper 인터페이스 추가/변경 시 XML namespace, id, parameter/result 매핑이 같이 맞는지 확인한다.
- 공개 경로나 권한 정책을 바꿨다면 SecurityConfig 와 Swagger 노출 범위를 같이 확인한다.
- 파일 업로드/다운로드 기능 수정 시 DB 상태, Redis 상태, 실제 파일 시스템 경로가 같이 맞는지 확인한다.
- TUS 경로를 바꾸면 nginx `/_upload_auth`, tusd hook URL, `SecurityConfig` 공개 경로, `JwtAuthenticationFilter.shouldNotFilter(...)` 를 함께 확인한다.
- SunEditor 또는 단순 업로드 설정을 바꾸면 `file.upload.root-path`, `file.upload.view.file-domain`, nginx `/uploads/` alias 경로가 같은 저장 루트를 가리키는지 확인한다.
## 기능 특성별 점검 포인트
- 스케줄러 코드는 실행 주기, 중복 실행 가능성, 로그량을 반드시 점검한다.
- 인증 방식이 섞여 있으므로 세션 기반 처리와 JWT `SecurityContext` 사용 위치를 먼저 구분하고 수정한다.
- 파일 경로를 다루는 기능은 상대경로 탈출, 루트 이탈 방지 같은 검증을 같이 본다.
- 설정 파일 수정 시 `local`, `pjt`, 공통 설정 간 차이를 함께 확인한다.
- file-domain 정적 파일은 `/uploads/editor/...` 같은 최종 파일 URL이 브라우저에서 직접 열리는지 확인한다.
- `/uploads/tmp/...` 는 404로 막히는지 확인한다.
- SunEditor 업로드는 응답 JSON의 `result[].url` 이 file-domain 절대 URL인지 확인하고, 에디터 본문에 이미지가 실제 삽입되는지 확인한다.