< 작업 환경 >
IDE: IntelliJ
Spring Boot: 3.4.1
Java: 21
EC2 Linux: Ubuntu
EC2 인스턴스를 연결하고 Spring Boot 프로젝트를 배포하려는데 서버가 실행되지 않았습니다. 로그를 확인해보니 아래와 같은 오류가 발생했습니다.
Caused by: java.io.FileNotFoundException: class path resource [location_kr.csv] cannot be resolved to absolute file path because it does not reside in the file
오류의 내용은 location_kr.csv 파일을 찾을 수가 없다는 거였습니다.
하지만 해당 파일이 있는 디렉토리에서 ls 명령어를 실행해 보면 아래와 같이 파일이 존재하는 걸 확인할 수 있습니다.
ubuntu@ip-172-31-46-72:~/study-hub/src/main/resources$ ls application.properties location_kr.csv static templates
파일이 존재하는데 파일을 인식할 수 없다면, 파일을 인식하는 방법에 문제가 있는게 아닐까 생각했습니다. 그리고 해당 파일을 인식하는 코드를 살펴보며 해답을 찾을 수 있었습니다.
@PostConstruct
public void initLocationData() throws IOException {
if (locationRepository.count() == 0) {
Resource resource = new ClassPathResource("location_kr.csv");
List<Location> locations = Files.readAllLines(resource.getFile().toPath(), StandardCharsets.UTF_8).stream()
.map(line -> {
String[] parts = line.split(",");
return Location.builder().province(parts[0]).city(parts[1]).build();
}).toList();
locationRepository.saveAll(locations);
}
}
<수정된 코드>
@PostConstruct
public void initLocationData() throws IOException {
if (locationRepository.count() == 0) {
Resource resource = new ClassPathResource("location_kr.csv");
try (InputStream inputStream = resource.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
List<Location> locations = reader.lines()
.map(line -> {
String[] parts = line.split(",");
return Location.builder().province(parts[0]).city(parts[1]).build();
}).toList();
locationRepository.saveAll(locations);
}
}
}
- 결론 : InputStream을 사용하여 리소스를 읽도록 함
Spring Boot에서는 JAR 파일로 패키징된 애플리케이션에서 리소스를 읽을 때 ClassPathResource#getFile() 메서드가 동작하지 않을 수 있습니다. 이는 JAR 파일 내의 리소스가 파일 시스템의 파일로 표현되지 않기 때문입니다.
이때문에 resource.getFile()를 호출하여 JAR 파일 내부의 리소스를 읽으려고 했을 때 java.io.FileNotFoundException이 발생한 것입니다. 따라서 ClassPathResource#getInputStream() 메서드를 사용하여 JAR 파일 내부에서도 리소스를 읽을 수 있도록함으로써 문제를 해결할 수 있었습니다.