본문 바로가기
Blog/TIL

2025-01-07 (화)

by 코젼 2025. 1. 7.
728x90
반응형

Kotlin Result 클래스

  • 작업의 성공 또는 실패를 나타내는 표준화된 방식
    fun findUserAuthByBusinessAndEmail(businessId: String, email: String): Result<BusinessUserAuth> {  
      val userAuth = businessUserAuthRepository.findByBusinessIdAndEmail(businessId, email)  
      return if (userAuth != null) {  
          // 성공적인 작업의 결과
          Result.success(userAuth)  
      } else { 
          // 실패한 작업의 예외 처리
          Result.failure(ServiceException(ErrorCode.BUSINESS_USER_AUTH_NOT_FOUND))  
      }  
    }

Spring Data JPA 엔티티 상태 관리

  • Persistable
    • 엔티티 상태(신규 생성/기존 데이터)를 커스터마이징하여 판단할 수 있도록 하기 위해 사용
    • 엔티티의 식별자가 DB가 아닌 애플리케이션에서 직접 생성한다면(UUID, KeyGenerator 등) 엔티티가 신규인지 기존인지 구분하기 어려움
    • 사용하지 않을 경우, 식별자가 비어있지 않은 경우 엔티티를 기존 데이터로 간주해 UPDATE 를 실행하기 때문에 문제가 될 수 있다.
  • 기존 JPA 방식
    • INSERT: 식별자가 null 인 경우, 신규 엔티티로 간주
    • UPDATE: 식별자가 null 이 아닌 경우, 기존 엔티티로 간주
      //  
      // Source code recreated from a .class file by IntelliJ IDEA  
      // (powered by FernFlower decompiler)  
      //  
      

package org.springframework.data.domain;

import org.springframework.lang.Nullable;

public interface Persistable {
@Nullable
ID getId();

boolean isNew();  

}

- entity class
- 신규 상태(new) 를 관리하기 위해 작성된 메서드와 필드
```kotlin
package com.mobiai.business.business.vo.entity  

import com.mobiai.business.common.orm.AllEntity  
import jakarta.persistence.*  
import org.springframework.data.domain.Persistable  
import java.time.LocalDateTime  
import kotlin.jvm.Transient  

@Entity(name = "business")  
class Business(  
    @Id  
    @Column(name = "id")  
    val tsid: String,  // 식별자
    var companyName: String,  
    var ownerName: String,  
    var ownerNameFile: String,  
    @Column(unique = true, nullable = false)  
    var companyNumber: String,  
    var companyNumberFile: String,  
    var approveYn: Boolean = false,  
    var approvedAt: LocalDateTime? = null,  
    var approvedBy: String? = null,  

    @OneToMany(mappedBy = "business")  
    val businessUserAuth: MutableList<BusinessUserAuth> = mutableListOf(),  
    @OneToMany(mappedBy = "business")  
    val businessAdPlatform: MutableList<BusinessAdPlatform> = mutableListOf(),  
    @OneToMany(mappedBy = "business")  
    val project: MutableList<Project> = mutableListOf(),  
// 엔티티 클래스가 Persistable 인터페이스를 상속 받고 있음
// AllEntity 는 각 엔티티 클래스에서 기본적으로 사용하는 updatedAt, updateBy 정의되어 있음
) : Persistable<String>, AllEntity() {  

    // JPA의 Persistable 인터페이스를 구현해 엔티티 식별자 반환
    // -> Business 클래스의 ID 는 tsid 필드에 저장되어 있음
    override fun getId() = tsid  

    // JPA가 엔티티가 새로 생성된 것인지(new) / DB에서 로드된 데이터인지 판단할 때 사용
    // true: 신규 / false: 기존 엔티티
    override fun isNew() = _isNew  

    @Transient  // _isNew 필드를 DB 에 매핑하지 않고, 메모리 상에서만 유지함
    private var _isNew = true  // 엔티티가 신규 상태인지 추적하기 위한 플래그

    @PostPersist   // 엔티티가 영속성 컨텍스트에 저장된 후 호출됨
    @PostLoad   // 엔티티가 DB에서 로드된 후 호출됨
    protected fun load() {  
    // DB 조회 엔티티 + 저장된 엔티티 모두 신규 상태 아님
        _isNew = false  // 두 경우 모두 _isNew 를 false 로 설정함
    }  
}

Private 필드 리플렉션 접근

엔티티 관련 데이터를 생성하고 싶은 경우, _isNew 필드가 private 로 되어있기 때문에 접근할 수 없다.
따라서, Java 리플렉션을 사용하여 private/protected 필드에 접근하고 값을 수정할 수 있다.

  • getDeclaredField
    그러나, Java 리플렉션을 사용하는 것은 권장되지 않으므로 테스트 전용 서브클래스 또는 테스트 팩토리 패턴을 사용해 필드 상태를 관리한다.
    .apply {  
          // this -> entity 객체 (ex. Business, BusinessUserAuth)
          this.javaClass.getDeclaredField("_isNew") // _isNew 필드 가져오기
          .apply {  
              isAccessible = true  // private 필드를 외부에서 수정할 수 있도록 접근 허용
              // apply 스코프 함수 바깥의 객체(컨텍스트) 참조
              set(this@apply, false)  // _isNew 필드를 false 로 설정 > 신규 상태가 아님
          }  
      }}
728x90
반응형

'Blog > TIL' 카테고리의 다른 글

2025-01-09 (목)  (0) 2025.01.09
2025-01-08 (수)  (0) 2025.01.08
2025-01-06 (월)  (0) 2025.01.06
[240907] 이진법  (0) 2024.09.07
[240906] 우선 순위 큐 (PriorityQueue)  (0) 2024.09.06

댓글