롬복(Lombok)이란?   

 

 롬복은 자바로 개발할 때 자주 사용되는 코드인 Getter, Setter, 기본 생성자, toString 등을 어노테이션으로 자동 생성해준다. 참고로 이클립스의 경우에는 롬복 설치가 번거롭지만, 인텔리제이에서는 플러그인 덕분에 쉽게 설정이 가능하다. 

 


 

    롬복(Lombok) 플러그인 설치하기   

 

 롬복 플러그인을 설치하기 전에 build.gradle에 다음의 코드를 추가해야 한다.

 

 

 

 build.gradle에 코드를 추가했다면 맨 오른쪽에 Gradle 버튼을 누르고 아래 빨간색으로 표시된 새로고침 버튼을 눌러주어 라이브러리(의존성)를 내려 받는다.

 

 

 라이브러리를 다 받았다면 롬복 플러그인을 설치해야 한다. 윈도우는 [ctrl + shift + A], 맥은 [command + shift + A]를 눌러  Action 탭을 선택한다. 그리고 plugins를 검색하고 선택한다. 그 후 Marketplace 탭에서 "lombok"을 검색하고 [install] 버튼을 눌러 설치한다. 설치가 완료되면 아래 오른쪽에 팝업창이 뜨면서 Enable annotation processing이 뜬다. 클릭 해주고 인텔리제이를 재시작 해준다.

 

 

 만약 팝업창이 뜨지 않았다면 [File]-[Settings]-[Build, Execution, Deployment]-[Compiler]-[Annotation Processor]로 가서 [Enable annotation processing]에 체크를 해준다.

 

 


 

    기존 코드 롬복으로 리팩토링 하기   

 

 먼저, main 폴더의 web 패키지에 dto 패키지를 추가하고 HelloResponseDto를 생성하고 다음과 같이 코드를 작성한다.

 

 

package com.daldalhada.springboot.web.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
    private final String name;
    private final int amount;
}

 

 

1. @Getter

  • 선언된 모든 필드의 get 메소드를 생성해준다.

2. @RequiredArgsConstructor

  • 선언된 모든 final 필드가 포함된 생성자를 생성해 준다.
  • final이 없는 필드는 생성자에 포함되지 않는다. 

 

 

 그리고 잘 작동하는지 확인하기 위해 간단한 테스트 코드를 작성한다. test 폴더의 web 패키지에 dto 패키지를 추가하고 HelloResponseDtoTest를 생성하고 다음과 같이 코드를 작성한다.

 

 

 

package com.daldalhada.springboot.web.dto;

import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class HelloResponseDtoTest {
    @Test
    public void lombok_test() {
        //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);
    }
}

 

1. assertThat

  • assertj라는 테스트 검증 라이브러리의 검증 메소드로 검증하고 싶은 대상을 메소드 인자로 받는다.
  • 메소드 체이닝이 지원되어 isEqualTo와 같은 메소드를 이어서 사용할 수 있다.

2. isEqualTo

  • assertThat에 있는 값과 isEqualTo 값을 비교해서 같을 때만 성공한다.

※참고: 여기서 Junit의 기본 aseertThat이 아닌 assertj의 assertThat을 사용한 이유는 추가적인 라이브러리가 필요하지 않고 자동완성이 좀 더 확실하게 지원되기 때문이다. 

 

작성된 테스트 메소드를 실행해보면 정상적으로 기능이 수행되는 것을 알 수 있다. @Getter로 get 메소드가, @RequiredArgsConstructor로 생성자가 자동으로 생성되었다. 

 

 

 

이제 HelloController에도 새로 만든 ResponseDto를 사용하도록 코드를 추가한다.

 

package com.daldalhada.springboot.web;

import com.daldalhada.springboot.web.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);
    }
}

 

1. @RequestParam

  • 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션이다.
  • 여기서는 name이란 이름으로 넘긴 파라미터를 String name에 저장한다. amount도 마찬가지이다.

 

 이제 HelloController도 테스트를 하기 위해 HelloControllerTest에도 다음과 같이 코드를 추가한다. 

 

 

package com.daldalhada.springboot;

