Image 업로드(+Jpa, TestCode)

박도영·2022년 5월 7일
0

Image Upload

목록 보기
1/2
post-thumbnail

계기

  • 게시판 기능에 이미지 업로드 기능을 추가하기 위해 연습
  • 내가 생각한 요구 사항 :
    • 여러 이미지를 올릴 수 있어야 함
    • 데이터 저장을 위해 Jpa를 사용
    • 이미지 저장은 서버에 저장하고, 후에 AWS S3로 변경 예정 ( 최소한의 변경 고려)
    • 테스트 코드가 있었으면 좋겠음

ImageUpload Entity

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ImageUpload {

    @Id @GeneratedValue
    private Long id;

    private String originFilename;
    private String storageImageName;

    @ManyToOne
    @JoinColumn(name = "post_id")
    private Post post;

    private ImageUpload(String originFilename, String storageImageName) {
        this.originFilename = originFilename;
        this.storageImageName = storageImageName;
    }

    public static List<ImageUpload> createImageUploadList(List<MultipartFile> multipartFiles) {
        if( multipartFiles.isEmpty()) {
            return null;
        }
        List<ImageUpload> storeImageList = new ArrayList<>();
        for (MultipartFile multipartFile : multipartFiles) {
            storeImageList.add(ImageUpload.createImageUpload(multipartFile));
        }
        return storeImageList;
    }

    public static ImageUpload createImageUpload(MultipartFile multipartFile) {

        if (multipartFile.isEmpty()) {
            return null;
        }

        String originalFilename = multipartFile.getOriginalFilename();
        String storeImageName = createStoreImageName(originalFilename);

        return new ImageUpload(originalFilename, storeImageName);
    }

    private static String createStoreImageName(String originalFilename) {
        String ext = extractExt(originalFilename);
        return UUID.randomUUID() + "." + ext;
    }

    private static String extractExt(String originalFilename) {
        // test.png ->
        int pos = originalFilename.lastIndexOf(".");
        return originalFilename.substring(pos + 1);
    }

    protected void setPost(Post post) {
        this.post = post;
    }
}
  • 처음에는 굳이 엔티티로 만들고 싶지 않았지만, 게시글과 다대일 관계를 가지는 점을 고려해서 엔티티로 만듬
  • multipartFile을 매개변수로 받는 정적 생성자 메서드를 통해 객체 생성 가능
  • UUID를 이용해서 이미지의 이름이 겹치지 않게 해줬다.
  • (김영한님의 강의 코드에 ddd를 적용해서 녹여봤다... )
@Service
@RequiredArgsConstructor
public class PostService {

    private final PostRepository postRepository;
    private final ImageUploadRepository imageUploadRepository;

    @Transactional
    public Long postSave(List<MultipartFile> multipartFiles, String content){
        Post post = new Post(content);

        List<ImageUpload> imageUploadList = ImageUpload.createImageUploadList(multipartFiles);
        if(imageUploadList != null) {
            // 이미지 저장 로직 (추가 예정)

            post.addImageUploads(imageUploadList);
            imageUploadRepository.saveAll(imageUploadList);
        }

        return postRepository.save(post).getId();
    }
}
  • post 생애주기를 담당하는 서비스
  • 이미지를 서버 내부에 저장하는 객체를 따로 만들 예정 (단일 책임 원칙 적용)
//@ExtendWith(SpringExtension.class)
//@DataJpaTest
class ImageUploadTest {

    @Test
    void createImageUploadTest() throws IOException {
        // given
        URL resource = getClass().getResource("/img/404.png");
        MockMultipartFile multipartFile = new MockMultipartFile("image",
                "test.png", "image/png",
                new FileInputStream(resource.getFile()));

        // when
        ImageUpload imageUpload = ImageUpload.createImageUpload(multipartFile);

        // then
        String originFilename = (String) ReflectionTestUtils.getField(imageUpload, "originFilename");

        assertThat(originFilename).isEqualTo("test.png");
        assertThat(imageUpload).extracting("storageImageName").isNotNull();
    }

    @Test
    void pathTest() throws FileNotFoundException {
        URL resource = getClass().getResource("/img/404.png");
        System.out.println("URL : " + resource );
        FileInputStream fileInputStream = new FileInputStream(resource.getFile());
        System.out.println("fileInputStream : " + fileInputStream);
    }

    @Test
    void createImageUploadListTest() throws IOException {
        // given
        URL resource = getClass().getResource("/img/404.png");
        MockMultipartFile multipartFile1 = new MockMultipartFile("image",
                "404.png", "image/png",
                new FileInputStream(resource.getFile()));

        URL resource2 = getClass().getResource("/img/raspberry.png");
        MockMultipartFile multipartFile2 = new MockMultipartFile("image2",
                "raspberry.png", "image/png",
                new FileInputStream(resource2.getFile()));

        List<MultipartFile> arrayList = new ArrayList<>();
        arrayList.add(multipartFile1);
        arrayList.add(multipartFile2);

        // when
        List<ImageUpload> imageUploadList = ImageUpload.createImageUploadList(arrayList);

        // then
        assertThat(imageUploadList).extracting("originFilename")
                .contains("raspberry.png", "404.png");
        assertThat(imageUploadList).extracting("storageImageName")
                .isNotNull();
    }
}
  • ImageUpload 관련 단위 테스트
  • ImageUpload의 캡슐화를 위해서 getter를 막아뒀는데 덕분에 테스트 코드 작성에 애먹었다...
    • 해결책으로 ReflectionTestUtils를 사용해서 비공개 필드나 메서드를 꺼낼 수 있다
    • AssertJ의 검증 기능을 이용할 수 도 있다
  • UUID는 검증할 방법이 떠오르지 않아서 null 체크만 해주었다

다음에는 image를 저장하는 내용과 그에 관련된 테스트 코드를 작성 할 예정이다.

전체 코드 확인

ps

깃 잘못 사용해서 코드가 다 날아가고 commit 기록이 없어서 멘붕...
git reflog 명령어로 숨겨진? 커밋 찾아서 복구했다
정말 식겁했다 휴


참고 자료 :
https://www.inflearn.com/course/스프링-mvc-2/dashboard

profile
좋은 개발자란?

0개의 댓글