Skip to content

Commit

Permalink
Release:v1.0.0-beta.15
Browse files Browse the repository at this point in the history
ygyg[v1.0.0-beta.15]
- API 개발
  - aws s3 파일 업로드를 위한 presignedUrl 생성 API 개발
- Fix/Modify
  - 타입별 내 소분글 조회 api에서 중복 데이터가 조회되는 버그 수정
  - igthub actions workflow에서 환경변수 주입 방법 변경
  • Loading branch information
Hwan0518 authored Jan 29, 2025
2 parents 37d723b + b4712a0 commit e3542be
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 1 deletion.
8 changes: 7 additions & 1 deletion .github/workflows/gradle-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,11 @@ jobs:
sudo docker stop "${{ secrets.SERVICE_NAME }}" || true
sudo docker rm "${{ secrets.SERVICE_NAME }}" || true
sudo docker pull "${{ secrets.DOCKER_USERNAME }}"/"${{ secrets.PRODUCTION_REPO }}":$((${github.run_number}-1))
sudo docker run -d --name "${{ secrets.SERVICE_NAME }}" -p "${{ secrets.PRODUCTION_SERVICE_PORT }}":"${{ secrets.PRODUCTION_SERVICE_PORT }}" --network "${{ secrets.PRODUCTION_DOCKER_NETWORK }}" "${{ secrets.DOCKER_USERNAME }}"/"${{ secrets.PRODUCTION_REPO }}":$((${github.run_number}-1))
sudo docker run -d \
-e "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" \
-e "MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }}" \
-e "AWS_ACCESS_KEY=${{ secrets.AWS_ACCESS_KEY }}" \
-e "AWS_SECRET_KEY=${{ secrets.AWS_SECRET_KEY }}" \
-e "JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }}" \
--name "${{ secrets.SERVICE_NAME }}" -p "${{ secrets.PRODUCTION_SERVICE_PORT }}":"${{ secrets.PRODUCTION_SERVICE_PORT }}" --network "${{ secrets.PRODUCTION_DOCKER_NETWORK }}" "${{ secrets.DOCKER_USERNAME }}"/"${{ secrets.PRODUCTION_REPO }}":$((${github.run_number}-1))
sudo docker image prune -a -f
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ dependencies {
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// api interceptor에서 사용되는 commons-io-IOUtils 사용을 위한 의존성 추가
implementation 'commons-io:commons-io:2.14.0'
// s3 presigned url을 위한 aws 의존성
implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.0.0'
}

