TDD - 켄트백(4) 리팩토링 & 단련

InSeok·2023년 2월 16일
0

TDD

목록 보기
4/4

리팩토링

차이점 일치시키기

  • 비슷해 보이는 두 코드 조각을 합치려면 어떻게 해야 할까?
    • 두 코드가 단계적으로 닮아가게끔 수정한다.
    • 하위클래스의 내용이 비어 있으려면 메서드의 내용이 상위 클래스의 메서드 내용과 동일하면된다.
    • 하나씩 하위클래스의 내용을 비우고 모두 비게 되면 하위 클래스에 대한 참조를 상위 클래스로 바꾼다.

변화 격리하기

  • 객체나 메서드의 일부만 바꾸려면 어떻게 해야 할가?
    • 일단 바궈야 할 부분을 격리한다.
  • 변화를 격리하기 위한 바업
    • 메서드 추출
    • 객체 추출
    • 매서드 객체

데이터 이주시키기

  • 표현양식을 변경하려면 어떻게 해야 할까?
    • 일시적으로 데이터를 중복시킨다.
  • 내부의 표현 양식을 변경한 후 외부 인터페이스를 변화시키는 방법
    • 새로운 포맷의 인스턴스 변수를 추가한다.
    • 기존 포맷의 인스턴스 변수를 세팅하는 모든 부분에서 새로운 인스턴스 변수도 세팅하게 만든다.
    • 기존 변수를 사용하는 모든 곳에서 새변수를 사용하게 만든다.
    • 기존 포맷을 제거한다.
    • 새포멧에 맞게 외부 인터페이스를 변경한다.

메서드 추출

  • 길고 복잡한 메서드를 읽기 쉽게 만들려면 어떻게 할까?
  • 긴메서드의 일부분을 메서드로 분리해내고 이를 호출하게 한다.

메서드 인라인

  • 너무 꼬여있거나 산재한 제어 흐름을 단순화하려면 어떻게 할까?
    • 메서드를 호출하는 부분을 호출될 메서드의 본문으로 교체한다.
  • 방법
    1. 메서드를 복사한다.
    2. 메서드를 호출하는 부분을 지우고 복사한 코드를 붙인다.
    3. 모든 형식 매개변수를 실제 매개 변수로 변경한다.

인터페이스 추출

  • 메서드에 대한 두번째 구현을 추가하려면 어떻게 해야 할가?
    • 공통되는 메서드를 담고있는 인터페이스를 만들면된다.
  • 방법
    1. 인터페이스를 선언한다.
    2. 기존 클래스가 인터페이스를 구현하도록 만든다.
    3. 필요한 메서드를 인터페이스에 추가한다.
    4. 필요한 메서드를 인터페이스에 추가한다.
    5. 가능한 모든 곳의 타입 선언부에 클래스이름 대신 인터페이스 이름을 사용하게 바꾼다.

메서드 옮기기

  • 메서드를 원래 있어야 할 장소로 옮기려면 어떻게 해야 할까?
    • 어울리는 클래스에 메서드를 추가해주고, 그것을 호출하게 하자
  • 방법
    1. 메서드를 복사한다.
    2. 원하는 클래스에 붙이고 이름을 적절히 지어준 다음 컴파일 한다.
    3. 원래 객체가 메서드 내부에서 참조된다면, 원래 객체를 새 메서드의 매개변수로 추가한다.
    4. 원래 메서드의 본체를 지우고, 그곳에 새 메서드를 호출하는 코드를 넣는다.
  • 다른 객체에 두개 이상의 메시지를 보내는 코드를 볼 때마다 메서드 옮기기를 해주면된다

메서드 객체

  • 여러개의 매개 변수와 지역변수를 갖는 복잡한 메서드를 어떻게 표현할까?
    • 메서드를 꺼내서 객체로 만들자.
    • 메서드 객체는 메서드 추출하기를 적용할 수 없는 코드를 간결하게 만들기 위한 용도로도 적합하다.
  • 방법
    • 메서드와 같은 매개변수를 갖는 객체를 만든다.
    • 메서드의 지역변수를 개체의 인스턴스 변수로 만든다.
    • 원래 메서드와 동일한 내용을 갖는 run() 이란는 이름의 메서드를 만든다.
    • 원래 메서드에서는 새로 만들어진 클래스의 인스턴스를 생성하고 run()을 호출한다.

