Python & AI Tutorials Logo
Python 프로그래밍

40. 깔끔하고 읽기 쉬운 코드 작성하기

이 책 전반에 걸쳐 여러분은 Python의 문법, 자료구조, 제어 흐름, 함수(function), 클래스(class), 그리고 그 밖의 다양한 프로그래밍 개념을 배웠습니다. 이제 동작하는 프로그램을 작성할 수 있습니다. 하지만 동작하는 코드와 유지보수 가능한 코드—즉 여러분과 다른 사람이 몇 달 혹은 몇 년 뒤에도 이해하고 수정하고 디버깅할 수 있는 코드—사이에는 결정적인 차이가 있습니다.

이 장은 깔끔하고 읽기 쉬운 코드(clean, readable code)를 작성하는 데 초점을 맞춥니다. Python 코드를 전문적이고 유지보수 가능하게 만드는 관례와 실천 방법을 배우게 됩니다. 이것들은 임의의 규칙이 아니라, 협업을 더 쉽게 하고 버그를 줄이며 나중에 다시 돌아왔을 때 자신의 코드를 이해하는 데 도움을 주는 검증된 가이드라인입니다.

40.1) 스타일이 중요한 이유: 코드를 읽기 vs. 쓰기

40.1.1) 코드는 쓰는 것보다 읽는 일이 더 많습니다

코드를 작성할 때는 몇 분에서 몇 시간 동안 만들어 냅니다. 하지만 그 코드는 여러 번 읽히게 됩니다. 디버깅할 때, 기능을 추가할 때, 다른 개발자가 함께 작업할 때, 그리고 몇 달 뒤에 무엇을 하는 코드였는지 기억하려고 다시 돌아왔을 때 말입니다.

다음은 동작하지만 스타일이 좋지 않은 코드입니다:

python
# 경고: 나쁜 스타일 - 시연용으로만 사용
def c(l):
    t=0
    for i in l:
        t=t+i
    return t/len(l)
 
data=[85,92,78,90,88]
result=c(data)
print(result)  # Output: 86.6

이 코드는 완벽하게 동작합니다. 숫자 리스트의 평균을 계산합니다. 하지만 무엇을 하는지 이해하려면 꼼꼼히 분석해야 합니다. 이제 다음 버전과 비교해 보세요:

python
def calculate_average(numbers):
    """숫자 리스트의 산술 평균을 계산합니다."""
    total = 0
    for number in numbers:
        total = total + number
    return total / len(numbers)
 
test_scores = [85, 92, 78, 90, 88]
average_score = calculate_average(test_scores)
print(average_score)  # Output: 86.6

두 번째 버전이 더 나은 이유는?

  • 함수 이름 (calculate_average)이 목적을 명확히 나타냄
  • 변수 이름 (numbers, total, test_scores)이 설명적임
  • Docstring이 함수가 하는 일을 설명함
  • 적절한 간격이 구조를 명확하게 만듦
  • 누구나 연구하지 않고도 이 코드를 이해할 수 있음

두 버전 모두 동일한 결과를 생성하지만, 두 번째 버전은 즉시 이해 가능합니다.

핵심 통찰: 코드는 한 번 쓰지만, 수십 번 또는 수백 번 읽게 됩니다. 명확한 이름과 포맷에 몇 초를 더 투자하면 나중의 혼란을 몇 시간이나 줄일 수 있습니다.

40.1.2) 가독성(readability)은 버그를 줄입니다

명확한 코드는 각 부분이 무엇을 하는지 빠르게 이해할 수 있기 때문에 디버깅하기 쉽습니다. 변수 이름이 설명적이고 구조가 깔끔하면 논리 오류를 더 쉽게 발견할 수 있습니다.

python
# 디버그하기 어려움 - 이 변수들이 무엇을 나타내나?
# WARNING: Poor style - for demonstration only
def process(x, y):
    if x > y:
        return x * (1 - y)
    return x
 
result = process(100, 0.1)
python
# 디버그하기 쉬움 - 무슨 일이 일어나는지 명확
def apply_discount(price, discount_rate):
    """할인율(0.0 to 1.0) 적용 후 가격을 계산합니다."""
    discount_amount = price * discount_rate
    final_price = price - discount_amount
    return final_price
 
original_price = 100
discount = 0.1  # 10% 할인
final_price = apply_discount(original_price, discount)
print(f"Final price: ${final_price}")
# Output: Final price: $90.0

두 번째 버전에서는 즉시 로직이 보입니다: "할인 금액을 계산한 다음, 가격에서 빼고 있다." 첫 번째 버전에서는 xy가 무엇을 나타내는지 머릿속으로 추적하고 x * (1 - y)가 무엇을 의미하는지 파악해야 합니다.

40.1.3) 일관성(consistency)은 협업을 가능하게 합니다

팀의 모든 사람이 같은 스타일 규약을 따르면 코드가 예측 가능해집니다. 서로 다른 포맷 스타일을 해독하느라 정신적 에너지를 낭비하지 않고, 로직을 이해하는 데 집중할 수 있습니다.

Python에는 PEP 8 (Python Enhancement Proposal 8)이라는 공식 스타일 가이드가 있습니다. PEP 8은 다음에 대한 규칙을 정의합니다:

  • 변수, 함수, 클래스의 이름을 짓는 방법
  • 코드를 포맷하는 방법 (간격, 줄 길이, 들여쓰기)
  • 주석과 docstring을 사용하는 시기
  • import를 구성하는 방법

PEP 8을 따르면 코드가 다른 Python 프로그래머에게 익숙하게 보여 협업이 더 원활해집니다. 다음 섹션에서 필수적인 PEP 8 가이드라인을 다룰 것입니다.

아니오

코드 작성

스타일 가이드를 따를까?

일관되고 읽기 쉬운 코드

일관되지 않은 코드

이해하기 쉬움

유지보수하기 쉬움

협업하기 쉬움

인지적 부담

혼란

버그

40.2) 네이밍 규약: 변수, 함수, 클래스(PEP 8)

40.2.1) 일반적인 네이밍 원칙

좋은 이름은 설명적(descriptive)이고 모호하지 않아야(unambiguous) 합니다. 구현을 읽지 않아도 무엇을 나타내거나 무엇을 하는지 알려줘야 합니다.

