-
Spring Boot와 AWS 로 혼자 구현하는 웹서비스 1, 2 장책/스프링부트와 AWS로 혼자 구현하는 웹서비스 2022. 4. 22. 23:45
이동욱님이 쓰신 "스프링부트와 AWS로 혼자 구현하는 웹 서비스" 를 현재(SpringBoot 2.6.7) 버전에 맞게 일부 변형했습니다. 전반적인 내용은 동일합니다.
1장
intellij 의 간단한 세팅 및 github 와 intellij 를 연결한 부분이 나옵니다. 이 부분은 다른 블로그 글도 있으니 참고하셔도 될 거 같습니다.
2장
테스트 코드는 현재 웹 서비스에서 매우 중요한 요소입니다. 테스트 코드에서 꼭 짚고 넘어가야 하는것은 TDD와 단위테스트(unit test)입니다.
2.1 테스트 코드 소개
TDD
테스트가 주도하는 개발(Test-Driven-Development, 혹은 Test-First-Development) 을 의미합니다.
- 항상 실패하는 테스트를 먼저 작성하고(RED)
- 테스트가 통과하는 프로덕션 코드를 작성하고(Green)
- 테스트가 통과하면 프로덕션 코드를 리팩토링합니다.(Refacetor)
더 자세한 내용은 (https://repo.yona.io/doortts/blog/issue/1) 를 참고하면 됩니다.
단위 테스트
TDD 의 첫번째 단계인 기능 단위의 테스트 코드를 작성 을 얘기합니다. 즉, 순수하게 테스트 코드만 작성하는 것을 얘기합니다.
테스트 코드를 작성할 때 이점은 무엇일까요? 불확실성을 많이 제거 해준다, 나중에 개발자가 코드를 리팩토링 하거나 라이브러리를 업그레이드 등에 기존 기능이 올바르게 작동하는지를 확인할 수 있습니다.(예, 회귀 테스트) 등 여러가지가 있지만 가장 크게 공감할 수 있는건 톰캣을 띄워서 System.out.println 등으로 확인할 시간을 줄여준다. 는 측면이 있습니다. 톰캣을 띄우면 크게 몇 분 이상이 소요됩니다. 만약 테스트가 개발자가 원하는대로 나오지 않으면 다시 톰캣을 재시작해야 합니다. 이 과정을 계속하면 크게 몇 시간이 소비되기도 합니다.
또한 자동검증 이 되게 합니다. 작성된 단위테스트를 실행만 하면 더는 수동검증은 필요가 없습니다.
마지막으로, 개발자가 만든 기능을 안전하게 보호 해줍니다. 코드를 작성하다보면 여러 코드들이 상호간 영향을 주고 받게 됩니다. 이 과정에서 A 코드를 수정했을 때 B 코드에 영향을 줘 B 코드에 대한 테스트 케이스가 실패해 기존 코드에 영향이 없도록 수정해 줄 수있습니다.
언어별로 테스트 코드 여러 테스트코드 프레임워크가 있습니다. 가장 대중적인 테스트 프레임워크로는 xUnit 이 있습니다.
개발환경(x) 에 따라 Unit. 테스트를 도와주는 도구라고 생각하면 됩니다.
- Java - JUnit
- DB - DBUnit
- .net - Unit
본격적으로 코드를 작성해보겠습니다.
package com.purple.purplebook; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class PurpleBookApplication { public static void main(String[] args) { SpringApplication.run(PurpleBookApplication.class, args); } }
@SpringBootApplication
- 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성을 모두 자동으로 설정됩니다.
- @SpringBootApplication 이 있는 위치부터 설정을 읽어가기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치 해야합니다.
- main 메소드에서 실행하는 SpringApplication.run 으로 인해 내장 WAS(Web Application Server) 를 실행합니다.
- 내장 WAS란 별도의 외부에 WAS 를 두지 않고 애플리케이션을 실행할 때 내부에서 WAS를 실행하는 것을 의미합니다.
- 스프링 부트에선 내장 WAS 사용을 권장합니다.
- 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있기 때문입니다.
이전 포스팅에서 다양한 Spring Annotation을 다루었으므로 이후 Spring Annotion이 궁금하면 해당 블로그 글을 참고하시면 됩니다.
package com.purple.purplebook.web; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "hello"; } }
컨트롤러 코드를 이제 다 작성했으니 테스트 코드를 작성해보겠습니다.
package com.purple.purplebook.web; import static org.junit.jupiter.api.Assertions.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.web.servlet.MockMvc; @WebMvcTest(HelloController.class) class HelloControllerTest { @Autowired private MockMvc mvc; @Test void hello가_리턴된다() throws Exception { //given String hello = "hello"; //when mvc.perform(get("/hello")) //then .andExpect(status().isOk()) .andExpect(content().string(hello)); } }
- private MockMvc mvc
- 웹 API 를 테스트할 때 사용합니다.
- 스프링 MVC 테스트의 시작점입니다.
- 이 클래스를 통해 HTTP GET, POST 등에 대한 API 테스트를 할 수 있습니다.
- mvc.perform(get("/hello"))
- MockMvc를 통해 /hello 주소로 HTTP GET 요청을 합니다.
- 체이닝(Chaining) 이 되기 때문에 아래와 같이 여러 검증 기능을 이어서 선언할 수 있습니다.
- .andExpect(status().isOk())
- mvc.perform 의 결과를 검증합니다.
- HTTP Header 의 status를 검증합니다.
- isOk() 는 200인지 아닌지를 검증합니다.
- .andExpect(content().string(hello))
- mvc.perform의 결과를 검증합니다.
- 응답 본문의 내용을 검증합니다.
- Controller에서 "hello" 를 리턴하기 때문에 이 값이 맞는지 검증합니다.
위 코드대로 실행해보면 우리가 원하는 대로 테스트 코드가 잘 돌아가는것을 확인 할 수 있습니다. 참고로 수동으로 검증하고 테스트 코드를 작성하지는 않습니다. 역의 경우(테스트 코드 작성 -> 수동 검증) 는 충분히 가능합니다.
HelloController 코드 롬복으로 전환하기
이제 Lombok 을 사용한 DTO 를 만들어 보고 이와 관련된 테스트 코드도 만들어보겠습니다.
package com.purple.purplebook.dto; import lombok.Getter; import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor public class HelloResponseDto { private final String name; private final int amount; }
package com.purple.purplebook.dto; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HelloResponseDtoTest { @Test void 롬복_기능_테스트 () throws Exception { //given String name = "test"; int amount = 1000; //when HelloResponseDto dto = new HelloResponseDto(name, amount); //then assertThat(dto.getName()).isEqualTo(name); assertThat(dto.getAmount()).isEqualTo(amount); } }
참고로 테스트 코드의 assertThat 을 사용할 땐 Junit 의 assertThat 보다 assertj 의 assertThat 을 사용하는게 더 좋습니다.
- CoreMatchers 와 달리 추가적인 라이브러리가 필요하지 않습니다.
- Junit 의 assertThat 을 쓰게 되면 is() 와 같이 CoreMatchers 라이브러리가 필요합니다.
- 자동완성이 좀 더 확실하게 지원됩니다.
- IDE에서는 CoreMathers 와 같은 Matcher 라이브러리의 자동완성 지원이 약합니다.
자세한 설명은 백기선님의 유튜브를 참조하면 더 좋습니다.
위 테스트 코드가 잘 작동하는걸 확인했습니다. 이제 HelloController 에도 새로 만든 ResponseDto 를 사용하도록 코드를 추가하겠습니다.
package com.purple.purplebook.web; import com.purple.purplebook.dto.HelloResponseDto; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "hello"; } @GetMapping("/hello/dto") public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("amount") int amount) { return new HelloResponseDto(name, amount); } }
위와 같이 /hello/dto 로 들어오게 되면 Dto를 리턴하도록 했습니다.
package com.purple.purplebook.web; import static org.junit.jupiter.api.Assertions.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.hamcrest.Matchers.is; import com.purple.purplebook.dto.HelloResponseDto; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.web.servlet.MockMvc; @WebMvcTest(HelloController.class) class HelloControllerTest { @Test void helloDto가_리턴된다() throws Exception { //given String name = "hello"; int amount = 1000; HelloResponseDto dto = new HelloResponseDto(name, amount); //when mvc.perform(get("/hello/dto") .param("name", name) .param("amount", String.valueOf(amount))) //then .andExpect(status().isOk()) .andExpect(jsonPath("$.name", is(name))) .andExpect(jsonPath("$.amount", is(amount))); } ... }
- param
- API 테스트 할 때 사용될 요청 파라미터를 설정합니다.
- 단, 값은 String만 허용됩니다.
- 그래서 숫자/날짜 등의 데이터도 등록할 때는 문자열로 변경해야만 가능합니다.
- jsonPath
- JSON 응답값을 필드별로 검증할 수 있는 메소드입니다.
- $를 기준으로 필드명을 명시합니다.
- 여기서는 name과 amount를 검증하니 $.name, $.amount 로 검증합니다.
'책 > 스프링부트와 AWS로 혼자 구현하는 웹서비스' 카테고리의 다른 글
스프링 부트와 AWS 로 구현하는 웹서비스 3장 (0) 2022.05.01