TDD 테스트 주도 개발

manNomi·2024년 10월 15일

WEB

목록 보기
4/9
post-thumbnail

테스트 주도 개발을 공부해보고 남기는 회고록

처음 TDD 라는 단어를 접했을때는 내가 사용하는 코드를 테스트 코드를 한다는 것에서 어려울 것이라고 느껴졌지만 막상 공부해보면 충분히 누구나 따라할 수 있을 것이라 생각한다 .

우리는 테스트 주도 개발을 왜할까 ?

  • 프론트엔드의 핵심은 유지보수이다 우리가 흔히 코드를 작성할때에는 내일 일어나면 이해하지 못할 코드를 작성하곤 한다. 가독성이 떨어져서 로직이 복잡해서 등 다양한 이유가 존재하는데 이 같은 문제가 유지보수를 해친다고 표현한다 . 그렇다면 프론트엔드의 핵심이 왜 유지보수 일까?
  • 다른 분야에 비해 프론트엔드의 기술은 나날이 발전하고 바뀌어 가고 있다 . 물론 하드웨어에 비해서 소프트웨어의 발전은 느리게 되고 있지만 소프트웨어의 기술 중 안드로이드를 제외하면 가장 빠르게 변하는 분야가 프론트엔드가 아닐까 싶다 . 안드로이드를 개발공부의 시작으로 선택했었는데 지금 내 코드를 보면 레거시 투성이다.
  • 프론트엔드는 트렌드에 민감하다 → UI , UX 가 쉽게 바뀌고 이에 따라 빠르게 변경을 해야한다 이는 유지보수가 느리다면 시간이 오래걸리게 되고 시간 = 돈 이기 때문에 유지보수를 저해한 코드는 악영향을 끼칠 수 밖에 없다 . 뷰와 로직을 분리하는것 부터 시작해 장기적으로 코드 품질을 유지하기 위해서는 유지보수가 필수적이고 사용자와 직접 대화를 하는 소통의 창구인 프론트엔드의 경우 사용자 신뢰를 위해서 누구보다도 유지보수를 신경 써야한다. 특히 다양한 디바이스와 상호작용하는 프론트엔드 기술은 더더욱 중요하다고 생각한다 .
  • 위 의 이유에 따라 유지보수를 높이기 위해서는 TDD (테스트 주도 개발) 방식을 이용해서 개발을 한다면 충분히 유지보수를 높이는데 기여할 수 있다
  • 서론이 길었다 그렇다면 테스트 주도 개발은 무엇일까 ?

TDD (테스트 주도 개발)란

코드를 작성하기 전에 먼저 테스트를 작성하는 개발 방법론

  • 말 그대로 테스트 먼저 작성하고 개발에 시작하는 것이다
  • 이 방식이 왜 유지보수에 도움이 될까?
  • tdd는 코드의 유지 보수성을 높이고 핵심원칙을 제공하는 방법론이다.
  • 나는 tdd가 단순히 기능을 개발하기 위한 도구가 아니라고 생각한다 . 즉 나와 팀원이 정한 규약이기 때문에 이 규약에 맞추어 개발을 진행하는 것 이기에 코드가 일관적이고 높은 품질을 가질 수 있다고 믿는다
    • 규약 → 개발 과정에서의 가이드라인을 의미하는 것 따라서 아래와 같은 생각이 나온다

      • “TDD는 코드 스타일 규칙은 아니지만, 개발 절차에 대한 규약과 가이드라인 역할을 한다.”

      • “TDD를 따르는 개발자는 동일한 형식과 흐름으로 개발을 진행하게 되므로, 팀 내 코드가 일관성을 유지하고 유지보수성이 높아진다.”

  • 리팩토링 시에도 테스트 진행이 쉽다 . 코드 리팩토링을 진행하다보면 코드가 제대로 동작하지 않는 경우가 많이 발생한다 흔히 우리가 말하는 “건들면 안되는 코드” 가 되는것이다 . 이를 사전에 방지하기 위해서 tdd가 도움이 될 수 있을 것이라고 믿는다
  • 새로운 기능 추가시에도 부작용을 방지할 수 있고 빠른 버그를 잡을 수 도 있다. → 일일이 테스트를 하지 않아도 오류가 나는 부분을 찾기 쉬움
  • 테스트 코드를 보면 우리는 기능명세서와 비슷한 인상을 받을 수있다. 입력값 → 결과값이 정해져 있기에 코드로 쓰는 문서다 . 개발자는 문제를 해결하지만 문서를 작업하는 직업이라고 생각한다. 문서화를 통해 모두가 같은 코드를 작성하게 끔 유도 되게 해서 코드의 일관성을 높인다고 생각하는데 이를 코드로서 작성해 규약화 해놓는다면 코드품질이 확실히 일관적일 것이다 즉 개발자에게 코드의 동작 이해하는 가이드 라인이 되는 것이다.
  • 즉 위의 이유로 인해 예측가능한 부작용을 막을 수 있고 빠른 피드백 루프를 가질 수 있다는 것이다 .