핵심 원칙:

  • 약어 대신 완전한 단어를 사용합니다(단, id, url, html처럼 매우 흔한 것은 예외)
  • 구체적으로 작성합니다: user_countcount보다 낫고, calculate_total_pricecalculate보다 낫습니다
  • 아주 짧은 반복문(loop)이나 수학 공식이 아니라면 한 글자 이름은 피합니다
  • 이름에 타입 정보를 넣지 않습니다(Python은 동적 타입 언어입니다)
python
# 나쁜 이름 - 무엇을 의미하는지 불명확함
# 경고: 나쁜 스타일 - 시연용으로만 사용
# 'n'이 무엇인가? 숫자(number)? 이름(name)? 노드(node)?
# 'd'가 무엇인가? 날짜(date)? 거리(distance)? 기간(duration)?
# 'l'이 무엇인가? 숫자 1처럼 보임!
n = "Alice"
d = 25
l = [1, 2, 3]
calc = lambda x: x * 2
 
# 좋은 이름 - 명확하고 설명적임
student_name = "Alice"
age_in_years = 25
test_scores = [1, 2, 3]
double_value = lambda x: x * 2

예외: 짧은 루프 변수

python
# 허용 가능: 매우 짧고, 맥락이 명확
for i in range(10):
    print(i)
 
for x, y in coordinates:
    distance = (x**2 + y**2) ** 0.5
 
# 하지만 명확성을 위해 설명적 이름이 더 좋음
for student_index in range(len(students)):
    print(students[student_index])
 
for point_x, point_y in coordinates:
    distance = (point_x**2 + point_y**2) ** 0.5

40.2.2) 변수와 함수 이름: snake_case

Python에서 변수와 함수는 snake_case를 사용합니다: 모두 소문자이며 단어는 밑줄로 구분합니다.

python
# Variables
user_name = "Bob"
total_price = 99.99
is_valid = True
max_retry_count = 3
 
# Functions
def calculate_tax(amount, rate):
    """주어진 금액에 대한 세금을 계산합니다."""
    return amount * rate
 
def send_email_notification(recipient, message):
    """지정된 수신자에게 이메일을 보냅니다."""
    print(f"Sending to {recipient}: {message}")
 
# Using the functions
tax_amount = calculate_tax(100, 0.08)
send_email_notification("user@example.com", "Welcome!")

왜 snake_case일까요? 가독성이 매우 좋기 때문입니다. 밑줄이 단어 경계를 명확하게 만들어 이름을 훑어보기 쉽습니다. calculatetotalprice(읽기 어려움)와 calculate_total_price(즉시 명확함)를 비교해 보세요.

40.2.3) 상수 이름: UPPER_SNAKE_CASE

상수(constant)—프로그램 실행 중에 변경되지 않아야 하는 값—는 UPPER_SNAKE_CASE를 사용합니다: 모두 대문자이며 밑줄로 구분합니다.

python
# Constants at module level
MAX_LOGIN_ATTEMPTS = 3
DEFAULT_TIMEOUT_SECONDS = 30
PI = 3.14159
DATABASE_URL = "postgresql://localhost/mydb"
 
def validate_password_length(password):
    """password가 최소 길이 요구사항을 만족하는지 확인합니다."""
    MIN_PASSWORD_LENGTH = 8  # 함수 내부의 상수
    return len(password) >= MIN_PASSWORD_LENGTH
 
# Using constants
if login_attempts > MAX_LOGIN_ATTEMPTS:
    print("Account locked")

중요: Python에는 상수를 다루는 문법이 없습니다. 일부 언어(JavaScript의 const나 Java의 final)와 달리, Python에는 변수를 변경할 수 없다고 선언하는 방법이 없습니다.

대신, Python 프로그래머들은 이름 짓기 규칙을 사용하여 의도를 나타냅니다:

  • UPPER_SNAKE_CASE는 의미: "이것을 상수로 사용할 의도입니다—수정하지 마세요"
  • 언어 기능이 아니라 프로그래머 간의 의사소통 도구입니다
python
# Python에는 상수 문법이 없음 - 이것은 그냥 일반 변수
MAX_LOGIN_ATTEMPTS = 3
 
# Python은 수정을 막지 않음
MAX_LOGIN_ATTEMPTS = 5  # ❌ 기술적으로 작동하지만, 규칙 위반
 
# 이름 짓기 규칙은 의도에 대한 신호:
# "대문자로 이름을 지어서 변경하지 않겠다는 의도를 보여줌"

모범 사례: 값이 프로그램 실행 중에 정말로 변경되어야 한다면, 상수로 이름 짓지 마세요:

python
# 이 값은 변경될 것 - 소문자 사용
max_login_attempts = 3
max_login_attempts = 5  # ✅ 괜찮음 - 이름이 변경 가능함을 나타냄
 
# 이 값은 절대 변경되지 않아야 함 - 대문자 사용
MAX_LOGIN_ATTEMPTS = 3
# 나중에 코드에서 재할당하지 마세요

이 규칙은 프로그래머가 당신의 의도를 이해하고 버그를 피하는 데 도움이 됩니다. MAX_LOGIN_ATTEMPTS를 보면, 수정하지 말아야 한다는 것을 알 수 있습니다.

40.2.4) 클래스 이름: PascalCase

클래스 이름은 PascalCase(CapWords라고도 함)를 사용합니다: 각 단어가 대문자로 시작하며 밑줄은 사용하지 않습니다.

python
# Class definitions
class Student:
    """이름과 성적을 가진 학생을 나타냅니다."""
    def __init__(self, name):
        self.name = name
        self.grades = []
 
class ShoppingCart:
    """쇼핑 카트의 아이템을 관리합니다."""
    def __init__(self):
        self.items = []
    
    def add_item(self, item):
        """카트에 아이템을 추가합니다."""
        self.items.append(item)
 
class DatabaseConnection:
    """데이터베이스 연결과 쿼리를 처리합니다."""
    def __init__(self, url):
        self.url = url
 
# Creating instances (note: instances use snake_case variable names)
student = Student("Alice")
shopping_cart = ShoppingCart()
db_connection = DatabaseConnection("localhost")

왜 클래스에는 PascalCase를 사용할까요? 함수와 변수에서 시각적으로 구분되기 때문입니다. Student()를 보면 즉시 클래스 인스턴스를 생성한다는 것을 알 수 있습니다. calculate_average()를 보면 함수 호출이라는 것을 알 수 있습니다.

40.2.5) 비공개 및 내부 이름: 선행 밑줄

