콘텐츠로 건너뛰기

REST API에서 파일 업로드와 JSON 요청 동시 처리하기

  • 테크

REST API(Representational State Transfer)는 클라이언트와 서버 간에 데이터를 주고받기 위한 설계 원칙이자 아키텍처 스타일입니다. REST는 HTTP 프로토콜을 기반으로 동작하며, URI를 사용해 자원을 식별하고 HTTP 메서드를 통해 자원에 접근하거나 조작하는 방식을 제공합니다. REST API는 단순히 데이터를 주고받는 것을 넘어 효율적이고 직관적인 방식으로 다양한 종류의 데이터를 처리할 수 있도록 설계되었습니다.

클라이언트와 서버는 JSON, XML, 파일과 같은 여러 형태의 데이터를 주고받을 수 있으며, 이 중 이미지 파일과 JSON 데이터를 함께 처리해야 하는 경우가 많습니다. 예를 들어, 온라인 쇼핑 플랫폼에서 상품을 등록할 때 상품 이미지와 상품 정보(상품명, 가격, 설명 등)를 함께 전송하는 경우나, 소셜 미디어에서 게시물을 작성 시 사진이나 동영상과 함께 텍스트, 해시태그 등을 JSON 형식으로 함께 전송하는 경우가 있습니다.

이러한 요청을 효율적으로 처리하기 위해 HTTP 요청의 multipart/form-data 형식을 사용합니다. 이번 글에서는 REST API 환경에서 이미지 파일과 JSON 요청을 동시에 처리하는 방법을 단계별로 알아보겠습니다.

클라이언트에서 서버로 파일을 전송하는 과정

이미지 출처: https://medium.com/hackernoon/why-use-a-cdn-here-are-10-data-driven-reasons-ee0a02672988

일반적으로 이미지 파일은 CDN(Content Delivery Network) 환경의 에지 서버에 저장하여 관리합니다. 클라이언트에서 이미지 파일을 업로드하면 서버는 이를 CDN서버에 저장한 후, 클라이언트가 이미지에 접근할 수 있도록 파일 객체의 URL을 생성해 제공합니다. 이 URL은 클라이언트에서 이미지 미리보기, 상품 등록, 사용자 프로필 등과 같은 용도로 활용될 수 있습니다.

웹 브라우저에서 서버로 이미지 파일을 전송하는 방법은 다양합니다.

  • HTML <form> 태그를 이용한 전통적인 업로드 방식
  • 자바스크립트와 XMLHttpRequest, Fetch API를 이용한 Ajax 업로드 방식
  • 웹소켓(WebSocket)을 활용한 실시간 업로드 방식

이 중 Ajax 방식은 비동기 요청을 통해 페이지 리로드 없이 파일을 업로드할 수 있어, 현대적인 웹 애플리케이션에서 널리 사용됩니다. 특히, JSON 데이터와 파일을 동시에 전송해야 하는 경우에도 Ajax(Axios, fetch)는 매우 적합한 방식입니다.

이미지 파일과 JSON 객체를 동시에 전송하는 방법

이미지 파일과 JSON 데이터를 동시에 서버로 전송할 때 주로 사용되는 방식은 multipart/form-data 형식을 사용하는 것입니다. 이 형식은 데이터가 여러 부분으로 나뉘어 하나의 HTTP 요청으로 전송되며, 각각의 부분은 고유한 이름과 데이터 값을 가질 수 있습니다. 파일과 JSON 데이터를 함께 처리해야 하는 경우에 가장 적합하며, 서버는 이를 파싱해 데이터를 처리합니다. 아래 예시에서는 JavaScript, Spring boot를 기반으로 한 구현 방식을 단계별로 소개합니다.

1. HTML 업로드 폼 작성

간단한 HTML 업로드 폼을 작성합니다.

<form id="uploadForm">
    <input type="text" id="contentId" name="contentId" placeholder="Content ID" required />
    <input type="text" id="title" name="title" placeholder="Title" required />
    <input type="text" id="description" name="description" placeholder="Description" required />
    <input type="file" id="creativeFile" name="creativeFile" accept="image/*,video/*" required />
    <button type="button" id="uploadButton">Upload</button>
