야근에 시달리다 드디어...!
요약
-
테스트 스타일
-
출력 기반 테스트 :
- sut에 입력을 주고 출력을 확인하는 테스트 스타일
- 숨은 입출력이 없다고 가정한다.
- 출력 기반 테스트가 테스트 품질이 가장 좋다.
-
상태 기반 테스트 :
- 작업이 완료된 후의 시스템 상태를 확인한다.
- 상태 기반 테스트는 안정성을 위해 더 신중해야 한다. 단위 테스트를 하려면 비공개 상태를 노출하지 않도록 해야 한다.
-
통신 기반 테스트
- 목을 사용해 테스트 대상 시스템과 협력자 간의 통신을 검증한다.
- 통신 기반 테스트도 안전성을 위해 더 신중해야 한다.
- 애플리케이션 경계를 넘어서 외부 환경에 사이드 이펙트가 보이는 통신만 확인해야 한다.
-
-
수학적 함수는 숨은 입출력이 없는 함수(또는 메서드)다.
- 사이드이펙트와 예외가 숨은 입출력에 해당한다.
- 내부 상태 또는 외부 상태에 대한 참조는 숨은 입출력인다.
- 수학적 함수는 명시적이므로 테스트 용이성을 상당히 높인다.
- 함수형 프로그래밍의 목표는 비즈니스 로직과 사이드 이펙트를 분리하는 것이다.
- 함수형 아키텍처는 모든 코드를 함수형 코어와 가변 셀이라는 두 가지 범주로 나눈다. 가변 셀은 입력 데이터를 함수형 코어에 공급하고, 코어가 내린 결정을 사이드 이펙트로 변환한다.
-
함수형 아키텍처와 헥사고날 아키텍처의 차이는 사이드 이펙트의 처리에 있다.
- 함수형 아키텍처는 모든 사이드 이펙트를 도메인 계층 밖으로 밀어낸다.
- 헥사고날 아키텍처는 도메인 계층에만 한정돼 있는 한은 도메인 계층에 의해 만들어진 사이드 이펙트도 괜찮다.
- 아키텍처 사이의 선택은 성능과 코드 유지 보수성 사이의 절충이며, 함수형 아키텍처는 유지 보수성 향상을 위해 성능을 희생한다.
-
모든 코드 베이스를 함수형 아키텍처로 전환할 수 없다.
- 함수형 아키텍처를 전략적으로 사용할 것
- 시스템의 복잡도와 중요성을 고려
- 코드 베이스가 단순하거나 그렇게 중요하지 않으면, 함수형 아키텍처에 필요한 초기 투자는 별 효과가 없다.
단위 테스트의 스타일
-
세 가지 스타일
-
출력 기반 테스트 :
- 테스트 대상 시스템(SUT)에 입력을 넣고 생성되는 출력을 점검하는 방식
- 전역 상태나 내부 상태를 변경하지 않는 코드에만 적용 -> 반환 값만 검증하면 된다.
- 이러한 테스트는 사이드 이펙트가 없다.
- 출력 기반의 테스트 스타일은 함수형이라고도 한다.
-
상태 기반 테스트
- 작업이 완료된 후 시스템 상태를 확인하는 것
- 상태 : SUT, 협력자 또는 데이터베이스, 파일 시스템 등과 같은 프로세스 외부 의존성의 상태 등을 의미
-
통신 기반 테스트
- 목을 사용해 테스트 대상 시스템과 협력자 간의 통신을 검증
-
-
세 가지 스타일 비교
-
회귀 방지, 피드백 속도 기준
-
회귀 방지 지표와 피드백 속도는 테스트 스타일에 크게 영향을 받지 않는다.
-
회귀 방지 지표는 다음 세 가지 특성을로 결정된다.
- 테스트 중에 실행되는 코드의 양
- 코드의 복잡도
- 도메인 유의성
- 피드백 속도는 테스트가 프로세스 외부 의존성과 떨어져 단위 테스트 영역에 있는 한, 모든 스타일의 테스트 실행 속도는 거의 동일하다.
-
-
-
리팩터링 내성 기준
- 리팩터링 내성 : 리팩터링 중에 발생하는 거짓 양성 수에 대한 척도 -> 거짓 양성은 식별할 수 있는 동작이 아니라 코드의 구현 세부 사항에 결합된 테스트의 결과
-
출력 기반 테스트
- 테스트가 테스트 대상 메서드에만 결합 -> 거짓 양성 방지가 가장 우수
-
상태 기반 테스트
- 일반적으로 거짓 양성이 되기 쉽다.
- 테스트와 제품 코드 간의 결합도가 클수록 유출되는 구현 세부 사항에 테스트가 얽메일 가능성이 커진다.
-
통신 기반 테스트
- 허위 경보에 가장 취약
- 테스트 대역으로 상호 작용을 확인하는 테스트는 대부분 깨지기 쉽다. -> 이러한 상호 작용을 확인해서는 안된다.
-
유지 보수성 기준
- 단위 테스트의 유지비를 측정
-
기준
- 테스트를 이해하기 얼마나 어려운가? (테스트 크기에 대한 함수)
- 테스트를 실행하기 얼마나 어려운가? (테스트에 직접적으로 관련 있는 프로세스 외부 의존성 개수에 대한 함수)
-
출력 기반 테스트
- 전역 상태나 내부 상태를 변경하는 일이 없음 -> 프로세스 외부 의존성을 다루지 않음
- 굿
-
상태 기반 테스트
- 출력 기반 테스트보다 어려움. -> 종종 상태 검증이 더 많은 공간을 차지하기 때문 (더 많은 데이터를 확인해야 한다.)
- 헬퍼 메서드를 사용할 수 있지만 이를 유지하는 데 상단한 노력이 필요함.
-
검증하고자 하는 값을 객체로 만들 수 있으나 값 객체인 경우에만 효과적. 그렇지 않을 경우 코드가 오염될 수 있다.
- 코드 오염 : 단지 단위 테스트를 가능하게 하거나 단순화하기 위한 목적만으로 제품 코드베이스를 오염시키는 것
-
통신 기반 테스트
- 테스트 대역과 상호 작용을 검증
- 목이 사슬 형태로 있을 때 테스트가 커지고 유지보수하기 어려워 진다.
-
결론
- 항상 출력 기반 테스트를 선호하는 것이 좋지만 실행하기 어려움.
- 코드를 순수 함수로 만들면 상태 기반 테스트와 통신 기반 테스트 대신 출력 기반 테스트가 가능해진다.
-
함수형 아키텍처
-
함수형 프로그래밍이란?
- 수학적 함수를 사용한 프로그래밍
-
숨은 입출력이 없는 함수
-
숨은 입출력의 유형
- 사이드 이펙트 : 메서드 시그니처에 표시되지 않은 출력
-
예외 :
- 메서드가 예외를 던지면 프로그램 흐름에 메서드 시그니처에 설정된 계약을 우회하는 경로를 만든다.
- 호출된 예외는 호출 스택의 어느 곳에서도 발생할 수 있음 -> 메서드 시그니처가 전달하지 않는 출력을 추가
-
내외부 상태에 대한 참조
- 정적 속성을 사용해 데이터에 접근하는 메서드 사용 등
-
- 수학적 함수는 호출 횟수에 상관없이 주어진 입력에 대해 동일한 출력을 생성한다.
-
참조 투명성 : 메소드 호출을 해당 값으로 바꾸는 것
- 숨은 입출력이 있을 경우 참조 투명성을 보장할 수 없다.
- 함수의 반환 값으로 대체할 수 없다.
const increment = (x:number) => x+1
const y = increment(4)
const y = 5
- 어떤 사이드 이펙트도 일으키지 않는 애플리케이션은 만들 수 없다. 비현실적
- 함수형 프로그래밍의 목표는 사이드 이펙트를 완전히 제거하는 것이 아니라 비즈니스 로직을 처리하는 코드와 사이드 이펙트를 일으키는 코드를 분리하는 것
-
비즈니스 로직과 사이드 이펙트 분리
- 결정을 내리는 코드 (함수형 코어, 불변 코어) : 사이드 이펙트가 필요없다. -> 수학적 함수를 사용해 작성할 수 있다.
- 해당 결정에 따라 작용하는 코드 (가변 셸) : 수학적 함수에 의해 이뤄진 모든 결정을 데이터베이스의 변경이나 메시지 버스로 전송된 메시지와 같이 가시적인 부분으로 변환한다.
-
함수형 아키텍처
- 사이드 이펙트를 다루는 코드를 최소화하면서 순수 함수 방식으로 작성한 코드의 양을 극대화한다.
- 함수형 아키텍처에서 함수형 코어는 수학적 함수를 사용해 구현. 모든 결정을 내린다.
- 가변 셸은 함수형 코어에 입력 데이터를 제공하고 데이터베이스와 같은 프로세스 외부 의존성에 사이드 이펙트를 적용해 그 결정을 해석한다.
-
함수형 코어와 가변 셸의 협력 방식
- 가변 셸은 모든 입력을 수집한다.
- 함수형 코어는 결정을 생성한다.
- 셸은 결정을 사이드 이펙트로 변환한다.
- 두 계층의 분리 -> 가변 셸이 의사 결정을 추가하지 않게끔 결정을 나타내는 클래스에 정보가 충분히 있는지 확인해야 한다.
-
캡슐화와 불변셩
- 캡술화와 같이 함수형 아키텍처와 불변성은 단위 테스트와 같은 목표가 있음. -> 소프트웨어 프로젝트의 지속적인 성장을 가능하게 하는 것.
- 캡슐화는 코드에 모순이 생기지 않도록 보호하는 행위
- 불변 클래스를 사용하면 변경할 수 없는 것을 처음부터 변형할 수 없으므로 상태 변질에 대해 걱정할 필요가 없다.
- 모든 데이터가 불변일 때 캡슐화가 되지 않아 생긴 문제는 모두 간단히 사라진다.
- 객체지향 프로그래밍은 작동 부분을 캡슐와해 코드를 이해할 수 있게 한다. 함수형 프로그래밍은 작동 부분을 최소화해 코드를 이해할 수 있게 한다.
함수형 아키텍처와 육각형 아키텍처 비교
-
공통점
-
둘 다 관심사 분리라는 아이디어를 기반으로 함.
- 육각형 아키텍처의 도메인 계층과 애플리케이션 서비스 계층, 함수형 아키텍처의 결정과 실행 분리
-
의존성 간의 단방향 흐름
- 육각형 아키텍처 - 도메인 계층 내 클래스는 서로에게만 의존해야 함. 도메인 객체가 애플리케이션 서비스 계층의 클래스에 의존해서는 안된다.
- 함수형 아키텍처 - 불변 코어는 가변 셸에 의존하지 않는다.
-
-
차이점
-
사이드 이펙트에 대한 처리
- 육각형 아키텍처 - 도메인 계층에 제한하는 한 도메인 계층으로 인한 사이드 이펙트도 괜찮다고 생각 -> 모든 수정 사항은 도메인 계층 내에 있어야 하며 계층의 경계를 넘어서는 안 된다.
- 함수형 아키텍처 - 모든 사이드 이펙트를 불변 코어에서 비즈니스 연산 가장자리로 밀어낸다.
-
함수형 아키텍처와 출력 기반 테스트로의 전환
-
리팩터링 단계
- 프로세스 외부 의존성에서 목으로 변경
- 목에서 함수형 아키텍처로 변경
함수형 아키텍처의 단점
- 코드베이스가 커지고 성능에 영향을 미칠 수 있음 -> 유지보수서의 이점이 상쇄
-
함수형 아키텍처의 적용 가능성
- 실행 흐름이 간단하지 않을 경우 적용이 어려울 수 있음. -> 숨은 입출력이 반드시 생길 수 밖에 없는 실행 흐름이 필요할 수 있다.
-
성능 단점
- 함수형 아키텍처와 전통적인 아키텍처 사이의 선택은 성능과 코드 유지 보수성 간의 절충.
- 성능 영향이 그다지 눈에 띄지 않은 일부 시스템에서는 함수형 아키텍처를 사용해 유지 보수성을 향상시키는 편이 낫다.
- 두루 적용되는 해결책은 없다.
-
코드 베이스 증가
- 함수형 아키텍처는 함수형 코어와 가변 셸 사이를 명확하게 분리해야 한다. -> 초기에 코딩이 더 필요
- 복잡도가 낮다면 굳이 함수형 아키텍척를 적용할 필요 없다.
- 항상 시스템의 복잡도와 중요성을 고려해 함수형 아키텍처를 전략적으로 사용해야 한다.
- 함수형 방식에서 순수성에 많은 비용이 든다면 순수성을 따르지 말라.