회원 가입 시 첨부된 프로필 이미지를 업로드하는 부분이다.
현재는 로컬 pc를 서버로 만들어 사용하고 있기 때문에 로컬 pc에 저장하는 방식을 먼저 적용해보고, S3에 파일을 업로드하도록 리팩토링을 할 예정이다.
하나의 회원은 하나의 프로필 이미지만 가질 수 있기에 1:1 관계로 매핑했다.
MultipartFile(프로필 이미지)
을 필드로 가지는 JoinDto로 요청을 받는다.
문자, 파일 등 해당 dto 필드와 일치하는 정보들을 form-data 요청에 실어 보내면 해당 정보들을 dto 객체로 받는다. (@ModelAttribute가 default로 적용)
@RequestPart 등 multipart를 받아오는 여러 방법이 있지만, 현재 필드의 개수가 그리 많지 않고, 이 방법이 제일 편리하다고 생각되어 택했다.
(@RequestPart
를 사용하면 form-data의 value에 json 형태로 요청을 받을 수도 있다)
Dto는 가입에 필요한 정보들과 MultipartFile 타입 객체인 image를 가지고 있다.
사용자는 프로필 사진을 첨부할 수도 있고, 하지 않을 수도 있다.
(사진을 첨부하지 않을 경우, 프론트에서 기본 이미지로 처리해준다)
이메일이 이미 사용되고 있는지 중복 여부를 체크하고, 비밀번호를 암호화한다.
사용자가 사진을 첨부했을 경우, memberImageService의 saveImage
를 호출해 해당 파일을 저장하고, 해당 member를 영속화시킨다.
(cascade 옵션이 걸려있기 때문에, memberImage도 같이 영속화된다)
파일이 저장되는 경로는 속성 파일에 지정해놓고, @Value로 받아와서 전역적으로 사용한다.
사용자가 업로드한 원본 파일의 이름인 fileOriName
과 서버 내부에 저장할 이름인 fileName
을 추출한다
파일을 저장한 후, 생성된 memberImage 객체를 리턴한다.
지정한 경로에 파일이 잘 저장되었다.
파일을 응답으로 반환하는 방법에는 여러가지가 있다.
회원 id를 포함한 요청을 보내면 회원의 이미지 경로가 포함된 회원 정보를 응답으로 제공할 수도 있고, 이미지를 요청하는 별도의 api를 만들어 이미지만을 요청하도록 할 수도 있다.
여기서는 특정 회원의 프로필 사진을 요청하는 별도의 api를 구현했다.
또한 Resource로 반환할 수도 있고, byte Array로 반환할 수도 있다.
사실 Resource를 리턴하면 내부적으로 ResourceHttpMessageConverter
가 해당 리소스의 바이트 정보를 응답 body에 담아준다.
이미지를 byteArray로 변환할 필요 없이 편리하게 사용할 수 있도록 스프링에서 제공하는 것이다.
먼저 pathVariable로 회원의 id를 받아오고, service에서 해당 회원의 프로필 사진이 존재하는지 확인한 후, 존재할 경우 해당 파일의 경로를 리턴한다.
해당 경로에 존재하는 파일을 byte Array로 변환하여 리턴한다.
지금은
produces = "image/png"
속성을 추가했기 때문에, 현재 이 API를 호출하면 byte Array가 아닌 실제 이미지를 리턴한다.
이미지가 아닌 byte Array를 반환하고 싶으면 해당 속성을 제거해주면 된다. byte 정보를 리턴하면 프론트엔드에서
<img>
태그를 사용해 이 byte 정보를 읽어 이미지로 반환할 수 있다.
이전 방법과 동일하게 memberService에서 파일의 경로를 받아와서, UrlResource로 반환한다. 이렇게 하면 해당 경로에 존재하는 파일을 찾아서 리턴해준다.
(경로 앞에 "file:"
를 붙여야 한다)
❌ MultipartFile의 존재여부를 확인할때는
!= null
로 확인해서는 안된다.
MultipartFile은 필드에 아무것도 입력하지 않을 경우 이상한 값이 들어가버려서 null 값이 아니게 되기 때문에 null 체크에 실패하게 된다.
따라서 MultipartFile이 제공하는 isEmpty()
로 체크해야 원하는 결과를 얻을 수 있다.