로버트 C. 마틴의 『Clean Code』라는 개발 관련 서적을 읽고 있다.
이 책은 프로그래머뿐만 아니라 소프트웨어 공학도, 프로젝트 관리자, 팀 리더, 시스템 분석가 등 소프트웨어 개발과 관련된 다양한 역할의 사람들에게 도움이 되는 책이라고 한다.
교보문고에서는 이 책을 다음과 같이 소개하고 있다.
『Clean Code(클린 코드)』는 오브젝트 멘토(Object Mentor)의 동료들과 함께 클린 코드를 만드는 최상의 애자일 기법을 소개하는 책이다. 소프트웨어 장인 정신의 가치를 심어 주며 프로그래밍 실력을 높여준다. 여러분이 노력만 한다면.
어떤 노력이 필요하냐고? 코드를 읽어야 한다. 아주 많은 코드를. 그리고 그 코드의 무엇이 옳은지, 그른지 생각해야 한다.
더 나아가, 전문가로서 자신이 지닌 가치와 장인으로서 자신의 작품에 대한 헌신을 돌아보게 된다.
아직 책을 완독하지는 못했지만, 절반 정도 읽으면서 깨달았던 것들이 많다. 그런 깨달음을 잊지 않고 기록으로 남기기 위해 블로그에 정리해본다. 이 기록은 단순히 내용을 정리하는 것뿐만 아니라, 내가 느낀 점을 바탕으로 실천하고 싶은 부분을 되새기기 위함이다.
#지속적인 개선
잘 짠 코드가 전부가 아니라, 시간이 지나도 언제나 깨끗하게 유지해야 한다.
*미국 보이스카우트가 따르는 규칙 : 캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라
체크아웃할 때보다 좀 더 깨끗한 코드를 체크인한다면 코드는 절대 나빠지지 않는다.
한꺼번에 많은 시간과 노력을투자해 코드를 정리할 필요는 없다.
변수 이름 하나를 개선하고, 조금 긴 함수 하나를 분할하고, 약간의 중복을 제거하고, 복잡한 if문 하나를 정리하면 충분하다.
이 부분을 보고 지난 내 개발 과정을 떠올려보니, 한 번에 코드를 개선하려다가 점점 복잡해지고 원래 형태를 알아보기 어려워져서 테스트도 힘들어졌던 적이 많았던 것 같다. 결국 깃에서 로컬 변경사항을 버렸던 일도 종종 있었다. 어차피 하루 개발하고 끝낼 코드도 아닌데 왜 그렇게 한 번에 하려고 했는지 모르겠다. 이 단락을 읽고 나서야, 전보다 조금 더 깨끗하게 만드는 게 결국 지속적인 개선으로 이어진다는 것을 깨달았다.
#읽는 사람이 차이를 알도록 이름을 지어라
불용어를 추가한 이름 역시 아무런 정보도 제공하지 못한다.
Product 클래스가 있을 때 다른 클래스를 ProductInfo, ProductData라 부른다면 개념을 구분하지 않은 채 이름만 달리하는 경우다.
실제로 이렇게 사용했던 적이 있어서 찔렸다. 그러다 보니 매번 "이게 무슨 클래스였더라?" 하고 헷갈리곤 했었다. 시간이 조금 더 걸리더라도, 클래스 이름을 다른 사람이 봤을 때도 바로 이해할 수 있을지 가정해보고 신중하게 지어야겠다는 생각이 들었다.
일반적으로 짧은 이름이 긴 이름보다 좋다. (단, 의미가 분명한 경우에 한해서)
아니라면 길더라도 서술적인 이름을 사용하라! 그게 낫다.
이름에 불필요한 맥락을 추가하지 않도록 주의한다.
accountAddress, customerAddress는 Address 클래스 인스턴스로는 좋은 이름이나, 클래스 이름으로는 적합하지 못하다.
나도 클래스명을 accountAddress, customerAddress처럼 사용하고 있었다. (이게 맞는 줄 알았다.)
하지만 의미를 명확하게 하고, 불필요한 맥락은 추가하지 않는 게 더 낫다는 것을 이번에 깨달았다.
함수 인수는 0개가 이상적인 개수이며, 그 다음은 1개, 2개이고 3개는 가능한 피하는 편이 좋다.
4개부터는 특별한 이유가 필요하다. (특별한 이유가 있어도 사용하지마라)
StringBuffer를 인자로 넘겨줬다고 생각하면 코드를 읽는 사람은 매 함수에서 StringBuffer를 발견할 때마다 이 StringBuffer의 의미를 해석해야 한다.
플래그(Flag) 인수는 추하다.
이항 함수도 되도록이면 단항 함수로 바꾸려 노력해라.
잘 알려진 휴리스틱으로, 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙
즉, 객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출하면 안된다.
예)
// 여러 객체를 한 번에 호출하며 의존성을 노출함
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
// 각 객체가 자신만의 책임을 가지며 유지보수가 용이해짐
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
코드의 길이를 줄이고 "이렇게 해도 직관적이고 충분히 괜찮지 않나?" 싶어서 어쩔 때는 줄줄 호출하곤 했다.
하지만 각자의 책임을 분리하는 방식이 훨씬 더 좋은 접근이라는 것을 위 내용을 읽고 알게 됐다.
함수는 작게, 매개변수 목록을 짧게 전략을 따르다 보면 때때로 몇몇 메서드만이 사용하는 인스턴스 변수가 많아진다. 이는 십중팔구 새로운 클래스로 쪼개야 한다는 신호다.
응집도가 높아지도록 변수와 메서드를 적절히 분리해 새로운 클래스 두세 개로 쪼개준다.
클래스가 응집력을 잃으면 쪼개라!
클래스에 있는 큰 함수를 작은 함수 여러 개로 쪼개다 보면 몇몇 함수가 몇몇 변수만 사용한다?
→ 독자적인 클래스로 분리해도 되지 않는가!
⇒ 종종 작은 클래스 여럿으로 쪼갤 기회가 생긴다.
항상 코드를 짜면서 "이럴 땐 어떻게 해야 옳은 걸까? 더 나은 방향은 뭘까?"라는 고민을 많이 했지만, 답을 잘 찾지 못할 때가 많았다. 인터넷에서 검색해도 뭔가 명쾌한 답을 얻기가 어려웠다. 그런데 이런 개발 서적을 읽으니 가려운 곳을 시원하게 긁어주는 기분이다. 왜 진작에 책을 읽지 않았는지 후회가 된다. 물론, 여기 적힌 내용이 항상 정답은 아니겠지만, 내 사고를 확장하는 데에는 충분히 큰 도움이 된다. 이제 다음에 읽을 책은 또 어떤 깨달음을 줄지 기대가 된다.