Share
Sign In
Lighthouse Dev
Clean Code 깊게파보기
L
Lighthouse
👍
2
❤️
1
😘
1
개요
개발을 하면서 과거에 작업했던 코드를 다시 봐야 하는 상황이 종종 있다. 과거에 깨끗하지 않게 코드를 작성했다면, 코드를 다시 이해하고 원인을 찾는 부분에서 시간을 소요 할 가능성이 높다.
특히, 서비스 중인 프로그램에서 버그가 발생해 빠르게 이슈 대응을 해야 한다면, 머리가 새하얘질 것 이다.
또한, 이런 경험을 한 적이 있을 것이다.
시간이 부족해서 빠르게 개발하고, ‘아.. 나중에 리팩토링 해야겠다.’ 라고 생각한 경우도 있을 것이다. 하지만, 리팩토링을 하는 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 등)외에도 많은 내용(클린 아키텍처 등)을 포함하고 있다. 아직 공부해야 하는 부분들은 많지만 클린 코드의 핵심인 “누구에게나 쉽게 읽히는 코드”를 만들기 위해 노력할 것이다.
출처: 클린 코드, 이제는 파이썬이다: “알 스웨이가트”
Kp
Subscribe to 'kpmg-lighthouse'
Welcome to 'kpmg-lighthouse'!
By subscribing to my site, you'll be the first to receive notifications and emails about the latest updates, including new posts.
Join SlashPage and subscribe to 'kpmg-lighthouse'!
Subscribe
👍
2
❤️
1
😘
1
Lighthouse
파이썬의 동시성 관리 : 코루틴(Corutine) 上
0. 들어가기 앞서 지난 시간에는 파이썬의 GIL 제약과 그 제약으로 인해 “찐”효율을 내지 못하는 멀티 스레드 프로그래밍에 대해서 알아보았다. 이번 시간에는 파이썬 비동기의 핵심 키워드 네이티브 코루틴인 Asyncio, async, await를 이해하기 전 이것들의 근간이라 불리는 코루틴(Corutine)에 대해서 알아보자. 들어가기 앞서, 복습 차원에서 지난 시간 내용을 간략하게 정리하자 1. 제네레이터 1.1. 제네레이터 제네레이터는 쉽게 말해서, 여러개의 데이터를 미리 만들어 놓지 않고 필요한 때마다 즉석에서 하나씩 만들어낼 수 있는 객체를 의미한다. 일반적인 함수와 달리 상태를 유지할 수 있다. 즉, 제네레이터는 yield 표현식을 사용하여 값을 반환하고, 다음 호출 시 마지막으로 실행된 yield 표현식 이후부터 실행을 재개한다. 일반함수는 return을 만나면 실행이 끝나버린다. 하지만 제네레이터는 yield 구문에서 “일시정지”의 상태로 값을 외부로 내보낸다. 그 이후에 필요할 때 다시 실행 흐름을 이어나갈 수 있다. 함수 내부에서 사용된 지역 변수등이 메모리에 그대로 유지되어 있기 때문이다. 아래 코드를 살펴보자. 함수 return_abc()는, 알파벳을 1초마다 하나하나 리스트에 적재하는 코드이다. print를 찍어본다면 어떻게 될까? 너무 당연한 결과이다. 그렇다면 해당 함수를 for loop에 돌려보면 어떻게 될까? 이 결과 역시 너무나도 쉽게 예상할 수 있다. 여기서 3초라는 시간을 잘 기억해주길 바란다. 위에서 제네레이터는 데이터를 미리 만들어두지 않고, 필요할 때마다 하나씩 만들어내는 객체를 뜻한다 했다. 이것도 코드를 통해 알아보자. 뭔가 위의 코드랑 별로 달라진게 없어보인다. print를 찍어보자. !! 예상했던 것과는 달리 제네레이터가 출력 되었다 !! 위에서 언급했던 것처럼 제네레이터는 “필요할 때마다” “하나씩” 만들어 낸다고 했으니, 한번 for문을 통해서 “하나씩” 값을 받아와 보자.
😍
1
donggyun_woo
FastAPI 특징 with Pydantic
이번 글에서는 python FastAPI의 특징과 FastAPI내에서 Pydantic을 사용한 코드를 확인해 보겠습니다. Fast API Fast API란? FastAPI는 Python 프레임워크 중 하나입니다. Python 프레임워크들 중 django, flask 와 같이 비교를 많이 하며, 빠른 속도로 인해 인기가 많아진 프레임워크입니다. FastAPI에서 제공하는 기능들을 간략히 보면 아래와 같습니다. API 문서 자동 생성 (Swagger와 ReDoc 스타일 동일) 의존성 주입 위주의 설계를 통한 DB 등에 대한 관리 편리 비동기 동작으로 빠른 성능 보장 (Starlette) Pydantic을 사용한 Validation 체크 뛰어난 공식문서 가이드 Python Framework 깃허브 스타 github star history 그래프를 보면 FastAPI(노란색)의 스타가 급격히 증가하는 것을 확인 할 수 있습니다. 그래프를 통해 FastAPI를 많이 사용한다는 의미는 될 수 없겠지만, 개발자들 사이에서 관심을 많이 갖고 있는것은 확인 할 수 있을것 같습니다. FastAPI의 특징 특징들을 Fast API 공식문서를 통해 자세하게 확인해 보겠습니다. 파이썬 3.8+ 버전의 Type hint를 사용하는 트랜디하고 높은 성능을 가진 파이썬 프레임워크 빠름: NodeJS 및 Go와 동등한 매우 높은 성능을 제공 실제 파이썬 기반의 프레임워크들을 비교한 내용을 확인해 보면 성능이 상위에 존재하는 것을 확인 할 수 있습니다. 빠른 코딩: 기능 개발 속도를 약 200%~300% 향상 버그 감소: 인간(개발자)이 유발한 오류의 약 40% 감소 직관적: 뛰어난 편집기 지원. 디버깅 시간이 줄어듭니다.
👍🟩😘😀👍🏻
6
김원준
[1] - Sync 와 Async 그리고 Blocking과 NonBlocking
0. 개요 아무것도 모르는 대학생 시절에, 학부 인턴을한 적이 있다. 그때 팀장님이 신입 면접을 보고 오신 뒤, 나에게 질문을 하신 적이 있다. “oo아 동기와 비동기 차이를 설명해 봐” 동기 - “하나하나 순차적으로 실행하는 것 !” 비동기 - “동시에 실행되는 것 !” 이라고 대답한 기억이 있다. 아마 이렇게 대답한 나는 그 당시에 동기 / 비동기를 아래 사진과 같이 이해하고 있었던 것 같다. 내가 이해한 "동기" 내가 이해한 "비동기" 얼핏 보기엔 맞는 대답이기도 하다. 하지만 지금 와서 생각해 보면 “하나하나 순차적으로 실행하는 것 !”의 대답은, Blocking Sync에 대한 설명일 수도 있고, Blocking Async에 대한 설명일 수도 있다. 또, “동시에 실행되는 것 !” 의 대답은, NonBlocking Sync일 수도 있고 NonBlocking Async일 수도 있다. 물론, 이론적으론 그렇다. 해당 개념을 잘 모르는 사람에게는, Sync와 Async의 단어 자체는 익숙할 수 있어도, Blocking과 NonBlocking이 앞에 붙는 순간 머리가 아파올 수 있다.
👍❤️😀😘
9