tasks.named('test') {
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/foiegras/ygyg/aws/api/controller/AwsController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package foiegras.ygyg.aws.api.controller;


import foiegras.ygyg.aws.api.request.GetPutObjectPreSignedUrlRequest;
import foiegras.ygyg.aws.api.response.GetPreSignedUrlResponse;
import foiegras.ygyg.aws.application.dto.in.GetPutObjectPreSignedUrlInDto;
import foiegras.ygyg.aws.application.dto.out.GetPutObjectPreSignedUrlOutDto;
import foiegras.ygyg.aws.application.service.AwsService;
import foiegras.ygyg.global.common.response.BaseResponse;
import foiegras.ygyg.global.common.security.CustomUserDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@Validated
@RestController
@RequestMapping("/api/v1/aws")
@RequiredArgsConstructor
public class AwsController {

// service
private final AwsService awsService;
// util
private final ModelMapper modelMapper;


/**
* AwsController
* 1. S3 PreSigned URL 생성
*/

// 1. S3 PreSigned URL 생성
@Operation(summary = "S3 PreSigned URL 생성", description = "S3 PreSigned URL 생성", tags = { "Aws" })
@GetMapping("/presigned-url")
@SecurityRequirement(name = "Bearer Auth")
public BaseResponse<GetPreSignedUrlResponse> getPutObjectPreSignedUrl(@Valid GetPutObjectPreSignedUrlRequest request, @AuthenticationPrincipal CustomUserDetails authentication) {
GetPutObjectPreSignedUrlInDto inDto = modelMapper.map(request, GetPutObjectPreSignedUrlInDto.class);
inDto = inDto.toBuilder()
.userEmail(authentication.getUserEmail())
.build();
GetPutObjectPreSignedUrlOutDto outDto = awsService.getPutObjectPreSignedUrl(inDto);
return new BaseResponse<>(modelMapper.map(outDto, GetPreSignedUrlResponse.class));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package foiegras.ygyg.aws.api.request;


import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;


@Getter
@AllArgsConstructor
public class GetPutObjectPreSignedUrlRequest {

@NotNull
@NotEmpty
@NotBlank
private String fileName;

@NotNull
@NotEmpty
@NotBlank
private String contentType;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package foiegras.ygyg.aws.api.response;


import lombok.Getter;
import lombok.NoArgsConstructor;


@Getter
@NoArgsConstructor
public class GetPreSignedUrlResponse {

private String preSignedUrl;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package foiegras.ygyg.aws.application.dto.in;


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Getter
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public class GetPutObjectPreSignedUrlInDto {

private String fileName;
private String contentType;
private String userEmail;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package foiegras.ygyg.aws.application.dto.out;


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GetPutObjectPreSignedUrlOutDto {

private String preSignedUrl;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package foiegras.ygyg.aws.application.service;


import foiegras.ygyg.aws.application.dto.in.GetPutObjectPreSignedUrlInDto;
import foiegras.ygyg.aws.application.dto.out.GetPutObjectPreSignedUrlOutDto;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.time.Duration;


@Service
@RequiredArgsConstructor
public class AwsService {

// aws
private final S3Presigner s3Presigner;
@Value("${aws.s3.bucket}")
private String bucket;
@Value("${aws.s3.duration}")
private Long duration;


/**
* AwsService
* 1. S3 PutObject PreSigned URL 생성
*/

// 1. S3 PutObject PreSigned URL 생성
public GetPutObjectPreSignedUrlOutDto getPutObjectPreSignedUrl(GetPutObjectPreSignedUrlInDto inDto) {
// key: 저장될 파일명
String key = inDto.getUserEmail() + "-" + inDto.getFileName();
// PutObjectRequest
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucket)
.key(key)
.contentType(inDto.getContentType())
.build();
// preSignRequest
PutObjectPresignRequest preSignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(duration))
.putObjectRequest(putObjectRequest)
.build();
return new GetPutObjectPreSignedUrlOutDto(s3Presigner.presignPutObject(preSignRequest).url().toString());
}

}
34 changes: 34 additions & 0 deletions src/main/java/foiegras/ygyg/global/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package foiegras.ygyg.global;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;


@Configuration
public class S3Config {

@Value("${spring.cloud.aws.region.static}")
private String region;
@Value("${spring.cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${spring.cloud.aws.credentials.secret-key}")
private String secretKey;


// S3Presigner: s3 임시접근 url 생성 도구
@Bean
public S3Presigner s3Presigner() {
AwsCredentials awsCredentials = AwsBasicCredentials.create(accessKey, secretKey);
return S3Presigner.builder()
.region(Region.of(region))
.credentialsProvider(() -> awsCredentials)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public UserPostListQueryDataByCursorOutDto findPostListByCursor(GetMyPostListInD
booleanExpression.selectType(inDto.getType(), inDto.getUserUuid(), inDto.getCurrentTime()),
booleanExpression.getNextUserPost(inDto.getLastCursor(), inDto.getOrder()))
.orderBy(inDto.getOrder().equals(ASC) ? userPostEntity.id.asc() : userPostEntity.id.desc())
.distinct()
.limit(pageable.getPageSize() + 1) // 다음 페이지 여부 확인을 위해 +1개 조회
.fetch();
// 다음 페이지 여부 확인 & content에서 마지막 값 제거, result = {hasNext, content}
Expand Down

0 comments on commit e3542be

Please sign in to comment.