클린 코드가 ‘단지’ 코드에 대한 책이 아닌 사실에 대해, 11월 COMMIT 현장 스케치

2023년 11월 22일 열린 COMMIT에서는 컬리 물류 프로덕트 본부장 박성철 님이  《클린 코드》에 대한 이야기를 풀었습니다. 

성철 님은 《클린 코드》가 코드 외에도 객체 지향, 설계, 아키텍처, 테스트 등 소프트웨어 개발과 관련된 폭넓은 이야기를 다루고 있지만, ‘클린’한 ‘코드’만 소비되고 있다고 보았어요. 저자인 로버트 C. 마틴이 책에서 밝혔듯 클린 코드는 완벽하지 않고 논란의 여지도 있습니다. 우리가 《클린 코드》를 좀 더 균형 있게 바라보아야 하는 이유죠. 

클린 코드를 둘러싼 논란은 어떤 것이 있고, 우리가 놓치고 있는 이야기는 무엇일까요? 《클린 코드》를 좀 더 균형 있게 이해하기 위한 성철 님의 관점을 들어봅니다.

🎤 이 아티클은 박성철 님의 2023년 11월 COMMIT 세미나 내용을 바탕으로 재구성되었습니다.

여러분, 《클린 코드》를 읽어본 적 있으신가요? 《클린 코드》는 개발자에게 바이블로 자리 잡았지만, 제 생각과 다르게 받아들여지고 있는 것 같습니다. 오늘 발표를 준비하게 된 계기이기도 해요. 제 이야기가 정답은 아니지만, 이번 COMMIT에서 《클린 코드》를 보는 제 관점을 공유하려고 합니다. 

주제는 ‘클린 코드가 코드에 대한 책이 아닌 사실에 대해’인데요. 강조하고 싶은 건 《클린 코드》가 ‘단지’ 코드에 대한 책이 아니라는 거예요. 

《클린 코드》는 개발자라면 모르는 사람이 없을 거예요. 굉장히 성공한 책입니다. 《클린 코드》가 출간된 이후에 관련 서적이 연달아 나왔죠. 《파이썬 클린 코드》, 《파이썬을 이용한 클린 코드를 위한 테스트 주도 개발》, 《클린 코드의 기술》 등이죠. 여전히 많은 개발자에게 영향력을 미치는 책입니다.

요즘은 《클린 코드》가 채용 시장에서 일종의 스펙이 되는 것 같아요. 이력서에 《클린 코드》를 읽었다고 쓰기도 하고, 면접에서도 《클린 코드》를 추구한다고 이야기합니다. 

《클린 코드》를 둘러싼 논란

클린 코드에 대한 여러 가지 논란이 있습니다. 클린 코드를 추구해서 주석을 작성하지 않는다거나, 코드가 클린하면 리팩터링이나 테스트 코드가 없어도 된다는 건데요. 과연 리팩터링이 필요 없는 클린 코드가 존재할 수 있을까요? 

레거시 코드는 클린하지 않아서 손도 안 댈 거라고 하는 분들도 있습니다. 조금 당황스럽죠. 모든 코드는 언젠가 레거시 코드가 되는데, 이를 깨끗하게 정리하기보다는 회피하는 게 건강한 방법은 아니거든요.

이외에도 클린 코드를 작성해야 해서 개발 일정을 지킬 수 없다거나, 클린 코드 원칙에 위배되어서 리뷰를 승인할 수 없다는 등 많은 논란이 있습니다. 저도 실제로 들었거나, 본 적 있는 이야기고요. 

왜 논란이 생겼을까?

문제가 생긴 이유는 ‘클린(Clean)’이라는 표현 때문인 것 같아요. 코드에 어떤 상태가 있다는 오해가 생긴 거죠. ‘깨끗하다’ 혹은 ‘더럽다’ 같이 이분법적으로 생각하게 된 겁니다. 

