진행 중인 프로젝트의 마무리 단계에서 테스트 중 에러가 발생했다.
새로운 습관을 등록할 때 이미지를 등록하게 되는데 이 과정에서 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
해결
해결 방법은 크게 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)
마무리
하지만 난 위 두 가지 방법을 사용하지 않았다. 임시로 넣었던 코드였기 때문에 주석처리로 마무리했다.
예상치 못한 에러로 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 |