TDD

테스트 주도 개발(Test Driven Development) 또는 테스트 우선 개발(Test First Development)이란, 특정 기능을 테스트하기 위한 테스트 코드를 먼저 작성한 후에 그 테스트를 통과시키기 위해 실제 기능을 개발하는 개발 방법을 말한다. TDD에서는 이렇게 테스트 코드를 만들고 테스트를 통과시키는 짧은 주기의 사이클을 계속해서 반복하며 프로그램을 완성시켜 나간다. 이 주기는 세가지 단계로 나누어진다.

 

RED : 실패하는 테스트 코드를 작성한다.

GREEN : 테스트가 성공하도록 실제 기능을 구현한다. 

BLUE : 구현한 코드를 리팩토링한다.

 

 

(1) 테스트 코드 작성 

 제일 첫번째 단계에서는 실패하는 테스트 코드를 작성한다. 현재 구현하고자 하는 기능에 대한 단위 테스트(Unit Test)를 작성하는 것이다. 아직 기능을 구현하지 않았으니 당연히 실패한다.

 - 어떤 입력이 들어올 예정인지

 - 그 때 어떤 함수가 호출되는지

 - 그러면 어떤 결과가 나와야 하는지

 

(2) 기능 구현

 테스트코드가 작성되었으면 다음은 그 코드가 통과되도록 기능을 구현한다. 

 - 호출된 함수의 내부 작성

 

(3) 리팩토링

 테스트가 통과되었으면 마지막으로, 구현한 코드를 깔끔하게 정리한다. 중복된 코드를 함수로 추출하거나, 더 좋은 형태로 수정한다. 이 과정을 리팩토링이라고 한다. 리팩토링이 완료되면 테스트를 한번 더 수행하여 여전히 통과되는지 확인한다. 

 

 

 

왜 하는가

코드에 대한 확신을 가질 수 있다.

 이런 식으로 테스트를 먼저 작성해두면, 나중에 기능의 변경이나 추가가 있을 때 새로 작성한 코드가 기존의 기능을 건드리지 않고 안정적으로 동작하는지 테스트 한번에 확인할 수 있다. 또한 매번 테스트가 성공하는 것을 보면서 작성한 코드에 대한 확신을 가질 수 있어 가벼운 마음으로 다음 단계로 넘어갈 수 있다. 

 

빠짐 없이 테스트 할 수 있다.

 TDD의 기본 원칙은 "실패한 테스트를 성공시키기 위한 목적이 아닌 코드는 만들지 않는다"는 것이다. 따라서, 이 원칙을 잘 따랐다면 만들어진 모든 코드는 빠짐없이 테스트로 검증된 것이라고 볼 수 있다. 개발을 먼저 하고 미루고 미루다 테스트 코드를 작성한다면 성의 없는 테스트를 하거나 몇몇 기능은 테스트를 아예 빼먹을 수도 있다. TDD 방식으로 개발한다면 테스트를 빼먹지 않고 꼼꼼하게 만들 수 있다! 

 

전체적인 개발 속도가 빨라진다.

 누군가는 매번 테스트코드를 작성하면 전체적인 개발 일정이 지연되지 않을까 생각할지도 모르겠다. 하지만 테스트 코드를 작성하는것은 실제 기능을 구현하는것보다 상대적으로 간단하기때문에 생각보다 시간이 오래 걸리지 않는다. 오히려 테스트 덕분에 오류를 빨리 잡아낼 수 있기 때문에 전체적인 개발 속도는 빨라진다. 반대로 테스트가 없다면, 개발자는 프로그램을 일일히 실행시켜 보면서 어느 부분에서 오류가 발생하는지 확인해봐야 할 것이다.

 

기능설계와 테스트

 테스트 코드를 작성하는것은 결국 '어떤 기능이 필요한지'를 정의하는것과 비슷하다. 즉, 추가하고 싶은 기능을 테스트 코드로 표현하는 것이다. 이렇게 작성된 테스트들은 잘 작성된 기능정의서처럼 볼 수 있다. 테스트코드를 작성하는것으로 기능설계와 테스트라는 두가지 작업을 동시에 끝내는 것이다.  

 

 

 

 

어떻게 하는가 - 테스트 코드는 어떻게 작성하는가 

 그러면 테스트 코드는 어떤식으로 작성해야 할까? 예를 들어, DB에 접속하여 신규 사용자 데이터를 추가하는 MyDBConnection class에 대한 테스트를 작성한다고 생각 해 보자. 

 

 

기능적으로 어떤 회원 정보를 입력하면 DB에 해당 데이터가 저장되고, 똑같은 정보로 DB를 조회했을 때 동일한 회원 정보가 조회되어야 한다. 이를 코드로 표현하면 다음과 같다. 

public class UnitTest {
	public static void main(String[] args) throws SQLException {
    	
        //테스트할 DB 인터페이스 객체라고 가정
        MyDBConnection db = new MyDBConnection();
        
        User user = new User();
        user.setEmail("hong@example.com");
        user.setName("홍길동");
        user.setAge(20);
        
        db.add(user);
        
        System.out.println(user.getEmail() + "등록 성공");
        
        User user2 = db.get(user.getEmail());
        System.out.println(user2.getName());
        System.out.println(user2.getAge());
        
        System.out.println(user2.getEmail() + "조회 성공");
	}
}

 

그런데, 이 테스트 코드엔 몇가지 문제가 있다. 

(1) 먼저, 조회한 데이터가 입력한 데이터와 같은지 확인해주지 않고 화면에 출력하여 개발자가 눈으로 직접 확인해야 한다.

(2) 그리고 결과가 성공이든 아니든 "조회 성공"이라는 텍스트가 출력된다.

(3) 마지막으로, 테스트가 main 함수에 작성되었기 때문에 매번 서로 다른 테스트 클래스를 동작시켜야 한다.

 

