✅ 개요
이제 실제 프로젝트에서 Spring boot를 통해 S3에 이미지를 업로드하고 수정하는 비즈니스 로직을 구현하는 과정을 진행하겠다.
내 프로젝트에서 S3에 접근하는 로직은 두 가지이다.
1. 회원가입 - 이미지 업로드
2. 프로필 이미지 수정 - 이미지 수정
1️⃣ 회원가입 - 이미지 업로드
- 프론트에서 사용자에게 아이디, 비밀번호, 전화번호, 프로필 이미지를 입력 및 업로드해 백엔드로 해당 내용을 가지고 Post 요청
- 백엔드에서 해당 이미지를 S3에 업로드를 하고
- S3의 URL을 받아옴. 정확히는 S3가 반환해주는 것은 아니다.
하지만 S3의 이미지는 항상 https://{버킷이름}.s3.{리전}.amazonaws.com/{이미지이름}의 형식으로 저장되기 때문에 해당 형태로 직접 문자열을 만들어준다. - 데이터베이스에 회원의 정보(아이디, 비밀번호, 전화번호, S3 URL)을 저장함
- 성공시 200번, 실패시 서버 내부(데이터베이스 or S3)의 오류가 발생한 것이므로 500번 상태 코드를 반환한다.
컨트롤러 계층
@PostMapping("/signup")
public ResponseEntity<String> signUp(@ModelAttribute SignupDTO signupDTO) {
System.out.println("MemberController.signUp");
String memberId = signupDTO.getMemberId();
String password = signupDTO.getPassword();
String phone_num = signupDTO.getPhoneNum();
MultipartFile image = signupDTO.getImage();
Member member = new Member(memberId, password, phone_num);
if(image!= null) s3Service.uploadImage(image, member); // 이미지 업로드
memberService.signup(member); // 회원가입
return ResponseEntity.ok("SIGNUP_SUCCESS");
}
먼저 이미지 업로드를 진행하고 해당 회원정보를 데이터베이스에 저장하는 식으로 구현했다.
서비스 계층
이미지 업로드하는 부분만 보겠다.
@Transactional
public String uploadImage(MultipartFile image, Member member) {
// 기존의 프로필 이미지가 있었다면 삭제
if(member.getProfileImg() != null)
amazonS3.deleteObject(bucket, getImageKey(member.getProfileImg()));
// 파일 확장자 추출
String extension = getImageExtension(image);
String fileName = UUID.randomUUID() + "_" + member.getMemberId() + "_profile" + extension;
// 메타데이터 설정
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(image.getContentType());
metadata.setContentLength(image.getSize());
// S3에 파일 업로드 요청 생성
PutObjectRequest putObjectRequest = null;
try {
putObjectRequest = new PutObjectRequest(bucket, fileName, image.getInputStream(), metadata);
} catch (IOException e) {
log.error(String.valueOf(e.getCause()));
throw new S3ImageUploadException("서버오류: 이미지 업로드에 실패하였습니다.");
}
// S3에 파일 업로드
amazonS3.putObject(putObjectRequest);
// 업로드 후 멤버 객체에 URL 할당
String publicUrl = getPublicUrl(fileName);
member.setProfileImg(publicUrl);
return publicUrl;
}
1. Member객체를 컨트롤러 단에서 전달 받아 기존의 이미지를 삭제한다.
// 기존의 프로필 이미지가 있었다면 삭제
if(member.getProfileImg() != null)
amazonS3.deleteObject(bucket, getImageKey(member.getProfileImg()));
이 부분은 수정을 염두에 두고 추가한 부분인데 당연히 회원가입 경우엔 기존이미지가 없을 테니 이 부분은 건너 뛰어질 것이다.
2. 프론트엔드에서 넘어온 이미지 파일 정보를 통해 파일이름을 생성한다.
// 파일 확장자 추출
String extension = getImageExtension(image);
String fileName = UUID.randomUUID() + "_" + member.getMemberId() + "_profile" + extension;
// ...
private String getImageExtension(MultipartFile image) {
String extension = "";
String originalFilename = image.getOriginalFilename();
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
}
return extension;
}
- 파일이름은 UUID + 회원아이디 + 확장자 조합으로 한다.
3. 이미지 업로드를 위한 메타데이터 설정
// 메타데이터 설정
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(image.getContentType());
metadata.setContentLength(image.getSize());
4. S3에 이미지 업로드
// S3에 파일 업로드 요청 생성
PutObjectRequest putObjectRequest = null;
try {
putObjectRequest = new PutObjectRequest(bucket, fileName, image.getInputStream(), metadata);
} catch (IOException e) {
log.error(String.valueOf(e.getCause()));
throw new S3ImageUploadException("서버오류: 이미지 업로드에 실패하였습니다.");
}
// S3에 파일 업로드
amazonS3.putObject(putObjectRequest);
여기서 amazonS3는 빈으로 등록했던 AmazonS3 객체이다.
@Configuration
public class S3Config {
@Value("${spring.cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${spring.cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${spring.cloud.aws.region.static}")
private String region;
@Bean
public AmazonS3 amazonS3() {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
}
}
2️⃣ 프로필 이미지 변경
- 프론트에서 사용자가 업로드한 이미지를 백엔드에 POST 요청과 함께 보낸다.
- 서버는 세션을 통해 사용자를 판별하고 기존 사용자의 이미지를 S3에서 삭제하고 새로운 이미지를 업로드한다.
- 이후 바뀐 Url을 데이터베이스에 업데이트 한다.
- 성공시 200번 실패시 500번 상태코드를 반환한다.
컨트롤러 계층
@PostMapping("/changeProfile")
public ResponseEntity<String> changeProfileImage(@ModelAttribute ChangeProfileImageDTO imageDTO, HttpSession session) {
MultipartFile image = imageDTO.getImage();
Long login_id = (Long) session.getAttribute("member");
Member memberInfo = memberService.getMemberInfo(login_id);
String newImgUrl = s3Service.uploadImage(image, memberInfo);
return ResponseEntity.ok(newImgUrl);
}
서비스 계층
회원가입 때 사용했던 메서드를 재활용한다.
@Transactional
public String uploadImage(MultipartFile image, Member member) {
// 기존의 프로필 이미지가 있었다면 삭제
if(member.getProfileImg() != null)
amazonS3.deleteObject(bucket, getImageKey(member.getProfileImg()));
// 파일 확장자 추출
String extension = getImageExtension(image);
String fileName = UUID.randomUUID() + "_" + member.getMemberId() + "_profile" + extension;
// 메타데이터 설정
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(image.getContentType());
metadata.setContentLength(image.getSize());
// S3에 파일 업로드 요청 생성
PutObjectRequest putObjectRequest = null;
try {
putObjectRequest = new PutObjectRequest(bucket, fileName, image.getInputStream(), metadata);
} catch (IOException e) {
log.error(String.valueOf(e.getCause()));
throw new S3ImageUploadException("서버오류: 이미지 업로드에 실패하였습니다.");
}
// S3에 파일 업로드
amazonS3.putObject(putObjectRequest);
// 업로드 후 멤버 객체에 URL 할당
String publicUrl = getPublicUrl(fileName);
member.setProfileImg(publicUrl);
return publicUrl;
}
1. 기존 이미지를 지워준다.
// 기존의 프로필 이미지가 있었다면 삭제
if(member.getProfileImg() != null)
amazonS3.deleteObject(bucket, getImageKey(member.getProfileImg()));
2. 저장 로직은 회원가입 때와 일치
3. 영속성 컨텍스트를 이용해 setter를 호출해 이미지 Url을 업데이트 한다.
// 업로드 후 멤버 객체에 URL 할당
String publicUrl = getPublicUrl(fileName);
member.setProfileImg(publicUrl);
'AWS' 카테고리의 다른 글
[AWS 실습 프로젝트] 6. RDS를 통해 MySQL 서버 배포하기 - (1) 안전하고 저렴하게 배포하기 위한 VPC도입 (0) | 2024.09.17 |
---|---|
[AWS 실습 프로젝트] 5. EC2에 스프링부트 백엔드 서버 배포하기 (0) | 2024.09.17 |
[AWS 실습 프로젝트] 3. Spring Boot와 S3 연동해서 이미지 업로드하기 (0) | 2024.09.17 |
[AWS 실습 프로젝트] 2. 이미지 업로드를 위한 AWS S3 인프라 구성 (1) | 2024.09.17 |
[AWS 실습 프로젝트] 1. 프로젝트 개요 (0) | 2024.09.17 |