Group Study (2022-2023)/Spring 입문

[Spring 입문] 1주차 스터디 - 스프링부트 시작 / 테스트 코드 작성하기

우주호 2022. 10. 2. 22:33

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

1.4 그레이들 프로젝트를 스프링부트 프로젝트로 변경

build.gradle 파일에서 해당 프로젝트의 플러그인 의존성 관리를 위한 설정 할 수 있다

buildscript {
    ext {
        springBootVersion = '2.1.7.RELEASE'
    }
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

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')
}

1. ext : build.gradle에서 사용하는 전역변수를 설정하겠다는 의미
→ springBootVersion 전역변수 생성
→ spring-boot-gradle-plugin라는 스프링부트 그레이들 플러그인의 2.1.7 RELEASE를 의존성으로 받음

2. apply plugin : ‘java’(’eclipse’, ‘org.springframework.boot’, ‘io.spring.dependency-management’) : 앞에서 선언한 플러그인 의존성들을 적용할 것인지 결정
→ io.spring.dependency-management는 스프링부트의 의존성을 관리해주는 플러그인이기 때문에 꼭 추가

3. repositories : 각종 의존성(라이브러리)들을 어떤 원격 저장소에서 받을지 결정
→ mavenCentral 많이 사용하지만 자유로운 라이브러리 업로드가 불편하여 최근에는 jcenter도 많이 사용

4. dependencies : 프로젝트 개발에 필요한 의존성들을 선언
→ 의존성 코드는 직접 작성해도 되고 자동완성으로 만들어도 되지만 특정 버전을 명시하지 않아야 맨 위에 작성한 ‘org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}’을 따라간다.

 

1.5 인텔리제이에서 깃과 깃허브 사용

교재에서 나온 화면과 달라서 아래처럼 진행

[Ctrl + Shift + A] 단축키를 이용하고 Action 검색창에서 gitignore 검색하면 Add to .gitignore이라는 메뉴가 보이는데 이를 이용하여 파일 생성

 

> 1장 레퍼런스 <

스프링부트에 대해 자세히 알아보자!!

A. 객체 지향 설계 (SOLID) 란?


위의 표에서 보이는 OCP, DIP는 자바로 작성할 때 제대로 지켜지지 않을 수도 있다. 이때 스프링 프레임워크는 OCP, DIP를 지원하고 추가적으로 다형성까지 제공해주면서 개발자들이 객체 지향 원칙을 지켜서 개발할 수 있도록 도와준다.

   DIP, 의존성 주입
   의존성 주입은 객체가 의존하는 다른 객체를 외부에서 선언하고 이를 주입받아서 사용하는 것이다. 따라서 이를 통해 객     체 간 의존성이 줄어들어서 재사용성이 높은 코드가 되고 테스트하기에도 좋은 환경이 만들어진다. 의존성 주입을 통해     서 객체 간 의존성이 줄어들면서 하나의 객체가 더 다양한 타입을 가질 수 있게 되었다. 즉, 다형성을 이용할 수 있도록       지원해준 것이다. 

   IoC(제어의 역전)
   제어의 역전 또한 다형성을 이용할 수 있도록 지원해준다. IoC는 컨트롤의 제어 권한이 개발자가 아닌 프레임워크에게       있어 Servelt, Bean 등 코드를 프레임워크가 대신 작성하는 것을 말한다. 즉, 객체 생성과 의존관계 설정 등을 프레임워       크가 대신 해준다는 의미이다.

따라서 역할구현을 구분해서 개발을 하는 것이 중점이 된 것이다.
쉽게 풀면 역할은 인터페이스이고 구현은 인터페이스를 구현한 객체라고 생각하면 된다. 역할과 구현을 구분한다면 다음과 같은 이점들을 얻을 수 있다.
  - 클라이언트는 대상의 역할(인터페이스)만 알면 된다
  - 클라이언트는 내부 구조의 변경에 영향 받지 않는다.
  - 클라이언트는 내부 구조를 알 필요가 없다.
  - 클라이언트는 구현 대상 자체를 변경해도 영향 받지 않는다.

위의 설명이 스프링/스프링부트 프레임워크를 사용하는 이유이다.


B. 스프링과 스프링부트의 차이점

먼저 스프링은 dependency 설정을 할 때 모든 dependency에 대해서 버전 관리를 해야 하는 번거로움이 있었다. 하지만 스프링부트의 경우 dependency를 더 쉽게 설정할 수 있고 버전 관리도 자동으로 해준다. 또한, 빌드 툴을 gradle을 사용하는 경우 자신이 필요한 dependency를 추가해주면 스프링부트에서 그에 따라 필요한 모든 dependency를 자동으로 추가하고 관리해준다.  

두 번째로 스프링은 configuration 설정을 할 때 어노테이션, bean 등 모든 등록을 수동으로 설정해주어야 한다. 하지만 스프링부트는 application.properties(혹은 yml) 파일에 설정하면 된다.

