기타

이미지 전송

infobox503 2025. 2. 7. 13:55

1. 요약

  • 목적
    • 이미지 전송 원리 이해
  • 동기
    • 현재 제작하고 있는 프로젝트의 더 깊은 이해
      • 현 프로젝트에서는 이미지를 GCS에 저장하여 사용하고 있음
      • 해당 이미지 전송에 대한 이해도가 부족하다고 생각되어, 이미지 전송 과정을 공부하고자 함
  • 과정
    • FE → BE로 이미지 전송하는 과정을 그리기

2. FE

  • 요약
    • 통신 방법
      • XML
      • XHL
      • XMLHttpRequest
      • AJAX
    • 데이터 형식
      • MIME-TYPE
      • Content-Type
      • multipart/*
      • multipart/form-data
  • XML
    • 데이터 표기 방법 중 하나
      • 데이터 전송 시, xml 형식으로 데이터 정보 기술 가능
    • 예시
<book>
	<title>Harry Potter</title>
	<author>J.K. Rowling</author>
	<year>1997</year>
</book>

 

 

  • XHL
    • 비동기 통신 방법
    • XML, JSON 등의 데이터를 주고받게 됨
    • 이점
      • 페이지 새로고침 없이 최신 정보로 업데이트

※ 동기 / 비동기 통신

  • 동기
    • 상대방의 응답이 올때가지 일을 하지 않음
    • 예시
      • 상대방에게 요청 보냄 → 상대방에게 응답 받음 → 다음 일 수행
  • 비동기
    • 상대방의 응답을 기다리지 않고 다음 작업 수행
    • 목적
      • 일부 페이지만 리렌더링 시키는 것
      • 전체 페이지 새로고침 없이 일부만 비동기적으로 처리해서 페이지를 사용하는 걸 목적으로 함
    • 예시
      • 상대방에게 요청 보냄 → 다음 일 수행 → 상대방에게 응답 받음
  • XMLHttpRequest
    • XHL 형식의 통신을 가능하게 해주는 객체
    • 해당 객체로 통신 시, 상대방의 응답을 기다리지 않고 내 작업을 계속 수행
    • 예시
      • XHR : GET 요청 보내기
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);  // true -> 비동기 요청

xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
        // 서버 응답을 처리
        console.log(xhr.responseText);
    }
};

xhr.send();

//send() 이후, 응답을 기다리지 않고 다음 작업 수행

 

 

  • AJAX
    • XHL을 이용해서 비동기 통신을 수행함
      • XMLHttpRequest로 XHL을 사용함
    • 이점
      • 웹페이지 동적 업데이트
        • XHL만 이용해서는 새로고침 없이 웹페이지를 동적으로 업데이트하기에는 난이도가 있음
        • AJAX는 XHL을 편리하게 사용할 수 있도록 구성함으로써, 동적 업데이트를 쉽게 구현 가능
  • MIME-TYPE
    • 요약
      • 인터넷 통신에서 데이터 타입을 정하는 표준
        • (인터넷 통신 : HTTP 통신, 이메일 통신, …)
    • 예시
      • 웹 페이지: text/html
      • 이미지 파일: image/png, image/jpeg
      • JSON 데이터: application/json
      • PDF 파일: application/pdf
  • Content-Type
    • 요약
      • HTTP 통신에서 Request Body 데이터에 대한 MIME-type 지정
    • 설명
      • POST 요청 시, Request Body에 요청 데이터를 첨부한다.
      • 요청을 받는 입장에서는 Content-Type을 통해 요청 데이터의 형식을 이해할 수 있다.
    • 예시
      • XHL
        • HTTP Method : POST
        • Content-Type : JSON
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://example.com/upload', true);

// 요청 헤더에 MIME-TYPE 설정
xhr.setRequestHeader('Content-Type', 'application/json');  // JSON 데이터 전송

// 요청 보내기
var data = JSON.stringify({ name: 'John', age: 30 });
xhr.send(data);

 

 

  • multipart/*
    • 요약
      • 여러 타입의 데이터를 한 번에 보내기 위한 Content-Type
    • 설명
      • image, text 등의 한정된 Content-Type으로는 Form Request를 보내기 어렵다.
      • 예를 들어, 사진 및 설명 글을 백엔드에 요청 시, 여러 번 나누어서 Request를 보내야 한다.
      • 그때, multipart는 여러 타입의 데이터를 한 번에 보낼 수 있다.
      • multipart는 boundary라는 문자열로 각 타입의 데이터를 분리함
    • 종류
      • multipart/form-data
        • 텍스트 및 파일 동시 전송
        • 각 타입의 데이터들은 name 필드로 구분됨
          • HTML Form 데이터 전송할 때 좋음
        • 예시
          • 받는 입장에서는 name을 통해 각 데이터를 가져오는게 가능함
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----Boundary123

------Boundary
Content-Disposition: form-data; name="username"

john_doe
------Boundary
Content-Disposition: form-data; name="file"; filename="example.jpg"
Content-Type: image/jpeg

(binary data)
------Boundary--

 

 

  • multipart/alternative
    • 동일한 내용을 서로 다른 타입으로 보냄
    • 응답을 받는 입장에서는 자기가 원하는 타입으로 골라 받음
  • multipart/mixed
    • 텍스트 및 파일 동시 전송
    • form-data처럼 name 필드는 없음
      • name 필드 구분없이 데이터 보내도 좋을 때 사용함
      • ex) 파일 묶음 전송할때 좋음
    • 예시
Content-Type: multipart/mixed; boundary=----Boundary123

------Boundary
Content-Type: text/plain

This is plain text.
------Boundary
Content-Type: image/jpeg
Content-Disposition: attachment; filename="image.jpg"

(binary data)
------Boundary-

 

 

  • multipart/related
    • 루트 데이터와 연관 데이터로 나뉨
    • 루트 데이터는 연관 데이터들을 가져와서 완성된 루트 데이터를 형성함
      • 예시를 보면, 루트 데이터인 HTML은 연관 데이터인 image 데이터를 가져와서 완성된 HTML을 만듦
    • 예시
Content-Type: multipart/related; boundary="boundary123"; start="<main.html>"; type="text/html"

--boundary123
Content-Type: text/html
Content-ID: <main.html>

<!DOCTYPE html>
<html>
<body>
    <h1>Hello, World!</h1>
    <img src="cid:image1.jpg">
</body>
</html>
--boundary123
Content-Type: image/jpeg
Content-ID: <image1.jpg>

(binary image data)
--boundary123--

 

 

  • multipart/form-data
    • 위 글을 통해 HTTP Form Request에서는 multipart/form-data가 적절한 전송 방식인 것을 확인 할 수 있다.
    • form-data로 Request 요청을 내리기 위한 예시는 아래와 같다.
    • 예시 - FormData 객체 사용
var formData = new FormData();
formData.append("textField", "sample text");
formData.append("fileInput", fileInput);

var xhr = new XMLHttpRequest();
xhr.open("POST", "/upload", true);

//formData를 xhr에 넣으면 자동으로 Content-Type = multipart/form-data
//xhr.setRequestHeader("Content-Type", "multipart/form-data");

xhr.send(formData);

 

 

예시 - HTML Form 사용

<form action="/upload" method="POST" enctype="multipart/form-data">
    <input type="text" id="username" name="username"><br><br>
    <input type="file" id="profile-pic" name="profile_pic"><br><br>
    <input type="submit" value="Submit">
</form>

 

 

3. BE(Spring)

  • 요약
    • 이미지 전송 과정을 확인
    • 설명
      • 프론트로부터 이미지 파일 받기
        • MultipartHttpServletRequest
        • @ModelAttrubite
        • MultiPartFile
      • 이미지 파일 저장
        • Base64
        • Blob
  • MultipartHttpServletRequest
    • Multipart/* 요청을 받기위해서 사용되는 타입
    • 예시
      • multipart/form-data 요청 시, 각 텍스트 및 파일을 받아올 수 있다.
@PostMapping("/item")
public ResponseEntity<String> item(MultipartHttpServletRequest request){

    log.info("request : {}", request);

    return ResponseEntity.ok("ok");
}

 

 

 

  • @ModelAttrubite
    • HTML Form 데이터를 보다 쉽게 받을 수 있는 어노테이션
    • 사용 예시
@PostMapping("/item")
public ResponseEntity<?> item(@ModelAttribute ItemFormRequestDto itemFormRequestDto){
    if(itemService.save(itemFormRequestDto) != null){
        return ResponseEntity.ok("ok");
    }

    return ResponseEntity.badRequest().body("상품 등록에 실패했습니다");
}

 

 

  • MultipartFile
    • 스프링에서 이미지 파일 저장을 위한 객체
    • 구조
      • 파일의 이름, 타입, 크기 등을 담을 수 있음
public interface MultipartFile extends InputStreamSource {
    String getName();

    @Nullable
    String getOriginalFilename();

    @Nullable
    String getContentType();

    boolean isEmpty();

    long getSize();

    byte[] getBytes() throws IOException;

    InputStream getInputStream() throws IOException;

    default Resource getResource() {
        return new MultipartFileResource(this);
    }

    void transferTo(File dest) throws IOException, IllegalStateException;

    default void transferTo(Path dest) throws IOException, IllegalStateException {
        FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
    }
}

 

 

  • 예시
    • FE
      • 이미지 전송
  <form>
      <label>상품 이미지 파일</label>
      <input type="file" name="imageFile"/>
      <button type="submit" className={styles.submitButton}>상품 등록</button>
  </form>

 

 

  • BE
    • 이미지 받기
import org.springframework.web.multipart.MultipartFile;

public class ItemFormRequestDto {
		...
    private MultipartFile imageFile;

}

 

 

  • Base64
    • 각 0, 1의 조합을 글자로 해석하는 것
    • 방법
      • 주어진 바이너리 데이터를 6비트씩 분리
      • 각 6비트의 10진수 값과 매칭되는 문자열을 도출함
      • 인코딩 : 바이너리 데이터 → 문자
      • 디코딩 : 문자 → 바이너리 데이터
    • 예시
      • 000000 = A

 

  • Base64 ↔ HTML
    • HTML에서는 Base64로 인코딩된 값을 디코딩하여 이미지 출력이 가능함
    • 예시
      • 해당 src 값은 Base64로 인코딩된 값
      • HTML에서는 해당 값을 디코딩하여 이미지 출력
	
 <img src="data:image/jpeg;base64,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">

 

 

  • 장점
    • 이미지를 온라인으로 불러올 필요없이 바로 출력
  • 단점
    • HTML에서 데이터 크기가 33% 증가
    • 이유
      • 가정
        • HTML에서 문자 1개는 8비트
        • 바이너리 데이터 24bit가 주어짐
      • HTML에서 바이너리 데이터를 그대로 옮기면 3개의 문자임
        • 8bit 문자 3개
      • 하지만 Base64 인코딩 하면 4개의 문자임
        • Base64는 6개의 비트를 사용함
        • 따라서, 24bit면 4개의 문자를 만들 수 있음
        • HTML에서 4개의 문자로 표현되니 32bit로 표현되버림
      • ⇒ Base64 인코딩 과정을 거치면서 데이터 뻥튀기
  • Blob (Binary Larage object)
    • 요약
      • 바이너리 데이터를 다루기 위한 객체
    • 설명
      • 사용자 정의 바이너리 데이터 저장 객체
      • 각 플랫폼에 따라 바이너리 데이터를 어떻게 다루느냐에 따라 Blob의 기능이 다를 수 있음
    • Blob으로 데이터 저장
      • 바이너리 데이터를 그대로 저장함
      • 따라서, Base64와 같이 데이터 크기가 증가하는 일이 없음
    • 예시
      • GCS
        • Blob
          • 바이너리 데이터를 꺼냄
package com.google.cloud.storage;

public class Blob extends BlobInfo {
		...
    private final StorageOptions options;
    private transient Storage storage;

    public byte[] getContent(BlobSourceOption... options) {
        return this.storage.readAllBytes(this.getBlobId(), Blob.BlobSourceOption.toSourceOptions(this, options));
    }
    ...
}

 

 

  • BlobInfo
    • Blob의 데이터에 대한 정보 표기
package com.google.cloud.storage;

public class BlobInfo implements Serializable {
		...
    private final BlobId blobId;
    private final String generatedId;
    private final String selfLink;
    private final String cacheControl;
    private final List<Acl> acl;
    private final Acl.Entity owner;
    private final Long size;
    ...

 

 

  • 이미지 전송 및 저장
    • MultipartFile (이미지)의 바이너리 데이터를 BlobInfo 및 BlobId를 통해 타입 표기
    • 해당 이미지 바이너리 데이터를 GCS에 저장
public String uploadImage(MultipartFile image){
    if(storage == null){
        log.info("Storage 생성 실패");
        return null;
    }

    String uuid = UUID.randomUUID().toString(); // Gcs 에 저장될 파일 이름
    String ext = image.getContentType(); // 파일의 형식 ex) JPG

    // Gcs 이미지 업로드
    BlobId blobId = BlobId.of(bucketName, uuid);
    BlobInfo blobInfo = BlobInfo.newBuilder(blobId)
            .setContentType(ext)
            .build();

    try (WriteChannel writer = storage.writer(blobInfo)) {
        byte[] imageData = image.getBytes();
        writer.write(ByteBuffer.wrap(imageData));
    } catch (Exception e) {
        log.error("이미지 전송 실패");
        return null;
    }

    return createURL(uuid);
}

 

 

QnA)

1) 이미지 파일을 JSON 방식으로 보낼 수 있나?

  • 의문
    • FE → BE으로 이미지 파일 전송 시, multipart/form-data의 content-type으로 데이터를 종합적으로 전송한다.
    • 그렇다면, 이미지 파일을 JSON 타입으로 보낼 수 없을까?
  • 결론
    • 이미지를 바이너리 데이터 형식으로 JSON 전송을 할 수 없다
    • 이미지를 Base64 인코딩된 문자열 형식으로 JSON 전송을 할 수 있다
  • 이미지를 바이너리 데이터 형식으로 JSON 전송을 할 수 없다
    • 요약
      • JSON은 텍스트 기반이므로, 바이너리 데이터 형식으로 데이터 전송 불가
    • 설명
      • JSON 데이터를 읽는 데는 4가지 타입을 지원
        • 문자열, 숫자, bool, 객체
      • 바이너리 데이터 형식대로 데이터를 보내도, 읽을 수 있는 타입이 없음
        • 따라서 바이너리 데이터 형식으로 데이터 전송 불가
  • 이미지를 Base64 인코딩된 문자열 형식으로 JSON 전송을 할 수 있다
    • 요약
      • Base64로 인코딩된 값은 문자이므로, JSON으로 전송 가능
    • 예시
      • FE
        • image → Base64 인코딩
        • Json 형태로 인코딩된 image 값 전달
import React, {useState} from 'react';
import styles from '../css/login.module.css'
import Cookies from 'js-cookie';
import {Link} from "react-router-dom";


const Image= () => {
    return (
        <div>
            <ImageForm/>
        </div>
    );
};

const ImageForm= () => {
    const BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:8080';
    const TEST_ENDPOINT = '/test';

    const [base64Image, setBase64Image] = useState("");

		// image -> Base64 인코딩
    const handleImageChange = (event) => {
        const file = event.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = (e) => {
                setBase64Image(e.target.result);
            };
            reader.readAsDataURL(file);
        }
    };

    const handleSubmit = async (e) => {
        e.preventDefault(); // 기본 폼 제출 방지

        try {
            console.log("image : ", base64Image )
            // POST 요청 보내기
            const response = await fetch(`${BASE_URL}${TEST_ENDPOINT}`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ image: base64Image }),
            });

            if (response.ok) {
                alert('조회 성공' + response.text());
            } else {
                alert('조회 실패');
            }
    };
    return (
        <>
            <form onSubmit={handleSubmit}>
                <div className={styles.heading}>로그인</div>

                <input type="file" id="imageInput" accept="image/*" onChange={handleImageChange}/>
                <img id="preview" alt="Preview Image"/>

                <input type="submit" value="제출" className={styles['submit-button']}/>

            </form>
        </>
    );
};

export default Image;

//image 예시
//image :  data:image/jpeg;base64,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

 

 

  • BE
    • String으로 인코딩된 image값 전달 받기
@Slf4j
@Controller
public class TestController {

    @PostMapping("/test")
    public ResponseEntity<?> test(@RequestBody Dto dto){
        log.info("dto : {}", dto);

        return ResponseEntity.ok("ok");
    }

    @Data
    static class Dto{
        String image;
    }

}

//dto 출력 예시
//dto : TestController.Dto(image=data:image/jpeg;base64,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)