TDD 작성 방식은 아래와 같다

  1. Red (테스트 작성 및 실패 확인)

• 코드를 작성하기 전에 테스트 케이스를 먼저 작성한다

• 이때 해당 테스트는 당연히 실패 → (아직 기능이 구현되지 않았으므로)

  1. Green (테스트 통과를 위한 최소한의 코드 작성)

• 테스트를 통과시키기 위해 최소한의 코드를 작성한다

• 이 단계에서는 코드의 품질보다는 테스트 통과가 목표

• 그린 단계에서 가장 중요한것은 최소한의 코드 라는 것이다

  1. Refactor (리팩터링)

• 테스트를 통과한 코드의 가독성유지보수성을 높이기 위해 코드를 개선

• 리팩터링 후에도 테스트가 여전히 통과하는지 확인

  • 실패 케이스를 먼저 작성하면서 우리가 구현하고자 하는 기능이 무엇인지 명확하게 정의를 해야한다
  • 우리는 입력에 따라 출력이 어떤지를 항상 염두해두고 개발해야하는데 이 점이 명확해지는 부분이 테스트코드이다. 이 테스트코드로 인해서 우리는 로직이 다르더라도 함수에 들어가는 값 , 나오는 값 , 함수의 명명이 같아지는 즉 기능이 동일한 코드를 쓸 수 있게 된다 .
  • 간단한 예시로 우리는 add(2,3) 은 5가 되어야 한다고 알 고 있다 . 왜냐 이 테스트 코드를 보더라도 add 의 기능은 덧셈이고 2와 3 이 들어가면 5가 반환되어야함을 알 고 있기 때문이다 . 그리고 이 코드가 작동하지 않으면 개발이 아직 안되었다는 것도 쉽게 확인 가능하다 테스트 주도 설계 방법의 단점은 찾기가 힘들다

리팩터링 이후에는 테스트 코드에 난수를 넣는 등 다양한 테스트를 시험해보는 것도 좋다 무작위 값을 넣어보는등

코드의 강건성을 검증하는 것도 테스트 주도 개발에서 얻을 수 있는 장점이다 모든 경우의수를 직접 테스트하는 것은 힘들기 때문

아래는 간단한 테스트코드 예제이다

예제를 보면 조금더 직관적으로 이해하기 쉽다

calculator.test.js

const Calculator = require('./calculator');

describe('Calculator', () => {
  let calculator;

  // 각 테스트 전에 새 계산기 인스턴스 생성
  beforeEach(() => {
    calculator = new Calculator();
  });

  test('두 수를 더할 수 있다', () => {
    expect(calculator.add(2, 3)).toBe(5);
  });

  test('두 수를 뺄 수 있다', () => {
    expect(calculator.subtract(5, 3)).toBe(2);
  });

  test('두 수를 곱할 수 있다', () => {
    expect(calculator.multiply(2, 3)).toBe(6);
  });

  test('두 수를 나눌 수 있다', () => {
    expect(calculator.divide(6, 3)).toBe(2);
  });

  test('0으로 나누면 에러를 던진다', () => {
    expect(() => calculator.divide(6, 0)).toThrow('0으로 나눌 수 없습니다');
  });
});

