개요
Java Spring Boot에서 프로젝트를 만들다 보면 엑셀 혹은 내부 json 파일을 읽고 return 하거나,
static 파일들을 사용자가 직접 제공받을 수 있도록 web으로 열어두고 싶은 경우가 생긴다.
위 경우의 해결 방법과 주의할 점을 알아보자.
앱 내 활용
앱에서 resource로 사용할 파일들을 읽어서 return 하거나 재활용하기 위해서는 resource 폴더에 파일을 두고, ClassPathResource 클래스를 통해 가져다가 사용할 수 있다.
예시로 파일을 하나 만들어 필요한 내용을 기입하고 해당 파일의 내용을 읽어 반환할 수 있도록 구현해 보자.
먼저 test.json 파일을 만들어 아래 내용으로 작성한다.
{
"version": "v1",
"template": [
{"key": "key_1", "value": "value_1"},
{"key": "key_2", "value": "value_2"}
]
}
이후 resources 폴더에 해당 파일을 배치한다.
우리는 resources 폴더 내에 template이라는 폴더를 만들고 이 폴더 내에 test.json 파일을 배치했다.
이후 해당 파일의 내용을 읽어서 출력해 보자.
String filePath = "template/test.json"; // resources 폴더 내의 파일 경로
ClassPathResource classPathResource = new ClassPathResource(filePath);
if (!classPathResource.exists()) throw new RuntimeException("파일이 존재하지 않습니다.");
// 빌드 이후 classPathResource.getFile() 사용 불가
InputStream inputStream = classPathResource.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(inputStream);
String result = new String(bytes);
System.out.println(result);
ClassPathResource를 통해 원하는 경로의 파일을 가져와서 String으로 변환해 출력하였다.
여기서 주의해야 할 점은 classPathResource에서 getFile()을 통해 File 형식으로 반환할 수 없는 경우가 존재한다는 것이다.
로컬에서는 getFile()로 테스트했을 때 정상적으로 동작하지만,
jar로 빌드 후 실행했을 땐 FileNotFoundException이 발생하므로 File 대신 InputStream으로 해당 파일을 읽어 처리해야 한다.
참고) https://sonegy.wordpress.com/2015/07/23/spring-boot-executable-jar에서-file-resource-처리
배포
프로젝트 내에 resource로 사용할 static 파일들을 외부에서도 사용하기 위해서는,
resources 폴더 내에 static 폴더를 생성하고,
해당 폴더에 파일을 두고 배포하면 해당 경로에 맞춰서 resource 파일 접근이 가능하다.
이번에는 위에서 만든 test.json 파일 자체에 사용자가 접근할 수 있게 배치해 보자.
static 폴더도 마찬가지로 resources 폴더 내에 존재하기 때문에 위에서 ClassPathResources를 통해 가져와서 사용했던 것처럼 동일하게 사용하는 것도 가능하다.
위의 폴더 구조로 배치하고 애플리케이션을 실행하면 바로 해당 경로로 접근하여 해당 파일을 읽어 들일 수 있다.
이때 우리가 해당 파일의 경로를 알기 때문에 바로 서버의 도메인과 함께 return 해줄 수도 있지만,
도메인이 바뀐다거나 여러 서버에 구성하는 경우 일일이 대응해줘야 하기 때문에 번거로울 수 있다.
그렇기 때문에 동적으로 접근 경로를 전달해 줄 필요가 존재한다.
우리는 사용자가 요청한 서버의 경로를 가져올 수 있으니 그에 맞춰 해당 파일의 접근 경로를 출력하도록 구성해 보자.
여기에서 파일 경로는 static 폴더 이후의 경로를 기준으로 작성해야 한다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/test")
public class TestController {
private final HttpServletRequest httpServletRequest;
@GetMapping
public void test() {
String filePath = "test.json"; // static 폴더 내의 파일 경로
String serverPath = httpServletRequest.getScheme() + "://" + httpServletRequest.getServerName();
int port = httpServletRequest.getServerPort();
if (port != 80 && port != 443) {
serverPath += ":" + port;
}
// tomcat 등의 servlet container로 구동했을 때 contextPath 대응
serverPath += httpServletRequest.getContextPath() + "/" + filePath;
System.out.println(serverPath);
}
}
포트의 경우 그대로 출력해도 사용 상 문제는 없으나 사용자가 알 필요 없는 포트까지 노출하는 건 좋지 않으므로 80이나 443일 때에는 생략하도록 구성하였다.
또한 tomcat 같은 서블릿 컨테이너로 애플리케이션을 구동하는 등의 경우 존재하는 contextPath까지 대응이 되었다. contextPath가 존재하지 않으면 빈 값으로 나타나므로 따로 contextPath 대응이 필요 없는 경우 해당 부분은 생략해도 무방하다.
추가) 아래와 같이 사용자의 requestURL에서 servletPath 내용 부분만 제거하고 파일의 경로를 추가하는 방법으로 적용할 수도 있다.
HttpServletRequest에서 servletPath는 contextPath를 제외한 requestURI라고 보면 된다.
@GetMapping
public String test() {
String filePath = "template/test.json"; // static 폴더 내의 파일 경로
String serverPath = httpServletRequest.getRequestURL().toString();
serverPath = serverPath.substring(0, serverPath.lastIndexOf(httpServletRequest.getServletPath())) + "/" + filePath;
return serverPath;
}
다만 웹 서버를 해당 서버 내의 애플리케이션의 경로와 동일하게 지정해주지 않고 proxyPass를 이용하는 등의 방법으로 다른 경로로 지정해 줬다면 해당 경로는 애플리케이션에서 파악할 방법이 없으므로 마찬가지로 코드에서도 다른 경로로 지정해서 사용해야 한다.
참고
위에서 기술한 resources/static 폴더 말고도 public, webapp 폴더 등 파일을 두고 배포하면 해당 경로에 맞춰서 resource 파일을 자동으로 호스팅해주는 폴더들이 몇개 더 존재한다.
따라서 해당 폴더들이 각각 존재한다면 당연히 호스팅 시 경로가 겹치지 않도록 설계해야 한다.
아래는 그 중 하나인 webapp 폴더에서 동일한 경로로 test.json 파일을 호스팅하는 예시.
마무리
필자는 배포 방식의 경우 프로젝트에서 자주 접근이 필요하지만 개수가 많지 않은 이미지를 띄울 때 해당 방식을 많이 사용한다.
아래는 이미지를 띄우는 예시.
'Java & Spring' 카테고리의 다른 글
[Spring] QueryDsl transform 및 SqmCaseSearched 오류 해결방법 with Hibernate 6.x (0) | 2023.10.05 |
---|---|
[Spring] JPA 다중 서버 환경 DB 동시성 문제 해결하기 (2) | 2023.08.18 |
[JAVA] LocalDate로 한국식 월 주차 구하기 (6) | 2023.07.27 |
[JAVA] Calendar로 한국식 월 주차 구하기 (0) | 2023.07.24 |
[JAVA] Lombok Builder, SuperBuilder와 Generic 사용하기 (0) | 2023.07.21 |