본문 바로가기
Blog/TIL

2025-03-11 (화)

by 코젼 2025. 3. 11.
728x90
반응형

✅ 오늘의 학습 정리

  • INNER JOIN과 LEFT JOIN의 차이를 이해하고, 데이터 조회 시 상황에 맞게 사용할 수 있도록 함.
  • containOrNull()을 사용하여 리스트 내 요소를 안전하게 검색하고, 없을 경우 null을 반환하는 방식 학습.
  • 리플렉션을 활용한 setPrivateField() 구현 및 이슈 해결
    • 리플렉션이 예상대로 동작하지 않을 수 있는 문제를 발견하고 generateSequence를 활용해 해결.
  • @MappedSuperclass를 활용한 공통 엔티티 관리 방법 학습
    • 테이블이 생성되지 않는 특성과 상속을 통해 코드 중복을 줄이는 방식을 배움.
  • generateSequence의 활용 방법
    • 무한 시퀀스 생성 및 부모 클래스 탐색 등 다양한 활용법 익힘.

🔹 1. INNER JOIN vs LEFT JOIN 차이

  • INNER JOIN: 두 테이블 간 공통된 데이터를 기준으로 교집합을 반환한다. (일치하는 행만 가져옴)
  • LEFT JOIN: 왼쪽 테이블의 모든 데이터를 기준으로 하며, 오른쪽 테이블에 매칭되는 데이터가 없으면 NULL을 반환한다. (왼쪽 테이블 기준으로 모든 데이터를 가져옴)
  • 비즈니스 활용 예시: 특정 엔티티와 연관된 데이터를 조회할 때, 연관 데이터가 필수인 경우 INNER JOIN을 사용하고, 선택적인 경우 LEFT JOIN을 사용한다.

🔹 2. containOrNull

  • 리스트나 컬렉션에서 특정 요소를 포함하는지 확인하고, 없을 경우 null을 반환하는 함수.
  • 활용 예시
fun List<String>.containOrNull(value: String): String? = this.find { it == value }

val items = listOf("apple", "banana", "cherry")
val result = items.containOrNull("banana") // "banana"
val notFound = items.containOrNull("grape") // null

기존의 contains()는 Boolean을 반환하는 반면, containOrNull()은 실제 값을 반환하거나 null을 반환한다.

🔹 3. setPrivateField() 리플렉션 이슈

📌 이슈 개요

  • 부모 클래스를 따라가며 필드를 찾도록 했지만, 가끔 특정 필드를 찾지 못하는 경우 발생.
  • 특정 환경에서는 리플렉션이 예상대로 동작하지 않아 NoSuchFieldException이 발생하기도 함.
  • 필드가 존재하는데도 getDeclaredField()가 실패하는 문제 발생.

📌 해결 방법

  • 안정적인 필드 탐색을 위해 generateSequence 활용
  • Kotlin 리플렉션(kotlin.reflect.full)과 Java 리플렉션(java.lang.reflect)의 차이 고려
  • 부모 클래스까지 안전하게 탐색하도록 수정
fun <T : Any> T.setPrivateField(fieldName: String, value: Any) {
    generateSequence(this::class.java) { it.superclass }
        .firstOrNull { it.declaredFields.any { field -> field.name == fieldName } }
        ?.getDeclaredField(fieldName)
        ?.apply {
            isAccessible = true
            set(this@setPrivateField, value)
        } ?: throw NoSuchFieldException("Field $fieldName not found in class hierarchy of ${this::class.simpleName}")
}

이점:

  • generateSequence를 활용해 부모 클래스를 자동으로 탐색
  • 불필요한 예외 처리 없이, 필드가 존재하는 클래스만 찾을 수 있도록 개선

🔹 4. @MappedSuperclass

  • JPA에서 상속을 위한 어노테이션
  • 공통 필드를 가지는 부모 클래스를 정의할 때 사용하며, 실제 테이블을 생성하지 않는다.
@MappedSuperclass
open class BaseEntity {
    @Column(name = "created_at")
    var createdAt: LocalDateTime? = null

    @Column(name = "created_by")
    var createdBy: String? = null
}

@Entity
class User : BaseEntity() {
    @Id
    @GeneratedValue
    var id: Long? = null
}

이점:

  • 공통 속성을 관리할 때, 코드 중복을 줄일 수 있음
  • @Entity 대신 @MappedSuperclass를 사용하면 해당 클래스의 테이블이 생성되지 않음

🔹 5. generateSequence

  • Kotlin에서 무한 시퀀스를 생성할 때 사용
  • 지연 계산을 통해 메모리를 효율적으로 사용할 수 있음

📌 활용 예시 1: 숫자 생성기

val sequence = generateSequence(1) { it + 1 } // 1, 2, 3, 4, ...
println(sequence.take(5).toList()) // [1, 2, 3, 4, 5]

📌 활용 예시 2: 부모 클래스 탐색

generateSequence(this::class.java) { it.superclass }
    .takeWhile { it != Any::class.java }
    .forEach { println(it.simpleName) }

  • 현재 객체의 클래스부터 시작해 상위 클래스를 차례로 탐색
  • takeWhile을 활용해 Any::class.java에 도달하면 중단
728x90
반응형

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

2025-03-14 (금)  (0) 2025.03.14
2025-03-13 (목)  (0) 2025.03.13
2025-03-10 (월)  (1) 2025.03.10
2025-03-07 (금)  (2) 2025.03.07
2025-03-06 (목)  (0) 2025.03.06

댓글