import com.daldalhada.springboot.web.HelloController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void hello_Return() throws Exception {
        String hello = "hello";

        mvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(hello));
    }

    @Test
    public void helloDto_Return() throws Exception {
        String name = "hello";
        int amount = 1000;

        mvc.perform(
                get("/hello/dto")
                        .param("name", name)
                        .param("amount", String.valueOf(amount)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name", is(name)))
                .andExpect(jsonPath("$.amount", is(amount)));
    }
}

 

1. param

  • API 테스트할 때 사용될 요청 파라미터를 설정한다. (단, 값은 String 값만 허용)
  • 따라서 숫자/날짜 등의 데이터도 문자열로 변경해야 한다.

2. jsonPath

  • JSON의 응답값을 필드별로 검증할 수 있는 메소드
  • $를 기준으로 필드명을 명시한다. 여기서는 name과 amount를 $.name과 $.amount로 검증한다. 

 

 

 이제 추가된 API도 테스트를 실행해보면

 

 

 JSON이 리턴되는 API도 통과하는 것을 볼 수 있다.

    (1)  테스트 코드   

 

 TDD(Test Driven Development)는 테스트가 주도하는 개발을 말하며 테스트 코드를 먼저 작성하는 것부터 시작한다. 아래 그림과 같이 RED-GREEN Cycle을 말한다.

 

  1. RED: 항상 실패하는 테스트를 먼저 작성한다.
  2. GREEN: 테스트가 통과하는 프로덕션 코드를 작성한다.
  3. REFACTOR: 테스트가 통과하면 프로덕션 코드를 리팩토링한다. 

 

출처: https://hyem-study.tistory.com/98?category=893981

 

 반면 단위 테스트는 TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것을 말한다. TDD와 달리 테스트 코드를 꼭 먼저 작성하는 것이 아니고 리팩토링 작업도 포함되지 않는다. 순수하게 테스트 코드만 작성하는 것을 말한다. 

 


 

    (2) 단위테스트 장점    

 

 위키피디아에서의 단위 테스트의 장점은 다음과 같다.

 

  • 개발단계 초기에 문제를 발견하게 도와준다.
  • 개발자가 나중에 코드를 리팩토링하거나 라이브러리 업그레이드 등에서 기존 기능이 올바르게 작동하는지 확인할 수 있다. 
  • 기능에 대한 불확실성을 감소시킬 수 있다.
  • 시스템에 대한 실제 문서를 제공한다. 즉, 단위 테스트 자체를 문서로 사용할 수 있다. 

 

 이 밖에도 더 많은 장점이 있는데 단위 테스트를 하기 전의 개발 방식은 보통 다음가 같은 것이다. 

 

  1. 테스트 코드를 작성한다.
  2. 프로그램(Tocmat 등)을 실행한다.
  3. Postman과 같은 API 테스트 도구로 HTTP 요청한다.
  4. 요청 결과를 출력(console.log, System.out.println 등)하여 눈으로 검증한다.
  5. 결과가 다르면 다시 프로그램을 중지하고 코드를 수정한다. 

 여기서 2~5번은 매번 코드를 수정할 때마다 반복해야 한다. 

 


 

    (3)  테스트 코드 작성하기    

 

 프로젝트 내 [src] - [main] - [java] 폴더의 마우스 오른쪽 버튼을 클릭하여 [New ▶ Package]를 차례로 선택해서 생성한다. 일반적으로 패키지명은 웹 사이트 주소의 역순으로 한다고 한다. 예를 들어, admin.daldalhade.com이라는 사이트라면 패키지명은 com.daldalhade.admin으로 하면 된다. 

 

 

 패키지가 생성됐으면 패키지를 마우스 오른쪽 버튼으로 클릭하고 [New ▶ Java Class]를 차례로 선택하면 된다. 클래스의 이름은 Application으로 설정한다.

 

 

 

그리고 클래스의 코드를 다음과 같이 작성한다. 

 

package com.daldalhada.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

 

 방금 생성한 Application 클래스는 프로젝트의 메인 클래스가 된다. @springBootApplication으로 인해 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성이 모두 자동으로 설정된다. 특히,  @springBootApplication이 있는 위치부터 설정을 읽기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치해야 한다. 

 

 main 메소드에서 실행하는 SpringApplication.run으로 인해 내장 WAS를 실행한다. 여기서 내장 WAS는 별도로 외부에 WAS를 두지 않고 애플리케이션을 실행할 때 내부에서 WAS를 실행하는 것을 이야기 한다. 이러면 항상 서버에 톰캣을 설치할 필요가 없게 되고, 스프링 부트로 만들어진 Jar 파일(실행 가능한 Java 패키징 파일)로 실행하면 된다. 

 

 스프링 부트는 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있기 때문에 내장 WAS를 사용하는 것을 권장하고 있다. 외장 WAS를 쓰게 되면 모든 서버는 WAS의 종류와 버전, 설정일 일치시켜야 하기에 복잡하다. 1대면 문제가 없지만 30대의 서버가 존재할 때 WAS의 버전을 올린다고 하면 실수할 여지가 많고 시간적인 면에서도 비효율적이 된다. 

 

 

 이번에는 테스트를 위한 Controller를 만들기 위해 패키지 하위에 web이라는 패키지를 만든다.

 

 

 그리고 테스트를 해 볼 Hello Cotroller를 하나 생성한다.

 

 

 

package com.daldalhada.springboot.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";
    }
}

 

 위의 코드 중 @RestController는 JSON을 반환하는 컨트롤러로 만들어 주는 역할을 하고 @GetMapping은 HTTP 메서드인 Get의 요청을 받을 수 있는 API를 만들어 준다. 

 

 작성한 코드가 제대로 작동하는지 테스트를 하기 위해 WAS를 실행하지 않고 테스트 코드로 검증해보기 위해 [src]-[test]-[java] 디렉토리에 앞에 생성했던 패키지를 그대로 다시 생성한다. 그리고 테스트 코드를 작성할 클래스를 생성한다. 일반적으로 테스트 클래스는 대상 클래스 이름에 Test를 붙인다고 한다. 따라서, HelloControllerTest로 생성한다. 

 

 

 생성되면 다음과 위의 사진과 같게 되고 HelloControllerTest에 코드를 추가한다. 

 