단일 선행 밑줄(_name)로 시작하는 이름은 내부용(internal use)을 의미합니다—외부 코드가 아니라 모듈 또는 클래스 내부에서 사용하도록 의도된 것입니다.

Python에는 메서드나 속성을 "private"으로 표시하는 문법이 없습니다 (Java나 C++의 private와 달리). 대신, Python은 선행 언더스코어 (_name)를 사용한 이름 짓기 규칙으로 의도를 전달합니다.

_name의 의미:

  • "이것은 내부 용도 전용입니다"
  • "이것은 이 클래스/모듈 안에서 사용하기 위해 만들었고, 외부 코드용이 아닙니다"
  • "이것은 향후 버전에서 언제든지 변경될 수 있습니다—의존하지 마세요"
python
class BankAccount:
    """잔액 추적 기능이 있는 은행 계좌를 나타냅니다."""
    
    def __init__(self, account_number, initial_balance):
        self.account_number = account_number
        self._balance = initial_balance  # 내부 속성
    
    def deposit(self, amount):
        """계좌에 돈을 입금합니다."""
        if self._validate_amount(amount):  # 내부 메서드
            self._balance += amount
    
    def _validate_amount(self, amount):
        """거래 금액을 검증하는 내부 헬퍼입니다."""
        return amount > 0
    
    def get_balance(self):
        """현재 잔액을 반환합니다."""
        return self._balance
 
# Using the class
account = BankAccount("12345", 1000)
account.deposit(500)
print(account.get_balance())  # Output: 1500
 
# 기술적으로 작동하지만, 규칙 위반
print(account._balance)
# Output: 1500 (작동하지만, 이렇게 하면 안 됩니다!)
 
# 기술적으로 작동하지만, 규칙 위반
result = account._validate_amount(100)
# Output: True (작동하지만, 이렇게 하면 안 됩니다!)

핵심: Python은 _balance에 접근하거나 _validate_amount()를 호출하는 것을 막을 수 없습니다. 언더스코어는 보안 기능이 아니라 프로그래머 간의 신호입니다.

이 규칙이 존재하는 이유

Python이 프라이버시를 강제할 수 없기 때문에, 언더스코어는 클래스 작성자가 의도를 전달하는 방법입니다:

언더스코어가 나타내는 것:

  • "이것은 내부 구현입니다—향후 버전에서 변경될 수 있습니다"
  • "대신 공개 메서드를 사용하세요—안정적으로 유지될 것을 보장합니다"
  • "내부 세부사항에 의존하면, 라이브러리 업데이트 시 코드가 깨질 수 있습니다"

이 규칙은 계약을 만듭니다: 클래스 작성자는 내부 구현(_이 있는 것)을 자유롭게 변경할 수 있지만, 공개 인터페이스는 안정적으로 유지해야 합니다. 이를 통해 라이브러리가 사용자 코드를 깨뜨리지 않으면서 발전할 수 있습니다.

40.2.6) 특수 이름: 이중 밑줄

앞뒤로 이중 밑줄이 붙은 이름(__name__)은 Python이 정의한 특수 메서드(special methods) 또는 매직 메서드(magic methods)입니다. 이 패턴으로 여러분만의 이름을 만들지 마세요—Python이 사용하는 용도로 예약되어 있습니다.

python
class Point:
    """2차원 점을 나타냅니다."""
    
    def __init__(self, x, y):  # 특수 메서드: 초기화
        self.x = x
        self.y = y
    
    def __str__(self):  # 특수 메서드: 문자열 표현
        return f"Point({self.x}, {self.y})"
    
    def __add__(self, other):  # 특수 메서드: 덧셈 연산자
        return Point(self.x + other.x, self.y + other.y)
 
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1)  # Output: Point(1, 2)
print(p1 + p2)  # Output: Point(4, 6)

31장에서 배웠듯이, 이런 특수 메서드는 연산자 오버로딩(operator overloading)과 Python 내장 함수와의 통합을 가능하게 합니다.

40.2.7) 네이밍 요약 표

유형관례예시
변수snake_caseuser_name, total_count
함수snake_casecalculate_tax(), send_email()
상수UPPER_SNAKE_CASEMAX_SIZE, DEFAULT_TIMEOUT
클래스PascalCaseStudent, ShoppingCart
내부/비공개_leading_underscore_balance, _validate()
특수/매직double_underscore__init__, __str__

40.3) 코드 레이아웃: 들여쓰기, 공백, 빈 줄

40.3.1) 들여쓰기: 공백 4칸

Python은 들여쓰기로 코드 블록을 정의합니다. 들여쓰기 수준마다 항상 공백 4칸을 사용하세요—탭은 사용하지 말고, 탭과 공백을 섞어 쓰지도 마세요.

python
def calculate_grade(score):
    """숫자 점수로부터 문자 등급을 결정합니다."""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    else:
        return "F"
 
# 중첩 들여쓰기: 레벨마다 공백 4칸
def process_students(students):
    """학생 레코드 리스트를 처리합니다."""
    for student in students:
        if student["active"]:
            grade = calculate_grade(student["score"])
            print(f"{student['name']}: {grade}")
 
students = [
    {"name": "Alice", "score": 92, "active": True},
    {"name": "Bob", "score": 78, "active": True}
]
process_students(students)
# Output:
# Alice: A
# Bob: C

왜 공백 4칸일까요? Python 커뮤니티의 표준이기 때문입니다. 여러분이 접하는 대부분의 Python 코드는 공백 4칸을 사용하므로, 이 관례를 따르면 생태계와 일관된 코드를 작성할 수 있습니다.

에디터 설정하기: 최신 코드 편집기 대부분은 Tab 키를 눌렀을 때 공백 4칸을 삽입하도록 설정할 수 있습니다. 이렇게 하면 Tab 키의 편의성을 유지하면서도 공백 4칸 표준을 지킬 수 있습니다.

40.3.2) 최대 줄 길이: 79자

PEP 8은 줄을 79자로 제한할 것을 권장합니다 (docstring과 주석은 최대 99자). 이것이 제한적으로 보일 수 있지만, 실용적인 이점이 있습니다:

  • 작은 화면에서도 코드가 읽기 쉬움
  • 두 파일을 나란히 볼 수 있음
  • 복잡한 표현식을 더 간단한 부분으로 나누도록 장려

참고: 많은 현대 프로젝트는 약간 더 긴 제한(88자, 100자, 또는 120자)을 사용합니다. 핵심은 프로젝트 내 일관성입니다. 제한을 선택하고 고수하세요.

