Home Binary 데이터 변환과 데이터 유실
Post
Cancel

Binary 데이터 변환과 데이터 유실

1. 글을 작성하게 된 계기


사람들과 스프링 서버를 만들어보는 프로젝트를 진행하며, 이미지 파일이 깨져서 나오는 문제가 발생했습니다. 이를 해결하는 과정에서 왜 이런 일이 발생하는지, 이를 어떻게 해결했는지에 대해 정리하고 싶어 해당 글을 작성하게 되었습니다.

  1. 왜 이미지를 문자열로 바꿔서 응답하면 데이터가 깨질까?
  2. 어떻게 해결해야 할까?







2. 데이터 손실


이미지가 나오지 않는 이유는 데이터가 손실됐기 때문인데, 이에 대해 먼저 살펴보겠습니다. 이미지, 음악, 영화와 같은 바이너리 파일을 텍스트로 전송하면 데이터가 손실될 수 있는데, 이는 문자 집합만으로 바이너리 데이터를 모두 표현할 수 없기 때문입니다.

애초에 웹에서 전송되는 형식(Content-Type)이 구분 돼 있기도 하고요.





예를 들어, ASCII를 사용해 바이너리 데이터를 변환하는 경우를 살펴보겠습니다. ASCII 문자 집합은 128개의 문자(7bit)만을 포함하고 있기 때문에, 해당 범위를 넘어가는 데이터를 변환할 때 데이터가 손실됩니다. ASCII 문자 집합만으로는 바이너리 파일에 포함된 다양한 데이터를 표현할 수 없기 때문입니다.

image







바이너리 데이터는 0과 1의 조합으로 구성되어 있으며, 이를 통해 7bit가 넘는 복잡한 데이터를 표현할 수 있습니다. 이는 ASCII의 범위를 초과하므로 올바르게 표현/변환될 수 없는 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@DisplayName("byte 변환 단위 테스트")
class ByteConvertUnitTest {

    @Test
    @DisplayName("ASCII 범위를 넘어가면 문자열이 깨진다.")
    public void base64EncodingEqualsTest() {
        byte[] binaryDataArray = {(byte) 0xe4, (byte) 0xb8, (byte) 0x80};

        String asciiStr = new String(binaryDataArray, US_ASCII);

        String originalData = Base64.getEncoder().encodeToString(binaryDataArray);
        String asciiData = Base64.getEncoder().encodeToString(asciiStr.getBytes(US_ASCII));

        assertNotEquals(originalData, asciiData);
    }
}

image







범위를 초과하는 데이터를 올바르게 변환하기 위해서는 Base64 인코딩, Buffer, Stream, Channel과 같은 다른 방법을 사용해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@DisplayName("byte 변환 단위 테스트")
class ByteConvertUnitTest {

    @Test
    @DisplayName("Base64 인코딩/디코딩을 통해 원본 문자열을 복원할 수 있다.")
    void base64EncodingTest() {
        String str = "한글";
        byte[] byteArray = str.getBytes(UTF_8);
        String base64Encoded = Base64.getEncoder().encodeToString(byteArray);

        byte[] decodedBytes = Base64.getDecoder().decode(base64Encoded);
        String decodedStr = new String(decodedBytes, UTF_8);

        assertEquals(str, decodedStr);
    }
}







정리해 보면 바이너리 데이터(이미지)를 문자열로 변환해 응답 본문에 실었기 때문에 데이터 손상이 발생한 것입니다. 지금까지 이미지 데이터를 문자열 변환해 전송하면 왜 데이터가 손상되는지를 살펴보았습니다. 이제 이를 어떻게 해결했는지 살펴보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StaticView implements View {

    ......

    private void setResponseBody(
        HttpServletResponse response,
        byte[] imageData
    ) {
        // 바이너리 데이터를 문자열로 치환 후 본문에 실음
        response.setBody(new String(imageData));
    }
}







3. 문제 해결


이미지 데이터를 버퍼에 담은 후, 소켓에 데이터를 write 했습니다. 즉, 바이너리 데이터를 문자열로 바꾸는 과정에서 데이터가 손상될 수 있기 때문에 바이너리 데이터를 직접 처리한 것입니다.

1
2
3
4
5
SocketChannel channel = response.getSocketChannel();
ByteBuffer buffer = ByteBuffer.wrap(body);
while (buffer.hasRemaining()) {
    channel.write(buffer);
}

Base64 인코딩 방식을 활용할 수도 있지만 Socket과 Channel을 사용하고 있었기 때문에 해당 방식을 채택했습니다.







이를 통해 화면을 올바르게 렌더링할 수 있었습니다.

image







4. 정리


바이너리 데이터를 문자열로 변환하는 과정에서 데이터가 손상될 수 있습니다. 따라서 이미지, 영상 등과 같은 바이너리 데이터를 전송해야 할 경우, 바이너리 데이터를 직접 전송/처리해야 합니다.


This post is licensed under CC BY 4.0 by the author.

Nginx DNS 캐싱 이슈 해결

URI, URL