POST

All
Product
Team
Tech
DocVLM: Make Your VLM an Efficient Reader
  • 최윤진
  1. Tech
Python 3.10 신규 문법 : Parenthesized context managers와 PEG Parser
  • S
    seunghoChoe
  1. Tech
UReader: Universal OCR-free Visually-situated Language Understanding with Multimodal Large Language Model
  • 최윤진
  1. Tech
[팀 소개편] KPMG Lighthouse는 어떤 팀인가요?
  • L
    Lighthouse
  1. Team
[챕터 소개편] Backend Chapter를 소개합니다
  • L
    Lighthouse
  1. Team
[챕터 소개편]Frontend Chapter를 소개합니다
  • L
    Lighthouse
  1. Team
[챕터 소개편] AI Chapter를 소개합니다
  • L
    Lighthouse
  1. Team

Clean Code 깊게파보기

Created by
  • L
    Lighthouse
Created at
Category
  1. Tech

개요

개발을 하면서 과거에 작업했던 코드를 다시 봐야 하는 상황이 종종 있다. 과거에 깨끗하지 않게 코드를 작성했다면, 코드를 다시 이해하고 원인을 찾는 부분에서 시간을 소요 할 가능성이 높다.
특히, 서비스 중인 프로그램에서 버그가 발생해 빠르게 이슈 대응을 해야 한다면, 머리가 새하얘질 것 이다.
또한, 이런 경험을 한 적이 있을 것이다.
시간이 부족해서 빠르게 개발하고, ‘아.. 나중에 리팩토링 해야겠다.’ 라고 생각한 경우도 있을 것이다. 하지만, 리팩토링을 하는 Task를 따로 추가하는 경우는 매우 희박한 경험들이 있을 것이다.
이런 경험을 하면서 개발 초기 부터 이해가 잘되는 코드를 만들기 위한 방법을 찾아 보게 되었고, 클린 코드에 대해 알게 되면서 학습을 진행했다.
클린 코드를 알기 전 개발에 급급해서 만들었던 과거의 코드

클린 코드란?

한 개발자는 클린 코드에 대해 다음과 같이 말했다.
그래디 부치(Grady Booch)
클린 코드는 단순하고 직접적이다. 클린 코드는 잘 쓴 문장처럼 읽힌다. 클린 코드는 설계자의 의도를 숨기지 않는다. 오히려 명쾌한 추상화와 단순한 제어문으로 가득하다."
이 외에도 여러 개발자들의 말을 정리하면 다음과 같다.
다른 사람이 읽기 쉬운 코드
유지 보수가 쉬운 코드
간단 명료하고, 의도가 분명한 코드
클린 코드에 대해 많은 말들이 있지만 개인적으로 클린코드의 핵심은
라고 생각 된다.
“누구에게나” 라고 생각한 이유는 프로젝트는 팀원들과 같이 진행을 하기 때문에 내가 작성한 코드를 팀원들이 읽기 때문이다.
클린 코드를 실천하면 개인의 생산성을 높이는 것 외에도 팀의 생산성을 높이는데 기여를 한다고 생각된다.
클린 코드가 무엇 인지 알았으면 이제는 클린코드를 실천하는 방법에 대해 알아볼 차례이다.
이 번 글에서는 Python 에서 클린 코드를 적용하는 방법에 대해 알아볼 것이다.
이해하기 쉬운 이름 (변수 명)
코드 악취(Code Smell) 감지와 대응

이해하기 쉬운 이름

PEP 8의 명명 규약

PEP 8 문서에는 파이썬 명명 규약과 관련된 권장 사항이 몇 가지가 존재한다.
모든 문자는 아스키 문자여야 한다.
강세 표시 부호가 없는 영문 대소문자로 표기해야 한다.
모듈 이름은 짧아야 하며 모두 소문자로 표기한다.
클래스 이름은 파스칼(SomeClassName) 표기법으로 작성한다.
상수 변수는 대문자 스네이크(SOME_API_KEY) 표기법으로 작성한다.
함수, 메소드, 변수 이름은 소문자 스네이크(find_user_id()) 표기법으로 적어야 한다.
메소드에 대한 첫 번째는 인수는 항상 소문자로 self라고 이름을 붙여야 한다.
클래스 메소드에 대한 첫 번째 인수는 항상 소문자로 cls 라고 이름을 붙여야 한다.
클래스의 프라이빗 속성은 항상 _ 로 시작해야 한다.
클래스의 퍼블릭 속성은 절대로 _ 로 시작해서는 안된다.
class SomeClassName: def __init__(self, user_id): self._user_id = user_id @classmethod def from_user_id(cls, user_id): return cls(user_id) def _private_method(): return def public_method(): return
PEP 8에는 프로그래머가 해당 지침을 엄격하게 따를 필요는 없다고 한다. 예를 들어 변수명에 스네이크 표기법이 아닌 카멜 케이스(camelCase)를 사용해도 된다는 얘기다. 하지만 중요한 점은 가독성을 위해 표기법에 대한 일관성이 필요하다.

