TDD 란 ?

Test Driven Development의 약자로 '테스트 주도 개발'을 뜻한다.

단순히 테스트 코드를 작성하는 기법이 아니라, 소프트웨어 개발의 한 방법론이자 개발 철학에 가깝다.

 

TDD는 실패하는 테스트 코드를 먼저 작성한 후, 그 테스트 코드를 통과시키기 위한 최소한의 실제 코드를 작성하고,

마지막으로 코드를 개선(리팩토링)하는 과정을 반복하는 개발 방식이다.

이름대로 테스트가 개발 과정을 이끌어 나가는 방식이라고 볼 수 있다.

 

핵심 과정 : ( Red-Green-Refactor Cycle)

 

TDD는 다음의 세 단계를 매우 짧은 주기로 반복하며 진행된다.

 

1. 레드(RED)

  • 실패하는 테스트 코드를 먼저 작성한다.
  • 새로 구현하려는 기능의 요구사항을 정의하고, 이 요구사항을 만족하는 코드가 아직 없기 때문에 당연히 이 테스트 코드는 실행 시 실패해야 한다.
  • 어떤 기능을 만들고 싶은 지, 그 기능이 어떤 입력에 대해 어떤 출력을 내보내야 하는 지를 테스트 코드를 먼저 명확하게 정의하는 단계이다.
  • 테스트가 왜 실패하는 지 확인하는 것까지가 중요하다.

 

2. 그린(GREEN)

  • 실패하는 테스트 코드를 통과시키기 위한 '최소한의' 실제 코드를 작성한다.
  • 이 단계에서는 코드의 품질이나 설계의 아름다움보다는 오직 테스트를 통과시키는 것에 집중한다.
  • 코드가 중복되거나 비효율적이어도 상관 없다.
  • 테스트가 통과하여 상태가 'Green'으로 바뀌는 것을 확인하는 것이 목표이다.

 

3. 리팩터(REFACTOR)

  • 테스트 코드가 모두 통과하는 상태(Green 상태)를 유지하면서 코드의 품질을 개선한다.
  • 중복된 코드를 제거하거나, 함수/클래스를 더 작고 명확하게 분리하거나, 변수 이름을 더 잘 이해할 수 있도록 바꾸는 등 코드의 구조와 가독성을 개선한다.
  • 리팩터링 후에도 모든 테스트가 여전히 통과되는 지 다시 한번 확인한다.
  • 테스트가 안전망 역할을 해주기 때문에 코드 변경 시 안정성을 확보할 수 있다.

위 사이클을 새로운 기능을 추가하거나 기존 기능을 수정할 때마다 반복한다.

각 사이클은 짧게는 몇 분에서 길게는 몇십 분 내로 완료하는 것을 목표로 한다.

 

TDD의 장점

TDD는 처음에는 개발 속도를 늦추는 것처럼 느껴질 수 있지만, 장기적으로 볼 때 다음과 같은 많은 이점을 제공한다.

 

1. Better Design

  • 코드를 작성하기 전에 코드를 사용하는 방법(테스트 코드)를 먼저 고민하게 만든다.
  • 이는 실제 사용할 때 편리하고 모듈성이 높은 코드를 설계하도록 유도한다.
  • 테스트하기 쉬운 코드는 일반적으로 의존성이 낮으며 응집도가 높다.

2. Reduced Bugs

  • 요구사항을 테스트 코드로 먼저 명확하게 정의하면서 설계 단계에서 많은 잠재적인 버그나 예외 상황을 미리 발견하고 고려하게 된다.
  • 잘 작성된 테스트 스위트(Test Suite)는 코드 변경 시 예상치 못한 Regression을 방지하는 강력한 안전망 역할을 한다.

3. Increased Confidence

  • 모든 기능이 자동화된 테스트를 통해 검증되므로, 코드가 예상대로 정확하게 동작한다는 확신을 가질 수 있다.
  • 이는 코드 변경이나 기능 추가 시 두려움을 줄여준다.

4. Living Documentation

  • 테스트 코드는 해당 기능이 어떻게 사용되어야 하는 지를 보여주는 가장 정확하고 항상 최신 상태로 유지되는 역할을 한다.
  • 새로운 개발자가 코드베이스를 이해하는 데 큰 도움이 된다.

