Back-End/Spring 자료실

[Java 웹개발 마스터] #3. REST API 개발

Chipmunks 2020. 8. 10.
728x90


안녕하세요. 패스트캠퍼스 Java 웹 개발 수강생입니다.

세 번째로 Rest API 를 개발을 해봤는데요. REST API 강의를 들으면서 새로 알게된 점들을 정리해보려 합니다.


스프링 테스트 클래스를 설정하는 법을 정리해봤다.

스프링을 이용한 테스트는 @RunWith(SpringRunner.class) 어노테이션을 테스트 클래스에 붙인다.

@WebMvcTest(RestaurantController.class) 어노테이션으로 어떤 컨트롤러를 테스트한다고 명시해줄 수 있다.


필드로 MockMvc 객체를 만들어 @Autowired 로 스프링으로부터 주입받는다.

1
2
3
4
5
public void list() throws Exception {
mvc.perform(get("/restaurants"))
    .andExpect(status().isOk())
    .andExcept(content().containsString("Bob zip")));
}
cs

컨트롤러에서 URL에서 값을 가져오려면 @PathVariable 어노테이션으로 명시를 해줘서 파라미터로 넘겨받는다.

@Component 어노테이션을 붙이면 스프링에서 해당 클래스를 관리해준다.
@Autowired 어노테이션을 붙이면 스프링이 객체를 알아서 생성해서 만들어준다.
@RestController 도 @Component 의 일종이다.

테스트 환경에서 컨트롤러가 제대로된 저장소를 사용하지 못하는 문제점이 있었는데,
직접 컨트롤러에 의존성을 주입시켜야 한다.
이 때, @SpyBean 어노테이션으로 원하는 의존성을 주입해줄 수 있다.

의존성 주입을 하면 어떤 점이 도움이 되는 지는 다음과 같다.
사용해야 할 객체들을 다양하고 유연하게 변경할 수 있다.

1
2
@SpyBean(RestaurantRepositoryImpl.class)
private RestaurantRepository repository;
cs

테스트 코드에서 인터페이스를 의존시킬 때는 구현체도 지정해줘야 한다.

한 클래스에 코드량이 많아지니 레이어를 분리시켰다.
UI Layer (interfaces 패키지), Domain Layer (domain 패키지), Application Layer (application 패키지) 으로 분리헀다.
Application Layer 에는 컨트롤러에 있는 로직을 분리하려고 Service 클래스를 만들었다.

하나의 테스트를 통과하기 위해 모든 의존성을 주입시켜야 한다는 문제가 있었다.
Mock 객체는 진짜 객체를 따라하는 객체다.
Mock 객체를 만드는 방법 중에서 스프링은 Mockito 프레임워크를 사용해 만든다.

스프링은 기본적으로 POJO, 전통적인 자바 객체를 사용할 것을 권장하고 지원한다.
@MockBean 어노테이션으로 가짜 Service 클래스로 변경하고 리포지토리 필드를 삭제한다.
가짜 객체가 테스트를 위해 올바른 행위를 할 수 있도록 선언을 해줘야 한다.
given(Mock 객체의 메소드).willReturn(반환 값)
으로 가짜 객체의 메소드를 실행했을 때 반환되어야 할 값들을 지정해줄 수 있다.

Service 클래스의 테스트도 Repository 를 실제로 사용할 필요가 없으므로 @Mock 어노테이션으로 변경한다.
Mock 객체를 초기에 할당시켜주기 위해서는 @Before 메소드로 테스트가 진행되기 전에 할당해야 한다.
1
MockitoAnnotations.initMocks(this);
cs
그 뒤에 각 테스트마다 given() 으로 레포지토리가 반환해야 할 것들을 지정을 해준다.
실행이 되는것을 테스트해야 한다면 verify() 메소드로 검증한다.

JPA 를 이용해 Repository 에서 CrudRepostiroy<> 를 상속받는다.
구현체를 직접 만들지 않아도 된다.
Repository의 조회 메소드는 Optional<> 타입으로 반환해야 한다.



