1. S3 이미지 업로드 구현
설정 파일 추가
@Configuration
open class AwsConfig {
@Value("\${cloud.aws.credentials.accessKey}")
private val accessKey: String? = null
@Value("\${cloud.aws.credentials.secretKey}")
private val secretKey: String? = null
@Value("\${cloud.aws.region.static}")
private val region: String? = null
@Bean
open fun amazonS3(): AmazonS3 {
val awsCredentials: AWSCredentials = BasicAWSCredentials(accessKey, secretKey)
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(AWSStaticCredentialsProvider(awsCredentials))
.build()
}
}
S3 Service 구현
이미지 업로드 구현
- S3 BucketEnum에 activeProfile 값을 넘겨주어서 어떤 환경(local | prod)에서 업로드 하느냐에 따라 S3 bucket 이름을 구분해주었다.
- 파일명은 DB의 name 컬럼에 따로 저장하고 uuid.{extension}으로 저장한다. 불필요하게 URL Encoding된 이름이 S3의 key값으로 잡히는 것 + 중복 방지를 위함이다.
@Service
class S3Service(
private val s3Client: AmazonS3
) {
@Value("\${spring.config.activate.on-profile}")
val activeProfile: String = ""
@Throws(IOException::class)
fun directUpload(uploadPath: String, file: MultipartFile): String {
val bucketName = S3BucketEnum.getValue(activeProfile)
val originalFilename = file.originalFilename ?: ""
val fileExtension = originalFilename.substringAfterLast(".", "")
val fileName = "$uploadPath/${UUID.randomUUID()}.$fileExtension"
val objMeta = ObjectMetadata()
val bytes = IOUtils.toByteArray(file.inputStream)
objMeta.contentLength = bytes.size.toLong()
val byteArrayIs = ByteArrayInputStream(bytes)
s3Client.putObject(
PutObjectRequest(bucketName, fileName, byteArrayIs, objMeta)
.withCannedAcl(CannedAccessControlList.PublicRead))
return s3Client.getUrl(bucketName, fileName).toString()
}
}
BucketEnum
환경(Profile)에 따라 다른 S3 bucketName이 반환되도록 만들었다.
enum class S3BucketEnum(private val testValue: String, private val prodValue: String) {
NAME("test", "prod");
companion object {
fun getValue(activeProfile: String): String {
return when {
activeProfile.contains("local") || activeProfile.contains("dev") -> NAME.testValue
activeProfile.contains("prod") -> NAME.prodValue
else -> throw IllegalStateException("Unexpected profile")
}
}
}
}
application.yml 설정, 보안 처리
aws 연결을 위해서 아래와 같이 설정했다.
관리자(루트) 계정의 access_key 및 secret_key는 AWS 콘솔 우상단의 계정 이름 클릭 -> 보안 자격 증명 -> 액세스 키 쪽에서 만들고 다운로드 받을 수 있다. 해당 파일을 ~/.ssh에 보관했다.
cloud:
aws:
credentials:
accessKey: ${CLOUD_ACCESS_KEY}
secretKey: ${CLOUD_SECRET_KEY}
s3:
bucket: ${CLOUD_S3_PROD_BUCKET_NAME}
region:
static: {지역 이름}
stack:
auto: false
S3 버킷 정책 추가
S3 관련한 접근이 가능하도록 정책을 추가했다. 필요한 Action만 추가해야겠지만, 일단 모두(*) 추가해주었다.
{
"Version": "2012-10-17",
"Id": "{정책 편집기에서 만들어짐}",
"Statement": [
{
"Sid": "{정책 편집기에서 만들어짐}",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::{버킷 이름}/*"
}
]
}
2. Profile 설정 및 불러오기
설정하기
스프링부트 2.4.x 이상부터 아래와 같은 방식으로 작성한다. # Common에 적힌 방식은 어떤 group의 profile들을 사용할 것인지 정의하는 내용이다. group 이름이 local인데, 여기에 local 외에 다른 on-profile 값도 추가해주면 2가지에서 설정한 값들이 모두 적용된다.
spring.config.activate.on-profile식으로 작성하면된다.
---은 중요한 구분자이고, 이를 통해 각 환경을 구분한다.
# Common
spring:
profiles:
group:
local: local
prod: prod
---
# Local
spring:
config:
activate:
on-profile: local
---
spring:
config:
activate:
on-profile: prod
불러오기
실행 시(intellij)
Active profiles에 on-profile에 작성한 내용을 적으면 된다. 프로그램 실행 초기에 어떤 profile이 활성화 되었는지 알려준다.
불러올때
상기 기술한 것처럼 @Value로 불러온다. 이 값을 인자로 써서 profile 별로 다르게 처리해야할 분기문들을 처리하면 된다.
@Value("\${spring.config.activate.on-profile}")
lateinit var activeProfile: String
추가: Swagger multipart 이미지 업로드 설정
아래 코드처럼 controller를 작성했다.
consumes에 MULTIPART_FORM 임을 알린다.
@ModelAttribute로 Multipart를 받는다.
@PostMapping("/some/{some_id}/direct-upload", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
fun directUploadImage(
@PathVariable("some_id") someId: Long,
@ModelAttribute @Valid imageRequest: ImageRequest
): ApiResponse<ImageResponse> {
... 중략
}
class ImageRequest (
val file: MultipartFile,
val description: String?
){
}
file을 선택할 수 있다.
추가: s3 업로드 multipart 이미지 용량 servlet
multipart의 기본 업로드 용량 값이 1MB 정도로 설정되어있다. application.yml에서 servlet.multipart.maxFileSize, servlet.multipart.maxRequestSize를 조정해주면 된다.
spring:
... 중략
servlet:
multipart:
maxFileSize: 10MB
maxRequestSize: 10MB
'Project > Poppin' 카테고리의 다른 글
코틀린 logback 환경 분리, validated 검증, exception 처리, CD 적용 (1) | 2024.01.01 |
---|---|
스프링부트: 시큐리티- 회원가입, 로그인 기능 추가하기, swagger 로그인 하기, @ControllerAdvice (0) | 2023.12.25 |
Thymeleaf로 input 확인 및 수정 Admin 페이지 만들기(jquery, script) (0) | 2023.12.17 |
AWS Credential(AcceesKey, SecretKey), github Actions - ECR CI 설정하기 (0) | 2023.12.09 |
AWS EC2 다른 계정용 pem key 만들고 접속: Permission Denied error (1) | 2023.12.07 |