[Spring Boot] 정적자원(Static Resources, img; js; css 파일 등)에 대한 접근 설정
Spring Boot 개발 환경에서 정적 자원에 대한 접근 설정에 대해 이야기하고자 한다.
웹(WEB) 환경에서 클라이언트와 서버는 서로 자원들을 주고받는데, 이 자원들의 속성은 크게 '동적이냐 정적이냐'에 따라 동적 자원(Dynamic Resource)와 정적 자원(Static Resource)로 구분한다.
※ 동적자원 vs 정적자원
동적자원(Dynamic Resource)이란, 프로그램을 실행하기 위해 자원을 배당할 시 적용되는 기준에 따라 결정되는 배당기법이다. 가령 우리가 구글, 네이버와 같은 포털이나 유튜브에서 실시간으로 업로드되고 변하는 정보(뉴스나 날씨)를 볼 수 있는데, 이런 것들이 모두 동적 자원의 한 형태라고 볼 수 있다(좀 더 정확히 말하면, 동적 페이지를 열람하는 것이다).
반면, 정적자원(Static Resource)이란, 프로그램 실행시 변경되는 정보가 거의 없는 자원이다. 웹사이트에 고정적으로 박혀 있는 로고(이미지), HTML 소스 등이 해당된다.
※ 문제상황
Springboot + Java의 개인 프로젝트를 진행하면서, JSP 소스에서 프로젝트 폴더 내의 js, css의 정적 파일을 호출해야 할 상황이 생겼다. 그래서 script, link 태그를 이용해서 호출을 시도해보았다.
<script src="/js/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="/css/bootstrap.min.css">
프로젝트 폴더의 구조는 아래와 같이 css, img, js와 같은 정적자원을 resource/static/css, img, js 폴더에 각각 넣어놓은 상태.
그런데 프로그램을 빌드하니 404 에러만 발생하고, 이미지와 js 파일은 호출되지 않는 깨진 화면만 나타났다.
원인은 spring mvc config에서 정적 자원에 대한 위치를 별도로 지정하지 않았기 때문이었다(처음엔 내 마음대로 입력한 경로를 자동으로 찾아주겠거니 했다. 그런데 그게 가능이나 한 일인가?ㅎㅎㅎ 컴퓨터는 똑똑한 바보다. 정말 하라는 대로만 한다. 거두절미하고, spring이 이미 지정해놓은 정적자원의 탐색 위치가 어디인지에 대해 미리 알아보지 않고 프로젝트를 진행했다는 점은 반성해야 할 부분이다).
이는 ResourceHandlerRegistry 클래스의 addResourceHandler 메소드를 사용하니 간단하게 해결되었다. 해당 메소드가 정의된 클래스에도 설명이 잘 나와있다.
Resourece Handler를 추가하여 정적자원에 대한 접근을 허용하는데, URL 패턴은 "/static/**" 혹은 "/css/{filename:\\w+\\.css}" 형태가 지원된다.
/**
* Add a resource handler to serve static resources. The handler is invoked
* for requests that match one of the specified URL path patterns.
* <p>Patterns such as {@code "/static/**"} or {@code "/css/{filename:\\w+\\.css}"}
* are supported.
* <p>For pattern syntax see {@link PathPattern} when parsed patterns
* are {@link PathMatchConfigurer#setPatternParser enabled} or
* {@link AntPathMatcher} otherwise. The syntax is largely the same with
* {@link PathPattern} more tailored for web usage and more efficient.
*/
public ResourceHandlerRegistration addResourceHandler(String... pathPatterns) {
ResourceHandlerRegistration registration = new ResourceHandlerRegistration(pathPatterns);
this.registrations.add(registration);
return registration;
}
먼저, spring mvc에 대한 설정을 정의하는 'MvcConfiguration'이라는 클래스를 하나 정의하였고, WebMvcConfigurer 인터페이스를 구현받았다. 'MvcConfiguration' 클래스명은 임의로 정한 것이지만, 상단의 @Configuration, @EnableWebMvc 어노테이션을 추가하여 이 클래스가 spring mvc의 설정 class임을 명시해줘야 한다는 점이 중요하다.
@Configuration
@EnableWebMvc
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
}
여기까지 메소드를 클래스와 메소드를 정의했으면, '어떤 방식으로 호출했을 때, 어떤 디렉토리(폴더)로 매핑해줄지?'를 적어주면 된다. 이 때, ResourceHandlerRegistry 클래스를 메소드의 매개변수로 정의하여 하나하나씩 지정해준다. 방식은 아래 소스코드의 주석 참고.
@Configuration
@EnableWebMvc
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/* '/js/**'로 호출하는 자원은 '/static/js/' 폴더 아래에서 찾는다. */
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/").setCachePeriod(60 * 60 * 24 * 365);
/* '/css/**'로 호출하는 자원은 '/static/css/' 폴더 아래에서 찾는다. */
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/").setCachePeriod(60 * 60 * 24 * 365);
/* '/img/**'로 호출하는 자원은 '/static/img/' 폴더 아래에서 찾는다. */
registry.addResourceHandler("/img/**").addResourceLocations("classpath:/static/img/").setCachePeriod(60 * 60 * 24 * 365);
/* '/font/**'로 호출하는 자원은 '/static/font/' 폴더 아래에서 찾는다. */
registry.addResourceHandler("/font/**").addResourceLocations("classpath:/static/font/").setCachePeriod(60 * 60 * 24 * 365);
}
}
위의 주석을 아래 그림과 같이 이해하면 편하다.
결국, jsp, js 등에서의 정적자원을 아래와 같이 호출했을 때, 지정한 'bootstrap.min.js'이라는 파일이 /static/js/ 폴더의 아래에 있다는 것을 알려주기 위해 'ResourceHandlerRegistry' 클래스를 사용한다는 것.
<script src="/js/bootstrap.min.js"></script>
2주동안 삽질했던 것이 드디어 해결되었다.
지금까지 spring boot, 정적자원에 대한 접근 설정 포스팅을 마친다.
감사합니다.