python
# 너무 김 - 읽기 어려움
# 경고: 나쁜 스타일 - 시연용으로만 사용
def calculate_monthly_payment(principal, annual_rate, years):
    return principal * (annual_rate / 12) * (1 + annual_rate / 12) ** (years * 12) / ((1 + annual_rate / 12) ** (years * 12) - 1)
 
# 더 나음 - 읽기 쉬운 줄로 분리
def calculate_monthly_payment(principal, annual_rate, years):
    """상환 공식(amortization formula)을 사용해 월 상환금을 계산합니다."""
    monthly_rate = annual_rate / 12
    num_payments = years * 12
    
    numerator = principal * monthly_rate * (1 + monthly_rate) ** num_payments
    denominator = (1 + monthly_rate) ** num_payments - 1
    
    return numerator / denominator
 
payment = calculate_monthly_payment(200000, 0.045, 30)
print(f"Monthly payment: ${payment:.2f}")  # Output: Monthly payment: $1013.37

긴 줄 나누기: 줄을 나눠야 할 때는 괄호, 대괄호, 중괄호 안에서의 암시적 줄 이어쓰기를 사용하세요:

python
# 긴 함수 호출
result = some_function(
    first_argument,
    second_argument,
    third_argument,
    fourth_argument
)
 
# 긴 리스트
colors = [
    "red", "green", "blue",
    "yellow", "orange", "purple",
    "pink", "brown", "gray"
]
 
# 긴 문자열
message = (
    "This is a very long message that needs to be broken "
    "across multiple lines for readability. Python automatically "
    "concatenates adjacent string literals."
)
print(message)
# Output: This is a very long message that needs to be broken across multiple lines for readability. Python automatically concatenates adjacent string literals.

40.3.3) 연산자 주변과 콤마 뒤의 공백

가독성을 높이기 위해 연산자 주변과 콤마 뒤에 공백을 사용하세요:

python
# 나쁜 공백 - 답답하고 읽기 어려움
# 경고: 나쁜 스타일 - 시연용으로만 사용
x=5
y=x*2+3
result=calculate_tax(100,0.08)
data=[1,2,3,4,5]
 
# 좋은 공백 - 명확하고 읽기 쉬움
x = 5
y = x * 2 + 3
result = calculate_tax(100, 0.08)
data = [1, 2, 3, 4, 5]
 
# 표현식에서의 공백
total = (price * quantity) + shipping_cost
is_valid = (age >= 18) and (has_license == True)
 
# 함수 정의에서의 공백
def calculate_discount(price, discount_rate, minimum_purchase=0):
    """최소 구매 금액을 만족하면 할인된 가격을 계산합니다."""
    if price >= minimum_purchase:
        return price * (1 - discount_rate)
    return price

예외: 키워드 인자나 기본 매개변수 값에서 = 주변에는 공백을 넣지 마세요:

python
# 키워드 인자의 올바른 공백
result = calculate_discount(price=100, discount_rate=0.1, minimum_purchase=50)
 
# 기본 매개변수의 올바른 공백
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

40.3.4) 논리적 분리를 위한 빈 줄

빈 줄을 사용해 코드의 논리적 섹션을 분리하세요:

최상위 함수와 클래스 사이에는 빈 줄 두 줄:

python
def first_function():
    """첫 번째 함수."""
    pass
 
 
def second_function():
    """두 번째 함수."""
    pass
 
 
class MyClass:
    """클래스 정의."""
    pass

클래스 내부 메서드 사이에는 빈 줄 한 줄:

python
class Student:
    """성적을 가진 학생을 나타냅니다."""
    
    def __init__(self, name):
        self.name = name
        self.grades = []
    
    def add_grade(self, grade):
        """학생 기록에 성적을 추가합니다."""
        self.grades.append(grade)
    
    def get_average(self):
        """학생의 평균 성적을 계산합니다."""
        if not self.grades:
            return 0
        return sum(self.grades) / len(self.grades)

함수 내부에서는 논리적 단계를 분리하기 위해 빈 줄을 사용합니다:

python
def process_order(order_items, customer):
    """고객 주문을 처리하고 총액을 계산합니다."""
    
    # 소계 계산
    subtotal = 0
    for item in order_items:
        subtotal += item["price"] * item["quantity"]
    
    # 고객 할인 적용
    discount = 0
    if customer["is_premium"]:
        discount = subtotal * 0.1
    
    # 세금 계산
    tax = (subtotal - discount) * 0.08
    
    # 최종 합계 계산
    total = subtotal - discount + tax
    
    return {
        "subtotal": subtotal,
        "discount": discount,
        "tax": tax,
        "total": total
    }

이런 빈 줄은 시각적인 "문단" 역할을 하여 코드 구조를 즉시 파악할 수 있게 해줍니다.

40.3.5) 줄 끝 공백(trailing whitespace) 피하기

줄 끝에 공백을 남기지 마세요—눈에 보이지 않지만 버전 관리 시스템이나 일부 편집기에서 문제를 일으킬 수 있습니다.

python
# 나쁨 - 보이지 않는 줄 끝 공백(설명을 위해 ·로 표시)
# 경고: 나쁜 스타일 - 시연용으로만 사용
def calculate(x):···
    return x * 2···
 
# 좋음 - 줄 끝 공백 없음
def calculate(x):
    return x * 2

대부분의 최신 에디터는 파일을 저장할 때 줄 끝 공백을 자동으로 제거하도록 설정할 수 있습니다.

40.4) 문서화: 도움이 되는 주석과 독스트링 작성하기

40.4.1) 주석을 써야 할 때

주석(comment)은 코드가 무엇을 하는지(what)가 아니라, 왜 그렇게 하는지(why)를 설명합니다. 이름이 잘 지어진 변수와 함수는 "무엇"을 하는지 분명해야 합니다.

python
# 나쁜 주석 - 뻔한 내용을 말함
# 경고: 나쁜 스타일 - 시연용으로만 사용
x = x + 1  # x에 1을 더함
 
# 좋은 주석 - 이유를 설명함
x = x + 1  # 0부터 시작하는 인덱싱에 맞추기 위해 조정
 
# 나쁜 주석 - 코드와 중복됨
# 경고: 나쁜 스타일 - 시연용으로만 사용
# age가 18보다 크거나 같은지 확인
if age >= 18:
    print("Adult")
 
