Clean Code (1)

Understanding 'Clean Code' in a Pythonic way

2017-08-23


Clean Code: A Handbook Of Agile Software Craftsmanship (2009) λΌλŠ” 책을 읽고 μ •λ¦¬ν•œ κΈ€μž…λ‹ˆλ‹€. μ±…μ˜ μ˜ˆμ œλŠ” Java 둜 μ“°μ—¬ μžˆμ–΄μ„œ Python으둜 쓰인 μ½”λ“œλ„ ν•¨κ»˜ μ°Ύμ•„λ³΄μ•˜μŠ΅λ‹ˆλ‹€. 이 책은 κ·Έ μžμ²΄κ°€ ν΄λ¦°μ½”λ“œμž…λ‹ˆλ‹€. μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 원칙과 μ² ν•™μ—μ„œ μ‹€μ œ μ½”λ“œκΉŒμ§€ μ„€λͺ…이 λͺ…ν™•ν•˜κ³  ꡰ더더기가 μ—†μŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œ 읽닀가 λ…ΈνŠΈλΆμ„ 켜고 λ¦¬νŒ©ν† λ§ν•˜λ©° λ¬΄λ¦Žμ„ νƒμΉœ κ²½μš°κ°€ 적지 μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

πŸ‘‰ Clean Code (2)


1. κΉ¨λ—ν•œ μ½”λ“œ

'Later equals never'. μš°λ¦¬λŠ” 항상 μ§€κΈˆμ€ κΈ‰ν•˜λ‹ˆ λΉ λ₯΄κ²Œ 짜고 λ‚˜μ€‘μ— λ‹€μ‹œ μ •λ¦¬ν•˜μžκ³  λ‹€μ§ν•˜κ³€ ν•œλ‹€. κ·ΈλŸ¬λ‚˜ μš°λ¦¬λŠ” μ•ˆλ‹€. λ‚˜μ€‘μ€ κ²°μ½” μ˜€μ§€ μ•ŠλŠ”λ‹€λŠ” 것을.

μ €μžλŠ” 보이슀카우트 원칙을 λ§ν•œλ‹€. 'μΊ ν”„μž₯은 처음 왔을 λ•Œλ³΄λ‹€ 더 κΉ¨λ—ν•˜κ²Œ 해놓고 λ– λ‚˜λΌ'κ³ . 빨리 κ°€λŠ” μœ μΌν•œ 방법은 μ–Έμ œλ‚˜ μ½”λ“œλ₯Ό μ΅œλŒ€ν•œ κΉ¨λ—ν•˜κ²Œ μœ μ§€ν•˜λŠ” μŠ΅κ΄€μ΄λ‹€. μ½”λ“œλ₯Ό μ½λŠ” μ‹œκ°„ λŒ€ μ§œλŠ” μ‹œκ°„ λΉ„μœ¨μ€ 10λŒ€ 1을 ν›Œμ© λ„˜λŠ”λ‹€. μš°λ¦¬λŠ” λŠμž„μ—†μ΄ κΈ°μ‘΄ μ½”λ“œλ₯Ό μ½λŠ”λ‹€. κΉ¨λ—ν•œ μ½”λ“œ, 가독성이 쒋은 μ½”λ“œκ°€ μ€‘μš”ν•œ μ΄μœ λ‹€.

ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”κ΅¬μ‚¬ν•­μ„ νƒ“ν•˜κ³  싢은 유혹이 λ“€ λ•Œλ„ μžˆλ‹€. λΆˆκ°€λŠ₯ν•œ μš”μ²­μ„ ν–ˆλ‹€κ³ , κΈ°ν•œμ΄ λ„ˆλ¬΄ μ§§μ•˜λ‹€κ³ . λ‚˜λ„ 초반 λͺ‡ 번의 μ™Έμ£Όμ—μ„œ 이런 λ‚˜μœ νƒœλ„λ₯Ό 가지곀 ν–ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ˜κ²¬μ„ μ œμ‹œν•˜μ§€ μ•Šκ³  μ‹œν‚€λŠ”λŒ€λ‘œ ν•œκ²ƒλ„ 개발자의 잘λͺ»μ΄λ‹€. 이 책에선 μ˜μ‚¬μ˜ 경우λ₯Ό λ“€μ–΄ μ„€λͺ…ν•œλ‹€. 'μ–΄λŠ ν™˜μžκ°€ 였래 기닀릴 수 μ—†μœΌλ‹ˆ 수술 전에 손을 씻지 말라고 μš”κ΅¬ν•œλ‹€λ©΄, μ˜μ‚¬λŠ” λ‹¨ν˜Έν•˜κ²Œ κ±°λΆ€ν•΄μ•Ό ν•œλ‹€.'

