본문 바로가기
Spring-Boot

토비의 스프링 3.1 Vol.1 145p ~ 250p 정리

by 준형코딩 2023. 12. 28.

2장. 테스트

스프링이 개발자에게 제공하는 가장 중요한 가치 = 객체지향과 테스트

2.1 UserDaoTest 다시 보기

2.1.1 테스트의 유용성

테스트가 성공하면 모든 결함이 제거됐다는 확신을 얻을 수 있음

2.1.2 UserDaoTest의 특징

- 웹을 통한 DAO 테스트 방법의 문제점

1. 모든 레이어의 기능을 다 만들고 나서야 테스트가 가능하다.

2. 테스트를 하는 중에 에러가 나면 어디에서 문제가 발생했는지 찾아야 한다.

3. 오류가 있을 때 빠르고 정확하게 대응하기가 힘들다.

 

- 작은 단위의 테스트

그 대상에만 집중해서 테스트하는 것이 바람직

작은 단위의 테스트를 단위 테스트

 

- 단위 테스트 필요 이유

개발자가 설계하고 만든 코드가 원래 의도한 대로 동작하는지를 개발자 스스로 빨리 확인받기 위해서다.

2.1.3 UserDaoTest의 문제점

- 수동 확인 작업의 번거로움

- 실행 작업의 번거로움

2.2 UserDaoTest 개선

2.2.1 테스트 검증의 자동화

if문을 활용하여 테스트 실패 성공을 자동화하자.

"테스트란 개발자가 마음 편하게 잠자리에 들 수 있게 해주는 것" - 켄트 백

2.2.2 테스트의 효율적인 수행과 결과 관리

Junit : 자바 테스팅 프레임워크, 자바 단위 테스트를 만들 때 유용하게 사용

 

- Junit 테스트로 전환

Junit은 프레임워크이다. (IoC) main() 메소드도 필요 없고 오브젝트를 만들어서 실행시키는 코드를 만들 필요도 없다.

 

- 테스트 메소드 전환

1. 메소드를 public으로 선언하자.

2. 메소드에 @Test 어노테이션을 붙여주자.

import org.junit.Test;

public class UserDaoTest{
	@Test
    public void addAndGet() throws SQLException {
    	...
    }
}

 

- 검증 코드 전환

if -> assertThat을 통해 검증 가능

 

2.3 개발자를 위한 테스팅 프레임워크 JUnit

2.3.1 테스트의 효율적인 수행과 결과 관리

- IDE

테스트가 시작되면 JUnit 테스트 정보를 표시해주는 view가 나타난다.

이 뷰에서 테스트의 총 수행시간, 실행한 테스트의 수, 테스트 에러의 수, 테스트 실패의 수를 확인할 수 있따.

 

- 빌드 툴

ANT나 메이븐 같은 빌드 툴과 스크립트를 사용하고 있다면 빌드 툴에서 제공하는 JUnit 플러그인이나 태스크를 이용해 테스트를 실행할 수 있다.

 

2.3.2 테스트 결과의 일관성

테스트 일관성을 유지하기 위해 이전 정보를 삭제하는 기능이 필요하다.

 

- deleteAll()의 getCount() 추가

1. deleteAll (User 테이블의 모든 레코드를 삭제) 

2. getCount (User 테이블의 레코드 개수를 돌려준다.)

 

- 포괄적인 테스트

성공하는 테스트만 골라서 만들지 말자.

테스트를 작성할 때 부정적인 케이스를 먼저 만드는 습관을 들이자.

 

2.3.4 테스트가 이끄는 개발

테스트를 먼저 만들어 테스트가 실패하는 것을 보고 UserDao의 코드 만들기 = TDD

 

- 테스트 주도 개발

TDD 기본 원칙 - " 실패한 테스트를 성공시키기 위한 목적이 아닌 코드는 만들지 않는다."

 

2.3.5 테스트 코드 개선

- 중복된 코드를 별도의 메소드로 뽑아내자.

- @Before이나 @After 어노테이션을 활용하자.

- 픽스처 fixture : 테스트를 수행하는 데 필요한 정보나 오브젝트

 

2.4 스프링 테스트 적용

2.4.1 테스트를 위한 애플리케이션 컨텍스트 관리

애플리케이션 컨텍스트는 테스트마다 생성하기에는 많은 리소스가 소요되므로 테스트 개수에 상관없이 한 번만 만들어서 공유해 주자.

 

- 테스트 클래스의 컨텍스트 공유 = 여러개의 애플리케이션 컨텍스트를 생성해도 테스트 클래스끼리 공유할 수 있어 필요 시 자원 낭비를 막을 수 있다.

- @Autowired = 스프링의 DI에 사용되는 특별한 애노테이션이다.

 

2.4.2 DI와 테스트

- DataSource의 구현 클래스를 바꾸지 않더라도 Interface를 사용하여야 하는 이유

1. 소프트웨어 개발에서 절대로 바뀌지 않는 것은 없다. 

2. 인터페이스를 통해 다른 차원의 서비스 기능을 도입 가능하다.

3. 테스트에 용이하다.

 

- 테스트 코드에 의한 DI

DI를 통하여 테스트 환경에서만 동작하는 DataSource를 테스트 가능하다.

 

