롬복이란?

  • 자바 개발자들의 필!수! 라이브러리 롬복, 롬복이란 무엇인지와 어떻게 사용하는지 알아보자.

롬복은 자바 개발할 때 자주 사용하는 코드 Getter, Setter, 기본생성자, toString 등을 어노테이션으로 자동 생성해 줍니다.

인텔리제이에서 프로젝트에 롬복 추가

  1. Build.gradle에 코드 추가
  2. compile('org.projectlombok:lombok')
  3. Gradle 새로고침하여 라이브러리(의존성) 추가
  4. Lombok 플러그인 다운로드 및 인텔리제이 재시작
  5. 롬복에 대한 설정 팝업 클릭 or [Preferences > Build,... > Compiler > Annotaion Processor]에서 Enable annotaion processing 체크

롬록으로 리팩토링하기

Hello Controller 코드 롬복으로 전환하기

  1. web 패키지에 dto 패키지 추가
  • 모든 응답 Dto를 관리
  1. Hello ResponseDto 생성
package com.choee.service.springboot.web.dto; 
import lombok.Getter; 
import lombok.RequiredArgsConstructor; 
@Getter 
@RequiredArgsConstructor 
public class HelloResponseDto { 
	private final String name; 	
    private final int amount; 
}

@Getter

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

@RequiredArgsConstructor

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

Dto에 적용된 롬복이 잘 작동하는지 테스트 코드 작성

package com.choee.service.springboot.web.dto;

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

public class HelloResponseDtoTest {

    @Test
    public void 롬복_기능_테스트() {
        // 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

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

isEqualTo

  • assertj의 동등 비교 메소드
  • assertThat에 있는 값과 isEqualTo의 값을 비교해서 같을 때만 성공

Junnit과 비교하여 assertj의 장점

  • CoreMatchesrs와 달리 추가적으로 라이브러리가 필요하지 않다.
  • -> Junit의 asserThat을 쓰게 되면 is()와 같이 CoreMatchers 라이브러리가 필요하다.
  • 자동완성이 좀 더 확실하게 지원된다.
  • -> IDE에서 CoreMatcher와 같은 Matcher 라이브러리의 자동완성 지원이 약하다.

!트러블슈팅!

  • 문제: 그레이들 5에서 롬복을 비롯해서 Querydsl 등의 플러그인 설정 방법 변경
  • 해결: build.gradle 의존성에 annotationProcessor('org.projectlombok:lombok') 추가
  • 비고: gradle 5 이상부터 어노테이션을 구별해서 추가해야 한다.

HelloReponseDto를 사용하여 HelloController에 코드 추가

package com.choee.service.springboot.web;

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

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.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
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가_리턴된다() throws Exception {
        String hello = "hello";

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

    // 추가된 API 테스트 코드
    @Test
    public void helloDto가_리턴된다() 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)));
    }
}

param

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

jsonPath

  • JSON 응답값을 필드별로 검증할 수 있는 메소드
  • $를 기준으로 필드명을 명시

테스트 코드 필요성

  • 대부분의 서비스 회사가 테스트 코드에 관해 요구
  • 프로젝트형 코딩 테스트에 단위 테스트 필수조건 요구
  • 테스트 자동화 경험 우대

테스트 코드란?

** TDD vs 단위 테스트(Unit Test) **

TDD

  • 테스트가 주도하는 개발
  • 테스트 코드를 먼저 작성
  • 레드 그린 사이클
  1. 항상 실패하는 테스트를 먼저 작성: Red
  2. 테스트가 통과하는 프로덕션 코드를 작성: Green
  3. 테스트가 통과하면 프로덕션 코드를 리팩토링: Refactor

단위 테스트

  • TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것을 의미
  • TDD와 달리 테스트 코드를 꼭 먼저 작성할 필요 X, 리팩토링 포함 X
  • 순수하게 테스트 코드만 작성하는 것을 의미

테스트 코드를 왜 작성해야 하는가?

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

테스트 코드를 작성하면 좋은 점

  1. 톰캣을 내렸다가 다시 실행하는 일을 반복하지 않아도 된다.
  2. 눈으로 검증(수동검증)하지 않고 테스트 코드로 자동검증이 가능하다.
  3. 개발자가 만든 기능을 안전하게 보호해준다.( 규모가 큰 서비스에서 새로운 기능이 추가될 때, 기존 기능이 잘 작동되는 것을 보장해준다.)

테스트 코드 작성을 도와주는 프레임워크: xUnit

  • 가장 대중적인 테스트 프레임워크
  • 개발환경(x)에 따라 Unit 테스트를 도와주는 도구
  • JUnit - Java
  • DBUnit - DB
  • ... 등

+ Recent posts