728x90
반응형
2025-02-24 (월)
🎯 오늘의 핵심 정리
✅ DTO 조회 시 selectNew를 사용하여 성능 최적화
✅ JOIN이 일반적으로 더 빠르며, 서브쿼리는 꼭 필요한 경우에만 활용
✅ fetchOne()은 유일한 결과가 보장될 때만 사용하고, 다수의 결과 가능성이 있으면 fetchFirst() 사용
✅ null 처리는 ?:(엘비스 연산자)를 활용하여 안전하게 기본값 적용
✅ 테스트 코드 리팩토링을 통해 중복을 줄이고 가독성을 높이는 것이 중요함
📌 TIL (Today I Learned) - Kotlin + JDSL 활용 및 테스트 최적화
1️⃣ 리스트 조회 시 findAll()의 반환값 처리
- 조회 결과가 없을 경우 null이 아닌 emptyList() 반환
- emptyList()를 반환하면 null 체크 없이 안전하게 컬렉션 연산 (map, forEach) 수행 가능
val results = repository.findAll() ?: emptyList()
println(results.size) // 결과가 없으면 0
✔ 컬렉션 반환 시 null을 방지하고 emptyList()를 기본값으로 적용하는 것이 안전함.
2️⃣ 데이터 조회 방식: select vs selectNew
🔹 엔티티 조회 (select)
- JPA 영속성 컨텍스트에서 관리됨
- Dirty Checking 가능 (조회 후 수정 시 자동 반영)
- 필요 없는 필드까지 조회될 가능성이 있음
val query = queryFactory.select(entity(DomainEntity::class))
.from(entity(DomainEntity::class))
.where(col(DomainEntity::identifier).equal(identifier))
🔹 DTO 조회 (selectNew)
- JPA 영속성 컨텍스트에서 관리되지 않음
- 불필요한 엔티티 필드를 로드하지 않으므로 성능 최적화 가능
- 조회한 데이터는 변경해도 DB에 반영되지 않음
data class DomainDTO(val id: Long, val name: String)
val query = queryFactory.selectNew<DomainDTO>(
col(DomainEntity::identifier),
col(DomainEntity::name)
).from(entity(DomainEntity::class))
✔ API 응답용 DTO가 필요할 경우 selectNew를 사용하여 엔티티 로딩을 방지하는 것이 바람직함.
3️⃣ JOIN vs Subquery 차이점
비교 항목 JOIN Subquery
사용 목적 | 여러 테이블을 결합하여 조회 | 특정 값을 서브쿼리로 가져오기 |
조회 방식 | 조인된 테이블에서 데이터 조회 | 서브쿼리 결과를 컬럼으로 반환 |
성능 | 일반적으로 빠름 | 성능이 떨어질 가능성 있음 |
데이터 양 | 여러 행 반환 가능 | 단일 컬럼 또는 값 반환 |
✔ 기본적으로 JOIN을 사용하고, 서브쿼리는 꼭 필요한 경우에만 활용하는 것이 성능적으로 유리함.
4️⃣ fetchOne()과 fetchFirst()의 차이점
- fetchOne(): 결과가 0개 또는 1개인 경우에만 사용 (2개 이상이면 NonUniqueResultException 발생)
- fetchFirst(): 여러 개의 결과 중 첫 번째 값을 가져옴 (예외 발생 없음)
val result = queryFactory.select(entity(DomainEntity::class))
.from(entity(DomainEntity::class))
.where(col(DomainEntity::identifier).equal(identifier))
.fetchOne() // 결과가 없으면 null, 2개 이상이면 예외 발생
✔ fetchOne()은 유일한 결과가 보장될 때 사용, 여러 개 가능성이 있으면 fetchFirst() 활용.
5️⃣ null 처리 및 안전한 기본값 적용
- 데이터가 없을 경우 기본값을 설정하여 NPE(Null Pointer Exception)를 방지
- 엘비스 연산자(?:)를 사용하여 기본값 제공
val domainData = domainService.findDomainByIdentifier(identifier)
?: DomainDTO(id = 0, name = "Default")
✔ null을 직접 반환하기보다는 기본값을 설정하는 것이 안전한 코드 작성 방법.
6️⃣ 테스트 코드 리팩토링 및 최적화
- Mock 설정 중복 제거 → setUpMocks() 함수 활용
- 반환값 비교 코드 단순화 → expectedResponse 변수 활용
- 테스트 메서드 가독성 향상
fun setUpMocks(
domainData: DomainDTO? = null,
domainExists: Boolean = true
) {
every { domainRepository.findByIdOrNull(any()) } returns if (domainExists) domain else null
every { domainService.findDomainByIdentifier(any()) } returns domainData
}
it("정상적인 도메인 ID가 주어진 경우") {
val domainData = DomainDTO(id = 1, name = "Test Domain")
setUpMocks(domainData)
val expectedResponse = DomainDetailDTO.Response(
id = domainData.id,
name = domainData.name
)
domainDetailService.findDomainByIdentifier(identifier) shouldBe expectedResponse
}
✔ 테스트 코드의 유지보수성을 높이고, 가독성을 개선함.
728x90
반응형
'Blog > TIL' 카테고리의 다른 글
2025-02-28 (금) (1) | 2025.02.28 |
---|---|
2025-02-25 (화) (0) | 2025.02.25 |
2025-02-21 (금) (0) | 2025.02.21 |
2025-02-20 (목) (0) | 2025.02.20 |
2025-02-19 (수) (0) | 2025.02.19 |
댓글