본문 바로가기
Study/우아한 테크 캠프 Pro

우아한 테크 캠프 PRO, 5주차 인수 테스트 기반 TDD

반응형

우아한 테크 캠프 PRO, 5주차 과제 - 인수 테스트 기반 TDD


TDD 테스트 방법 #1, OUTSIDE IN

  • 시스템 외부(에서 오는 요청)에 대한 첫 번째 테스트를 작성하여 개발을 시작
  • 테스트를 통해 구현하고자 하는 객체의 인터페이스를 먼저 만들고 협력 객체(모의 객체, ex. mocks, stubs)를 통해 개발을 이어감
    • 실제 Java interface가 아니더라도 빈 클래스 껍데기만 만들어가면서 진행해도 무방할 것 같다.
  • 협력 객체에 대해서는 예측하여 테스트 할 대상과 협력객체들 사이의 상호작용을 고려하여 진행
  • 테스트가 성공하면, Mock 객체에 대해 명세를 시작하고 이는 다음 테스트의 시작점이 된다.
  • 단계적으로 한번에 하나의 레이어를 대상으로 진행한다.



TDD 테스트 방법 #2, INSIDE OUT

  • 구현하기 전 다른 도메인과의 관계를 미리 고려하지 않고 개발 시작
  • 작성이 끝나면 해당 객체와 관계를 가지는 객체나 모듈을 작성
  • 즉, 내부를 먼저 구현한 후 외부에서 참조하는 것들을 구현하는 순서로 진행
    • 도메인에 대한 지식이 필요하다.



어떤 방식을 선택 해야 할까?

  • 정답은 없다.
  • OUTSIDE IN 방식은 자세한 도메인 지식이 없이 평소에 하는 프로덕션 코드 개발 순서(Controller -> Service -> Repository)

    처럼 테스트를 검증해나갈 수 있다는 장점이 있지만, 어느정도 테스트 코드가 완성된 후에 보면... 큰 의미가 없는 테스트들이 생기는 것 같다.
    • 비즈니스 로직은 Domain 레벨로 들어가있고, 인수 테스트가 End-To-End를 테스트해주는 상황이라면..

      Controller단 테스트(Slicing)와, Service단 테스트(Slicing)가 역할이 애매해보인다. (물론 상황에 따라 다르다)
  • INSIDE OUT 방식은 도메인에 담긴 비즈니스 로직부터 검증하기 때문에 의미없는 테스트 코드(불필요한 Mock, Stub)를 작성하지 않는다.
    • 하지만 처음부터 도메인 지식을 파악해 테스트 상황을 작성해야하기 때문에 이에 따른 시간이 소요될 것으로 보인다.



Next-Step 추천 방식!!!

  • OUTSIDE-IN 방식으로 방향성을 잡고, 도메인 지식을 쌓다가 일정 수준 쌓이게 되면 불필요한 부분을 지우고 INSIDE OUT 방식으로 변경
  • 미션으로 진행해본 결과, 개발 시간이 많이 걸린 듯 하지만(TDD가 숙달이 안되서 그런 것 같다..) 결국에는 깔끔한 테스트 코드가 나오게 된다.



단위 테스트

  • 단위 테스트는 통합하고 있는 부분이 정상적으로 동작한다고 가정하고 단일 기능에 대해서만 검증
  • 테스트 더블
    • 테스팅을 목적으로 진짜 객체대신 사용되는 모든 종류의 위장 객체
    • Stub, Mock
  • TDD를 연습할 때 가급적이면 실제 객체를 활용하는 것을 우선으로 진행
  • 테스트 작성이 어렵거나 흐름이 잘 이어지지 않는다면 테스트 더블을 활용하는 방법으로 접근



Stub vs Mock

  • Stub
    • 인스턴스화하여 구현한 가짜 객체(기능 구현 X)를 이용해 실제로 동작하는 것처럼 보이게 만드는 객체입니다
    • 해당 인터페이스 or 클래스를 최소한으로 구현합니다
    • 테스트에서 호출된 요청에 대해 미리 준비해둔 결과를 전달합니다
    • 메소드가 수행된 후, 객체의 상태를 확인하여 올바르게 동작했는지를 확인하는 검증법입니다
  • Mock
    • 호출에 대한 기대를 명세하고, 내용에 따라 동작하도록 프로그래밍 된 객체입니다
    • 테스트 작성을 위한 환경 구축이 어려울 때, 테스트하고자 하는 코드와 엮인 객체들을 대신하기 위해 만들어진 객체입니다
    • 메소드의 리턴 값으로 판단할 수 없는 경우 특정 동작을 수행하는지확인하는 검증법입니다



