객체지향 & TDD

2024. 9. 16. 21:19·백엔드/Spring

https://github.com/jyun-KIM/KUIT-4th-Server-study.git

 

GitHub - jyun-KIM/KUIT-4th-Server-study: 4th server- 혼자 공부하는 레포

4th server- 혼자 공부하는 레포 . Contribute to jyun-KIM/KUIT-4th-Server-study development by creating an account on GitHub.

github.com

전체 코드는 위 레포의 week-1 브랜치에서 확인할 수 있다.

 

객체지향 프로그래밍과 테스트 주도 개발(TDD)의 개념을 실습 코드와 함께 살펴보자

 

TDD의 기본 원칙은 테스트 코드를 먼저 작성하고, 그 테스트가 실패한 상태에서 이를 통과시키기 위한 최소한의 기능을 실제 코드에 구현하는 것이다. 즉, 테스트 코드가 개발의 방향을 제시하고, 그에 맞춰 실제 기능이 구현되는 과정이 TDD의 핵심이라고 할 수 있다.

 

프로젝트 구조는 다음과 같다.

src
├── main
│   └── java
│       └── org.example
│           ├── Account
│           ├── Balance
│           └── NaturalNumber
└── test
    └── java
        └── AccountTest
            ├── createAccount()
            ├── createAccount1000()
            ├── 계좌_입금_확인()
            ├── 계좌_출금_확인()
            └── 계좌_출금_잔액_부족_예외()

 

주제가 TDD인 만큼 test 코드에 정의된 함수를 바탕으로 main의 클래스 내용을 채워가며 개념을 정리해 보겠다.


createAccount(): 빈 계좌 생성 테스트

@Test
void createAccount() {
    Account account = Account.createAccountEmpty(new Balance());

    assertThat(account.getBalance()).isEqualTo(0);
}

 

코드에 사용되는 객체를 생성 방법에는 2가지가 있다.

1. new 키워드를 사용한 객체 생성

new 키워드는 자바에서 직접적으로 생성자를 호출하여 객체를 생성하는 가장 기본적인 방법이다.

Account account = new Account(new Balance());
  • 직접 생성자 호출: new 키워드를 사용하면 해당 클래스의 생성자를 호출하여 객체를 생성한다.
  • 직관적이고 단순: 객체 생성 과정이 명확하며, 클래스에 정의된 생성자를 그대로 사용한다.
  • 생성 과정 제어 불가: 객체 생성 시 특별한 제어(조건, 로직, 캐싱 등)를 할 수 없다. 즉, 생성자가 호출되면 객체가 바로 생성된다.
  • 유연성 부족: 생성자를 직접 호출하는 방식은 유연성이 떨어진다. 객체 생성 방식에 조건이 추가되거나, 객체 생성을 제어할 필요가 있을 때는 새로운 생성자를 추가해야 한다.

new 키워드 사용 시 new Balance()로 적는 이유는 이것이 생성자를 호출하는 방법 이기 때문이다.

Balance 클래스의 인스턴스를 만들기 위해서는 new 키워드와 함께 생성자를 호출해야 한다. 즉, new Balance()는 Balance 클래스의 인스턴스를 생성하는 표현이다.

생성자 호출 없이 단순히 new Balance 라고만 작성하면, 이는 문법적으로 잘못된 표현인 것이다. 생성자에는 괄호가 포함되어야 객체가 생성된다.


2. 팩토리 메서드를 이용한 객체 생성

팩토리 메서드는 클래스의 정적(static) 메서드를 사용하여 객체를 생성하는 방식이다. 생성자를 직접 호출하지 않고, 메서드를 통해 객체 생성 로직을 캡슐화한다.

Account account = Account.createAccountEmpty(new Balance());
  • 정적 메서드로 객체 생성: 객체 생성 로직을 메서드 내부에 캡슐화하고, new 키워드를 직접 사용하지 않는다.
  • 유연한 객체 생성 로직: 팩토리 메서드는 객체 생성 시 추가적인 로직(검증, 조건 처리, 객체 캐싱 등)을 넣을 수 있어 유연성이 크다.
  • 의미 있는 이름 제공: 팩토리 메서드는 생성자의 역할을 대체하면서 의미 있는 이름을 제공할 수 있어 코드의 가독성을 높인다.
    ex) createEmptyAccount()는 빈 계좌를 생성한다는 의미를 명확히 전달한다.
  • 객체 생성 로직 변경 가능: 팩토리 메서드를 사용하면 객체 생성 방식을 쉽게 변경할 수 있다.
    ex) 여러 조건에 따라 다른 객체를 반환 가능

main에 구현된 Account 객체를 살펴보면

  public static Account createAccountEmpty(Balance balance) {
        return new Account(balance);
    }


createAccountEmpty 함수가 static 메서드로 정의된 것을 볼 수 있다. 또한 반환값이 객체의 인스턴스 이므로 팩토리 메서드를 이용해서 "객체를 생성"한다는 말도 잘 들어맞는다.

