[Spring Boot] 스프링 이미지 업로드 예제 (4) - AWS S3에 이미지 업로드

2024. 11. 7. 21:59· Spring Boot
목차
  1. 0. AWS 스토리지 서비스
  2. 1. AWS S3
  3. 1-1. S3 구성 요소
  4. 2. AWS S3 사용 이미지 업로드 예제
  5. 2-1. S3 버킷 생성
  6. 2-2. IAM (Identity and Access Management)
  7. 3. S3로 구현한 이미지 업로드 예제
  8. 3-1. 의존 라이브러리
  9. 3-2. S3Config 생성
  10. 3-3. S3ImageService 구현
  11. 3-4. 나머지 코드 작성
  12. 3-5. 실행 결과

0. AWS 스토리지 서비스

  • 스토리지
    • AWS의 퍼블릭 클라우드에 데이터 저장 가능
  • AWS 스토리지 서비스 종류 (파일 저장 목적에 따라 나뉨)
    1. 객체 스토리지 S3 (Simple Storage Service)
    2. 블록 스토리지 EBS (Elastic Block Store)
    3. 파일 스토리지 EFS (Elastic File Store)

 

1. AWS S3

  • AWS의 객체 스토리지 서비스
  • 데이터 저장, 관리 및 분석을 안전하고 확장 가능하게 해 줌
  • 특징
    1. 객체 스토리지 : 파일을 객체 형태로 저장함
      • 각 객체는 데이터, 메타데이터, 고유한 식별자로 구성됨
    2. 내구성 : 99.999%의 내구성 제공함
    3. 가용성 : 데이터는 여러 가용 영역 (AZ)에 자동으로 복제됨 ⇒ 데이터 손실 위험 최소화
    4. 확장성 : 거의 무한한 저장 용량 제공, 데이터 양에 관계없이 성능 유지됨
      • 사용자가 데이터 저장, 삭제 할 때 자동으로 확장, 축소됨!

 

1-1. S3 구성 요소

  • 버킷 (Bucket)
    • S3에서 데이터를 저장하는 기본 단위, 하나의 버킷은 여러 객체를 포함 가능
    • 각 버킷은 전 세계적으로 고유한 이름을 가져야 함
    • 각 버킷은 특정 리전(지역)에 생성됨
  • 객체 (Object)
    • S3에 저장되는 데이터의 단위
    • 파일(데이터)과 해당 메타데이터로 구성됨
      • 메타데이터 : 객체에 대한 정보를 담고 있는 데이터
      • ex) 컨텐츠 유형, 크기, 생성 날짜 등
    • 각 객체는 고유한 키(이름)를 가지며, 최대 5TB까지 저장 가능
      • 버킷 이름 + 객체 키 조합하여 식별자로 사용됨

 

2. AWS S3 사용 이미지 업로드 예제

2-1. S3 버킷 생성

  1. 버킷 만들기 클릭

 

2. 생성 정보 기입

  • 일반 구성

  • 버킷 이름 : 대문자 사용 불가, 전세계에서 고유한 이름으로 생성해야 함

 

  • 객체 소유권

  • ACL 활성화
  • 객체 소유권 : 버킷 소유자 선호

 

  • 퍼블릭 액세스 차단 설정

  • 모든 퍼블릭 액세스 차단 선택 해제
  • 아래의 고지 사항 확인 후 체크

 

  • 나머지 설정은 기본값으로 유지

 

2-2. IAM (Identity and Access Management)

  1. 검색창에서 IAM 검색

 

2. 사용자 생성 클릭

 

3. 사용자 세부 정보 지정

  • 사용자 이름 작성

 

4. 권한 설정

  • 직접 정책 연결 선택

 

  • S3 검색 후 AmazonS3FullAccess 선택

 

5. 검토 및 생성

  • 내용 확인하고 사용자 생성 클릭

 

6. 액세스 키 생성

  • 생성된 사용자 이름 클릭

 

  • 액세스 키 만들기 클릭

 

  • 사용 사례를 본인의 상황에 맞게 선택

 

  • 설명 태그 작성 (선택)
  • 액세스 키 만들기 클릭

 

  • 액세스 키가 생성됨
  • 액세스 키는 이 페이지를 나가는 순간 다시 확인 불가능함 ⇒ 복사해서 저장해두거나 csv 파일로 다운로드 해놓을 것!

 

3. S3로 구현한 이미지 업로드 예제

2024.09.02 - [Spring Boot] - [Spring Boot] 스프링 이미지 업로드 예제 (0) - 예시 상황, 공통 코드

 

[Spring Boot] 스프링 이미지 업로드 예제 (0) - 예시 상황, 공통 코드

1. 예제 프로젝트 설명 게시글을 작성하는 기본 CRUD 프로젝트 가정게시글 작성 시 이미지 업로드를 포함하여 구현하고 싶음 2. 공통 코드이미지 업로드 로직을 제외한 기본적인 게시글 작성, 조

