-
Notifications
You must be signed in to change notification settings - Fork 0
KR_BestPractices
somaz edited this page Apr 22, 2025
·
4 revisions
명확하고 일관된 코드 작성 방법을 설명한다.
# 1. 명확한 변수명과 함수명 사용
# 나쁜 예
def f(x):
return x * 2
# 좋은 예
def double_number(number: int) -> int:
return number * 2
# 2. 클래스 구조화
class User:
"""사용자 정보를 관리하는 클래스"""
def __init__(self, name: str, email: str):
self.name = name
self.email = email
@property
def display_name(self) -> str:
return f"{self.name} <{self.email}>"✅ 특징:
- 명확한 이름 사용
- 타입 힌트 활용
- 문서화 주석 추가
효과적인 예외 처리 및 에러 관리 방법을 다룬다.
from typing import Any, Dict, Optional
import logging
class CustomError(Exception):
"""사용자 정의 예외"""
def __init__(self, message: str, error_code: int):
self.message = message
self.error_code = error_code
super().__init__(self.message)
def safe_operation(func):
"""에러 처리 데코레이터"""
def wrapper(*args, **kwargs) -> Dict[str, Any]:
try:
result = func(*args, **kwargs)
return {'success': True, 'data': result}
except CustomError as e:
logging.error(f"Custom error: {e.message}")
return {
'success': False,
'error': e.message,
'error_code': e.error_code
}
except Exception as e:
logging.exception("Unexpected error occurred")
return {
'success': False,
'error': str(e),
'error_code': 500
}
return wrapper✅ 특징:
- 커스텀 예외 정의
- 로깅 활용
- 데코레이터 패턴 사용
- 구조화된 에러 응답
- 상세한 에러 정보 제공
애플리케이션 설정을 효율적으로 관리하는 방법이다.
from dataclasses import dataclass
from typing import Optional
import yaml
import os
@dataclass
class DatabaseConfig:
host: str
port: int
username: str
password: str
database: str
@dataclass
class AppConfig:
debug: bool
secret_key: str
db: DatabaseConfig
class ConfigManager:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
self.config = self.load_config()
def load_config(self) -> AppConfig:
env = os.getenv('APP_ENV', 'development')
config_path = f'config/{env}.yml'
with open(config_path) as f:
config_data = yaml.safe_load(f)
return AppConfig(
debug=config_data['debug'],
secret_key=config_data['secret_key'],
db=DatabaseConfig(**config_data['database'])
)✅ 특징:
- 데이터클래스 활용
- 싱글톤 패턴 구현
- 환경별 설정 분리
- 타입 안전성 보장
- 계층적 구조화
품질 높은 코드를 위한 테스트 작성 방법을 설명한다.
import pytest
from typing import List, Dict
class Calculator:
def add(self, x: int, y: int) -> int:
return x + y
def divide(self, x: int, y: int) -> float:
if y == 0:
raise ValueError("Cannot divide by zero")
return x / y
class TestCalculator:
@pytest.fixture
def calculator(self):
return Calculator()
def test_add(self, calculator):
assert calculator.add(2, 3) == 5
def test_divide(self, calculator):
assert calculator.divide(6, 2) == 3.0
def test_divide_by_zero(self, calculator):
with pytest.raises(ValueError):
calculator.divide(1, 0)✅ 특징:
- pytest 활용
- 픽스처 사용
- 예외 테스트 포함
- 명확한 테스트 구조
- 단위 테스트 분리
효과적인 코드 문서화 방법과 도구를 설명한다.
from typing import List, Optional
class DocumentationExample:
"""
문서화 예시 클래스
이 클래스는 파이썬 문서화의 모범 사례를 보여준다.
Attributes:
name (str): 객체의 이름
value (int): 객체의 값
"""
def __init__(self, name: str, value: int):
self.name = name
self.value = value
def process_data(self, data: List[int]) -> Optional[float]:
"""
데이터를 처리하고 결과를 반환한다.
Args:
data (List[int]): 처리할 정수 리스트
Returns:
Optional[float]: 처리된 결과값, 실패시 None
Raises:
ValueError: 빈 리스트가 입력된 경우
"""
if not data:
raise ValueError("Empty data list")
try:
return sum(data) / len(data)
except Exception:
return None✅ 특징:
- 도큐멘트 스트링 활용
- 타입 힌트 포함
- 예외 명세 기록
- 일관된 문서화 스타일
- 속성 및 메서드 설명
실제 애플리케이션에서 사용할 수 있는 모범 사례 예제를 살펴본다.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
class UserBase(BaseModel):
name: str
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
is_active: bool
class UserAPI:
def __init__(self):
self.app = FastAPI()
self.setup_routes()
def setup_routes(self):
@self.app.post("/users/", response_model=User)
async def create_user(user: UserCreate):
return await self.create_user_handler(user)
async def create_user_handler(self, user: UserCreate) -> User:
# 사용자 생성 로직
pass✅ 특징:
- FastAPI 활용
- Pydantic 모델 사용
- 컨텍스트 매니저 활용
from contextlib import contextmanager
from typing import Generator
from sqlalchemy.orm import Session
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
class DatabaseManager:
def __init__(self, connection_string: str):
self.engine = create_engine(connection_string)
self.SessionLocal = sessionmaker(bind=self.engine)
@contextmanager
def get_session(self) -> Generator[Session, None, None]:
session = self.SessionLocal()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
def execute_transaction(self, operations):
with self.get_session() as session:
return operations(session)✅ 특징:
- 데이터베이스 접근 패턴 보여줌
import asyncio
from typing import List, Dict, Any
import aiohttp
class AsyncDataFetcher:
def __init__(self, base_url: str):
self.base_url = base_url
self.session = None
async def __aenter__(self):
self.session = aiohttp.ClientSession()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.session:
await self.session.close()
async def fetch_data(self, endpoint: str) -> Dict[str, Any]:
if not self.session:
raise RuntimeError("세션이 초기화되지 않았습니다. 컨텍스트 매니저로 사용하세요.")
url = f"{self.base_url}/{endpoint}"
async with self.session.get(url) as response:
if response.status != 200:
raise ValueError(f"데이터 가져오기 실패: {response.status}")
return await response.json()
async def fetch_multiple(self, endpoints: List[str]) -> List[Dict[str, Any]]:
tasks = [self.fetch_data(endpoint) for endpoint in endpoints]
return await asyncio.gather(*tasks, return_exceptions=True)
# 사용 예:
async def main():
async with AsyncDataFetcher("https://api.example.com") as fetcher:
data = await fetcher.fetch_multiple(["users", "products", "orders"])
print(data)
# 실행
# asyncio.run(main())✅ 특징:
- 비동기 코드 구성
- 컨텍스트 매니저 활용
- 타입 안전성 보장
- 에러 처리 고려
import logging
import time
from functools import wraps
from typing import Any, Callable, Dict
# 로거 설정
def setup_logger(name: str, level=logging.INFO) -> logging.Logger:
logger = logging.getLogger(name)
logger.setLevel(level)
if not logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
# 성능 측정 데코레이터
def measure_performance(logger: logging.Logger):
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
start_time = time.time()
result = func(*args, **kwargs)
execution_time = time.time() - start_time
logger.info(f"함수 {func.__name__} 실행 시간: {execution_time:.4f}초")
return result
return wrapper
return decorator
# 사용 예시
logger = setup_logger("app_logger")
@measure_performance(logger)
def process_data(data: Dict[str, Any]) -> Dict[str, Any]:
# 데이터 처리 로직
time.sleep(0.5) # 처리 시간 시뮬레이션
return {"processed": True, "original": data}
# 실행
# result = process_data({"id": 1, "value": "test"})✅ 특징:
- 로깅 및 성능 모니터링
- 컨텍스트 매니저 활용
- 타입 안전성 보장
- 에러 처리 고려
✅ 특징:
- API 디자인 패턴
- 데이터베이스 트랜잭션 관리
- 비동기 코드 구성
- 로깅 및 성능 모니터링
- 컨텍스트 매니저 활용
- 타입 안전성 보장
- 에러 처리 고려
파이썬 코드의 성능을 개선하는 방법을 설명한다.
from collections import defaultdict, deque
from typing import List, Dict, Set, Deque
# 리스트보다 효율적인 작업을 위한 데크 활용
def sliding_window(data: List[int], window_size: int) -> List[List[int]]:
"""
데크를 사용한 효율적인 슬라이딩 윈도우 구현
"""
result = []
window: Deque[int] = deque(maxlen=window_size)
for item in data:
window.append(item)
if len(window) == window_size:
result.append(list(window))
return result
# 루프 대신 딕셔너리 활용
def count_occurrences(items: List[str]) -> Dict[str, int]:
"""
각 항목의 발생 횟수를 계산
"""
counter = defaultdict(int)
for item in items:
counter[item] += 1
return dict(counter)
# 집합을 사용한 효율적인 중복 제거
def find_unique(items: List[str]) -> List[str]:
"""
집합을 사용하여 중복 제거 및 순서 유지
"""
seen: Set[str] = set()
return [item for item in items if not (item in seen or seen.add(item))]import functools
import time
from typing import Dict, Any, Callable, TypeVar
T = TypeVar('T')
# 메모이제이션을 통한 비용이 많이 드는 계산 최적화
@functools.lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
"""
피보나치 수열 계산 (캐싱 사용)
"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 제너레이터를 사용한 메모리 효율적인 처리
def process_large_file(file_path: str, chunk_size: int = 1024):
"""
대용량 파일을 청크 단위로 처리하는 제너레이터
"""
with open(file_path, 'r') as f:
while True:
data = f.read(chunk_size)
if not data:
break
yield data
# 리스트 컴프리헨션 사용 (루프보다 효율적)
def transform_data(data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
리스트 컴프리헨션을 사용한 데이터 변환
"""
return [{
'id': item['id'],
'name': item['name'].upper(),
'value': item['value'] * 2
} for item in data if item['active']]✅ 특징:
- 적절한 자료구조 선택
- 메모이제이션 활용
- 제너레이터와 이터레이터 활용
- 리스트 컴프리헨션 최적화
- 불필요한 계산 방지
- 메모리 사용량 고려
파이썬 애플리케이션의 보안을 강화하는 방법을 설명한다.
import hashlib
import os
import hmac
import binascii
from typing import Tuple
def hash_password(password: str) -> Tuple[str, str]:
"""
비밀번호를 안전하게 해싱하고 솔트와 해시를 반환한다
Args:
password: 해싱할 원본 비밀번호
Returns:
(salt, hash) 튜플
"""
# 랜덤 솔트 생성
salt = os.urandom(32)
# 비밀번호 해싱
pw_hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
100000 # 반복 횟수
)
# 16진수 문자열로 변환
salt_hex = binascii.hexlify(salt).decode('utf-8')
hash_hex = binascii.hexlify(pw_hash).decode('utf-8')
return salt_hex, hash_hex
def verify_password(password: str, salt_hex: str, stored_hash: str) -> bool:
"""
입력된 비밀번호가 저장된 해시와 일치하는지 확인한다
Args:
password: 확인할 비밀번호
salt_hex: 저장된 솔트(16진수 문자열)
stored_hash: 저장된 해시(16진수 문자열)
Returns:
비밀번호 일치 여부
"""
# 솔트를 바이트로 변환
salt = binascii.unhexlify(salt_hex)
# 입력된 비밀번호 해싱
pw_hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
100000 # 반복 횟수 (저장 시와 동일)
)
# 해시 비교 (타이밍 공격 방지를 위해 hmac.compare_digest 사용)
new_hash = binascii.hexlify(pw_hash).decode('utf-8')
return hmac.compare_digest(new_hash, stored_hash)import sqlite3
from typing import List, Dict, Any, Tuple
class SafeDatabase:
"""안전한 데이터베이스 작업을 위한 클래스"""
def __init__(self, db_path: str):
self.db_path = db_path
self.connection = None
def connect(self):
self.connection = sqlite3.connect(self.db_path)
self.connection.row_factory = sqlite3.Row
def close(self):
if self.connection:
self.connection.close()
def __enter__(self):
self.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
def safe_query(self, query: str, params: Tuple = ()) -> List[Dict[str, Any]]:
"""
매개변수화된 쿼리를 안전하게 실행한다
Args:
query: SQL 쿼리(매개변수는 ? 사용)
params: 쿼리 매개변수
Returns:
쿼리 결과 리스트
"""
cursor = self.connection.cursor()
cursor.execute(query, params)
results = [dict(row) for row in cursor.fetchall()]
cursor.close()
return results
# 잘못된 방법 (참고용으로만 표시)
def unsafe_query(self, user_input: str) -> List[Dict[str, Any]]:
"""
절대 사용하면 안 되는 방법
SQL 인젝션에 취약함
"""
query = f"SELECT * FROM users WHERE username = '{user_input}'"
cursor = self.connection.cursor()
cursor.execute(query)
results = [dict(row) for row in cursor.fetchall()]
cursor.close()
return results✅ 특징:
- 안전한 비밀번호 해싱
- 솔트 사용
- 매개변수화된 SQL 쿼리
- 보안 취약점 방지
- 타이밍 공격 방어
- 적절한 암호화 기법 사용
✅ 모범 사례:
- 일관된 코드 스타일: PEP 8 준수, 자동 포맷터 사용(Black, YAPF)
- 명확한 명명 규칙: 의미 있는 변수명, 함수명 사용
- 타입 힌트 활용: 코드의 가독성과 안전성 향상
- 적절한 에러 처리: 예외 계층 구조화, 상세한 에러 메시지
- 환경 변수 분리: 개발, 테스트, 프로덕션 설정 구분
- 철저한 테스트: 단위 테스트, 통합 테스트 작성
- 문서화 습관화: 함수, 클래스, 모듈 수준의 문서화
- 보안 고려: 안전한 암호화, 입력 검증
- 성능 최적화: 적절한 자료구조, 알고리즘 선택
- 코드 리뷰 활성화: 피어 리뷰를 통한 품질 개선
- 디펜던시 관리: 의존성 최소화, 버전 고정
- 로깅 전략 수립: 구조화된 로깅, 적절한 로그 레벨
- 리팩토링 정기화: 기술 부채 누적 방지
- 코드 재사용성: DRY 원칙, 적절한 추상화
- 마이크로서비스 고려: 기능별 분리, 확장성 확보