카카오톡 클론코딩(7) - S3Service
2025. 3. 14. 22:01ㆍProject
이번에는 그놈의 중복코드가 계속 나와서 만들었다던 S3Service를 보여줄 것이다. 그닥 보여줄게 많지는 않은 부분이기도 하고
진작에 보여줬어야 했을거 같지만 조금 늦은감이 있더라도 지금이라도 올려본다.
@Service
@RequiredArgsConstructor
@Slf4j
public class S3Service {
private final AmazonS3 amazonS3;
@Value("${cloud.aws.s3.bucket}")
private String bucket;
/**
* S3에 파일 업로드 (임시 저장)
*/
public String uploadFileToTemp(MultipartFile multipartFile) {
return uploadFile(multipartFile, "temp/");
}
/**
* S3에 파일 업로드 (최종 저장)
*/
public String uploadFile(MultipartFile multipartFile, String path) {
if (multipartFile == null || multipartFile.isEmpty()) {
throw new CustomException(ExceptionCode.INVALID_FILE_FORMAT);
}
String fileName = path + createFileName(multipartFile.getOriginalFilename());
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
objectMetadata.setContentType(multipartFile.getContentType());
try (InputStream inputStream = multipartFile.getInputStream()) {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata));
} catch (IOException e) {
throw new CustomException(ExceptionCode.FILE_UPLOAD_FAILED);
}
return amazonS3.getUrl(bucket, fileName).toString();
}
/**
* 파일을 최종 저장소로 이동
*/
public String moveFileToFinalLocation(String tempUrl) {
String tempPath = tempUrl.substring(tempUrl.indexOf("/temp") + 1);
String finalPath = tempPath.replace("temp/", "chat/");
try {
amazonS3.copyObject(bucket, tempPath, bucket, finalPath);
amazonS3.deleteObject(bucket, tempPath);
} catch (AmazonS3Exception e) {
throw new CustomException(ExceptionCode.FILE_UPLOAD_FAILED);
}
return amazonS3.getUrl(bucket, finalPath).toString();
}
/**
* S3에서 파일 삭제
*/
public void deleteFileByUrl(String fileUrl) {
if (!fileUrl.contains(bucket)) {
throw new CustomException(ExceptionCode.INVALID_FILE_URL);
}
String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
try {
amazonS3.deleteObject(new DeleteObjectRequest(bucket, fileName));
log.info("S3에서 파일 삭제 : " + fileName);
} catch (Exception e) {
throw new CustomException(ExceptionCode.FILE_DELETE_FAILED);
}
}
/**
* 파일 이름을 UUID 기반으로 생성
*/
private String createFileName(String fileName) {
return UUID.randomUUID().toString().concat(getFileExtension(fileName));
}
/**
* 파일 확장자 추출
*/
private String getFileExtension(String fileName) {
try {
return fileName.substring(fileName.lastIndexOf("."));
} catch (StringIndexOutOfBoundsException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "잘못된 형식의 파일 : " + fileName + " 입니다.");
}
}
}
특별할것은 없고 단순히 보이는게 다인 코드다. 하지만 이 S3Service를 만들 생각도 안하고 생각없이 이미지 업로드 부분을
매번 Member면 MemberServcie에 ChatMessage 면 ChatService에 넣어버린 대가는 처참했다... 끔찍할 정도의 중복코드와
각 서비스가 해야할 역할 외에도 다른 역할까지 하면서 책임 분리가 확실하게 되지 않았다는 것이다. 그리고 해당 부분들을 리팩토링 하면서 버리는 시간은 덤.. 설계를 좀더 꼼꼼히 했어야 했는데 확실히 실수한 부분인것 같다.
이 다음글은 도메인단위 테스트를 순수 자바코드로 작성한후에 올릴것이다. 그 이후에는 이전 글에서 언급했듯이 mySql 이중화 작업을 진행 해볼것이다.
'Project' 카테고리의 다른 글
카카오톡 클론코딩(9) - nGrinder (0) | 2025.04.10 |
---|---|
카카오톡 클론코딩(8) - swagger 달기 (0) | 2025.03.19 |
카카오톡 클론코딩(6) - ChatMessage (0) | 2025.03.12 |
카카오톡 클론코딩(4) - Friend (0) | 2025.03.10 |
카카오톡 클론코딩(3) - MEMBER (0) | 2025.03.09 |