blogan99.tistory.com

  • 이전에 올린 이미지 업로드 예제와 상황을 유사하게 설정함
  • 위의 글을 읽고 진행하면 수월할 듯

 

3-1. 의존 라이브러리

  • build.gradle
dependencies {

	// Spring Cloud AWS 라이브러리
    implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}

 

  • application.yml
spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 128MB    # 단일 파일의 최대 크기 설정
      max-request-size: 128MB # 전체 요청의 최대 크기 설정
      
cloud:
  aws:
    credentials:
      accessKey: (2-2에서 발급받은 액세스 키)
      secretKey: (2-2에서 발급받은 비밀 액세스 키)
    s3:
      bucketName: (S3 버킷 이름)
    region:
      static: ap-northeast-2  # 리전 설정
    stack:
      auto: false  # 자동으로 스택 생성하지 않게 설정

 

3-2. S3Config 생성

  • S3Config
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class S3Config {

    // AWS 자격 증명을 application.yml에서 읽어오기 위한 필드
    @Value("${cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secretKey}")
    private String secretKey;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    // AmazonS3 클라이언트를 생성하는 메서드
    public AmazonS3 amazonS3() {

        // AWS 자격 증명을 기본 자격 증명 클래스를 사용하여 생성
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        // AmazonS3 클라이언트 빌더를 사용하여 S3 클라이언트 구성
        return AmazonS3ClientBuilder
                .standard() // 기본 클라이언트 빌더 사용
                .withCredentials(new AWSStaticCredentialsProvider(credentials)) // 인증 정보 제공
                .withRegion(region) // 사용할 AWS 리전 설정
                .build();
    }
}

 

3-3. S3ImageService 구현

  • S3ImageService
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.PutObjectRequest;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class S3ImageService {

    // Amazon S3 클라이언트를 주입받음
    private final AmazonS3 amazonS3;

    // S3 버킷 이름을 application.yml에서 읽어옴
    @Value("${cloud.aws.s3.bucketName}")
    private String bucketName;

    // 이미지 파일을 S3에 업로드하는 메서드
    @Transactional
    public List<String> uploadImages(List<MultipartFile> imageList) throws IOException {

        // 업로드한 파일 리스트가 null인 경우, null을 반환
        if (imageList == null) return null;

        // 업로드 파일이 이미지 형식인지 확인
        if (!isImageFile(imageList)) throw new IllegalArgumentException("사진 이외의 파일은 업로드 불가능합니다!");

        // S3에 업로드된 이미지 URL을 저장할 리스트 생성
        List<String> imageUrlList = new ArrayList<>();

        // 각 이미지 파일을 S3에 업로드
        for (MultipartFile image : imageList) {

            // 원본 파일 이름 가져오기
            String originalFileName = image.getOriginalFilename();

            // 중복을 피하기 위해 UUID를 추가한 새로운 파일 이름 생성
            String modifiedFileName = UUID.randomUUID() + "_" + originalFileName;

            // S3에 업로드할 PutObjectRequest 생성
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, modifiedFileName,
                    image.getInputStream(), null) // InputStream과 메타데이터(null)를 사용하여 요청 생성
                    .withCannedAcl(CannedAccessControlList.PublicRead); // 파일을 공개적으로 읽을 수 있도록 설정

            // S3에 파일 업로드
            amazonS3.putObject(putObjectRequest);

            // 업로드된 파일의 URL을 리스트에 추가
            imageUrlList.add(String.valueOf(amazonS3.getUrl(bucketName, modifiedFileName)));
        }

        // 업로드된 이미지의 URL 리스트 반환
        return imageUrlList;
    }

    // 파일이 이미지 형식인지 검사하는 메서드
    private static boolean isImageFile(List<MultipartFile> imageList) {
        // 각 이미지 파일의 MIME 타입을 검사
        for (MultipartFile image : imageList) {
            String contentType = image.getContentType();
            // MIME 타입이 null이거나 'image/'로 시작하지 않으면 false 반환
            if (contentType == null || !contentType.startsWith("image/")) return false;
        }

        // 모든 파일이 이미지 형식이라면 true 반환
        return true;
    }
}

 

3-4. 나머지 코드 작성

3-4-1. entity

  • Post
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Post {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "post_id")
    private Long id;

    private String title;
    private String content;

    @ElementCollection
    private List<String> imageUrlList;

    public Post(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

3-4-2. dto

  • PostDto
@Data
public class PostDto {

    private String title;
    private String content;

    // 다수의 이미지 (리스트) 입력받기 위해 추가
    private List<MultipartFile> imageList;
}

 

3-4-3. repository

  • PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
}

 

3-4-4. service

  • PostService
@Service
@RequiredArgsConstructor
public class PostService {

    private final PostRepository postRepository;
    private final S3ImageService s3ImageService;

    public Post findPost(Long id) {

        return postRepository.findById(id).orElse(null);
    }