5. Faster Development in the Long Run

  • 초기 테스트 작성 시간 때문에 속도가 느려 보이는 것 같지만, 디버깅 시간이 현저히 줄어들고 버그 수정 및 회귀 테스트 비용이 감소하면서 장기적으로는 전체 개발 속도가 향상된다.

+ TDD가 디버깅을 없애는 것은 아니다. 하지만 디버깅에 소요되는 시간과 노력을 크게 줄여준다.

 

일반 개발 방식과 TDD 의 차이

일반적인 개발 방식 : 요구사항 분석 -> 설계 -> 개발 -> 테스트 -> 배포

  • 코드를 먼저 작성하고, 기능이 잘 작동하는지 테스트를 통해 확인한다.
  • 주로 기능 구현 후, 버그 수정 및 테스트를 진행하는 형태이다.
  • 테스트는 기능 개발 후에 추가되거나, 필요한 경우에만 작성된다.

.테스트의 역할:

  • 일반 개발 방식:
    • 테스트는 개발 후에 기능이 정상적으로 동작하는지 확인하는 수단으로 사용된다.
    • 개발 완료 후에도 기능이 변경될 때마다 추가적인 테스트나 수정이 필요하다.
  • TDD:
    • 테스트는 개발의 출발점
    • 테스트는 코드가 개발되기 전부터 정의되며, 코드가 테스트를 통과할 때까지 반복적으로 작성되고 수정된다.
    • 테스트가 항상 코드를 검증하는 중요한 도구로 기능한다.

 

TDD의 대표적인 Tool

JUnit란 ?

Java 언어로 작성된 애플리케이션에서 단위 테스트를 작성하고 실행할 수 있도록 도와주는 테스트 프레임워크이다.

JUnit은 TDD(Test-Driven Development)를 실천하는 데 중요한 역할을 하며, 코드가 요구 사항을 충족하는지 자동으로 검증하는 데 사용된다.

JUnit을 사용하면 수동으로 테스트를 실행할 필요 없이 테스트를 자동으로 실행할 수 있다.

이를 통해 테스트 자동화가 가능해지고, 코드 변경 시 테스트를 반복적으로 실행하여 버그를 빨리 찾을 수 있다.

또한, JUnit은 테스트가 서로 독립적으로 실행되도록 보장한다.

 

'CS' 카테고리의 다른 글

Microsoft Azure에서 제공하는 API 디자인 모범 사례  (0) 2025.04.04
[소프트웨어 공학] UML  (1) 2024.10.20

https://learn.microsoft.com/ko-kr/azure/architecture/best-practices/api-design

 

웹 API 디자인 모범 사례 - Azure Architecture Center

플랫폼 독립성과 서비스 발전을 지원하는 웹 API를 디자인하기 위한 모범 사례를 알아봅니다.

learn.microsoft.com

 

위 문서를 읽고 내용을 공부하며 요약한 포스팅이다.

 

API는 시스템의 중요한 부분으로, 애플리케이션과 서비스가 상호작용할 수 있도록 한다.

잘 설계된 API는 효율적이고, 안전하며, 유지보수가 용이하다.

 

1. 명확하고 일관된 URL 경로 설계

URL 경로는 API의 주요 부분이며, 사용자와 다른 개발자에게 API의 목적을 명확히 전달해야 한다

  1. 리소스 중심의 경로 사용: URL 경로는 명사를 사용하여 리소스를 나타내야 한다.
  2. 복수형 명사 사용: 리소스 목록을 다룰 때는 복수형을 사용하는 것이 좋다.
  3. 계층적 경로 설계: 리소스 간 관계를 명확히 하기 위해 계층적 경로를 사용한다.
  4. HTTP 메소드 활용: 경로와 함께 HTTP 메소드를 적절히 사용해야 한다.

 

2. 적절한 HTTP 상태 코용

HTTP 상태 코드는 API의 응답 결과를 나타낸다.

각 상태 코드는 요청이 성공했는지, 오류가 발생했는지를 명확히 전달한다.

  • 200번대: 성공적인 요청을 나타냄. 예: 200 OK, 201 Created
  • 400번대: 클라이언트 오류를 나타냄. 예: 400 Bad Request, 404 Not Found
  • 500번대: 서버 오류를 나타냄. 예: 500 Internal Server Error, 502 Bad Gateway

