[README] 문서 갱신
This commit is contained in:
@@ -1,153 +1,400 @@
|
||||
# alist API
|
||||
# alist API
|
||||
|
||||
Spring Boot 기반의 RESTful API 서버
|
||||
Spring Boot 3 기반의 `alist` 백엔드 API 서버입니다.
|
||||
|
||||
## 프로젝트 정보
|
||||
## 프로젝트 개요
|
||||
|
||||
- **그룹**: com.alist
|
||||
- **포트**: 8106
|
||||
- **빌드 결과물**: `api.jar`
|
||||
- **GitLab**: https://gitlab.pjt.kr/alist/api
|
||||
- 그룹: `com.alist`
|
||||
- Java: `21`
|
||||
- Spring Boot: `3.5.10`
|
||||
- 포트: `8106`
|
||||
- 빌드 결과물: `build/libs/api.jar`
|
||||
|
||||
## 기술 스택
|
||||
|
||||
- **Java**: 21
|
||||
- **Framework**: Spring Boot 3.5.10
|
||||
- **빌드 도구**: Gradle
|
||||
- **데이터베이스**: MariaDB
|
||||
- **ORM**: MyBatis
|
||||
- **인증**: JWT (jjwt 0.11.5) + Spring Security
|
||||
- **API 문서**: Swagger (springdoc-openapi 2.6.0)
|
||||
- **기타**: Lombok, Validation, Actuator, log4jdbc
|
||||
- Spring Web
|
||||
- Spring Security
|
||||
- JWT (`jjwt 0.11.5`)
|
||||
- Spring Session + Redis
|
||||
- MyBatis (`mapper/**/*.xml`)
|
||||
- MariaDB
|
||||
- Swagger / OpenAPI (`springdoc-openapi 2.8.0`)
|
||||
- Validation
|
||||
- Actuator
|
||||
- Lombok
|
||||
- log4jdbc
|
||||
|
||||
## 주요 기능
|
||||
|
||||
- JWT 기반 인증/인가
|
||||
- MyBatis XML Mapper를 통한 데이터 액세스
|
||||
- 표준화된 API 응답 (`ApiResponse<T>`)
|
||||
- Swagger API 문서 (HTTP Basic 인증)
|
||||
- 파일 업로드/다운로드
|
||||
- Actuator 헬스 체크
|
||||
- JWT 발급 및 검증
|
||||
- Redis 기반 세션 관리와 쿠키 설정
|
||||
- Swagger UI Basic 인증 보호
|
||||
- DB 기반 허용 Origin 캐시를 사용하는 동적 CORS
|
||||
- TUS 업로드 초기화, 권한 검증, 상태 조회, 완료 처리, 취소 처리
|
||||
- 파일 조회/다운로드 API
|
||||
- 공통 응답 래퍼 `ApiResponse<T>` 및 `ApiResponseCode` 사용
|
||||
|
||||
## 빌드 및 실행
|
||||
## 디렉터리 구조
|
||||
|
||||
```text
|
||||
src/main/java/com/alist/api
|
||||
├── common
|
||||
│ ├── modules/file # 공통 파일 업로드/다운로드 모듈
|
||||
│ ├── response # ApiResponse, ApiResponseCode
|
||||
│ └── utils # 공통 유틸리티
|
||||
├── config
|
||||
│ ├── cache # CORS 허용 Origin 캐시
|
||||
│ ├── exception # 전역 예외 처리
|
||||
│ ├── filter # DynamicCorsFilter
|
||||
│ ├── jwt # JWT 인증 관련 구성
|
||||
│ └── properties # 설정 프로퍼티
|
||||
└── modules
|
||||
├── auth # 인증/세션 관련 API
|
||||
├── main # 루트 리다이렉트
|
||||
└── testUser # 테스트 사용자 API
|
||||
```
|
||||
|
||||
리소스 파일은 아래 위치를 사용합니다.
|
||||
|
||||
- 설정: `src/main/resources/application*.yaml`
|
||||
- Mapper XML: `src/main/resources/mapper/**/*.xml`
|
||||
- 로그 설정: `src/main/resources/logback-*.xml`
|
||||
|
||||
## 실행 방법
|
||||
|
||||
### 빌드
|
||||
|
||||
```bash
|
||||
./gradlew bootJar
|
||||
```
|
||||
|
||||
### 로컬 실행
|
||||
|
||||
```bash
|
||||
./gradlew bootRun --args='--spring.profiles.active=local'
|
||||
```
|
||||
|
||||
### JAR 실행
|
||||
|
||||
```bash
|
||||
java -jar build/libs/api.jar --spring.profiles.active=local
|
||||
```
|
||||
|
||||
## 환경 설정
|
||||
|
||||
### 프로파일
|
||||
## 프로파일
|
||||
|
||||
| 프로파일 | 설명 | 설정 파일 |
|
||||
|---------|------|----------|
|
||||
| `local` | 로컬 개발 환경 | `application-local.yml` |
|
||||
| `pjt` | 프로젝트(개발) 환경 | `application-pjt.yml` |
|
||||
| --- | --- | --- |
|
||||
| `local` | 로컬 개발 환경 | `src/main/resources/application-local.yaml` |
|
||||
| `pjt` | 프로젝트 개발 서버 환경 | `src/main/resources/application-pjt.yaml` |
|
||||
|
||||
### 환경별 접속 정보
|
||||
공통 설정은 `src/main/resources/application.yaml`에 있습니다.
|
||||
|
||||
#### 로컬 (local)
|
||||
- **API**: http://localhost:8106
|
||||
- **Swagger**: http://localhost:8106/swagger-ui.html
|
||||
## 필수 설정 항목
|
||||
|
||||
#### 개발 서버 (pjt)
|
||||
- **API**: https://api-alist.pjt.kr
|
||||
- **파일**: https://file-alist.pjt.kr
|
||||
- **Swagger**: https://api-alist.pjt.kr/swagger-ui/index.html
|
||||
실행 전 아래 설정들이 환경에 맞게 준비되어 있어야 합니다.
|
||||
|
||||
## API 문서
|
||||
### 데이터 저장소
|
||||
|
||||
Swagger UI를 통해 API 문서를 확인할 수 있습니다.
|
||||
- `spring.datasource.*`
|
||||
- `spring.data.redis.*`
|
||||
- `spring.session.*`
|
||||
|
||||
- **접속**: `/swagger-ui.html`
|
||||
- **인증**: HTTP Basic (환경별 yaml에 설정된 ID/PW 사용)
|
||||
### 인증/보안
|
||||
|
||||
## 패키지 구조
|
||||
- `jwt.secret`
|
||||
- `jwt.access-token-validity-seconds`
|
||||
- `jwt.refresh-token-validity-seconds`
|
||||
- `cookie.secure`
|
||||
- `cookie.domain`
|
||||
- `cookie.name`
|
||||
- `cookie.same-site`
|
||||
- `swagger.login.id`
|
||||
- `swagger.login.password`
|
||||
|
||||
```
|
||||
com.alist.api
|
||||
├── common
|
||||
│ ├── response/ # ApiResponse, ApiResponseCode
|
||||
│ └── utils/ # 공통 유틸리티
|
||||
├── config
|
||||
│ ├── jwt/ # JWT 필터, 핸들러, Provider
|
||||
│ ├── exception/ # GlobalExceptionHandler
|
||||
│ ├── properties/ # JwtProperties 등
|
||||
│ ├── SecurityConfig.java
|
||||
│ └── OpenApiConfig.java
|
||||
└── modules
|
||||
└── {도메인}/ # Controller, Service, Mapper, DTO
|
||||
```
|
||||
### 파일 업로드
|
||||
|
||||
## 보안
|
||||
- `file.upload.tus-endpoint`
|
||||
- `file.upload.public-base-url`
|
||||
- `file.upload.tmp-root`
|
||||
- `file.upload.final-root`
|
||||
- `file.upload.interrupt-seconds`
|
||||
- `file.upload.auth-cache.ttl-seconds`
|
||||
|
||||
### 인증 방식
|
||||
- **Swagger**: HTTP Basic 인증 (InMemory)
|
||||
- **API**: JWT Bearer 토큰 인증 (Stateless)
|
||||
`pjt` 프로파일은 DB/Redis/JWT/Swagger 값을 환경변수로 주입받도록 작성되어 있습니다.
|
||||
|
||||
## 보안 구조
|
||||
|
||||
### Swagger
|
||||
|
||||
- 보호 경로: `/v3/api-docs/**`, `/swagger-ui/**`, `/swagger-ui.html`
|
||||
- 인증 방식: HTTP Basic
|
||||
- 계정 정보: `swagger.login.id`, `swagger.login.password`
|
||||
|
||||
### API
|
||||
|
||||
- 기본 인증 방식: JWT Bearer
|
||||
- 세션 저장소: Redis
|
||||
- 세션 쿠키: `RedisSessionConfig`에서 도메인, Secure, SameSite 제어
|
||||
|
||||
### 공개 경로
|
||||
- `/` - 루트
|
||||
- `/actuator/health` - 헬스 체크
|
||||
- `/auth/**` - 인증 관련 API
|
||||
- `/api/user/signup` - 회원가입
|
||||
|
||||
## 응답 코드
|
||||
- `/`
|
||||
- `/actuator/health`
|
||||
- `/auth/**`
|
||||
- `/test/**`
|
||||
- `/files/tusHook`
|
||||
|
||||
API 응답은 `ApiResponseCode` enum으로 표준화되어 있습니다.
|
||||
루트 `/` 요청은 `/swagger-ui/index.html`로 리다이렉트됩니다.
|
||||
|
||||
### 주요 응답 코드
|
||||
## CORS
|
||||
|
||||
| 코드 | 메시지 | HTTP Status | 용도 |
|
||||
|------|--------|-------------|------|
|
||||
| `CODE_200` | 성공 | 200 OK | 일반 성공 |
|
||||
| `CODE_2001` | {0} 정보 조회에 성공하였습니다. | 200 OK | 단건 조회 성공 |
|
||||
| `CODE_2002` | {0} 등록 되었습니다. | 201 Created | 등록 성공 |
|
||||
| `CODE_2003` | 조회된 정보가 없습니다. | 200 OK | 조회 결과 없음 |
|
||||
| `CODE_2004` | 중복된 {0} 정보 입니다. | 409 Conflict | 중복 데이터 |
|
||||
| `CODE_4001` | 입력값을 확인해주세요. | 400 Bad Request | Validation 오류 |
|
||||
| `CODE_4003` | 필수 요청 파라미터가 누락되었습니다. | 400 Bad Request | 필수 파라미터 누락 |
|
||||
`DynamicCorsFilter`가 최우선 필터로 동작하며, 허용 Origin 목록은 `CorsAllowedOriginsCache`에서 조회합니다.
|
||||
|
||||
## 배포
|
||||
- 허용된 Origin에만 `Access-Control-Allow-Origin` 설정
|
||||
- Credential 허용
|
||||
- `OPTIONS` preflight 요청은 `200 OK`로 즉시 응답
|
||||
- DB를 직접 매 요청마다 조회하지 않고 캐시된 목록을 사용
|
||||
|
||||
### Docker
|
||||
```bash
|
||||
# 이미지 빌드
|
||||
docker build -t registry.pjt.kr/alist/api .
|
||||
## 파일 업로드/다운로드
|
||||
|
||||
# 컨테이너 실행
|
||||
docker run -d -p 8106:8106 --name alist-api registry.pjt.kr/alist/api
|
||||
이 프로젝트는 대용량 업로드를 위해 TUS 서버(`tusd`)와 연동합니다. API는 업로드 메타데이터 관리, 업로드 권한 검증, 상태 저장, 완료/취소 처리를 담당합니다.
|
||||
|
||||
### 주요 업로드 엔드포인트
|
||||
|
||||
- `POST /files/uploadInit`
|
||||
- `GET /files/uploadAuth`
|
||||
- `POST /files/uploadStatus`
|
||||
- `POST /files/tusHook`
|
||||
- `POST /files/uploadCancel`
|
||||
|
||||
### 파일 조회 엔드포인트
|
||||
|
||||
- `GET /files/list/{fileMasterIdx}`
|
||||
- `GET /files/view/{fileUuid}`
|
||||
- `GET /files/download/{fileUuid}`
|
||||
|
||||
### 운영 메모
|
||||
|
||||
- TUS 업로드 엔드포인트: `https://file-alist.pjt.kr/tus/files/`
|
||||
- 업로드 임시 경로: `/srv/project/alist/uploads/tmp`
|
||||
- 업로드 최종 경로: `/srv/project/alist/uploads`
|
||||
- 정적 파일 도메인: `https://file-alist.pjt.kr`
|
||||
|
||||
## 주요 인증 엔드포인트
|
||||
|
||||
- `POST /auth/token`
|
||||
- `POST /auth/refresh`
|
||||
- `GET /auth/loginChecked`
|
||||
- `POST /auth/logout`
|
||||
- `POST /test/testSignup`
|
||||
|
||||
## API 예시 요청/응답
|
||||
|
||||
아래 예시는 실제 컨트롤러의 요청 필드와 `ApiResponse<T>` 응답 구조를 기준으로 정리했습니다.
|
||||
|
||||
### 1. 토큰 발급
|
||||
|
||||
요청:
|
||||
|
||||
```http
|
||||
POST /auth/token
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "test"
|
||||
}
|
||||
```
|
||||
|
||||
### CI/CD
|
||||
- **Jenkins**: `Jenkinsfile.pjt`
|
||||
- **Docker Registry**: registry.pjt.kr
|
||||
- **배포 스크립트**: `deploy/` 디렉토리
|
||||
응답 예시:
|
||||
|
||||
## 개발 가이드
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": null,
|
||||
"accessToken": "eyJhbGciOiJI..."
|
||||
},
|
||||
"code": "CODE_2001",
|
||||
"message": "임시 토큰 정보 조회에 성공하였습니다."
|
||||
}
|
||||
```
|
||||
|
||||
상세한 개발 가이드는 [CLAUDE.md](./CLAUDE.md)를 참고하세요.
|
||||
### 2. 테스트 회원가입
|
||||
|
||||
### 코드 작성 규칙
|
||||
- 응답은 `ApiResponse<T>` 래퍼 사용
|
||||
- 응답 코드는 `ApiResponseCode` enum 사용
|
||||
- MyBatis Mapper XML은 `src/main/resources/mapper/` 하위에 작성
|
||||
- 새 모듈 추가 시: `modules/{moduleName}/` 구조로 생성
|
||||
요청:
|
||||
|
||||
### MyBatis 규칙
|
||||
- Mapper XML 위치: `src/main/resources/mapper/**/*.xml`
|
||||
- 카멜케이스 자동 변환 활성화 (`map-underscore-to-camel-case: true`)
|
||||
- Mapper 인터페이스와 XML의 namespace, id 일치 필수
|
||||
```http
|
||||
POST /test/testSignup
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "testuser01",
|
||||
"password": "pass1234"
|
||||
}
|
||||
```
|
||||
|
||||
응답 예시:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"userIdx": 101,
|
||||
"id": "testuser01"
|
||||
},
|
||||
"code": "CODE_2002",
|
||||
"message": "아이디 등록 되었습니다."
|
||||
}
|
||||
```
|
||||
|
||||
중복일 경우 예시:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"userIdx": null,
|
||||
"id": null
|
||||
},
|
||||
"code": "CODE_2004",
|
||||
"message": "중복된 아이디 정보 입니다."
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 파일 업로드 초기화
|
||||
|
||||
요청:
|
||||
|
||||
```http
|
||||
POST /files/uploadInit
|
||||
Content-Type: application/json
|
||||
Cookie: ALIST_SESSION=...
|
||||
|
||||
{
|
||||
"fileCategory": "notice",
|
||||
"folderPath": "/2026/03",
|
||||
"itemList": [
|
||||
{
|
||||
"originName": "guide.pdf",
|
||||
"sizeBytes": 102400,
|
||||
"contentType": "application/pdf"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
응답 예시:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"fileMasterIdx": 55,
|
||||
"tusEndpoint": "https://file-alist.pjt.kr/tus/files/",
|
||||
"itemList": [
|
||||
{
|
||||
"fileSeq": 1,
|
||||
"fileUuid": "2f5f3ef1-8a4e-4b2d-84da-1c1111111111",
|
||||
"uploadToken": "upload-token-sample",
|
||||
"originName": "guide.pdf",
|
||||
"sizeBytes": 102400,
|
||||
"contentType": "application/pdf"
|
||||
}
|
||||
]
|
||||
},
|
||||
"code": "CODE_200",
|
||||
"message": "성공"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 업로드 상태 조회
|
||||
|
||||
요청:
|
||||
|
||||
```http
|
||||
POST /files/uploadStatus
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"fileUuid": "2f5f3ef1-8a4e-4b2d-84da-1c1111111111"
|
||||
}
|
||||
```
|
||||
|
||||
응답 예시:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"fileUuid": "2f5f3ef1-8a4e-4b2d-84da-1c1111111111",
|
||||
"status": "UPLOADING",
|
||||
"uploadedBytes": 51200,
|
||||
"totalBytes": 102400,
|
||||
"percent": 50,
|
||||
"updatedAt": "2026-03-11T09:30:00"
|
||||
},
|
||||
"code": "CODE_200",
|
||||
"message": "성공"
|
||||
}
|
||||
```
|
||||
|
||||
조회 결과가 없을 경우 예시:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"fileUuid": null,
|
||||
"status": null,
|
||||
"uploadedBytes": null,
|
||||
"totalBytes": null,
|
||||
"percent": null,
|
||||
"updatedAt": null
|
||||
},
|
||||
"code": "CODE_2003",
|
||||
"message": "조회된 정보가 없습니다."
|
||||
}
|
||||
```
|
||||
|
||||
## Actuator
|
||||
|
||||
외부 노출 대상은 아래와 같습니다.
|
||||
|
||||
- `health`
|
||||
- `info`
|
||||
- `metrics`
|
||||
|
||||
헬스체크 기본 경로:
|
||||
|
||||
- `GET /actuator/health`
|
||||
|
||||
## 응답 규칙
|
||||
|
||||
모든 API 응답은 `ApiResponse<T>` 래퍼를 사용하며, 상태/메시지는 `ApiResponseCode` enum으로 관리합니다.
|
||||
|
||||
자주 사용하는 코드 예시는 아래와 같습니다.
|
||||
|
||||
| 코드 | HTTP Status | 의미 |
|
||||
| --- | --- | --- |
|
||||
| `CODE_200` | `200 OK` | 일반 성공 |
|
||||
| `CODE_204` | `204 No Contnet` | 일반 성공 |
|
||||
| `CODE_2001` | `200 OK` | 단건 조회 성공 |
|
||||
| `CODE_2002` | `201 Created` | 등록 성공 |
|
||||
| `CODE_2003` | `200 OK` | 조회 결과 없음 |
|
||||
| `CODE_2004` | `409 Conflict` | 중복 데이터 |
|
||||
| `CODE_4001` | `400 Bad Request` | 입력값/바인딩 오류 |
|
||||
| `CODE_4003` | `400 Bad Request` | 필수 파라미터 누락 |
|
||||
| `CODE_401` | `401 Unauthorized` | 인증 필요 |
|
||||
| `CODE_403` | `403 Forbidden` | 권한 없음 |
|
||||
| `CODE_500` | `500 Internal Server Error` | 서버 오류 |
|
||||
|
||||
## 배포 관련
|
||||
|
||||
- Dockerfile: `Dockerfile`
|
||||
- Jenkins 파이프라인: `Jenkinsfile.pjt`
|
||||
- 배포 스크립트: `deploy/`
|
||||
- 개발 서버 API 도메인: `https://api-alist.pjt.kr`
|
||||
- 파일 도메인: `https://file-alist.pjt.kr`
|
||||
- Swagger: `https://api-alist.pjt.kr/swagger-ui/index.html`
|
||||
|
||||
## 참고
|
||||
|
||||
- Swagger 로컬 접속: [http://localhost:8106/swagger-ui.html](http://localhost:8106/swagger-ui.html)
|
||||
- 운영 환경에서는 `application-pjt.yaml`의 환경변수 주입 방식 사용을 권장합니다.
|
||||
|
||||
## 라이선스
|
||||
|
||||
|
||||
Reference in New Issue
Block a user