이름의 적정 길이

변수명은 “적당한” 길이로 만들어야 한다. 너무 길다면 입력하기 귀찮고, 너무 짧다면 변수의 의미를 혼동할 수가 있다.
하지만, 코드를 쓰는 것 보다 보통은 읽는 경우가 많기 때문에 피할 수 없다면 긴 이름을 선택하여 의미 혼동을 적게 하는 것이 더 바람직하다.
너무 짧은 이름
변수명을 너무 짧게 작성한다면 처음에는 적절한 단어로 보일 수 있지만, 나중에 코드를 확인하면 처음에 생각했던 의미를 잊어 버릴 수가 있다.
개발자가 자주 사용하는 짧은 변수 명(예시)
한두 글자로 된 이름
number의 축약으로 n을 적고 싶은 경우가 있을 것이다. 하지만 n으로 시작하는 단어가 많아 다른 개발자가 봤을 때, 의미를 바로 파악하지 못하는 경우가 발생할 수 있다.
또한 IDE에서 변수 명 찾기를 했을 때, n이 포함된 단어들이 나와 찾고 싶은 변수의 위치를 찾는데 방해가 될 확률이 높다.
줄임말
month, monitor, monster의 의미로 mon으로 축약하는 경우가 있다. 여러 단어를 의미 할 수가 있어 이런 표기는 피하는 것 이 좋다.
과거에는 단어들을 축약해서 변수명을 만드는 스타일이 유행했다.
memcpy, strcmp
하지만 읽기 힘든 스타일이기 때문에 오늘날에는 사용하지 않는 것이 좋다.
start와 같은 한 단어 이름도 모호할 수 있다. “무엇의 시작인가?” 이렇게 통상적으로 사용되는 단어의 경우 코드의 맥락을 확인 하기 위해 한번 더 생각해야 하는 문제가 (시간 소요) 발생할 수 있다.
하지만, 한 글자로 변수명을 사용해도 되는 예외적인 상황도 있다.
for i in range(10): for j in range(3): print(i, j) # index의 줄임말인 i를 사용하고 i이후의 알파벳인 j를 중첩된 반복문에서 사용이 되기도 한다. # i,j 외에도 좌표를 다루는 코드에서는 x, y를 사용하는 경우도 있다. for _ in range(10): some_func() # 파이썬에서는 _를 사용하여 의미 없는 변수(무시하는 값)를 사용하는 경우도 있다.
너무 긴 이름
일반적으로, 이름은 쓰이는 범위가 넓을 수록 더 설명적인 이름을 사용 해야 한다.
짧은 함수나 코드에서 start를 사용한다면 의미가 모호해지는 경우가 아니라면 사용을 해도 괜찮다. 하지만 전역변수로 start를 사용한다면 추가적인 설명이 포함된 변수명으로 만들어야 할 것이다.
긴 변수 명에 대한 지침
이름에 붙이는 접두사
오래된 코딩 방식 중 하나인 헝가리식 표기법이 있다. 헝가리식 표기법은 변수 앞에 접두사로 데이터 타입을 적는 방식이다. list_indexes 와 같이 index들이 List 데이터 타입으로 들어있다는 것을 의미한다.
요즘의 IDE의 경우 데이터 타입이 무엇인지 나오기 때문에 접두사 없이도 파악할 수 있다.
이름의 순차적 숫자 접미사
변수명이 payment1, payment2 … 등의 숫자가 붙어 있다면 변경해야한다.
이런 변수들이 있다면, 튜플이나 리스트로 담아서 사용하는 것을 권장한다.
함수 명이 find_payment1(), find_payment2() 등으로 되어있다면
find_payment(1), find_payment(2) 등으로 인수로 받는 함수로 만들거나 payment의 의미를 조금더 세분화 하여 만들어야한다. find_1st_quarter_payment(), find_2nd_quarter_pament()
순차적 접미사가 있는 이름을 선택한 이유가 타당하다면 사용해도 좋다. 하지만, 그냥 만들기 쉽다는 의미로 만들었다면 수정할 필요가 있다.

