Group Study (2024-2025)/Spring 입문

[Spring 입문] 1주차 - 인텔리제이 환경 설정과 테스트코드

choubung 2024. 9. 29. 23:02

1. 인텔리제이로 스프링 부트 시작하기

▷ 인텔리제이가 갖는 장점

  • 강력한 추천 기능
  • 훨씬 더 다양한 리팩토링과 디버깅 기능
  • 이클립스의 깃에 비해 훨씬 높은 자유도
  • 프로젝트를 시작할 때 인덱싱을 하여 파일을 비롯한 자원들에 대한 빠른 검색 속도
  • HTML과  CSS, JS, XML에 대한 강력한 기능 지원
  • 자바, 스프링부트 버전업에 맞춘 빠른 업데이트

 

▷ [실습] Gradle 프로젝트를 스프링 부트 프로젝트로 변경하기

buildscript {
    ext {
        springBootVersion = '2.1.7.RELEASE'
    }
    repositories {
        mavenCentral()
        jcenter() // 2024년 8월 15일 서버 종료, 모든 요청은 MavenCentral로 redirect
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
} // 프로젝트 의존성 관리를 위한 설정

plugins {
    id 'java'
    id 'eclipse'
    id 'org.springframework.boot'version '2.6.7'
    id 'io.spring.dependency-management' version '1.0.12.RELEASE' // 스프링부트의 의존성을 관리하는 플러그인
}

group = 'com.jojoldu.book'
version = '1.0-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.projectlombok:lombok')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

test {
    // JUnit 5 플랫폼 사용
    useJUnitPlatform()
}
  • ext
    • build.gradle에서 사용하는 전역변수를 설정하겠다는 의미
    • 여기서는 springBootVersion 전역변수를 생성하고 그 값을 '2.1.7.RELEASE'로 하겠다는 의미

 

  • repositories
    • 각종 의존성(라이브러리)들을 어떤 원격 저장소에서 받을지를 정함

 

  • dependencies
    • 프로젝트 개발에 필요한 의존선을 선언하는 곳

 

 

2. 스프링 부트에서 테스트 코드를 작성하자

▷ 테스트 코드 소개

  • TDD
    • 테스트가 주도하는 개발, 테스트 코드를 먼저 작성하는 것부터 시작
    • 항상 실패하는 테스트를 먼저 작성 (Red)
      → 테스트가 통과하는 프로덕션 코드를 작성 (Green)
      → 프로덕션 코드를 리팩토링 (Refactor)

 

  • 단위 테스트
    • TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것
    • (TDD와 달리) 테스트 코드를 꼭 먼저 작성해야하는 건 아님
    • 리팩토링 미포함

 

▷ 테스트 코드의 이점

  • 빠른 피드백 → 톰캣을 재시작할 필요가 없으므로
  • 자동 검증이 가능
  • 개발자가 만든 기능을 안전하게 보호 → 새 기능이 추가될 때 기존 기능이 잘 작동되는 것을 보장

 

▷ [실습] Hello Controller 테스트 코드 작성하기

// Application.java

package com.jojoldu.book.springboot;

import ...

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args); // 내장 WAS 실행
    }
}
  • Application 클래스
    • 앞으로 만들 프로젝트의 메인 클래스가 됨
    • @SpringBootApplication
      • 스프링부트, 스프링 Bean 읽기와 생성을 자동 설정
      • 이 어노테이션이 있는 위치부터 설정을 읽어가므로 → 프로젝트 최상단에 위치해야함

 

  • 내장 WAS
    • 별도로 외부에 WAS를 두지 않고 애플리케이션을 실행할 때 내부에서 WAS를 실행
    • 서버에 톰캣을 항상 설치할 필요가 없음 → Jar 파일로 실행하면 됨
    • 언제 어디서나 같은 환경에서 스프링부트를 배포할 수 있게 됨

 

// HelloController.java

package com.jojoldu.book.springboot.web;

import ...

@RestController // JSON을 반환하는 컨트롤러로 만듦
public class HelloController {
    @GetMapping("/hello") // GET 요청(HTTP 메소드)을 받을 수 있는 API로 만듦
    public String hello() {
        return "hello";
    }
}
// HelloControllerTest.java

package com.jojoldu.book.springboot.web;

import ... // 생략

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = HelloController.class) 
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc; // 웹 API를 테스트할 때 사용

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

        mvc.perform(get("/hello")) // /hello 주소로 GET 요청을 함
                .andExpect(status().isOk()) // HTTP Header의 Status를 검증
                .andExpect(content().string(hello)); // 응답 본문의 내용을 검증 (hello가 맞는지 확인)
    }
}
  • 수동 검증 하지 않고 테스트 코드를 작성해야 함
    → 테스트 코드로 먼저 검증 후, 정말 못 믿겠을 때만 프로젝트 실행

 

 

▷ 롬복(Lombok)

  • 자바 개발자들의 필수 라이브러리
  • 자바를 개발할 때 자주 사용하는 코드인 Getter, Setter, 생성자, toString 등을 어노테이션으로 자동 생성

 

▷ [실습] Hello controller 코드를 롬복으로 전환하기

// HelloResponseDto.java

package com.jojoldu.book.springboot.web.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter // 선언된 모든 필드의 get 메소드를 생성
@RequiredArgsConstructor // 선언된 모든 final 필드가 포함된 생성자를 생성
public class HelloResponseDto {
    private final String name;
    private final int amount;
}
// HelloResponseDtoTest.java

package com.jojoldu.book.springboot.web.dto;

import ...

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
    • assetj(테스트 검증 라이브러리)의 검증메소드
      • cf. Junit과 비교한 장점
        1. CoreMatchers와 달리 추가적으로 라이브러리가 필요하지 않음
        2. 자동완성이 좀 더 확실히 지원됨
    • 검증하고 싶은 대상을 메소드 인자로 받음
    • 메소드 체이닝(메소드를 줄줄이 이어 사용할 수 있게 하는 것)이 지원됨

 

  • isEqualTo
    • assertj의 동등 비교 메소드
    • 값이 같을 때만 성공

 

package com.jojoldu.book.springboot.web;

import ...

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

	// 추가한 부분
    @GetMapping("hello/dto")
    public HelloResponseDto helloDto(@RequestParam("name") String name, 
    								@RequestParam("amount") int amout) {
        return new HelloResponseDto(name, amout);
    }
}
  • RequestParam
    • 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션
    • 여기서는 외부에서 name(@RequestParam("name"))이란 이름으로 넘긴 파라미터를 메소드 파라미터 name(String name)에 저장

 

package com.jojoldu.book.springboot.web;

import ...

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

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

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

    // 추가된 코드
    @Test
    public void helloDtoIsReturned() 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 응답값을 필드별로 검증할 수 있는 메소드
    • $를 기준으로 필드명을 명시

 

  • ⚠ 실습 도중 생긴 문제
    • amount를 amout로 오타 내 코드 오류 발생...