# 좋은 주석 - 비즈니스 로직을 설명함
# 미국의 법적 음주 가능 연령
if age >= 21:
    print("Can purchase alcohol")

주석이 유용한 경우:

  1. 복잡한 알고리즘 설명:
python
def binary_search(sorted_list, target):
    """이진 탐색을 사용해 정렬된 리스트에서 target을 찾습니다."""
    left = 0
    right = len(sorted_list) - 1
    
    while left <= right:
        # 정수 오버플로를 피하면서 중간 지점을 계산
        # (right + left) // 2는 인덱스가 매우 클 때 오버플로가 날 수 있음
        mid = left + (right - left) // 2
        
        if sorted_list[mid] == target:
            return mid
        elif sorted_list[mid] < target:
            left = mid + 1  # target은 오른쪽 절반에 있음
        else:
            right = mid - 1  # target은 왼쪽 절반에 있음
    
    return -1  # target을 찾지 못함
  1. 자명하지 않은 비즈니스 규칙 명확화:
python
def calculate_shipping_cost(weight, distance):
    """무게와 거리를 기반으로 배송 비용을 계산합니다."""
    base_cost = 5.00
    
    # 무거운 물품에 대한 무료 배송 프로모션 (2024년 기준 회사 정책)
    # 대량 주문을 장려하고 단위당 배송 비용을 줄이기 위함
    if weight > 50:
        return 0
    
    # 표준 요금: 파운드당 $0.50 + 마일당 $0.10
    # 2024년 1분기에 협상된 운송업체 계약 기반
    return base_cost + (weight * 0.50) + (distance * 0.10)
  1. 우회 방법이나 임시 해결책 문서화:
python
def process_data(data):
    """들어오는 데이터 레코드를 처리합니다."""
    # TODO: 잘못된 형식의 레코드에 대한 임시 수정입니다
    # 상위에서 데이터 검증이 구현되면 제거하세요
    if not isinstance(data, list):
        data = [data]
    
    for record in data:
        # 각 레코드를 처리
        pass

40.4.2) 효과적인 독스트링 작성하기

독스트링(docstrings)은 모듈, 클래스, 함수 문서화를 위한 특수 주석입니다. 삼중 따옴표로 감싸며 정의의 첫 번째 문장으로 위치합니다.

python
def calculate_bmi(weight_kg, height_m):
    """
    체질량지수(BMI)를 계산합니다.
    
    BMI는 킬로그램 단위의 체중을 미터 단위 키의 제곱으로 나누어 계산합니다.
    
    Args:
        weight_kg: 체중(킬로그램, float 또는 int)
        height_m: 키(미터, float 또는 int)
    
    Returns:
        float: 계산된 BMI 값
    
    Example:
        >>> calculate_bmi(70, 1.75)
        22.857142857142858
    """
    return weight_kg / (height_m ** 2)
 
# 독스트링 접근하기
print(calculate_bmi.__doc__)
# Output:
#     체질량지수(BMI)를 계산합니다.
#     
#     BMI는 킬로그램 단위의 체중을 미터 단위 키의 제곱으로 나누어 계산합니다.
#     ...

단순한 함수에는 한 줄 독스트링을 사용합니다:

python
def square(x):
    """x의 제곱을 반환합니다."""
    return x * x
 
def is_even(n):
    """n이 짝수면 True, 아니면 False를 반환합니다."""
    return n % 2 == 0

복잡한 함수에는 여러 줄 독스트링을 사용합니다:

python
def find_prime_factors(n):
    """
    양의 정수의 모든 소인수를 찾습니다.
    
    이 함수는 곱하면 입력값이 되는 소수들의 리스트를 반환합니다.
    인수는 오름차순으로 반환됩니다.
    
    Args:
        n: 1보다 큰 양의 정수
    
    Returns:
        list: 오름차순의 소인수
    
    Raises:
        ValueError: n이 2보다 작을 때
    
    Example:
        >>> find_prime_factors(12)
        [2, 2, 3]
        >>> find_prime_factors(17)
        [17]
    """
    if n < 2:
        raise ValueError("n must be at least 2")
    
    factors = []
    divisor = 2
    
    while n > 1:
        while n % divisor == 0:
            factors.append(divisor)
            n = n // divisor
        divisor += 1
    
    return factors

클래스 독스트링:

python
class BankAccount:
    """
    입금 및 출금 작업을 지원하는 은행 계좌를 나타냅니다.
    
    이 클래스는 계좌 잔액을 유지하고 입금 및 출금 메서드를 제공합니다.
    모든 거래는 음수 잔액을 방지하기 위해 검증됩니다.
    
    Attributes:
        account_number: 계좌의 고유 식별자
        balance: 달러 기준 현재 계좌 잔액
    """
    
    def __init__(self, account_number, initial_balance=0):
        """
        새 은행 계좌를 초기화합니다.
        
        Args:
            account_number: 고유 계좌 식별자(문자열)
            initial_balance: 시작 잔액(기본값: 0)
        """
        self.account_number = account_number
        self.balance = initial_balance
    
    def deposit(self, amount):
        """
        계좌에 돈을 추가합니다.
        
        Args:
            amount: 입금할 금액(양수여야 함)
        
        Raises:
            ValueError: amount가 양수가 아닐 때
        """
        if amount <= 0:
            raise ValueError("Deposit amount must be positive")
        self.balance += amount

40.4.3) 독스트링 관례

첫 줄: 함수/클래스가 무엇을 하는지에 대한 간단한 요약입니다. 한 줄에 들어가야 합니다.

빈 줄: 요약과 자세한 설명을 분리합니다.

자세한 설명: 함수가 하는 일, 중요한 세부 사항, 사용 방법을 설명합니다.

Args/Parameters: 각 매개변수와 타입, 목적을 나열합니다.

Returns: 반환값과 타입을 설명합니다.

Raises: 함수가 발생시킬 수 있는 예외를 문서화합니다.

Example: 일반적인 사용 예시를 보여줍니다(선택 사항이지만 유용함).

python
def calculate_compound_interest(principal, rate, time, compounds_per_year=1):
    """
    투자에 대한 복리 이자를 계산합니다.
    
    복리 공식 A = P(1 + r/n)^(nt)를 사용합니다.
    여기서 A는 최종 금액, P는 원금, r은 연 이자율,
    n은 연 복리 횟수, t는 연 단위 기간입니다.
    
    Args:
        principal: 초기 투자 금액(float)
        rate: 소수로 표현한 연 이자율(예: 5%는 0.05)
        time: 투자 기간(년 단위, float)
        compounds_per_year: 연간 복리 계산 횟수
                           (기본값: 연 1회 복리)
    
    Returns:
        float: 복리 이자 적용 후 최종 금액
    
    Example:
        >>> calculate_compound_interest(1000, 0.05, 10, 12)
        1647.0095
    """
    return principal * (1 + rate / compounds_per_year) ** (compounds_per_year * time)

