입력 글자수 제한, 이미지 파일 업로드

김재현·2023년 12월 15일
0

TIL

목록 보기
60/88

오늘은 게시판 만들기 복습 과제를 진행하면서 새롭게 알게된 내용들을 정리해본다.

입력 글자수

게시물 작성에서 입력글자수 제한에 대한 요구사항이 있었다.
제목은 500자, 내용은 5000자 제한이었다.
처음에는 RequestDto에 max =500으로 작성하면 끝일 줄 알았다.

처음에 작성한 코드

@Getter
public class PostRequestDto {

    @Size(max = 500, message = "제목 500자 이하")
    private String title;
    @Size(max = 5000, message = "작성 내용 5000자 이하")
    private String content;
}

하지만 제목에 500자 이상을 작성했을 때 무려 status 500이라는 서버 에러를 만들어냈다.

응답 내용

글자수 제한을 넘었으면 status 400이어야하는게 아닌가!? 라고 생각했다.
그리고 잘 작성될 것이라 생각하며 400글자도 작성해봤다. 하지만 포스트맨은 예상과 달리 똑같이 500 서버에러를 띄웠다.

400자에 대한 에러코드를 확인해봤을 때는 다음과 같았다.

Console

쿼리문은 내가 작성한 코드와 같이,

  1. 현재 작성하려는 사용자가 가진 토큰이 유효한지 검증
  2. 검증이 끝났으므로 Post를 insert

로 잘 나타났다.

하지만 DB에는 저장이 되지 않는 문제였다. 그렇다면 DB와의 뭔가 문제가 있다는 얘기인데, 그 밑의 오류 내용을 확인해보았다.

SqlExceptionHelper   : SQL Error: 1406, SQLState: 22001
SqlExceptionHelper   : Data truncation: Data too long for column 'title' at row 1

SqlExceptionHelper가 발생한 입력한 title이 너무 길다는 메세지..!
500자 이하로 넣어줬는데 왜 여기서 너무 크다는걸까? 라고 생각하며 SQL Error: 1406, SQLState: 22001를 검색해봤다.

그랬더니 "해당 컬럼에 정의된 최대 길이를 초과"에 대한 내용이 나왔다.

DB의 컬럼에는 생성 될 때 크기가 정해져있고, 명시하지 않을 시 default값인 255로 고정되어 있었던 것이다! 평소 알아차릴 수 없어 내가 간과하고 있던 사실이었다.

따라서 Post 엔터티의 속성에 다음과 같이 크기에 대한 옵션을 넣어주니 오류가 해결되었다

Post 엔터티 수정

    @Column(length = 500)
    private String title;

    @Column(length = 5000)
    private String content;

이번 오류를 해결하면서 느낀 것은 '이래서 테스트코드가 꼭 필요하겠구나..!' 였다.
내가 간과한 부분을 테스트를 통해 잡아내자!


이미지 파일 업로드

요구사항에 이미지 파일 업로드가 있었다.

파일..? 그동안은 Json형태로 데이터를 주고받았기에 파일 업로드는 생소했다.
이미지 파일은 어떻게 전송해야하는 것이지?, 이미지는 어떤 형태로 저장되는 것이지? 등 알고 있던 것이 아무것도 없었기 때문에 폭풍 검색을 진행하며 해결했다.

multipart/form-data

우선 이미지 전송에는 multipart/form-data 라는 방법이 주로 사용된다고 했다.
이것은 웹 폼에서 파일 업로드를 지원하기 위한 데이터 전송 방식 중 하나로, HTML 폼 요소에서 사용자가 파일을 선택하고 제출할 때 주로 사용된다.

특징으로는 폼 데이터를 여러 부분으로 분할하여 전송하며, 각 부분은 다른 종류의 데이터를 포함 할 수 있다는 것이다.
이 각 부분이란 의미는 폼에서 여러 필드와 파일을 업로드할 때 (헤더-데이터로 이루어진) 각 필드 또는 파일은 개별적인 부분으로 표현된다는 것이다.

Postman의 form-data 화면

파일이 담기는 것을 볼 수 있다!

MultipartFile

그럼 이 요청을 받아올 때는 어떻게 해야하지?

