[Spring & Spring Boot] 이미지 저장

이동엽·2025년 11월 25일

개인적으로 궁금해서 시도 해보고 정리하고 있습니다.

방법으론
1. 바이너리 형태로 DB 저장.
2. 로컬 서버 디렉토리에 직접 저장
3. 이미지 호스팅 이용해 저장.

첫번째 방법으론 Lob을 사용해 대형 객체 데이터를 저장합니다.

대신 이렇게 하면 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();
    }

두번째 방법은 로컬서버에 저장해서 이 저장한 경로를 DB에 저장하는 방식이다.

그래서 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)를 사용해 파일, 다양한 유형의 데이터를 저장하고 관리한다.

핵심은 파일 및 데이터들은 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.....

왜 이렇게 나눌까?

  1. DB에 저장하면 너무 성능도 안좋아지고, 비용이 안좋다.
  2. S3는 파일 제공하거나 관리하는거에 최적화되어 있다.

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사용하는 방식을 사용하는것 같습니다.

profile
씨앗

0개의 댓글