백엔드 개발을 진행하다 보면, 이미지나 파일 같은 raw data 를 직접 return 해야 하는 경우가 발생합니다. 물론 download 기능을 구현할 때에도 마찬가지겠지만, 그 이외에도 이러한 기능이 필요한 경우가 간혹 있습니다.

이 post에는 sample 이 존재하지 않습니다. 필요 시 나중에 추가하도록 하겠습니다.

dependency 추가

API 를 작성할 것이므로 아래 dependency 를 추가합니다. 아마 대부분의 API서버에 이미 추가되어 있을 것입니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
implementation("org.springframework.boot:spring-boot-starter-web")

@ResponseBody 사용하기

가장 간단히 사용할 수 있는 것은 @ResponseBody annotation 을 추가하는 것입니다.

@GetMapping("/get-text")
public @ResponseBody String getText() {
    return "Hello world";
}

이렇게 하면, view 를 찾는 것 대신에 Hello World 라는 문자열 자체를 return 하게 됩니다.

@Controller 대신 @RestController를 사용하는 Controller 를 구현하게 되면, 위 @ResponseBody 가 없어도 view를 찾지 않고 그대로 문자열을 return 합니다.

이미지 return 을 위한 produces 사용하기

이미지나 파일을 return 하는 데 있어서 byte array 를 return 하는 것이 쓰입니다.

@GetMapping(value = "/image")
public @ResponseBody byte[] getImage() throws IOException {
    InputStream in = getClass()
      .getResourceAsStream("/com/baeldung/produceimage/image.jpg");
    return IOUtils.toByteArray(in);
}

이렇게 하면 이미지에 대한 정보가 아무것도 없기 때문에 client 에서는 이미지임을 알 수 없습니다. 이러한 문제를 방지하기 위해서, @GetMapping annotation에 produces 값을 넣어줍니다.

@GetMapping(
  value = "/get-image-with-media-type",
  produces = MediaType.IMAGE_JPEG_VALUE
)
public @ResponseBody byte[] getImageWithMediaType() throws IOException {
    InputStream in = getClass()
      .getResourceAsStream("/com/baeldung/produceimage/image.jpg");
    return IOUtils.toByteArray(in);
}

이렇게 produces 값에 MediaType.IMAGE_JPEG_VALUE를 지정하면 return 되는 값을 이미지로 처리해야 함을 알려줄 수 있습니다.

이제, 브라우저는 이 return 값이 이미지임을 알고 이미지로 올바르게 처리해 줍니다.

Raw file 을 return 하기 위한 produces

produces 는 여러가지 값을 가질 수 있습니다. 전체 목록은 여기에 있습니다. return 하고자 하는 타입에 맞게 적절한 값을 넣어주면 됩니다.

그래서, 만약 우리가 파일 자체를 return 하고 싶다면 APPLICATION_OCTET_STREAM_VALUE 를 사용합면 됩니다.

@GetMapping(
  value = "/get-file",
  produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
)
public @ResponseBody byte[] getFile() throws IOException {
    InputStream in = getClass()
      .getResourceAsStream("/com/baeldung/produceimage/data.txt");
    return IOUtils.toByteArray(in);
}

그렇지 않다면, Byte array 대신에 ByteArrayResource 를 사용해도 됩니다.

@GetMapping(
  value = "/get-file-via-byte-array-resource", 
  produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
)
public @ResponseBody Resource getFileViaByteArrayResource() throws IOException, URISyntaxException {
    Path path = Paths.get(getClass().getResource("/com/baeldung/produceimage/data.txt").toURI());
    ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
    return resource;
}

Resource 클래스는 Spring이 제공하는 높은 수준의 abstract class입니다. 이 클래스는 다양한 소스로부터 리소스를 다루는 데 도움을 줍니다. InputStream 을 통한 Resource의 사용은 다음과 같은 이점이 있습니다.

  • 리소스에 대한 추가적인 메타데이터를 제공한다.
  • Spring 의 다른 abstract 들과 유연하게 연결지을 수 있다.
  • mock 하기 쉽다.

참고자료 및 출처


Leave a comment