1. PEP8의 주요 규칙 요약
항목 |
규칙 예시 |
들여쓰기 |
4칸 공백 사용 (탭 말고 스페이스 ) |
최대 줄 길이 |
79자 이하 (주석은 72자 이하 권장) |
줄 간격 |
함수/클래스 사이 2줄, 함수 내부에서는 논리 단위마다 1줄 |
임포트 정렬 |
표준 라이브러리 → 서드파티 → 로컬 모듈 (줄바꿈) |
공백 사용 |
연산자 앞뒤에는 공백, 괄호 안쪽은 공백 ❌ |
이름 규칙 |
함수, 변수: snake_case , 클래스: PascalCase |
docstring |
함수/클래스에는 설명용 주석 (트리플 따옴표) 추가 |
a. 줄 간격 예시
def fetch_data():
# Step 1: API 호출
response = requests.get("https://example.com")
# Step 2: 응답 파싱
data = response.json()
return data
def save_to_file(data):
with open("data.json", "w") as f:
json.dump(data, f)
b. 임포트 정렬 예시
# isort 기준
import os
import sys
import requests
from app.models import User
c. 공백 사용 예시
# ✅ 올바른 예
if a == b
func(x, y)
# ❌ 잘못된 예
if(a==b)
func( x , y )
d. 네이밍 컨벤션
항목 |
컨벤션 예시 |
규칙 |
변수/함수 |
snake_case |
get_user_info() |
클래스 |
PascalCase |
UserService |
상수 |
UPPER_SNAKE_CASE |
DEFAULT_TIMEOUT |
모듈/파일명 |
snake_case.py |
user_service.py |
가. 코딩 규칙, 함수/클래스 명명 규칙 정의
- 함수는 동사형:
get_user_info
, send_email
- 클래스는 명사형:
UserService
, EmailSender
e. Type hint는 필수
def get_user(user_id: int) -> Optional[User]:
...
- 함수 인자, 반환값에 모두 명시
- 타입 모호할 경우 →
Any
또는 Union
, Literal
등 사용
None
반환 가능 시 → Optional[Type]
또는 -> None
f. 주석 & Docstring 스타일
종류 |
용도 |
예시 |
Docstring (""" ) |
함수/클래스/모듈의 기능, 입력, 출력 설명 |
def func(): """...""" |
일반 주석 (# ) |
코드 내부의 구현 설명, 예외 처리 이유 등 |
# 예외 처리 로직 |
가. Docstring 규칙
"""
트리플 쿼트 사용
- 함수/클래스 시작 부분에만 작성
- 구현 설명은 일반
#
주석으로
- Docstring은 Google 스타일 이용
def add(a: int, b: int) -> int:
"""
두 수를 더합니다.
Args:
a (int): 첫 번째 숫자
b (int): 두 번째 숫자
Returns:
int: 두 수의 합
"""
나. 주석 규칙
TAG |
의미 |
예시 |
# NOTE: |
중요한 참고사항 (특이점, 의도 등) |
# NOTE: API에서 status가 문자열로 옴 |
# TODO: |
해야 할 작업 (아직 구현 안된 부분) |
# TODO: 에러 처리 추가 필요 |
# FIX: |
버그 관련 정보, 수정 예정 또는 필요 |
# FIX: 간헐적으로 None 반환됨 |
# HACK: |
임시 코드, 추후 개선 필요 |
# HACK: 간단한 우회로 처리함 |
# WARN: |
경고, 주의 사항 (비용 큼, 느림 등) |
# WARN: 이 함수는 매우 느립니다. |
일반 주석 |
구현 설명, 흐름 설명, 예외 처리 이유 등 |
# 파일이 없으면 기본값으로 생성 |
2. PEP8 적용 도구
a. black
→ 코드 자동 정리
-
들여쓰기, 괄호 배치, 줄바꿈, 따옴표 종류 등 자동 수정
# 원래 코드
def foo(a,b=1): return(a+b)
# black 적용 후
def foo(a, b = 1):
return a + b
b. ruff
→ 스타일 검사
- 들여쓰기, 공백, 줄 길이, 괄호 사용 등 스타일 오류 잡기
- 미사용 변수, import, 중복 변수 선언 등 불필요한 코드 제거 유도
- 함수, 변수, 클래스가 올바른 네이밍 패턴(
snake_case
, PascalCase
)을 따르는지 검사
- import 순서 정렬
3. 로깅 컨벤션
a. 기본 원칙
원칙 |
설명 |
구조적 로깅 |
로그는 JSON 형태로, 추후 분석 및 필터링이 용이하게 구성 |
레벨 구분 철저 |
DEBUG , INFO , WARNING , ERROR , CRITICAL 단계별 역할 명확히 |
로거 이름 통일 |
__name__ 또는 모듈명 기준으로 통일 |
상황 중심 메시지 작성 |
단순 상태가 아닌 맥락 있는 설명 포함 |
요청 식별자 포함 |
필요한 경우 요청 ID 등 삽입 |
예외는 로그 + raise 병행 |
예외는 exc_info=True 로 스택 트레이스 포함 |
b. 구조적 로깅
{
"timestamp": "2025-04-16T09:00:00",
"level": "ERROR",
"message": "앨범 생성 실패",
"name": "app.services.image_service",
"album_id": "al_001",
"reason": "timeout",
"exception": "Traceback (most recent call last):\n File \"/app/services/image_service.py\", line 42, in create_album\n result = call_external_api()\n File \"/app/services/api_client.py\", line 10, in call_external_api\n raise TimeoutError(\"Request timed out\")\nTimeoutError: Request timed out"
}
c. 로깅 구조 예시
import logging
logger = logging.getLogger(__name__)
def process_image(image_id: str) -> None:
try:
logger.info("Start processing", extra={"image_id": image_id})
...
logger.debug("Image resized", extra={"image_id": image_id, "size": (224, 224)})
except Exception as e:
logger.error("Image processing failed", exc_info=True, extra={"image_id": image_id})
raise
d. 로깅 레벨 가이드
레벨 |
사용 시점 |
예시 |
DEBUG |
디버깅용, 로직 흐름 파악 |
"임베딩 계산 시작", "API 응답 파싱 완료" |
INFO |
정상 흐름 요약, 단계 표시 |
"앨범 생성 완료", "유저 요청 처리 시작" |
WARNING |
문제 가능성 있음, 오류 아님 |
"입력 이미지가 작습니다", "재시도 요청 감지됨" |
ERROR |
실행 중 오류 발생 |
"DB 연결 실패", "API 요청 실패" |
CRITICAL |
치명적 오류, 즉시 조치 필요 |
"GPU 리소스 없음", "전체 서비스 중단 감지" |
e. 로거 이름 통일
logger = logging.getLogger(__name__)
__name__
은 현재 파일의 모듈명 (app.services.clustering
등)을 자동으로 참조함
- 로그에 **출처(module 단위)**가 자동으로 찍혀서 어떤 코드에서 발생한 로그인지 추적이 쉬움
- 이용 방식
# app/services/image_service.py
import logging
logger = logging.getLogger(__name__)
f. 로그 메시지 작성 스타일
- 고유 명사 영어를 더 많이 쓰는 용어는 영어로(예: ControlNet, OpenCV, DB 등)
- 행동 & 상태는 한글로 작성
- 메시지는 행동+상태 형태로 명확히 작성
잘못된 예 |
개선된 예 |
"실패" |
"앨범 생성 중 DB 저장 실패" |
"시작함" |
"인물 클러스터링 작업 시작" |
"안됨" |
"ControlNet 추론 중 OpenCV 오류 발생" |
g. 예외 처리 시 로그 + raise 병행
가. 원칙
- 예외가 발생하면 무조건 로그를 남기고, 필요한 경우 다시 예외를 던져야 한다.
-
단순히 raise
만 하면 어디서 어떤 상황에 발생했는지 알기 어려움
-
logger.error(..., exc_info=True)
로 스택 트레이스까지 출력하기
Traceback (most recent call last):
File "app/image.py", line 12, in process_image
resize_image(img)
File "app/image.py", line 5, in resize_image
return img.resize((0, 0)) # <- 여기서 에러 발생
ValueError: height and width must be > 0
나. 예시
try:
run_ai_pipeline()
except Exception as e:
logger.error("AI 파이프라인 실패", exc_info=True, extra={"user_id": user_id})
raise