• 깔끔한 코드

    2023. 7. 26.

    by. 안녕진

    ⚠️ 저자의 개인적인 의견이 포함된 글입니다.
    독자분의 상황에 더 잘 맞는 비판적인 시선으로 읽어주시면 더 좋을 것 같습니다!

    ℹ️ ”클린코드” 책과, 웹/유튜브 자료, 저자의 경험을 바탕으로 작성한 글입니다.

    ℹ️ 충남대학교 SW아카데미 TA 뉴스레터 활동으로 작성한 글입니다.

    🤔 깔끔한 코드❓

    “깔끔한 코드를 작성해야 한다”라는 말.

    제가 코드 리뷰를 받으면서도 많이 들었던 말이고, 깔끔한 코드를 작성하고 싶어서 여러 책들을 읽곤 합니다.

    “깔끔하지 않은 코드”보다는 “깔끔한 코드”가 좋을 것 같다는 생각은 누구나 쉽게 떠올릴 수 있습니다.

    하지만 과연 코드가 깔끔하다는 것이 무엇을 의미하는 것이고, 깔끔한 코드를 작성하려면 어떻게 해야할까요?

    책들과 인터넷을 통해 접하고, 모바일 앱 개발자로 일을 하며 배웠던 내용들을 정리해봤습니다.



    어떤 코드가 깔끔한 코드일까요❓

    사실, “어떤 코드가 깔끔한가”라는 질문에 대한 답을 하기는 꽤나 부담되는 일입니다.

    누군가 사전에 깔끔한 코드가 무엇인지 정의해놓은 것도 아니고, 사람/상황마다 깔끔한 코드의 정의는 변할 수 있다고 생각하기 때문입니다.

    부담이 되는 행동임에도 불구하고 이 물음에 대한 답을 내려는 이유는, “깔끔하다”라는 용어를 사용해서 나타내려는 “이상적인 코드”가 명확히 정의되어야 그대로 코드를 작성할 수 있기 때문입니다.

    제 경험과 다양한 자료를 바탕으로 내린 답은 “깔끔한 코드는 쉽게 이해되는 코드이다.” 입니다.

    팀에 속해서 코드를 작성할 때는 물론이고,
    심지어 혼자 코딩할 때도 쉽게 이해되는 코드는 도움이 됩니다.

    한 달 전에 작성한 코드를 읽어보신 적이 있으신가요?

    저는 제가 개발했던 기능을 리팩토링하느라 자주 읽어보곤 합니다.

    그럴 때마다 코드가 너무 새롭고, 심지어 제가 작성한 코드인지도 헷갈려서 git blame으로 확인해서 기억난 적도 있습니다.

    따라서 한 달 뒤의 “나”를 위해서도 쉽게 이해되는 코드를 작성하면 편합니다.

    앞으로 평생 기존 기능 수정 없이 기능 추가만 하는 프로그램을 만든다고 하더라도, 쉽게 이해되는 코드는 도움이 됩니다.

    기능을 추가한다면 코드만 계속 작성할 것 같지만,
    프로그램의 의도치 않은 동작 오류를 피하기 위해 기존 코드를 읽고 이해하는 시간이 생각보다 많이 소요되기 때문입니다.



    쉽게 이해되지 않는 코드 → 깔끔한 코드🧑‍💻

    쉽게 이해되는 코드를 작성하기 위해, 쉽게 이해되지 않는 코드의 특징을 생각해봤습니다.

    제가 자주 들은 리뷰 내용들이 포함되어 마음이 아픕니다.

    ⚠️ 특징을 부각하기 위해 과장된 면이 있습니다.

    변수/함수명이 이해되지 않는 코드

    def get_circle(r):
        a = 3.14
        b = a * (r**2)
        return b

    위 코드는 원의 면적을 구하는 코드입니다.

    3.14라는 숫자를 보고 유추할 수는 있겠지만, 명확하지 않습니다.

    또한, 함수를 사용하는 부분에서 봤을 때는 get_circle이 원 "객체"를 반환하는 함수인지 원의 면적을 반환하는 함수인지 함수 몸체를 읽어보지 않고는 알기 어렵습니다.

    def get_circle_area(radius: float) -> float:
        pi = 3.14
        area = pi * (radius ** 2)
        return area

    위와 같이 수정해봤습니다. 입력/반환 타입이 추가된 모습입니다.

    타입 정보는 타입 에러를 막는 역할 뿐만 아니라 그 자체로도 변수/함수에 관한 정보인 셈이기 때문에 추가했습니다.

    pi, area, radius와 같이 줄이지 않고 단어를 그대로 표현했습니다.

    짧은 코드를 작성하는 것이 좋다고 생각할 수도 있지만,
    아무리 긴 이름을 사용해도 IDE가 자동 완성을 해주기 때문에 조금 더 길더라도 의미가 드러나는 이름을 사용하는 것이 좋다고 생각합니다.


    로직 자체가 너무 복잡한 코드

    def check_validity(nums):
        return 0 in [nums[i]-nums[i-1] for i in range(1,len(nums))]
    
    print(check_validity([1,2,3,2,3,4,1]))

    위 코드는 숫자 리스트가 주어졌을 때,
    같은 숫자가 2번 이상 반복되면 True 그렇지 않으면 False를 반환합니다.

    우선 위 코드는 pythonic한 문법이 있어서 python에 익숙하지 않다면 읽기 어려울 수 있습니다.

    하지만 그 점을 제외하더라도,
    i는 1에서 n까지 일 때, i번째숫자와 i-1번째숫자의 차를 구하여 0이 존재하면 False, 존재하지 않으면 True를 반환하겠다는 것은 이미 그 자체로 로직이 복잡합니다.

    def has_repeating_numbers(nums):
        for i in range(1, len(nums)):
            if nums[i] == nums[i - 1]:
                return True
        return False
    
    print(has_repeating_numbers([1,2,3,2,3,4,1]))

    위와 같이 수정할 수 있습니다.

    코드 길이는 늘어나긴 했지만, 인접한 숫자를 비교하겠다는 의미가 더 잘 드러난다고 생각됩니다.

    $a = b$와 $a - b = 0$의 차이입니다.

    이런 간단 예제 외에도 훨씬 복잡하게 로직이 얽혀있는 경우가 생길 수 있습니다.

    코드를 작성하기 전에 어떻게 해야 할지 막막한 상황도 있습니다.

    그런 문제가 발생하는 이유는 머릿속에 로직이 잘 정돈되지 않았기 때문이라고 생각합니다.

    그런 상황에 제 해결 방식은 아래와 같습니다.

    1. 목표 동작을 수행하는 코드를 작성하기 위해, 코드가 어떤 단계로 이루어져야할지 생각합니다.
    2. 타인에게 설명하듯, 마인드맵을 그리거나 코드 한쪽에 주석으로 정리해서 쉽게 잘 설명이 될 때까지 수정합니다.

    스크린샷 2023-07-26 오전 12.04.32.png

    위 사진은 현재 만들고 있는 앱의 한 기능을 추가하다가 로직을 고민해본 마인드맵입니다.

    어떤 방법이 좋은 고민 방법인지 아직도 저는 모르겠지만,
    코드를 키보드로 작성하기 전에 충분히 고민하는 과정은 꼭 필요하다고 생각합니다.


    매직 넘버의 사용

    def calculate_price(quantity, price):
        return quantity * price * 1.20

    1.20같이 코드 내에 직접적으로 삽입된 리터럴 같은 것을 매직 넘버라고 합니다.

    가격을 계산하는 데 곱해지는 1.2가 왜 곱해지는지 나타나지 않습니다.

    물론 주석을 작성할 수도 있겠지만, 애초에 코드에 사용된 것들이 제대로 의미를 나타내준다면 더 좋겠죠.

    TAX_RATE = 1.20
    
    def calculate_price_with_tax(quantity, price):
        return quantity * price * TAX_RATE

    이렇게 리터럴에 이름을 붙여 사용하여, 1.20은 세율이었다는 것을 알 수 있습니다.

    함수 이름을 통해서도 세율이 곱해진 가격을 계산하는 함수임을 알 수 있습니다.


    함수가 너무 길어서 눈에 잘 안 들어오는 코드

    def calculate_statistics(data):
        sum_val = 0
        for d in data:
            sum_val += d
    
        mean = sum_val / len(data)
    
        sum_diff_sq = 0
        for d in data:
            sum_diff_sq += (d - mean) ** 2
    
        variance = sum_diff_sq / len(data)
    
        min_val = min(data)
        max_val = max(data)
    
        return mean, variance, min_val, max_val

    평균, 분산, 최소값, 최대값을 구하는 함수입니다.

    복잡한 로직은 없어서 순서대로 읽으면 잘 읽히긴 합니다.

    하지만 함수 몸체가 너무 길기 때문에, 함수의 역할을 정확히 이해하기 위해서 기억해야할 것이 많습니다.

    예를 들어 함수 초반에 사용된 변수가 함수 후반 부분에서도 사용되어야 한다면,
    그동안 그 변수가 변하는지, 변한다면 어떤 값을 갖게 되는지 기억하며 함수를 읽어야 합니다.

    따라서, 함수를 짧은 길이로 쪼개는 것은 기억할 내용을 줄여주는 탁월한 방법입니다.

    def calculate_mean(data):
        sum_val = sum(data)
        return sum_val / len(data)
    
    def calculate_variance(data, mean):
        sum_diff_sq = sum((d - mean) ** 2 for d in data)
        return sum_diff_sq / len(data)
    
    def calculate_min(data):
        return min(data)
    
    def calculate_max(data):
        return max(data)
    
    def calculate_statistics(data):
        mean = calculate_mean(data)
        variance = calculate_variance(data, mean)
        min_val = calculate_min(data)
        max_val = calculate_max(data)
    
        return mean, variance, min_val, max_val

    위와같이 함수를 여럿으로 분리할 수 있습니다.

    만약 더 효율적인 최대값 구하는 방법을 사용하게 된다면, 다른 부분에는 영향을 주지 않고 최대값을 반환하는 함수만 수정하고, 개별적으로 테스트할 수 있습니다.

    또한, 함수를 분리하려면 함수의 몸체에 사용된 코드가 정리되어야 합니다.

    한 변수가 이곳 저곳에서 사용되어 추적하기 어렵다면, 함수를 분리하기 또한 어렵겠죠.

    따라서 함수를 분리하는 과정에 자연스럽게 로직을 정돈하는 과정을 거치게 되기 때문에,
    함수를 짧게 분리하는 것은 좋은 습관이라고 생각합니다.



    추가 내용들

    위에서 다룬 깔끔한 코드를 작성하는 방법은 다음과 같습니다.

    • 이해가 되는 변수명 짓기
    • 간단한 로직 구성하기
    • 매직넘버 사용을 지양하기
    • 함수 몸체를 짧게 만들기

    추가적으로, 깔끔한 코드 작성을 위한 내용들을 더 소개하고 글을 마무리 하려 합니다.

    Style guide 따르기

    언어별로 “{언어} Style guide”라고 구글에 검색하면 많은 자료들이 나옵니다.

    이름 그대로, 코딩하는 스타일에 대한 가이드입니다.

    문법적으로 옳은 코드일지라도, 스타일 가이드에는 맞지 않을 수 있습니다.

    선호에 따라서, 따를 수도 있고 따르지 않아도 됩니다.

    꼭 스타일 가이드에 있는 그대로 따라하지 않더라도, 본인만의 (혹은 소속 팀이 지키는) 스타일이 존재한다면 개발 과정에서 도움이 될 수 있습니다.

    스타일을 따르게 되면, 익숙한 방식으로 작성되기 때문에 읽기 편합니다.


    코드 리뷰 받기

    혼자만의 틀에 갇혀서 아무리 개선을 해도 나아지지 않는 경우도 많습니다.

    그럴 때는 팀 멤버들에게 코드 리뷰를 받아보는 것이 많은 도움이 됩니다.

    리뷰어가 읽으며 코드를 이해하기 어려운 부분에 코멘트를 달아줄 것이고,
    그렇게 수정을 거치다보면 더 이해하기 쉬운 코드로 발전할 수 있습니다.



    참고하면 좋을 내용들

    설명 링크
    깔끔하지 않은 코드에서 풍기는 악취(smell)를 다루는 내용이 소개되었습니다. 이 외에도, 각종 design pattern을 소개하니 천천히 읽어보시면 많은 도움이 될 겁니다. https://refactoring.guru/refactoring/smells
    개발을 접해보신 분이라면 아마 이 “클린코드” 라는 책은 많은 분들이 들어보셨거나 읽어보셨을 겁니다. 전달해주는 내용이 꽤나 많아서, 한번에 모든 내용을 외우겠다는 것보다는, 두고두고 읽으며 참고하면 좋을 것 같습니다. https://www.yes24.com/Product/Goods/11681152
    SOLID 원칙. 유지보수와 확장이 쉬운 시스템을 설계하기 위한 객체지향 원칙들입니다. 어떤 문제가 발생할 수 있고, 어떻게 해결하는지 아이디어를 배우면 좋을 것 같습니다. https://ko.wikipedia.org/wiki/SOLID_(%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%EC%84%A4%EA%B3%84)

    '잡담, 생각' 카테고리의 다른 글

    엄청난 블로그 작성 툴 발견  (0) 2024.03.01
    사망년 1학기  (6) 2023.06.19
    무의식의 루틴에서 의식적인 선택으로  (6) 2023.03.21

    댓글