Spring Boot + AWS S3 연동하기

이동영·2025년 11월 19일

웹개발

목록 보기
25/36

이번 프로젝트에서 회원 프로필 사진, 게시글 첨부 파일 등 파일 업로드 기능이 필요했다. 로컬 서버(내 컴퓨터)에 저장하면 배포 시에 데이터가 사라지거나 관리가 어렵기 때문에, 클라우드 저장소의 표준인 AWS S3를 연동하기로 결정했다.

무작정 S3 Bucket을 만들기 전에, IAM을 만들었다.

AWS 계정 보안 : Root vs IAM

AWS를 처음 쓰면 보통 이메일로 로그인을 하는 Root 계정을 쓴다. 하지만 이 계정은 만약 해킹을 당하면 내 서버가 삭제당하거나 엄청난 요금 폭탄을 맞을 수 있다.
라고 예전에 학원에 다닐 때 배웠어서, 그대로 실행에 옮겼다.

그래서 나는 용도에 맞춰 IAM(Identity and Access Management) 사용자를 2개로 분리해서 만들었다.

  1. 관리자용 (Console Admin) : 내가 웹사이트(AWS 콘솔)에 로그인해서 서버를 켜고 끌 때 사용하는 계정. 권한 정책은 AdministratorAccess를 받았다.
  2. 시스템용 (System User) : 내 Spring Boot 서버가 S3에 파일을 올릴 때 사용하는 계정.
    여기서 시스템용 계정은 웹사이트 로그인 권한을 아예 없애고, 오직 Access Key만 발급받아 코드에 넣어두었다. 권한 정책은 AmazonS3FullAccess만 받았다. 이 정책은 S3에 파일을 올리고, 지우고, 읽는 모든 권한이다. 이렇게 하면 만약 키가 탈취당하더라도 해커가 콘솔에 로그인해서 깽판 칠 수는 없다.

S3 버킷 생성 (저장소 만들기)

파일을 저장할 바구니(Bucket)를 생성했다.

  • Region : ap-northeast-2(Seoul) - 속도를 위해서 필수
  • 객체 소유권 : ACL 활성화(파일마다 공개 설정을 하기 위함)
  • 퍼블릭 액세스 차단 : 해제
    - 보안 상 막는 게 좋지만, 지금은 프로필 사진처럼 웹에서 누구나 볼 수 있어야 하는 파일을 다루므로 일단 해제했다. (나중에 버킷 정책으로 제어 가능)

Spring Boot 설정

의존성 추가 (build.gradle)

AWS SDK를 직접 쓰는 것 보다, Spring에 맞춰진 Spring Cloud AWS 라이브러리를 사용했다. (버전 3.x)

// AWS S3
implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3'

설정 파일 (application-oauth.properties)

발급받은 시스템용 IAM 계정의 키를 등록한다. 이 파일은 절대 Git에 올리면 안 되어서 이전에 oauth 설정하면서 .gitignore 처리 된 파일에 작성했다.

spring.cloud.aws.credentials.access-key=[발급받은_Access_Key]
spring.cloud.aws.credentials.secret-key=[발급받은_Secret_Key]
spring.cloud.aws.region.static=ap-northeast-2
spring.cloud.aws.s3.bucket=[버킷_이름]

여기에 AWS S3에서 생성한 키와 이름을 등록하면 된다.

S3Service 구현

파일 업로드와 삭제를 담당하는 S3Service를 만들었다. S3Template을 주입받아 사용하면 매우 간단하게 구현할 수 있다.

주요 로직

  1. 파일명 중복 방지 : 사용자가 올린 파일명이 겹칠 수 있으므로, UUID를 붙여서 고유한 파일명(uuid-파일명.jpg)으로 변환한다.
  2. 업로드 : s3Template.upload() 메소드 한 줄이면 끝.
  3. URL 반환 : 업로드된 파일의 S3 접속 URL을 반환한다.
// service/S3Service.java
@Service
@RequiredArgsConstructor
public class S3Service {

    private final S3Template s3Template;

    @Value("${spring.cloud.aws.s3.bucket}")
    private String bucketName;

    public String uploadFile(MultipartFile file, String dirName) {
        // 고유한 파일명 생성 (UUID)
        String originalFilename = file.getOriginalFilename();
        String uuid = UUID.randomUUID().toString();
        String s3Key = dirName + "/" + uuid + "-" + originalFilename;

        try (InputStream inputStream = file.getInputStream()) {
            // S3 업로드
            S3Resource resource = s3Template.upload(bucketName, s3Key, inputStream);
            
            // URL 반환
            return resource.getURL().toString();
        } catch (IOException e) {
            throw new RuntimeException("파일 업로드 실패", e);
        }
    }

    public void deleteFile(String fileUrl) {
        // ... (URL에서 Key 추출 후 삭제 로직)
    }
}

이제 백엔드에서 S3Service.uploadFile()만 호출하면 이미지가 클라우드에 저장되고 URL이 나온다.

0개의 댓글