40.4.4) 향후 작업을 위한 TODO 주석

미래에 확인해야 할 부분을 표시하려면 TODO 주석을 사용하세요:

python
def process_payment(amount, payment_method):
    """결제 트랜잭션을 처리합니다."""
    # TODO: 암호화폐 결제 지원 추가
    # TODO: 사기 탐지 검사 구현
    
    if payment_method == "credit_card":
        return process_credit_card(amount)
    elif payment_method == "paypal":
        return process_paypal(amount)
    else:
        raise ValueError(f"Unsupported payment method: {payment_method}")

많은 편집기는 TODO 주석을 검색할 수 있어, 작업이 필요한 부분을 쉽게 찾을 수 있습니다.

40.5) 코드 구성하기: import, 상수, 함수, main

40.5.1) 표준 모듈 구조

잘 정리된 Python 모듈은 다음 구조를 따릅니다:

  1. 모듈 독스트링: 모듈이 무엇을 하는지 설명
  2. import: 표준 라이브러리, 서드파티, 로컬 import 순
  3. 상수: 모듈 레벨 상수
  4. 함수와 클래스: 주요 코드
  5. 메인 실행 블록: 스크립트가 실행될 때 동작하는 코드
python
"""
student_manager.py
 
성적 및 GPA 계산을 포함한 학생 기록을 관리합니다.
 
이 모듈은 학생 추가, 성적 기록,
평점 평균 계산을 위한 함수를 제공합니다.
"""
 
# 표준 라이브러리 import
import sys
from datetime import datetime
 
# 서드파티 import(있다면)
# import requests
 
# 로컬 import(있다면)
# from .database import save_student
 
# 상수
MAX_GRADE = 100
MIN_GRADE = 0
PASSING_GRADE = 60
 
# 함수
def calculate_gpa(grades):
    """
    숫자 성적 리스트에서 GPA를 계산합니다.
    
    Args:
        grades: 숫자 성적 리스트(0-100)
    
    Returns:
        float: 4.0 스케일의 GPA
    """
    if not grades:
        return 0.0
    
    average = sum(grades) / len(grades)
    
    # 4.0 스케일로 변환
    if average >= 90:
        return 4.0
    elif average >= 80:
        return 3.0
    elif average >= 70:
        return 2.0
    elif average >= 60:
        return 1.0
    else:
        return 0.0
 
def validate_grade(grade):
    """
    grade가 유효 범위 내에 있는지 확인합니다.
    
    Args:
        grade: 검증할 숫자 성적
    
    Returns:
        bool: grade가 유효하면 True, 아니면 False
    """
    return MIN_GRADE <= grade <= MAX_GRADE
 
# Main execution
if __name__ == "__main__":
    # 스크립트가 직접 실행될 때 동작하는 코드
    test_grades = [85, 92, 78, 88]
    gpa = calculate_gpa(test_grades)
    print(f"GPA: {gpa}")  # Output: GPA: 3.0

40.5.2) import 구성

import는 빈 줄로 구분된 세 섹션으로 그룹화하세요:

  1. 표준 라이브러리 import: Python 내장 모듈
  2. 서드파티 import: 설치된 패키지(requests, numpy 등)
  3. 로컬 import: 여러분의 모듈
python
# 표준 라이브러리 import
import os
import sys
from datetime import datetime, timedelta
from pathlib import Path
 
# 서드파티 import
import requests
from flask import Flask, render_template
 
# 로컬 애플리케이션 import
from myapp.database import connect_db
from myapp.models import User, Product
from myapp.utils import format_currency

import 스타일:

python
# 모듈 전체 import
import math
result = math.sqrt(16)  # Output: 4.0
 
# 특정 항목 import
from math import sqrt, pi
result = sqrt(16)  # Output: 4.0
 
# 별칭(alias)으로 import
import numpy as np
array = np.array([1, 2, 3])
 
# 여러 항목 import
from os import path, getcwd, listdir

와일드카드 import(from module import *)는 피하세요—이름이 어디서 왔는지 불명확해집니다:

python
# 나쁨 - sqrt가 어디서 왔는지 불명확함
# 경고: 나쁜 스타일 - 시연용으로만 사용
from math import *
result = sqrt(16)
 
# 좋음 - 명시적 import
from math import sqrt
result = sqrt(16)

40.5.3) 상수 구성하기

모듈 레벨 상수는 import 뒤, 상단 근처에 배치하세요:

python
"""애플리케이션의 설정 값."""
 
import os
 
# 애플리케이션 상수
APP_NAME = "Student Manager"
VERSION = "1.0.0"
DEBUG_MODE = True
 
# 데이터베이스 설정
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///students.db")
MAX_CONNECTIONS = 10
 
# 비즈니스 규칙
MAX_STUDENTS_PER_CLASS = 30
PASSING_GRADE = 60
GRADE_WEIGHTS = {
    "homework": 0.3,
    "midterm": 0.3,
    "final": 0.4
}
 
def calculate_final_grade(homework, midterm, final):
    """가중치를 적용한 최종 성적을 계산합니다."""
    return (
        homework * GRADE_WEIGHTS["homework"] +
        midterm * GRADE_WEIGHTS["midterm"] +
        final * GRADE_WEIGHTS["final"]
    )

40.5.4) 논리적인 함수 순서

함수는 논리적인 순서로 구성하세요:

  1. 공개 함수(public functions)를 먼저: 다른 모듈에서 사용하도록 의도된 함수
  2. 헬퍼 함수(helper functions)를 뒤에: 공개 함수를 지원하는 내부 함수
  3. 관련 함수끼리 묶기: 함께 동작하는 함수는 그룹화
python
"""주문 처리 모듈."""
 
# Public API functions
def process_order(order_items, customer):
    """
    고객 주문을 처리합니다.
    
    주문 처리를 위한 메인 엔트리 포인트입니다.
    """
    subtotal = _calculate_subtotal(order_items)
    discount = _calculate_discount(subtotal, customer)
    tax = _calculate_tax(subtotal - discount)
    total = subtotal - discount + tax
    
    return {
        "subtotal": subtotal,
        "discount": discount,
        "tax": tax,
        "total": total
    }
 
