들어가며

좋은 코드란 무엇일까요? 이제 막 개발자로써 첫발을 내딛은 초심자부터 오랫동안 몸담아온 베테랑까지, 좋은 코드를 향한 열망과 고민은 개발자로 존재하는 이상 결코 멈출 수 없는 숙명이 아닐까 합니다. 세상 만사가 그렇듯이 코드 역시 무언가의 '좋음'을 판단하는 기준은 시기와 상황, 혹은 따르는 방법론에 따라 다양하게 정의될 것입니다. 하지만 이 글에서는 개발의 세계에 입문한 지 얼마 되지 않은 신입 개발자의 눈높이에서 바라보는 좋은 코드로 논의의 범주를 좁혀보고자 합니다. 진짜배기 초심자는 자신이 무엇을 모르는지도 몰라 나아갈 길을 잃고는 하죠. 이 글에서 다루고자 하는 좋은 코드를 향한 고민이 저와, 저와 같은 상황에 놓인 초급 개발자들에게 앞으로의 코딩생활을 관통하는 기본적인 지침이 될 수 있기를 바라봅니다.

좋은 코드

막 코딩, 개발을 접한 저-와 많은 분들-이 다들 같은 출발선상에 섰으리라 생각합니다.

"내 눈앞에 놓인 이 복잡시런 물건을 일단 돌아가게 만들어야 한다...!"

폭포처럼 쏟아지는 새로운 개념과 기능들의 물결 속에서, 손에 든 최첨단 도구를 다룰 줄 몰라 우왕좌왕하며 어떻게든 '돌아는 가게' 만드는 것을 지상목표로 삼고 전진하던 나날들을 모두 겪으셨으리라 믿습니다. '일단 돌아가는 코드가 좋은 코드다. 안 돌아가는 코드는 당최 쓸모가 없으니까.' 라고 생각하며 콘솔에 내가 원하는 값 하나를 찍어내기 위해 전력질주하던 나날, 기억 나시나요? 그 때의 좋은 코드는 곧 돌아가는, 어쨌든 결과물이 나오는 코드였습니다. 촉박한 시간과 일정에 쫓기며 해당 명제에 별로 이의를 제기해 보지 않으신 분도 계실 것이라 생각합니다. 완전히 틀린 말은 아니니까요. 작동하지 않는 코드뭉치가 좋은 코드일 수는 없겠죠.

하지만 그렇게 if문 복붙과 이중 for문을 신나게 돌리던 어느날 문득 생각해보게 됩니다. 이게 맞나?

혹은 짬을 내 개인 포트폴리오 정리를 하다가 본인의 옛날 깃헙 리포지토리를 들어가보았을 수도 있겠습니다. 이거...나 뭐 한 거더라?

좋은 코드를 향한 새로운 고민, 즉 발상의 전환은 여기에서부터 출발한다고 생각합니다.
그간 당연하게, 익숙하게 생각하던 것에서 무언가 위화감을 느꼈을 때, 사람은 그 원인을 파악하고 해결, 즉 개선하고자 합니다.

내가 프로그래머인데, 그래도 개발자인데... 똑같은 문장을 안에 들어가는 인자만 바꿔가며 몇번이고 복사-붙여넣기 하는 건 이상하다.

전부 내가 짠 코드인데... 이게 뭘 하는 내용인지 감이 안 와서 한줄 한줄 뜯어봐야 하는 것이 말이 되나?

일단 이상함을 느껴버린 이상, 더이상 기능이 굴러가기만 하는 코드는 '좋은 코드'일 수가 없게 되었습니다.

그렇다면 무엇이 새로운 '좋은 코드'일까요?

여기서 우리는 어깨너머로만 듣던, 당장은 내 일이 아니라고 여겼던 Clean Code, 명료한 코드라는 개념을 마주치게 됩니다.

클린 코드가 무엇인지 저명한 개발자들의 정의를 짧게나마 인용해보자면,