Session vs Token 기반 인증

  • 기본적으로 둘 다 HTTP 프로토콜의 비연결성, 무상태성을 보완하기 위해 나온 기술
  • Session 기반 인증
    • 인증 정보를 서버에서 저장하고 관리한다.
    • 인증된 정보를 세션 저장소에 저장한 후 매 요청시 마다 조회하여 검증
    • 클라이언트가 들고 있는 세션ID 자체는 아무 정보를 얻을 수 없기 때문에 쿠키 방식보다는 안전하다
    • 서버에서 세션 저장소를 사용하므로 요청이 많아지면 서버에 부하가 생김
  • Token 기반 인증 ( ex, JWT 방식 )
    • JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화 시킨 토큰을 의미
    • Header, Payload, Signature 3개의 구조를 '.' 구분자로 이어져 있는 문자열
      • Header: 암호화할 해싱 알고리즘, 토큰 타입
      • Payload: 토큰에 담을 정보들, 인증을 위한 중심 데이터(key-value)
      • Signature: JWT 보안의 핵심
        • 인코딩된 Header와 Payload를 더한 뒤 비밀키로 해싱하여 생성
        • Header와 Payload는 단순히 인코딩된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만,

          Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다
    • 인증 정보에 대한 별도의 저장소가 필요없습니다, 즉 요청이 많아도 서버에 부하가 생기지 않는다.
      • 단.. JWT가 길어짐에 따라 네트워크 부하가 생길 수도 있다.
    • 토큰 기반으로 다른 로그인 시스템에 접근 및 권한 공유가 가능합니다.
    • 특정 사용자의 접속을 강제로 만료시키기 어렵다.. 토큰이 탈취 당하면 후속 처리가 어려워진다.
      • 짧은 만료 시간 설정 -> 잦은 로그인을 불러옴
      • RefreshToken 추가 발급 전략
        • 기존의 AccessToken뿐 만 아니라 더 긴 만료시간을 갖는 서버 저장용 RefreshToken을 추가적으로 발급
        • AccessToken 만료시 RefeshToken을 사용하여 AcessToken 갱신 혹은 재로그인 판단
        • RefreshToken은 관리가 용이, 하지만 클라이언트, 서버 둘다 별도의 보안 Storage에 보관해야하므로 JWT의 이점을 약간..애매



미션 회고

  • 왜 불편함(?)을 감수하면서까지 TDD를 할까?
    • 단계별로 구현해야하는 테스트 코드도 많고... 익숙하지 않은 팀들은 오히려 시간만 늦춘다고 생각한다. (경험...ㅠ)
    • 그럼에도 TDD를 적용했을 때의 이점은 아래와 같이 엄청나다!!!
      • 오버 엔지니어링 방지 => TDD는 테스트를 만들고, 테스트가 통과할만큼만!! 프로덕션 코드를 만들기 때문에 오버 엔지니어링을 미연에 방지한다.
      • 테스트 불가능한 코드를 원천 방지 => 테스트부터 만들기 때문에 테스트가 불가능한 코드(UI, Time 이런것들빼고..)가 나올 수가 없다.
      • 리팩토링에 자신감을 부여 => 커버리지를 높게 관리하면 후에 있을 리팩토링에 테스트가 잡아준다는 자신감이 생긴다.
      • 의미있는 테스트 케이스들은 그 자체로 문서가 된다



피드백

  • 변경 단위(기능, 요구사항)별로 커밋을 남기면 동료들이 리뷰하기 편하다.
  • 각 Policy의 관심사들을 나누자!
    new Fare().acceptPolicy(new FareOfDistancePolicy(this.totalDistance))
              .acceptPolicy(new AdditionalFareOfLinePolicy(this.maxAdditionalFare))
              .acceptPolicy(new FareOfAgePolicy(loginMember.getAge()));
    • Fare 객체는 각 Policy들의 적용만 담당, 각 Policy가 어떻게 계산되는지 알 필요가 없다.
    • 각 Policy들은 공통 인터페이스를 구현하며 필요한 정보들을 들고 들어가 계산한다.
728x90
반응형