검색 가능한 이름 짓기

앞서 말했듯이 IDE에서의 검색기능을 활용하기 위해서는 긴 변수명을 사용하여 고유한 이름을 만들 필요가 있다.
또한, 최근의 IDE의 경우 전역변수, 지역변수 등을 구분하여 rename을 편리하게 할 수 있는 기능들이 존재한다. 하지만 항상 이런 IDE들을 사용하는 경우는 없기 때문에 최악의 상황을 생각해서 고유의 변수명을 만들어 주도록 해야한다.

농담, 말장난, 문화적 참조는 피하자

프로그램에서 이름을 선택할 때 농담이나 말장난 등을 사용하여 코드의 경쾌함을 넣고 싶어하는 개발자가 있다.
하지만, 이런 방식으로 이름을 넣는 다면, 후임 개발자나 농담을 모르는 개발자는 오타로 착각하여 버그 신고를 할 가능성이 존재한다.
항상 이름을 만들 때는 영어권 사용자가 아닌 사람들도 쉽게 이해를 할 수 있도록 직설적이고, 유머가 없는 이름으로 작성해야 한다.

코드 악취 감지와 대응

냄새로 가스 누출을 알아채거나 화재를 감지할 수 있는 것처럼 코드 악취(Code Smell)는 잠재적인 버그를 암시하는 소스 코드 패턴 입니다.
코드 악취가 있다고 해서 반드시 문제가 생기는 것은 아니지만, 적어도 프로그램을 조사할 필요가 있습니다.
코드 악취가 될 수 있는 항목은 다음과 같습니다.
중복된 코드
매직 넘버
주석 처리된 코드와 죽은 코드
숫자 접미사가 붙은 변수
그냥 함수나 모듈이어야 하는 클래스
중첩된 리스트 컴프리헨션
빈 예외처리 블록과 부실한 에러 메세지

중복된 코드

가장 흔한 코드 악취는 중복된 코드
모든 코드 악취가 그렇듯이, 꼭 제거를 할 필요는 없다. 하지만 중복되는 코드가 3군데 이상으로 많아지게 된다면 코드를 함수화를 하거나 반복문을 사용하는 것을 고려 해야 한다.
이를 무시하게 된다면, 오타로 인한 버그로 인해 추가적인 시간이 소요되거나 혹은 유지 보수를 해야하는 상황에서 추가적인 시간을 사용하게 될 것이다.

매직 넘버

프로그래밍을 하면서 숫자를 사용하는 일은 아주 많다. 이를 프로그래밍을 할 때 매직넘버(의미 있는 이름의 상수로 대체될 수 있는 숫자)라는 표현을 쓴다.
매직넘버 또한 코드 악취이다. 개발자의 의도를 제대로 전달하지 못하기에 코드의 가독성은 떨어지고, 업데이트 하기도 힘들어지며 감지하기 어려운 점이 있다. 이를 해결하기 위해 상수를 사용하여 코드 악취를 제거 할 수 있다.
코드로 예를 들어 설명하면 아래와 같은 경우이다.
expiration = time.time() + 604800 # 1주를 초로 환산한 경우
코드안에 인라인 주석을 통해 1주일이라는 정보를 알려줄 수도 있지만, 더 좋은 방법은 상수들로 풀어서 사용하는 것이다.
SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR SECONDS_PER_WEEK = 7 * SECONDS_PER_DAY expiration = time.time() + SECONDS_PER_WEEK
상수는 숫자가 아닌 값에 적용해도 좋다.
... for tr in trs: if table_head.text == tr.text: continue date_info = '확인필요' attendees_info = '확인필요' method_info = '확인필요' discussion_info = '확인필요' discuss_bool_info = '확인필요' ... ''' 위의 코드에서 “확인 필요” 구문을 넣으면서 오타가 발생할 가능성이 높다. 상수로 변경한다면 다음과 같다. ''' NEED_CONFIRM = "확인 필요" for tr in trs: if table_head.text == tr.text: continue date_info = NEED_CONFIRM attendees_info = NEED_CONFIRM
Python Enum Class
Python은 열거형 enum을 지원합니다.
열거형(enumeration)은 고유한 상수값에 연결된 기호 이름의 집합입니다.
열거형은 상수를 나타내는 데 보통 사용되기 때문에 열거형 멤버에 대핸 대문자로 사용하는 것을 지향합니다.
from enum import Enum class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 print(type(Color.GREEN)) print(Color.GREEN) print(Color.GREEN.name) print(Color.GREEN.value) ''' 출력 결과 ''' <enum 'Color'> Color.GREEN GREEN 3

