본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
TestContainers
TestContainers 사용 이유
- https://www.testcontainers.org
- JPA 이용하여 CRUD 테스트 코드를 작성할 때 어떤 DB 환경이 좋을까?
- 운영 환경과 유사한 스펙의 DB(개발 환경 DB) 사용하기
- 인메모리 DB(ex: H2) 사용하기
- Docker 이용하기
- TestContainers를 이용하기
- TestContainers는 운영환경과 유사한 DB 스펙으로 독립적인 환경에서 테스트 코드를 작성하여 테스트가 가능하다
TestContainers 소개
- TestContainers는 Java 언어만으로 docker container를 활용한 테스트 환경 구성
- 도커를 이용하여 테스트할 때 컨테이너를 직접 관리해야 하는 번거로움을 해결 해주며, 운영 환경과 유사한 스펙으로 테스트 가능
- 즉, 테스트 코드가 실행 될 때 자동으로 도커 컨테이너를 실행하여 테스트하고, 테스트가 끝나면 자동으로 컨테이너를 종료 및 정리
- TestContainers는 다양한 모듈이 존재
TestContainers 환경(MariaDB,Redis)
- MariaDB와 Redis를 독립된 환경에서 테스트 코드 작성을 위해 TestContainers 적용
- https://java.testcontainers.org/modules/databases/jdbc/
- https://java.testcontainers.org/modules/databases/mariadb/
- https://java.testcontainers.org/test_framework_integration/spock/
- build.gradle에 의존성 추가
testImplementation "org.testcontainers:mariadb:1.19.0"
testImplementation "org.testcontainers:spock:1.19.0"
- 테스트를 위한 test/resources/application.yaml 파일 생성
spring:
datasource:
driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
url: jdbc:tc:mariadb:10:///
Spring Data JPA 및 영속성 컨텍스트
Spring Data JPA란?
- JPA란 자바 어플리케이션에서 관계형 데이터베이스 사용하는 방식을 정의한 인터페이스
- Spring Data JPA는 Spring에서 제공하는 모듈 중 하나로, 개발자가 JPA를 더 쉽고 편하게 사용할수 있도록 도와준다
- JPA를 한 단계 추상화시킨 Repository 인터페이스 제공
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
public interface PharmacyRepository extends JpaRepository<Pharmacy, Long> {
}
- build.gradle 의존성 추가 및 인터페이스만 정의해주게 되면 JPA의 CRUD를 바로 사용 가능
- 단, 영속성 컨텍스트 및 Dirty Checking 개념을 잘 이해하고 사용하지 않으면 데이터 손실 및 성능 이슈가 있을 수 있다
Spring Data JPA 사용시 주의사항
- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행된다
- 즉, 트랜잭션 밖에서 데이터 변경은 반영되지 않는다
- Spring Data JPA 구현 코드를 살펴보면, 변경이 일어나는 코드는 @Transactional이 이미 추가되어 있다
- 즉, 구현 코드를 정확히 이해하지 않고 사용시 문제가 발생할 수 있다
영속성 컨텍스트(Persistence Context)
- 영속성 컨텍스트는 entity를 저장하고 관리하는 저장소이며, 어플리케이션과 데이터베이스 사이에 entity를 보관하는 가상의 데이터베이스 같은 역할
- Spring Data JPA에서 제공하는 save메소드 구현 코드를 보면 em.persist를 통해 영속성 컨텍스트에 저장
- 이때, entity는 영속상태
- 이미 영속상태인 경우 merge를 통해 덮어 쓴다
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
영속성 컨텍스트를 왜 사용할까?
- Database와 어플리케이션 사이의 중간 계층에 있으면서 여러가지 이점이 있다
- 영속성 컨텍스트 내에 1차 캐시
- 영속성 컨텍스트 내에 쓰기 지연 SQL 저장소
- 엔티티 수정(Dirty Checking)
영속성 컨텍스트 - 1차 캐시
- 영속성 컨텍스트 내부에 1차 캐시를 가지고 있다
- persist를 하는 순간 pk값(id), 타입과 객체를 매핑하여 1차 캐시에 가지고 있음
- 한 트랙잭션 내에 1차 캐시에 이미 있는 값을 조회하는 경우 DB를 조회 하지 않고 1차 캐시에 있는 내용을 그대로 가져온다
- 단, 1차 캐시는 어플리케이션 전체 공유가 아닌 한 트랜잭션 내에서만 공유
- 반면, 조회 했을 때 1차 캐시에 없다면 DB에서 가져와서 1차 캐시에 저장 후 반환
영속성 컨텍스트 - 쓰기 지연 SQL
- memberA를 persist 하는 순간, 1차 캐시에 넣고 쓰기 지연 SQL 저장소에 쿼리를 만들어 쌓는다
- memberB도 persist 하는 순간 동일한 과정을 거치며, commit 하는 순간 flush가 되면서 DB에 반영
- flush란 영속성 컨텍스트의 변경 내용을 DB에 반영하며, 1차 캐시를 지우지는 않는다
'패스트캠퍼스 강의' 카테고리의 다른 글
[31일] 50일 포트폴리오 챌린지 (0) | 2023.09.07 |
---|---|
[30일] 50일 포트폴리오 챌린지 (0) | 2023.09.06 |
[28일차] 50일 포트폴리오 챌린지 (0) | 2023.09.04 |
[27일차] 50일 포트폴리오 챌린지 (0) | 2023.09.03 |
[26일차] 50일 포트폴리오 챌린지 (0) | 2023.09.02 |