클린코드란
나쁜 코드 관련 일화
80년대 후반 MicroPro international은 워드프로세스인 WordStar를 개발해. 선풍적인 인기를 끌었지만, 업데이트 주기가 길어지고 이전 버전의 버그가 그대로 남아있더니 얼마 안 가 망했습니다. 원인은 출시 당시 너무 바빠 코드를 마구 작성했고, 기능을 추가할수록 감당을 할 수 없었습니다. 즉 회사가 망한 원인은 나쁜 코드에 있었습니다.
나쁜 코드를 짜는 이유
사람들은 시간이 없다고 급하게 코드를 짜고 프로그램이 돌아가면 안도하고 넘어갑니다.자신이 짠 나쁜 코드를 보며 나중에 수정하겠다고 생각하지만, 나중은 절대 오지 않습니다. 이를 르블랑의 법칙(Leblanc’s Law)이라고 합니다. Later equals never
나쁜 코드의 대가
협업하여 프로그램을 작성할 때 남들이 짠 나쁜 코드에 의해 개발 속도가 늦어지고 진도를 나가지 못합니다. 생산성이 낮아지면 인원을 늘리지만 새로운 팀원도 코드를 이해하지 못합니다. 결국 이러한 나쁜 코드가 쌓일수록 팀의 생산성은 0에 가까워집니다.
코드 속 나쁜 냄새
코드 스멜(Code smell)은 코드에서 문제를 일으킬 가능성이 있는 프로그램 소스 코드의 특징을 뜻 합니다.
코드 스멜 예시
긴 메서드
긴 메서드는 코드의 복잡성이 증가하고 가독성을 떨어뜨려 코드의 유지 관리가 어려워집니다. 메서드는 필요할 때 재사용할 수 있도록 어떠한 작업을 하는지 명확해야 합니다.
중복 코드
중복 코드는 쓸데없이 코드 길이를 늘어나게 합니다. 또한 중복 코드 중 하나의 코드가 변하면 나머지 중복 코드를 수정하지 않았을 때 오류가 날 확률이 높아집니다.
긴 매개 변수 목록
메서드의 매개 변수가 많으면 각 매개 변수의 일관성이 떨어질 것이 분명하며 이는 메서드를 이해하기 어렵게 만듭니다.
너무 많은 주석
자신의 코드에 주석을 다는 것이 나쁜 것은 아니지만 좋은 네이밍(Naming)을 통해 프로그램을 더욱 깨끗하게 작성하는 것이 코드 유지 관리에 좋습니다.
전역 변수
모든 곳에서 접근할 수 있고 변경할 수 있는 전역 변수는 무분별하게 사용하다가 수정하는 경우 예상치 못한 오류를 발생시킬 수 있습니다.
게으른 클래스
클래스를 생성할 때, 그것을 유지하고 이해하는 비용이 듭니다. 이 비용을 충당할 만큼 재사용을 할 수 없는 클래스는 차라리 만들지 않는 것이 낫습니다.
거대한 클래스
클래스를 너무 크게 만들면 클래스 안에 너무 많은 인스턴스 변수와 메서드가 존재하고 중복 코드가 있을 확률이 높습니다.
리팩토링(Refactoring)이란?
리팩토링은 프로그램 결과의 변경 없이 코드를 재작성 하는 것을 의미합니다. 코드 스멜을 제거하여 코드의 품질을 향상시키는 것을 뜻합니다.
클린 코드를 써야 하는 이유
종종 프로그래머는 작성 기한을 맞추기 위해서 나쁜 코드를 양산합니다. 하지만 엉망진창인 코드 때문에 오히려 개발 속도가 느려지고 결국 기한을 놓칩니다. 기한을 맞추는 유일한 방법은 언제나 코드를 최대한 깨끗하게 유지하는 것입니다.
클린 코드에 대한 프로그래머들의 의견
C++ 창시자, 비야네 스트롭스트룹(Bjarne Stroustrup)
나는 우아하고 효율적인 코드를 좋아한다. 논리가 간단해야 버그가 숨어들지 못한다. 의존성을 최대한 줄여야 유지보수가 쉬워진다. 오류는 명백한 전략에 따라 철저히 처리한다. 성능을 최적으로 유지해야 사람들이 원칙 없는 최적화로 코드를 망치려는 유혹에 빠지지 않는다. 클린 코드는 한 가지를 제대로 한다.
객체지향의 대가, 그래디 부치(Grady Booch)
클린 코드는 단순하고 직접적이다. 클린 코드는 잘 쓴 문장처럼 읽힌다. 클린 코드는 결코 설계자의 의도를 숨기지 않는다. 오히려 명쾌한 추상화와 단순한 제어문으로 가득하다.
실용주의 프로그래머, 데이브 토마스(Dave Thomas)
클린 코드는 작성자가 아닌 사람도 읽기 쉽고 고치기 쉽다. 단위 테스트 케이스와 인수 테스트 케이스가 존재한다. 클린 코드에는 의미 있는 이름이 붙는다. 특정 목적을 달성하는 방법은 (여러 가지가 아니라) 하나만 제공한다. 의존성은 최소이며 각 의존성을 명확히 정의한다. API는 명확하며 최소로 줄였다. 언어에 따라 필요한 모든 정보를 코드만으로 명확히 표현할 수 없기에 코드는 문학적으로 표현해야 마땅하다.
『레거시 코드 활용 전략』 저자, 마이클 페더스(Michael Feathers)
클린 코드의 특징은 많지만, 그중에서도 모두를 아우르는 특징이 하나 있다. 클린 코드는 언제나 누군가 주의 깊게 짰다고 느끼게 한다. 고치려고 살펴봐도 딱히 손댈 곳이 없다. 작성자가 이미 모든 사항을 고려했으므로, 고칠 궁리를 하다 보면 언제나 제자리로 돌아온다. 그리고는 누군가 남겨준 코드, 누군가 주의 깊게 짜놓은 작품에 감사를 느낀다.
익스트림 프로그래밍 공동 창시자, 론 제프리스(Ron Jeffries)
최근 들어서 나는 켄트 벡이 제안한 단순한 코드 규칙으로 구현을 시작한다.(그리고 같은 규칙으로 구현을 거의 끝낸다.) 중요한 순으로 나열하자면 간단한 코드는
- 모든 테스트를 통과한다.
- 중복이 없다.
- 시스템 내 모든 설계 아이디어를 포함한다.
- 클래스, 메서드, 함수 등을 최대한 줄인다.
위키(Wiki) 창시자, 워드 커닝햄(Ward Cunningham)
코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 클린 코드라 불러도 되겠다. 코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드라 불러도 되겠다.
클린 코드를 써야 하는 이유
미국 보이 스카우트는 캠핑에 관한 규칙이 있습니다. 바로 캠핑장을 사용한 뒤 처음 왔을 때보다 더 깨끗하게 해놓는 것입니다. 그들은 캠핑장 전체를 깨끗이 청소도 하지만 단순히 장소를 쓰레기로 버리지도 않고 적절한 청소 작업을 위한 연간 계획이 있습니다. 캠핑장에 도착했을 때보다 떠날 때 더 깨끗한지 확인함으로써 적어도 캠핑장의 청결에 관해서는 그들이 해를 끼치지 않는다는 것을 보장할 수 있습니다. 미국 보이스카우트가 따르는 간단한 규칙이 프로그래머에게도 유용합니다.
Always leave the campground cleaner than you found it. (캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라.)
작업을 완료하고 체크아웃할 때에 체크인했을 때보다 코드를 깨끗하게 해둔다면 코드는 절대 나빠지지 않습니다. 평소에 코드를 잘 관리한다면 나중에 많은 시간과 노력을 투자해 코드를 정리할 필요가 없습니다. 깨끗한 코드는 거창할 것 없이 변수 이름 하나를 개선하고, 조금 긴 함수 하나를 분할하고, 약간의 중복을 제거하고, 복잡한 if 문 하나를 정리하면 충분합니다.
좋은 주석 작성의 필요성
주석은 코드를 통해 자신의 의도를 충분히 표현하지 못할 때 사용하며 잘 작성된 주석은 코드를 이해하는 데 많은 도움을 줍니다. 반면 주석의 무분별한 사용은 오히려 코드를 나쁘게 만들 수 있습니다. 따라서 좋은 주석이 무엇인지 알고 꼭 필요한 상황에서 주석을 작성해야 할 필요가 있습니다.
좋은 주석 목록
법적인 주석
코드의 저작권과 소유권에 대한 정보를 나타내는 주석입니다.
# Copyright © 2016-2020 elice. All rights reserved.
정보를 제공하는 주석
필요에 따라 코드에 대한 기본적인 정보를 주는 주석입니다.
# a와 b를 비교한 결과를 반환한다.
def compareTo(a, b):
...
의도를 설명하는 주석
결정에 깔린 의도를 설명하는 주석입니다.
def compareTo(a, b):
...
# a와 b의 우선순위가 같은 경우에 반환된다.
return 0
의미를 명료하게 밝히는 주석
모호한 인수나 반환값의 의미를 밝히는 주석입니다.
assertTrue(compareTo(a, b) == 0 # a == b
결과를 경고하는 주석
다른 프로그래머에게 결과를 경고할 목적으로 사용되는 주석입니다.
# 여유 시간이 충분하지 않다면 실행하지 마십시오.
def big_file_testing():
...
TODO 주석
앞으로 할 일을 남겨두는 주석입니다. 단, 나쁜 코드를 남겨 놓은 핑계로 사용되면 안 됩니다.
# TODO 새로운 방식이 도입되면 필요하지 않다.
def version_info():
...
중요성을 강조하는 주석
별것 아니라고 생각될만한 코드의 중요성을 상기하기 위해 사용되는 주석입니다.
my_string.replace(" ", "")
# 여기서 replace로 공백을 제거함으로써 다른 문자열로 인식될 위험을 제거한다.
나쁜 주석 목록
주절거리는 주석
코드 작성자에게는 의미가 있겠지만 다른 사람들에게는 제대로 전달되지 않는 주석입니다. 아래 예시에서 코드를 처음 보는 사람은 try 구문을 모두 확인해야 주석의 의미를 파악할 수 있게 됩니다.
try:
x = int(input())
y = int(input())
print(x / y)
except Exception as err:
# y값에 0이 들어오면 실행된다.
같은 이야기를 중복하는 주석
아래 예시는 except 자체가 에러 발생에 대한 구문이므로 중복된 내용을 담고 있어 불필요한 주석입니다.
try:
...
except Exception as err:
# 에러 발생 시 실행되는 코드입니다.
오해할 여지가 있는 주석
엄격하지 않아 오해를 일으키는 주석입니다. 아래 예시에서 closed가 False인 경우 10초 기다린 뒤 다시 한번 closed를 검사한 후 0으로 반환이 되지만, 주석에는 그러한 설명을 하지 않고 있습니다.
# closed가 False일 때 0이 반환된다.
def check_close:
if !closed:
time.sleep(10)
if !closed:
print("error")
return 0
의무적으로 다는 주석
모든 변수나 함수 등에 주석을 달아야 한다는 규칙을 정하는 것은 어리석은 행위입니다. 이러한 주석은 아무 가치가 없습니다.
book_title = "Harry Potter" # 책 제목
book_author = "J.K. Rowling" # 책 작가
이력을 기록하는 주석
과거에는 코드가 변한 이력을 기록할 필요가 있었으나 현재는 소스 코드 관리 시스템이 있어 전혀 필요가 없습니다.
# 변경 이력 (11-10-2001부터)
# -----------------------------------------
# 13-10-2001 : 클래스 정리
있으나 마나 한 주석
당연한 사실을 언급하는 주석입니다. 아래 예시에서 생성자라는 주석이 없어도 누구나 생성자라는 것을 알 수 있습니다.
class Location:
# 기본 생성자
def __init__(self, x, y):
self.x = x
self.y = y
이 외에도 함수나 변수로 표현 가능한 주석, 위치를 표시하는 주석, 닫는 괄호에 다는 주석, 공로를 돌리거나 저자를 표시하는 주석, 주석으로 처리된 코드, 전역 정보 등 많은 나쁜 주석이 있습니다.