적절한 상태 코드를 사용하여 클라이언트가 요청의 결과를 이해할 수 있도록 해야 한다.

 

 

3. 입력 유효성 검사 및 오류 처리

API가 받는 입력 데이터에 대한 유효성 검사를 철저히 해야 하며, 오류가 발생한 경우에는 명확하고 일관된 오류 메시지를 반환해야 한다.

  • 유효성 검사: 필수적인 데이터나 잘못된 형식의 데이터가 요청에 포함되지 않도록 확인합니다.
  • 구체적인 오류 메시지: 오류 발생 시에는 구체적인 메시지를 반환하여 사용자나 개발자가 문제를 해결할 수 있도록 돕는다. 예를 들어, 400 Bad Request 응답에 "Invalid email format"과 같은 메시지를 포함할 수 있다.

 

4. 버전 관리 (Versioning)

API는 시간이 지나면서 변경될 수 있으므로, 버전 관리는 필수적이다.

버전 관리를 통해 기존 사용자가 API를 계속 사용할 수 있도록 하며, 새로운 기능을 추가할 수 있다.

  • URL 경로에 버전 정보 포함: /v1/, /v2/와 같은 방식으로 버전을 명시
  • 헤더에서 버전 관리: Accept 헤더를 사용하여 클라이언트가 요청하는 버전을 지정할 수도 있음. 예를 들어, Accept: application/vnd.myapi.v1+json.

 

5. 보안 고려사항

API 보안은 매우 중요. 인증 및 인가 절차를 통해 API가 안전하게 보호되어야 함

  • OAuth 2.0: API에 대한 안전한 인증 방법으로 OAuth 2.0을 사용하는 것이 권장된다.
  • TLS/SSL 사용: 모든 API 요청은 HTTPS를 통해 이루어져야 한다. 이를 통해 데이터가 전송 중에 안전하게 보호된다.
  • 권한 관리: 각 사용자가 접근할 수 있는 자원을 명확히 정의하고, 권한을 제한하여 불필요한 정보 유출을 방지해야 한다.

 

6. 응답 데이터 포맷 일관성 유지

API의 응답 데이터 포맷은 일관성 있게 설계되어야 하며, 일반적으로 JSON 형식을 사용한다.

  • 통일된 응답 구조: 모든 API 응답은 일정한 형식을 유지해야 한다. 예를 들어, 응답 본문에 항상 data, error, message 등의 필드를 포함하여 일관된 구조를 유지한다.
  • 상태 및 메시지 포함: 응답에 처리 결과와 상태 메시지를 포함시키는 것이 좋다.

 

7. 성능 최적화

효율적으로 동작해야하며, 성능 최적화가 필요하다.’

  • 쿼리 파라미터 사용: 필요한 데이터만 반환하도록 필터링, 정렬, 페이지네이션을 쿼리 파라미터로 처리할 수 있다. 예: /orders?page=1&limit=10
  • 캐싱 사용: 응답 결과를 캐시하여 성능을 향상시킬 수 있다. 적절한 Cache-Control 헤더를 설정하여 자주 요청되는 데이터를 캐시한다.

 

8. 문서화

API 문서화는 매우 중요하다. 개발자들이 API를 쉽게 이해하고 사용할 수 있도록 명확한 문서를 제공해야 한다.

  • 자동화된 문서화 도구: Swagger, OpenAPI 등과 같은 도구를 사용하여 API 문서를 자동으로 생성하고 관리할 수 있다.
  • 명확한 예시 제공: 요청 및 응답 예시를 통해 API 사용 방법을 쉽게 이해할 수 있도록 한다.

'CS' 카테고리의 다른 글

TDD 방법론  (0) 2025.04.28
[소프트웨어 공학] UML  (1) 2024.10.20

UML (Unified Modeling Language)

 

UML

  • UML 은 객체 지향 설계와 개발에 주로 사용되며, 다양한 다이어그램을 통해 시스템의 구조와 동작을 시각적으로 표현
  • 복잡한 시스템을 명확하게 설명하고 개발 과정에서 발생할 수 있는 오해나 오류를 줄일 수 있음
  • 소프트웨어의 전체를 판단할 수 있는 12개의 다이어그램 제시
  • 소프트웨어 시스템을 설계하고 이해하는 데 사용되는 표준화된 시각적 언어