모든 기능에 대하여 테스트를 작성하면서 결과를 개발자가 일일히 눈으로 확인하는것은 매우 비효율적인 일이다. 따라서 테스트 코드가 테스트의 성공/실패 여부를 자동으로 판단하도록 자동화시켜야 한다. 이를 고쳐 보자. 

 

우선, user와 user2의 데이터가 같은지 판단하고, 모든 데이터가 같을 때에만 테스트 성공 메세지가 출력되도록 다음과 같이 수정할 수 있다.

        User user2 = db.get(user.getEmail());
        if(!user.getName().equals(user2.getName())){
        	System.out.println("테스트 실패(name)");
        }
        else if(user.getAge() != user2.getAge()){
        	System.out.println("테스트 실패(age)");
        }
        else {
        	System.out.println("조회 테스트 성공");
        }

 

두번째로, 테스트코드를 모아 놓은 클래스를 만들어서 여러개의 테스트가 모두 실행되도록 수정하면 다음과 같다.

public class UnitTests {

	//테스트할 DB 인터페이스 객체라고 가정
    MyDBConnection db = new MyDBConnection();
    
    public static void main(String[] args) throws SQLException {
    	AddUserTest();
        AnotherTest();
        //... 
    }
    
    public void AddUserTest(){    
        User user = new User();
        user.setEmail("hong@example.com");
        user.setName("홍길동");
        user.setAge(20);
        
        db.add(user);
        
        System.out.println(user.getEmail() + "등록 성공");
        
        User user2 = db.get(user.getEmail());
        if(!user.getName().equals(user2.getName())){
        	System.out.println("테스트 실패(name)");
        }
        else if(user.getAge() != user2.getAge()){
        	System.out.println("테스트 실패(age)");
        }
        else {
        	System.out.println("조회 테스트 성공");
        }
    }
    
    public void AnotherTest(){
    	//...
    }
}

 

 이런 식으로 여러개의 테스트를 스스로 수행 가능하고, 기대하는 결과에 대한 확인까지 해주는 자동화된 테스트를 만들어 두는 것이다. 이 다음은 이 테스트를 통과하도록 MyDBConnection 클래스의 add, get 함수를 구현하고, 테스트가 통과되는것을 확인하면 되겠다. 

 

 

JUnit 

 위와 같은 테스트 코드의 작성을 보다 편하게 도와주는 프레임워크가 있다. 바로 JUnit이다. 

JUnit은 프레임워크다. 
프레임워크의 기본 동작 원리는 제어의 역전IoC 이다. 프레임워크는 개발자가 만든 클래스에 대한 제어 권한을 넘겨받아서 주도적으로 애플리케이션의 흐름을 제어한다. 따라서 프레임워크를 사용할 때엔 main() 메소드도 필요 없고 오브젝트를 만들어서 실행시켜 줄 필요도 없다.

 

위에서 만든 테스트를 JUnit 프레임워크에서 동작시키려면 조건 두가지를 따라야 한다.

 - 메소드를 public으로 만든다.

 - 메소드에 @Test 애노테이션을 붙여 준다.

 

위의 테스트 코드를 JUnit 프레임워크를 사용하는 코드로 수정하면 다음과 같이 바뀐다.

import org.junit.Test;

public class UnitTests {

	//테스트할 DB 인터페이스 객체라고 가정
    MyDBConnection db = new MyDBConnection();
    
