* 이 포스트는 인프런에 있는 김영한 님의 '스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술' 강의를 들으면서 내용을 정리한 글입니다. *

 

[메인 컨텐츠]

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의 (inflearn.com)

 

# 2022-03-04 #

 

#목차#

21. 순수 JDBC

22. 스프링 통합 테스트


21. 순수 JDBC

 

이번 시간에서는 데이터를 다루는 스프링 라이브러리 중 하나인 Spring JPA를 살펴보기 전, 이전에는 데이터를 어떻게 처리했는지를 보았다. 지금은 사용하지 않는 방법이므로 가볍게 보려고 한다. 

 

이전에는 'JDBC'를 사용했으며, (1) build.gradle 파일에서 implementation 선언을 한 뒤 (2) application.properties 파일에서 세부 설정을 해야 했다. 

 

+Jdbc(Java DataBase Connectivity)란, 자바에서 데이터베이스에 접속할 수 있도록 해 주는 API이다. 

 

1. 사전 환경설정

(1) build.gradle

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	runtimeOnly 'com.h2database:h2'
}

이렇게 jdbc와 사용할 데이터베이스 h2를 import하는 과정이 필요하다. 

 

(2) application.properties

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

해당 파일에서는 import를 한 뒤에 추가로 이뤄져야 하는 세부 설정을 관리한다. 예를 들면

[1] tcp(소켓이라고 한다. 여러 곳에서의 접속을 관리하는 용도라고 하는데, 후에 더 자세히 알아봐야겠다.)를 통해 어떤 경로로 접속할지를 명시하거나,

[2] Jdbc driver로 어떤 데이터베이스를 연결할지 해당 클래스를 명시하거나,

[3] H2 데이터베이스의 접근 권한을 받아내는 데 필요한 유저 이름, 비밀번호 등의 정보를 입력한다. 

이런 정보를 해당 파일에서 관리했다. 

 

2. DB 리포지토리 클래스 구현

앞서 DB를 사용하기 전에는 임시로 메모리를 사용하는 MemoryMemberRepository 클래스를 만들어서 DB를 대신하였다. 이제는 실제 DB에 연결했으므로 새로운 JdbcMemberRepository 클래스를 만들었다. 

(클래스 코드가 정말 복잡하고 길다. 지금은 이렇게 개발할 일이 없으므로 생략!)

 

리포지토리를 구현했다 치면, 이제는 리포지토리 역할을 해 오던 MemoryMemberRepository 클래스를 JdbcMemberRepository 클래스로 교체해 주면 된다. 

 

앞서 의존성 주입(DI)와 스프링 빈 등록 과정, DB 인터페이스를 사용하면 단 한 줄의 코드만 바꿔서 이 작업을 대신할 수 있다. 

@Bean
public MemberRepository memberRepository(){
    return new JdbcMemberRepository(dataSource);
    // 기존 코드에서는 MemoryMemberRepository()였다. 
}

또한 앞에서 궁금했던 '형 변환'의 이유도 여기서 밝혀졌다. 

형 변환이란 하위 클래스의 인스턴스를 생성할 때, 그 클래스의 타입을 자신의 상위 클래스나 인터페이스 타입으로 선언하는 것을 의미한다.

아래의 코드가 그 예시이다. 

MemberRepository memberRepository = new JdbcMemberRepository();

이처럼 해당 인스턴스를 상위 클래스나 인터페이스 타입으로 선언하면, 후에 구현체나 하위 클래스를 바꿀 때 해당 코드만 바꿔 주면 된다. (하위 클래스들의 메소드들은 모두 상위 클래스/인터페이스에게서 상속/구현받았다고 전제할 때 해당한다.) 

이를 '다형성'이라고 한다. 그때그때 필요에 맞춰서 해당 인스턴스가 다양한 타입으로 변화할 수 있어서 다형성이라고 말하는 것 같다. 

 

또한 위처럼 인터페이스와 다형성을 사용한 개발은 개발의 SOLID 원칙 중 하나인 개방-폐쇄 원칙(Open-close principle)을 지켰다고 볼 수 있다. 

폐쇄적으로 부분의 코드만 수정해도 코드가 프로그램 전반에 개방적으로 영향을 줄 수 있다는 의미인 듯 하다. 


22. 스프링 통합 테스트

통합 테스트와 단위 테스트

스프링 통합 테스트란, 테스트 시 스프링 환경을 사용하는 테스트이다. 스프링 통합 테스트에서는 스프링과 연결된 다른 프로그램들을 전체적으로 사용한다. 앞서 스프링과 DB를 연결하였으므로, 여기서는 DB와 연결된 테스트를 진행할 수 있다. 

통합 테스트의 반대말은 단위 테스트(unit test)이다. 강의에서는 단위 테스트가 대체로 더 좋은 테스트이고 에러를 잘 잡아낼 수 있는 테스트라고 언급했다. 

 

스프링 통합 테스트를 사용하기 위해서는 해당 테스트 클래스에다가 @SpringBootTest 어노테이션을 붙여 준다. 그러면 이 클래스 안의 테스트들은 스프링 환경에서 실행된다. 

 

실제로 통합 테스트를 실행할 때

또한, 현재 프로그램은 DB와도 연결되어 있기 때문에 DB에 있는 기존 데이터가 테스트의 결과에 영향을 줄 수 있다. 

이를 방지하기 위해서 실제 테스트를 할 때에는

(1) 테스트 전용 DB를 따로 만들어서 실행한다. 

(2) @Transactional 어노테이션을 사용하면 개별 테스트를 실행할 때마다 테스트에서의 변경 사항을 DB에 저장하지 않고 롤백(roll-back), 즉 쿼리가 실행되지 않은 상태로 되돌려 준다.

 

DB의 쿼리 처리

@Transactional은 DB가 쿼리를 처리하는 방식과 관련이 있다. 

DB는 SQL문 등의 쿼리를 받자마자 실행하는 것이 아니다. 쿼리를 받고, 커밋(commit)을 해야만 해당 내용이 실행되어 DB 내부에 변화가 일어난다. 

따라서 Transactional은 엄밀히 말하면 '쿼리 실행 전으로 상태를 되돌리는 것' 이 아니라, '쿼리를 쌓아두고(커밋하지 않고) 있다가 테스트가 끝나면 종료시키는 것'에 가깝다.

 

+뿐만 아니라, 테스트 환경에서도 필드나 생성자에 @Autowired 어노테이션을 설정할 수 있다.

 

+ Recent posts