Part 2, 읽기 좋은 코드가 좋은 코드다
읽기 좋은 코드를 만들기 위한 두 번째 글이다.
전 편은 변수명과 메소드에 대한 내용을 다뤘다.
이번 글에서는 지난 편에 다루지 못한 포맷팅, 그리고 좋은 메소드를 작성하기 위한 방법들을 유명한 아저씨들의 방법론과 더불어 소개하려고 한다. 우리는 개발자이기에 항상 코드의 합리성과 아름다움을 추구하고 있다는 사실을 잊으면 안 된다.
‘짧은 코드가 좋은 코드'는 맞는 말이기도, 틀린 말이기도 하다.
로직이 간결해야 하고, 군더더기 없는 코드가 좋은 코드는 맞지만, 항상 라인수가 적은 코드가 좋은 코드는 아니라는 의미다.
위의 예제에서 `createUser`뒤에서 어떤 것을 하는지 잘 네이밍 된 변수나 메소드를 보고 파악할 수는 있겠지만, 한 줄에서 너무 많은 일들을 처리하고 있다 보니 한눈에 들어오지 않는다.
과거에 라인 수로 개발자의 한 일을 측정하기도 했다고 들었다. 내가 대학교를 다닐 때만 해도, “얘가 짠 게 몇 라인인데, 이거 일주일 만에 다한 거래!”라는 소리를 듣곤 했다. 이런 말을 들은 탓에 초반에는 라인수가 많아야 좋은 것인 줄 알았지만, 근래에 와서는 "짧은 코드가 좋은 코드"라는 그 표면적 의미만 전달되서인지 한 줄에 다 몰아넣는 사람들이 자주 보이곤 했다.
코드는 가독성을 해치지 않는 선에서 간결해져야 한다.
위의 예제를 아래와 같이 바꾸면 어떨까?
1줄에서 4줄의 코드로 늘어났지만, 가독성은 훨씬 나아졌다.
한 줄에서는 세 개 이상의 ‘일'을 하지 않는 것을 추천한다.
가독성을 위한 부분이다.
코드를 한 화면에서 파악할 수 없고, 가로 스크롤링을 해야 하는 순간 코드의 가독성은 확 떨어진다.
스크롤이 길어질 경우 전에 어떤 코드를 보고 있었는지 기억이 잘 나지 않게 된다.
한 화면에 로직이 들어오지 않는다면, 로직을 재 구성하면 해결할 수 있다. Chaining method나 기타 argument를 어쩔 수 없이 길게 넘어가야 할 경우가 있다면 엔터라도 쳐서 한눈에 들어오게 포맷팅 해주자. 위에서도 이야기했지만 라인수보다는 가독성을 높이는 것이 중요하다.
좋게도 요즘 IDE들은 대부분 넘지 말아야 할 선을 표시해주고 있다.
코드에서 도출할 수 있는 내용을 주석에 다시 적는 것은 똑같은 말을 반복하는 것이며, 지루하다.
코드의 내용은 메소드 이름에 잘 녹아들어 갈 수 있도록 해야 한다.
“좋은 코드는 주석이 필요하지 않다.”는 말을 들어본 적이 있을 것이다.
주석은 해당 기능을 이해할 수 있도록 배경 지식을 담고 있어야 한다. 기능의 목적과 구현 배경이 특이한 경우 적어주는 것이 좋다. 추후 이 코드를 유지 보수하는 개발자에게 하는 코멘트 정도로 생각하면 되겠다.
코드에서 도출될 수 있는 내용은 적지 않도록 한다.
또한, 코드가 변경될 때마다 기존 주석도 변경되어야 하는지 체크하자. 코드와 주석의 싱크가 맞지 않는다면, 추후 이 코드를 읽는 개발자는 혼란스러워할 것이다. 또한, 주석란에 별도의 버저닝을 하거나, 누군가를 치하하는 행위, 일기를 쓰는 행위 등은 지양해야 한다. 버저닝은 git blame을 통해 특정 라인을 누가 왜 수정했는지 알 수 있다.
개발을 하다 보면 조건을 다뤄야 할 때가 온다.
멋진 꽃바구니를 반환하는 함수가 있다고 가정해보자.
아주 심플하다. 요청만 하면 언제든지 꽃바구니를 반환한다.
좀 더 실제 사례와 가깝도록 바꿔보자. 꽃바구니를 요청할 때, 선호하는 꽃 목록을 받기로 했다.
지금은 한참 해바라기가 예쁠 때라, 선호하는 꽃에 해바라기가 있으면, 해바라기를 넣어주기로 했다.
흡족하다.
.
.
앗 그런데?
장미를 더 끼워달라는 기획자의 요청이 있었다고 해보자
맘에 드는 꽃바구니가 나왔는가?
요구사항이 더 추가되어서 해바라기 옆에 있는 장미의 색까지 고려해야 한다면 코드를 어떻게 수정하겠는가?
서비스가 운영됨에 따라, 요구사항이 많아지고 변형되며 이럴 때마다 조건문을 위와 같이 추가한다면 가독성은 점점 떨어지고, 유지보수의 비용이 점점 더 높아질 것이다. 따라서 우리는 급변하는 요구사항에 맞설 대비책을 수립해야 한다.
살아있는 프로그램은 지속적인 요구사항을 받아들이면서 변화해간다.
예를 들어, 버튼의 색깔을 바꿔달라는 요구사항을 받았는데 데이터 릴레이션이 흔들리거나, 정책적인 부분이 수정되어야 함에도 불구하고, 코드의 복잡성 때문에 ‘안될 것 같아요'하는 상황은 발생하면 안 된다.
코드를 작성하면서 비슷한 부분은 비슷한 부분끼리 모아놓아야 하고, 다른 부분들과의 결합은 필요한 만큼만 느슨하게 연결해야 한다.
'높은 응집도와 낮은 결합도', 객체 지향의 설계 원리 중 하나이다. (반대로 하면 큰일 난다.)
높은 응집도 부분은 모듈이 되고 이러한 모듈들이 느슨하게 연결이 되어 레이어가 된다.
프로그램은 상황에 맞게 여러 레이어를 가질 수 있다. 다소 장황해질 수는 있으나 직면한 상황에 꼭 맞는 부분만 수정될 수 있도록 분리하여 레이어를 만들어야 한다.
다양한 레이어를 프로그램에 녹여내려면 본질에 대해 깊은 고민과 이해가 필요하다.
프로그램의 본질에 대해 많은 고민을 하고 다양한 레이어와 그에 맞는 모델들을 산출하는 설계 방법론들이 등장하고 있으며, 대표적으로 DDD(Domain Driven Design) 설계 방법론이 있다. 다소 복잡해 보일 수는 있으나 알아두면 반드시 쓸 일이 생긴다. DDD에 대한 자세한 내용은 아래 링크를 참고하자.
DDD와 별개로 레이어 내의 모듈 관점에서, 도메인의 행위를 하는 부분과 정책을 결정하는 부분을 분리해 놓는다면 변경사항의 파급범위를 효과적으로 줄일 수 있다.
그렇다면 좋은 인터페이스는 무엇일까?
꼭 필요한 정보만 넘기고, 필요한 만큼만 공개해야 한다.
처음 만나는 사람들에게 자신을 소개할 때, 자신의 본적과 주민등록 번호, 이상형, 삶에 대한 가치관까지 소개하지 않아도 된다. 상황에 맞게 필요한 정보만 전달하면 된다. 인터페이스는 정보를 전달하고 할 일을 나눈다. 만약 자기 소개하는 자리에서 나의 이야기를 하기 앞서 엄마와 아빠의 연애시절 이야기를 하면 옳은 것일까?
제대로 정의되지 않은 인터페이스는 도메인 본질을 제대로 담고 있지 않을 수도 있다. 그럴 경우 변경의 대한 파급 범위가 여러 레이어를 관통해버릴 수도 있다.
잘못된 인터페이스 설계가 퍼진 경우, 시스템 전체를 뛰어다니며 고쳐야 할 수도 있다.
인터페이스를 설계할 때에는 다양한 측면에서 발생 가능한 것들을 고민해야 한다.
여러 모듈이 서로 영향을 주지 않도록 설계하기 위해서는 2~3개가량의 적은 메소드만을 제공하는 인터페이스부터 설계하며 연습을 해보면 좋다.
목적이 명확한 인터페이스를 설계하며 연습해보자.
인터페이스를 설계할 때는 어떤 정보를 공개하고, 어떤 정보를 감출지도 잘 정의해야 한다. ‘지금 나의 역할은 시스템을 설계하는 것이다. 개발은 다른 사람이 할 것이다.’라고 상상하면서 요구사항을 인터페이스 화 하면 좋다.(실제론 본인이 개발해야겠지만, 역할을 철저하게 분리하여 상상을 하는 게 좋다.) 역할을 분리하라는 이유는 설계할 때는 설계에만 집중하기 위함이다.
바퀴의 재발명
이미 존재하는 걸 다시 만들지 말라.
화물 숭배
내가 쓰는 코드가 무슨 코드인지 이해하고 쓰자.
개발자들이 코드 작성 중 가장 많이 사용하는 검색 사이트는 구글이고, 구글에는 항상 상단에 스택오버플로우의 링크가 걸려있다. 내가 붙여 넣고자 하는 코드가 무엇인지 알고, 이 코드가 왜 필요한지 생각을 한 후 상황에 맞게 변형이라도 하자.
예외 삼키기
예외는 예상외의 동작을 말한다. 프로그램이 예상외의 동작을 하면 적절히 대응해야 한다. 무시하면 안 된다.(return null로 때려박으면 안된다는 이야기..)
예외에 기반하여 작성하지 않기
예외는 예외로 처리해야 한다. 예외 구문을 조건 구문처럼 쓰지 말자.
상속 지옥
상속 자체가 안티 패턴이다. 매우 유의해서 써야 한다.
Gold plating
지나친 상상력은 접어두고 실제 명세에 있는걸 충실히 구현하자. 과도한 확장성을 고려한 코드는 불필요한 코드가 될 것이다.
그 외에 좋은 코드를 만들기 위해 읽어보면 좋을 책 혹은 자료
클린 코드(Clean Code), Robert C. Martin
종이책 GoF의 디자인 패턴 :재사용성을 지닌 객체지향 소프트웨어의 핵심 요소
디자인 패턴 자체를 신봉하지는 말자. 맹목적인 신봉은 화물 숭배와 다를 바 없으며, 남용하는 것은 좋지 않다.
자바 아키텍처 안티 패턴 관련 읽어볼 만한 자료: https://www.slideshare.net/gyumee/ss-55616001
대부분의 개발자가 wow 할 만한 코드를 작성하고 싶어 할 것이다.
클린 코드의 첫걸음은 좋은 코드에 대한 나와 팀의 노력이 우선시 되어야 한다.
내가 작성했던 코드를 지속적으로 리뷰하며 개선해 나가는가?
클린 코드는 한 번에 만들어지는 것이 아니다. 여러 번의 개선을 통해 만들어져 나가는 것이다.
회사라면, 일정이 존재할 것이고 코드 퀄리티에만 100% 집중할 수 없기에, 적절한 정도의 클리닝을 하며 개선해 나가자. '적절'한 정도를 찾기 힘들다면, 팀 내의 동료/선임 개발자들에게 본인의 코드를 보여주고 문제점이 무엇인지 조언을 구해보자.
개인적으로 연습할 수 있는 방법은 별도의 시간을 할애하여 개인 프로젝트를 진행하거나, Problem solving skills 문제들을 풀 때 코드 리팩토링 등이 있겠다.
나의 팀은 코드리뷰에 대해 긍정적인가?
나의 팀이 코드리뷰를 하고 있다면 클린 코드를 업무 내외로 만들어나가기 좋은 환경이다. 리뷰를 잘 듣고 반영해보자! 하지만, 팀 내에서 코드를 리뷰하는 문화가 없다면 동료/선임 개발자들에게 코드리뷰를 요청해보자. '코드의 1부터 100까지 모두 다 봐주세요!'는 그 사람의 업무도 있기 때문에 리뷰가 업무의 일환이 아닌 이상 받아들여지기 힘든 요구다.
코드리뷰는 작은 로직 단위부터 시작하는 것이 좋다. 트위터 하나를 읽는 것과 소설책 한 챕터를 읽는 느낌은 다르다. 본인이 요구하는 코드리뷰의 양이 많아진다면, 리뷰어는 제대로 읽지 않을지도 모른다. 가독성의 정도가 어떤지, n+1 쿼리나 루프는 없는지 등 본인이 개선하고 싶은 포인트를 선택해 리뷰를 요청하고 반영해보자.
코드리뷰는 분명 이점이 있다. 코드리뷰 전/후의 코드 퀄리티를 비교해볼 수 있겠고, 리뷰하면서 동료는 나의 코드를 백업할 수 있는 것 등의 많은 장점들이 있다. 리뷰를 하며 느낀 점들을 팀원들에게 지속적으로 어필하고 제안해 보자. 코드리뷰는 나를 성장시키는 시키는 방법 중에 가장 간단하면서도 강력한 방법이다.
남의 코드를 리뷰하고 개선에 도움 주는 것을 좋아하는가?
나의 코드를 리뷰받는 것도 좋지만, 남의 코드를 읽고 개선점을 찾아내는 것도 좋은 연습이 될 수 있다.
내 코드에 버그가 존재할 수 있는 것처럼, 다른 사람들의 코드에도 버그가 존재할 수 있다. 눈으로 보고 미리 발견하는 것이 생각보다 쉬우며, 보물찾기 같은 기분이 들지도 모른다. 그들이 무슨 생각을 가지고 있는지, 나라면 어떻게 풀었을지 고민을 할 수 있으며, 최종적으론 그 코드를 내가 유지 보수해야 할지도 모르기 때문에 미리미리 봐 두는 것도 좋다.