Skip to content

Commit

Permalink
[Feat(멜리/윤채은)] AWS S3
Browse files Browse the repository at this point in the history
ReviewImage 여러 장 업로드
  • Loading branch information
melitina915 committed Jul 14, 2024
1 parent 6ff8af3 commit 93f1b58
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 14 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// 우선 S3에 업로드,
// 즉 외부 API를 사용하기 위해 아래의 의존성을
// build.gradle에 추가 해줍시다.
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}

tasks.named('test') {
Expand All @@ -56,6 +60,7 @@ tasks.named('test') {



// 11주차
jar{
enabled = false
}
93 changes: 93 additions & 0 deletions src/main/java/umc/spring/aws/s3/AmazonS3Manager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package umc.spring.aws.s3;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import umc.spring.config.AmazonConfig;
import umc.spring.domain.Uuid;
import umc.spring.repository.UuidRepository;

import java.io.IOException;

// 12주차 사진도 같이 업로드하기

// 이제 다음으로 AWS S3에 사진을 업로드 하는 매서드를 가진
// AmazonS3Manager라는 클래스를 만들어서 사용합시다.
// S3 말고도 다른 AWS의 서비스를 사용 할 수 있으니
// aws라는 폴더에 s3라는 폴더를 만들고
// 하위에 AmazonS3Manager를 만들어 두겠습니다.
@Slf4j
@Component
@RequiredArgsConstructor
public class AmazonS3Manager {

private final AmazonS3 amazonS3;
// amazonS3는 저희가 만들 필요는 없고 build.gradle에서
// amazon 관련 외부 모듈을 받으면 사용이 가능합니다.
// 해당 amazonS3가 제공하는 메서드를 사용하면 됩니다.
private final AmazonConfig amazonConfig;
// amazonConfig는 S3를 사용하기 위해서 필요한
// 인증에 대한 과정 등이 포함이 됩니다.
// 인증이 되었다는 것을 가지고 AmazonS3를 사용해서
// 파일을 업로드하면 됩니다
private final UuidRepository uuidRepository;

// AWS S3를 사용해서 파일을 업로드하고 그 결과로
// 우리가 필요한 것은 파일의 URL입니다.
// uploadFile은 내부적으로 Amazon이 제공하는
// putObject 메서드를 사용 할 것입니다.
public String uploadFile(String KeyName, MultipartFile file){

System.out.println(KeyName);

ObjectMetadata metadata = new ObjectMetadata();
// metaData는 필수는 아니고 추가적인 정보를 담아주는 것입니다.
metadata.setContentLength(file.getSize());

try {

amazonS3.putObject(new PutObjectRequest(amazonConfig.getBucket(), KeyName, file.getInputStream(), metadata));
// putObject 메서드는 PutObjectRequest를
// 파라미터로 받아 S3 버킷에 저장합니다.
// amazonS3의 putObject 매서드에 파라미터로
// PutObjectReqeust를 담는 것을 확인 가능하며,
// PutObjectRequest를 만들 때,
// 어떤 S3의 버킷에 올릴지, 식별 할 이름은 무엇인지,
// 그리고 파일의 데이터는 무엇인지를 담죠.
// keyName과 file의 경우는 당연히
// 해당 매서드를 호출할 서비스 계층에서 담아줍니다!

// 이제 저희가 S3에 사진을 업로드 할 때
// 이렇게 요청을 해야합니다.
// 어떤 버킷의 특정 디렉토리에 이런 식별자로
// 업로드 해줘!
// amazonConfig.getBucket()
// 어떤 버킷인지 가져온다
// KeyName
// 어떤 디렉토리의 어떤 식별자인지 KeyName으로 지정한다

}catch (IOException e){

log.error("error at AmazonS3Manager uploadFile : {}", (Object) e.getStackTrace());

}

return amazonS3.getUrl(amazonConfig.getBucket(), KeyName).toString();
// 우리는 getUrl 메서드를 이용해서 버킷에 저장된
// 파일의 URL을 받아서 최종적으로 return합니다.

}

// 이제 KeyName을 만들어서 리턴 해주는 매서드를
// Manager에 추가해봅시다!
public String generateReviewKeyName(Uuid uuid){

return amazonConfig.getReviewPath() + '/' + uuid.getUuid();

}

}
86 changes: 86 additions & 0 deletions src/main/java/umc/spring/config/AmazonConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package umc.spring.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 12주차 사진도 같이 업로드하기

// 이제 AWS S3에 사진을 업로드 하기 위한
// 설정 정보가 담긴 클래스를 만들어 줍시다.
// 해당 클래스는 순수하게 AWS의 서비스를 사용하기 위한
// 설정 정보가 담기게 됩니다.
// 11주차 CI/CD에서도 AWS는 보안에 굉장히 민감하고
// 외부에서 AWS의 서비스를 사용하기 위해
// Access Key와 Secret Key를 발급했죠?
// 그런 정보를 담아 둘 클래스라고 생각하면 됩니다.
@Configuration
@Getter
public class AmazonConfig {

private AWSCredentials awsCredentials;
// 이제 저 키를 어디에 보관 해야할지가 문제이죠
// CI/CD에서는 깃허브 액션에서 사용이 되니
// 리포지토리의 키에 저장을 했죠?
// 지금은 어떻게 할까요?
// 여기서 환경변수라는 개념을 활용 할 수 있습니다.
// 일단 무식하게 아래처럼 가능합니다.
// private String accessKey = "Springboot 액세스 키";
// private String secretKey = "Springboot 비밀 액세스 키";
// private String region = "ap-northeast-2";
// 네. 저렇게 했다가 깃허브에 올리면 바로 털리겠죠?
// 따라서 저런 상수 값들은 저렇게 직접 가져다 넣기 보다는
// application.yml에 두고 가져오는 것이 좋습니다.
// application.yml에서 처음 시작이 cloud
// (잘 보면 spring이랑 별개임)
// 1depth 아래가 aws 그리고 s3, region 등등이 같은 depth이죠
// 저렇게 depth에 맞춰서 어떤 값을 가져올지 기입하면 됩니다.
@Value("${cloud.aws.credentials.accessKey}")
private String accessKey;
@Value("${cloud.aws.credentials.secretKey}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
@Value("${cloud.aws.s3.bucket}")
private String bucket;
// 버킷의 정보 지정
@Value("${cloud.aws.s3.path.review}")
private String reviewPath;



@PostConstruct
public void init(){

this.awsCredentials = new BasicAWSCredentials(accessKey, secretKey);

}

@Bean
public AmazonS3 amazonS3(){

AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);

return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();

}

@Bean
public AWSCredentialsProvider awsCredentialsProvider() {

return new AWSStaticCredentialsProvider(awsCredentials);

}

}
21 changes: 21 additions & 0 deletions src/main/java/umc/spring/converter/ReviewConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package umc.spring.converter;

import umc.spring.domain.Review;
import umc.spring.domain.ReviewImage;
import umc.spring.web.dto.StoreRequestDTO;

import java.util.List;

// 12주차 사진도 같이 업로드하기
public class ReviewConverter {

public static ReviewImage toReviewImage(String imageUrl, Review review){

return ReviewImage.builder()
.imageUrl(imageUrl)
.review(review)
.build();

}

}
41 changes: 41 additions & 0 deletions src/main/java/umc/spring/domain/Uuid.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package umc.spring.domain;

import jakarta.persistence.*;
import lombok.*;
import umc.spring.domain.common.BaseEntity;

// 12주차 사진도 같이 업로드하기

// 우선 너무 코드가 복잡해지지 않도록 하기 위해
// 리뷰에 사진이 여러 장 가능하지만 한 장만 업로드를 해보겠습니다.
// 우선 사진 각각을 아마존 자체에서도 구분이 가능해야 합니다.
// 그 이유는 물론 사진마다 이름이 존재하겠지만…
// 사람이 붙이는 이름이 같을 수 있기 때문에
// 따로 겹치지 않는 정보가 필요합니다.
// 가장 쉬운 방법은 업로드 하는 파일에
// 일련번호를 붙이는 방법이 있겠지만.. 이 방법도 유효하지 않습니다.
// 왜냐하면 자바 자체적으로 변수를 두면
// 서버가 꺼질 때 초기화가 되기 때문에 영속되는 데이터로 둬야 합니다.
// 저희는 UUID라는 것을 사용해서 일련번호를 두겠습니다.
// 4UUID는 겹칠 확률이 극히 적은(사실상 안 겹칩니다)
// 일련의 식별자 입니다!
// Java에서는 UUID를 생성해주는 API가 존재하며
// 이를 이용해서 UUID를 담은 엔티티를 하나 만들어서 사용합시다.
// 이후 AWS S3에 업로드 시 uuid를 파일 이름에 붙여서
// 업로드 해서 업로더가 지은 파일의 이름이
// 동일하더라도 각각의 파일이 식별이 되도록 합시다!
@Entity
@Builder
@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Uuid extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true)
private String uuid;

}
11 changes: 11 additions & 0 deletions src/main/java/umc/spring/repository/ReviewImageRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package umc.spring.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import umc.spring.domain.ReviewImage;

// 12주차 사진도 같이 업로드하기
public interface ReviewImageRepository extends JpaRepository<ReviewImage, Long> {



}
11 changes: 11 additions & 0 deletions src/main/java/umc/spring/repository/UuidRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package umc.spring.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import umc.spring.domain.Uuid;

// 12주차 사진도 같이 업로드하기
public interface UuidRepository extends JpaRepository<Uuid, Long> {



}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public interface StoreCommandService {

// 9주차 2. 가게에 리뷰 추가하기 API
// 12주차 사진도 같이 업로드하기
Review createReview(Long memberId, Long storeId, StoreRequestDTO.ReviewDTO request);


Expand Down
Loading

0 comments on commit 93f1b58

Please sign in to comment.