</form>

2. JavaScript 파일 업로드 처리

입력 폼을 완성했다면, 이벤트 리스너를 통해 사용자가 버튼을 클릭했을 때 데이터를 수집하고 서버로 전송하는 로직을 추가할 수 있습니다. 아래 코드는 입력된 텍스트 데이터와 이미지 파일을 FormData로 묶어 Axios를 사용해 서버에 업로드하는 예제입니다.

⁕ 요청 시 Content-Type ‘application/octet-stream’ is not supported에러를 방지하기 위해 json 데이터는 Blob객체를 통해 Content-Type을 명시해야 합니다. 또한 form-data의 키가 Spring boot의 @RequestPart 어노테이션에 지정된 값과 일치해야 정상적으로 매핑됩니다.

import axios from 'axios';

document.getElementById('uploadButton').addEventListener('click', async () => {
    // 입력 필드와 파일 가져오기
    const contentId = document.getElementById('contentId').value;
    const title = document.getElementById('title').value;
    const description = document.getElementById('description').value;
    const fileInput = document.getElementById('creativeFile');
    const file = fileInput.files[0];

    // JSON 데이터 생성
    const jsonData = {
        contentId: contentId,
        title: title,
        description: description,
    };

// JSON 데이터를 Blob 객체로 변환
    const jsonBlob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });

    // FormData 객체 생성
    const formData = new FormData();
    formData.append('upload_img', file); // 이미지 파일 추가
    formData.append('creative', jsonBlob); // JSON 객체를 문자열로 추가

    try {
        // Axios로 서버에 요청
        const response = await axios.post('/upload', formData);
        console.log('Upload Successful:', response.data);
    } catch (error) {
        console.error('Upload Failed:', error);
    }
});

3. multipart/form-data 요청 확인

multipart/form-data는 요청 데이터를 여러 개의 파트로 나누어 전송하는 방식으로, 각 파트는 고유한 헤더와 데이터를 포함합니다. 요청 본문에서는 Boundary(경계 문자열) 를 사용하여 각 파트를 구분하며, 이를 통해 하나의 요청에서 여러 유형의 데이터를 함께 보낼 수 있습니다. 위에서 자바스크립트로 설정했던 json 객체의 Content-Type이 application/json으로 설정된 것을 확인하실 수 있습니다.

  • Boundary: 각 파트를 구분하는 구분자입니다.
  • Content-Disposition: 데이터의 이름(name)과 파일 이름(filename)을 지정합니다.
  • Content-Type: 데이터의 MIME 타입을 나타냅니다.

4. 서버에서 multipart 요청 받기

이렇게 보낸 요청은 컨트롤러에서 @RequestPart 어노테이션을 사용하여 처리할 수 있습니다. @RequestPart를 사용하면 JSON 데이터와 파일 데이터를 각각 분리해서 받을 수 있으며, JSON은 문자열로, 파일은 multipart 객체로 수신할 수 있습니다. 이를 통해 서버에서 JSON 데이터를 파싱하고, 업로드된 파일을 저장하거나 처리하는 로직을 간단히 구현할 수 있습니다.

@RestController
@RequestMapping("/upload")
public class UploadController {

    @PostMapping
    public String uploadData(
            @RequestPart("creative") String creativeJson, // JSON 데이터 수신
            @RequestPart("upload_img") MultipartFile uploadImg // 이미지 파일 수신
    ) {
        try {
            // JSON 데이터 출력
            System.out.println("Received JSON: " + creativeJson);

            // 파일 정보 출력
            System.out.println("Received File Name: " + uploadImg.getOriginalFilename());
            System.out.println("File Size: " + uploadImg.getSize());
            return "Upload Successful";
        } catch (Exception e) {
            e.printStackTrace();
            return "Upload Failed: " + e.getMessage();
        }
    }
}

Reference:
https://developer.mozilla.org/ko/docs/Web/API/FormData
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestPart.html

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다