"Clean code does one thing well.” 
클린 코드는 하나의 일을 잘 하는 코드이다.
- Bjarne Stroustrup, inventor of ‘C++

“Clean code is simple and direct." 
클린 코드는 간결하며, 직접적이다.
- Grady Booch, author of ‘Object-Oriented Analysis and Design with Applications’

간추리자면 클린 코드란 단순하여 읽기 쉽고, 각 역할마다 주어진 하나의 일만 담당하며, 따라서 복잡하거나 모호하지 않은 코드라고 할 수 있겠습니다.
다르게 말하면 원하는 로직을 빠르게 찾을 수 있는 코드, 모든 팀원이 이해하기 쉽도록 작성된 코드[1]입니다. 즉, "가독성이 좋은"코드[2]인 것입니다.

그렇다면 코드의 가독성은 왜 중요할까요? 신입 개발자가 맨땅에서부터 코드를 쌓아올려가는 경우는 잘 없습니다. 그것이 유지보수이든, 리팩토링이든 기존의 레퍼런스가 있게 마련입니다. 처음부터 끝까지 나홀로 개발이 아닌 이상 개발자라는 직업 자체가 늘 남이 작성한 코드를 읽어내고 해석해야 합니다. 코드의 가독성이 떨어진다면 이 코드가 무슨 일을 하는 코드인지, 어떤 메커니즘으로 굴러가는지 파악하는 데 오랜 시간이 소요되겠죠. 또 곳곳에 숨어있는 오류를 찾아내는 것도 쉽지 않을 것입니다.

복사-붙여넣기 한 5연속 if문 블록에서 조건을 바꾸다 실수로 오타를 냈다면, 이걸 찾아내 고치기 위해 얼마나 오랫동안 눈이 빠져라 IDE를 쳐다봐야 할까요? 바꿔 말하면 코드의 가독성이 올라가면 코드 파악, 리뷰, 디버깅에 드는 시간이 단축된다 는 것입니다.

깔끔한 코드는 구성원들간의 의사소통을 돕고, 유지보수하기도 편리하며, 팀의 생산성도 높여줍니다. 클린 코드를 추구하지 않을 이유가 없겠죠?

클린 코드를 작성하는 법에 대해서는 로버트 C. 마틴의 명저 Clean Code[3]를 위시하여 여러 제안을 찾아볼 수 있었습니다. 그 중에서 초보 개발자가 이해하고, 기억해두었다 적용해보려 시도할 수 있을 만한 내용을 골라 제시해보고자 합니다. 듣고 보면 그냥 당연한 거 아닌가? 싶을 수도 있지만, 그 당연함이 당연한 것인 줄 모르는 게 초보자이니까요.