calcultor.js

class Calculator {
  add(a, b) {
    return a + b;
  }

  subtract(a, b) {
    return a - b;
  }

  multiply(a, b) {
    return a * b;
  }

  divide(a, b) {
    if (b === 0) {
      throw new Error('0으로 나눌 수 없습니다');
    }
    return a / b;
  }
}

module.exports = Calculator;
  • 테스트 코드 사용 방법 (JS)
    # 프로젝트 폴더 생성
    mkdir tdd-calculator && cd tdd-calculator
    
    # Node.js 초기화
    npm init -y
    
    # Jest 설치
    npm install --save-dev jest
    packge.json
    {
      "scripts": {
        "test": "jest"
      }
    }
    테스트 방법
    npm test

테스트 코드의 단점이 없을 것 만 같지만 당연히 존재한다

우리에게 시간 = 돈이다. 테스트 주도 개발을 간단한 곳에서부터 다양한 곳에서 적용하면 물론 코드의 일관성과 장기적인 관점에서 매우 유용할 것이다. 허나 이는 개발 속도에도 영향을 미칠 수 있다. 다양한 케이스를 고려하며 개발을 하면 시간이 당연히 오래 걸릴 수 밖에 없고 빠르게 개발해야하는 경우에는 상당히 치명적인 개발 방법론이 될 것이다. 따라서 내 생각에는 TDD와 개발간의 적절한 조화가 중요하다고 생각한다 모든 코드를 테스트하기는 힘들고 테스트 코드 자체도 유지보수가 언젠가는 필요하게 되기 때문이다 .


결론

어디까지 테스트를 작성하고, 어디까지 테스트할지를 정하는 것은 개발자의 중요한 역량이라고 생각했다. 시간은 돈이라는 관점에서 코드 품질과 개발 속도의 균형을 찾는 것은 필수적이다. 따라서 테스트 코드를 작성하며 품질을 관리하는 습관을 들이는 것이 개발을 떠나 시간과 돈에서도 유용하다고 생각한다.

그동안 나는 유지보수를 위한 클린코드를 작성했을까 ? UI/UX 개발 후 다양한 테스트를 해왔지만, 정작 중요한 순간에 문제가 발생하는 경험을 여러 번 겪었었다. 유지보수와 클린 코드 작성에는 신경을 많이 썼지만, 테스트 주도 개발(TDD)에는 익숙하지 않았다. 이번에 TDD 방식을 체험하고, 테스트 코드를 동료들과 공유하며 배워 내 코드를 누구보다도 클린하고 유지보수하기 쉽도록 마치 그림책을 읽듯이 만들고자 하는 것이 목표다 .

이 기회를 통해 코드와 테스트를 문서화하는 습관을 체득하고자 한다. 문서는 개발의 필수다. 그 누가 문서를 보더라도 같은 기능을 설계하고 코딩하는데 코드를 통해 문서화 하는 방법 이를 나만의 개발 규약으로 삼고자 한다. 이 개발 규약은 누가 보더라도 이해하기 쉽고 단번에 유지보수가 가능할 것이다.. 개발자는 문제의 이유를 찾고 해결하는 사람이라고 생각하는데 이를 혼자 진행하지는 않는다. 내가 작성한 코드의 필요성을 이해하며, 팀원과 함께 코드를 리뷰하고 문제를 해결해 나가는 과정이 곧 더 나은 유지보수와 성장의 길이라고 믿기 때문에 TDD 개발방법을 채택하고자 한다

profile
weapp개발자

0개의 댓글