[Spring] 플러스 주차 개인과제 - Level4

Yuri·2025년 3월 21일

Spring

목록 보기
21/21

14. Entity 및 Repository CRUD 리팩토링(Kotlin)

  • Java로 작성된 EntityCRUD 메서드를 Kotlin으로 리팩토링
    • 데이터 클래스 활용
    • QueryDSL
    • Lombok → Kotlin 기본 기능으로 대체

✏️ 코드 추가

▶︎ build.gradle
▷ plugins 추가

	id 'org.jetbrains.kotlin.jvm' version '2.0.0'
    id 'org.jetbrains.kotlin.plugin.lombok' version '2.0.0'
    id 'org.jetbrains.kotlin.plugin.spring' version '2.0.0'
    id 'org.jetbrains.kotlin.plugin.jpa' version '2.0.0'
    id 'org.jetbrains.kotlin.kapt' version '2.0.0'

▷ kapt 설정 추가

kapt {
    keepJavacAnnotationProcessors = true
}

🧐 kapt

  • Kotlin에서 Java의 어노테이션 프로세서를 사용할 수 있도록 지원하는 도구
  • Spring 프레임워크에서 어노테이션 기반 코드를 생성할 때 사용
    • JPA(Entity) 라이브러리를 Kotlin 에서 사용할 때 필요

▷ dependencies 추가

	// kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib"
    implementation "org.jetbrains.kotlin:kotlin-reflect"

    // kotlin + QueryDSL
    implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta"
    kapt "com.querydsl:querydsl-apt:5.0.0:jakarta"
    kapt "jakarta.annotation:jakarta.annotation-api"
    kapt "jakarta.persistence:jakarta.persistence-api"

▷ allOpen 설정 추가

allOpen {
    annotation("jakarta.persistence.Entity")
    annotation("jakarta.persistence.Embeddable")
    annotation("jakarta.persistence.MappedSuperclass")
}

🧐 allOpen

  • kotlin 은 기본적으로 final → Spring AOP 및 JPA 같은 프레임워크에서 프록시 객체를 만들기 어렵다
  • allOpen 플러그인을 적용하면 @Entity 어노테이션이 있는 객체의 프록시 객체를 생성할 수 있게 된다

✏️ Java → Kotlin

▶︎ IntelliJ IDEA의 Convert Java File to Kotlin File 을 사용하여 기존 Java class를 Kotlin class 로 변경

▶︎ Todo(할일) Entity

@Entity
@Table(name = "todos")
class Todo(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    @Column(name = "title", nullable = false)
    val title: String,

    @Column(name = "contents", nullable = false)
    val contents: String,

    @Column(name = "weather")
    val weather: String,

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    val user: User

) : Timestamped() {

    @OneToMany(mappedBy = "todo", cascade = [CascadeType.REMOVE])
    var comments: MutableList<Comment> = mutableListOf()

    @OneToMany(mappedBy = "todo", cascade = [CascadeType.PERSIST])
    var managers: MutableList<Manager> = mutableListOf()

    init {
        managers.add(Manager(user, this))
    }
}
  • open, data 클래스 대신 일반 class
    • allOpen 플러그인 적용 → open 클래스로 선언할 필요 없음
    • data class: decompile 시 equals(), toString() 등의 Object 메서드가 자동 생성되어 예상치 못한 사이드 이펙트가 발생할 수 있다. copy() 메서드가 필요한 경우 적용 검토

▶︎ DTO(response)

class TodoResponse(
    val id: Long,
    val title: String,
    val contents: String,
    val weather: String,
    val user: UserResponse,
    val createdAt: LocalDateTime,
    val modifiedAt: LocalDateTime
) {
    companion object {
        fun from(todo: Todo) = with(todo) {
            TodoResponse(id,
                title,
                contents,
                weather,
                UserResponse.from(user),
                createdAt,
                modifiedAt
            )
        }
    }
}

🧐 companion object : 정적 팩토리 메서드 선언 시 사용

  • 클래스 내부에서 하나만 존재할 수 있는 객체로, Java의 static 멤버를 대체하는 개념
    • 클래스의 인스턴스 없이 접근 가능
    • 싱글톤 객체처럼 동작

▶︎ Kotlin 생성자

  • new 를 사용하지 않음
  • 엔티티 내 별도의 생성자를 선언하지 않고 프로퍼티(Property)를 명시하여 인스턴스 생성
profile
안녕하세요 :)

0개의 댓글