package com.daldalhada.springboot;

import com.daldalhada.springboot.web.HelloController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void hello_return() throws Exception {
        String hello = "hello";

        mvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(hello));
    }
}

 

 위의 코드는 다음과 같다.

 

1. @RunWith(SpringRunner.class)

  • 테스트를 진행할 때 JUnit에 내장된 실행자 외에 다른 실행자를 실행시키는데 여기서는 SpringRunners라는 스프링 실행자를 사용한다.
  • 즉, 스프링 부트 테스트와 JUnit 사이를 연결하는 역할을 한다.

2. @WebMvcTest

  • 수 많은 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션이다. 
  • 스프링이 관리하는 모든 빈을 등록시켜 테스트하는 @SpringBootTest와 달리 @WebMvcTest는 web 관련 빈들만 등록하므로 가볍다.

3. @Autowired

  • 스프링이 관리하는 빈을 주입 받는다.

4. private MockMvc mvc

  • 웹 API를 테스트 할 때 사용되고 스프링 MVC 테스트의 시작점이다. 
  • 이 클래스를 통해 HTTP의 메서드인 GET, POST 등에 대한 API 테스트를 할 수 있다. 

5. mvc.perform(get("/hello"))

  • MockMvc를 통해 /hello 주소로 HTTP GET 요청을 한다. 
  • 체이닝이 지원되어 .andExpect에 여러 검증 기능을 이어서 선언할 수 있다.

6. andExpect(status().isOk())

  • mvc.perform의 결과를 검증하며 특히, HTTP Header의 Status를 검증한다. 
  • isOk()는 Status code가 200인지 아닌지 검증한다.

7. andExpect(content().string(hello))

  • 응답 본문의 내용을 검증한다.
  • Controller에서 "hello"를 리턴하기 때문에 값이 맞는지 검증하는 역할을 한다.

 

 HelloControllerTest.java에서 빨간색으로 표시된 화살표 부분을 클릭하고 Run HelloControllerTest.java를 클릭해 테스트 코드를 실행해보면 테스트가 통과되었다면 Tests passed가 뜬다. 

 

 HelloControllerTest.java에서 빨간색으로 표시된 화살표 부분을 클릭하고 Run HelloControllerTest를 클릭해 테스트 코드를 실행해보면 테스트가 통과되었다면 Tests passed가 뜬다. 

 

 이번에는 Application.java로 가서 수동으로 실행해 정상적으로 값이 출력되는지 확인해본다. 빨간색으로 표시된 화살표 버튼을 클릭하고 Run Application을 클릭하면 톰캣 서버가 8080포트로 실행되었다는 것이 콘솔 창에 출력된다. 

 

 

 

 

 

 이제 웹 브라우저에서 localhost:8080/hello로 접속하여 문자열 hello가 잘 나오는지 확인한다. 웹 브라우저를 열어 localhost:8080/hello로 접속한다.

 

 하지만, localhost:8080/login으로 바뀌어 다음과 같은 화면이 뜨는 경우가 발생할 수 있다. 처음에는 포트 충돌 문제인지 알고 포트를 변경하는 방법을 시도했다.

 

 포트를 변경하는 방법은 먼저, [build]-[resources]-[main] 또는 [src]-[main]-[resource]의  application.properties을 찾는다. 

 

 

 

 application.properties을 열어 server.port 부분을 원하는 포트 번호로 수정한다. 

 

 

 하지만, 포트를 변경해도 소용없었고 구글링 하던 중 스택 오버 플로우에서 Username은 user로 Password는 콘솔창에 나와있는 password를 치면 된다고 한다.

 

 

 결과는 정상적으로 출력되었고 테스트 코드의 결과와 같은 것을 알 수 있다.

 

'스프링 부트 프로젝트 > 테스트 코드 작성' 카테고리의 다른 글

4. 롬복 설치하기  (0) 2020.10.05