Python - 13. 코드 품질과 테스팅

2025. 1. 20. 00:30프로그래밍 언어/Python

코드 품질과 테스팅은 안정적이고 유지보수 가능한 소프트웨어를 개발하기 위한 필수적인 과정입니다. 이번 섹션에서는 Python에서 제공하는 도구와 기법을 통해 코드 품질을 높이고, 효율적으로 디버깅 및 성능을 분석하는 방법을 다룹니다.


13.1 타입 힌트와 애너테이션

타입 힌트(Type Hint)

Python은 동적 타입 언어이지만, 타입 힌트를 사용하여 변수와 함수의 타입을 명시할 수 있습니다.

기본 예제

def greet(name: str) -> str:
    return f"Hello, {name}!"

name: str = "Alice"
print(greet(name))

Generic 타입

Generic 타입은 컬렉션의 요소 타입을 명시할 때 사용합니다.

from typing import List, Dict

def sum_numbers(numbers: List[int]) -> int:
    return sum(numbers)

data: Dict[str, int] = {"Alice": 30, "Bob": 25}
print(sum_numbers([1, 2, 3]))  # 6

Union과 Optional

  • Union: 여러 타입을 허용
  • Optional: 특정 타입 또는 None 허용
from typing import Union, Optional

def parse_value(value: Union[int, str]) -> str:
    return str(value)

def get_name(name: Optional[str] = None) -> str:
    return name if name else "Unknown"

print(parse_value(42))  # "42"
print(get_name())  # "Unknown"

TypeVar

TypeVar를 사용하여 제네릭 함수나 클래스를 정의할 수 있습니다.

from typing import TypeVar, List

T = TypeVar("T")

def get_first_element(elements: List[T]) -> T:
    return elements[0]

print(get_first_element([1, 2, 3]))  # 1
print(get_first_element(["a", "b", "c"]))  # "a"

13.2 단위 테스트 (unittest)

unittest란?

unittest는 Python 표준 라이브러리로 제공되는 단위 테스트 프레임워크입니다.

기본 사용법

import unittest

class TestMathOperations(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(2 + 3, 5)

    def test_subtraction(self):
        self.assertEqual(5 - 3, 2)

if __name__ == "__main__":
    unittest.main()

실행 결과

..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

13.3 pytest 활용

pytest란?

pytest는 간결하고 확장 가능한 Python 테스팅 프레임워크입니다.

설치

pip install pytest

기본 사용법

  1. 테스트 파일 이름은 test_로 시작하거나 _test로 끝나야 합니다.
  2. 테스트 함수는 test_로 시작해야 합니다.
def test_addition():
    assert 2 + 3 == 5

def test_subtraction():
    assert 5 - 3 == 2

pytest fixture

Fixture는 테스트 전에 필요한 환경을 설정하거나 초기 데이터를 제공합니다.

import pytest

@pytest.fixture
def sample_data():
    return [1, 2, 3]

def test_sum(sample_data):
    assert sum(sample_data) == 6

테스트 케이스 그룹화

class TestMathOperations:
    def test_addition(self):
        assert 2 + 2 == 4

    def test_subtraction(self):
        assert 5 - 3 == 2

Mock 활용

pytest-mock 패키지를 사용하여 모의 객체를 생성하고 동작을 테스트할 수 있습니다.

from unittest.mock import MagicMock

def test_mock_example():
    mock = MagicMock(return_value=10)
    assert mock() == 10

13.4 디버깅 기법

print()를 활용한 디버깅

코드 실행 중 특정 값을 출력하여 상태를 확인합니다.

pdb 모듈

Python의 기본 디버거로, 중단점 설정 및 단계별 실행을 지원합니다.

import pdb

def faulty_function(x):
    pdb.set_trace()
    return x + 1

faulty_function(5)

디버깅 툴

  • IDE 디버거: PyCharm, VSCode 등에서 제공하는 디버깅 도구 활용

13.5 코드 최적화와 성능

코드 최적화 원칙

  1. 불필요한 반복 제거
  2. 적합한 자료구조 사용
  3. 효율적인 알고리즘 선택

예제: 리스트 컴프리헨션 활용

# 비효율적
result = []
for i in range(10):
    result.append(i ** 2)

# 효율적
result = [i ** 2 for i in range(10)]

13.6 프로파일링과 벤치마킹

프로파일링

프로파일링은 코드의 실행 시간과 자원 사용을 분석하여 병목 지점을 파악합니다.

  1. cProfile 사용
import cProfile

def example():
    total = 0
    for i in range(100000):
        total += i
    return total

cProfile.run('example()')
  1. 메모리 프로파일링 memory_profiler를 사용하여 메모리 사용량을 분석합니다.
pip install memory_profiler
from memory_profiler import profile

@profile
def memory_intensive_function():
    data = [i ** 2 for i in range(100000)]
    return data

memory_intensive_function()

벤치마킹

벤치마킹은 코드의 성능을 비교하여 최적화를 평가합니다.

  1. timeit 모듈 사용
import timeit

setup = "nums = list(range(1000))"
stmt = "sum(nums)"
print(timeit.timeit(stmt, setup=setup, number=1000))

복잡도 분석

코드의 시간 복잡도와 공간 복잡도를 분석하여 최적화합니다.

def find_max(nums):
    max_num = nums[0]
    for num in nums:
        if num > max_num:
            max_num = num
    return max_num

# O(n) 시간 복잡도

이번 섹션에서는 Python의 코드 품질 향상 및 테스팅 기법을 다뤘습니다.