UML 의 역할

  • 시각화
  • 문서화
  • 설계
  • 코드 생성 및 유지보수

 

 

유스케이스 다이어그램

  • 시스템이 사용자(또는 외부 시스템)와 어떻게 상호작용하는지를 시각적으로 보여 줌
  • 시스템이 제공해야 하는 기능을 이해하고, 그 기능이 어떻게 구현되어야 하는 지 명확하게 하는 데 중요한 역할
  • 구성 요소
    • 액터
      • 시스템 외부에서 시스템과 사용하는 주체
      • 사용자 액터, 시스템 액터, 주요 액터, 보조 액터, 프록시 액터
    • 유스케이스
      • 시스템이 액터에게 제공하는 기능이나 서비스, 즉 사용자가 시스템을 통해 사용하고 싶은 기능
      • 유스케이스가 모여 하나의 서브시스템을 이루고 서브시스템이 모여 개발 시스템이 됨
      • 전체 시스템은 유스케이스를 모아 놓은 것과 같아야 함
      • 가능하다고 생각되는 것을 모두 도출해놓고 검증하면서 삭제하는 루트
    • 시스템
      • 시스템이 제공하는 모든 기능의 경계
      • 유스케이스들이 이 경계 내에 위치하고, 시스템 경계는 사각형으로 그림
    • 관계
      • 액터와 유스케이스 간의 상호작용
      • 액터의 일반화 관계 (↔ 특수화)
      • 연관 관계
        • 액터가 유스케이스를 실행하는 일반적인 관계
        • 선으로 표현
      • 포함 관계
        • 유스케이스의 일부 기능이 다른 유스케이스에서도 공통으로 필요하다면 이를 별도로 만든 뒤 호출해 사용하는 것
        • 피포함 유스케이스 : 공통으로 사용하기 위해 별도 유스케이스로 만들어 놓은 것
        • 포함 관계는 기본 유스케이스 → 피포함 유스케이스
        • 포함 관계는 선행 조건(유스케이스 명세서 작성 시 필요)과 혼동될 우려가 있음
          • 선행 조건 : 특정 유스케이스가 실행 되기 전에 반드시 충족되어야 하는 조건
        • 점선 화살표
      • 확장 관계
        • 기본 유스케이스의 기능에 선택적으로 추가할 수 있는 기능
        • 예외적인 상황이나 어쩌다 한 번 발생하는 것을 확장 관계로 표현 X
        • 점선 화살표
    • 활용
      • 요구사항 분석 : 기능적 요구사항을 정의하고 분석하는 데 사용
      • 시스템 설계 : 시스템이 제공할 기능을 설계하고, 각 기능의 연관성을 이해하는 데 사용
      • 개발팀 간 의사소통 : 여러 이해관계자 간의 의사소통 원활히 함

 

 

 

클래스 다이어그램

  • 소프트웨어의 기본 구성 단위인 시스템에서 사용하는 클래스를 정의
  • 클래스들이 서로 어떻게 연결되어 있고 어떤 역할을 하는 지 다이어그램으로 표현

클래스

  • 데이터(속성)과 메서드를 묶어 놓은 것
  • 세 칸의 직사각형 모양 ( 클래스 이름 - 클래스의 속성 - 클래스가 제공하는 기능인 메서드 )
  • 클래스는 다른 클래스와 구별되는 unique한 이름 가짐

메서드

  • 클래스가 외부의 다른 객체에게 제공할 서비스와 기능
  • 외부 클래스는 메서드를 통해 해당 클래스에 접근할 수 있음
  • 외부에서 이 기능을 요구하는 지에 따라 메서드로 도출할지 판단