def validate_order(order_items):
    """주문이 유효한 아이템을 포함하는지 검증합니다."""
    if not order_items:
        return False
    
    for item in order_items:
        if not _validate_item(item):
            return False
    
    return True
 
# Internal helper functions
def _calculate_subtotal(items):
    """주문 소계를 계산합니다(내부 사용)."""
    total = 0
    for item in items:
        total += item["price"] * item["quantity"]
    return total
 
def _calculate_discount(subtotal, customer):
    """고객 할인을 계산합니다(내부 사용)."""
    if customer.get("is_premium"):
        return subtotal * 0.1
    return 0
 
def _calculate_tax(amount):
    """판매세를 계산합니다(내부 사용)."""
    TAX_RATE = 0.08
    return amount * TAX_RATE
 
def _validate_item(item):
    """단일 주문 아이템을 검증합니다(내부 사용)."""
    required_fields = ["name", "price", "quantity"]
    return all(field in item for field in required_fields)

공개 함수(process_order, validate_order)가 먼저 나오고, _가 붙은 헬퍼 함수는 뒤에 오는 것을 확인할 수 있습니다. 이렇게 하면 어떤 함수가 주요 API인지 명확해집니다.

40.5.5) 모듈 내 클래스 구성

모듈에 클래스가 포함되어 있다면 논리적으로 정렬하세요:

python
"""사용자 관리 시스템."""
 
# 상수
DEFAULT_ROLE = "user"
ADMIN_ROLE = "admin"
 
# 기본 클래스는 먼저
class User:
    """기본 사용자 클래스."""
    
    def __init__(self, username, email):
        self.username = username
        self.email = email
        self.role = DEFAULT_ROLE
    
    def can_edit(self, resource):
        """사용자가 리소스를 편집할 수 있는지 확인합니다."""
        return resource.owner == self.username
 
# 파생 클래스는 기본 클래스 뒤에
class AdminUser(User):
    """권한이 강화된 관리자."""
    
    def __init__(self, username, email):
        super().__init__(username, email)
        self.role = ADMIN_ROLE
    
    def can_edit(self, resource):
        """관리자는 어떤 리소스든 편집할 수 있습니다."""
        return True
 
# 관련 클래스는 함께 그룹화
class Resource:
    """소유 및 편집될 수 있는 리소스를 나타냅니다."""
    
    def __init__(self, name, owner):
        self.name = name
        self.owner = owner
 
# 클래스와 관련된 유틸리티 함수
def create_user(username, email, is_admin=False):
    """적절한 사용자 타입을 생성하는 팩토리 함수입니다."""
    if is_admin:
        return AdminUser(username, email)
    return User(username, email)

클래스 구성 원칙:

  • 기본 클래스를 파생 클래스보다 먼저 배치 (독자가 기본 클래스를 먼저 이해해야 함)
  • 관련된 클래스들을 함께 그룹화 (User와 Resource는 관련되어 있음)
  • 클래스와 함께 작동하는 유틸리티 함수는 클래스 정의 다음에 배치
  • 각 클래스는 목적을 설명하는 명확한 docstring을 가져야 함

40.6) if name == "main" 패턴

40.6.1) 패턴 이해하기

모든 Python 파일에는 __name__이라는 내장 변수가 있습니다. Python은 파일이 어떻게 사용되는지에 따라 이 변수의 값을 자동으로 설정합니다:

  • 파일을 직접 실행할 때 (예: python my_script.py), Python은 __name__"__main__"으로 설정
  • 파일을 모듈로 임포트할 때, Python은 __name__을 모듈 이름(.py 확장자를 제외한 파일명)으로 설정

이를 통해 파일이 직접 실행될 때만 실행되고, 임포트될 때는 실행되지 않는 코드를 작성할 수 있습니다:

python
"""math_utils.py - 수학 유틸리티 함수."""
 
def add(a, b):
    """두 수를 더합니다."""
    return a + b
 
def multiply(a, b):
    """두 수를 곱합니다."""
    return a * b
 
# 이 코드는 파일이 직접 실행될 때만 실행됩니다
if __name__ == "__main__":
    # 함수 테스트
    print(f"5 + 3 = {add(5, 3)}")  # Output: 5 + 3 = 8
    print(f"5 * 3 = {multiply(5, 3)}")  # Output: 5 * 3 = 15

python math_utils.py를 실행하면 출력이 보입니다. 하지만 다른 파일에서 import하면:

python
# another_file.py
from math_utils import add, multiply
 
result = add(10, 20)
print(result)  # Output: 30
# math_utils.py의 테스트 코드는 실행되지 않습니다

임포트될 때는 (if __name__ == "__main__": 안의) 테스트 코드가 실행되지 않음을 주목하세요!

python math_utils.py

import math_utils

Python 파일 실행

어떻게 실행되나요?

name = 'main'

name = 'math_utils'

if name == 'main' 안의 코드가 실행됨

if name == 'main' 안의 코드가 건너뜀

40.6.2) 이 패턴이 중요한 이유

이 패턴은 몇 가지 중요한 목적을 제공합니다:

1. 테스트 및 시연: 함수와 같은 파일 안에 사용 예시를 포함할 수 있습니다:

python
"""temperature.py - 온도 변환 유틸리티."""
 
def celsius_to_fahrenheit(celsius):
    """섭씨를 화씨로 변환합니다."""
    return (celsius * 9/5) + 32
 
def fahrenheit_to_celsius(fahrenheit):
    """화씨를 섭씨로 변환합니다."""
    return (fahrenheit - 32) * 5/9
 
if __name__ == "__main__":
    # 함수 시연
    print("Temperature Conversion Examples:")
    print(f"0°C = {celsius_to_fahrenheit(0)}°F")  # Output: 0°C = 32.0°F
    print(f"100°C = {celsius_to_fahrenheit(100)}°F")  # Output: 100°C = 212.0°F
    print(f"32°F = {fahrenheit_to_celsius(32)}°C")  # Output: 32°F = 0.0°C

2. 재사용 가능한 모듈: 같은 파일을 독립 실행 스크립트이자 import 가능한 모듈로 사용할 수 있습니다:

python
"""data_processor.py - 데이터 파일을 처리하고 분석합니다."""
 
import sys
 
