AlgoMate
🚀 동적으로 파일을 제공하는 방법
SungHoJung
2025. 2. 16. 18:07
✅ 1️⃣ Spring 컨트롤러로 파일 제공 (가장 유연한 방법)
파일이 자주 추가되거나 변경된다면, 정적 리소스가 아니라 Spring 컨트롤러를 사용해서 파일을 서빙하는 것이 좋다.
이렇게 하면 새로운 파일이 추가되거나 수정되어도 서버를 재시작할 필요 없이 제공할 수 있다.
✔ 컨트롤러 구현
@RestController
@RequestMapping("/api/v1/files")
public class FileController {
// 🔹 외부 폴더 경로 (resources 내부 대신, 별도 경로 사용)
private final String BASE_PATH = "/Users/sungho/Documents/study/Algomate/algorithm-mate/src/main/resources/solutions/";
@GetMapping("/{problemId}/{lang}/{fileName}")
public ResponseEntity<Resource> getFile(
@PathVariable String problemId,
@PathVariable String lang,
@PathVariable String fileName) {
try {
// 🔹 동적으로 파일 경로 생성
Path filePath = Paths.get(BASE_PATH, problemId, lang, fileName).toAbsolutePath();
Resource resource = new UrlResource(filePath.toUri());
if (!resource.exists() || !resource.isReadable()) {
return ResponseEntity.notFound().build();
}
// 🔹 파일 다운로드 헤더 설정 (브라우저에서 열리도록)
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + fileName + "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
📌 사용 방법
http://localhost:8080/api/v1/files/1027/py/clw8679.py
✅ 파일이 추가되거나 변경되어도 서버를 재시작할 필요 없음!
✅ 파일이 존재하면 즉시 제공, 없으면 404 응답
✅ 2️⃣ 로컬 파일 시스템을 static으로 제공 (Spring 정적 리소스 기능 + 동적 폴더 추가)
정적 리소스를 사용하면서도, 새로운 파일이 추가될 때 자동 반영되도록 만들 수도 있다.
✔ application.yml 설정 추가
web:
resources:
static-locations: file:/Users/sungho/Documents/study/Algomate/algorithm-mate/src/main/resources/solutions/
✔ application.properties 설정 추가
spring.web.resources.static-locations=file:/Users/sungho/Documents/study/Algomate/algorithm-mate/src/main/resources/solutions/
📌 사용 방법
http://localhost:8080/1027/py/clw8679.py
✅ 새로운 파일이 추가되거나 수정되더라도 바로 반영됨!
✅ 컨트롤러 없이 Spring이 자동으로 제공
⚠ 파일이 보안에 민감하다면 권한을 조절해야 함.
✅ 3️⃣ 파일 시스템을 감지해서 자동으로 DB에 반영 (Advanced)
파일이 추가될 때마다 자동으로 데이터베이스에 기록하고 API로 조회할 수도 있다.
✔ WatchService로 파일 변경 감지
Java의 WatchService를 사용하면 폴더를 실시간으로 감지하고, 새로운 파일이 추가될 때 이벤트를 발생시킬 수 있다.
@Component
public class FileWatcherService {
private final String directoryPath = "/Users/sungho/Documents/study/Algomate/algorithm-mate/src/main/resources/solutions/";
@EventListener(ApplicationReadyEvent.class)
public void watchDirectory() {
Executors.newSingleThreadExecutor().execute(() -> {
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
Path path = Paths.get(directoryPath);
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context();
System.out.println("📌 파일 변경 감지됨: " + kind + " - " + fileName);
// 🔹 DB에 새로운 파일 경로 저장하는 로직 추가 가능
}
key.reset();
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
✅ 파일이 생성되거나 수정되면 자동 감지됨!
✅ DB에 경로를 기록하고 API에서 조회 가능
🔹 결론: 가장 적절한 방식은?
방법 | 장점 | 단점 |
Spring 컨트롤러로 파일 제공 | 파일 추가, 변경 즉시 반영 | 직접 구현 필요 |
정적 리소스로 파일 제공 | 설정만 하면 자동 반영 | application.properties 수정 필요 |
파일 감지 + DB 업데이트 | 파일이 추가될 때 자동으로 추적 가능 | WatchService 구현 필요 |
🚀 추천!
- 단순하게 파일을 제공하면서 동적 반영이 필요하면 Spring 컨트롤러(1️⃣)
- 외부 저장소를 정적 리소스로 활용하려면 static 설정(2️⃣)
- 파일이 추가될 때 DB에 자동 반영하려면 WatchService(3️⃣)
🔥 번외) S3 확장까지 고려한다면?
- 로컬에서는 spring.web.resources.static-locations=file:/your/local/path/
- S3로 이전하면 static-locations=https://s3.amazonaws.com/your-bucket/ 로 변경
- 별도 코드 수정 없이 설정만 변경하면 자동 적용 가능! 🎯
📌 S3 권한 문제(ACL/Public Access)도 고려해야 하니, 필요하면 Presigned URL 방식을 추가할 수도 있다! 🚀