가시성

  • 속성과 메서드의 접근 권한을 지정하는 방식
  • public(+) : 같은 시스템에 있는 모든 클래스에 접근할 수 있음
  • private(-) : 같은 시스템 내 다른 클래스는 직접 접근할 수 없고, 해당 클래스의 메서드를 통해서만 접근할 수 있음
  • protected(#) : 다른 클래스가 접근할 수 없고 해당 클래스의 메서드와 클래스를 상속받은 하위 클래스만 접근할 수 있음

 

 

순차 다이어그램

  • 실행 시점에 객체들이 어떻게 상호 동작하는 지를 메시지 순서에 초점을 맞춰 나타낸 것
  • 어떠한 작업이 객체 간에 발생하는 지를 시간 순서에 따라 쉽게 파악할 수 있음
  • 구성 요소
    • 객체
      • ‘객체명: 클래스명’으로 나타내며 한쪽은 생략할 수 있음
      • 메세지를 보내고 받는 것
    • 객체 생명선
      • 객체의 생존 기간을 나타내며 X 표시는 객체가 소멸되는 시점
      • 위에서 아래로 내려가는 점선으로 나타냄
      • 생명선을 따라 나타나는 직사각형은 활성 구간으로 객체의 메서드가 실행되고 있음을 나타냄
      • 구간의 길이는 메서드의 실행 시간
      • 활성 구간은 반드시 존재하는 것은 아니며 생략 가능
    • 메시지
      • 객체와 객체의 상호작용을 표시하는 것으로 화살표를 이용
      • 화살표 위에는 수신 객체의 함수명을 씀
      • 동기 메세지 / 비동기 메세지
      • 재귀 메세지 / 답신 메세지
  • 작성 과정
    • 시나리오 정의
    • 다이어그램과 시나리오의 1 : 1 대응
    • 시나리오 분할

 

통신 다이어그램

  • 객체 간 상호작용 관계에 주목
  • 화살표 위에 적힌 번호로 순서를 알 수 있음
  • 링크
    • 객체 간에 메세지를 주고 받는 관계
    • 통신 다이어그램에서는 링크를 사용해 객체 간의 관계를 표현

 

 

 

활동 다이어그램

  • 흐름도와 비슷하나 객체의 행위를 나타낸다는 점에서 다름
  • 상위 수준에서는 업무의 흐름을 표현
  • 분석 단계에서는 유스케이스의 구체적인 흐름을 나타냄
  • 설계 단계에서는 클래스 내부동작에 대한 알고리즘이나 로직을 표현
  • 구성요소
    • 시작점, 종료점, 활동, 전이
    • 분기와 병합
    • 동기화 막대
    • 신호 : 활동 사이의 상호작용은 제어 신호를 보내는 방식으로 이루어짐
      • 행위 호출, 신호 송신, 이벤트 수신, 시간 이벤트 수신
    • 구획면
      • 객체별 레인

 

 

상태 다이어그램

  • 이벤트 발생이나 시간의 흐름에 따라 바뀌는 객체의 상태를 나타냄
  • 구성요소
    • 상태
      • 객체가 존재할 수 있는 조건
      • 객체의 존재 가능한 모든 상태가 파악되어야 함
      • 상태는 모서리가 둥근 사각형으로 표현
    • 전이
      • 객체의 상태가 바뀌는 것을 나타냄
      • 화살표로 표현
    • 이벤트
      • 전이를 나타내게 하는 자극

 

 

컴포넌트 다이어그램

  • 구현 관점에서 정적 모델링을 할 때 사용
  • 어떤 실행 모듈이 존재하고 서로 어떤 연관성이 있는 지의 종속 관계를 나타냄
  • 논리적 또는 물리적 시스템의 구조를 볼 수 있게 해줌
  • 구성 요소
    • 컴포넌트
      • 시스템을 구성하는 물리적인 요소로 프로그램 코드를 포함
    • 인터페이스
      • 클래스 모양으로 나타내거나 아이콘을 사용해 간략하게 표현
    • 의존 관계
      • 서로 영향을 주고 받는 관계를 말함
      • 점선 화살표로 표현

 

 

배치 다이어그램

  • 하드웨어 자원을 명시적으로 정의해 시스템의 물리적인 요소를 모델링하고 노드 간의 관계를 나타냄
  • 노드 : 시스템을 구성하는 처리 장치로 실행 파일 수준의 컴포넌트가 탑재된 하드웨어
  • 노드를 연결하는 관계는 다양한 통신 방식을 의미

 

'CS' 카테고리의 다른 글

TDD 방법론  (0) 2025.04.28
Microsoft Azure에서 제공하는 API 디자인 모범 사례  (0) 2025.04.04

+ Recent posts