개인적으로 궁금해서 시도 해보고 정리하고 있습니다.
방법으론
1. 바이너리 형태로 DB 저장.
2. 로컬 서버 디렉토리에 직접 저장
3. 이미지 호스팅 이용해 저장.
대신 이렇게 하면 Post 안에 이미지 파일이 여러개가 되면 속도 및 캐싱 성능이 낮아져서 다른 방법이 필요하다.
public class Image {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String fileName;
private String contentType;
@Lob
@Column(columnDefinition = "LONGBLOB")
private byte[] data;
@ManyToOne
@JoinColumn(name = "post_id")
private Post post;
}
public Long saveImage(MultipartFile file) throws Exception {
Image image = new Image();
image.setFileName(file.getOriginalFilename());
image.setContentType(file.getContentType());
image.setData(file.getBytes());
Image saved = imageFileRepository.save(image);
return saved.getId();
}
그래서 handler같은 걸 만들어서
그안에 파일 이름과 경로를 생성해서 문자열로 변환시켜서 DB에 저장한다.
@Component
public class ImageHandler {
public String saveimage(MultipartFile image) throw IOException{
// 이미지의 원래 파일 이름을 가져옴
String fileName = getOriginName(image);
// 저장할 파일 경로 생성
Path filePath = Paths.get(uploadDir + fileName);
// 업로드 디렉토리가 존재하지 않으면 생성
if (!Files.exists(Paths.get(uploadDir))) {
Files.createDirectories(Paths.get(uploadDir));
}
// 파일을 지정된 경로에 저장
Files.write(filePath, image.getBytes());
// 저장된 파일 경로를 문자열로 반환
return filePath.toString();
}
핵심은 파일 및 데이터들은 S3에 저장하고, DB는 그 파일에 대한 URL, 어떤 장소(Post)에 들어있는지 그 파일에 대한 정보를 저장한다.
동작하는 구조는
1. 사용자가 이미지 업로드 한다.
2. 서버가 S3에 file.png를 저장
3. S3가 이미지 접근 URL 생성 ex) https://s3.aws.com/bucket/file.png
4. 이 url를 DB에 저장한다.
이런 형태로 저장
id: 1
url: https://s3.aws.com/bucket/file.png
filename: file.png
ownerId: 42
createdAt: 2025.....
Spring에서 사용할때 환경
의존성 추가
implementation platform('software.amazon.awssdk:bom')
implementation 'software.amazon.awssdk:s3'
application.yml에 AWS S3 설정 추가
cloud:
aws:
credentials:
access-key: {access-key}
secret-key: {secret-key}
s3:
bucket: 버킷 이름
region:
static: ap-northeast-2 # 리전 정보(서울)
stack:
auto: false
AmazonS3Config 추가
@Configuration
public class AmazonS3Config { // 설정 값 등록 파일
@Value(("${cloud.aws.credentials.access-key}"))
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
// yml파일에 작성한 값을 읽어와서 AmazonS3Client객체를 만들어 bean으로 주입
@Bean
public AmazonS3Client amazonS3Client(){
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
}
}
여기서 Image 엔티티에 url을 추가한다.
Controller
@PostMapping("/{post-id}/images")
public ResponseEntity<String> uploadImages(
@PathVariable("post-id") Long postId,
@RequestParam("files") List<MultipartFile> files) {
service.uploadImages(postId, files);
return ResponseEntity.ok("이미지 업로드 완료");
}
Service
@Transactional
public void uploadImages(Long postId, List<MultipartFile> files) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new RuntimeException("Post not found"));
for (MultipartFile file : files) {
String fileName = "post/" + postId + "/" + UUID.randomUUID() + "_" + file.getOriginalFilename();
String uploadUrl = putS3(file, fileName); // S3 업로드
Image image = new Image();
image.setFileName(file.getOriginalFilename());
image.setUrl(uploadUrl);
image.setContentType(file.getContentType());
image.setPost(post);
post.getImages().add(image);
}
postRepository.save(post);
}
삭제하고 싶으면 deleteS3(url);을 사용한다.
이러면 하나의 Post에 여러개의 이미지 파일 가지고 있는 형식.
블로그,채팅방 같은 이미지 많이 사용하는 방식 및 배포한다면 이 S3사용하는 방식을 사용하는것 같습니다.