자신이 클린하지 않은 코드를 쓰고 있다는 생각이 들면 진전을 못하게 돼요. 일종의 죄책감, 결벽증, 강박으로 이어지는 거죠. 깨끗하다는 생각이 들 때까지 코드를 못 써내는 문제도 생기게 됩니다. 

논란은 저자인 로버트 C. 마틴(Robert C. Martin)의 영향도 있습니다. ‘밥 아저씨(Uncle Bob)’라고 불리는 분이죠. 로버트 C. 마틴은 표현이 좀 강한 분이라고 생각해요. 《클린 코드》뿐만 아니라 다른 책과 강연을 봐도 주장을 단정적으로 얘기하는 경향이 있습니다. 

《클린 코드》에는 ‘르블랑의 법칙’이라는 게 나오는데요. ‘나중은 결코 오지 않는다.’는 이야기입니다. 마치 널리 알려진 법칙 같지만, 유래를 살펴보면 그저 친구끼리 했던 농담이에요. 이것을 널리 인정받는 법칙처럼 책에 인용한 거죠. 이런 법칙은 없습니다.  

《클린 코드》는 로버트 C. 마틴이 전부 쓴 책이 아니에요. ‘오브젝트 멘토(Object Mentor)’라는 컨설팅 회사에 속한 많은 컨설턴트가 챕터를 나눠 썼습니다. 물론 주 저자는 로버트 C. 마틴이지만, 《클린 코드》 자체는 오브젝트 멘토라는 한 문파의 생각이자 교리, 신조입니다. 

로버트 C.마틴도 1장 마지막에 이런 이야기를 덧붙입니다. 이렇게 강하게 주장하지만, 이건 자기 생각일 뿐이라고요. 완벽하지 않고 논쟁의 여지가 있음을 인정합니다. 우리가 선동적이고 극단적인 표현을 걷어내고 진짜 내용을 봐야 하는 이유예요. 

《클린 코드》의 구성

《클린 코드》는 크게 세 파트로 구성되어 있습니다. 첫 번째 파트는 원칙, 패턴, 그리고 해당 패턴을 적용한 연습 문제로 구성되어 있어요. 두 번째 파트는 사례 연구입니다. 마지막 파트는 휴리스틱이에요. 휴리스틱 파트에서는 ‘냄새’라는 표현을 자주 씁니다. 리팩터링에서 나온 표현인데, 딱 잘라서 안 좋다고 하기는 애매하지만 이런 느낌은 좋지 않다는 걸 의미해요. 이 파트에서는 책의 주장을 기계적으로 적용할 게 아니라, 스스로 어떤 감각을 체득해 그에 기반해 판단해야 한다는 걸 강조합니다. 오늘은 첫 번째 파트인 ‘원칙, 패턴, 연습’을 주로 살펴볼게요.

코드와 코딩이란 무엇인가?

’클린’에 대해서 이야기 했으니 이제 ‘코드’에 대해서 이야기해볼게요. 전통적인 소프트웨어 개발 주기는 다음과 같습니다. 기획 → 분석 → 설계 → 구현 → 테스트 → 운영 → 유지보수를 반복하죠. 여기서 주목할 건 ‘설계’와 ‘구현’이 나뉘어져 있다는 거예요.

전통적인 소프트웨어 개발 주기

제가 한창 개발을 했던 20세기에는 설계만 하는 분들이 있었습니다. 그분들이 UML(Unified Modeling Language, 통합 모델링 언어) 같은 다이어그램으로 설계하면 개발자들이 투입되죠. 개발자들은 설계된 걸 코드로 표현합니다. 그래서 개발자들을 ‘코더’라고 불렀어요. 코더는 별다른 생각할 필요 없이 설계된 문서를 코드로 표현할 수만 있으면 됐습니다. 그렇다 보니 코더는 뛰어난 역량을 가질 필요가 없다고 생각했었어요. 코더를 깎아내리는 용어로 ‘코드 원숭이’라는 표현도 있었습니다. 개발 용어나 라이브러리, 프레임워크 몇 개만 익히면 누구나 할 수 있는 일이라는 뜻이었죠. 