def load_data(filename):
    """파일에서 데이터를 로드합니다."""
    with open(filename) as f:
        return [line.strip() for line in f]
 
def analyze_data(data):
    """데이터에 대한 분석을 수행합니다."""
    return {
        "count": len(data),
        "average_length": sum(len(item) for item in data) / len(data)
    }
 
if __name__ == "__main__":
    # 스크립트로 실행될 때는 커맨드라인 인자를 처리합니다
    if len(sys.argv) < 2:
        print("Usage: python data_processor.py <filename>")
        sys.exit(1)
    
    filename = sys.argv[1]
    data = load_data(filename)
    results = analyze_data(data)
    
    print(f"Processed {results['count']} items")
    print(f"Average length: {results['average_length']:.2f}")

스크립트로 실행할 수 있습니다:

bash
$ python data_processor.py data.txt
Processed 42 items
Average length: 15.23

또는 다른 파일에서 import할 수 있습니다:

python
# my_analysis.py
from data_processor import load_data, analyze_data
 
my_data = load_data("myfile.txt")
results = analyze_data(my_data)
print(f"Found {results['count']} items")

40.6.3) 메인 블록의 일반적인 패턴

패턴 1: 간단한 테스트 케이스

python
"""calculator.py - 기본 계산기 연산."""
 
def add(a, b):
    """두 수를 더합니다."""
    return a + b
 
def subtract(a, b):
    """b를 a에서 뺍니다."""
    return a - b
 
if __name__ == "__main__":
    # 빠른 테스트
    assert add(2, 3) == 5
    assert subtract(10, 4) == 6
    print("All tests passed!")  # Output: All tests passed!

패턴 2: main 함수

더 복잡한 스크립트에서는 main() 함수를 정의합니다:

python
"""report_generator.py - 데이터로부터 보고서를 생성합니다."""
 
import sys
 
def load_data(filename):
    """파일에서 데이터를 로드합니다."""
    # Implementation here
    pass
 
def generate_report(data):
    """데이터로부터 보고서를 생성합니다."""
    # Implementation here
    pass
 
def save_report(report, output_file):
    """보고서를 파일로 저장합니다."""
    # Implementation here
    pass
 
def main():
    """스크립트의 메인 엔트리 포인트입니다."""
    if len(sys.argv) < 3:
        print("Usage: python report_generator.py <input> <output>")
        return 1
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    
    try:
        data = load_data(input_file)
        report = generate_report(data)
        save_report(report, output_file)
        print(f"Report saved to {output_file}")
        return 0
    except Exception as e:
        print(f"Error: {e}")
        return 1
 
if __name__ == "__main__":
    # main의 상태 코드로 종료 (0 = 성공, 1 = 오류)
    sys.exit(main())

이 패턴에는 몇 가지 장점이 있습니다:

  • main() 함수를 독립적으로 테스트할 수 있습니다
  • 스크립트의 명확한 엔트리 포인트가 됩니다
  • 올바른 종료 코드(성공은 0, 오류는 0이 아님)를 제공합니다
  • 스크립트 로직과 모듈 함수 사이의 깔끔한 분리를 제공합니다

40.6.4) 메인 블록을 위한 모범 사례

메인 블록은 핵심만: if __name__ == "__main__" 안의 코드는 주로 스크립트 실행을 처리해야 하며, 복잡한 로직을 담지 않아야 합니다:

python
# 나쁨 - 메인 블록에 복잡한 로직
# 경고: 나쁜 스타일 - 시연용으로만 사용
if __name__ == "__main__":
    data = []
    for i in range(100):
        if i % 2 == 0:
            data.append(i * 2)
    result = sum(data) / len(data)
    print(result)
 
# 좋음 - 로직은 함수에, 메인 블록은 조율만
def generate_even_doubles(limit):
    """limit까지의 짝수를 두 배한 값을 생성합니다."""
    return [i * 2 for i in range(limit) if i % 2 == 0]
 
def calculate_average(numbers):
    """numbers의 평균을 계산합니다."""
    return sum(numbers) / len(numbers)
 
if __name__ == "__main__":
    data = generate_even_doubles(100)
    result = calculate_average(data)
    print(result)  # Output: 99.0

복잡한 스크립트에는 main() 함수 사용: 앞에서 본 것처럼 main() 함수를 정의하면 스크립트를 더 테스트하기 쉽고 정돈되게 만들 수 있습니다.

스크립트 사용법 문서화: 스크립트가 커맨드라인 인자를 받는다면 문서화하세요:

python
"""
file_processor.py - 다양한 작업으로 텍스트 파일을 처리합니다.
 
Usage:
    python file_processor.py <input_file> <output_file> [--uppercase]
 
Arguments:
    input_file: 입력 파일 경로
    output_file: 출력 파일 경로
    --uppercase: 텍스트를 대문자로 변환(선택)
"""
 
import sys
 
def process_file(input_path, output_path, uppercase=False):
    """지정된 옵션으로 파일을 처리합니다."""
    with open(input_path) as f:
        content = f.read()
    
    if uppercase:
        content = content.upper()
    
    with open(output_path, 'w') as f:
        f.write(content)
 
if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(__doc__)  # 모듈 독스트링 출력
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    uppercase = "--uppercase" in sys.argv
    
    process_file(input_file, output_file, uppercase)
    print(f"Processed {input_file} -> {output_file}")

깔끔하고 읽기 쉬운 코드를 작성하는 능력은 연습을 통해 발전하는 기술입니다. 이 장의 관례와 패턴은 임의의 규칙이 아니라, 코드를 더 이해하기 쉽고 유지보수와 디버깅이 쉬워지게 만드는 검증된 실천 방법입니다. 더 많은 Python 코드를 작성할수록 이러한 패턴은 자연스럽게 몸에 익을 것입니다.

기억하세요: 코드는 쓰는 것보다 훨씬 더 자주 읽힙니다. 명확한 이름을 고르고, 도움이 되는 주석을 추가하고, import를 제대로 정리하는 데 들이는 몇 초는 나중에—여러분과 여러분의 코드로 작업하는 다른 사람 모두에게—몇 시간의 혼란을 줄여줄 것입니다.

다음 장에서는 이런 클린 코드 실천을 바탕으로 하는 디버깅(debugging)과 테스트(testing) 기법을 살펴보며, 읽기 쉬운 코드뿐 아니라 올바르고 신뢰할 수 있는 코드도 작성할 수 있도록 도와드리겠습니다.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai