[TIL 43일 차] Sprint Mission6 - BinaryContent 저장 로직 고도화
오늘의 학습
- 개발 진행 상황
- BinaryContent 저장 로직 고도화
- 로컬 저장 방식으로
BinaryContentStorage구현체를 구현 (LocalBinaryContentStorage)- 루트 경로 :
Path root - Bean 생성 시 자동으로 호출되어 디렉토리를 초기화하는 메서드 :
void init() - 파일의 실제 저장위치에 대한 규칙을 정의하는 메서드 :
Path resolvePath(UUID) - 바이너리 데이터 다운로드 메서드 :
ResponseEntity<Resource> download(BinaryContentDto)
- 루트 경로 :
- 로컬 저장 방식으로
- BinaryContent 저장 로직 고도화
-
파일 다운로드 처리 과정
/** * 파일의 실제 저장 위치에 대한 규칙을 정의 <br> * 파일 저장 위치 규칙 예시 : {@code {root}/{UUID}} <br> * {@code put}, {@code get} 메서드에서 호출해 일관된 파일 경로 규칙을 유지 * @param binaryContentId * @return {@code Path} */ public Path resolvePath(UUID binaryContentId) { // root 하위 경로로 새로운 Path를 만듬 // root = "/storage/binary" 이면 "/storage/binary/UUID.toString"이 만들어짐 return root.resolve(binaryContentId.toString()); } @Override public InputStream get(UUID binaryContentId) { Path path = resolvePath(binaryContentId); try { // 파일을 읽을 수 있는 통로를 연다. return Files.newInputStream(path); } catch(IOException e) { throw new IllegalStateException("조회 실패", e); } } @Override public ResponseEntity<Resource> download(BinaryContentDto binaryContentDto) { // Resource 타입 : Spring에서 파일, 스트림 같은 데이터 자원을 표현하는 타입 // InputStream : 바이트 데이터를 읽기 위한 통로 InputStream inputStream = get(binaryContentDto.id()); // InputStreamResource : Resource의 구현체 중 하나로 // 이미 얻어온 InputStream을 Spring이 처리하기 쉬운 Resource 형태로 감싼다. // Spring은 ResponseEntity의 body로 파일/스트림 같은 자원을 보낼 때 Resource 타입을 잘 처리한다. Resource resource = new InputStreamResource(inputStream); return ResponseEntity.status(HttpStatus.OK) // Content-Type 헤더 설정 // MediaType.parseMediaType(type) : type(`image/png` 등)을 Spring의 MediaType 객체로 변경 .contentType(MediaType.parseMediaType(binaryContentDto.contentType())) // Content-Length 헤더 설정 // 응답 본문 크기가 몇 바이트인지 알려주는 헤더 .contentLength(binaryContentDto.size()) .header( // Content-Disposition 헤더 설정 // 브라우저에서 이 응답을 첨부파일처럼 다운로드하라고 알려줌 HttpHeaders.CONTENT_DISPOSITION, // ContentDisposition 객체 생성 ContentDisposition.attachment() // ContentDisposition 빌더 // attachment()` : 브라우저가 응답을 화면에 바로 표시하기보다 다운로드 대상으로 처리하도록 유도 .filename(binaryContentDto.fileName()) // 파일명 설정 .build() // 객체 완성 .toString() // HTTP 헤더 값은 문자열이어야 하기 때문에 문자열로 변환 ) .body(resource); } - 문제 : “java.lang.NullPointerException: Cannot invoke “java.util.UUID.toString()” because “binaryContentId” is null” 문제
- 원인 : id가 객체를 new 하는 순간 생성되는 게 아니라, JPA가 그 엔티티를 영속화할 때 생성되기 때문
- 해결 : binaryContent를
save()해줘서 영속화를 해줘야 한다.
프로젝트 요구 사항
// ...
2-7. BinaryContent 저장 로직 고도화
// ...
- 로컬 디스크 저장 방식으로 BinaryContentStorage 구현체를 구현하세요.
-
클래스 다이어그램

-
discodeit.storage.type값이local인 경우에만 Bean으로 등록되어야 합니다.Path root- 로컬 디스크의 루트 경로입니다.
discodeit.storage.local.root-path설정값을 정의하고, 이 값을 통해 주입합니다.
void init()- 루트 디렉토리를 초기화합니다.
- Bean이 생성되면 자동으로 호출되도록 합니다.
Path resolvePath(UUID)- 파일의 실제 저장 위치에 대한 규칙을 정의합니다.
- 파일 저장 위치 규칙 예시:
{root}/{UUID}
- 파일 저장 위치 규칙 예시:
put,get메소드에서 호출해 일관된 파일 경로 규칙을 유지합니다.
- 파일의 실제 저장 위치에 대한 규칙을 정의합니다.
ResponseEntity<Resource> donwload(BinaryContentDto)get메소드를 통해 파일의 바이너리 데이터를 조회합니다.- BinaryContentDto와 바이너리 데이터를 활용해
ResponseEntity<Resource>응답을 생성 후 반환합니다.
// ...
3-4. MapStruct 적용
- Entity와 DTO를 매핑하는 보일러플레이트 코드를 MapStruct 라이브러리를 활용해 간소화해보세요.
GitHub Repository 주소
https://github.com/JungH200000/10-sprint-mission/tree/sprint6
Leave a comment