서비스를 만들면 꼭 필요한 기능. 바로 이미지를 업로드하는 것입니다.
Sluv의 1차 MVP 당시 이미지 업로드는 S3에 접속 정보를 프론트가 관리했고, 프론트에서 이미지를 업로드 한 뒤, S3 주소를 서버에게 전달해주는 구조였습니다.
위와 같은 방식으로 관리를 할 때 프론트에서 S3의 접속 정보를 관리하기 때문에 서버에서 관리하는 것보다 보안적인 측면에서 약하다는 문제가 있었습니다.
기존의 방식을 버리고, S3의 접속 정보를 서버에서 관리하며, 프론트에서 서버로 이미지 파일을 전달해주는 방식으로 해결하였습니다.
하지만 또 다른 문제점이 발생했습니다.
Sluv의 서비스는 사진이 매우매우 중요하며, 또한 한번의 요청에 많이 사진이 저장되어야 하는 구조입니다.
때문에 위 개선된 방식을 이용할 시, 프론트에서 서버로 사진파일을 전달하기 때문에 속도적인 측면에서 저하가 발생할 수 있고, 이는 대용량 트래픽이 발생한다면 치명적일 수 있습니다.
이런 문제를 PreSigned Url로 해결하였습니다.
S3의 접근 권한에 대한 인증을 마치면 S3에 업로드 할 수 있는 URL을 발급해 주는데, 이 URL을 Presigned URL이라고 합니다.
발급 받은 PreSigned URL을 이용하면 브라우저에서 AWS S3 버킷에 바로 파일을 업로드 할 수 있습니다.
이것을 이용해서 프론트가 요청 하면 서버가 Presigned URL을 프론트로 발급해주고, 발급 받은 Presigned URL로 프론트는 이미지를 업로드 한 뒤, S3 주소를 서버에게 전달하는 방식입니다.
@Configuration
public class S3Config {
@Value("${aws.s3.accessKey}")
private String accessKey;
@Value("${aws.s3.secretKey}")
private String secretKey;
@Value("${aws.s3.region}")
private String region;
@Bean
public AmazonS3 getS3ClientBean() {
AWSCredentials credentials = new BasicAWSCredentials(this.accessKey, this.secretKey);
return AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(Regions.AP_NORTHEAST_2)
.build();
}
}
S3의 인증을 얻는 과정입니다.
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PreSingedUrlResDto {
private String preSignedUrl;
private String key;
}
@Getter
public enum ImgExtension {
JPG("jpeg"),
JPEG("jpeg"),
PNG("png");
ImgExtension(String uploadExtension) {
this.uploadExtension = uploadExtension;
}
private final String uploadExtension;
}
@Service
@Slf4j
@RequiredArgsConstructor
public class AWSS3Service {
private final AmazonS3 amazonS3;
@Value("${aws.s3.bucketName}")
private String bucketName;
@Value("${aws.s3.baseUrl}")
private String baseUrl = "";
public PreSingedUrlResDto getPreSignedUrl(ImgExtension imgExtension) {
String fixedExtension = imgExtension.getUploadExtension();
String fileName = baseUrl + UUID.randomUUID() + "." + fixedExtension;
log.info(fileName);
URL url =
amazonS3.generatePresignedUrl(
getGeneratePreSignedUrlRequest(bucketName, fileName, fixedExtension));
return PreSingedUrlResDto.builder()
.preSignedUrl(url.toString())
.key(fileName)
.build();
}
/**
* PreSigned URL 생성
*/
private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String bucket, String fileName, String imgExtension) {
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(bucket, fileName)
.withMethod(HttpMethod.PUT)
.withKey(fileName)
.withContentType("image/" + imgExtension)
.withExpiration(getPreSignedUrlExpiration());
generatePresignedUrlRequest.addRequestParameter(
Headers.S3_CANNED_ACL,
CannedAccessControlList.PublicRead.toString());
return generatePresignedUrlRequest;
}
/**
* PreSigned URL의 유효기간 설정
*/
private Date getPreSignedUrlExpiration() {
Date expiration = new Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += 100000 * 60 * 2;
expiration.setTime(expTimeMillis);
log.info(expiration.toString());
return expiration;
}
}
스럽 서버 멀티모듈 전환기 (0) | 2024.09.30 |
---|---|
정보 공유 게시글의 조회 성능을 개선해보자! (1) | 2024.01.22 |
비동기 처리를 통해 검색 성능을 개선해보자! (0) | 2023.10.07 |
AOP!! Exception을 잡아줘!! (0) | 2023.04.29 |
카카오, 구글, 애플. 우리는 Sluv으로 만난다! (0) | 2023.04.29 |
댓글 영역