요즘은 설계와 구현 모두 ‘ChatGPT’나 ‘코파일럿(Copilot)’ 같은 AI의 힘을 빌리고 있는데요. AI 시대에 코딩은 계속 필요할까요? 《클린 코드》에서는 코딩이 계속 필요할 것이라고 이야기합니다. 

코드의 종말이 코앞에 닥쳤다고 주장하는 사람이 없지 않다. (…) 앞으로 코드가 사라질 가망은 전혀 없다! 왜? 코드는 요구 사항을 상세히 표현하는 수단이니까!

– 《클린 코드》 중

코드는 일종의 요구 사항 정의서이고, 코딩은 주어진 설계 문서를 코드로 표현하는 것이라고 정의한 거예요. 

1992년 ‘C++ Journal’ 잡지에 잭 리브스(Jack W. Reeves)라는 분이 소프트웨어 디자인, 즉 설계란 무엇인지에 대한 아티클을 기고했습니다. 요지는 코딩은 소프트웨어를 만드는 것이 아니라, 소프트웨어를 설계하는 것이라는 겁니다. 로버트 C. 마틴과는 다른 정의죠.

잭 리브스는 소프트웨어를 만드는 것과 소프트웨어를 설계하는 걸 다르게 보았습니다. 소프트웨어를 만드는 건 사람이 아니라, 컴파일러나 링커가 하는 일이라고 생각했어요. 소스 코드는 그냥 텍스트 파일일 뿐이고, 이 텍스트 파일을 컴파일러나 링커가 바이너리 파일로 전환하는 게 소프트웨어를 만드는 것이라고 본 겁니다. 우리는 그 소프트웨어를 설계하는 사람이고요. 

설계 단계가 따로 떨어져 있는 게 아니라, 설계와 코딩이 한 단계로 묶이게 됩니다. 코딩도 설계이고, 테스트와 디버깅도 설계의 일부이며, 우리가 일반적으로 소프트웨어 설계라고 부르는 것 역시 설계의 일부가 되죠. 같이 엮여 돌아가다가 설계 결과로 코드가 나오는 거예요.

소프트웨어 설계 프로세스

설계 재료

설계를 하려면 재료가 있어야 합니다. 소설에 주제, 문체, 구성이라는 3요소가 있는 것처럼 소프트웨어도 마찬가지죠. 목적을 달성하기 위해 다양한 재료를 사용합니다. 이를테면 주석, 함수, 객체와 자료 구조, 오류 처리, 클래스, 아키텍처 등이죠. 이 모든 걸 적당한 품질로 개발해야 합니다. 하지만, 대부분 기본 구성 요소인 함수, 객체와 자료 구조, 오류 처리만 신경 쓰는 경우가 많아요. 

OOP가 정답인가?

“코드를 잘 짜고 싶은데 어떻게 해야 하나요?”라는 질문에 객체 지향을 공부하라는 답변을 자주 접하는데요. 저는 좋은 답변이라고 생각하지 않습니다. 

객체 지향은 한가지 개념이 아니에요. 많은 사람이 다양한 관점에서 발전시켜 온 개념이다 보니 통합할 수 있다고 생각하지 않습니다. 

저는 보통 OOP(Object-Oriented Programming, 객체 지향 프로그래밍)를 4가지 관점에서 바라봅니다. 명사(데이터 중심), 동사(기능) 중심, 객체 중심, 객체 보조인데요. 보통 명사 중심의 객체 지향과 동사 중심의 객체 지향이 대비됩니다. 

명사 중심의 객체 지향은 도메인 모델링을 할 때 많이 쓰여요. 객체 하나하나가 특정 명사, 유저, 아이템 같은 이름을 갖게 되고, 그에 따라 데이터를 표현하는 이름과 거기에 맞는 메소드를 갖게 됩니다. 

