Red → Green으로 가는 것에 집중하기(’한번에 하나씩’) 태스크에 성공했다면, 이후 Refactor 사이클에서 더 나은 코드를 작성해볼 수 있습니다.
바꿔 말하면 Green이 되는 사이클 마다 이후 리팩토링의 기회가 생긴다는 의미입니다. 아쉽게도 적지 않은 개발자 분들이 리팩토링을 큰 시간을 내어 한꺼번에 하는 일로 오해하곤 합니다.
“요리를 잘 하는 사람은 중간중간 설거지, 정리를 하면서 진행한다. 초보는 나중에 몰아서 한다. 몰아서 하면 고통스럽다. 리팩토링도 마찬가지다. 아예 리팩토링 시간을 내서 한다는 거 자체가 좋은 시그널은 아닌 것 같다.”
하지만 초보의 경우 Refactor에서 가하는 변경이 복잡도나 인지부하를 낮추는데 기여하지 못하거나 오히려 악화시키는 경우가 더러 있습니다. 앞서 2-1에서 언급된 바람직하지 않은 코드 형상들에 대한 케이스를 떠올려 볼 수 있겠습니다.
혹은 ‘리팩토링의 기회가 생긴다’는 말의 의미를 ‘반드시 매번 리팩토링 해야 한다’로 오인하는 경우도 있습니다. 이런 잘못된 판단들이 쌓이면 결국 변경에 불리한 상태를 만들고 팀의 사기를 낮추고 개발 속도를 늦춥니다.
사실 시기와 방식 모든 측면에서 매번 트레이드 오프를 따져야 하기 때문에 리팩토링을 잘하기란 생각보다 쉽지 않습니다. 맥락과 상황 또한 다르기 때문에 명확한 기준을 내려주기도 어렵습니다.
때문에 어떠한 고정된 룰을 따르기 보다는 다소 유동적이더라도 리팩토링에 대한 ‘메커니즘’ 그 자체를 익히고 연습하는 것이 도움이 될 것입니다.
좋은 모듈화란? (feat. 응집 / 결합)
•
'높은 응집도'와 '낮은 결합도'라는 두 가지 힘을 이용해, 각 모듈을 '예측 가능한 하나의 완결된 부품'으로 만드는 행위 그 자체.
◦
이것이 잘 되면 추상화를 신뢰할 수 있게 됨. 모듈의 이름과 인터페이스만 보고도 동작과 사용법이 예측 가능한지, 즉 “예측 가능한 블랙박스”로 여길수 있는지를 기준으로 판단할 수 있음.
내부의 원칙: 높은 응집도 (High Cohesion) - 모듈 내부에 어떤 것들을 모아놓으면 좋을까?
외부의 원칙: 낮은 결합도 (Low Coupling) - 모듈이 외부와 어떻게 소통하면 좋을까?
•
좋은 모듈화를 통해 미래에 코드를 이해하고 수정하는 데 드는 정신적 노력(인지부하)을 줄여줄 수 있음. 즉, 코드가 아니라 ‘인지 비용’을 더 저렴하게 관리하는 것이 모듈화의 진짜 목표.
•
배경 지식: 인간의 작업 기억 용량은 5±2개로 제한되어 있어, 여러가지를 함께 처리하려 하면 인지 과부하가 발생함.
신경과학 연구에 따르면, 우리의 뇌는 제한된 의사결정 능력을 가지고 있습니다. Diederich와 Trueblood(2018)*의 연구 결과는 다음과 같습니다.
•
개발자는 2시간 연속 코딩 후 오류율이 30% 증가함
•
컴포넌트에 상태 변수(state variable)가 하나 추가될 때마다 인지 부하가 37% 증가함
•
복잡한 컴포넌트는 멀티태스킹과 유사한 "신경 전환 비용(neural switching cost)"을 유발함
변경에 유리한 코드를 작성하기 위해서는 코드가 올바른 단위와 형태로 모듈화 되어야 합니다. 하지만 의지만으로 가능하지는 않습니다. 변경을 가한 이후에는 이 변경이 어떤 혜택을 가져왔는지 점검해보아야 합니다.
복잡도와 인지 부하 개선에 기여하는 좋은 코드를 작성하기 위해서 필요한 기준과 실천 방식을 정리해보았습니다. 성공적인 Refactor 사이클을 위해 아래 질문들을 나침반 삼아볼 수 있습니다.
왜 우리는 리팩토링을 거듭하더라도 좋은 모듈화에 실패할까?
많은 경우에 ‘시간이 부족해서’ 좋은 코드를 만들지 못했다고 생각합니다. 일정 부분 사실이겠으나, 시간과 자원이 충분하더라도 사람마다 만들어낼 수 있는 최대치의 좋은 코드는 각기 다를 겁니다.
왜 다를까요? 이는 리팩토링 결과물이 ‘Trigger’와 ‘Ability’로 구성되기 때문입니다.
•
Trigger: 언제 리팩토링을 시작할까?
•
Ability: 어떤 기술로 코드를 개선할까?
리팩토링을 하려면 우리가 흔히 ‘코드 스멜(Code Smell)’이라고 일컫는, 트리거를 느껴야 합니다. “아, 코드가 이러이러하니까 고쳐야겠다” 라는 판단이 서야 한다는 것이죠.
하지만 초보자의 경우 트리거가 발달해있지 않으므로 아무리 코드를 들여다봐도 고칠 곳이 없다고 느낍니다. 혹은 고치면 좋을만한 코드가 맞기는 하나 지금 상황에서 굳이 손댈 필요가 없을 수도 있습니다. 전문가는 이 경우 잠시 보류하기를 선택하나 초보자는 모든 트리거에 반응하여 불필요하게 자원을 소모하는 경향성이 있습니다.
한편 코드를 수정한 뒤의 결과물이 이전보다 사용하기 좋아져야 합니다.
이 측면에서도 초보자는 ‘뭔가 마음에는 안드는데 어떻게 고칠지는 모르겠다’ 거나 ‘이상하게 고쳐놓고 잘했다고 생각(코드 퀄리티 평가 기준에 문제가 있음)’합니다. 반면 전문가는 어떤 형태가 좋은지에 대한 자신만의 기준과 심상이 있습니다.
한편 리팩토링은 위 내용들을 매 상황마다 적절한 판단을 내려야 한다는 점에서 어려움이 가중됩니다. 하나의 명문화된 규칙보다는 매 상황에 맞게 적용해나가는 ‘메커니즘’ 그 자체로 이해하는 것이 조금 더 적절하게 느껴집니다.
Fundamentals of Modularization (FoM)이란?
그래서 FoM(Fundamentals of Modularization) 이라는 이름으로 좋은 코드를 작성해나가는 메커니즘을 정리해보았습니다.
FoM을 다음과 같이 정의해보고자 합니다.
•
복잡한 코드 형상을
•
관리 가능한 의미 있는 덩어리로
•
지속적으로 구분지어나가는
•
복잡도 압축 메커니즘이다
FoM을 길을 잡아주는 나침반처럼 활용해보세요.
Red → Green 다음 Refactor 페이즈를 시작하면서 “이제 뭘 해야 하지?” 하는 막연함 대신, 나침반이 가이드하는 경계와 전환의 감각을 따라 리팩토링을 진행합니다.
순서대로 적용하거나, 한번에 모든 원칙들을 적용했다고 해서 바람직한 것이 아닙니다. 이 질문 세트를 트리거 삼아 인지 부하를 줄여주는 자연스러운 흐름 속에서 사용하는 감각이 중요합니다.
처음 접근할 때는 “변경하기 쉬운가?”에서 출발하여 다른 상위 항목으로 넓혀나가는 방식도 좋습니다. 반대로 상위 항목에서 시작하여 하위 항목까지 나아가는 방법도 좋습니다. 무엇을 하든 현재의 내 문제를 푸는 과정에서 길잡이가 되고 효능감을 느껴야 합니다.
-
1. 명확한 신호로부터 출발하는가? (Act on Signal)
2. 결과에 기여하고 있나? (Ship Value First)
3. 일반해로부터 출발할 수 있나? (Build on Patterns)
4. 안전한가? (Mark Your Checkpoint)
5. 변경하기 쉬운가? (Think Test, Keep it Soft) - Software가 ‘hard’하면 변경하기 어렵다.
•
[메타 전략] 인지 부하 관리 (Follow your ease)
◦
모든 원칙은 궁극적으로 미래의 '생각하는 비용'을 줄이기 위해 존재한다.
◦
내 뇌가 지금 혼란 / 고통스럽다면 무언가 잘못되고 있다는 가장 강력한 신호다. 작성하고 읽기에 편안하고 자연스러운 코드 형상을 따라라.
위 질문들은 1) 한국어로 질문을 떠올리고 2) 영어 표현의 방향성에 따라 행동을 이어나가도록 설계되어 있습니다. 예시를 들어보자면 이렇습니다.
"(한참 정신 팔려서 리팩토링 하다가) 나 지금 결과에 기여하고 있나? -> 아, Ship Value First 하자...”
“(일단 리팩토링 하려다가) 나 지금 코드 스멜 보고 그거 고치려고 리팩토링 하려고 했나? 아니, 그냥 일단 하려고 했던거 같아. Act on Signal 하자. 아직은 안해도 돼. 다음 사이클에 다시 생각해보자.”
“그룹핑 로직을 내가 for문으로 직접 짜고 있는게 맞아? 뭔가 도구가 있을거 같은데. Build on Patterns 하자.”
-
이렇게 자신의 행동에 대한 트리거를 잘 감지하게 되었다면 실제 코드를 잘 고칠 수 있어야 합니다. 그때는 “변경하기 쉬운가?(Think Test, Keep it Soft)”에서 제안하는 가치를 따라가시는게 도움이 될겁니다.
‘Think Test, Keep it Soft’의 하위 4개 항목은 Frontend Fundamentals에 정리된 기준(가독성, 예측 가능성, 응집도, 결합도)을 따르고 있습니다.
좋은 기준이지만 실체적인 행동으로 옮겨지기에는 추상적인 부분이 있어 이 또한 방향성을 가진 표현으로 바꾸어 표현했습니다.
•
가독성(Readability) → 한눈에 이해되는가? (Don’t Make Me Think)
•
예측 가능성(Predictability) → 안 까봐도 되나? (No Suprises)
•
응집도(Cohesion) → 잘 뭉쳐있나? (Group By Purpose)
•
결합도(Coupling) → 이대로 떼어낼 수 있나? (Stand Alone)
눈치 채셨겠지만 FoM은 하위 항목을 따르다보면 상위 항목의 목표가 달성되는 구조입니다.
예를 들면 가독성, 예측 가능성, 응집도, 결합도를 개선하다보면 유연하고 테스트하기 좋은 코드가 됩니다. 또, 명확한 신호로부터 출발하고, 결과에 기여하고, 일반해로부터 출발하고, 안전을 확보하고, 변경하기 쉬우면 복잡도와 인지부하가 낮은 코드를 작성하게 됩니다.
이 질문들이 정상적으로 작동했다면 결국 코드를 읽거나 수정하는 일이 복잡하게 느껴지지 않아야 합니다. 즉 ‘Follow your ease’에 부합해야 합니다. 각각의 항목이 꼭 순차적으로 적용될 필요는 없습니다. 상황에 맞게 유연하게 적용하시면 됩니다.

