TIL - 0704
java-ims
다운로드 기능 만들기
- 업로드 했던 파일을 반대로 다운로드 하는 기능을 만들어야함
- 특정 이슈에 특정 유저가 올린 특정 파일을 다운로드
- 업로드 했을 때 AttachmentRepository에 업로드 정보를 저장함 : 이슈 id, 사용자 id, 첨부파일 uuid(식별자 - 런타임 시에 고정된 전략이라서 static 메소드를 사용했지만 AttachmentNameConverter도 런타임 시에 다양한 전략을 사용하게끔 한다면 DI로 변경해도됨)와 origin name 등
public class AttachmentService {
public Issue upload(User loginUser, Issue issue, MultipartFile file) throws IOException {
String savedFileName = fileSaver.save(file, AttachmentNameConverter.convertName(file.getOriginalFileName()));
Attachment attachment = new Attachment()
.uploadBy(loginUser)
.toIssue(issue)
.setOriginName(file.getOriginalFilename())
.setManageName(savedFileName);
attachmentRepo.save(attachment);
return issue;
}
}
- 다운로드 URI는 GET 메소드 맵핑(attachment id값에 의한 식별) -> 파일 정보 찾고, HTTP 리스폰스 메세지 작성 후 리턴
- PathResource : 로컬에 저장된 파일을 다루는 API(현재 사용하고 있는 FileSaver의 구현체가 로컬에 저장하는 LocalFileSaver)
- HttpHeader 작성 역할 분리 필요 : HttpHeader 작성하는 것도 Controller에서 작성할 필요없이 PathResource만 Service로부터 리턴을 받은 후 HttpHeader작성 역할에 요청해서 Header를 만들어내도록 하는 것이 좋음
public class AttachmentController {
@GetMapping("/{id}")
public ResponseEntity<PathResource> download(@LoginUser User loginUser, @PathVariable Long issueId, @PathVariable Long id) throws IOException {
Attachment attachment = attachmentService.findById(id);
PathResource pathResource = attachmentService.download(loginUser, issueService.findById(issueId), id);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.MULTIPART_FORM_DATA);
header.setContentLength(pathResource.contentLength());
header.set(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", attachment.getOriginName)));
return new ResponseEntity<>(pathResource, header, HttpStatus.OK)
}
}
업로드 위치 관리 외부에서 해보기
- LocalFileSaver를 비롯한 FileSaver에서 사용하는 파일 업로드 위치를 외부에서 관리하기
- springboot.properties + @Value 사용해서 인젝션 하기
- 설정값을 한 곳(springboot.properties)에서 관리한다는 장점이
- 구현하기 : 상태값 없이 저장전략 각각 구현하도록하는 기존 인터페이스 구조와 상태값(uploadPath)을 가질 수 밖에 없는 구조 간에 충돌이 생김
- 각각 구현하되 상태값을 공통적으로 가지면서 공통적인 메소드로 묶는 방법은 추상클래스
/* application.properties */
file.save.local=/Users/imjinbro/Desktop/codesquad/workspace/jwp/java-ims/src/main/resources/upload/
/* FileSaver */
public abstract class FileSaver {
private String uploadPath;
public FileSaver(String uploadPath) {
this.uploadPath = uploadPath;
}
public abstract String save(MultipartFile file, String fileManageName) throws IOException;
Path getPath(String fileManageName) {
return Paths.get(uploadPath + fileManageName);
}
protected String getSavePath() {
return uploadPath;
}
}
/* LocalFileSaver */
@Component(value = "localFileSaver")
public class LocalFileSaver extends FileSaver {
public LocalFileSaver(@Value("${file.save.local}") String uploadPath) {
super(uploadPath);
}
@Override
public String save(MultipartFile file, String fileManageName) throws IOException {
File saveFile = new File(getSavePath() + fileManageName);
file.transferTo(saveFile);
return fileManageName;
}
}
- 업로드 통합테스트 결과 해당 구조만 변경하고 테스트 통과 : 컨트롤러, 서비스 코드 변경없음
git
- git add .만 사용해서 staging area로 옮기지 말 것 : 불필요한 파일 또한 관리 대상이 됨
- 아예 불필요한 파일은 git 관리 범위 바깥에 저장해두거나 git add . 와 같은 모든 파일을 한꺼번에 관리 대상으로 지정하는 명령어 쓰지않기
자바
- 추상클래스 vs 인터페이스 : 상태값을 가지는가 가지지않느냐의 차이, 상태값에 따라 다르게 동작하는가, 단순히 공통된 동작이나 구현체마다 다르게 동작하는가
데이터베이스
비용도 생각을 해보자
- 자원을 무한정 사용할 수 있으면 좋지만, 현실은 한정된 자원 내에서 최대한의 효율을 뽑아내야함
- 초기비용(라이센스 비용)과 운영비용(기술 지원 등을 포함한 비용, 서브스크립션 비용 - aws)을 고려해야함
- 에디션에 따라 기술 지원, 편의 기능(보안, 로그, 버젼 관리 - 무조건적으로 최신버젼을 도입하면 안정적이지 않기때문에 그러면 안됨 등)의 차이가 있음
- PaaS(Platform as a Service) : aws의 RDB 서비스 처럼 미들웨어(DBMS와 같은 소프트웨어)를 포함한 클라우드 서비스 임대 모델을 가리키는 말 차이