[우아한테크코스] 2월 16일 TIL

2 minute read

오늘 배운 것

  1. 학습로그 남기기
  2. TDD 강의

학습로그

  • 메타인지란?
    인지: 문제를 해결하기
    메타인지: 문제를 해결하는 것에 대해 생각하기
    메타인지를 통해 ‘나 자신을 제대로 알면 학습에 도움이 된다.’
    • 모니터링: 내가 무엇을 아는지 판단하는 단계
    • 컨트롤: 모니터링을 바탕으로 앞으로 어떻게 할지 결정하고 방향을 설정하는 단계
      학습 기록을 남기고 학습 로드맵 그려보기
      주제, 세부 주제(로드맵의 하나), 내용, 링크, 가중치 => pr의 코멘트에 남기
      백엔드 로드맵

TDD 강의

  • production code, test code
  • TDD란?
    Test First Development + 리팩토링: 기능은 유지하면서 설계만 개선해나가는 과정, 리팩터링을 기능을 구현하는 사이 사이에 반드시 하자.
    요구사항 분석과 설계가 갖추어져 있어야 TDD가 진행된다. TDD는 분석 기술이자, 설계 기술이다.
    TDD는 디버깅 시간을 줄여주고, 문서가 되어주고, 변화가 두렵지 않아진다.
  • TDD의 사이클
    실패하는 테스트 구현 -> 테스트가 통과하도록 코드 구현 -> 리팩토링
  • TDD의 원칙
    실패하는 단위테스트를 작성할 때까지 프로덕션 코드를 작성하지 않는다.
    컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트 작성
    실패하는 테스트를 통과할 정도로만 실제 코드 작성
  • TDD를 사용하는 이유
    한 번에 하나의 테스트를 작성하고, 통과하는데 집중함으로써 점진적으로 설계를 개선해나갈 수 있다.
    처음에 설계를 완전하게 구현하지 않아도 리팩토링을 거쳐나가면서 더욱 발전할 수 있고, 그게 낫다.
    변화에 빠르게 대응할 수 있는 소스 코드와 연습
    테스트 기반으로 학습하면 빠른 피드백을 받을 수 있고, 빠른 피드백은 삽질을 더 많이 하고 버그를 더 빨리 찾게 한다.
  • 리팩터링은 추가, 삭제, 변경이 없어야 한다. testcase에 대한 변경이 없도록 한다.

TDD로 자동차 경주게임 구현

  • 요구사항 분석 및 설계
    요구사항 분석을 통해 대략적인 설계 -> 객체 추출
    의존관계를 가지지 않는 핵심 도메인 영역을 집중 설계: 우선적으로 도메인 테스트 연습
  • 테스트 가능한 부분을 찾아 단위테스트
    ex) 이름 split, 자동차 이동, 이동에 따라 ‘-‘ 생성
  • 테스트하기 어려운 부분을 찾아 가능한 구조로 개선
    다른 객체와 의존관계를 가지지 않는 마지막 노드를 먼저 찾아서 테스트 가능한지 확인
    테스트하기 어려운 코드의 의존 관계를 상위 객체로 의존하도록 바꾸면 테스트할 수 있어진다. (ex: Random 객체를 main에 의존하도록 이동)
    이게 무조건적으로 좋은게 아닐 수도 있다.
  • 대표적으로 테스트하기 어려운 코드
    내부 API(Random, shuffle, 날짜 …), 외부 REST API, DB API

TDD 라이브 코딩 by 포비

  • test 코드 구현
  • 완전히 빨간불 없애고 통과할 때까지 프로덕션 코드 구현
  • 리팩토링하고 테스트 실행
  • test에서도 중복되는 부분은 field 빼도록 (ex Car car 필드 분리, 생성자 @BeforeEach 사용)
  • todo list를 계속 추가하면서 관리하도록
  • 설계를 개선하는 용도로도 TDD를 적용할 수 있다
  • 내가 구현하고자 하는 기능을 온전히 구현하고 컴파일 되었을 때 commit
  • cmd+l: 라인이동

로또 미션 시작

  • package by layer, package by feature
    • package by layer는 평소 내가 하던 것처럼 domain, view, controller로 패키지를 나누고 구현하는 방식
      관련이 없는 항목들을 묶어서 낮은 응집력과 낮은 모듈성을 가지고 있지만 패키지 간의 결합도는 크다.
    • package by feature는 예를들어 car라면 car 안에 carDto, carDao, car, carController, carClient 이런 식으로 넣는 방식
      기능별로 패키지를 묶어 단일 기능 자체를 하나의 패키지에 두어 높은 응집력, 높은 모듈성을 지니게 되고 패키지간 결합도가 최소화된다.
      추가적으로 특정 기능과 관련된 모든 항목을 패키지 내부에 두어 관리하므로써 다른 패키지를 절대 사용하지 못한다는 의미가 아니라 package-private을 선호하지만 경우에 따라서는 공용이 될 수도 있다.

    나는 단 한번도 내가 짜는 구조에 의심을 안하고 있었다는거를 반성해야겠다.
    프로젝트의 구조가 커지면 커질수록 feature 방식의 구현이 가독성도 뛰어나고 응집력, 모듈성, 추상화 등에 용이하다는걸 생각조차 하지 않았다니..
    무언가를 할 때 너무 당연스레 받아들이지 말고 왜?에 대해서 계속 고민하자

  • isBetween assertThat(~~).isBetween(1, 45)로 당연스레 사용하고 있었는데 페어는 isGreaterThanOrEqualTo(1), isLessThanOrEqualTo(45)를 사용하고 있었다.
    물론 이 방식이 더 낫다고 할 수는 없지만 정작 assertThat에 대해서 공부했을 때 띄엄띄엄한 것이니 .하면 나오는 메서드들 틈틈히 잘 봐두고 잘 사용해보자
  • @repeatedTest
    test 어노테이션을 @RepeatedTest(100)으로 주면 100번 테스트를 검사해보는 것이다.
  • 인터페이스 () 람다
    public interface NumbersGenerator {
      List<Integer> generate();
    }
    
    NumberGenerator numberGenerator = () -> Arrays.asList(1, 2, 3);
    

    이와 같은 구현과 사용도 가능하다!!
    람다 공부하기