Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
742e1ab
feat: 폴더, 포토 이미지 엔티티 추가
koosco Dec 23, 2025
d7f9ad0
feat: Jpa Auditing 설정 추가
koosco Dec 23, 2025
3666ead
feat: Usecase 식별용 annotation 추가
koosco Dec 23, 2025
0b39343
feat: jpa folder 영속성 Port 추가
koosco Dec 23, 2025
c49b854
feat: folder 기본 API
koosco Dec 23, 2025
a0a0a12
docs: file API swagger schema 추가
koosco Dec 23, 2025
485535b
Merge branch 'staging' into feat/#11
koosco Dec 28, 2025
c637106
feat: folder api converter 추가
koosco Dec 28, 2025
6040958
chore: 폴더명 변경 photobooth -> photo
koosco Dec 28, 2025
a9ea0cc
apply spotless
koosco Dec 28, 2025
a86a931
feat: E2E Test용 Base 클래스
koosco Dec 30, 2025
ddfe9bd
feat: Folder E2E Test용 Base 클래스
koosco Dec 30, 2025
ddaa2ae
fix: test jasypt 설정 제거
koosco Dec 30, 2025
cb35b4d
feat: test Media 구현체 추가
koosco Dec 30, 2025
275c05b
ref: application layer에 맞게 Folder Port 메서드 시그니처 수정
koosco Dec 30, 2025
f0c2c91
fix: 폴더 생성 중복 검사 로직
koosco Dec 30, 2025
deef531
fix: 폴더명 변경 중복 검사
koosco Dec 30, 2025
63e4d2b
fix: 폴더 목록 조회 메서드 시그니처 변경
koosco Dec 30, 2025
a049a69
fix: 폴더 목록 삭제 원자성 검증
koosco Dec 30, 2025
611643a
fix: 폴더 생성 응답 추가
koosco Dec 30, 2025
66d43a8
fix: 폴더 jpa 조회 메서드 추가
koosco Dec 30, 2025
5ed3d5a
fix: 일관성을 위해 ExceptionDto code 변수명 resultCode로 변경
koosco Dec 30, 2025
66e6602
test: Folder API E2E Test 추가
koosco Dec 30, 2025
ac9cc3a
feat: 폴더 conflict code 추가
koosco Dec 30, 2025
571b2ed
fix: JasyptTest Spring 의존성 제거
koosco Dec 30, 2025
94febd9
fix: DeleteFolder n+1 문제 해결
koosco Jan 8, 2026
b91eaa0
fix: RequiresSecurity class Target 추가
koosco Jan 8, 2026
64a7475
Merge branch 'staging' into feat/#11
koosco Jan 8, 2026
17c28f4
fix: Media 엔티티 추가
koosco Jan 8, 2026
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
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ dependencies {

// JPA
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("org.postgresql:postgresql")

// flyway (DB schema migration)
implementation("org.flywaydb:flyway-core")
Expand All @@ -90,7 +91,7 @@ dependencies {
testImplementation("io.kotest.extensions:kotest-extensions-spring:$kotestExtensionsVersion")
testImplementation("io.mockk:mockk:$mockkVersion")
testRuntimeOnly("com.h2database:h2")
runtimeOnly("org.postgresql:postgresql")
testImplementation("io.rest-assured:rest-assured")
}

spotless {
Expand Down
14 changes: 14 additions & 0 deletions src/main/kotlin/com/yapp2app/common/annotation/UseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.yapp2app.common.annotation

import org.springframework.stereotype.Component

/**
* fileName : UseCase
* author : koo
* date : 2025. 12. 23. 오후 6:51
* description : usecase annotation
*/
@Component
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class UseCase
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.yapp2app.common.api.document

@Target(AnnotationTarget.FUNCTION)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class RequiresSecurity
2 changes: 2 additions & 0 deletions src/main/kotlin/com/yapp2app/common/api/dto/ResultCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ enum class ResultCode(val code: String, var message: String) {
NOT_FOUND("D-04", "데이터를 찾을 수 없습니다."),
ALREADY_REQUEST("D-05", "이미 처리된 요청입니다."),

CONFLICT_FOLDER("D-06", message = "해당하는 폴더가 존재합니다."),

EXPIRED_TOKEN_ERROR("D-997", "토큰이 만료되었습니다."),
INVALID_TOKEN_ERROR("D-998", "토큰이 올바르지 않습니다."),
SECURITY_ERROR("D-999", "인증에 실패하였습니다."),
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/yapp2app/common/config/JasyptConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

/**
* fileName : JasyptConfig
* author : darren
* date : 2025. 12. 24.
* description : Jasypt 암호화 설정
*/
@Profile("!test")
@Configuration
class JasyptConfig(
@Value("\${jasypt.encryptor.password}")
Expand Down
27 changes: 27 additions & 0 deletions src/main/kotlin/com/yapp2app/common/domain/BaseTimeEntity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.yapp2app.common.domain

import jakarta.persistence.Column
import jakarta.persistence.EntityListeners
import jakarta.persistence.MappedSuperclass
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime

/**
* fileName : BaseTimeEntity
* author : koo
* date : 2025. 12. 23. 오후 7:09
* description : 생성일, 수정일 base entity
*/
@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
abstract class BaseTimeEntity(
@CreatedDate
@Column(name = "created_at", nullable = false, updatable = false)
var createdAt: LocalDateTime? = null,

@LastModifiedDate
@Column(name = "updated_at", nullable = false)
var updatedAt: LocalDateTime? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ data class FieldErrorDetail(var field: String, var message: String)

data class ExceptionMsg(
val message: String,
val code: String,
val resultCode: String,
val success: Boolean,
val errors: List<FieldErrorDetail>? = null,
) : Serializable
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ExceptionHandler {
fun businessExceptionHandler(ex: BusinessException): ResponseEntity<ExceptionMsg> {
val temp = ResponseEntity(
ExceptionMsg(
code = ex.resultCode.code,
resultCode = ex.resultCode.code,
message = ex.resultCode.message,
success = false,
errors = emptyList(),
Expand All @@ -59,7 +59,7 @@ class ExceptionHandler {

return ResponseEntity(
ExceptionMsg(
code = ResultCode.INVALID_PARAMETER.code,
resultCode = ResultCode.INVALID_PARAMETER.code,
message = errors.get(0).message,
success = false,
errors = errors,
Expand Down Expand Up @@ -104,7 +104,7 @@ class ExceptionHandler {
}
return ResponseEntity(
ExceptionMsg(
code = ResultCode.INVALID_PARAMETER.code,
resultCode = ResultCode.INVALID_PARAMETER.code,
message = ResultCode.INVALID_PARAMETER.message,
success = false,
errors = errors,
Expand All @@ -119,7 +119,7 @@ class ExceptionHandler {
request: WebRequest,
): ResponseEntity<ExceptionMsg> = ResponseEntity(
ExceptionMsg(
code = ResultCode.INVALID_PARAMETER.code,
resultCode = ResultCode.INVALID_PARAMETER.code,
message = ResultCode.INVALID_PARAMETER.message,
success = false,
errors = listOf(
Expand All @@ -139,7 +139,7 @@ class ExceptionHandler {
val enumValues = ex.requiredType!!.enumConstants?.joinToString(", ")
ResponseEntity(
ExceptionMsg(
code = ResultCode.INVALID_PARAMETER.code,
resultCode = ResultCode.INVALID_PARAMETER.code,
message = ResultCode.INVALID_PARAMETER.message,
success = false,
errors = listOf(
Expand All @@ -154,7 +154,7 @@ class ExceptionHandler {
} else {
ResponseEntity(
ExceptionMsg(
code = ResultCode.INVALID_PARAMETER.code,
resultCode = ResultCode.INVALID_PARAMETER.code,
message = ResultCode.INVALID_PARAMETER.message,
success = false,
errors = listOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.yapp2app.common.infra.config

import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaAuditing

/**
* fileName : JpaAuditingConfig
* author : koo
* date : 2025. 12. 23. 오후 7:10
* description : Jpa Auditing Config
*/
@Configuration
@EnableJpaAuditing
class JpaAuditingConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.yapp2app.common.infra.media.fake

import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

/**
* fileName : FakeS3MediaStorage
* author : koo
* date : 2025. 12. 28. 오후 10:30
* description :
*/
@Profile("test")
@Configuration
class FakeS3MediaStorage
70 changes: 70 additions & 0 deletions src/main/kotlin/com/yapp2app/media/domain/entity/Media.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.yapp2app.media.domain.entity

import com.yapp2app.common.domain.BaseTimeEntity
import com.yapp2app.media.domain.MediaType
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table

/**
* fileName : Media
* author : koo
* date : 2026. 1. 2.
* description : 미디어 파일 엔티티. fileId로 접근
*/
@Entity
@Table(name = "TB_MEDIA")
class Media(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,

@Column(name = "storage_key", nullable = false, length = 255)
val storageKey: String,

@Column(name = "owner_id", nullable = false)
val ownerId: Long,

@Column(name = "media_type", nullable = false, length = 30)
@Enumerated(EnumType.STRING)
val mediaType: MediaType,

@Column(name = "status", nullable = false, length = 30)
@Enumerated(EnumType.STRING)
var status: MediaStatus = MediaStatus.INITIATED,

@Column(name = "content_type", nullable = true, length = 100)
val contentType: String,
) : BaseTimeEntity() {

fun markAsUploaded() {
this.status = MediaStatus.UPLOADED
}

fun markAsFailed() {
this.status = MediaStatus.FAILED
}

fun markAsDeleteRequested() {
this.status = MediaStatus.DELETE_REQUESTED
}

fun markAsDeleted() {
this.status = MediaStatus.DELETED
}

fun isUploaded(): Boolean = status == MediaStatus.UPLOADED
}

enum class MediaStatus {
INITIATED, // ticket 발급됨(업로드 시작 전)
UPLOADED, // 업로드 완료 콜백/완료 API 호출됨
FAILED, // 실패
DELETE_REQUESTED, // 삭제 요청됨
DELETED, // 삭제 완료
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.yapp2app.media.infra.s3
import com.yapp2app.media.application.port.MediaStoragePort
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.regions.Region
Expand All @@ -17,6 +18,7 @@ import java.net.URI
* date : 2025. 12. 19. 오전 2:40
* description : S3 설정
*/
@Profile("!test")
@Configuration
class S3MediaStorageConfig(private val props: S3Properties) {

Expand Down
Loading