Spring Boot 개발 환경에서 정적 자원에 대한 접근 설정에 대해 이야기하고자 한다.
웹(WEB) 환경에서 클라이언트와 서버는 서로 자원들을 주고받는데, 이 자원들의 속성은 크게 '동적이냐 정적이냐'에 따라 동적 자원(Dynamic Resource)와 정적 자원(Static Resource)로 구분한다.
※ 동적자원 vs 정적자원
동적자원(Dynamic Resource)이란, 프로그램을 실행하기 위해 자원을 배당할 시 적용되는 기준에 따라 결정되는 배당기법이다. 가령 우리가 구글, 네이버와 같은 포털이나 유튜브에서 실시간으로 업로드되고 변하는 정보(뉴스나 날씨)를 볼 수 있는데, 이런 것들이 모두 동적 자원의 한 형태라고 볼 수 있다(좀 더 정확히 말하면, 동적 페이지를 열람하는 것이다).
실시간으로 올라오는 뉴스들은 모두 현 시간 기준으로 배당되는 동적 자원들이다
반면, 정적자원(Static Resource)이란, 프로그램 실행시 변경되는 정보가 거의 없는 자원이다. 웹사이트에 고정적으로 박혀 있는 로고(이미지), HTML 소스 등이 해당된다.
네이버(NAVER)의 로고(logo)와 검색 버튼 등은 홈페이지 개편등의 작업이 없는 한 거의 변하지 않은 정적자원(그 중 이미지)이다.
※ 문제상황
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);
}
}
위의 주석을 아래 그림과 같이 이해하면 편하다.
정적자원의 패턴을 /js/**로 호출하면, /static/js/ 폴더에서 자원을 찾는다
결국, jsp, js 등에서의 정적자원을 아래와 같이 호출했을 때, 지정한 'bootstrap.min.js'이라는 파일이 /static/js/ 폴더의 아래에 있다는 것을 알려주기 위해 'ResourceHandlerRegistry' 클래스를 사용한다는 것.
<script src="/js/bootstrap.min.js"></script>
2주동안 삽질했던 것이 드디어 해결되었다.
지금까지 spring boot, 정적자원에 대한 접근 설정 포스팅을 마친다.
감사합니다.
아래 블로그는 자바의 클래스 정의나 놓치기 쉬웠던 내용들도 다루는 블로그로 참고하시면 좋을 것 같습니다
출처 : https://tragramming.tistory.com/m/95
'Back-End > Spring' 카테고리의 다른 글
[Spring Security] Spring Boot로 만드는 Spring Security 로그인 구현 - Session(1) (0) | 2023.02.10 |
---|---|
[Spring Boot] custom error page + 사이트 추천 (0) | 2023.02.10 |
[Spring] Spring Boot 시작 및 기초 (0) | 2023.02.10 |
[Spring] JNDI lookup for name [spring.liveBeansView.mbeanDom... (0) | 2023.02.10 |
[Spring Boot] junit 기본 사용법 (0) | 2023.02.10 |