Java에서 파일 업로드를 처리하기 위한 인터페이스로 MultipartFile이 있으며, @RequestParam("file") MultipartFile file 의 형태로 받아올 수 있다고 한다.

근데 @RequestParam이라니 굉장히 익숙한데!? url에 담겨있는 정보를 가져올 때 사용하던 것이 아닌가!
하지만 포스트맨의 url을 확인해봤을 때 'Params'에 작성하는 것과는 달리 'Body'에 있는 'form-data' 라는 곳에 작성했다. 이게 어떻게 된 것 일까?

@RequestParam("file") MultipartFile file을 사용하는 경우에는 파일 데이터가 URL에 직접적으로 포함되지 않습니다.
대신, 클라이언트에서 multipart/form-data를 사용하여 HTTP 요청을 보낼 때, 파일 데이터는 요청 본문(body)에 담겨서 전송됩니다.
이렇게 하면 파일이 URL에 직접적으로 노출되지 않고, 요청 본문에 포함되어 서버로 전송됩니다.

url에 직접 전송되지 않지만 @RequestParam으로 받는 방법도 있었다니! 새롭다.

이 부분을 채택하고, form-data은 '각 부분' 이라고 했으므로 이전처럼 Dto 형태로 Json데이터를 보내는게 불가능하기 때문에 다음과 같이 title과 content를 따로 명시해서 받아줬다.

PostController

    @PostMapping("/image")
    public ResponseEntity<CommonResponseDto> createPostWithImage(
            @RequestParam("image")MultipartFile imageFile,
            @RequestParam("title") String title,
            @RequestParam("content") String content,
            @CookieValue(JwtUtil.AUTHORIZATION_HEADER) String value) {

        postService.createPostWithImage(imageFile,title, content, value);
        return ResponseEntity.ok().body(
                new CommonResponseDto("게시되었습니다.", HttpStatus.CREATED.value())
        );
    }

이진데이터, byte[]

그렇다면 받아온 image file은 어떻게 처리해야할까?

이미지는 이미지는 이진 데이터로 구성되어 있다.
'byte[]' 는 이진 데이터를 표현하기에 적합한 자료형이다.

따라서 받아온 image를 byte[]로 변환하는 메서드를 작성하고, 변환된 byte[] 형태의 이미지 데이터를 저장했다.

Service (이미지 변환 메서드)

    private byte[] imageToByteArray(MultipartFile imageFile) {
        try {
            return imageFile.getBytes();
        } catch (IOException e) {
            throw new RuntimeException("이미지 변환 실패.");
        }
    }

Service (save)

						...
                    
        byte[] imageByte = imageToByteArray(imageFile);
        Post post = new Post(title, content,imageByte,user);

        postRepository.save(post);
        				...

@Lob

검색하면서 알게 된 내용인데,

@Lob은 Large Object를 나타내는 JPA 어노테이션이다.
Large Object란 데이터베이스에 큰 크기의 데이터를 저장하기 위한 데이터 타입을 의미하며, 주로 문자열이나 이진 데이터를 나타내는 필드에 사용된다.

그리고 더불어 @ColumncolumnDefinition = "LONGBLOB" 를 사용했다.

LONGBLOB 옵션은 MySQL 데이터베이스에서 사용되는 데이터 타입으로, 이진 데이터를 저장하는 데 사용된다. 이 데이터 타입은 BLOB (Binary Large Object) 중에서도 매우 큰 크기의 이진 데이터를 저장할 때 사용.

이 둘을 적용하여 아래와 같이 엔터티의 컬럼을 작성해줬다.

Post 엔터티

							...
    @Lob
    @Column(name = "image", columnDefinition = "LONGBLOB")
    private byte[] image;
    						...

결과

결과는 성공적이었다!!
DB에는 이런식으로 저장이 되는구나~

다만 주의할 점은, 대부분의 경우 이미지 파일은 파일 시스템에 저장하고 데이터베이스에는 파일의 경로나 URL을 저장하는 것이 효율적일 수 있는 것이다.
LOB 형태로 저장하는 경우 성능에 영향을 줄 수 있기 때문이다.

이번 요구사항에는 그러한 내용이 없었기에 따로 파일 시스템을 두진 않았으나, 다음에는 도전해봐야겠다.

profile
I live in Seoul, Korea, Handsome

0개의 댓글