이 책을 μ„Έ λ§ˆλ””λ‘œ μš”μ•½ν•˜λ©΄ '쀑볡 쀄이기, ν‘œν˜„λ ₯ 높이기, μ΄ˆλ°˜λΆ€ν„° κ°„λ‹¨ν•œ 좔상화 κ³ λ €ν•˜κΈ°'이닀.

  • μ½”λ“œλ„ κΈ°μŠΉμ „κ²°μ΄ μžˆμ–΄μ•Όν•œλ‹€λŠ” λ‚΄μš©μ΄ μ‹ μ„ ν–ˆλ‹€. 'ν•΄κ²°ν•  문제의 κΈ΄μž₯을 λͺ…ν™•νžˆ λ“œλŸ¬λ‚΄κ³ , λͺ…λ°±ν•œ 해법을 μ œμ‹œν•˜λ©° 문제λ₯Ό ν’€μ–΄μ•Ό ν•œλ‹€'κ³ .
  • λ‚˜μœ μ½”λ“œκ°€ μŒ“μΌμˆ˜λ‘ νŒ€μ˜ 생산성은 떨어진닀. κ·ΈλŸ¬λ‹€κ°€ λ§ˆμΉ¨λ‚΄ 0에 μ ‘κ·Όν•œλ‹€. 심지어 νšŒμ‚¬λ₯Ό λ§ν•˜κ²ŒκΉŒμ§€ ν•œλ‹€. 'Killer app을 κ°œλ°œν–ˆλ˜ ν•œ νšŒμ‚¬κ°€ λ‚˜μœ μ½”λ“œ λ•Œλ¬Έμ— μ œν’ˆ μΆœμ‹œ μ£ΌκΈ°κ°€ 점점 길어지고 이전 λ²„μ „μ˜ 버그가 μƒˆλ‘œμš΄ 버전에 λ‚¨μ•„μžˆκ³ , ν”„λ‘œκ·Έλž¨μ΄ μ£½λŠ” νšŸμˆ˜λ„ λŠ˜μ–΄λ‚˜κ³ , 이λ₯Ό κ³ μΉ˜λ©΄μ„œ μ œν’ˆ μΆœμ‹œλ₯Ό λ§žμΆ”κΈ° μœ„ν•΄ λ‚˜μœ μ½”λ“œκ°€ 더 μ–‘μ‚°λ˜κ³ ...λ₯Ό λ°˜λ³΅ν•˜λ©° κ²°κ΅­ μ‚¬μš©μžκ°€ 등을 돌리고 νšŒμ‚¬κ°€ λ§ν–ˆλ‹€λŠ”' λ¬΄μ‹œλ¬΄μ‹œν•œ 상황도 μ†Œκ°œλ˜μ—ˆλ‹€.

2. 의미 μžˆλŠ” 이름

클래슀 μ΄λ¦„μœΌλ‘œλŠ” Data, Info 같은 일반적이고 μ˜λ―Έμ—†λŠ” 이름은 ν”Όν•˜λΌλŠ” 점이 ν₯λ―Έλ‘œμ› λ‹€. μ˜›λ‚ μ²˜λŸΌ λ³€μˆ˜ 길이λ₯Ό 더이상 μ œν•œν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 길더라도 μ„œμˆ μ μΈ 이름을 μ“°λŠ”κ²Œ μ’‹λ‹€. 같은 λͺ¨λ“ˆ λ‚΄μ—μ„œλŠ” 같은 λͺ…사/동사λ₯Ό μ“΄λ‹€. λ˜λ„λ‘ μ•Œκ³ λ¦¬μ¦˜ 이름, νŒ¨ν„΄ 이름 λ“± 해법 μ˜μ—­μ—μ„œ 온 이름을 μ‚¬μš©ν•˜κ³ , 이게 μ—†κ±°λ‚˜ 문제 μ˜μ—­(domain)의 λ§₯락이 μ€‘μš”ν•˜λ‹€λ©΄ μ—¬κΈ°μ„œ 온 이름을 μ“΄λ‹€.