동사 중심의 객체 지향은 메시지를 중요하게 생각합니다. 인터페이스를 만들어 객체가 하는 역할을 부여하고, 역할에 맞는 메소드를 만들죠. 데이터 상태는 숨겨져 있어 외부에서 보이지 않습니다.

프로그램의 기본 구조를 객체로 생각하는 분도 있습니다. 프로그램을 여러 작은 객체로 만들고, 그 객체 안에 또 다른 객체가 있는 거죠. 어떤 분은 기본 구조는 절차형 같은 패러다임을 쓰는데, 그 안에서 특별한 용도로만 객체를 쓰기도 합니다. 

이렇게 관점이 다르다 보니 객체 지향이라고 묶인 OOP, OOA(Object-Oriented Analysis, 객체 지향 분석), OOD(Object-Oriented Design, 객체 지향 설계), OOM(Object-Oriented Modeling, 객체 지향 모델링), OOT(Object-Oriented Test, 객체 지향 테스트), OOM(Object-Oriented Method, 객체 지향 방법론)이 사실 다 다를 수도 있습니다. 이걸 하나로 보는 건 잘못됐다는 생각이에요. 

《클린 코드》의 OOP

그러면 《클린 코드》에서는 객체 지향을 어떻게 보고 있을까요? 로버트 C. 마틴은 동사 중심의 큰 객체 지향으로 보는 경향이 있습니다. 기본 구조를 객체로 보면서 동사 중심의 설계를 생각하는 편이에요. 그리고 객체 지향과 절차적 프로그래밍 같은 다른 패러다임을 적절하게 섞어 쓰면 좋겠다고 이야기합니다. 

저는 《클린 코드》 6장 ‘객체 vs 자료구조(구조체)’가 굉장히 좋은 내용이라고 생각합니다. 객체와 자료 구조를 구별하고, 모든 걸 객체로만 표현하려고 하지 말라고 주장하고 있어요. 이런 걸 보면 우리도 객체 지향에 대한 기대를 좀 내려놓아야 하지 않을까 생각하게 됩니다. 

분별 있는 프로그래머는 모든 것이 객체라는 생각이 미신임을 잘 안다.

– 《클린 코드》 중

설계와 리팩터링

클린 코드와 관련된 흔한 오해는 처음부터 클린 코드를 작성하면 리팩터링이 필요 없다는 거예요. 

리팩터링의 사전적 정의는 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법입니다. 마틴 파울러가 《리팩터링》이라는 책에서 내린 정의예요. 

리팩터링의 ‘팩터(factor)’는 구성 요소, 인자라는 뜻입니다. 팩터링(factoring)은 인수 분해고요. 결국 소프트웨어 설계에 대한 이야기입니다. 설계라는 건 복잡한 덩어리의 문제를 잘게 쪼개고, 각각 해결한 다음 잘 합치는 거니까요. 

설계하고 보니 마음에 안 들거나, 개선할 수 있다고 생각하면 다시 설계하자는 의미로 리팩터링이라는 단어가 나온 거죠. 아마 리팩터링을 처음부터 ‘재설계’라는 말로 번역했다면 오해가 적었을 것 같아요. 

마틴 파울러는 기회가 되면 혹은 기회를 찾아서라도 리팩터링하라고 강조했습니다. 어떤 형태의 리팩터링이든 틈만 나면 하라는 거죠. 그래도 안 합니다. 

왜 리팩터링을 안 할까요? 개발자들은 늘 자신이 변경하는 것 때문에 문제가 발생하지 않을지 노심초사합니다. 이걸 고치면 또 다른 곳에 문제가 발생할 수 있으니, 자신이 없는 거예요. 이렇게 위험한데 어떻게 리팩터링을 할 수 있겠어요. 

