데이터베이스(DB)에 이미지를 직접 저장하는 것은 권장되지 않는 방법이다. 그 이유는 다음과 같다.
구글링을 해봤더니 대부분 S3와 같은 객체 저장소를 별도로 활용하는 방법이 많았다.
AWS S3를 사용하는 이유는 아래와 같다.
S3는 Amazon Simple Storage Service의 약자로, 대용량 파일을 안전하고 저렴하게 저장하기 위한 클라우드 스토리지 서비스를 말한다. 이미지와 같은 비정형 데이터를 저장하는 데 매우 효율적이며, 다음과 같은 장점이 있다.
데이터베이스(DB)에 이미지를 직접 저장하는 것은 권장되지 않는 방법이다. 그 이유는 다음과 같다.
사용자에게 보여지는 이미지는 사용자가 업로드한 파일명이 아니라 서버에서 생성된 URL이다.
만약 A, B 두 사용자가 존재하는 상황에서 A가 a라는 이름의 이미지를 저장하고 B 역시 a라는 이름의 이미지를 저장한다고 가정하자.
※ 문제 상황을 만들기 위해 같은 이름을 가지나 이미지는 다르다고 가정한다.
→ 데이터 정합성도 지키면서 효율적으로 데이터를 관리하기 위해 S3 객체 저장소와 DB를 같이 활용하기로 했다.
→ S3 버킷 생성과 관련된 부분은 남기지 않겠다.
cloud:
aws:
s3:
bucketName: S3 버킷명
credentials:
access-key: 액세스 키
secret-key: 비밀 키
region:
static: 리전
stack:
auto: false
@Configuration
public class AwsS3Config {
// 액세스 키
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
// 비밀 키
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
// S3 리전
@Value("${cloud.aws.region.static}")
private String region;
@Bean
public AmazonS3Client amazonS3Client() {
AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client) AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(region)
.build();
}
}
@Slf4j
@RequiredArgsConstructor
@Service
public class AwsS3Uploader {
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.s3.bucketName}")
private String bucket;
public String uploadFile(MultipartFile multipartFile) throws IOException {
File convertedFile = convertMultiPartFileToFile(multipartFile);
String fileName = UUID.randomUUID().toString();
String fileUrl = uploadToS3(convertedFile, fileName);
convertedFile.delete();
return fileUrl;
}
private File convertMultiPartFileToFile(MultipartFile file) throws IOException {
File convertedFile = new File(file.getOriginalFilename());
try (FileOutputStream fos = new FileOutputStream(convertedFile)) {
fos.write(file.getBytes());
}
return convertedFile;
}
// S3 이미지 업로드
private String uploadToS3(File file, String fileName) {
amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, file)
.withCannedAcl(CannedAccessControlList.PublicRead));
return amazonS3Client.getUrl(bucket, fileName).toString();
}
// S3 이미지 삭제(데이터 정합성)
public void deleteFile(String fileName) {
try {
if (amazonS3Client.doesObjectExist(bucket, fileName)) {
amazonS3Client.deleteObject(bucket, fileName);
log.info("파일 삭제가 정상적으로 처리되었습니다.={}", fileName);
} else {
log.warn("해당 파일이 존재하지 않습니다.={}", fileName);
}
} catch (Exception e) {
throw new RuntimeException("S3 파일 삭제 실패", e);
}
}
}