3. ν•¨μˆ˜

  1. μž‘κ²Œ, 더 μž‘κ²Œ

ν•˜λ‚˜μ˜ λ©”μ†Œλ“œμ— ν•˜λ‚˜μ˜ κΈ°λŠ₯만. SRP(Single Responsibility Principle)λ₯Ό μ‘΄μ€‘ν•œλ‹€. ν•¨μˆ˜λŠ” ν•œκ°€μ§€λ§Œν•˜κ³  κ·Έ ν•œκ°€μ§€λ₯Ό '잘'ν•΄μ•Ό ν•œλ‹€. κ·Έλ ‡λ‹€λ©΄ ν•¨μˆ˜κ°€ ν•œκ°€μ§€λ§Œ ν•˜λŠ”μ§€λŠ” μ–΄λ–»κ²Œ νŒλ‹¨ν• κΉŒ? ν•¨μˆ˜λͺ…을 λ‹€λ₯Έ μ΄λ¦„μœΌλ‘œλ„ ν‘œν˜„ν•  수 μžˆλ‹€λ©΄ κ·Έ ν•¨μˆ˜λŠ” μ—¬λŸ¬ μž‘μ—…μ„ ν•˜λŠ” μ…ˆμ΄λ‹€. 일반적인 기쀀은 'getter(쑰회)인지, setter(λͺ…λ Ή)인지' 이닀.

λ‹Ήμ—°νžˆ ν•˜λ‚˜μ˜ ν•¨μˆ˜μ— 좔상화 μˆ˜μ€€μ€ ν•˜λ‚˜μ—¬μ•Ό ν•œλ‹€. 좔상화 μˆ˜μ€€μ΄ λ‹€λ₯΄λ‹€λ©΄ μ—¬λŸ¬ λ‹¨κ³„λ‘œ λ‚˜λˆ μ„œ μ •μ˜ν•˜κ³  μˆ˜ν–‰ν•œλ‹€. λ“€μ—¬μ“°κΈ°λŠ” 2단을 λ„˜μœΌλ©΄ 쒋지 μ•Šλ‹€.

  1. 좔상화 μˆ˜μ€€μ΄ 높은 ν•¨μˆ˜λΆ€ν„° μ •μ˜ν•œλ‹€. 'μœ„μ• μ„œ μ•„λž˜λ‘œ μ½”λ“œμ½κΈ°'

ν•œ λ©”μ†Œλ“œ μ•ˆμ—μ„œ λ‹€λ₯Έ λ©”μ†Œλ“œλ₯Ό 뢈러올 λ•Œ λΆ€λ₯΄λŠ” ν•¨μˆ˜λ₯Ό λ¨Όμ €, λΆˆλŸ¬μ§€λŠ” ν•¨μˆ˜λ₯Ό λ‚˜μ€‘μ— μ •μ˜ν•œλ‹€. 그리고 μ΄μ–΄μ§€λŠ” μ½”λ“œμ—μ„œ κ·Έ λ©”μ†Œλ“œκ°€ μ •μ˜λ  것을 κΈ°λŒ€ν•˜λŠ” 것이 μžμ—°μŠ€λŸ½λ‹€. λ‹Ήμ—°νžˆ μ½”λ“œ 흐름이 κ³ μ°¨μ›μ—μ„œ μ €μ°¨μ›μœΌλ‘œ μ§„ν–‰λ˜κΈ° λ•Œλ¬Έμ— μ΄ν•΄ν•˜κΈ°λ„ 쉽닀. DRF 의 mixins.pyλŠ” μ•„λž˜μ™€ 같이 μ ν˜€μžˆλ‹€.

  • React 의 encapsulation?
# mixins.py

class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

destroy()λ₯Ό λž˜ν•‘ν•΄μ„œ perform_destroy()λΌλŠ” ν•¨μˆ˜λ₯Ό λ§Œλ“€μ—ˆλ‹€. λΆˆλŸ¬μ™€μ§€λŠ” λ©”μ†Œλ“œλͺ…이 λͺ…ν™•ν•΄μ•Ό 이 ν•¨μˆ˜κ°€ μ–΄λ–€ κΈ°λŠ₯을 μˆ˜ν–‰ν• μ§€ 미리 μ§μž‘ν•  수 μžˆλ‹€.

  1. ν•¨μˆ˜ 인수

