Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,25 +181,25 @@ jobs:
TASK_DEFINITION_ARN=$(aws ecs describe-task-definition --task-definition rmrt-task --query 'taskDefinition.taskDefinitionArn' --output text)

aws ecs update-service \
--cluster rmrt-cluster \
--service rmrt-service-1 \
--cluster rmrt-cluster-ec2version \
--service rmrt-task-service-o4qq7b02 \
--task-definition $TASK_DEFINITION_ARN \
--force-new-deployment

# 배포 완료 대기
echo "⏳ ECS 배포 완료 대기..."
aws ecs wait services-stable \
--cluster rmrt-cluster \
--services rmrt-service-1
--cluster rmrt-cluster-ec2version \
--services rmrt-task-service-o4qq7b02

echo "✅ ECS 배포 완료!"

- name: 배포 상태 확인
run: |
# 서비스 상태 확인
aws ecs describe-services \
--cluster rmrt-cluster \
--services rmrt-service-1 \
--cluster rmrt-cluster-ec2version \
--services rmrt-task-service-o4qq7b02 \
--query 'services[0].{status: status, runningCount: runningCount, desiredCount: desiredCount}'

echo "🌐 실제 도메인: https://rmrt.albert-im.com"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.albert.realmoneyrealtaste.adapter.infrastructure.s3

