Spring S3 연동

홍준식·2024년 5월 12일

S3 연동을 위해 build.gradle에 aws sdk를 추가한다.

dependencies {
	implementation 'com.amazonaws:aws-java-sdk-s3:1.12.715'
	...
}

aws에서 엑세스 키를 발급받은 후에 application.yml에 저장한다.

aws:  
  s3:  
    access-key: {엑세스 키}  
    secret-access-key: {시크릿 엑세스 키}
    bucket: {버킷 명}
package com.palindrome.studit.global.config.aws;  
  
import com.amazonaws.auth.AWSStaticCredentialsProvider;  
import com.amazonaws.auth.BasicAWSCredentials;  
import com.amazonaws.regions.Regions;  
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 {  
    @Value("${aws.s3.access-key}")  
    private String accessKey;  
  
    @Value("${aws.s3.secret-key}")  
    private String secretKey;  
  
    @Bean  
    AmazonS3 amazonS3Client() {  
        return AmazonS3ClientBuilder.standard()  
                .withCredentials(  
                    new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey))  
                )  
                .withRegion(Regions.AP_NORTHEAST_2)  
                .build();  
    }  
}

위와 같이 S3Config를 설정하여 외부에서 amazonS3Client 빈을 주입받을 수 있도록 하였다.

private final AmazonS3 amazonS3Client;
@Value("${aws.s3.bucket") private String bucketName;

public void updateImage(MultipartFile file) throws IOException {  
    ObjectMetadata objectMetadata = new ObjectMetadata();  
    objectMetadata.setContentLength(file.getSize());  
    objectMetadata.setContentType(file.getContentType());  
  
    PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, file.getName(), file.getInputStream(), objectMetadata);  
  
    amazonS3Client.putObject(putObjectRequest);  
}

위와 같이 주입받은 amazonS3Client를 통하여 이미지를 생성할 수 있다.

@PostMapping("/image")  
public ResponseEntity<Object> uploadImage(@RequestPart("file") MultipartFile file) throws IOException {  
    imageService.updateImage(file);  
    return ResponseEntity.status(HttpStatus.CREATED).build();  
}

위와 같이 전달받은 파일을 그대로 저장할 수 있다.

하지만 DB에서 해당 파일 URL을 알고 접근하는 것이 필요할 수 있다.

@entity
@Getter
@Builder
public class Study {
	@Id  
	@GeneratedValue(strategy = GenerationType.IDENTITY)  
	private Long studyId;

	@Size(max = 255)  
	private String studyImage;
}

위와 같이 Study 엔티티에 studyImage 필드가 있다.
이미지 명은 중요하지 않고 중복되면 안되기에 unique 옵션을 추가하였다.

@entity
@Getter
@Builder
public class Study {
	@Id  
	@GeneratedValue(strategy = GenerationType.IDENTITY)  
	private Long studyId;

	@Size(max = 255)  
	@Column(unique = true)
	private String studyImage;

	public static String generateRandomStudyImageName() {  
    return "study/images/" + UUID.randomUUID().toString();  
}  
  
	public void updateStudyImage(String fileName) {  
	    this.studyImage = fileName;  
	}
}
public class StudyService {
	private final StudyRepository studyRepository;
	private final AmazonS3 amazonS3Client;  
	@Value("${aws.s3.bucket") private String bucketName;

	@Transactional
	public void uploadStudyImage(Long studyId, MultipartFile file) throws IOException {
		Study study = studyRepository.findById(studyId).orElseThrow();
		
		study.updateStudyImage(Study.generateRandomStudyImageName());
		ObjectMetadata objectMetadata = new ObjectMetadata();  
		objectMetadata.setContentLength(file.getSize());  
		objectMetadata.setContentType(file.getContentType());  
		  
		PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, file.getInputStream(), objectMetadata);  
		  
		amazonS3Client.putObject(putObjectRequest);
	}

	public String getStudyImageUrl(Long studyId) {  
	    Study study = studyRepository.findById(studyId).orElseThrow();  
	    return amazonS3Client.getUrl(bucketName, study.getStudyImage()).toString();  
	}

위와 같이 스터디 이미지 변경 로직을 작성하여 DB에 이미지 URL을 저장하거나 저장된 URL을 파싱할 수 있다.

0개의 댓글