static 메서드 내부적으로는 생성자를 사용해서 객체를 생성하는 것이다 !!


createAccount1000(): 1000원 입금된 계좌 생성 테스트

 @Test
    @DisplayName("1000원 계좌 생성")
    void createAccount1000() {
        Account account = Account.createAccountWithInitialDeposit(new Balance(), NaturalNumber.from(1000));

        Assertions.assertThat(account.getBalance()).isEqualTo(1000);
    }

 

이제 객체 생성 방식이 눈에 들어올 것이다.

Account.createAccountWithInitialDeposit(new Balance(), NaturalNumber.from(1000))는 static 메서드를 사용한 객체 생성 방식이며, new Balance()와 NaturalNumber.from(1000)를 인자로 받아 초기 입금된 계좌를 생성하는 코드이다.

이 중 NaturalNumber.from(1000) 또한 static 메서드를 사용하여 NaturalNumber 객체를 생성하는 방식이다.

Account 클래스의 getBalance() 함수에 대해 살펴보자

public class Account{

    private Balance balance;

    public int getBalance() {
        return this.balance.getBalance();
    }
 }
  • this: 현재 인스턴스를 가리키는 참조이다. 즉, 메서드나 생성자에서 호출되는 객체 자체를 나타낸다.
  • this.balance: 해당 Account 인스턴스가 가지고 있는 인스턴스 변수인 balance에 접근할 때 사용된다. 여기서 this는 현재 메서드를 호출한 특정 Account 객체를 가리키며, 그 객체의 balance 값을 참조한다.

따라서 getBalance() 메서드에서 this.balance.getBalance()는 현재 인스턴스의 balance 필드에 저장된 Balance 객체의 getBalance() 메서드를 호출하여 잔고 값을 반환한다.

1. 클래스 (Class)

  • 클래스는 객체를 생성하기 위한 설계도이다. 데이터(필드, 멤버 변수)와 해당 데이터를 처리하는 메서드를 정의한다.
  • 클래스 자체는 메모리에 할당되지 않고, 인스턴스를 생성하기 위한 템플릿일 뿐이다.

ex) Account 클래스는 계좌의 속성(예: 잔액)과 동작(예: 입금, 출금)을 정의한다.

public class Account {
    private int balance;  // 필드

    public Account(int balance) {  // 생성자
        this.balance = balance;
    }

    public int getBalance() {  // 메서드
        return balance;
    }
}

 


2. 인스턴스 (Instance)

  • 인스턴스는 클래스로부터 실제로 생성된 객체 이다. 클래스는 템플릿이고, 인스턴스는 그 템플릿을 바탕으로 메모리에 할당된 실제 데이터 인 것이다.
  • 인스턴스를 만들기 위해서는 new 키워드를 사용한다. 한 클래스는 여러 개의 인스턴스를 가질 수 있다.
    각 인스턴스는 서로 다른 속성값을 가지며, 독립적으로 존재한다.
Account myAccount = new Account(1000);  // 클래스 Account의 인스턴스 생성
Account anotherAccount = new Account(500);  // 또 다른 인스턴스 생성

 

여기서 myAccount와 anotherAccount는 서로 독립적인 인스턴스이다.

하나의 인스턴스에서 변경이 다른 인스턴스에 영향을 미치지 않는다.


3. 참조 (Reference)

  • 참조는 인스턴스(객체)의 메모리 주소를 저장하는 변수이다. 참조는 인스턴스 자체를 직접 다루는 것이 아니라, 인스턴스가 있는 메모리 위치를 가리키고 있다.
  • 참조 변수를 사용하여 인스턴스의 데이터에 접근하거나, 메서드를 호출할 수 있다. 자바에서 참조 변수는 기본적으로 객체를 다루는 방식이다.
Account myAccount = new Account(1000);  // myAccount는 참조 변수
Account anotherAccount = myAccount;  // anotherAccount는 myAccount를 참조

 

여기서 myAccount와 anotherAccount는 동일한 인스턴스를 가리킨다. 즉, 하나의 참조를 변경하면 다른 참조에도 영향을 미친다.

참조와 객체의 관계

  • Account myAccount = new Account(1000); 에서 myAccount는 Account 인스턴스를 가리키는 참조 변수이다.
    즉, myAccount 는 객체 그 자체가 아니라, 메모리에 있는 객체의 주소를 참조하고 있다.

계좌_출금_잔액_부족_예외(): 잔액 부족 출금 시 예외 발생 테스트

  @Test
    void 계좌_출금_잔액_부족_예외(){
        Account account = Account.createAccountWithInitialDeposit(new Balance(), NaturalNumber.from(10000));

        assertThatThrownBy(() -> account.withdraw(NaturalNumber.from(20000)))
                .isInstanceOf(IllegalArgumentException.class);
    }

 

마지막으로 검증 코드에 대해서 정리해보자

assertThat(account.getBalance()).isEqualTo(1000);