클린 코드 작성하는 법

  1. UML을 통해 명확히 필요한 요소와 기능을 적시한다

    UML이란 Unified Modeling Language의 약자로, 언어라고 되어 있지만 언어보다는 다이어그램입니다. 코딩을 시작하기 전, 프로젝트 혹은 아이디어를 다이어그램으로 표현해 필요한 요소와 기능들을 누구나 알아보기 쉽도록 정리해주는 것이 UML의 역할입니다.[4] UML을 통하여 소통하면 같은 프로젝트를 서로 다르게 이해하는 난감한 상황이 줄어듭니다. 통일된 모델링 언어라는 이름답게, 같은 프로젝트를 같게 이해하도록 도와주는 것이 UML이지요. 아마 신입 개발자라면 UML을 직접 그리기보다는 보고 이해해야 할 경우가 더 많을 것입니다. 냅다 코딩부터 시작하지 말고, UML을 찬찬히 살펴보도록 합시다.


  2. 함수는 특정한 동작을 수행하므로 동사로, 클래스나 속성의 이름은 명사로 짓자

    클래스나 속성이 어떠한 대상을 나타낸다면, 함수(메소드)는 그 대상을 받아 특정한 동작을 수행한다고 할 수 있습니다. 이를 나타내기 위해 클래스나 속성은 명사형으로, 함수는 동사형으로 이름을 붙여준다면 따로 부연설명하거나 코드를 전부 읽어보지 않아도 한눈에 이건 어떠한 대상이구나, 이건 그 대상으로 무언가를 하는구나를 바로 알 수 있습니다. 예를 들어,

    public class Account {
         //
         private int amount;
    
         public static int transferMoney(int money){
             amount -= money;
         };
    }
    
    Account.transferMoney(1000);
    

    위 코드처럼 작성할 경우, 주석을 달지 않아도 계좌에 잔고가 들어있고, 계좌에서 송금을 1000원 하는구나~ 하고 자연스럽게 읽혀집니다.


  3. 변수는 어떤 것을 위한 값인지를 분명히 나타낼 수 있게 이름지어야 한다

    코딩을 하다 보면 여러 가지 이유로 변수명을 줄이거나 알파벳으로 대체하고픈 유혹에 빠지게 됩니다. 하지만 변수의 이름은 무엇을 나타내는지 명확히 서술해주는 것이 좋습니다. 위의 코드 예제에서 잔고 amount를 약어 am, 혹은 그냥 a로 바꾼다면 어떻게 될까요?

    public class Account {
         //
         private int am;
    
         public static int transferMoney(int m){
             am -= m;
         };
    }
    
    Account.transferMoney(1000);
    

    이 코드를 읽는 다른 사람(혹은 미래의 나)는 am과 m이 무엇을 뜻하는지 코드 흐름을 전부 찾아보며 추측해야 할 것입니다. 바로 알 수 있다고 하더라도, 머리에 am은 amount, m은 money라고 한번 더 입력해야 합니다. 불필요하게 생각의 단계가 하나 더 늘고, 가독성이 심각하게 떨어지겠죠. amount라고 밝혀 적어주면 문제가 해결됩니다.


  4. camelCase를 사용하자

    카멜 케이스는 중간의 대문자가 낙타의 봉 같아서 붙여진 이름으로, 단어 여러 개를 결합할 때 뒤에 오는 단어의 첫 자를 대문자로 쓰는 표기법입니다. 사실 위의 코드에서 이미 한번 사용했지요.

    transfermoney()
    transferMoney()

    카멜 케이스를 사용하지 않은 메소드명보다, 사용한 메소드명이 훨씬 transfer와 money가 구분되어 눈에 잘 들어오는 것을 느낄 수 있을 것입니다. 일견 사소하다 생각할 수 있는 부분이지만, 몇백 줄이 넘는 코드에서는 사소함이 큰 차이를 만들기도 합니다.


  5. 함수 작성 요령

    함수를 작성할 때, 돌아가기만 하면 되지! 혹은 이 메소드가 이것도 한꺼번에 처리하면 좋지 않을까?라고 생각하며 모든 기능을 메소드 하나에 때려박고 계시지는 않으신가요? 하지만 가독성재사용성을 고려할 때, 함수는 간단할수록 좋고, 하나의 함수가 하나의 일만 수행하는 것이 좋습니다. 저는 A 메소드가 B 기능도 수행하고, C 기능도 수행했으면 좋겠는데요? 라고 생각하신다면 B와 C를 별도의 메소드로 분리해 주세요. A 안에서 B와 C 메소드를 호출하면 됩니다. 좋은 함수 작성 요령은 다음과 같습니다.

    • 작게 만들기
      • 함수 안의 줄 수는 적을수록 좋다.
      • 들여쓰기 레벨도 최대 2단까지만 있는 것이 가장 바람직하다.
    • 한 가지만 하기
      • 한 메서드는 하나의 동작에만 관련이 있어야 한다.
      • 만약 한 메서드에 다른 것들을 동시에 처리를 한다면, 기능적으로 다른 메소드들로 나눌 것을 고려해야 한다.
      • 1 메서드 1 기능으로 만들어준다면, 더 이해하기 쉽고 다른 메소드에서 재사용하기도 편리하다.
      • 의미 있는 이름으로 함수를 추출할 수 있다면 한 가지의 일만 하고 있는 것이다.
    • 함수 인수 최대한 적게 쓰기
      • 가장 좋은 경우는 인수가 없는 것, 다음에는 단항 함수(인수가 1개)이다.
      • 입력값이 있는 변환 함수는 반환 값이 있는 것이 좋다.
      • 인수가 많이 필요하다면 독자적인 클래스 생성을 고려해보는 것도 좋은 방법이다.
    • 명령과 조회 분리하기
      • 함수는 무언가를 수행하거나 답하거나 둘 중 하나만 해야 한다.[5]

  6. 주석을 최소화하자

    잘못된 코드는 빠르게 수정되지만, 잘못된 주석은 잘 수정되지 않습니다. 오래된 주석, 혹은 주석처리된 코드는 이 코드를 읽는 다른 사람에게 혼란을 부여합니다. 나중에 쓸 코드인가? 언제 작성한 코드인가? 지금 코드에 달린 주석이 맞나? 이전 버젼인건가? 지워도 되나?

    따라서 TODO / 외적인 맥락 / 제한사항과 같이 코드로 설명할 수 없는 부문만 주석으로 설명하고, 가능하면 코드 자체가 스스로를 설명할 수 있게끔(Self-Descriptive) 작성합니다.[6] 더이상 쓰지 않는 코드는 주석처리하지 말고 깔끔하게 지워 줍시다.


  7. null을 리턴하거나, 인수로 받지 말자

    어떤 함수가 null을 리턴한다는 것은, 마치 함수에게 무언가를 물어봤는데 '없는데 어쩌라고' 를 돌려주는 것과 비슷합니다. 배째라식 코딩은 좀 아닌 것 같죠? 이럴 땐 예외를 던지거나, null값에 대응하는 다른 객체를 돌려줍시다.

    또, 우리가 사용하는 대부분의 언어는 인수로 들어온 null을 적절하게 처리하지 못합니다.[7] 믿고 있었는데... 냅다 NullPointerException을 띄우며 뻗어버리는 프로그램을 보면 마음도 머리도 아플 것입니다. 뜨는 오류는 잡는다 쳐도 적어도 개발자가 의도적으로 null을 넘기지는 말도록 합시다.