    @Transactional
    public void save(PostDto postDto) {

        Post post = new Post(postDto.getTitle(), postDto.getContent());

        // 이미지 저장 로직 추가
        try {
            List<String> imageUrlList = s3ImageService.uploadImages(postDto.getImageList());
            post.setImageUrlList(imageUrlList);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        postRepository.save(post);

    }
}

 

3-4-5. controller

  • PostController
@Controller
@RequiredArgsConstructor
@RequestMapping("/s3/post")
public class PostController {

    private final PostService postService;

    // 게시글 단건 조회
    @GetMapping("/{postId}")
    public String getPost(@PathVariable("postId") Long postId, Model model) {

        model.addAttribute("post", postService.findPost(postId));

        return "home";
    }

    // 게시글 작성
    @ResponseBody
    @PostMapping(consumes = "multipart/form-data")
    public ResponseEntity<Void> savePost(@ModelAttribute PostDto postDto) {

        postService.save(postDto);

        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
}

 

3-4-6. html

  • home.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" >

<h1>제목: [[${post.title}]]</h1>
<h1>내용: [[${post.content}]]</h1>

<div th:each="imageUrl : ${post.imageUrlList}">
    <img th:src="${imageUrl}" style="width: 200px"/>
</div>

</html>

 

3-5. 실행 결과

3-5-1. 이미지 첨부 게시글 작성 (POST localhost:8080/s3/post)

  • 이미지 파일 2개와 함께 게시글이 작성됨을 확인

 

3-5-2. AWS S3 버킷에 이미지 업로드 됨을 확인

  • POST 요청을 보낼 때 포함되어 있던 2개의 이미지가 업로드 됨을 확인

 

3-5-3. MySQL 확인

  • Post의 imageUrlList 에 S3 버킷에 업로드 된 url이 저장됨을 확인

 

3-5-4. 이미지 출력 확인(http://localhost:8080/s3/post/1)

  • 업로드 한 2개의 이미지가 출력됨을 확인
저작자표시 변경금지 (새창열림)

'Spring Boot' 카테고리의 다른 글

[Spring Boot] Spring Security 설정 방법 (쿠키 기반 인증, 세션 관리, HTTPS, CSRF 보호)  (0) 2024.11.08
[Spring Boot] 스프링 로깅 방법 (SLF4J, Logback)  (0) 2024.09.15
[Spring Boot] TDD와 given-when-then 패턴으로 테스트 코드 작성 (JUnit, AssertJ, 단위 테스트, 통합 테스트, Mock 등)  (0) 2024.09.11
[Spring Boot] 주소 입력으로 날씨 정보 출력 예제 프로젝트 (Geocoder API 2.0, open weather map API)  (0) 2024.09.08
[Spring Boot] Jpa Auditing (+ BaseEntity) 로 엔티티 관련 이벤트 자동 기록하기  (2) 2024.09.07
  1. 0. AWS 스토리지 서비스
  2. 1. AWS S3
  3. 1-1. S3 구성 요소
  4. 2. AWS S3 사용 이미지 업로드 예제
  5. 2-1. S3 버킷 생성
  6. 2-2. IAM (Identity and Access Management)
  7. 3. S3로 구현한 이미지 업로드 예제
  8. 3-1. 의존 라이브러리
  9. 3-2. S3Config 생성
  10. 3-3. S3ImageService 구현
  11. 3-4. 나머지 코드 작성
  12. 3-5. 실행 결과
'Spring Boot' 카테고리의 다른 글
  • [Spring Boot] Spring Security 설정 방법 (쿠키 기반 인증, 세션 관리, HTTPS, CSRF 보호)
  • [Spring Boot] 스프링 로깅 방법 (SLF4J, Logback)
  • [Spring Boot] TDD와 given-when-then 패턴으로 테스트 코드 작성 (JUnit, AssertJ, 단위 테스트, 통합 테스트, Mock 등)
  • [Spring Boot] 주소 입력으로 날씨 정보 출력 예제 프로젝트 (Geocoder API 2.0, open weather map API)
공대생안씨
공대생안씨
전자공학과 학부생의 코딩 일기
티스토리
|
로그인
공대생안씨
공대생의 코딩 일기
공대생안씨
글쓰기
|
관리
전체
오늘
어제
  • All Categories (152)
    • Spring Boot (55)
      • JPA (7)
      • Lombok (2)
    • Java (21)
    • DevOps (12)
      • CI,CD (8)
      • Monitoring (1)
    • Database (7)
      • MySQL (5)
      • MongoDB (1)
      • H2 (1)
    • Trouble Shooting (5)
    • FE (4)
    • IntelliJ (3)
    • Git (3)
    • Algorithm (41)

블로그 메뉴

  • 홈
  • 태그
  • Github

공지사항

인기 글

hELLO · Designed By 정상우.v4.2.2
공대생안씨
[Spring Boot] 스프링 이미지 업로드 예제 (4) - AWS S3에 이미지 업로드
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.