《클린 코드》에서는 테스트 케이스가 있으면 리팩터링이 쉬워진다고 이야기합니다. 우리가 리팩터링을 못 하는 이유는 안전망이 없기 때문이고, 그 안전망이 테스트 케이스인 거죠.

실제 코드를 점검하는 자동화된 단위 테스트 슈트는 설계와 아키텍처를 최대한 깨끗하게 보존하는 열쇠다. 테스트는 유연성, 유지보수성, 재사용성을 제공한다. 테스트 케이스가 있으면 변경이 쉬워지기 때문이다.

– 《클린 코드》 중

TDD와 리팩터링

TDD(Test-Driven Development, 테스트 주도 개발)에서는 테스트를 먼저 작성합니다. 그리고 이 테스트를 통과하기 위한 최소한의 코드를 만들어요. 일단 동작하게 만드는 거죠. 그러고 나서 리팩터링하는 구조를 반복합니다.

흔히 TDD를 이야기할 때 테스트에만 집중하는데요. 일단 테스트를 작성해 안전망을 치고, 지저분하더라도 동작하게 만드는 겁니다. 계속 동작하는지 확인하면서 공격적으로 씹고, 뜯고, 맛보고, 즐기면서 코드를 고치는 거죠. 이렇게 설계하면서 마음에 들면 다음으로 넘어가는 사이클을 반복하는 프로그래밍이 TDD입니다. 설계는 미리 다 해두는 게 아니라 개발하면서 수시로 하는 거죠.  

반면 DDD(Domain-Driven Design, 도메인 주도 설계)에서의 리팩터링은 굉장히 과감해요. 우리가 리팩터링을 하면서 새로운 모델을 이해하다 보면 어느 순간 모든 것을 뒤흔드는 통찰력을 얻게 된다고 말합니다. 그래서 광범위한 대규모 리팩터링을 진행하면서 도약해야 한다고 하죠. 

《클린 코드》 12장에서 마이클 C. 페더스(Michael C. Feathers)는 창발적 설계라는 이야기를 합니다. ‘창발’은 의도하지 않았던 게 툭 튀어나온다는 뜻이죠. 여기서는 설계 자체를 창발한다는 개념을 이야기해요. 켄트 백이 이야기한 4가지 설계 단계를 적용하며 리팩터링하면 어느 순간 좋은 설계가 창발된다고 합니다. 처음부터 의도한 게 아니고, 반복하다 보니 창발되는 거죠.

켄트 백의 단순한 설계 규칙

① 모든 테스트를 실행한다.
② 중복을 없앤다.
③ 프로그래머의 의도를 표현한다.
④ 클래스와 메서드 수를 최소로 줄인다.

– 《클린 코드》 중

구조(아키텍처)

놀랍게도 《클린 코드》에는 아키텍처 이야기가 있습니다. 여러분은 아마도 아키텍처는 시스템에 큰 영향을 미치기에 처음부터 신중하게 결정하지 않으면 나중에 바꾸기 힘들다고 생각하실 텐데요. 

TDD를 만든 켄트 백은 《익스트림 프로그래밍》 책에서 아키텍처는 시작하기 충분할 정도로만 만들고, 진행하면서 꾸준히 다듬어야 한다고 주장합니다. 저는 말도 안 된다고 생각했어요. 아키텍처는 처음에 정해두면 바꿀 수 없다고 생각했거든요. 

우리는 일을 하면서 문제를 더 잘 이해하게 되고, 그를 바탕으로 또 다른 문제를 정복하고 해결해 나갑니다. 켄트 백의 주장도 이와 같아요. 우리가 모든 문제를 정복할 수 없는 것처럼 아키텍처도 시스템이 성장함에 따라 바뀌어야 한다는 거죠. 

소프트웨어 시스템은 물리적인 시스템과 다르다. 관심사를 적절히 분리해 관리한다면 소프트웨어 아키텍처는 점진적으로 발전할 수 있다.

