[도전 기술!] ν…ŒμŠ€νŠΈ μ½”λ“œ

2021. 8. 24. 08:00ㆍBack-End μž‘μ—…μ‹€/Spring Framework

728x90
λ°˜μ‘ν˜•

μ•ˆλ…•ν•˜μ„Έμš”? μ£Όλ‹ˆν•˜λž‘ μž…λ‹ˆλ‹€.

λ³Έ λ‚΄μš©μ€ μ£Όλ‹ˆν•˜λž‘μ΄ Web 개발 ν”„λ‘œμ νŠΈλ₯Ό ν•˜λ©΄μ„œ λ„μ „ν–ˆλ˜ κΈ°μˆ λ“€μ— λŒ€ν•œ λ‚΄μš©μ„ μ •λ¦¬ν•˜λŠ” κ³³ μž…λ‹ˆλ‹€!

이 글은 ν•¨κ»˜ ν”„λ‘œμ νŠΈ ν–ˆλ˜ Back End κΏˆλ‚˜λ¬΄ Crewκ°€ μž‘μ„±ν•œ κ²ƒμ΄μ—μš”.

μ£Όλ‹ˆν•˜λž‘λ„ Back End κ°œλ°œμ„ λ§‘μ•˜μ—ˆλ‹΅λ‹ˆλ‹€!

저희 ν”„λ‘œμ νŠΈμ—μ„œλŠ” JUnit κ³Ό Mockitoλ₯Ό μ‚¬μš©ν•˜μ—¬ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

 

 


 

 

πŸ‘¨‍πŸ’» κ²°λ‘ 


ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό 각 κ³„μΈ΅λ§ˆλ‹€ μž‘μ„±ν•¨μœΌλ‘œμ¨ 개발 μ‹œκ°„μ΄ λ‹¨μΆ•λ˜λŠ” 효과λ₯Ό 얻을 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈλ₯Ό μ‰½κ²Œν•˜κΈ° μœ„ν•΄μ„œ, μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ μ½”λ“œλ₯Ό μ‰½κ²Œ 짜게 λ˜μ—ˆκ³  κ²°κ΅­ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό 짜기 μœ„ν•΄ λ…Έλ ₯ν•˜λ‹€ λ³΄λ‹ˆ, μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ μ½”λ“œκ°€ κΉ”λ”ν•΄μ§€λŠ” 것을 κ²½ν—˜ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

무엇보닀도 μ„œλ²„λ₯Ό μ‹€ν–‰μ‹œν‚€μ§€ μ•Šμ•„λ„, DB에 값을 넣지 μ•Šμ•„λ„, 직접 눈으둜 ν™•μΈν•˜μ§€ μ•Šμ•„λ„ ν…ŒμŠ€νŠΈ μ½”λ“œλ§Œ μ‹€ν–‰μ‹œν‚€λ©΄ μ—¬λŸ¬ λ‹¨μœ„ν…ŒμŠ€νŠΈλ₯Ό λͺ‡ 초 λ§Œμ— μ™„λ£Œν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” ν˜‘μ—…μ‹œ λ¬Έμ„œλ‘œ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— 가독성이 λ†’κ²Œ, F.I.R.S.T 원칙에 잘 λ§žμΆ°μ„œ μž‘μ„±ν•΄μ•Ό ν•œλ‹€λŠ”κ±Έ λŠλ‚„ 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

 

πŸ‘¨‍πŸ’» ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ™œ μž‘μ„±ν•΄μ•Ό ν• κΉŒμš”?


APIλ₯Ό κ°œλ°œν•˜λ©΄μ„œ ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” κΌ­ μž‘μ„±λ˜μ–΄μ•Ό ν•œλ‹€κ³  μƒκ°ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ§œμ§€ μ•Šκ³  ν…ŒμŠ€νŠΈλ₯Ό 진행할 경우 μ„œλ²„λ₯Ό 켜고, DB에 값을 λ„£λŠ” μž‘μ—…μ΄ 이루어져야 ν•©λ‹ˆλ‹€. 그리고 κ°œλ°œμžκ°€ μ •μƒμ μœΌλ‘œ 둜직이 처리 λ˜μ—ˆλŠ”μ§€ 직접 눈으둜 ν™•μΈν•΄μ•Όν•©λ‹ˆλ‹€.

ν•˜μ§€λ§Œ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•œλ‹€λ©΄ μ„œλ²„λ₯Ό μ‹€ν–‰ν•˜μ§€ μ•Šκ³ λ„, DB에 값을 넣지 μ•Šμ•„λ„ λΉ λ₯Έ μ‹œκ°„μ•ˆμ— ν…ŒμŠ€νŠΈλ₯Ό 진행할 수 μžˆλ‹€λŠ” 것을 느끼게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€. ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ‹€ν–‰ μ‹œν‚€κΈ°λ§Œ ν•˜λ©΄ μˆ˜μ‹­ms μ•ˆμ— ν…ŒμŠ€νŠΈλ₯Ό 진행할 수 있게 λ©λ‹ˆλ‹€.

λ˜ν•œ ν…ŒμŠ€νŠΈ μ½”λ“œ μžμ²΄κ°€ λ¬Έμ„œλ‘œμ„œμ˜ 역할도 κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— ν˜‘μ—…μ˜ 생산성을 λ†’νž 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

 

πŸ‘¨‍πŸ’» μ›Ή 계측 λ§ˆλ‹€ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ–΄λ–»κ²Œ μž‘μ„±ν–ˆλ‚˜μš”?


Controller Layer

@WebMvcTest λ‹¨μœ„ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

