테스트 기초 개념과 JUnit 5
테스트 코드를 처음 배울 때 가장 자주 듣는 말이 있다. “버그를 잡기 위해 테스트를 작성하세요.” 틀린 말은 아니지만, 테스트 코드가 주는 가장 큰 가치는 버그 감지가 아니라 변경에 대한 안전망이다. 기능 하나를 고쳤을 때 다른 곳이 망가지지 않았는지 즉시 알 수 있고, 리팩토링할 때 기존 동작이 유지되는지 확인할 수 있고, 테스트 코드 자체가 코...
테스트 코드를 처음 배울 때 가장 자주 듣는 말이 있다. “버그를 잡기 위해 테스트를 작성하세요.” 틀린 말은 아니지만, 테스트 코드가 주는 가장 큰 가치는 버그 감지가 아니라 변경에 대한 안전망이다. 기능 하나를 고쳤을 때 다른 곳이 망가지지 않았는지 즉시 알 수 있고, 리팩토링할 때 기존 동작이 유지되는지 확인할 수 있고, 테스트 코드 자체가 코...
JUnit 5의 기본 assert 메서드만으로도 검증은 가능하다. 하지만 실패했을 때 메시지가 빈약하고, 코드를 읽을 때 어느 값이 기대값이고 어느 값이 실제값인지 헷갈린다. AssertJ는 이 문제를 해결한다. 메서드 체이닝 방식으로 자연스러운 영어 문장처럼 검증 코드를 작성할 수 있고, 실패 시 실제 값과 기대 값을 풍부하게 출력해준다. 이 글...
1. QueryDSL과 커스텀 Repository JpaRepository만으로는 뭐가 부족한가? ↓ 커스텀 Repository 패턴이란 무엇인가? ↓ 동적 쿼리를 직접 짜면 왜 힘든가? ↓ QueryDSL이 그 문제를 어떻게 해결하는가? ↓ 커스텀 Repository + QueryDSL = 완성된 전체 그림 ...
1. 동시성 제어 — 낙관적·비관적 잠금 📌 낙관적 잠금 (Optimistic Lock) — @Version 낙관적 잠금은 “대부분의 트랜잭션에서 충돌이 없을 것”이라고 낙관적으로 가정하고, 실제 충돌이 발생한 시점에 예외를 던지는 방식이다. @Entity public class Book extends BaseEntity { @Id ...
1. 벌크 연산과 읽기 전용 트랜잭션 📌 @Modifying 벌크 UPDATE/DELETE 벌크 연산은 영속성 컨텍스트를 거치지 않고 DB에 직접 실행된다. 이 때문에 실행 전후로 영속성 컨텍스트 상태와 DB 상태가 불일치하는 문제가 생길 수 있다. // 벌크 연산 후 영속성 컨텍스트 불일치 시나리오 @Service @RequiredArgsC...
1. QueryDSL 설정 📌 의존성 및 Q클래스 생성 설정 (Spring Boot 3.x / Gradle Kotlin DSL) // build.gradle.kts plugins { kotlin("jvm") version "..." kotlin("plugin.spring") version "..." id("com.ewerk...
1. Criteria API란 Criteria API는 자바 코드로 타입 안전하게 동적 쿼리를 작성하는 JPA 표준 API다. JPQL이 문자열 기반이라 오타나 필드명 변경 시 런타임에야 오류를 발견하는 반면, Criteria API는 컴파일 타임에 타입 오류를 잡을 수 있다. // JPQL — 문자열 기반. 오타가 있어도 컴파일 타임에 모름 ...
1. JPQL 기초 📌 SQL vs JPQL JPQL(Jakarta Persistence Query Language)은 테이블과 컬럼이 아닌 엔티티와 필드를 기준으로 작성하는 객체 지향 쿼리 언어다. -- SQL — 테이블명, 컬럼명 기준 SELECT m.member_id, m.member_name, m.age FROM member m WHE...
1. 즉시 로딩(EAGER)과 지연 로딩(LAZY) 📌 FetchType 기본값 정리 JPA 연관관계 어노테이션의 기본 FetchType은 다음과 같다. 어노테이션 기본 FetchType @ManyToOne EAGER @OneT...
1. UUID PK — Hibernate 6.x 공식 지원 Spring Boot 3.x (Hibernate 6.x)부터 UUID를 PK로 사용하는 것이 공식 지원된다. @Entity public class Book extends BaseEntity { @Id @GeneratedValue(strategy = GenerationTy...