🍒배경
AWS S3는 객체를 고유한 키(Key) 값으로 관리하는 스토리지 서비스입니다. 개발을 하다보면 특정 버킷 안에 지정된 키 값을 가진 객체가 실제로 존재하는지 확인해야 하는 경우가 발생합니다. 예를 들어, 업로드가 정상적으로 완료되었는지 검증하거나 다운로드 전에 객체 존재 여부를 확인하는 경우 등이 있습니다.
이는 HeadObject를 사용하여 쉽게 확인할 수 있습니다. 객체의 실제 데이터를 가져오지 않고 해당 객체의 메타데이터(크기, 수정 시각, ETag 등)만 반환하기 때문에 가볍게 존재 여부를 확인할 수 있습니다.
🍒사전 준비
🌱의존성 주입
implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.0.1")
implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3'
🌱S3Config
package com.alphano.alphano.common.config;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
@Configuration
@RequiredArgsConstructor
@Slf4j
public class S3Config {
@Value("${spring.cloud.aws.region.static}")
private String awsRegion;
private S3Client s3Client;
private S3Presigner s3Presigner;
@Bean
public S3Client amazonS3() {
this.s3Client = S3Client.builder()
.region(Region.of(awsRegion))
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
return this.s3Client;
}
@Bean
public S3Presigner amazonS3Presigner() {
this.s3Presigner = S3Presigner.builder()
.region(Region.of(awsRegion))
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
return this.s3Presigner;
}
@PreDestroy
public void shutdown() {
try {
if (s3Presigner != null) s3Presigner.close();
if (s3Client != null) s3Client.close();
log.info("S3 SDK 자원 정상 해제 완료");
} catch (Exception e) {
log.warn("S3 SDK 자원 해제 실패", e);
}
}
}
🍒코드 작성
공식문서를 참고하여 작성하였습니다.
아래는 공식문서의 코드입니다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Exception;
import java.nio.file.Path;
import static com.example.s3.util.S3DirectoryBucketUtils.createDirectoryBucket;
import static com.example.s3.util.S3DirectoryBucketUtils.createS3Client;
import static com.example.s3.util.S3DirectoryBucketUtils.deleteAllObjectsInDirectoryBucket;
import static com.example.s3.util.S3DirectoryBucketUtils.deleteDirectoryBucket;
import static com.example.s3.util.S3DirectoryBucketUtils.getFilePath;
import static com.example.s3.util.S3DirectoryBucketUtils.putDirectoryBucketObject;
/**
* Retrieves metadata for an object in the specified S3 directory bucket.
*
* @param s3Client The S3 client used to interact with S3
* @param bucketName The name of the directory bucket
* @param objectKey The key (name) of the object to retrieve metadata for
* @return True if the object exists, false otherwise
*/
public static boolean headDirectoryBucketObject(S3Client s3Client, String bucketName, String objectKey) {
logger.info("Retrieving metadata for object: {} from bucket: {}", objectKey, bucketName);
try {
// Create a HeadObjectRequest
HeadObjectRequest headObjectRequest = HeadObjectRequest.builder()
.bucket(bucketName)
.key(objectKey)
.build();
// Retrieve the object metadata
HeadObjectResponse response = s3Client.headObject(headObjectRequest);
logger.info("Amazon S3 object: \"{}\" found in bucket: \"{}\" with ETag: \"{}\"", objectKey, bucketName,
response.eTag());
logger.info("Content-Type: {}", response.contentType());
logger.info("Content-Length: {}", response.contentLength());
logger.info("Last Modified: {}", response.lastModified());
return true;
} catch (S3Exception e) {
logger.error("Failed to retrieve object metadata: {} - Error code: {}", e.awsErrorDetails().errorMessage(),
e.awsErrorDetails().errorCode(), e);
return false;
}
}
해당 코드는 디렉터리 버킷에 objectKey를 가진 객체가 존재할 경우, 객체의 메타데이터를 로그로 출력한 다음 true 반환합니다. 객체가 존재하지 않는다면 S3Exception이 발생하고 false를 반환합니다.
- `HeadObjectRequest` : Amazon S3와 같은 서비스에서 객체의 메타데이터(예: 크기, 마지막 수정 날짜, ETag)를 가져오기 위해 사용되는 요청 객체
- `HeadObjectResponse` : Amazon S3와 같은 서비스에서 HeadObjectRequest를 실행한 결과로 반환되는 응답 객체. 해당 객체의 메타데이터와 HTTP 헤더 정보를 담고 있음.
메타데이터는 필요하지 않고 지정된 키 값을 가진 객체가 존재하는지 확인하는 것이기 때문에 코드를 리팩토링하겠습니다.
🌟최종 코드
@Service
@RequiredArgsConstructor
public class S3Service {
private final S3Client s3Client;
public boolean objectExists(String bucketName, String objectKey) {
try {
HeadObjectRequest headObjectRequest = HeadObjectRequest.builder()
.bucket(bucketName)
.key(objectKey)
.build();
s3Client.headObject(headObjectRequest);
return true;
} catch (S3Exception e) {
return false;
}
}
}
- 메서드명을 직관적으로 변경했습니다.
- 기존에 인자로 전달받던 `s3Client`는 의존성 주입 방식으로 변경했습니다.
- 메타데이터가 필요하지 않으므로 `HeadObjectResponse` 객체를 별도로 받지 않고 단순히 호출만 수행하도록 수정했습니다.
관련 글:
2025.06.15 - [SPRING/HOW-TO] - Presigned URL로 S3 프라이빗 버킷 이미지 조회하기 (SDK v2)
2025.06.22 - [SPRING/HOW-TO] - Presigned URL로 S3 이미지 업로드 및 삭제 기능 구현하기 (SDK 2)
'SPRING > HOW-TO' 카테고리의 다른 글
| Presigned URL로 S3 이미지 업로드 및 삭제 기능 구현하기 (SDK 2) (5) | 2025.06.22 |
|---|---|
| SSH 터널링으로 프라이빗 RDS에 MySQL Workbench 연결하기 (3) | 2025.06.18 |
| Presigned URL로 S3 프라이빗 버킷 이미지 조회하기 (SDK v2) (2) | 2025.06.15 |
