Framework/Spring

[Spring] Resource는 어떻게 처리할까?

KOOCCI 2022. 8. 11. 01:36

목표 : 스프링에서 Resource 처리를 구현할 수 있다.


Resource 

IoC Container에 대한 챕터를 마무리하고, Resource 챕터를 확인해보자.

먼저 Resource가 무엇인지 이해가 필요할 것 같다.

resources

intellij로 스프링 프로젝트를 시작하면, resources 폴더가 생기게 된다.

Spring에서 java 뿐 아니라, 외부의 파일들을 읽어야 하는 경우가 생긴다.

(앞서 배웠던 properties도 외부 파일 중 하나다.)

이런 외부 파일들을 읽어드리거나, URL을 통해 가져와서 프로그래밍에 활용을 하는데, Spring에서는 Resource라는 Interface를 통해, 추상화를 해주었다.

 

Spring이 없으면, 어떻게 할 수 있었을까?

public static void main(String[] args) throws IOException {
    String path = "C:\\Users\\lazye\\Desktop\\dev\\src\\main\\resources\\application-dev.properties";
    File file = new File(path);
    InputStream is = new FileInputStream(file);
    byte[] bytes = is.readAllBytes();
    String properties = new String(bytes);
    System.out.println(properties);
}

간단히 파일을 읽어보았다.

이렇게 특정 파일을 읽어오고 싶을 때는 위와 같이 했고, 다만 절대 경로를 알 수 없는 경우가 많으니, classPath를 설정해주는 경우가 많았다.

public static void main(String[] args) throws IOException {
    InputStream is = Main.class.getClassLoader().getResourceAsStream("application-dev.properties");
    byte[] bytes = is.readAllBytes();
    String properties = new String(bytes);
    System.out.println(properties);
}

 

Resource Interface

그럼 이제, Resource Interface를 보도록 하자.

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isReadable();

    boolean isOpen();

    boolean isFile();

    URL getURL() throws IOException;

    URI getURI() throws IOException;

    File getFile() throws IOException;

    ReadableByteChannel readableChannel() throws IOException;

    long contentLength() throws IOException;

    long lastModified() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();
}

위에서 보듯, InputStreamSource를 상속받고 있고, 이 Interface는 간단히, getInputStream을 가져올 수 있도록 되어 있다.

즉, 위에서 해본 것처럼 Byte 배열을 가져올 수 있다.

public interface InputStreamSource {

    InputStream getInputStream() throws IOException;
}

 

Built-in Resource Implementations

다만, 우리는 Resource의 구현체를 주로 사용한다.

Spring에서 제공하는 구현체가 있고, 관례에 따라(추후 확인할 Resource Loader에서 사용하는) 이를 가져와 사용한다.

위 구현체 중, 자주 사용하는 2가지만 알아보고 상황에 따라, 다른 구현체도 사용해볼 수 있도록 하자.

 

UrlResource

file:
 for accessing filesystem paths, 
https:
 for accessing resources through the HTTPS protocol, 
ftp:
 for accessing resources through FTP

위와 같이, file:, https:, ftp: 같이 프로토콜을 명시하고, 해당 리소스의 위치를 URL 방식으로 알려주는 형태다.

 

ClassPathResource

앞서, ClassLoader를 통해, properties를 읽었듯이, ClassPath를 통해 리소스 위치를 찾는 방식이다.

 

ResourceLoader

실제로는 구현체의 사용보다, ResourceLoader를 통해 리소스를 가져오는 경우가 많다.

public interface ResourceLoader {

    Resource getResource(String location);

    ClassLoader getClassLoader();
}

위와 같이, ResourceLoader는 Interface다.

그리고, 우리가 사용하는 ApplicationContext의 상속구조를 타고 올라가보면 다음과 같다.

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver
// ResourcePatternResolver Interface를 타고 들어간다.

public interface ResourcePatternResolver extends ResourceLoader

즉, Spring에서 사용하는 ApplicationContext 구현체는 ResourceLoader를 상속받아 구현하고 있다.

따라서, 다음과 같이 사용할 수 있다.

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");

ResourceLoaderAware

Aware라는 postfix가 붙은 Interface의 공통점은, 앞선 포스팅에서 알아봤듯이, ResourceLoader를 set하고, get하여 사용하는 형태로 Bean에서 사용한다.

 

Wrap up

이렇게 Resource를 사용하는 방법까지 알아보았다.

이제 validation과 AOP를 알아본 후에, Core 부분을 마무리하도록 하자.