μŠ€ν”„λ§ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰ν•˜λŠ” 것이 μ•„λ‹Œ κ°€μ§œ 객체인 MockMvcλ₯Ό λ§Œλ“€μ–΄μ„œ MVC λ™μž‘μ„ ν…ŒμŠ€νŠΈ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 MockMvcλ₯Ό μ΄μš©ν•΄ λΈŒλΌμš°μ €μ—μ„œ λ°œμƒν•˜λŠ” μš”μ²­μ„ κ°€μƒμœΌλ‘œ λ§Œλ“€κ³  μ»¨νŠΈλ‘€λŸ¬κ°€ μ‘λ‹΅ν•˜λŠ” λ‚΄μš©μ„ 기반으둜 검증을 μˆ˜ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

@Autowired
MockMvc mvc;

@MockBean
MessageSenderService messageSenderService;


@Test
public void μͺ½μ§€_전솑_성곡() throws Exception {


        DefaultRes result = new DefaultRes(HttpStatus.OK.value(), "전솑성곡");

        given(messageSenderService.messageSender(any())).willReturn(result);


        mvc.perform(post("/api/users/messages")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\n" +
                        "  \"content\": \"μ•ˆλ…•ν•˜μ„Έμš”\",\n" +
                        "  \"receiver_id\": 1,\n" +
                        "  \"sender_id\": 2\n" +
                        "}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.message", equalTo(result.getMessage())));

    }

 

Service Layer

Service Layerκ°€ 잘 μž‘λ™ν•˜λŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ Service Layer만 λ…λ¦½μ μœΌλ‘œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

즉, μ‹€μ œ λΉ„μ§€λ‹ˆμŠ€ 둜직이 잘 μž‘μ„± λ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜λŠ” ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 given, when, then 으둜 λ‚˜λˆ  μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

ν•„μš”ν•œ 데이터λ₯Ό 미리 κΈ°μž…ν•˜κ³ , λΉ„μ§€λ‹ˆμŠ€ 둜직이 μ˜¬λ°”λ₯΄κ²Œ μ²˜λ¦¬λ˜λŠ” 지 ν…ŒμŠ€νŠΈν•˜λŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

@Test
public void μͺ½μ§€_전솑_성곡(){

//given
User user1 =newUser();
User user2 =newUser();

given(userRepository.findById(1L)).willReturn(Optional.of(user1));
given(userRepository.findById(2L)).willReturn(Optional.of(user2));

//when
DefaultRes defaultRes = messageSenderService.messageSender(new MessageSenderRequestDto(1L, 2L, "μ•ˆλ…•ν•˜μ„Έμš”"));

//then
assertThat(defaultRes.getMessage()).isEqualTo("전솑성곡");
assertThat(defaultRes.getStatusCode()).isEqualTo(200);
}

@Test
public void μͺ½μ§€_전솑_μ‹€νŒ¨_λ°œμ‹ μžμ—†μŒ(){

//given
User user1 =newUser();
User user2 =newUser();

given(userRepository.findById(1L)).willReturn(Optional.of(user1));
given(userRepository.findById(2L)).willReturn(Optional.of(user2));

//when
DefaultRes defaultRes = messageSenderService.messageSender(new MessageSenderRequestDto(3L, 2L, "μ•ˆλ…•ν•˜μ„Έμš”"));

//then
assertThat(defaultRes.getMessage()).isEqualTo("λ°œμ‹ μžμ •λ³΄μ—†μŒ");
assertThat(defaultRes.getStatusCode()).isEqualTo(422);

}

 

 

Repository Layer

@DataJpaTest μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ JPA κ΄€λ ¨ ν…ŒμŠ€νŠΈ μ„€μ •λ§Œ λ‘œλ“œν•˜μ—¬ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

λ°μ΄ν„°μ†ŒμŠ€μ˜ 섀정이 정상적인지, JPAλ₯Ό μ‚¬μš©ν•˜μ—¬ 데이터λ₯Ό μ œλŒ€λ‘œ 생성, μˆ˜μ •, μ‚­μ œν•˜λŠ”μ§€ λ“±μ˜ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ μ½”λ“œ μ‹€ν–‰ ν›„ μžλ™ rollBack이 되기 λ•Œλ¬Έμ— DB에 side effectκ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

λ˜ν•œ, Querydsl μ½”λ“œλ₯Ό κ΅¬ν˜„ν•˜κ³  μ˜¬λ°”λ₯Έ 쿼리가 μ‹€ν–‰ λ˜λŠ”μ§€μ— λŒ€ν•œ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

 

@Test
void νšŒμ›_μ €μž₯(){

   //given
   User user = User.builder()
		               .email("test@test.com")
		               .password("1234")
	                 .name("test")
	                 .nickName("ν…ŒμŠ€ν„°")
	                 .phoneNumber("010-1234-5678")
	                 .grade(UserGrade.USER)
	                 .build();

    //when
    User saveUser = userRepository.save(user);

     //then
     assertThat(user).isEqualTo(saveUser);

}

 

 


 

μ£Όλ‹ˆν•˜λž‘μ˜ 글이 λ§ˆμŒμ— λ“œμ…¨λ‚˜μš”? ꡬ독과 곡감! 그리고, λŒ“κΈ€μ€ μ£Όλ‹ˆν•˜λž‘μ—κ²Œ λ§Žμ€ 힘이 λ©λ‹ˆλ‹€

728x90
λ°˜μ‘ν˜•

'Back-End μž‘μ—…μ‹€ > Spring Framework' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

[Spring] μŠ€ν”„λ§ 핡심 원리  (0) 2021.09.10
[도전 기술!] JPA와 Querydsl  (0) 2021.08.25
[도전 기술!] Web Layer  (0) 2021.08.23
[Spring Boot] AOP  (0) 2021.08.20
[Spring Boot] Spring Data JPA  (0) 2021.08.19