[ Spring + AWS S3 ] 이미지 업로드 / 다운로드 구현하기

Dev_ch·2022년 11월 4일
0
본 글은 2022.10.27에 작성되었습니다.

최근에 이미지를 업로드 및 다운로드를 해야 할 일이 생겨서 구현하게 되었는데 로컬로 진행하던 과정이어서 컴퓨터 내부에 파일이 저장되게끔 유도했었다. 하지만 EC2를 이용한 배포와 RDS를 사용함에 따라 원격 저장소가 필요하게 됐고 자연스럽게 S3를 선택하게 되었다. 이미지를 업로드하고 다운로드하는 코드를 짜는 건 어렵지 않다. 그리고 다운되는 곳을 로컬이 아닌 S3로 저장되게끔 유도만 해주면 되기 때문에 사실 간단한 서비스 구현이 될 수 있다.

1. 준비사항


  1. S3 버킷 생성해놓기
  2. IAM 사용자 권한 추가시키기 (IAM 사용자 등록을 안 했다면 생성)

2. 진행


이미지 업로드 / 다운로드 로직
1. form-data 형식으로 file을 request 받음
2. 이미지가 저장될 이름과 경로를 설정
3. 저장
4. 이미지 다운로드 & 이미지 화면에 노출

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

첫번째로 build.gradle에 위와 같은 dependency를 추가해준다.

application.properties

cloud.aws.credentials.accessKey = IAM Access Key ID 입력
cloud.aws.credentials.secretKey = IAM Secret Key 입력
cloud.aws.s3.bucket = 자신이 만든 버킷 이름
cloud.aws.region.static = ap-northeast-2
cloud.aws.stack.auto = false

S3를 사용하기 위해 위와 같이 properties파일에 설정해준다. 액세스 키와 시크릿키는 보안이 강조되는 사항이기 때문에 꼭 외부로 노출되지 않게끔 하자.

S3 Config.java

@Configuration
public class S3Config {

    @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
    public AmazonS3 amazonS3Client() {
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
        return AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(region)
                .build();
    }
}

S3를 사용하기 위해 config 클래스를 구현해주었다.

1. Controller Class

Controller에서는 Put을 통해 MultpartFile을 이용하여서 file이라는 이름으로 request 받게끔 명시해주었다.

2. Service Class

Service에서는 profile_image_name으로 전송할 파일의 이름을 설정해주었는데 필자의 경우 JWT에서 꺼내온 유저 정보 중 고윳값인 이메일을 이미지 이름으로 설정해주었다. 처음 "profile/"은 S3에 만들어놓은 폴더에 이미지를 업로드하기 위해서 붙여놓았다. 이미지 이름에 "/"가 들어가 있으면 그 전의 이름들은 경로로 지정이 된다.

ObjMetadata를 설정하지 않으면 아래와 같은 Exception이 발생하니 꼭 설정해주도록 하자.

c.amazonaws.services.s3.AmazonS3 Client : No content length specified for stream data.

위와 같이 구현하면 S3에 정상적으로 업로드될 것이다.

.profileImagePath(amazonS3Client.getUrl(bucket, profile_image_name).toString())

필자의 경우 builder를 통해 DB에 save 해줄 때 위의 코드를 통해 S3에 저장된 url을 저장해주었다. 이 url로 외부에서 파일을 접근할 수 있다. 실제로 url을 창에 입력해보면 파일이 다운로드되며 front-end에서 이미지 태그 등을 통해 홈페이지, 또는 앱에서 바로 이미지를 노출시킬 수 있다.

테스트를 해본 결과 파일 이름이 이메일로 S3 내부 경로 profile에 잘 저장된 것을 확인할 수 있다. 만약 이미지를 저장할 때 고유번호가 필요하다면 UUID 객체를 하나 생성하여 붙여주도록 하자.

위와 같이 UUID 객체를 통해 고유번호를 랜덤 값으로 생성하고 mycloset이라는 경로에 이미지 이름을 지정받은 랜덤 값으로 저장하게끔 바꾸어 줄 수 있다.

3. 해당 기능을 구현하면서 발생한 오류들


1. S3/버킷 권한 오류

구현 완료 후 저장된 이미지의 url을 가지고 창에 입력했을 경우 오류 -> AWS 계정과 버킷의 권한 설정이 public이 아니기 때문에 발생하는 오류이다.

해결 : 아래의 사이트를 참조해 버킷 권한 부분을 확인해 권한을 수정해주도록 하자.
권한 오류 해결

2. SdkClientException

위의 경우 Exception이 발생하나 서버가 잘 돌아가는 것을 알 수 있다. 문제는 EC2 환경에서 서버를 가동하지 않아서 발생하는 것인데 사실 수정할 사항 없이 그냥 가동해도 된다. 다만 가동할 때마다 해당 Excpeiton이 발생하는 것이 거슬린다면

logging.level.com.amazonaws.util.EC2MetadataUtils = error

application.properties에 위의 코드를 한 줄 적어주면 사라지게 된다.

4. 도움이 된 블로그


https://doing7.tistory.com/45
https://devlog-wjdrbs96.tistory.com/323
https://inpa.tistory.com/entry/AWS
https://lannstark.tistory.com/71

profile
내가 몰입하는 과정을 담은 곳

0개의 댓글