이 외에도 클린 코드를 위한 많은 규칙들과 제안사항이 있지만, 적어도 이 대여섯가지만 적용해 보더라도 이전과는 훨씬 달라진 내 코드를 마주할 수 있을 것입니다.

마치며

이것이 좋은 코드다! 이것이 정답이다! 라고 딱 잘라 말하기에 개발의 세계는 너무도 넓고 깊습니다. 하지만 비록 신입이어도 개발자로 발을 내딛은 이상, 이제 단순히 기능상 작동하는 코드를 넘어 앞으로 어떤 코드를 작성해야 할 지, 어떤 방향이 추구할 만한 방향인지 같이 고민해 보면 좋지 않을까요? 이 글이 저와, 또 저와 같은 길을 걸어갈 동료들에게 미래를 향한 생각의 단초나 계기가 될 수 있었으면 하는 바람입니다. 우리 모두 멋진 개발자가 되는 그날까지 서로에게 열렬한 응원을 보냅니다. 감사합니다.

binary

참조


  1. https://talkwithcode.tistory.com/73 ↩︎

  2. https://www.samsungsds.com/kr/insights/cleancode-0823.html ↩︎

  3. https://g.co/kgs/94nSvS ↩︎

  4. https://ko-de-dev-green.tistory.com/19 ↩︎

  5. https://tasddc.tistory.com/84 ↩︎

  6. https://medium.com/naver-cloud-platform/네이버클라우드-개발자-스토리-좋은-코드란-무엇일까-클린코드-이야기-c7811f73a46b ↩︎

  7. https://github.com/Yooii-Studios/Clean-Code/blob/master/Chapter 07 - 에러 핸들링.md ↩︎

https://www.youtube.com/watch?v=QK-PEVI7eDk&t=65s