- DI를 이용한 테스트 방법 선택

항상 스프링 컨테이너 없이 테스트할 수 있는 방법을 가장 우선적으로 고려하자. (빠르고 간결)

2.5 학습 테스트로 배우는 스프링

학습 테스트란?

자신이 만들지 않은 프레임워크나 다른 개발팀에서 만들어서 제공한 라이브러리 등에 대해서 공부하며 테스트를 만드는 것

2.5.1 학습 테스트의 장점

- 다양한 조건에 따른 기능을 손쉽게 확인해볼 수 있다.

- 학습 테스트 코드를 개발 중에 참고할 수 있다.

- 프레임워크나 제품을 업그레이드할 때 호환성 검증을 도와준다.

- 테스트 작성에 대한 좋은 훈련이 된다.

- 새로운 기술을 공부하는 과정이 즐거워진다.

2.5.2 학습 테스트 예제

- JUnit 테스트 오브젝트 테스트

- 스프링 테스트 컨텍스트 테스트

2.5.3 버그 테스트

버그 테스트란?

코드에 오류가 있을 때 그 오류를 가장 잘 드러내줄 수 있는 테스트

 

- 테스트의 완성도를 높여준다.

- 버그의 내용을 명확하게 분석하게 해준다.

- 기술적인 문제를 해결하는 데 도움이 된다.

 

3장 템플릿

템플릿이란?

바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜서 효과적으로 활용할 수 있도록 하는 방법

3.1 다시 보는 초난감 DAO 

3.1.1 예외처리 기능을 갖춘 DAO

- JDBC 수정 기능의 예외처리 코드

JDBC 코드에서는 어떤 상황에서도 가져온 리소스를 반환하도록 try/catch/finally 구문 사용을 권장한다.

 

-JDBC 조회 기능의 예외처리

ResultSet의 close() 메소드를 호출하자.

3.2 변하는 것과 변하지 않는 것

3.2.1 JDBC try/catch/finally 코드의 문제점

복잡하고 메소드마다 반복된다.

3.2.2 분리와 재사용을 위한 디자인 패턴 적용

- 메소드 추출

 

- 템플릿 메소드 패턴의 적용 

 

- 전략 패턴의 적용

개방 폐쇄 원칙 OCP을 잘 지키는 구조이면서도 템플릿 메소드 패턴보다 유연하고 확장성이 뛰어난 것이, 오브젝트를 아예 둘로 분리하고 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 만드는 전략 패턴이다.

 

- DI 적용을 위한 클라이언트/컨텍스트 분리

public void jdbcContextWithStatementStrategy(StatementStrategy stmt) throws SQLException {
	...
}

StatementStrategy 타입의 전략 오브젝트를 DI 받아서 사용하게 하자. / 유연한 확장관계, 관심사 분리

3.3 JDBC 전략 패턴의 최적화

3.3.1 전략 클래스의 추가 정보

클라이언트로부터 생성자를 통해서 User 타입 오브젝트를 받을 수 있도록 하자.

3.3.2 전략과 클라이언트의 동거

- 로컬 클래스

전략 클래스를 매번 독립된 파일로 만들지 말고 UserDao 클래스 안에 내부 클래스로 정의해 버리자.

 

- 익명 내부 클래스

로컬 클래스에서 더 나아가서 익명 내부 클래스를 사용하여 이름이 없게 클래스를 만들어보자.

 

3.4 컨텍스트와 DI

3.4.1 JdbcContext의 분리

- 클래스 분리

컨텍스트 메서드를 분리해서 JdbcContext 클래스를 만들자.

 

- 빈 의존관계 변경

빈 의존관계를 따라서 XML 설정파일을 수정하자.

3.4.2 JdbcContext의 특별한 DI

UserDao와 JdbcContext 사이에는 인터페이스를 사용하지 않고 DI를 적용했다.

 

- 스프링 빈으로 DI

왜 인터페이스를 사용하지 않았는가?

인터페이ㅡ가 없다는 건 UserDaodhk JdbcContext가 매우 긴밀한 관계를 가지고 강하게 결합되어 있다는 의미.

3.5 템플릿과 콜백

템플릿?

어떤 목적을 위해 미리 만들어둔 모임이 있는 틀을 가리킨다.

 

콜백?

실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트를 말한다.

3.5.1 템플릿/콜백의 동작원리

- 템플릿/콜백의 특징

DI 방식의 전략 패턴 구조

 

- JdbcContext에 적용된 템플릿/콜백

 

3.5.2 편리한 콜백의 재활용

- 콜백의 분리와 재활용

변하지않는 부분을 묶어서 재활용

 

- 콜백과 템플릿의 결합

DI, 익명 내부 클래스 등의 기술은 최대한 감춰두고, 외부에는 꼭 필요한 기능을 제공하는 단순한 메소드만 노출해 주자.

3.5.3 템플릿/콜백의 응용

- 테스트와 try/catch/finaly

 

- 중복의 제거와 템플릿/콜백 설계

템플릿이 파일을 열고 각 라인을 읽어올 수 있는 BufferedReader를 만들어서 콜백에게 전달해 주고, 콜백이 각 라인을 읽어서 알아서 처리한 후에 최종 결과만 템플릿에게 돌려주는 것