본문으로 바로가기

TIL 2022-03-13 클린코드 6~9

category TIL 2022. 3. 13. 18:14

6.

 

책에서 말하는

 

객체 : 추상화 뒤로 자료를 숨기고 함수만 공개

자료구죠 : 자료를 그대로 공개하고 다른 함수 제공 x

 

절차적인 코드는 새 함수 추가 하기 쉬움

객체 지향 코든느 기존 함수 변경하지 않으면 새 클래스 추가 쉬움 (그대로 implement해버림)

 

반대로 절차 코드는 새로운 클래스를 추가하면 함수를 다 바꿔야하고

객체 지향 코드는 새로운 함수 추가하면 클래스들을 다 고쳐야함.

 

상호보완적!

 

디미터 법칙 : 모듈은 자신이 조작하는 객체 속사정 몰라야함 (조회함수로 내부 구조 공개하면 안돼요!)

 

클래스 C의 메서드 f는

클래스 C

f가 생성한 객체

f 인수로 넘어온 객체

C인스턴스 변수로 저장된 객체

만 접근해야한다고 주장함.

 

근데 이러면 허용된 객체가 반환하는 객체의 메서드는 호출하면 안됨.

ctx가 허용된 객체고 이 객체가 getText()라는 메서드를 가지면

ctx.getText()가 반환하는 객체의 메서드는 호출하면 안된다는 것이다!!

 

만약 이 코드가 객체면 디미터 법칙을 위반하는것이고 자료구조면 위반하는 것이 아니다.

 

만약 자료구조라면 조회함수를 쓰지 않고 ctx.text.~~~ 로 접근할 수 있게 했으면 굳이 디미터 법칙 따질 필요가 없었을 것이다.

 

자료구조는 공개변수만 포함하고 객체는 비공개 변수와 공개 함수를 포함하면 될텐데 이를 허용하지 않는 프레임워크와 표준이 있다.

 

7.

 

오류코드보다 예외!

 

오류 코드나 플래그를 반환하는 방식을 사용하면 호출한 즉시 확인하기 때문에 뎁스가 깊어지고 복잡해짐

 

예외를 던지면 훨씬 깔끔.

가독성이 늘어날뿐만 아니라 오류 처리, 실제 동작을 분리함으로써 품질도 업!

 

언체크 예외(Unchecked Exception) 사용!

Checked Exception, 책에서는 확인된 예외라고 하는데, 이는 컴파일 시점에서 Exception 발생이 확인되는 Exception이다. 반대로 언체크 예외는 런타임에서 일어나는 예외들.

확인된 예외는 만약 메서드에서 예외가 발생했는데 catch 블록에 세 단계 위에 있으면 그 사이 모든 메서드가 이 예외를 정의해야함.

또한 모든 함수가 최하위 함수가 던지는 예외를 알아야하니 캡슐화를 꺤다는 문제도 있었음.

 

예외에 의미를

예외 던질떄는 전후 상황을 붙여 오류 원인과 위치를 찾기 쉽게 하자.

 

호출자 고려한 예외클래스!

외부 라이브러리를 호출하는 try catch 문을 포함한 코드를 작성하면 외부 라이브러리가 던질 예외를 모두 잡아버림!!

이를 Wrapper로 감싸고 이에 대한 예외를 반환하도록 하면 코드가 깔끔해짐.

부가이득

외부 라이브러리와의 의존성 감소

Wrapper 클래스에서 테스트 코드를 넣어주는 방법으로 외부 API 호출하는거보다 테스트도 쉬워짐

 

정상 흐름 고려

 

식비를 청구하면 그 식비 값을, 청구하지 않았으면 기본 식비를 더하는 코드를 예외처리로 작성하면 논리 따라가기가 어려움.

특수 처리를 하기보다 청구 식비가 없다면 기본식비를 반환하는 객체를 새로 생성하기. (근데 이건 그냥 if문으로 구분하는게 낫지 않나 싶은데 캡슐화라는 명목에서 유의미한듯)

 

이렇게 특수 사례를 객체나 클래스를 통해 처리하는 방법을 Special Case Pattern이라고 함. 클래스가 예외적 상황을 캡슐화하기 때문에 클라이언트 코드가 이런 상황을 생각할 필요가 없다.

 

null 반환 NO

null check를 통한 관리는 누락하기 너무 쉽다. 예외를 던지거나 특수 사례 객체를 반환하는 것이 좋다. 외부 라이브러리가 null 반환하면 이를 감싸자.

 

null 전달 NO

