본문 바로가기
source-code/software

소프트웨어 장인 정신 이야기 (4)

by mattew4483 2024. 5. 23.
728x90
반응형

5장. 리팩터링

더보기

리팩터링은 동작을 바꾸지 않으면서 코드를 연속적으로 조금씩 바꿔서 소프트웨어의 구조를 개선하는 것인데, 각 변경을 마무리할 때마다 포괄적인 테스트 묶음을 통과시킴으로써 동작이 바뀌지 않았음을 증명한다.

여기서 핵심은

→ 1) 리팩터링은 동작을 보존한다 2) 개별 리팩터링은 작다. 디버깅이 필요하지 않을 만큼!

 

기본 도구

이름 바꾸기

무언가의 정확한 이름을 찾는 일은 연속적이고 반복적인 개선 과정인 경우가 많다. 정확한 이름을 추구하는 일을 두려워하지 말라. 프로젝트가 아직 초기일 때 가능한 자주 이름을 개선하라.

 

좋은 이름 짓기는 정말 어렵다!!

이때 기억하면 좋을 교훈은 → 연속적이고, 반복적인 개선을 통해 최선의 이름을 찾을 수 있다는 것.

따라서 변경을 두려워하지 말아야 한다. 지속적으로 점검하고, 수정해나가야 한다.

 

메서드 추출하기

내 조언은 '바닥날 때까지 추출하라' 규율을 따르라는 것이다.
이 규율은 두 가지 목표를 추구한다.
첫째, 모든 함수는 '한 가지 일'만 해야 한다. 둘째, 여러분의 코드는 '잘 쓴 문장'처럼 읽혀야 한다.
(...)
작고 조그만 함수가 너무 많아서 코드의 의도를 흐리는 것처럼 느껴질 수도 있다. 거대한 함수 더미 속에서 헤매게 될까 봐 걱정할지도 모르겠다. 
하지만 정반대 일이 일어난다. 코드의 의도가 훨씬 더 명확해진다. 추상화 계층들은 견고해지고 각 계층 사이의 경계는 명확해진다.

 

동감, 또 동감.

나 역시 리팩터링 과정에서 메서드를 추출하면서 '오히려 이게 더 알아보기 힘든 것 같은데..?'라는 생각이 들곤 했다.

 

하지만 천만의 말씀!

동료 개발자, 또는 미래의 내가 보았을 때

하나의 함수에 오만가지 로직이 작성되있는 것보다는

1) 모듈, 클래스, 네임스페이스를 적극적으로 활용하고

2) 잘게 추출한 비일반적인 함수에 적절한 이름을 부여함으로써 마치 잘 쓴 문장처럼 읽히는 코드를 작성할 수 있었다.

 

변수 추출하기

변수를 분리하고, 적절한 설명용 변수들을 추가하자!

 

규율

코드를 안전하게 안정적으로 리팩터링 하려면 목숨을 걸고 믿을 수 있는 테스트 묶음이 필요하다. 여러분은 테스트가 필요하다.
(...)
높은 수준의 테스트 모듈과 높은 수준의 제품 코드 모듈 사이에는 아마 일대일 대응이 있을 것이다. (...) 하지만 테스트가 쉽게 깨지지 않도록 하려면 이런 일대일 대응이 계속 낮은 수준까지 이어지지 않는 편이 좋다. 그러니 모듈과 컴포넌트 수준 아래에서는 일대일 대응을 일부러 깨트려야 한다.
(...)
리팩터링 시간을 기다리지 말라. 진행하면서 리팩터링 하라.
(...)
바꾸기를 주저하지 말라. 코드는 점토고 여러분은 조각가라고 생각하며 코드를 조작하라.

 

6장. 단순한 설계

단순하다는 건 얽혀있지 않다는 것이고 얽히지 않게 하는 일은 어렵다.
소프트웨어 시스템에서 무엇이 서로 얽힐까? 높은 수준의 정책과 낮은 수준의 세부 사항이 함께 얽혀 있는 경우가 가장 큰 대가를 치러야 하는 경우다.
(...)
단순한 설계는 높은 수준의 정책이 낮은 수준의 세부 사항을 모르는 설계다. 높은 수준의 정책이 낮은 수준의 세부 사항으로부터 분리되어 격리되므로 낮은 수준의 세부 사항을 바꿔도 높은 수준의 정책에는 아무 영향이 없다.

 

이러한 분리와 격리를 만드는 주요 수단은 '추상화'이며

추상화를 통해 높은 수준의 정책(본질)은 증폭하고, 낮은 수준의 세부 사항(지엽적인 부분)은 분리해서 격리시킨다.

 

그리고 이는 '다형성'을 통해 실제로 구현할 수 있다.

높은 수준 정책이 다형성 인터페이스를 사용해서 낮은 수준의 세부 사항을 조정하도록 해야 하며,

낮은 수준의 세부 사항은 이 다형성 인터페이스의 구현에 위치해야 한다.

→ 이를 통해 높은 수준의 정책은 낮은 수준의 세부 사항 구현을 몰라도 되는 것!

 

React를 통한 프론트엔드 개발을 하다 보면, 

각 React 컴포넌트와 높은 수준의 비즈니스 정책을 결합하고 싶은 욕구에 휩싸이고 만다.

왜? C.마틴의 말로 대답할 수 있다. → 일단 만들기는 쉬우니까!!!

 

명심하자.

설계는 단순해야 하며,

단순함은 중요한 것과 중요하지 않은 것의 분리와 격리에서 비롯되며,

이는 프론트엔드 개발에서도 유의미하게 적용된다.

 

테스트 커버리지

이 책에서 나는 첫 번째 규칙을 다음과 같이 표현하려고 한다.
1. 테스트로 검사한다.
(...)
코드 커버리지 숫자가 얼마면 좋은 걸까? (...) 바로 100%였다. (...) 
따라서 내가 단순한 설계의 첫 번째 규칙에서 사용한 '검사함'이라는 단어는 코드가 커버리지에 포함된다는 뜻이다. 100%의 줄 커버리지와 100%의 브랜치 커버리지를 의미한다.

 

이 얼마나 도발적인 말인가!

물론 C.마틴 역시 목표의 비현실성을 인지한다.

 

하지만 기억해야 할 점은

영원히 달성하지 못할 수도 있다는 사실이, 매 커밋마다 이에 더 가까이 가기 위해 노력하지 않는 핑계는 될 수 없다는 것!

 

그런데 높은 코드 커버지리지 단순한 설계와 무슨 상관일까?
(...)
코드 각 부분에서 줄 커버리지와 브랜치 커버리지를 높이려면 이 부분들을 테스트 코드에서 접근할 수 있어야 한다. 그러려면 해당 부분이 코드의 나머지 부분과 결합되어 있지 않아서 개별 테스트에서 분리한 후 실행할 수 있어야 한다. 
(...)
잘 분리된 테스트를 작성하는 일은 설계 행위다. 테스트할 코드를 테스트에 적합하도록 설계해야 하기 때문이다.

 

테스트가 어려운 게 아니다. 내가 짠 코드가 테스트하기 어렵게 설계된 것일 뿐.

 

 

728x90
반응형