import com.albert.realmoneyrealtaste.application.image.dto.ImageUploadRequest
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPostResponse
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPutResponse
import com.albert.realmoneyrealtaste.application.image.required.PresignedUrlGenerator
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
Expand All @@ -24,23 +24,22 @@ class S3PresignedUrlGenerator(

private val logger = LoggerFactory.getLogger(S3PresignedUrlGenerator::class.java)

override fun generatePresignedPutUrl(imageKey: String, request: ImageUploadRequest): PresignedPostResponse {
override fun generatePresignedPutUrl(imageKey: String, request: ImageUploadRequest): PresignedPutResponse {

val expiration = Instant.now().plus(Duration.ofMinutes(s3PutUrlExpirationMinutes))

val metadata = mapOf(
"original-name" to request.fileName,
"content-type" to request.contentType,
"file-size" to request.fileSize.toString(),
"width" to request.width.toString(),
"height" to request.height.toString()
)
val putObjectRequest = PutObjectRequest.builder()
.bucket(s3Config.bucketName)
.key(imageKey)
.contentType(request.contentType)
.metadata(
mapOf(
"original-name" to request.fileName,
"content-type" to request.contentType,
"file-size" to request.fileSize.toString(),
"width" to request.width.toString(),
"height" to request.height.toString()
)
)
.metadata(metadata)
.build()

val presignRequest = PutObjectPresignRequest.builder()
Expand All @@ -52,11 +51,11 @@ class S3PresignedUrlGenerator(

logger.info("Generated presigned PUT URL for key: $imageKey")

return PresignedPostResponse(
return PresignedPutResponse(
uploadUrl = presignedRequest.url().toString(),
key = imageKey,
fields = emptyMap(), // PUT 방식에서는 fields가 필요 없음
expiresAt = expiration
expiresAt = expiration,
metadata = metadata,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.albert.realmoneyrealtaste.adapter.infrastructure.security.MemberPrinc
import com.albert.realmoneyrealtaste.application.image.dto.ImageInfo
import com.albert.realmoneyrealtaste.application.image.dto.ImageUploadRequest
import com.albert.realmoneyrealtaste.application.image.dto.ImageUploadResult
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPostResponse
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPutResponse
import com.albert.realmoneyrealtaste.application.image.provided.ImageDeleter
import com.albert.realmoneyrealtaste.application.image.provided.ImageReader
import com.albert.realmoneyrealtaste.application.image.provided.ImageUploadRequester
Expand Down Expand Up @@ -38,7 +38,7 @@ class ImageApi(
fun requestImageUpload(
@RequestBody @Valid request: ImageUploadRequest,
@AuthenticationPrincipal member: MemberPrincipal,
): ResponseEntity<PresignedPostResponse> {
): ResponseEntity<PresignedPutResponse> {
val response = imageUploadRequester.generatePresignedPostUrl(request, member.id)

return ResponseEntity.ok(response)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package com.albert.realmoneyrealtaste.application.image.dto

import java.time.Instant

data class PresignedPostResponse(
data class PresignedPutResponse(
val uploadUrl: String,
val key: String,
val fields: Map<String, String>,
val expiresAt: Instant,
val metadata: Map<String, String>,
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.albert.realmoneyrealtaste.application.image.provided

import com.albert.realmoneyrealtaste.application.image.dto.ImageUploadRequest
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPostResponse
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPutResponse

fun interface ImageUploadRequester {
fun generatePresignedPostUrl(request: ImageUploadRequest, userId: Long): PresignedPostResponse
fun generatePresignedPostUrl(request: ImageUploadRequest, userId: Long): PresignedPutResponse
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.albert.realmoneyrealtaste.application.image.required

import com.albert.realmoneyrealtaste.application.image.dto.ImageUploadRequest
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPostResponse
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPutResponse

interface PresignedUrlGenerator {
fun generatePresignedPutUrl(imageKey: String, request: ImageUploadRequest): PresignedPostResponse
fun generatePresignedPutUrl(imageKey: String, request: ImageUploadRequest): PresignedPutResponse

fun generatePresignedGetUrl(imageKey: String): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.albert.realmoneyrealtaste.application.image.service

import com.albert.realmoneyrealtaste.application.image.dto.ImageUploadRequest
import com.albert.realmoneyrealtaste.application.image.dto.ImageUploadResult
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPostResponse
import com.albert.realmoneyrealtaste.application.image.dto.PresignedPutResponse
import com.albert.realmoneyrealtaste.application.image.exception.ImageConfirmUploadException
import com.albert.realmoneyrealtaste.application.image.exception.ImageGenerateException
import com.albert.realmoneyrealtaste.application.image.provided.ImageKeyGenerator
Expand Down Expand Up @@ -32,7 +32,7 @@ class ImageUploadService(

private val logger = LoggerFactory.getLogger(ImageUploadService::class.java)

override fun generatePresignedPostUrl(request: ImageUploadRequest, userId: Long): PresignedPostResponse {
override fun generatePresignedPostUrl(request: ImageUploadRequest, userId: Long): PresignedPutResponse {
try {
// 1. 사용자 검증
val todayUploadCount = imageReader.getTodayUploadCount(userId)
Expand Down
1 change: 0 additions & 1 deletion src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ spring:
properties:
hibernate:
format_sql: false
dialect: org.hibernate.dialect.MySQLDialect
jdbc:
time_zone: Asia/Seoul

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,16 @@ <h6 class="mb-2">
// Presigned PUT URL 방식 사용
console.log(uploadRequest)
console.log(uploadRequest.uploadUrl)
let metadata = uploadRequest.metadata
const response = await fetch(uploadRequest.uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': file.type
'Content-Type': metadata['content-type'],
'x-amz-meta-content-type': metadata['content-type'],
'x-amz-meta-file-size': metadata['file-size'],
'x-amz-meta-height': metadata['height'],
'x-amz-meta-original-name': metadata['original-name'],
'x-amz-meta-width': metadata['width']
},
body: file
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ImageApiTest : IntegrationTestBase() {
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.uploadUrl").isString)
.andExpect(jsonPath("$.key").isString)
.andExpect(jsonPath("$.fields").isMap)
.andExpect(jsonPath("$.metadata").isMap)
.andExpect(jsonPath("$.expiresAt").isString)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ class ImageUploadRequesterTest : IntegrationTestBase() {
assertTrue(response.key.contains("images/"))
assertTrue(response.key.endsWith(".jpg"))

assertNotNull(response.fields)
assertTrue(response.fields.isEmpty())

// 만료 시간 확인 (기본 15분 후)
val expectedMinExpiry = Instant.now().plus(Duration.ofMinutes(14))
val expectedMaxExpiry = Instant.now().plus(Duration.ofMinutes(16))
Expand Down Expand Up @@ -106,7 +103,6 @@ class ImageUploadRequesterTest : IntegrationTestBase() {

assertNotNull(response.uploadUrl)
assertNotNull(response.key)
assertTrue(response.fields.isEmpty())
assertTrue(response.expiresAt.isAfter(Instant.now()))
}
}
Expand Down Expand Up @@ -266,9 +262,6 @@ class ImageUploadRequesterTest : IntegrationTestBase() {
assertNotNull(response.key)
assertTrue(response.key.isNotBlank())

assertNotNull(response.fields)
assertTrue(response.fields.isEmpty())

// 만료 시간 검증
assertTrue(response.expiresAt.isAfter(Instant.now()))
assertTrue(response.expiresAt.isAfter(Instant.now().plus(Duration.ofMinutes(10))))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class PresignedUrlGeneratorTest(
assertTrue(response.uploadUrl.contains(imageKey))

assertEquals(imageKey, response.key)
assertTrue(response.fields.isEmpty()) // PUT 방식에서는 fields가 비어있음

// 만료 시간 확인
val expectedMinExpiry = Instant.now().plus(Duration.ofMinutes(14))
Expand Down Expand Up @@ -72,8 +71,6 @@ class PresignedUrlGeneratorTest(
assertEquals(key2, response2.key)
assertTrue(response1.uploadUrl.contains(key1))
assertTrue(response2.uploadUrl.contains(key2))
assertTrue(response1.fields.isEmpty())
assertTrue(response2.fields.isEmpty())
}

@Test
Expand Down Expand Up @@ -102,7 +99,6 @@ class PresignedUrlGeneratorTest(

assertNotNull(response.uploadUrl)
assertEquals(imageKey, response.key)
assertTrue(response.fields.isEmpty())
assertTrue(response.expiresAt.isAfter(Instant.now()))
}
}
Expand Down Expand Up @@ -133,7 +129,6 @@ class PresignedUrlGeneratorTest(

assertNotNull(response.uploadUrl)
assertEquals(imageKey, response.key)
assertTrue(response.fields.isEmpty())
assertTrue(response.expiresAt.isAfter(Instant.now()))
}
}
Expand Down Expand Up @@ -163,7 +158,6 @@ class PresignedUrlGeneratorTest(
assertNotNull(response.uploadUrl)
assertEquals(imageKey, response.key)
assertTrue(response.uploadUrl.contains(imageKey))
assertTrue(response.fields.isEmpty())
assertTrue(response.expiresAt.isAfter(Instant.now()))
}
}
Expand All @@ -188,7 +182,6 @@ class PresignedUrlGeneratorTest(
// Then
assertNotNull(minResponse.uploadUrl)
assertEquals(minImageKey, minResponse.key)
assertTrue(minResponse.fields.isEmpty())

// Given - 최대값
val maxRequest = ImageUploadRequest(
Expand All @@ -208,7 +201,6 @@ class PresignedUrlGeneratorTest(
// Then
assertNotNull(maxResponse.uploadUrl)
assertEquals(maxImageKey, maxResponse.key)
assertTrue(maxResponse.fields.isEmpty())
}

@Test
Expand Down Expand Up @@ -236,8 +228,6 @@ class PresignedUrlGeneratorTest(
assertNotNull(response.key)
assertEquals(imageKey, response.key)

assertNotNull(response.fields)
assertTrue(response.fields.isEmpty()) // PUT 방식에서는 fields가 비어있음

assertNotNull(response.expiresAt)
assertTrue(response.expiresAt.isAfter(Instant.now()))
Expand All @@ -255,7 +245,6 @@ class PresignedUrlGeneratorTest(
height = 600,
imageType = ImageType.POST_IMAGE
)
val expirationMinutes = 30L

// When
val response1 = presignedUrlGenerator.generatePresignedPutUrl(imageKey, request)
Expand Down
8 changes: 1 addition & 7 deletions src/test/resources/application-devdb.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
spring:
jpa:
hibernate:
ddl-auto: validate
# Flyway 설정
flyway:
enabled: true
baseline-on-migrate: true
baseline-version: 0
validate-on-migrate: true
ddl-auto: create-drop