front에서 MultipartFile형식의 Img파일을 넘겨주기 위해서는 form-data 형식으로 받아야 한다.
@ModelAttribute 를 통해서 Dto와 같이 받거나 @RequestPart를 사용해 따로 받을 수 있다.
@PostMapping()
public ResponseEntity register(
@ModelArribute RequestRegisterDto dto
@RequestPart("file") MultipartFile img
){
...
}
class RequestRegisterDto { MultipartFile img; ... others }
파일 이름 그대로 저장하기 위해 노력했다.
public ImgEntity saveImg(MutipartFile imgFile) {
String fileName = imgFile.getOriginFilename();
Path path = Paths.get(System.getProperty("user.dir"), IMG_DEFAULT_PATH, fileName);
Files.write(path, imgFile.getBytes());
ImgEntity savedImg = new ImgEntity(fileName);
ImgRepository.save(savedImg);
return savedImg;
혹시라도 db에 똑같은 이름이 있다면 1~10 까지 숫자를 추가로 붙이는 방법을 추가했다
front에게는 위에서 저장한 ImgEntity의 uri(fileName)값을 반환한다
--- back ResponseDto 반환
class ResponseDto { String imgURI = imgEntity.getURI(); }
-- front Img파일 요청
<HTML>
<img src="backserver/img/${ResponseDto.imgURI}" />
</HTML>
Img tag에 의해서 back으로 GET 방식으로 backserver/img/{ImgURI}로 요청이 들어온다
Img요청을 Spring 을 사용해서 반환해도 되지만 Nginx를 통해서 반환하는 것이 server의 부담을 조금이니마 줄일 것 같아서 nginx를 사용했다
location /img/ {
root /usr/share/nginx/html;
try_files $uri $uri/ =404;
}
location / {
proxy_pass http://springboot:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass_header Access-Control-Allow-Origin;
proxy_pass_header Access-Control-Allow-Credentials;
}
img를 어디에 저장하든 Img파일 자체는 transaction을 지원해주지 않는다.
ImgEntity imgEntity = imgService.save(imgFile);
UserEntity newUser = new UserEntity(imgEntity);
userRepository.save(newUser); // throw RunTimeException("email conflict")
userReposirory.save(userUser); 부분에서 exception이 발생하면 newUser, imgEntity는 rollback이 되지만 저장된 imgFile은 rollback되지 않는다.
ImgService의 함수들을 호출하는 service 함수들에 Aop를 사용한 ImgTransaction을 건다
ImgService 함수들에 마지막 변수로 ArrayList<Path> path를 받는다
저장한 모든 Img파일들을 path에 추가한다
aop가 끝나면서 exception이 발생했다면 path들의 Img파일들을 지운다
@Around("@annotioan(imgTransaction)")
public Object around(ProceedingJoinPoint, Imgtransaction transaction) {
ArrayList<Path> path = new ArrayList<>();
Objects[]
try{
Object[] args = joinPoint.getArgs();
args[args.length-1] = imgPaths;
return joinPoint.proceed(args);
} catch(Eception e) { imgService.deleteImgFile(img); }
}
public ImgEntity saveImg(MutipartFile imgFile, ArrayList<Path\> path);
imgService 함수를 호추하는 모든 함수에 마지막 인자에 ArrayList<Path>가 추가되어야 한다.
public void UserService.register(RequestRegisterDto dto, ArrayList<Path> path);
다른 방법도 존재하긴 하지만 aop를 사용해서 직접 구현해보고 싶어서 이 방법을 선택했다.
내가 직접 구현한 방법인 만큼 TestCode를 정확하게 짜고 모듈화에 노력했다.