    @Test
    public void AddUserTest(){    
        User user = new User();
        user.setEmail("hong@example.com");
        user.setName("홍길동");
        user.setAge(20);
        
        db.add(user);
        
        System.out.println(user.getEmail() + "등록 성공");
        
        assertThat(user2.getName(), is(user.getName());
        assertThat(user2.getAge(), is(user.getAge());
    }
    
    @Test
    public void AnotherTest(){
    	//...
    }
}

 

JUnit의 요구조건 외에도 기존 코드에서 바뀐 부분이 있다.

        assertThat(user2.getName(), is(user.getName());
        assertThat(user2.getAge(), is(user.getAge());

위 코드는 JUnit에서 지원하는 Matcher 이다. assertThat(A,B)는 A가 B 조건을 만족하는지 확인하여 같으면 통과, 다르면 테스트 실패를 발생시킨다. JUnit은 이 밖에도 다양한 Matcher를 지원한다. 

 

https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions

 

JUnit 5 User Guide

Although the JUnit Jupiter programming model and extension model will not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and cus

junit.org

 

JUnit을 사용하면 테스트를 작성하는것 뿐만 아니라, 실행하고 결과를 확인하는것도 훨씬 간편해진다. IDE에서 JUnit 테스트를 실행하면 테스트가 통과되는 경우와 실패한 경우에 아래 그림과 같이 표시된다. 테스트에 실패할 경우, 어떤 테스트 메소드가 실패했으며 왜 실패했는지가 간략히 표시된다.

 

테스트를 모두 통과한 경우
테스트에 실패한 경우

 

 


참고자료

1. https://marsner.com/blog/why-test-driven-development-tdd/

 

Why Test-Driven Development (TDD) | Marsner Technologies

%

marsner.com

2. 토비의 스프링 3.1 Vol.1 

3. https://velog.io/@velopert/TDD%EC%9D%98-%EC%86%8C%EA%B0%9C#%EC%A0%95%EB%A6%AC

 

TDD의 소개

TDD (Test Driven Development · 테스트 주도 개발) 에 대해서 알아봅시다! TDD 는 테스트가 개발을 이끌어 나가는 형태의 개발론입니다. 가장 쉽게 설명하자면, 선 테스트 코드 작성, 후 구현 인데요, 이��

velog.io

 

'스터디' 카테고리의 다른 글

인터넷 일반  (0) 2020.07.14
Spring Framework 소개  (0) 2020.06.25

본 글에서는 2020 백엔드 개발자 로드맵의 첫번째 단계인 인터넷에 대해 알아보고, 정리 해 두고자 합니다.

https://developer.mozilla.org/ 사이트를 주로 참고하였습니다.

 

2020 백엔드 개발자 로드맵

 

1. 인터넷은 어떻게 작동될까요?

 인터넷이란, 컴퓨터와 컴퓨터를 물리적으로(무선 통신도 포함해서) 연결하는 데에서부터 출발합니다. 두 대의 컴퓨터를 연결하려면 하나의 전선만 있으면 되겠지만, 여러 대의 컴퓨터를 서로 연결하려면 필요한 선이 기하급수적으로 늘어나기 마련입니다. 그렇기 때문에, 우리는 여러 대의 컴퓨터들을 서로 서로 연결하는 대신, 중앙에 라우터 라는 장비를 두고 각각의 컴퓨터를 연결합니다.

 

 

 

 같은 원리로, 라우터와 라우터끼리 연결하여 수십 수백대의 컴퓨터끼리도 연결할 수 있겠습니다. 이는 로컬 네트워크라고 부릅니다. 로컬 네트워크를 아주 멀리 있는 지역의 컴퓨터와 연결하려면 이 네트워크를 ISP(Internet Service Provider)에 연결해야 합니다. ISP는 큰 라우터들을 관리하며 연결할 수 있게 해 주는 회사입니다. 인터넷은 이러한 전체 네트워크들의 인프라로 구성됩니다. 

 

2. HTTP는 무엇일까요?

 HTTP(Hyper Text Transfer Protocol)이란 텍스트 기반으로 인터넷에서 데이터를 주고받을 때에 사용되는 통신 규약(Protocol)입니다. TCP와 UDP 통신 위에서 동작하며, 80번 포트를 사용합니다.

 

 HTTP는 연결 상태를 유지하지 않는 비연결성 프로토콜입니다. HTTP는 사용자(클라이언트)가 어떠한 서비스를 서버에 요청하는것(Request)과 서버가 이 요청에 응답하는(Response) 두가지 단계로 이루어집니다. 

 

 

   클라이언트 -> 서버 : 요청(Request)

      서버 -> 클라이언트 : 응답(Response) 

 

 - 요청(Request) 의 종류

HTTP Request에는 다음의 네가지 종류가 있습니다.

GET

데이터의 조회를 요청

POST

데이터의 생성을 요청

PUT

데이터의 수정을 요청

DELETE

데이터의 삭제를 요청

 - 응답(Response)의 종류

HTTP Response는 요청의 결과에 따라 몇가지 상태 코드를 가집니다. 상태 코드의 종류는 크게 분류하자면 다음과 같습니다.

1XX

조건부 응답. 요청을 받았으며 작업을 계속한다.

2XX

성공. 클라이언트의 요청이 성공적이었으며 작업을 완료했다.

3XX

리다이렉션 완료. 클라이언트는 요청을 마치기 위해 추가 작업을 해야 한다.

4XX

요청 오류. 클라이언트의 요청에 오류가 있었다.

5XX

서버 오류. 서버가 유효한 요청을 수행하지 못했다.

 

 - 메시지의 구성 (HTTP/1.1 기준) 

HTTP 요청과 응답은 서로 비슷하게 구성되어 있습니다.

  1. 시작 줄(start-line)에는 실행되어야 할 요청, 또은 요청 수행에 대한 성공 또는 실패가 기록되어 있습니다. 이 줄은 항상 한 줄로 끝납니다.

  2. 옵션으로 HTTP 헤더 세트가 들어갑니다. 여기에는 요청에 대한 설명, 혹은 메시지 본문에 대한 설명이 들어갑니다.

  3. 요청에 대한 모든 메타 정보가 전송되었음을 알리는 빈 줄(blank line)이 삽입됩니다.

  4. 요청과 관련된 내용(HTML 폼 콘텐츠 등)이 옵션으로 들어가거나, 응답과 관련된 문서(document)가 들어갑니다. 본문의 존재 유무 및 크기는 첫 줄과 HTTP 헤더에 명시됩니다.

- HTTP/1.1과 HTTP/2

 HTTP/1.1 버전의 성능 상의 몇가지 결함 때문에, HTTP/2 버전이 나오게 되었습니다. HTTP/2 버전에서는 프레임 이라는 개념을 도입하여 HTTP 메시지를 나누어 전송합니다. 또한 여러개의 프레임을 묶어 스트림을 구성합니다. HTTP/2에서는 데이터와 헤더 프레임을 분리하여 헤더를 압축할 수 있으며, 스트림 여러개를 하나로 묶을 수도 있습니다.(멀티플렉싱) 

 

 

3. 브라우저와 동작 원리

 브라우저(웹 브라우저)는 인터넷에서 쌍방향으로 통신하며, HTML 문서나 파일을 출력하여 화면에 보여주는 그래픽 사용자 인터페이스(GUI) 기반의 응용 소프트웨어를 말합니다. 주요 웹 브라우저로는 ms의 인터넷 익스플로러, 엣지, 구글 크롬, 모질라 파이어폭스, 오페라, 사파리 등이 있습니다.

 

 브라우저의 주요 기능은 사용자가 선택한 자원을 서버에 요청하고, 서버가 응답한 HTML파일을 해석해서 화면에 표시하는 것입니다. 이 과정은 다음과 같습니다.

 

  1. 사용자가 HTTP를 사용하여 서버에 데이터(HTML 파일) 요청

  2. 서버가 응답함(HTML 파일이 전송됨)

  3. 브라우저에서 HTML 파일을 해석

  4. 브라우저의 렌더링 엔진이 HTML 파일을 화면에 그림 

 

4. DNS와 작동 원리, 도메인 이름이란?

 인터넷에 연결된 모든 장비들은 개인 IP 주소를 가지고 있습니다. 라우터들은 IP 주소를 통해 각각의 장비를 구분할 수 있고, 네트워크에 연결된 다른 장비를 찾아갈 수 있습니다. IP 주소는 32비트의 IPv4(173.194.121.32 와 같은), 128비트의 IPv6(2027:0da8:8b73:0000:0000:8a2e:0370:1337 와 같은) 등으로 표현됩니다. 

 하지만 이러한 IP 주소는 바뀔 수도 있고, 사람이 기억하기엔 너무 어렵습니다. 그렇기때문에 우리는 IP주소 대신, 사람이 읽고 기억하기 쉬운 주소인 도메인 이름(domain name)를 사용하게 되었습니다. 이렇게 IP 주소와 연결된 도메인 주소를 서로 변환시키는 것을 도메인 네임 시스템(Domain Name System, DNS)이라고 부릅니다.

 

 DNS 서버는 각각의 IP 주소와 도메인 주소를 기억하고 있는 DNS 데이터베이스를 갱신하고 관리합니다. 우리가 매번 특정 IP에 접근할때(DNS 리퀘스트를 요청할 때), DNS에서는 다음과 같은 단계를 거칩니다.

 

  1. 브라우저의 주소창에 도메인 이름을 입력 (ex. www.google.com)

  2. 해당 컴퓨터의 local DNS cache에 해당 도메인 이름의 IP 정보가 있는지 확인. 있다면 바로 해당 IP로 접속.

  3. 해당 도메인 이름의 IP를 모른다면, DNS 서버에 관련 정보를 요청함.

  4. 전달받은 IP로 접속하고, 컴퓨터의 local DNS cache에 해당 도메인과 IP 정보를 저장함.

 

5. 호스팅은 무엇일까요?

 웹 호스팅은 기업이나 사용자가 웹 사이트나 웹 페이지를 인터넷에 게시할 수 있도록 해주는 서비스입니다. 이러한 서비스를 통해 인터넷에 게시하고자 하는 데이터를 웹 호스팅 업체의 서버에 등록하면, 사용자는 등록한 IP와 도메인 이름을 통해 해당 데이터에 언제든 접근할 수 있습니다. 호스팅 서비스를 사용하려면 도메인 이름을 가지고 있어야 하고, 만일 도메인 이름이 없다면 호스팅 서비스 업체에서 제공해주기도 합니다.

 

 

 

 


참고자료

1. https://developer.mozilla.org/ko/docs/Learn/Common_questions/How_does_the_Internet_work

2. https://developer.mozilla.org/ko/docs/Learn/Common_questions/What_is_a_domain_name

3. https://developer.mozilla.org/ko/docs/Web/HTTP/Messages

4. https://velog.io/@surim014/HTTP%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80

5. https://d2.naver.com/helloworld/59361

6. https://www.website.com/beginnerguide/webhosting/6/1/what-is-web-hosting?.ws

 

'스터디' 카테고리의 다른 글

테스트 주도 개발, JUnit  (0) 2020.08.05
Spring Framework 소개  (0) 2020.06.25

1. 소개

 1.1 Spring framework

   - 자바 엔터프라이즈 개발을 위한 오픈 소스 애플리케이션 프레임워크 (간단히 스프링이라고 부른다)

   - 자바 가상 머신(JVM) 위에서 동작하는 언어들을 지원한다. (Groovy, Kotlin 등)

   - 아파치 라이선스 2.0

   - 스프링 프레임워크 5.1 기준 JDK 8+(Java SE 8+)를 필요로 하고 JDK 11 LTS 버전도 지원한다.

   - 대한민국 공공기관의 웹 서비스 개발 시 사용을 권장하고 있는 전자정부 표준프레임워크의 기반 기술로서 쓰이고 있다.

 

 스프링은 여러 개의 모듈로 이루어져 있고, 필요한 모듈만 선택하여 사용하는것이 가능하다. 

 

 

1.2 스프링 프레임워크와 스프링 부트

  스프링 프레임워크는 지원하는 기능과 라이브러리가 많기때문에 환경설정을 하기가 복잡하다. 때문에 필요한 라이브러리를 간편하게 설정하고 바로 실행 가능한 프로그램을 만들 수 있도록 나온것이 스프링 부트다.

스프링 부트의 주요 특징은 다음과 같다.

 

 - 내장 Tomcat, Jetty등을 포함하고 있어 WAR 파일을 배포하지 않아도 프로그램을 바로 실행할 수 있게 해 준다.

 - 프로젝트 생성 시 필요한 라이브러리 의존성을 선택하고 바로 시작할 수 있도록 해 준다. (Spring Initializr)

 - 스프링 프레임워크, 3rd 라이브러리를 자동으로 configure 해 준다.

 

2. 스프링의 주요 Features

 2.1 자바 객체를 직접 관리

  - 스프링은 경량 컨테이너로서 자바 객체를 직접 관리한다.

  - 각 객체의 생성, 소멸과 같은 라이프 사이클을 관리해준다.

  - 프로그래머는 필요한 객체를 스프링에게서 받아와서 사용하면 된다.

 

 2.2 POJO 사용

  - Plain Old Java Object (POJO) 방식의 프레임워크이다.

  - 스프링의 기능을 사용하기 위해 특정한 인터페이스나 클래스를 상속받을 필요가 없어 사용하기에 편하고 객체가 가볍다.

 

 2.3 의존성 주입(Dependency Injection, DI)

  - 의존성 주입을 방식으로 제어 역전을 구현한다.

  - 제어 역전(Inversion of Control, IoC), 또는 제어 반전이란 프로그래머가 작성한 프로그램이 라이브러리의 흐름 제어를 받게 되는 소프트웨어 디자인 패턴을 말한다. 전통적인 프로그래밍에서 흐름은 프로그래머가 작성한 프로그램이 외부 라이브러리의 코드를 호출해 이용한다. 하지만 제어 반전이 적용된 구조에서는 외부 라이브러리의 코드가 프로그래머가 작성한 코드를 호출한다. (참고)

 - 각각의 계층이나 서비스 객체 간에 의존성이 존재할 경우, 스프링이 이를 확인하고 런타임에 서로 연결시켜 준다.

 

 2.4 관점 지향 프로그래밍(Aspect Oriented Programming, AOP)

  - 관점 지향 프로그래밍은 횡단 관심사(cross-cutting concerns)들을 코드에서 분리함으로써 모듈성을 증가시키는 것이 목적인 프로그래밍 패러다임이다.

  - 횡단 관심사란, logging, security, transaction 등과 같이 프로그램의 핵심 기능은 아니면서도 코드 전체에 걸쳐서 사용되며, 다른것에 영향을 미칠 수 있는 것들을 뜻한다.

  - AOP는 이러한 "부가 기능"들을 모듈화하여 관리한다.  

 

 

 2.5 이식 가능한 서비스 추상화 (Portable Service Abstraction, PSA)

 - 스프링은 다른 완성도 높은 프레임워크들과 쉽게 연결할 수 있는 인터페이스를 제공한다.

 - 예를 들어 MySQL, PostgreSQL, MongoDB와 같은 DB를 위한 인터페이스나 Servlet, React, Tomcat과 같은 서비스와 연동을 지원한다.

 - 프로그래머는 코드를 바꾸지 않고도 연결된 구현체를 쉽게 교체할 수 있다.   

 

 예를 들어, 스프링의 JPA 인터페이스를 사용하면 다음과 같이 Query문을 작성하지 않고도 간편하게 DB와 연동시킬 수 있다. 

package payroll;
import org.springframework.data.jpa.repository.JpaRepository;

interface EmployeeRepository extends JpaRepository<Employee, Long> {
          //JPA를 사용하여 DB와 연동하기 위한 클래스 선언
}
@GetMapping("/employees")
List<Employee> all() {
    return repository.findAll();   //DB의 모든 데이터를 반환 
}

@PostMapping("/employees")
Employee newEmployee(@RequestBody Employee newEmployee) {
    return repository.save(newEmployee);      //DB에 대로운 객체 저장
}

 

 

3. 결론적으로

 - 스프링을 사용하면 모듈화 하기 좋고 매우 유연한 코드 작성 가능

 - 안정적인 라이브러리 제공으로 프로그램의 품질 관리, 유지보수성 향상

 - 프로그래머의 개발 속도 향상 

 

 


아파치 라이선스 2.0

 아파치 소프트웨어 재단에서 자체적으로 만든 소프트웨어에 대한 라이선스 규정. 누구나 해당 소프트웨어에서 파생된 프로그램을 제작할 수 있으며 저작권을 양도, 전송할 수 있다. 아파치 라이선스에 따르면 누구든 자유롭게 아파치 소프트웨어를 다운 받아 부분 혹은 전체를 개인적 혹은 상업적 목적으로 이용할 수 있으며 재배포시에는 원본 소스 코드 또는 수정한 소스 코드를 반드시 포함시켜야 하는 것은 아니고 아파치 라이선스, 버전 2.0을 포함시켜야 하며 아파치 소프트웨어 재단에 개발된 소프트웨어라는 것을 명확하게 밝혀야 한다.

 

 

'스터디' 카테고리의 다른 글

테스트 주도 개발, JUnit  (0) 2020.08.05
인터넷 일반  (0) 2020.07.14

Lombok

롬복(Lombok)은 자바에서 많이 사용되는 getter/setter, equlas, 생성자 등의 소스코드를 자동으로 생성하고 관리 해 주는 자바 라이브러리이다.  lombok을 사용하면 각 변수에 대하여 개발자가 일일히 getter/setter를 추가하지 않아도 되므로 소스코드가 매우 간결해지고 유지보수가 편리해진다.

 

 

IntelliJ IDE와 Lombok 연동하기

Lombok을 사용하여 프로젝트를 빌드하기 위해, build 파일의 의존성 부분에 lombok annotation processor를 사용할것임을 명시하여야 한다. build tool로 Gradle을 사용할 경우 다음과 같다.

 

compileOnly, annotationProcessor 부분에 lombok 사용을 명시하였다.

 

그리고 IntelliJ에 아직 Lombok 플러그인을 설치하지 않았다면 File->Settings->Plugins 메뉴에서 [Lombok]을 검색하여 플러그인을 설치한다. 설치가 완료되면 IntelliJ를 재시작하게 되고, 플러그인이 Installed 상태로 변경된다.

 

IntelliJ IDE에 Lombok plugin 설치

 

LomBok Annotation

Lombok은 Annotation을 사용하여 코드를 매우 간단하게 만들어 준다. 몇가지 Annotation의 사용법을 정리 해 보자.

 

@Getter/@Setter

   클래스에 선언할 경우 해당 클래스의 모든 멤버변수에 대하여 Getter/Setter 함수를 생성한다. 

   변수 앞에 선언할 경우 해당 변수에 대한 Getter/Setter 함수를 생성한다.

 

@NoArgsConstructor/@AllArgsConstructor

   클래스에 선언하여 각각 해당 클래스의 기본 생성자(NoArgsConstructor)와

   모든 멤버변수를 포함하는 생성자(AllArgsConstructor)를 생성한다.

 

@Builder

  Builder Pattern을 사용위한 builder() 함수를 생성한다.

 

Lombok Annotation을 사용하면 class가 이와 같이 단순해진다.

 

 

Builder Pattern

 다음과 같은 생성자가 있다고 가정 해 보자.

 간단한 객체라면 상관없지만 만약 새로운 멤버 변수가 계속해서 추가되고 객체 생성 시에 이들 모두를 초기화해야 한다면, 생성자의 어떤 파라미터가 어떤 멤버 변수에 해당하는지 눈에 잘 들어오지 않고 코드의 가독성이 많이 떨어질 것이다. 그렇다고 멤버 변수를 Setter를 사용하여 설정한다면 객체 생성이 한번에 끝나지 않아 좋지 않은 구현이 될 것이다.

 이런 상황에 사용할 수 있는것이 Builder 패턴이다.

 

 이러한 Builder 패턴을 사용하면 어떤 객체를 생성할 때 각 인자의 의미가 무엇인지 한눈에 볼 수 있기 때문에 소스 코드의 유지보수가 편해지고, 파라미터의 조합에 따른 모든 종류의 생성자를 선언하지 않아도 되며, 생성자의 파라미터를 순서대로 넘겨주지 않아도 된다는 여러 가지 장점이 있다.

 

 본래 이와 같은 Builder 패턴을 구현하려면 개발자가 직접 클래스에 builder() 함수를 작성해야만 한다. 하지만 Lombok을 사용하면 클래스에 @Builder 어노테이션을 작성하는것만으로 간단하게 builder 패턴을 사용할 수 있다. 

 

 

'개발노트' 카테고리의 다른 글

REST API  (0) 2020.03.19
[Spring] 가짜 객체(Mock Object)  (0) 2020.03.18
[Spring] 의존성 주입(Dependency Injection)  (0) 2020.03.18
Test Driven Development 실습  (0) 2020.03.12
[Spring Boot] Spring Boot 시작하기  (0) 2020.03.11

REST(REpresentational State Transfer) API

REST는 2000년도에 로이 필딩(Roy Fielding)의 박사학위 논문에서 최초로 소개되었으며, HTTP를 기반으로 필요한 자원에 접근하고 제어하는 방식을 정의 해 놓은 구현 방식이다. REST는 다음과 같이 구성된다고 볼 수 있다.

 

자원(Resource) - URI

행위(Verb) - HTTP METHOD

표현(Representation)

 

자원(Resource)

REST에서는 URI를 통해 특정 자원에 접근한다. 즉, 서버에 있는 모든 resource에는 특정 URI가 부여되고, 이를 통해 접근이 가능하여야 한다. 

플레이 리스트(목록) localhost:8080/musicplayer/playlists
플레이 리스트의 1번째 resource localhost:8080/musicplayer/playlists/1

 

행위(Verb)

REST에서의 행위(CRUD)는 HTTP METHOD를 통해 이루어진다.

HTTP METHOD 행위 CRUD
POST 해당 URI의 resource를 조회한다. Create
GET 해당 URI의 resource를 조회한다. Read
PUT 해당 URI의 resource를 수정한다. Update
DELETE 해당 URI의 recourse를 삭제한다. Delete

 

예를 들어, 다음과 같이 사용될 수 있다.

 

GET musicplayer/playlists                      -  플레이 리스트 목록을 조회한다. (목록조회)

GET musicplayer/playlists/1                   -  플레이 리스트의 1번 resource를 조회한다. (상세조회)

POST musicplayer/playlists {DATA}          -  {DATA} 를 플레이 리스트에 추가한다.

DELETE musicplayer/playlists/1               - 플레이 리스트의 1번 resource를 삭제한다.

 

 

표현(Representation)

뭘까

 

 

HTTP 상태코드

REST API를 사용하여 resource에 대한 작업을 수행하면, 호출하는 쪽에서는 그 결과에 따른 HTTP 상태 코드를 받게 된다. 보다 안정적인 서비스를 위하여 개발자는 상태 코드에 따라 적절한 처리를 해야 한다.

주로 사용되는 상태 코드 몇가지는 다음과 같다.

 

상태코드 설명
200 클라이언트의 요청을 정상적으로 수행함 
201 (POST) 클라이언트의 리소스가 정상적으로 생성됨
400 클라이언트의 요청이 부적절함
(요청에 해당하는 메서드가 정의되지 않았음)
401 클라이언트가 인증되지 않은 상태에서 리소스를 요청함
404 클라이언트가 요청한 리소스를 찾을 수 없음

 

'개발노트' 카테고리의 다른 글

Lombok, Builder Pattern  (0) 2020.03.24
[Spring] 가짜 객체(Mock Object)  (0) 2020.03.18
[Spring] 의존성 주입(Dependency Injection)  (0) 2020.03.18
Test Driven Development 실습  (0) 2020.03.12
[Spring Boot] Spring Boot 시작하기  (0) 2020.03.11

앞서 얘기한 의존성 주입 패턴을 적용시켜 테스트 코드를 작성하다 보면, 테스트 코드를 포함한 클래스에 다음과 같이 의존성 주입을 위한 필드 멤버가 여럿 필요하게 된다. 

 

하지만 실제 테스트 코드에서 호출하는것은 RestaurantService 하나 뿐이고, 나머지 둘은 RestaurantService의 동작에 필요한 의존성 객체들이다. 테스트 하는 입장(사용자 입장)에서는 RestaurantService가 어떤 의존성을 필요로 하는지 알 필요가 없고, 이를 가짜 객체(Mock Object)를 사용하여 감출 수 있다.

 

Spring의 mokito framework를 사용하여 Mock Object를 선언하고 사용하는 방법은 다음과 같다.

 

직접 사용할 RestaurentService를 Mock Object로 선언하고 나머지는 지운다.

 

테스트 메소드에서는 기존 테스트(mvc.perform 부분)를 만족시키기 위해 가짜 객체(restaurantService)를 사용하여 테스트를 만족시키는 return값을 설정한다. 가짜 객체에서는 실제 필요로하는 의존성 객체를 할당하지 않았으므로 그에 해당하는 의존성 주입을 하지 않아도 사용이 가능하다. 

 

 

Mock Object의 의의

 

 가짜 객체(Mock Object)를 사용하면 테스트 하고자 하는 부분과 그 테스트를 수행하기 위해 필요로 하는 부분(ex. 의존하는 객체들의 상태)을 분리하여 검증할 수 있다. 즉, 개발자가 테스트 외적인 부분을 통제 해 두고 테스트 하고자 하는 행위 자체에 집중할 수 있게 한다.  

'개발노트' 카테고리의 다른 글

Lombok, Builder Pattern  (0) 2020.03.24
REST API  (0) 2020.03.19
[Spring] 의존성 주입(Dependency Injection)  (0) 2020.03.18
Test Driven Development 실습  (0) 2020.03.12
[Spring Boot] Spring Boot 시작하기  (0) 2020.03.11

객체의 의존성

 한 객체가 다른 객체를 참조(사용)하고 있다면, 객체 사이에 의존성이 존재하게 된다.

 

A가 B를 사용한다.

A가 B에 의존한다.

 

 위와 같은 상황은 다음과 같이 말할 수 있다.

 

B의 변화가 A에 영향을 준다. 

 

 객체 사이의 의존성은 실제 코드로 살펴보면 다음과 같은 형태로 나타난다.

 

이처럼 하나의 객체가 다른 객체를 직접 생성하여 가지고있다면 객체 사이에 강한 결합이 존재하게 된다. 위 코드에서 repository의 다른 구현을 테스트하고자 한다면 객체 생성 부분의 코드를 수정하여야 한다. 또한, 변경사항이 생길때마다 기존의 코드를 수정하여야 할 것이다. 이런 상황을 피하기 위해 의존성 주입 패턴을 적용시킬 수 있다.

 

 

 

의존성 주입(Dependency Injection)

의존성 주입(DI)이란 디자인 패턴 용어로, 하나의 객체가 다른 객체에 의존성을 제공하는 패턴이다. 의존성 주입 패턴에서는 사용하고자 하는 객체의 실제 구현체를 나중에 할당하는 것으로 미룰 수 있으며, 이를 통해 같은 코드를 여러 가지 구현에 사용할 수 있다. 의존성 주입을 사용하면 객체간의 결합이 느슨해지고 객체들을 독립적으로 분리할 수 있어 유지보수성이 좋아진다. 의존성 주입 패턴을 적용하여 보다 일반적인 의미로 쓰이는 IoC(Inverse of Control, 역제어)를 구현할 수 있다.

 

Controller를 사용하는 쪽에서 repository를 생성하여 넣어준다.

 

 

Spring에서의 DI

Spring Framework의 Container를 통해 DI를 쉽게 사용할 수 있다. Container는 응용 프로그램을 구성하는 bean이라는 객체를 관리하며 필요한 객체를 찾아 알아서 주입해준다. 간단한 사용 예시는 다음과 같다.

 

1. 객체를 사용하는 쪽 (@Autowired)

 사용할 객체의 field에 @Autowired 어노테이션을 표기하여 의존성 주입을 사용할것임을 명시한다. 

 

2. 의존성 주입을 수행하는 쪽(@SpyBean)

 의존성 주입을 수행하는 쪽(예시에서는 test 함수)에서는 @SpyBrean 어노테이션에 어떤 구현 클래스를 사용할것인지 명시하여 다음과 같이 선언한다.

 

위 코드를 실행할 경우, 실행 단계에 spring이 해당하는 클래스의 bean을 찾아 1번의 @Autowired 변수에 할당해주며 프로그램이 동작하게 된다.

 

 


인터페이스를 사용해서 실제 구현은 나중에 넘겨주는 패턴까지는 이해했는데

spring에서 동작시키려니까 혼란스럽다.. 미래의 내가 이해하길 바래

'개발노트' 카테고리의 다른 글

Lombok, Builder Pattern  (0) 2020.03.24
REST API  (0) 2020.03.19
[Spring] 가짜 객체(Mock Object)  (0) 2020.03.18
Test Driven Development 실습  (0) 2020.03.12
[Spring Boot] Spring Boot 시작하기  (0) 2020.03.11

테스트 주도 개발방법(Test Driven Development, TDD)

 테스트 주도 개발방법(이하 TDD)은 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 방식이다. 폭포수 모델(Waterfall Model)과 같은 기존의 개발 방식에서는 설계-구현-테스트 순으로 개발을 진행했다면, TDD에서는 반대로 테스트 코드를 먼저 작성하고 후에 구현을 진행한다. TDD는 다음과 같은 세가지 단계로 진행된다.

 

 

1. RED 

먼저, 개발하고자 하는 것을 어떻게 쓸 것인지 사용자 입장에서 테스트 코드를 작성한다. 이 단계에서는 테스트 코드만이 존재하므로 테스트는 실패하게 된다. 

- 이 과정에서 사용자 중심으로 생각하게 되기 때문에 TDD는 사용자 중심 개발, 인터페이스 중심 개발이라고 볼 수 있다. 

 

2. GREEN

실제 코드를 구현하여 1에서 실패한 테스트를 성공하게 만든다. 이 단계에서는 테스트를 성공시키는것이 목적이기 때문에 임시 데이터 등을 사용하여도 좋다. 

 

3. REFACTORING

2에서 구현한 코드가 일반적인 상황(같은 테스트 코드를 사용한 다른 index 호출 등)에서도 동작하도록 수정하고, 필요한 멤버변수 등을 만들고 코드를 효율적이게 정리한다.  

 

 

 

JUnit을 활용한 TDD

 JUnit은 자바 프로그래밍 언어용 유닛 테스트 프레임워크로, 사용하면 컴파일 타임에 JAR 형태로 링크된다. JUnit을 사용하려면 테스트 메소드에 @Test 와 같이 어노테이션을 사용해 표시한다. 현재 JUnit5 버전이 최신이며 JUnit4 버전과는 변경점이 좀 있다. (더 알아볼 것) 

 

 

IntellJ IDE에서 JUnit을 사용하여 TDD의 세 단계를 수행 해 보자.

 

 

1. 테스트 코드 작성하기

 

 우선 테스트하고자 하는 class를 생성하고 class 명을 우클릭 - Go To - Test 를 선택한다. 그 다음 나타나는 Create Test 다이얼로그에서 사용할 테스팅 라이브러리(JUnit)를 선택하고 테스트 클래스 이름을 적고 생성을 완료한다. 

 

 

테스트 클래스가 생성되면, 테스트 하고자 하는 내용을 메서드로 작성하고 그 위에 @Test 어노테이션을 붙여준다. 이때, 테스트를 하기 위해 호출되어야 하는 메서드도 정의 해 둔다.

 

Person kim 객체를 만들고, getName()이 "KIM"을 반환하는지 확인하는 테스트 메서드
Person class의 메서드

 작성한 테스트 메서드를 실행시켜 보면(테스트 메서드 좌측에 있는 초록색 삼각형 아이콘!) 테스트가 실패하는것을 볼 수 있다. 

 

2. 테스트를 성공하게 만들기

 위에서 작성한 테스트가 실패한 이유는 Person의 getName() 함수가 빈 문자열을 반환하기 때문이다. 테스트 성공을 위하여 "KIM" 문자열을 반환하도록 수정한다. 

테스트를 성공시키기 위해 문자열 "KIM"을 반환한다.

테스트에 성공하였다.

 

3. 일반적인 상황에서도 동작하도록 Refactoring 하기

Person park에 대하여 "PARK"을 반환하는지 검사하는 테스트 코드를 추가하였다.

 맨 처음 작성한 테스트는 성공하였으나, 위 그림과 같은 테스트 코드를 추가한다면 테스트에 다시 실패하게 된다. 이는 Person 클래스가 아무 데이터도 가지고 있지 않고, 작성된 문자열만 반환하기 때문이다. 이제 테스트하고자 하는 Person 클래스가 다른 상황에서도 동작하도록 Person 클래스에 생성자와 멤버 변수 등을 추가하고 코드를 정리한다.  

 

생성자에서 name 문자열을 받고, getName() 메서드에서 이를 반환한다.
문제 없이 동작하는 테스트 메서드

이제 테스트 메서드가 잘 동작 하는것을 확인할 수 있다! :)

 

 


처음 이렇게 개발해본 소감은 너무 신기하다..

개발 단계를 완전히 거꾸로 하는 기분인데 이렇게 개발해도 된다는점이 제일 신기하다.

프로젝트 볼륨이 커져도 이런식으로 개발할 수가 있나? 있으니까 쓰겠지.. 

신선해서 재밌긴 한데 IntelliJ나 JUnit 쓰는법이 익숙하지가 않아서 컴파일 오류에 고생하는중

'개발노트' 카테고리의 다른 글

Lombok, Builder Pattern  (0) 2020.03.24
REST API  (0) 2020.03.19
[Spring] 가짜 객체(Mock Object)  (0) 2020.03.18
[Spring] 의존성 주입(Dependency Injection)  (0) 2020.03.18
[Spring Boot] Spring Boot 시작하기  (0) 2020.03.11

스프링 프레임워크(Spring Framework)는 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크로, 동적인 웹 사이트를 개발하기 위한 여러 가지 서비스를 제공하고 있다. 또한 대한민국 공공기관의 웹 서비스 개발 시 사용을 권장하고 있는 전자정부 표준 프레임워크의 기반 기술로 쓰이고 있다. (위키백과)

 

(자세한건 무슨말인지 모르겠으니 나중에 추가하자)

 

 

스프링 부트(Spring Boot)

스프링 부트는 독립적이고, 상업적, 산업적으로 많이 사용되는 스프링 프레임워크 베이스의 어플리케이션을 쉽게 생성할 수 있게 해 준다. 스프링 플랫폼과 사용할 3rd-party 라이브러리를 설정하는것으로 쉽게 시작할 수 있다. 

 - 독립적인 스프링 어플리케이션을 생성한다.

 - Tomcat,Jetty,Undertow 등을 내장하고있어 WAR 파일로 서버에 배포할 필요가 없다.

 - 'starter'를 제공해주므로 간단하게 build 환경을 구성할 수 있다.

 - 가능할 때 마다 Spring과 3rd Party 라이브러리를 자동으로 구성해준다.

 

 

Spring Initializr

Spring Initializr(https://start.spring.io/) 를 통해 프로젝트를 간단하게 생성할 수 있다! 

 

1) Project의 기본 설정을 입력한다. (프로젝트 메타데이터는 >Options를 눌러 자세하게 설정 가능)

2) 사용할 3rd-party를 검색하고 추가한다.

3) Generate 버튼을 누르면 프로젝트 파일이 다운로드 된다.

4) 프로젝트 파일의 압축을 풀고 IDE에서 폴더 열기로 실행

 

 

Spring Boot Project

IntelliJ 에서 프로젝트를 연 상환

1) src 안에 프로젝트 소스가 위치하게 됨

2) main>java>,,,>OOOApplication.java 파일이 프로그램 시작 파일이다.

3) test>java>,,,>OOOApplicationTests.java 파일에서는 테스트 함수가 들어 있다.

 

4) OOOApplication.java 파일을 실행하면 위와 같은 결과 화면이 나오고, 8080 포트에 톰캣 서버가 실행되었음을 알 수 있다. 

5) localhost:8080 으로 접속하여 실행 결과를 확인한다.

'개발노트' 카테고리의 다른 글

Lombok, Builder Pattern  (0) 2020.03.24
REST API  (0) 2020.03.19
[Spring] 가짜 객체(Mock Object)  (0) 2020.03.18
[Spring] 의존성 주입(Dependency Injection)  (0) 2020.03.18
Test Driven Development 실습  (0) 2020.03.12

+ Recent posts