파이썬 클린 코드 1-3장 요약.
코드를 작성하는 시간만큼 리뷰를 하는 시간이 길어지면서 이론을 기반으로 한 코드 리뷰를 위해 파이썬 클린 코드를 다시 읽어보았다.
3줄 요약:
- 클린 코드의 핵심 키워드: 기술 부채 최소화, 가독성, 유지보수성 향상
- 높은 응집력과 낮은 결합력(high cohesion and low coupling)
- 관심사를 분리해 독립성을 높이고, 파급 효과를 최소화한다.
클린 코드란?
- 다른 개발자가 코드를 읽고 유지 관리할 수 있는지 여부에 달려있다.
- 기술 부채 최소화, 가독성, 유지보수성 향상
- 프로젝트 코딩 스타일 가이드(python의 경우 PEP-8)를 준수하는 것이 좋다.
- 일관성은 가독성과 이해력을 높여준다.
- 주석은 가급적 지양하고, 어노테이션/docstring(컴포넌트에 대한 문서화)은 지향한다.
+pythonic한 코드?
- 관용구를 따른 코드를 관용적이라 부르고, 파이썬에서는 pythonic 하다고 말한다.
- 디자인 패턴은 언어와 무관한 고차원 개념이지만, 관용구는 실제 코드로 변환되는 개념이다.
- 책의 2장 내용으로 slicing, context manager, property, iterable 등에 대해 다루지만 코드적인 부분이 포함되어 있어 해당 글에는 추가하지 않을 예정
좋은 코드의 일반적인 특징
계약에 의한 디자인(Design by Contract)
- 오류 발생 시 손상 부분 파악이 명확해진다.
- 사전 조건(코드가 실행되기 전에 체크해야하는 것)과 사후 조건(반환 값의 유효성)
- json schema, marshmallow 같이 api의 input, output을 명확히 하는 것
- 불변식(함수가 실행되는 동안 유지되어야 하는 것), 부작용(코드의 부작용을 docstring에 언급)
에러 핸들링 (값 대체, 에러 로깅, 예외 처리)
- 제어 흐름(to-go)의 수단으로 사용해선 안된다.
- 예외는 캡슐화를 약화시키기 때문에 신중하게 사용해야 한다.
- 비어있는 except 블록은 지양해야 한다. (에러는 결코 조용히 전달되어서는 안된다)
- 예외의 타입을 변경할 때는 raise Error from e 구문을 사용한다.
- assertion 실패 시 프로그램을 종료해야 하기 때문에, 비즈니스 로직에 섞거나 소프트웨어 제어 흐름 메커니즘으로 사용해서는 안 된다.
관심사의 분리
- 목표: 파급(ripple) 효과를 최소화해 유지보수성을 향상시키는 것. 높은 응집력과 낮은 결합력!
- 응집력: 객체는 가능하면 작아야 하고, 잘 정의된 목적을 가져야 한다.
- 결합력: 두 개 이상의 객체가 서로에게 의존하는 정도
- 결합력이 높을 경우 재사용성이 낮아지고, 파급 효과는 커지며, 낮은 수준의 추상화가 된다.
- 특정 객체에 지나치게 의존
- 너무 많은 파라미터를 가지는 경우에도 결합력이 높다고 봄. 다른 상황에서 사용하기 어렵기 때문에
개발 지침 약어
- DRY(Do not Repeat Yourself) / OAOO(Once and Only Once)
- 코드에 있는 지식은 단 한번, 단 한곳에 정의되어 있어야 한다. 코드 변경 시 수정이 필요한 부분은 단 한군데만 있어야 한다.
- YAGNI(You Ain’t Gonna Need It) / KIS(Keep It Simple)
- 미래의 요구 사항을 예측해 복잡하게 만들기보다, 현재 요구사항을 해결하기 위한 소프트웨어를 작성하고, 나중에 수정하기 쉽도록 작성한다. 즉 과잉 엔지니어링을 피한다.
- EAFP(Easier to Ask Forgiveness than Permission) vs LBYL(Look Before You Leap)
- 허락보단 용서를 구하는게 낫다 vs 도약하기 전에 살펴볼 것 → 파이썬은 EAFP 방식으로 만들어졌다.
# LBYL 방식 if os.path.exists(filename): with open(filename) as f: ... # EAFP 방식 try: with open(filename) as f: ... except FileNotFoundError as e: logger.error(e)
컴포지션과 상속
- 둘다 객체 지향 프로그래밍에서 나온 개념. 컴포지션은 객체 간 has-a relationship, 상속은 is-a relationship.
- 예시는 정리글 참고
- has-a: Person who has a Job
public class Job { // variables, methods etc. } public class Person { //composition has-a relationship private Job job; //variables, methods, constructors etc. object-oriented
- is-a: Cat is an Animal
public class Animal { // variables, methods etc. } public class Cat extends Animal{ }
- 컴포지션은 커플링이 약하고 상속은 강하기 때문에, 상속보다는 컴포지션을 선호해야 한다.
- 부모 클래스의 메서드를 공짜로 얻을 수 있기 때문에 상속하는 것은 좋지 않는 방법이다.
- 상속한 대부분의 메서드를 재정의, 대체해야 하는 경우 → 부적절
- 부모 클래스의 기능을 그대로 물려받으면서 추가 기능을 더하거나 특정 기능을 수행 → 적절
- 해결책 → 컴포지션 또는 믹스인 클래스를 정의해 사용한다.
함수와 메서드의 인자
- 함수 인자를 변경하는 것은 가급적 피해야한다.
- 파이썬의 모든 인자는 값에 의해 전달 (pass by a value)되기 떄문.
- 함수에 값을 전달하면 함수의 서명에 있는 변수에 할당해서 사용. immutable은 변경 시 새로운 객체를 만들어 변수에 할당하지만, mutable은 참조를 보유하고 있는 변수를 통해 값을 수정
- 변경 가능한 객체를 전달하고 함수에서 값을 변경할 경우, 실제 값이 변경되는 부작용이 생긴다. → 변경 불가능한 객체를 사용해 부작용을 최소화 해야 한다.
추가로…
- 소프트웨어 독립성: 수정한 컴포넌트가 외부에 영향을 미치지 않아야 한다.
- 독립성은 변경을 쉽게 한다
- 좋은 코드라면 유사한 컴포넌트끼리 정리하여 구조화해야 한다.
- 탐색, 검색이 용이하고 모듈 임포트 시 메모리에 로드할 객체가 줄어든다.
PREVIOUS포트 포워딩이란?
NEXT쿠버네티스 기본 개념 정리