메서드에서 null 반환하는 것도 나쁘지만 null을 전달받는 것은 더 나쁨!

 

타입스크립트가 괜히 Strict Null Checking을 recommend에 넣어놓는것이 아닌듯

 

8.

 

외부 코드

프레임 워크, 패키지 제공자는 최대한 적용성을 높이려고 하고 사용자는 자신의 요구에 맞는 것에 집중한 인터페이스가 되길 원함. 이러한 괴리감 때문에 시스템 경계에 문제가 생길 소지가 많다.

 

그러니 객체 유형을 제너릭이나, 객체 유형을 지정한 별도의 클래스로 래핑하는 등의 행위를 통해 필요한 인터페이스만 제공하도록 할 수 있다.

 

캡슐화하란 의미가 아니라 경계 인터페이스(이곳 저곳에 쓸 수 있는 인터페이스?)를 쉽게 넘기지 말라는 의미.

 

경계 살피고 익히기

외부 코드는 빠르게 개발 가능하지만 이 외부 코드를 사용하는 사용자가 가져오자마자 완벽히 사용법을 익힌다고는 할 수 없음. 예상대로 외부 라이브러리가 작동하는지도 애매함. 코드가 잘못된건지 라이브러리 버그인지....

 

이는 사용자가 프로그램에 외부 코드를 사용하려는 방식으로 '학습 테스트'라는 테스트 케이스를 작성해 이에 맞게 외부API를 호출한다. 이런 방식으로 제대로 api를 사용하는지 확인할 수 있음.

 

학습 테스트를 통해 패키지가 예상대로 도는지 검증하고 이후에 패키지의 코드가 변경되고 업데이트되어도 학습 테스트를 통해 바로 확인할 수 있음.

 

아직 존재 안하는 코드 사용

다른 팀의 API 설계가 아직 안됐을때, 우리가 이 코드가 필요하다면 일단 이를 미루고 그 API에 원하는 기능에 대한 인터페이스를 정의한다. 이후에 API가 정의되고 설계되면 인터페이스와 간격을 메꾸고 캡슐화해 사용함.

 

이렇게 하면 테스트도 편하고 실 api가 나온 이후 API를 올바르게 사용하는지 테스트도 가능.

 

9.

TDD 법칙 3개

실패하는 단위 테스트 작성할떄까지 실코드작성 x

컴파일 실패 안한느데 실행 실패하는 정도로 단위테스트

실패하는 테스트 통과하는 정도로 실 코드 작성

 

테스트 => 유연성 유지보수성 재사용성

테스트 케이스 있으면 변경해도 버그가 있을가 두렵지 않음!

자동화된 단위테스트는 설계와 아키텍쳐를 깨끗하게 보존하는 키다.

 

깨끗한 테스트코드

무엇보다 가독성이 중요함.

 

테스트당 assert 하나

assert문이 하나면 결론이 하나라는 뜻이기에 코드 이해 편함.

다만 assert를 꼭 하나로 제한하면 중복되는 코드가 많아지고 이를 별도의 패턴으로 분리하여 중복을 제한할 수 있지만 이것이 오히려 코스트를 높이는 것일수도 있음.

그러나 꼭 assert가 하나일 필요는 없지만 최대한 적게를 유지하고...

 

테스트당 개념 하나

assert가 하나가 아니라 한가지 개념을 테스트하는 것으로 유지하는 것이다. 사실 assert도 단순히 assert 개수가 문제가 아니라 여러 개념을 한 테스트에 넣는게 문제인 것임.

 

 

깨긋한 테스트의 5가지 규칙 F.I.R.S.T

Fast : 테스트는 빨리 돌아야함. 느리면 돌릴 엄두가 안남.

Independent : 각 테스트는 의존하면 안됨! 독립적으로 순서 상관없이 돌아도 괜찮아야됨. 의존적이면 어떤 부분이 문제인지 찾기 어려워짐.

Repeatable : 어떤 환경에서도 반복 가능해야함. 환경 문제라고 탓하는 순간 변명이 생김.

Self-Validating : 테스트는 boolean 값으로 나와야함.

Timely : 적절한 타이밍에 작성해야함.

'TIL' 카테고리의 다른 글

TIL 2022-03-16 깃 관리 rebase, reset, force-push  (0) 2022.03.16
TIL 2022-03-14 클린코드 10장  (0) 2022.03.14
TIL 2022-03-12 클린 코드 1~5장  (0) 2022.03.12
TIL 2022-03-10 SSH  (0) 2022.03.10
TIL 2022-03-07 PHP, JSON Schema,  (0) 2022.03.07