그리고 AutoConfiguration이라는 기능이 스프링부트에 있다. 이 기능으로 인해 어노테이션을 이용해서 많은 외부 라이브러리, 내장 톰캣 서버 등을 실행하고 @Component, @Controller, @Service 등 어노테이션이 붙은 객체들을 자동으로 스캔하여 Bean에 등록해준다. 또한, 외부 의존성을 확인하고 조건에 따라 의존성을 주입해주기도 한다. 


마지막으로 스프링의 경우 Web Application Server에 war파일을 담아 배포했다. 하지만 스프링부트의 경우 톰캣과 같은 내장 서버를 이용해서 jar 파일로 간편하게 배포할 수 있다.


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

견고한 서비스를 만들고 싶은 개발자는 TDD를 하거나 최소한 테스트 코드는 꼭 작성해야 한다.

 

  • TDD VS 단위 테스트
    • TDD(Test Driven Development) : 테스트가 주도하는 개발, 즉 테스트 코드를 먼저 작성하고 그 테스트를 통과하도록 코드를 짜는 것
    • 단위 테스트(Unit Test) : TDD의 첫 단계인 기능 단위의 테스트 코드를 작성하는 것을 의미, 즉 순수하게 데스트 코드만 작성하는 것

 

 

 

=> TDD와 달리 테스트 코드를 꼭 먼저 작성해야 하는 것X, 리팩토링도 포함X

 

  • 테스트 장점
    • 개발단계 초기에 문제 발견하게 도와줌
    • 개발자가 코드를 리팩토링하거나 라이브러리 업그레이드 등에서 기존 기능이 올바르게 작동하는지 확인 가능 ex. 회귀 테스트
    • 기능에 대한 불확실성 감소
    • 시스템에 대한 실제 문서 제공

 

  • 테스트 코드 작성를 도와주는 프레임워크
    • JUnit - Java
    • DBUnit - DB
    • CppUnit - C++
    • NUnit - .net

 

2.2 Hello Controller 테스트 코드 작성하기

  • 일반적으로 패키지명은 웹 사이트 주소의 역순으로 함
    • 예를 들어, 사이트 이름이 admin.jojoldu.com라면, 패키지명은 com.jojoldu.admin
  • 일반적으로 테스트 클래스는 대상 이름에 Test를 붙임

 

  • 앞으로 만들 프로젝트의 메인 클래스 Application
    • src/main/java/com/jojoldu/book/springboot/Application
      • @SpringBootApplication : 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성을 모두 자동으로 설정, 항상 프로젝트의 최상단에 위치해야 함
      • SpringApplication.run으로 내장 WAS를 실행 → 항상 서버에 톰캣을 설치할 필요X
package com.jojoldu.book.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);
    }
}

 

  • 컨트롤러와 관련된 클래스를 담는 패키지 web
    • src/main/java/com/jojoldu/book/springboot/web/HelloController
      • @RestController : 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어 줌
      • @GetMapping : HTTP Method인 Get의 요청을 받을 수 있는 API를 만들어 줌
package com.jojoldu.book.springboot.web;

import com.jojoldu.book.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 helloResponseDto(@RequestParam("name") String name,
                                             @RequestParam("amount") int amount)
    {
        return new HelloResponseDto(name, amount);
    }
}

 

  • 테스트 코드 작성
    • src/test/java/com/jojoldu/book/springboot/web/HelloControllerTest
      • @RunWith : 스프링 부트 테스트와 JUnit 사이에 연결자 역할
      • @Autowired : 스프링이 관리하는 Bean을 주입 받음
package com.jojoldu.book.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.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가_리턴된다() throws Exception {
        String hello = "hello";

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

 

2.3 롬복 설치하기

롬복이란?

  • 자바 개발 시 자주 사용하는 코드 Getter, Setter, 기본생성자, toString 등을 어노테이션으로 자동 생성해 줌
  • build.gradle에 implementation('org.projectlombok:lombok') 추가하여 사용

 

2.4 코드를 롬복으로 전환하기

 

  • HelloResponseDto 클래스
    • @Getter
      • 선언된 모든 필드의 get 메서드 생성
    • @RequiredArgsConstructor
      • 선언된 모든 final 필드가 포함된 생성자 생성
      •  final이 없는 필드는 생성자에 포함 X
package com.jojoldu.book.springboot.web.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {

    private final String name;
    private final int amount;
}

 

 

  • HelloResponseDtoTest 클래스
    • @assertThat
      • assertj라는 테스트 검증 라이브러리 검증 메소드
      • 검증하고 싶은 대상을 메서드 인자로 받는다
      • 메서드 체이닝이 지원되어 isEqualTo와 같이 메서드를 이어서 사용 가능
    • @isEqualTo
      • assertj의 동등 비교 메서드
      • assertThat에 있는 값과 isEqualTo의 값을 비교해서 같을 때만 성공
package com.jojoldu.book.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);
    }
}

 

  • HelloController 클래스
    • @RequestParam
      • 외부에서 API로 넘긴 파라미터 가져온다
      • 외부에서 name(@RequestParam("name"))으로 넘긴 파라미터를 메서드 파라미터 name(String name)에 저장
package com.jojoldu.book.springboot.web;

import com.jojoldu.book.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);
    }
}

reference