메서드 매개변수를 생성자 매개변수로 바꾸기

  • 동일한 매개 변수를 같은 객체의 서로 다른 몇몇 메서드로 전달하는 경우, 매개변수를 한번만 전달 하게끔 API를 단순화할 수 있다.
  1. 생성자에 매개변수를 추가
  2. 매개변수와 같은 이름을 갖는 인스턴스 변수를 추가
  3. 생성자에서 인스턴수 변수의 값을 설정한다.
  4. ‘parameter’를 this.parameter로 하나씩 찾아 바꾼다.
  5. 매개변수에 대한 참조가 더 이상 존재하지 않으면 해당 매개변수를 메서드와 모든 호출자에서 제거한다.
  6. 이제 필요 없어진 this.를 제거한다.
  7. 변수명을 적적히 변경한다.

TDD 마스터하기

단계가 얼마나 커야하나?

  • 각 테스트가 다뤄야 할 범위는 얼마나 넓은가?
  • 리팩토링을 하면서 얼마나 많은 중간단계를 거쳐야 하는가?

테스트할 필요가 없는 것은 무엇인가?

  • 테스팅해야할 목록
    • 조건문
    • 반복문
    • 연산자
    • 다형성
  • 다른사람이 만든 코드를 테스트하지는 마라.

좋은 테스트를 갖췄는지 여부를 어떻게 알 수 있는가?

  • 설계문제가 있음을 알려주는 테스트의 속성
    • 긴 셋업 코드:
      • 하나의 단순한 단언을 수행하기 위해 수백 줄의 객체 생성코드가 필요하다면 문제가 있는것이다.
      • 객체가 너무 크다는 뜻이므로 나눌 필요가 있다.
    • 셋업중복
      • 공통의 셋업 코드를 넣어 둘 공통의 장소를 찾기 힘들다면, 서로 밀접하게 엉킨 객체들이 너무 많다는 뜻이다.
    • 실행시간이 오래걸리는 테스트
    • 깨지기 쉬운테스트
      • 예상치 못하게 실패하는 테스트가 있다면 어느 애플리케이션의 특정 부분이 다른 부분에 이상한 방법으로 영향을 끼친다는 뜻이다.
      • 연결을 끊거나 두 부분을 합하는것을 통해 멀리 떨어진 것의 영향력이 없어지도록 설계해야 한다.

피드백이 얼마나 필요한가?

  • 테스트를 얼마나 작성해야 하나?
  • 만약 어떤 구현에 대한 지식이 신뢰할 만 하다면 그에 대한 테스트는 작성하지 않아도 된다.

테스트를 지워야 할때는 언제인가?

  • 테스트를 삭제할 경우 자신감이 줄어드는 것 같으면 절대 테스트를 지우지 말아야한다.
  • 두 개의 테스트가 코드의 동일한 부분을 실행하더라도, 이 둘이 서로 다른 시나리오를 말한다면 그대로 남겨두어야한다.

거대한 시스템을 개발할 때에도 TDD를 할 수 있는가?

  • 시스템에 있는 기능의 양은 TDD의 효율에 영향을 미치지 않는다.

프로젝트 중반에 TDD 도입하려면 어떻게 해야할까?

  • 변경의 범위를 제한하고, 시스템에서 극적으로 단순화될 수 있지만 지금 당장 변할 필요가 없는 부분을 봤다면 그냥 그대로 놔두자.
  • 테스트와 리팩토링 사이에 존재하는 교착상태를 푼다.
  • TDD는 더 깔금한 설계를 할 수 있도록, 그리고 더 많은 것을 배워감에 따라 설계를 더 개선할 수 있도록, 적절한 때 적절한 문제에 집중할 수 있게끔 도와준다.
  • TDD는 훨씬더 적은 수의 결함과 훨씬 더 깨끗한 설계의 코드를 작성하게 해준다.
  • TDD는 시간이 지남에 따라 코드에 대한 자신감을 점점 더 쌓아가 수 있게 해준다.
  • 테스트가 쌓여감에 따라 시스템의 행위에 대한 자신감을 더 많이 얻게 된다.
  • 설계를 개선해 나감에 따라 점점 더 많은 설계 변경이 가능해진다.

