[SpringBoot] 정적자원관리(Resource handle)

Static Resource handle in Spring boot

Posted by JongMin-Lee on March 13, 2020

정적 자원(Static Resource)

정적자원이란 html, css, image, javascript와 같이 컴파일이 필요없는 파일들을 말합니다.
스프링 부트에서 Web MVC 설정을 담당하는 WebMvcAutoConfiguration 클래스는 기본 설정으로 웹 리소스 폴더에서 정적 자원들을 찾습니다. 이 때 템플릿 엔진(Thymeleaf, Groovy, FreeMarker)등을 추가하면 src/main/resources/templates 경로를 기본 경로로 인식하게 됩니다.
간단하게 확인해 보겠습니다. 의존성은 웹을 실행시키기 위해서 spring-boot-starter-web만을 추가 하였습니다.
src/main/static/image경로에 git_img.png 이미지 파일을 추가 했습니다. 이제 프로젝트를 실행시키고 이미지를 불러와 보겠습니다. 이미지가 화면에 잘 나오는 것을 보실 수 있습니다.
여기서 중요한 것은 경로 입니다. localhost:8081/image/git_img.png로써 이미지를 불러옵니다.(8081포트는 제가 application.properties에서 따로 변경한 것 입니다.) 다음과 같은 경로로 불러오는 이유에 대해 알아보겠습니다. 어떻게 /resources/static/image에 있는 이미지를 image부터 가져 올 수 있는지 확인하려면 WebMvcAutoConfiguration을 참고 해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
    } else {
        Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
        CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
        if (!registry.hasMappingForPattern("/webjars/**")) {
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

        String staticPathPattern = this.mvcProperties.getStaticPathPattern();
        if (!registry.hasMappingForPattern(staticPathPattern)) {
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

    }
}

위의 메소드는 WebMvcAutoConfiguraion의 한 부분인 addResourceHandlers입니다. 아마 정적 파일 설정을 해보신 분들에게는 익숙한 메소드이실 겁니다. 정적파일의 위치를 등록시켜주는 메소드입니다. 여기서 참조하고 있는 것 중에 resourceProperties가 있습니다.

1
2
3
4
public class ResourceProperties {
    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = 
    new String[]{"classpath:/META-INF/resources/", 
    "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

ResourceProperties를 확인해 보시면 첫 줄에 CLASSPATH_RESOURCE_LOCATIONS가 선언되어 있습니다. 여기서 /resources//static/ 이 선언되어 있는 걸 볼 수 있습니다. 때문에 /static/ 까지는 Default로 정적파일경로를 인지 할 수 있다는 것 입니다. 따라서 우리는 /static 하위인 /image부터 시작하여 정적파일에 접근할 수 있습니다.
그렇다면 스프링 부트의 기본 설정 대신 다른 경로를 추가하기 위해서는 어떻게 해야 할까요? WebMvcConfigurerAdapter을 이용하여 설정해 보겠습니다.

resources폴더 밑에 testImage 폴더를 추가하고, 이미지를 추가해보겠습니다. 그 다음 WebMvcConfig클래스에서 설정을 해보겠습니다.

1
2
3
4
5
6
7
8
9
10
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/testImage/**")
                .addResourceLocations("classpath:/testImage/", "/testImage/");
    }

}

WebMvcConfigurer를 implements하여 addResourceHandlers 메소드를 재정의 했습니다. (위에서는 WebMvcConfigereAdapter를 이용한다고 했지만 변경했습니다. 뒤에서 다뤄보겠습니다.)
먼저 addResourceHandlerhttp://localhost:8081/testImage 와 같이 호출경로로 사용될 URI값을 입력합니다. addResourceLocations는 실제 파일이 위치할 경로를 입력합니다.
위의 결과로 확인해 보실수 있습니다.

위에서 말한 WebMvcConfigurerAdapter에 대해서 보겠습니다.

1
2
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

기존에 설정을 위해서 위와 같이 선언 했습니다. 그런데 WebMvcConfigurerAdapter deprecated 라는 경고가 뜨는 경우가 있습니다. 이것은 Spring5, Spring Boot2 버전에서 발생하는 경고 입니다. 기존의 WebMvcConfigurerAdapter클래스는 WebMvcConfigurer를 implements한 추상 클래스 였습니다. 또한 상속된 모든 메서드는 비어있는 메서드 입니다. 그러나 Java8이 추가되고 interface에서 default methods에 대한 개념이 추가 되었습니다. 이로 인해 Spring team에서 위의 버전부터 이것을 반영한 것 입니다.
결과적으로 WebMvcConfigurer 인터페이스는 모든 메소드의 default 구현을 포함하고 있기 때문에 Spring5, SpringBoot2 버전부터는 WebMvcConfigurer를 사용하여 웹 설정을 진행해야 합니다.

Default Methods In Java8

기존의 자바에서 인터페이스는 추상메소드만을 가질 수 있었습니다. 때문에 메소드를 만들기 위해서는 인터페이스를 구현하여 사용해야 했습니다. 그러나 Default Methods라는 개념이 추가되어 인터페이스에서 구현클래스에 영향을 끼치지 않고 메소드를 선언할 수 있게 되었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// A simple program to Test Interface default 
// methods in java 
interface TestInterface 
{ 
    // abstract method 
    public void square(int a); 
  
    // default method 
    default void show() 
    { 
      System.out.println("Default Method Executed"); 
    } 
} 
  
class TestClass implements TestInterface 
{ 
    // implementation of square abstract method 
    public void square(int a) 
    { 
        System.out.println(a*a); 
    } 
  
    public static void main(String args[]) 
    { 
        TestClass d = new TestClass(); 
        d.square(4); 
  
        // default method executed 
        d.show(); 
    } 
} 
1
2
3
output :
16
Default Method Executed
참고
  • 스프링 부트로 배우는 자바 웹 개발(저자 : 윤석진)
  • Baeldung(https://www.baeldung.com/web-mvc-configurer-adapter-deprecated)
  • Geeksforgeeks(https://www.geeksforgeeks.org/default-methods-java/)