@WebMvcTest 어노테이션 설명은 스프링 공식 문서에서 확인할 수 있었다.

위 어노테이션으로 어떤 스프링 어노테이션들을 스캔할지를 명시해주는 역할을 한다.

그러나 일반적인 @Component 빈들은 스캔 대상이 아니다.

Mock 객체를 사용하기 위해 @MockBean 과 같이 사용한다.


MockMvc 는 스프링 테스트마다 자동으로 의존받을 수 있는데, @AutoConfigureMockMvc 어노테이션을 테스트 클래스에 붙이면, 각 테스트 메소드에 @Autowired 어노테이션이 붙은 MockMvc 를 파라미터로 받으면 주입된다.

비슷한 어노테이션으로 @AutoConfigureWebTestClient, @AutoConfigureWeb~~~ 등이 있다.


JPA 를 테스트할 떄는 @DataJpaTest 라는 어노테이션을 붙인다. @Entity 클래스를 스캔하고 JPA 리포지토리를 설정해준다. JPA 테스트 클래스에서는 영속성을 관리해주는 TestEntityManager 을 자동으로 주입받을 수 있고 @AutoConfigureTestEntityManager 어노테이션으로도 주입받을 수 있다.


또한 테스트 환경은 메모리 기반의 데이터베이스를 사용하는데, 만약 실제 데이터베이스를 사용한다면 @AutoConfigureDatabase(replace = Replace.NONE) 어노테이션을 사용한다.


@SpyBean 과 @MockBean 의 별다른 특징을 중심으로 문서를 읽었다.

@MockBean 은 Application Context 가 새로고침 되는 도중에 일어나는 행동을 테스트할 순 없다. 이 상황에서는 @Bean 어노테이션을 사용해야 한다.


@SpyBean 은 @Cacheable 메소드가 있는 빈을 사용할 때는 -parameters 인수와 함께 컴파일이 되어야 한다.

스프링이 관리하는 프록시에 사용한다면은 중지해야할 수도 있다. given, when 같은 메소드를 사용하려면 AopTestUtils.getTargetObject(yourProxiedSpy) 을 실행해야 한다.


JPA 를 사용할 때 JpaRepository 를 주로 사용했는데, CrudRepository 와는 어떤 차이점이 있고 어떤 리포지토리 종류들이 있는 것인지 궁금했다. JPA 문서는 여기를 참고했다.


CrudRepository 인터페이스는 CRUD 기능을 명세한 인터페이스다. JpaRepository 는 페이징과 플러시 작업 등 JPA에 특화된 기능들을 사용할 수 있고, CrudRepository 를 상속한 구조다.


CrudRepository 인터페이스는 다음과 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface CrudRepository<T, ID> extends Repository<T, ID> {
 
  <extends T> S save(S entity);      
 
  Optional<T> findById(ID primaryKey); 
 
  Iterable<T> findAll();               
 
  long count();                        
 
  void delete(T entity);               
 
  boolean existsById(ID primaryKey);   
 
  // … more functionality omitted.
}
cs



문서에서 인터페이스 상속 관계도를 가져왔다.




스프링 프레임워크 자체에서도 테스트 코드를 작성할 것을 권장하는 것으로 보아 테스트 코드를 잘 작성하는 방법은 반드시 몸에 익혀야 하는 것 같다.

공식 문서로 스프링 테스트 내용을 잠깐 훑어봤는데, Application Context 가 어떻게 설정되고 동작하는지를 알면 좋을 것 같다. 공식 문서를 언젠가 정독해보면 좋을 것 같다.

Jpa는 한글 책으로 스터디를 하고 있는데, 마찬가지로 공식 문서를 읽으면 몰랐던 지식을 많이 알 수 있을 것 같다.


프론트엔드 파트로 넘어가기 전 3번 포스팅까지 정리를 했다.

다시 정리하는 것만으로도 꽤 시간을 써서 앞으로 내용 정리를 조금씩 하며 진행해야 할 것 같다.

댓글