TDD는 초기조건에 민감한가?

  • 저자는 완벽하게 사리에 맞는 설계 아이디어가 결국은 틀린 것으로 판명날때가 종종 있는데, 그냥 시스템이 무슨일을할지 생각하고 나중에 설계가 알아서 정해지도록하는 것이 더 낫다는 판단을하였다.
  • TDD는 스트레스를 훨씬 적게 받게 만든다.
  • 모든 것에 대해 한꺼번에 걱정할 필요가 없고 지금 직면한 테스트 하나를 실행하게끔만 하면된다. 그뒤에 나머지를 전부 실행하면된다.
  • TDD는 미지에 대한 두려움을 지루함으로 바꿔주는 효과가 있다.
  • TDD는 설계 결정에 대한 피드백 고리를 단축시킨다
  • 설계결정에 대한 피드백 루프의 길이는 설계에 대한 생각(API가 이런식으로 생기면 좋겠다)와 그에 대한 첫번째 예제(그 생각을 담고 있는 테스트 작성) 사이의 간격이다.
  • 설계 아이디어를 몇 분사이에 그럴듯한 인터페이스로 전환하기만 함녀 피드백을 받을 수 있게 된다.
  • TDD는 테스트 기술이 아니라 분석기술이며, 설계기술이다. 또한 개발의 모든 활동을 구조화 하는 기술이다.
  • 단순 설계: 테스트를 통과하기 위해 필요한 만큼만 코딩하고 모든 중복을 제거한다면, 자동으로 현재까지의 요구사항에 딱 들어 맞는 설계를 얻게 될 것이고, 이 설계는 앞으로의 어떠한 사용자 스토리에 대해서도 동등하게 준비된 상태일 것이다.
  • 리팩토링: 테스트가 있다면 큰 리팩토링을 수행하더라도 시스템의 행위가 변하지 않았다는 자신감을 얻을 수 있게 된다. 자신감이 커질수록 더 큰 리팩토링을 공격적으로 수행할 수 있게 되고, 결과적으로 시스템 수명이 더 길어질 것이다. 또한 리팩토링을 통해 다음 단계의 테스트를 더 쉽게 작성할 수 있다.
  • CI/CD : 테스트는 좀더 자주 통합할 수 있게 해준다. 그리고 고객을 혼란 시키지 않으면서도 훨씬 더 자주 코드를 출시할 수 있게 된다.

다락의 주장

  • GUI 에 대한 자동화 테스트는 만들 수 없다 ex) JSP, Servlet
  • 분산객체에 대한 자동화 테스트는 만들 수 없다.
  • 데이터 베이스 스키마를 개발할 땐 TDD를 적용할 수 없다. ex) JDBC
  • 외부도구가 생성한 코드나 서드파티 코드를 테스트할 필요는 없다.
  • 언어 컴파일러나 인터프리터등은 TDD로 만들어 낼수 없다.

TDD 수련법

  • 간단하고 쉬운문제들을 TDD로 시도한다.
  • 초록막대 주기는 가능하면 짧도록 한다.
  • 초록 막대 주기의 최대시간을 정해놓고 진행하다가 시간을 초과하면 직전 초록막대 상태로 돌린다음 새로 시작한다.
  • 진짜로 만들기 전까지만 가짜로 구현하기를 적극적으로 사용하려고 노력한다.
    • 가짜로 구현하기는 초록 막대 주기가 짧아지는 가장 간단하고 빠른 방법이다.
  • 같은 문제를 여러번 풀어본다.
  • 초기에는 리팩토링 툴을 사용하지 않는 것이 좋다. 순서와 과정을 성큼 건너뛰게 되기 때문
  • 여유를 가지고 겸손하게 속도를 조절하며 자신이 하는것을 관찰하고 기
profile
백엔드 개발자

0개의 댓글