Paging
이건 그냥 전에 했던 Myselectshop 보고 따라했다.
S3 Bucket
S3(Simple Stroage Service)
AWS에서 제공하는 클라우드 객체 스토리지 서비스, S3 = 클라우드/ Bucket = 폴더(디렉토리) 개념
1) S3 Bucket 만들기
이하 링크 참고함. 매우 자세하게 써주셔서 걍 따라했음.
https://velog.io/@jinseoit/AWS-S3-bucket
AWS - S3 사용하기 (버킷 만들기)
S3는 AWS(Amazon Web Service)에서 제공하는 인터넷 스토리지 서비스입니다. S3(Simple Storage Service) 를 뜻합니다.높은 내구도를 자랑하며 정보를 안전하게 저장 할 수 있습니다.저렴한 비용으로 사용이 가
velog.io
1️⃣ 버킷 만들기
2️⃣ 버킷 생성 확인
3️⃣ 이미지 업로드
4️⃣ 이미지 확인을 위해 URL 클릭하면 AccessDenied
5️⃣ 퍼블릭 액세스 차단 수정 > 체크 모두 해제 (다 읽고 따라할 걸 ㅇㅅㅇ)
6️⃣ 버킷 정책 편집
{
"Id": "Policy1739000001061",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1730008880433",
"Action": [
"s3:GetObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::tenten-s3-bucket/*",
"Principal": "*"
}
]
}
7️⃣ 다시 확인하니까 사진 잘 확인됨.
2) IAM 사용자 생성
https://galid1.tistory.com/590
AWS SDK - Java에서 AWS S3 사용하는 법
1. Guide 이번 포스팅에서는 JAVA에서 AWS의 S3 스토리지를 이용하는 방법을 알아 보도록하겠습니다. 1. GuideJava AWS SDK 가이드https://docs.aws.amazon.com/ko_kr/sdk-for-java/v1/developer-guide/aws-sdk-java-dg.pdf위의 링크
galid1.tistory.com
3) 액세스 키 만들기
4) 파일 업로드 해보기
build.gradle
// AWS SDK S3
implementation platform('software.amazon.awssdk:bom:2.20.56')
implementation 'software.amazon.awssdk:s3'
application.yml
aws:
access:
key:
id: ${AWS_ACCESS_KEY_ID}
secret:
access:
key: ${AWS_SECRET_ACCESS_KEY}
region: ${AWS_REGION}
s3:
bucket:
name: ${BUCKET_NAME}
S3Config
@Configuration
public class S3Config {
@Value("${aws.access.key.id}")
private String accessKeyId;
@Value("${aws.secret.access.key}")
private String secretAccessKey;
@Value("${aws.region}")
private String region;
@Bean
public S3Client s3Client() {
// AWS 자격 증명 생성
AwsBasicCredentials awsCredentials = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
// S3Client 빌더에서 자격 증명과 리전 설정
return S3Client.builder()
.region(Region.of(region)) // AWS 리전 설정
.credentialsProvider(StaticCredentialsProvider.create(awsCredentials)) // 자격 증명 추가
.build();
}
}
S3 Service
package com.sparta.tentenbackend.global.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import java.io.IOException;
import java.util.UUID;
@Service
public class S3Service {
private final S3Client s3Client;
private final String bucketName;
private final String region;
public S3Service(S3Client s3Client, @Value("${aws.s3.bucket.name}") String bucketName, @Value("${aws.region}") String region) {
this.s3Client = s3Client;
this.bucketName = bucketName;
this.region = region;
}
// 파일 업로드
public String uploadFile(MultipartFile file) throws IOException {
String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.contentType(file.getContentType())
.acl(ObjectCannedACL.PUBLIC_READ) // 공개 접근 가능 설정
.build();
s3Client.putObject(putObjectRequest, RequestBody.fromBytes(file.getBytes()));
return getFileUrl(fileName);
}
// 파일 수정 (대체 업로드)
public String updateFile(String fileName, MultipartFile file) throws IOException {
if (!fileExists(fileName)) {
throw new IllegalArgumentException("File does not exist: " + fileName);
}
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.contentType(file.getContentType())
.acl(ObjectCannedACL.PUBLIC_READ)
.build();
s3Client.putObject(putObjectRequest, RequestBody.fromBytes(file.getBytes()));
return getFileUrl(fileName);
}
// 파일 삭제
public void deleteFile(String fileName) {
if (!fileExists(fileName)) {
throw new IllegalArgumentException("File does not exist: " + fileName);
}
System.out.println(fileName);
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.build();
s3Client.deleteObject(deleteObjectRequest);
}
// 파일 존재 여부 확인
private boolean fileExists(String fileName) {
try {
s3Client.headObject(HeadObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.build());
return true;
} catch (S3Exception e) {
System.out.println("파일 존재 확인 실패: " + e.awsErrorDetails().errorMessage());
return false;
}
}
// S3 파일 URL 반환
private String getFileUrl(String fileName) {
return "https://" + bucketName + ".s3." + region + ".amazonaws.com/" + fileName;
}
}
S3Controller - 테스트용
package com.sparta.tentenbackend.global;
import com.sparta.tentenbackend.global.service.S3Service;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/s3")
public class S3Controller {
private final S3Service s3Service;
public S3Controller(S3Service s3Service) {
this.s3Service = s3Service;
}
// 파일 업로드
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
try {
String url = s3Service.uploadFile(file);
return ResponseEntity.ok("File uploaded successfully: " + url);
} catch (Exception e) {
return ResponseEntity.status(500).body("Failed to upload file: " + e.getMessage());
}
}
// 파일 수정 - 파일을 대체하는 거라 파일명이 바뀌진 않음.
@PutMapping("/update/{fileName}")
public ResponseEntity<String> updateFile(@PathVariable String fileName, @RequestParam("file") MultipartFile file) {
try {
String url = s3Service.updateFile(fileName, file);
return ResponseEntity.ok("File updated successfully: " + url);
} catch (Exception e) {
return ResponseEntity.status(500).body("Failed to update file: " + e.getMessage());
}
}
// 파일 삭제
@DeleteMapping("/delete/{fileName}")
public ResponseEntity<String> deleteFile(@PathVariable String fileName) {
try {
s3Service.deleteFile(fileName);
return ResponseEntity.ok("File deleted successfully: " + fileName);
} catch (Exception e) {
return ResponseEntity.status(500).body("Failed to delete file: " + e.getMessage());
}
}
}
테스트 해봤는데 수정, 삭제가 안되길래 버킷에 권한 DeleteObject, PutObject 추가로 설정하고 뻘짓을 다 해봤는데ㅋㅋㅋㅋㅋㅋㅋ
1) @PathVariable을 별 생각없이 @RequestParam으로 생각해버림.
2) UUID+파일명으로 저장되어야 하는데 그것도 안되고 수정, 삭제하려는 파일명(key)도 못찾음.
이런 문제로... 결국 튜터님에게 찾아감.
객체 소유권을 객체 라이터로 변경하지 않아서 생긴 문제였다. 개허무결말 ㅎㅎ
5) 결과
S3Service에서 메서드 가져다 쓰면 됨.
'TIL' 카테고리의 다른 글
TIL8. AI API (0) | 2025.02.20 |
---|---|
TIL7. S3 Bucket 적용, DTO Validation (0) | 2025.02.19 |
TIL5. 카테고리 API 마무리 + Review API 만들기 (2) | 2025.02.17 |
TIL4. PR, PostgreSQL, .env, 카테고리 CRUD (0) | 2025.02.14 |
TIL3. Git Flow (2) | 2025.02.13 |