주석 처리된 코드와 죽은 코드

개발을 하면서 일시적으로 코드 일부를 주석 처리하는 것은 흔한 과정인 만큼 자주 사용한다.
죽은 코드는 간단하게 말해 논리적으로 절대 실행될 수 없는 코드이거나, 도달 할 수 없는 코드이다.
def some_func(flag): if flag is None: # something_call_func() return False else: return True return "test" # 이 라인은 절대 작동이 안되는 부분이다. 이 부분을 죽은 코드라고 한다.
주석 처리가 된 코드와 죽은 코드가 그대로 남아 있게 된다면, 이를 처음 본 다른 개발자는 왜 코드에서 제거되었고, 향후에 어떤 부분에 필요한지 모르는 상태가 된다. 중요하지 않은 내용 이여도 이것을 파악하는데 있어 다른 개발자는 추가적인 시간을 소요 하게 될 것이다.

그냥 함수나 모듈이어야 하는 클래스

자바를 먼저 사용했던 개발자들은 클래스를 만드는 것에 익숙하다. 하지만 이런 습관이 파이썬에 적용된다면 코드 악취를 발생 시킬 수 있다.
파이썬은 클래스나 보일러 플레이트에 코드가 존재할 필요가 없기 때문에 다른 언어에 비해 코드를 구성하는 부분에서 조금 더 자유롭다.
즉, 일회성 함수를 위해 객체를 만들거나 정적 메소드만 포함된 클래스를 작성하는 것 보다 함수를 만들고, 이들을 묶어 모듈화를 하는 것이 조금 더 효율적인 프로그래밍이 될 수 있다.

중첩된 리스트 컴프리헨션

파이썬은 리스트 컴프리헨션 이라는 것을 제공한다.
''' 0부터 100까지 5의 배수를 제거한 리스트를 만든다면 for문이 필요한데 리스트 컴프리헨션으로 한줄로 완성 할 수 있다. ''' spam = [] for number in range(100): if number % 5 != 0: spam.append(number) '''''' spam = [number for number in range(100) if number % 5 != 0]
반복문을 한번만 사용하는 경우 리스트 컴프리헨션이 깔끔한 코드가 될 수 있다.
하지만, for문이 중첩이 되는 경우 이를 리스트 컴프리헨션으로 만든 다면 코드악취의 원인이 될 수있다.
code_smell_list_comprehension = [[j for j in range(i)] for i in range(100)] ''' 중첩 리스트 컴프리헨션을 사용하지 않고, 하나는 for문으로 풀어쓰는것이 가독성이 더 좋다. ''' code_smell_list_comprehension = [] for i in range(100): code_smell_list_comprehension.append([j for j in range(i)])

빈 예외 처리 블록과 부실한 에러 메세지

파이썬의 try-except 구문은 프로그램에 문제가 발생하여도 계속 작동이 되도록 하는 구문이다.
프로그래머들은 except 처리를 어떻게 진행 할지 결정하기 어려울 수가 있어 except 블록을 pass 처리하고 싶은 유혹에 빠지기 쉽다.
이러한 코드들은 에러 발생으로 인한 코드 충돌은 발생하지 않는다. 하지만, 그냥 넘어가는 것은 충돌보다 더 안좋은 상황을 만들 수도 있다.
try: test_function() except: pass # 혹은 개발자들은 print()만 사용해 에러가 발생했다 정도만 알린다. print("alert here!") # 빈약한 정보를 알리는 것은 pass처리 한 것과 같다. ''' 에러 메세지는 프로그래머가 아닌 사용자가 읽는 부분이다. - 무슨 일이 발생했는지 - 사용자가 무엇을 해야 하는지 이 두 가지를 설명해 줘야 한다. '''
프로그램에서 발생할 수 있는 모든 예외를 처리하지 않으면 프로그램 개발은 완료된 것이 아님을 명시해야 한다.

마무리

클린 코드에 대한 내용은 위에 정리한 내용(코드 적인 부분이나 Docstring 등)외에도 많은 내용(클린 아키텍처 등)을 포함하고 있다. 아직 공부해야 하는 부분들은 많지만 클린 코드의 핵심인 “누구에게나 쉽게 읽히는 코드”를 만들기 위해 노력할 것이다.
출처: 클린 코드, 이제는 파이썬이다: “알 스웨이가트”