역할
- 서버 로컬 파일 시스템에 이미지 및 첨부파일 저장, 이동, URL 변환 담당
- 업로드 과정에서 검증, 안전 장치, tmp → 최종 경로 이동 처리
- 에디터 이미지와 일반 첨부파일 모두 관리
1. 핵심 구조
@Service
public class FileStorageService {
private final Path root;
public FileStorageService(@Value("${app.upload-dir}") String uploadDir) {
this.root = Paths.get(uploadDir).normalize(); // 절대 경로 변환
}
// module 명 검증 및 정리
private String editorDir(String module) { ... }
// 임시 tmp 폴더에 에디터 이미지 저장
public String storeEditorImageToTmp(MultipartFile file, String module) { ... }
// content에서 tmp 이미지 URL 추출
public List<String> extractTmpEditorImagePaths(String content, String module) { ... }
// tmp → owner 폴더로 이동
public String moveTmpToOwnerDir(String tmpPath, String module, Long ownerId) { ... }
// content 내 tmp 이미지 이동 + URL 치환
public String commitEditorImagesInContent(String content, String module, Long ownerId) { ... }
// 깨진 iframe <a> 태그 정리
private String normalizeQuillHtml(String html) { ... }
// /uploads/... → 실제 서버 경로 변환
public Path resolveUploadPath(String storagePath) { ... }
// 첨부파일 저장 (task 관련)
public StoredAttachment storeTaskAttachmentToTaskDir(MultipartFile file, String module, Long taskId) { ... }
}
2. 설명
- 에디터 이미지 처리
- 임시 폴더(
tmp)에 저장 → storeEditorImageToTmp()
- content 내 URL 추출 →
extractTmpEditorImagePaths()
- 최종 owner 폴더로 이동 →
moveTmpToOwnerDir()
- content HTML 내 URL 치환 + iframe 정리 →
commitEditorImagesInContent()
- 첨부파일 처리
- taskId 기준 폴더에 저장 →
storeTaskAttachmentToTaskDir()
- UUID + 안전한 파일명 사용
- 용량, 확장자 검증, 경로 안전장치
- 공통 안전장치
- root 밖으로 파일 못 나가게 경로 검증
- 입력값 검증 및 용량/형식 체크
- 잘못된 요청 →
ApiException + ErrorCode throw
- IO 오류 → INTERNAL_ERROR 처리
3. 포인트
- tmp 폴더 전략 → 작성 중 이미지 안전하게 임시 저장 후 commit
- 정규식 + 안전장치 → content 내 URL 추출/치환, iframe 깨짐 방지
- 모듈/ownerId 기반 폴더 구조 → 업로드 파일 관리 용이
- 첨부파일 → taskId 기준 폴더 + UUID 저장 → DB 저장 경로 반환
- 일관된 예외 처리 → 파일 관련 문제 발생 시 모두
ApiException throw