진행 중인 프로젝트의 마무리 단계에서 테스트 중 에러가 발생했다.
새로운 습관을 등록할 때 이미지를 등록하게 되는데 이 과정에서 S3과의 연결 시간 초과로 요청을 처리할 수 없다는 것이다. 분명 문제없이 잘되던 작업이었기 때문에 멘붕이었다.
c.s.f.storage.AmazonS3ResourceStorage : Unable to execute HTTP request: Timeout waiting for connection from pool
하지만 에러 로그를 따라 읽어보니 쉽게 발생지를 찾을 수 있었다. 얼마 전에 업로드된 S3 Object 정보를 읽어 오기 위해서 코드를 추가했었다.
out.println(amazonS3Client.getObject(bucket, fullPath).toString());
바로 이 코드가 원인이었다. 왜 이 코드가 원인일까?
@Slf4j
@Component
@RequiredArgsConstructor
public class AmazonS3ResourceStorage {
private final AmazonS3Client amazonS3Client;
@Value("${cloud.aws.s3.bucket}")
private String bucket;
@Value("${cloud.aws.region.static}")
private String region;
/**
* S3로 파일을 업로딩하는 메서드입니다.
*/
public String store(String fullPath, MultipartFile multipartFile) {
File file = new File(MultipartUtil.getLocalHomeDirectory(), fullPath);
try {
multipartFile.transferTo(file);
createThumbnail(fullPath, file);
amazonS3Client.putObject(new PutObjectRequest(bucket, fullPath, file)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (Exception e) {
log.error(e.getMessage());
throw new BusinessLogicException(ExceptionCode.FAILED_TO_UPLOAD_FILE);
} finally {
if (file.exists()) removeNewFile(file);
}
out.println(amazonS3Client.getObject(bucket, fullPath).toString()); // 바로 이 코드가 문제였다!
return amazonS3Client.getUrl(bucket, fullPath).toString().replaceFirst("s", "");
}
원인
amazonS3Client.getObject(bucket, fullPath) 는 S3Object 객체다.
- S3Object 클래스는 Closable 인터페이스를 구현하고 있다.
- 또한 S3Object 는 HTTP Connection으로부터 데이터를 스트리밍 할 수 있는 S3ObjectInputStream 을 포함하고 있다.
- S3Object 를 조회하기 위해 getObject() 메서드를 호출하면 HTTP Connection이 계속 열려있기 때문에 스트림을 조회한 후 HTTP 연결이 종료될 수 있도록 닫아주어야 한다.
- 즉, getObject() 메서드를 호출한 후 스트림을 닫아주어야 한다.
자세한 내용은 아래 링크를 참고하자
Closeable S3Objects | Amazon Web Services
Closeable S3Objects | Amazon Web Services
The com.amazonaws.services.s3.model.S3Object class now implements the Closeable interface (AWS SDK for Java 1.4.8 onwards). This allows you to use it as a resource in a try-with-resources statement. S3Object contains an S3ObjectInputStream that lets you st
aws.amazon.com
해결
해결 방법은 크게 2가지 방법이 있다.
1. clsoe()
간단하다. 직접 닫아주면 된다.
S3Object s3Object = amazonS3Client.getObject(bucket, fullPath);
out.println(s3Object.toString());
s3Object.close();
2. try-with-resources
생소한 문법이다. try-catch는 알아도 try-with-resource는 처음 들어봤다.
try-with-resource는 자바7에 새롭게 추가된 기능으로서, 리소스를 다 사용하고 나면 자동으로 반납(close) 해주는 기능이다.
try-with-resource를 사용해서 getObject()를 닫아주자
try (S3Object s3Object = amazonS3Client.getObject(bucket, fullPath)) {
out.println(s3Object.toString());
} catch (Exception e) {
System.out.println("Unable to get object from Amazon S3: " + e);
}
이렇게 되면 알아서 getObject()를 조회한 후 자동으로 반납해 준다.
The try-with-resources Statement (The Java™ Tutorials >
Essential Java Classes > Exceptions)
The try-with-resources Statement (The Java™ Tutorials > Essential Java Classes > Exceptions)
The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated
docs.oracle.com
마무리
하지만 난 위 두 가지 방법을 사용하지 않았다. 임시로 넣었던 코드였기 때문에 주석처리로 마무리했다.
예상치 못한 에러로 try-with-resource라는 새로운 기능을 알게 되어서 기분이 좋다.
다음에 BufferedReader나 S3Objecgt를 사용할 때 이 기능을 적용해 봐야겠다.
'나의 에러 일지' 카테고리의 다른 글
Spring Boot - Swagger(3.0.0) 적용 시 발생하는 오류 원인과 해결 방법 (0) | 2023.03.20 |
---|---|
Spring - MissingServletRequestPartException 원인과 해결법 (1) | 2023.03.20 |
Spring - MaxUploadSizeExceededException 원인과 해결 방법 (0) | 2023.03.18 |
Git - .gitignore가 작동되지 않을 때 해결 방법 (1) | 2023.02.16 |
Java - UnsupportedOperationException 원인과 해결법 (0) | 2023.02.09 |