'μ΅œμ„ μ€ 무항 ν•¨μˆ˜, 차선은 1ν•­ ν•¨μˆ˜'λΌλŠ” μ£Όμž₯이 ν₯λ―Έλ‘œμ› λ‹€. writeField(name)이 writeField(outputStream, name) 보닀 μ΄ν•΄ν•˜κΈ° 쉽닀. λ‹€ν•­ ν•¨μˆ˜κ°€ 항상 μ΄ν•΄ν•˜κΈ°κ°€ μ–΄λ €μ›Œμ„œ μ’Œμ ˆν•˜κ³€ ν–ˆλŠ”λ° λ‚˜λ§Œ μ–΄λ €μš΄κ²Œ μ•„λ‹ˆλΌλ‹ˆ 닀행이닀. 이항 ν•¨μˆ˜λ₯Ό μ“°λŠ” κ²½μš°λŠ” μ’Œν‘œλ₯Ό ν‘œν˜„ν•  λ•Œμ²˜λŸΌ λ‘κ°œ μΈμžκ°€ μžμ—°μ μΈ μˆœμ„œκ°€ μžˆλŠ” κ²½μš°λ‹€. μΈμˆ˜κ°€ λ§Žμ•„μ§„λ‹€λ©΄ outputStream.writeField(name)κ³Ό 같이 클래슀 λ³€μˆ˜λ‘œ μ„ μ–Έν•˜λŠ” 방법도 μžˆλ‹€.

  • Dijkstra μ•Œκ³ λ¦¬μ¦˜(μ΅œλ‹¨ 경둜 μ°ΎκΈ°)을 κ°œλ°œν•œ Edsger DijkstraλŠ” λͺ¨λ“  ν•¨μˆ˜μ—μ„œ μž…κ΅¬μ™€ μΆœκ΅¬κ°€ ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•΄μ•Ό ν•œλ‹€κ³  μ£Όμž₯ν–ˆλ‹€. λ£¨ν”„μ•ˆμ—μ„œ breakμ΄λ‚˜ continue λ₯Ό μ“°λŠ” 것을 μ§€μ–‘ν•˜κ³  ν•œλ²ˆμ˜ return만 ν•˜λ„λ‘.
  • 결둠은 ν›Œλ₯­ν•œ ν”„λ‘œκ·Έλž˜λ¨ΈλŠ” μ‹œμŠ€ν…œμ„ 'κ΅¬ν˜„ν• ' ν”„λ‘œκ·Έλž¨μ΄ μ•„λ‹ˆλΌ 'ν’€μ–΄κ°ˆ' μ΄μ•ΌκΈ°λ‘œ μ—¬κΈ΄λ‹€. ν•¨μˆ˜λŠ” 이야기λ₯Ό ν’€μ–΄κ°€λŠ” 언어이닀.

4. 주석

주석은 μ–Έμ œ, μ–΄λ–»κ²Œ 달아야 ν• κΉŒ? μ €μžλŠ” 주석을 달지 말라고 이야기 ν•œλ‹€. License 정보λ₯Ό λ‹΄λŠ” 것이 μ•„λ‹Œ 이상, ν•„μš”κ°€ μ—†λ‹€. μ½”λ“œμ™€ 주석도 항상 λ™κΈ°ν™”ν•΄μ•Όν•˜λ©° 쀑언뢀언 달린 주석은 였히렀 μ½”λ“œλ₯Ό μ½λŠ” 것을 λ°©ν•΄ν•œλ‹€. 차라리 μ„€λͺ…이 ν•„μš” μ—†λŠ” κΉ¨λ—ν•œ μ½”λ“œλ₯Ό μ“°μž.


5. ν˜•μ‹ λ§žμΆ”κΈ°