assertThat()은 AssertJ라는 테스트 라이브러리에서 제공하는 함수로, 주어진 조건이 기대하는 값과 일치하는지 검증하는 데 사용된다. JUnit과 함께 사용되어, 테스트에서 특정 값이 예상된 결과와 맞는지 여부를 확인하는 중요한 역할을 한다.

 

이 코드는 account.getBalance()의 반환값이 1000과 같은지를 검증한다. 검증이 실패하면 테스트가 실패하게 되며 코드의 결과가 의도한 대로 동작하는지 확인할 수 있다.

 

1. 람다 표현식 () -> account.withdraw(NaturalNumber.from(20000))

() -> account.withdraw(NaturalNumber.from(20000))
  • 람다 표현식은 익명 함수(즉, 이름이 없는 함수)를 작성하는 방식이다.
  • 여기서 () ->는 매개변수가 없다는 의미이다. 만약 매개변수가 있었다면 () 안에 넣었을 것이다.
  • account.withdraw(NaturalNumber.from(20000))는 람다 표현식에서 실행되는 코드로, 20,000원을 출금하려고 시도하는 메서드이다.
  • 해당 표현식은 함수형 인터페이스를 인수로 받는 메서드에 전달될 수 있다. 여기서는 assertThatThrownBy 가 그 메서드이다.

2. assertThatThrownBy 메서드

assertThatThrownBy(() -> account.withdraw(NaturalNumber.from(20000)))
  • assertThatThrownBy 는 AssertJ 라이브러리에서 제공하는 메서드로, 예외가 발생할 것으로 예상되는 코드 블록을 인수로 받는다.
  • 여기서는 인수로 람다 표현식을 전달하여 account.withdraw(NaturalNumber.from(20000)) 메서드를 호출하는 동안 예외가 발생하는지 확인한다.
  • 이 메서드는 예외를 처리하고, 발생한 예외에 대해 추가적인 검증을 할 수 있는 체이닝 메서드를 제공한다.

3. 메서드 체이닝

.isInstanceOf(IllegalArgumentException.class)
  • 메서드 체이닝은 하나의 메서드를 호출한 뒤, 그 결과로 또 다른 메서드를 연속해서 호출하는 방식이다.
    즉, 여러 메서드를 점(.)을 사용해 이어서 호출할 수 있는 것을 말한다.
  • 이 방식의 핵심은 앞의 메서드가 객체를 반환하기 때문에, 그 반환된 객체에서 바로 다음 메서드를 호출할 수 있다는 것이다.
    • 여기서 첫 번째 메서드인 assertThatThrownBy()는 어떤 예외가 발생하는지를 확인하고, 그 결과를 반환한다.
    • 그 반환된 결과에서 바로 다음 메서드인 isInstanceOf()를 호출할 수 있다. 이 방식으로, 여러 메서드를 하나의 줄에서 계속 연결할 수 있다.
  • isInstanceOf()는 전달된 예외가 특정 타입(IllegalArgumentException)인지 확인하는 메서드이다.
    이 경우, 출금 시도에서 발생한 예외가 IllegalArgumentException인지 검증한다.

'백엔드 > Spring' 카테고리의 다른 글

[자바 ORM 표준 JPA 프로그래밍 - 기본편] 04. 엔티티 매핑  (0) 2025.03.03
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 03. 영속성 관리  (0) 2025.02.28
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 02. JPA 시작  (0) 2025.02.28
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 01. JPA 소개  (1) 2025.02.26
[실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발] 도메인 분석 설계  (1) 2025.01.18
'백엔드/Spring' 카테고리의 다른 글
  • [자바 ORM 표준 JPA 프로그래밍 - 기본편] 03. 영속성 관리
  • [자바 ORM 표준 JPA 프로그래밍 - 기본편] 02. JPA 시작
  • [자바 ORM 표준 JPA 프로그래밍 - 기본편] 01. JPA 소개
  • [실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발] 도메인 분석 설계
JYUN_
JYUN_
예비 개발자 성장기록
  • JYUN_
    데브 스토리
    JYUN_
  • 전체
    오늘
    어제
    • 분류 전체보기 (89)
      • AWS & 클라우드 컴퓨팅 (1)
        • AWS (2)
      • AI & ML (17)
        • 딥러닝 (3)
        • 인공지능 기초 (2)
        • 자연어 처리 (3)
        • 컴퓨터 비전 (8)
      • CS 지식 (9)
        • 알고리즘 (1)
        • 자료구조 (4)
        • 지식확장 (1)
        • 컴퓨터 네트워크 (3)
      • 백엔드 (22)
        • Node.js (12)
        • Spring (9)
      • 웹 프론트엔드 (21)
        • HTML (3)
        • React (7)
        • 바닐라 JavaSctipt (11)
      • 코딩 테스트 & 문제 해결 (11)
        • 코딩 테스트 연습 (10)
        • 실전 문제 풀이 (1)
      • 트러블 슈팅 (1)
      • 기타 (4)
        • 개인 지식 관리 (1)
        • 외부 활동 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
JYUN_
객체지향 & TDD
상단으로

티스토리툴바