– 《클린 코드》 중

우리는 잘 몰랐기 때문에 아키텍처도 처음부터 잘 결정하지 않으면 안 된다고 생각했던 거예요. 

테스트와 아키텍처

아키텍처도 대규모 리팩터링을 하기 위한 테스트가 필요합니다. 소프트웨어 구조와 관점을 효과적으로 분리한다면 개선할 수 있고요. 다시 말해 아주 단순하면서도 멋지게 분리된 아키텍처로 결과물을 재빨리 출시한 다음 기반 구조를 추가하며 조금씩 확장해 나가도 괜찮다는 이야기입니다.

이 의견을 발전시켜 최근 출간된 책이 《진화적 아키텍처》입니다. 쉽게 변경할 수 있도록 만들어진 시스템과 테스트, CI/CD 파이프라인이 있다면 아키텍처도 계속 진화할 수 있다고 주장합니다. 

마무리

한 유튜브 영상에서 《클린 코드》 책에서 하라는 대로 코드를 작성했더니 성능이 15배 떨어졌다는 이야기를 전했습니다. 15배면 컴퓨터가 발전하는 속도로 봤을 때 12년 정도의 갭이고요. 그분의 요지는 프로그래머의 삶을 편하게 만들기 위해 10년 이상의 하드웨어 성능을 포기하는 게 맞냐는 거예요. 곧이곧대로 《클린 코드》 원칙을 적용하는 게 맞을지 고민해 볼 필요가 있죠.

저는 소프트웨어에 대한 관점이 조금 달라져야 한다고 생각합니다. 우리는 공산품을 만들 것인지, 생물을 키울 것인지 선택해야 해요. 저는 소프트웨어는 ‘키우는 것’이라고 생각합니다. 한 번 만들고 대충 쓰다 버리는 게 아니라, 우리에게 필요한 최소한의 결과물을 내고 계속 개선해 나가는 거죠. 

소프트웨어는 계속 성장하고 분화해야 해요. 생물이 자가 증식하고 포식하면서 살아남는 것처럼요. 

이렇게 하려면 테스트를 중심으로 한 피드백 루프가 필요해요. 함수, 객체, 컴포넌트, 아키텍처, 시스템 단위로 테스트가 필요하고, 이를 모두 통합한 테스트도 있어야 하죠. 

그리고 너무 한 가지 패러다임에 매몰되지 마세요. OOP와 FP(Functional Programming, 함수형 프로그래밍) 외에 다양한 프로그래밍 스타일이 있으니 넓게 보면 좋겠습니다.

테스트 코드를 중심으로 변화 주기를 만들면 좋겠어요. 테스트 코드를 작성해도 결함은 생깁니다. 여기서 안 해도 되는 일을 한다고 말하는 분도 있을 텐데요. 테스트 코드가 안전망이라고 생각하면 이야기가 달라집니다. 안전망을 치는 건 결국 나를 위한 일이니까요. 

《클린 코드》는 나 그리고 나와 일하는 동료를 위한 겁니다. 소프트웨어를 만드는 건 사람이에요. 만드는 사람이 안전망 안에서 행복하게 만들어야 잘 만들어지겠죠. 나와 우리를 위해 《클린 코드》에서 이야기하고 있는 테스트 코드 같은 부분에도 관심을 가지면 좋겠습니다.

마이클 페더스가 설명한 클린 코드로 마무리하겠습니다. “클린 코드를 신경 쓴 사람이 쓴 코드가 클린 코드다.” 로버트 C. 마틴은 이 말을 인용하면서 자신이 본 클린 코드에 대한 정의 중 최고라고 이야기했어요. 일방적으로 원칙을 따르는 게 클린 코드가 아닙니다. 《클린 코드》는 생각보다 ‘클린’이나 ‘코드’와 상관없을 수 있습니다.

Posted by
goorm

ANYONE CAN DEVELOP