[Kotlin] data class와 주의사항


데이타 클래스(data class)란

  • Kotlin에서 데이터를 저장하는 목적으로 사용되는 특별한 종류의 클래스. 데이터 클래스는 일반 클래스와 달리 몇가지 유용한 기능을 자동으로 제공함.

데이타 클래스의 특징

  1. 간단한 선언: data 키워드를 사용하여 쉽게 선언

    data class Person(val name: String, val age: Int) 
  2. 자동 생성 메소드: 컴파일러가 아래의 메소드를 자동으로 생성

    • toString()
    • equals()
    • hashcode()
    • componentN() 함수들
  3. 불변성: 주 생성자의 파라미터를 val로 선언하여 불변 객체를 만들 수 있음

    • var로 선언하는 경우 해시 기반 컬랙션에서 예상치 못한 동작이 발생할 수 있다.
  4. Destructuring (구조분해): 데이터 클래스의 속성을 쉽게 분해할 수 있다.

    val persion = Person("Alice", 30)
    val (name, age) = person 
    

data class 사용 예시

data class User(val name: String, val id: Int) 

fun main(){ 
  val user1 = User("John", 1) 
  val user2 = User("John", 1) 

  println(user1) // User(name=John, id=1)
  println(user1 == user2) //true

  val user3 = user1.copy(name = "Jane")
  println(user3) // User(name=Jane, id=1) 

user1 == user2 에서 자동 생성된 equals method 가 사용 되는데, 이때 객체 비교를 메모리 주소로 하는것이 아니라 을 기준으로한다.


data class의 제한 사항

  • Primary Constructor(주 새성자)는 최소 하나의 파라미터를 가져야한다.
  • 모든 Primary Constructor는 val 또는 var로 표시되어야한다.
  • data class는 abstract, sealed, inner class가 될 수 없다.

data class가 자동 생성하는 메소드

toString()

  • data class는 모든 프로퍼티를 포함하는 가독성 좋은 문자열 표현을 제공한다.
  • Primary Constructor에 정의된 프로퍼티를 표시해준다.
  • 필요하다면 override해서 사용할 수 있다.
data class User(val name:String, val age: 25) 

val user1 = User("Alice", 25)
println(user1.toString()) 
// output: User(name=Alice, age=25) 

hashCode()

  • Primary Constructor에 정의된 모든 프로퍼티를 사용하여 해시코드를 계산한다.
  • 필요한 경우 override해서 커스터 마이징 할 수 있다. 하지만 이 경우 equals() 메소드와의 일관성 유지가 필요하다.

  val user1 = User("John", 1) 
  val user2 = User("John", 1)
  val user3 = User("Alice", 2) 

  println(user1.hashCode()) // ex: 421124123
  println(user2.hashCode()) // ex: 421124123 user1과 같은 값 
  println(user3.hashCode()) // ex: -127486178 user1과 다른  값 

  val userSet = hashSetOf(user1, user2, user3)
  println(userSet.size) // 2 user1과 user2는 동일한 것으로 간주됨

equals()

  • 구조적 동등성을 판단한다 : 객체의 내용을 비교하여 동등성을 판단한다. 모든 프로퍼티의 값이 같으면 동등하다고 간주한다.
  • Primary Constructor에 정의된 모든 프로퍼티를 비교에 사용한다.
  • type safe : 비교하는 객체의 타입을 먼저 확인한다.
  • null safe : null에 대한 안전한 비교를 수행한다.
  • hashCode()와 일관성: equals()가 true르 반환하는 두 객체는 반드시 같은 hashCode()를 가져야한다.
  val person1 = Person("Alice", 30)
  val person2 = Person("Alice", 30)
  val person3 = Person("Bob", 25)

  println(person1 == person2) // true
  println(person1 == person3) // false
  println(person1.equals(person2)) // true
  println(person1.equals(person3)) // false

copy()

  • 불변성 유지: 원본 객체를 변경하지 않고 새로운 객체를 생성.
  • 선택적 파라미터: 변경하고자 하는 속성만 지정할 수 있음
  • type safe: 컴파일시 타입체크가 이루어짐
  • 모든 프로퍼티 복사 : 명시적으로 변경하지 않은 프로퍼티는 원본 객체의 값을 그대로 유지
  data class Person(val name: String, val age: Int, val city: String)

  val alice = Person("Alice", 30, "New York")

  // 나이만 변경
  val olderAlice = alice.copy(age = 31)
  println(olderAlice) // Person(name=Alice, age=31, city=New York)

  // 도시만 변경
  val aliceInLondon = alice.copy(city = "London")
  println(aliceInLondon) // Person(name=Alice, age=30, city=London)

  // 여러 속성 변경
  val bobInParis = alice.copy(name = "Bob", age = 25, city = "Paris")
  println(bobInParis) // Person(name=Bob, age=25, city=Paris)

copy의 주의사항

  • copy():는 shallow copy를 수행한다. 중첩된 객체나 컬랙션은 참조만 복사된다.

  • 복사를 너무 많이하면 성능에 악영향이 있을 수 있다.

  • 변경되지 않은 값은 그대로 사용한다.

    • 파라미터로 전달된 값만을 변경하고, 변경되지 않은 필드는 원본 객체 값을 그대로 사용한다.
    • 이 방식으로 불필요한 객체 생성 작없을 회피한다.
      data class User(val name: String, val age: Int)
    
      val user1 = User("Alice", 25)
      val user2 = user1.copy(age = 30)
    
      println(user1.name === user2.name) // true, name 필드는 그대로 재사용
    
      data class LargeData(val id: Int, val name: String, val description: String)
    
      val large1 = LargeData(1, "Test", "This is a large data object.")
      val large2 = large1.copy(description = "Updated description.")
    
      println(large1.id === large2.id)        // true, id 필드는 그대로 재사용
      println(large1.name === large2.name)    // true, name 필드도 그대로 재사용
      println(large1.description === large2.description) // false, 변경된 description만 새로 생성

    `* == 값 동등성 비교 / === 참조 동등성 비교

반응형

'웹프로그래밍 > Kotlin' 카테고리의 다른 글

[KOTLIN] CLASS  (0) 2023.02.25
[코틀린 KOTLIN 정리]01.코틀린 시작하기  (0) 2021.06.02
[코틀린KOTLIN 정리]0. 들어가며  (0) 2021.06.02

@Transactional 메소드 내에서 @Transactional(propagation = Propagation.REQUIRED_NEW) 메소드를 실행하게되면 어떤 일이 발생할까

트랜잭셔널 메소드에서 새로운 트랜잭션을 시작하면 어떤일이 발생하는지 알아보자.


1. 애플리케이션 시작 시 작업

1-1. 트랜잭션 관리자 등록

  • 애플리케이션 시작 시 트랜잭션 관리자가 등록된다.
  • 트랜잭션 전파 정책(Propagation)을 처리할 수 있도록 AOP 프록시가 생성된다.

1-2. AOP 프록시 생성

  • @Transactional이 적용된 메소드에 대해 프록시가 생성된다.
  • 각각의 메소드가 호출될 때 전파 정책을 해석하도록 설정된다.

2. 부모 매소드 실행(@Transactional, REQUIRED 전파 정책)

2-1. 트랜잭션 시작

  • 부모 메소드가 호출되면 프록시가 트랜잭션 관리자를 통해 트랜잭션을 시작
  • 트랜잭션 전파 정책 Propagation.REQUEIRED의 기본 동작에 따라 기존 트랜잭션이 없으면 새 트랜잭션을 시작.
  • 데이터베이스 연결이 확보되고, auto-commit이 비활성화됨

2-2. 비지니스 로직 실행 중 자식 메소드 호출

  • 부모 메소드 내에서 Propagation.REQUIRED_NEW가 적용된 자식 메소드가 호출됨.

3.자식 메소드 실행(@Transactional, Propagation.REQUIRED_NEW)

3-1. 부모 트랜잭션 일시 중단

  • 자식 메소드가 호출되면 부모 트랜잭션이 일시중단 상태로 변경됨.
  • 일시 중단된 트랜잭션은 컨텍스트에 보관되며, 자식 메소드가 끝날때까지 대기.

3-2. 새로운 트랜잭션 시작

  • 자식 메소드는 항상 별도의 트랜잭션에서 실행
    • 트랜잭션 관리자가 새로운 데이터베이스 연결을 확보하거나 기존 연결을 재사용 하여 별도 트랜잭션을 시작.
    • 새로운 트랜잭션은 부모 트랜잭션과 독립적으로 커밋 또는 롤백될 수 있다.

3-3. 비지니스 로직 실행

  • 자식 메소드의 비지니스 로직이 새로운 트랜잭션 내에서 실행됨.
  • 데이터베이스 작업은 자식 트랜잭션의 컨텍스트에서만 적용됨.

3-4. 자식메소드 종료

  • 자식 메소드 실행이 끝나면 아래와 같은 동작 발생
    • 정상종료시, 커밋
    • 예외 발생시 자식 트랜잭션롤백

4. 부모 메소드 재개

4-1. 부모 트랜잭션 복구

  • 자식 메소드 실행이 끝난 후, 무보 트랜잭션이 일시중단 상태에서 복구됨.
  • 부모 트랜잭션은 자식 트랜잭션과는 독립적으로 계속 진행됨.

4-2. 부모 메소드 비지니스 로직 실행(남은 작업)

  • 부모 메소드의 나머지 비지니스 로직 실행.
  • 데이터베이스 작업은 부모 트랜잭션 컨텍스트에 반영.

4-3 부모 메소드 종료

  • 부모 메소드 종료되면 아래와 같은 동작 발생
    • 정상 종료시 커밋
    • 예외 발생시 부모 트랜잭션 롤백

5. 특이 케이스

5-1. 자식 트랜잭션 커밋 후 부모 트랜잭션 롤백

  • 부모 트랜잭션이 나중에 롤백 되더라도 자신 트랜잭션은 독립적으로 커밋 되었기 때문에 영향을 받지 않는다.

5-2. 자식 트랜잭션 롤백 후 부모 트랜잭션 정상 종료

  • 자식 트랜잭션이 실패하면 부모 트랜잭션은 이를 감지할 수 없다.
    • 단, 예외를 명시적으로 처리한다면 감지시킬 수 있다.
    • 부모 트랜잭션은 독립적으로 커밋 또는 롤백된다.

정리:

  1. 부모 메소드(@Transactional, Propagation.REQUIRED)가 호출되며 트랜잭션이 시작됨.
  2. 자식 메소드(@Transactional, Propagation.REQUIRED_NEW)가 호출되면 부모 트랜잭션 일시중단.
  3. 자식 메소드는 새로운 트랜잭션에서 실행되며 독립적으로 커밋 또는 롤백됨
  4. 자식 메소드 실행 후, 부모 트랜잭션이 북구되어 남은 로직 실행
  5. 자식과 부모 트랜잭션은 서로 독립정이며, 서로의 성공 여부에 영향을 주지 않음.

이런 구조로 트랜잭션간의 독립성이 보장되며, 필요 시점에 새로운 트랜잭션을 생성해 데이터 무결성을 관리하는데 도움이 된다.

반응형

@Transactional 어노테이션이 있는메소드, 스프링 시작부터 메소드 시작까지의 작업

@Transactional 어노테이션이 있는 메소드가 실행될때 어떤 일이 일아날까.
스프링 시작부터 메소드가 실행되고 종료될때까지 어떤일 일어나는지 알아보자.


1.어플리케이션 시작시 작업

1-1.트랜잭션 관리자 등록

  • Spring Boot는 자동 설정(Auto Configuration)을 통해 적절한 트랜잭션 관리자를 등록한다.
    • 에를들어, JPA를 사용하는 경우 JpaTransactionManager를 기본으로 등록한다.
  • 이 관리자는 트랜잭션의 시작, 커밋, 롤백 등을 담당함.

1-2.AOP 프록시 생성

  • @EnableTransactionManagement 또는 자동 설정에 의애 트랜잭션 메소드가 AOP프록시로 감싸진다.
  • 스프링은 빈 스캐닝 중 @Transactional이 붙은 메소드를 확인하고, 해당 메소드가 실행될 때 트랜잭션 관리가 적용되도록 프록시 객체를 생성한다.
    • 기본적으로 CGLIB 또는 JDK 동적 프록시를 사용한다.

2.메소드 실행 시 작업

2-1.프록시를 통한 메소드 호출

  • 트랜잭션 메소드를 호출하면 실제 메소드를 실행하기 전에 프록시 객체가 인터셉트한다.
  • 프록시는 트랜잭션 설정과 관리작업을 수행한 후 실제 메소드를 호출한다.

2-2.트랜잭션 동작 정의 확인

  • 프록시는 메소드에 선언된 @Transactional 속성을 확인한다.
    • 트랜잭션 전파(Porpagation)
    • 격리수준(Isolation Level)
    • 읽기 전용(Read-only)
    • Timeout
    • 롤백 정책(Rollback Rules)

2-3.트랜잭션 시작

  • 트랜잭션 관리자는 트랜잭션을 시작한다.
    • 트랜잭션 전파 전책에 따라 새 트랜잭션을 생성하거나 기존 트랜잭션에 참여한다.
  • 데이터베이스 커넥션을 확보하고(필요시), auto-commit이 비 활성화 된다.

3.실제 메소드 실행

3-1.비지니스 로직 실행

  • 트랜잭션 컨텍스트 내에서 실제 메소드가 실행됨.
  • 데이터베이스의 CRUD 작업이 발생하면, 변경 사항은 트랜잭션 컨텍스트 내에 보류됨

3-2.예외 처리

  • 메소드 실행 중 예외가 발생하면, 프록시는 롤백 정책을 참조하여 트랜잭션 롤백 여부를 결정.
    • 기본적으로 RuntimeExceptionError 가 발생하면 롤백됨.
    • checked exception은 명시적으로 롤백 설정이 없으면 커밋됨.

4.메소드 실행 완료 후 작업

4-1. 트랜잭션 커밋 또는 롤백

  • 메소드 실행이 성공적으로 끝나면 트랜잭션 관리자는 트랜잭션을 커밋한다.
    • 데이터베이스 변경 사항이 확정되고, 커넥션이 반환된다.
  • 예외가 발생한 경우 롤백 작업이 수행된다.
    • 변경된 데이터는 폐기되며, 트랜잭션이 중단됨.

4-2.리소스 정리

  • 트랜잭션이 종료되면 사용된 데이터베이스 커넥션 및 기타 리소스가 pool로 반환된다.
  • 트랜잭션 컨텍스트와 관련된 쓰레드 로컬 데이터가 정리된다.

5.애플리케이션 종료 시 작업

  • 트랜잭션 관리자는 애플리케이션 종료 시 등록된 리소스를 정리하며, 사용중인 데이터베이스 연결이나 트랜잭션 관련 매니저를 닫는다.

정리

  1. 애플리케이션 시작 : 트랜잭션 관리자 설정, AOP프록시 생성
  2. 메소드 호출시 :
    • 프록시가 트랜잭션을 시작, 트랜잭션 속성 확인
    • 메소드를 실행하며 예외 여부에 따라 커밋 또를 롤백을 결정
  3. 메소드 실행 후 : 트랜잭션이 종료되고 리소스 반환
  4. 애플리케이션 종료시 : 트랜잭션 관련 리소스 정리
반응형

+ Recent posts