μ—°κ΄€λœ ν•¨μˆ˜λΌλ¦¬λŠ” 수직 거리λ₯Ό κ°€κΉκ²Œ, λ³€μˆ˜λŠ” μ‚¬μš©ν•˜λŠ” μœ„μΉ˜μ— μ΅œλŒ€ν•œ κ°€κΉŒμ΄ μ„ μ–Έν•˜κΈ°, μ΄μ™Έμ—λŠ” μ€„λ°”κΏˆμœΌλ‘œ κ°œλ… 뢄리(importλ¬Έκ³Ό ν•¨μˆ˜ 사이 μ€„λ°”κΏˆ) λ“± ν˜•μ‹μ— κ΄€ν•œ λ‚΄μš©μ΄μ—ˆλŠ”λ° 아직은 에디터에 μ˜μ‘΄ν•˜λŠ”κ²Œ 마음이 νŽΈν•˜λ‹€. PEP8 μŠ€νƒ€μΌ κ°€μ΄λ“œλ₯Ό μ°Έκ³ ν•˜κ±°λ‚˜ Pylama 같은 도ꡬλ₯Ό ν™œμš©ν•  μˆ˜λ„ μžˆλ‹€.


6. 객체와 자료 ꡬ쑰

  1. 좔상화

μ‚¬μš©μžκ°€ κ΅¬ν˜„μ΄ μ–΄λ–»κ²Œ λ˜μ–΄ μžˆλŠ”μ§€ μ•Œ ν•„μš”κ°€ μ—†λ‹€. 자료λ₯Ό μ„Έμ„Έν•˜κ²Œ κ³΅κ°œν•˜κΈ° λ³΄λ‹€λŠ” 좔상적인 κ°œλ…μœΌλ‘œ ν‘œν˜„ν•˜λŠ”κ²Œ μ’‹λ‹€. 좔상화 μˆ˜μ€€μ— 따라 ν•¨μˆ˜λ₯Ό wrapping ν•˜μ—¬ 논리λ₯Ό μˆ¨κ²¨μ£ΌλŠ” 기법이 μœ μš©ν•˜λ‹€.

# ꡬ체적 Bad
class Vehicle:
    def getFuelTankCapacityInGallons():
        # pass
    def getGallonsOfGalsoline():
        # pass

# 좔상적 Good
class Vehicle:
    def getPercentFuelRemaining():
        # pass
  1. 객체 지ν–₯ vs 절차 지ν–₯

μƒˆλ‘œμš΄ 데이터 νƒ€μž…μ΄ ν•„μš”ν•œ 경우라면 클래슀/객체 지ν–₯ 기법이 μ ν•©ν•˜λ‹€. 반면, μƒˆλ‘œμš΄ ν•¨μˆ˜κ°€ ν•„μš”ν•œ 경우라면 절차/μžλ£Œκ΅¬μ‘°κ°€ μ ν•©ν•˜λ‹€. μ‹œμŠ€ν…œμ„ μƒˆλ‘œ 지 λ•Œ μ–΄λ–€ 뢀뢄이 μœ μ—°ν•΄μ•Ό 할지 νŒλ‹¨ν•˜κ³  μ„ νƒν•˜λŠ” 것이 μ’‹λ‹€. 객체 지ν–₯은 자료λ₯Ό 숨기고 λ™μž‘(ν•¨μˆ˜)을 κ³΅κ°œν•œλ‹€. κ·Έλž˜μ„œ μƒˆλ‘œμš΄ 객체λ₯Ό 계속 μ°μ–΄λ‚΄κΈ°λŠ” μ‰½μ§€λ§Œ 클래슀 λ‚΄ μƒˆλ‘œμš΄ λ©”μ†Œλ“œλ₯Ό μΆ”κ°€ν•˜κΈ°λŠ” κ³¨μΉ˜μ•„ν”Œ 수 μžˆλ‹€. 반면 자료ꡬ쑰 ν˜•νƒœλ‘œ κ΅¬ν˜„ν–ˆμ„ 경우, 자료λ₯Ό μˆ¨κΈ°μ§€ μ•Šκ³  별닀λ₯Έ λ™μž‘λ„ μ—†λ‹€. κ·Έλž˜μ„œ μƒˆλ‘œμš΄ λ™μž‘μ„ μΆ”κ°€ν•˜κΈ° 쉽닀.

객체와 절차λ₯Ό κ³ λ―Όν•˜λ©° μ½”λ”©ν•΄λ³Έ 적이 μ—†μ–΄μ„œ 이 뢀뢄은 잘 와닿지 μ•Šμ•˜λ‹€. λ‚˜μ€‘μ— λ‹€μ‹œ ν•œλ²ˆ 읽어봐야 ν•  λ‚΄μš©μ΄λ‹€.


'Coding is as much an art as it is a science'
- Rebecca Parsons

To be continued!