본문 바로가기
  • AI 시대에 적응하는 현대인을 위한 지식 공간
  • AI를 위한 데이터를 과학으로 역어본다
AI 코딩

파이썬 코딩에서 추천하는 네이밍 규칙 정리

by 피크나인 2025. 11. 18.

코딩에 처음 입문하는 초보 개발자가 가장 먼저 실수 또는 헷갈리는 부분 중 하나가 바로 이름 짓기(Naming Convention)입니다.  함수, 변수, 클래스 이름을 어떻게 지어야 할지 고민하게 되고, 프로젝트가 커질수록 규칙이 없으면 유지보수가 어려워집니다. 특히 백엔드, 데이터 처리, 자동매매 시스템처럼 많은 파일과 코드가 오가는 프로젝트는 네이밍 규칙이 매우 중요합니다. 


이 글에서는 파이썬에서 많이 사용하는 표준 규칙을 바탕으로, 실무에서 바로 사용할 수 있을 정도로 쉽게 설명해보겠습니다.

프로젝트의 코딩 표준화는 개발 생산성 향상 및 유지보수 편의성을 위해 반드시 필요합니다. 
 

1. 함수 이름   |  snake_case |  동사 + 목적어로 ‘무슨 일을 하는지’ 보여주기

파이썬에서는 함수 이름을 snake_case(소문자 + 밑줄)로 작성하는 것이 기본 규칙입니다.
이 방식은 눈으로 보기에도 편하고, 읽었을 때 함수가 어떤 동작을 하는지 쉽게 이해할 수 있게 만들어줍니다.
특히 초보 개발자일수록 함수 이름만 보고도 기능을 유추할 수 있게 만드는 것이 굉장히 중요합니다.
그래서 함수 이름은 “load_data”, “send_email”, “calculate_score”처럼 동사 + 목적어 형태로 작성하는 것을 추천합니다.

def load_user_data():
def calculate_moving_average():
def send_notification_email():

2. 클래스 이름  |  PascalCase  |  현실 세계의 ‘사물’처럼 명확하게

클래스는 대체로 어떤 ‘사물’이나 ‘주체’를 표현하므로, 이름도 명사(Noun) 형태로 작성하는 것이 좋습니다.
파이썬에서는 클래스 이름을 PascalCase(첫 글자 대문자)로 짓는 것이 표준입니다.
예를 들어 MarketAnalyzer, TradeExecutor, UserRepository 같은 이름은 클래스의 역할이 명확하게 드러납니다.
초보 개발자라면 “이 클래스는 어떤 객체를 표현하는가?”를 먼저 생각하고 이름을 짓는 습관을 들이면 좋습니다.

class UserRepository:
class MarketAnalyzer:
class TradeExecutor:

3. 변수 이름  |  snake_case  |  데이터가 무엇을 의미하는지 알 수 있게

변수는 snake_case로 작성하며, 정확한 의미를 담는 것이 가장 중요합니다.
current_price, user_id, market_sentiment_score처럼 이름을 길게 적어도 괜찮습니다.
짧고 애매한 변수 이름은 오히려 나중에 코드를 읽을 때 더 큰 혼란을 줍니다.
또한 데이터 타입이 자연스럽게 떠오르는 이름을 사용하면 디버깅할 때 매우 유용합니다.

user_id = 100
current_price = 105.42
market_score = 3.5

4. 상수  |  UPPER_SNAKE_CASE  |  모두 대문자로 쓰기

상수는 한 번 정해지면 바뀌지 않는 값이기 때문에, 일반 변수와 구분되도록 대문자 + 밑줄(UPPER_SNAKE_CASE)로 작성합니다.
예를 들어 API 주소, 최대 재시도 횟수, 기본 타임아웃 값 등이 여기에 해당됩니다.
이런 규칙을 지키면 “이 값은 고정된 값이구나”라는 것이 코드만 봐도 바로 드러납니다.
팀 개발에서 특히 큰 도움이 되는 규칙입니다.

MAX_RETRY_COUNT = 5
DEFAULT_TIMEOUT = 30
API_BASE_URL = "https://api.binance.com"

5. 모듈/파일 이름  |  snake_case  |  폴더는 기능별로

파일명(모듈)은 snake_case로 작성하며, 하나의 파일에는 한 가지 책임만 넣는 것이 좋습니다.
예를 들어 order_service.py라는 파일이 있다면, 그 안에는 주문 로직과 관련된 코드만 있어야 합니다.
폴더 구조 또한 기능별로 구분해두면 프로젝트가 커져도 길을 잃지 않게 됩니다.
services, models, config 같은 이름은 실무에서 매우 자주 사용됩니다.

services/
repositories/
models/
utils/
config/

6. 데이터베이스  |  snake_case  |  테이블은 복수형, 컬럼은 의미 중심

DB 네이밍 규칙은 백엔드 개발자라면 특히 중요한 부분입니다.
일반적으로 테이블 이름은 복수형(users, orders)으로 작성하여 “여러 행의 집합”이라는 느낌을 나타냅니다.
반면 컬럼명은 snake_case로 작성하며, 역할이 명확하게 드러나도록 만드는 것이 좋습니다.
또한 boolean 컬럼은 is_active, has_permission처럼 is_, has_ 접두사를 붙이면 읽기 쉬워집니다.

# 테이블명
users
orders
market_metrics
sentiment_scores

# 컬럼명
user_id
created_at
is_active
btc_dominance

7. 환경 변수  |  UPPER_SNAKE_CASE  |  환경변수는 모두 대문자로

프로젝트에서 사용하는 환경 변수(.env 파일)는 모두 UPPER_SNAKE_CASE로 작성하는 것이 일반적입니다.
이렇게 하면 민감한 값을 한눈에 파악할 수 있고, 다른 코드와 명확히 구분됩니다.
특히 API_KEY, DB_PASSWORD, REDIS_HOST 같은 이름은 실무에서 거의 표준처럼 사용됩니다.
환경 변수만 따로 모아두면 운영 환경 변경에도 빠르게 대응할 수 있습니다.

API_KEY=
REDIS_HOST=
DB_PASSWORD=

8. API URL 규칙(RESTful)  | kebab-case  |  하이픈 스타일로 깔끔하게

API 엔드포인트는 보통 kebab-case(하이픈)를 사용하여 URL을 깔끔하고 읽기 쉽도록 작성합니다.
예를 들어 /market-metrics, /trade-history처럼 이름만 보아도 어떤 데이터인지 바로 알 수 있습니다.
URL에는 동사를 넣지 않고, 어떤 HTTP 메서드(GET/POST/PUT/DELETE)를 사용하는지로 행동을 구분합니다.
이 규칙은 프론트엔드/백엔드 협업에서 특히 큰 도움이 됩니다.

GET /api/v1/users
POST /api/v1/orders
GET /api/v1/market-metrics

9. 네이밍 규칙 활용 예

기술 블로그 사이트를 만든다고 가정하고, 위에서 설명한 네이밍 규칙을 모두 적용한 예시를 하나의 코드블럭에 모아보면 다음과 같습니다.

# =========================================
# 1. 환경 변수 (.env)
# =========================================
API_BASE_URL=https://api.mytechblog.com
DB_HOST=localhost
DB_PORT=5432
DB_NAME=tech_blog
DB_USER=blog_user
DB_PASSWORD=super_secret_password
REDIS_HOST=localhost
REDIS_PORT=6379


# =========================================
# 2. 데이터베이스 스키마 (예: PostgreSQL)
#    - 테이블: snake_case + 복수형
#    - 컬럼: snake_case
#    - FK: <entity>_id
# =========================================

-- users: 블로그 유저(작성자 포함)
CREATE TABLE users (
    id              SERIAL PRIMARY KEY,
    email           VARCHAR(255) UNIQUE NOT NULL,
    password_hash   VARCHAR(255) NOT NULL,
    display_name    VARCHAR(100) NOT NULL,
    is_active       BOOLEAN NOT NULL DEFAULT TRUE,
    created_at      TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP NOT NULL DEFAULT NOW()
);

-- posts: 블로그 글
CREATE TABLE posts (
    id              SERIAL PRIMARY KEY,
    author_id       INTEGER NOT NULL REFERENCES users(id),
    title           VARCHAR(255) NOT NULL,
    slug            VARCHAR(255) UNIQUE NOT NULL,
    content_markdown TEXT NOT NULL,
    content_html    TEXT NOT NULL,
    is_published    BOOLEAN NOT NULL DEFAULT FALSE,
    published_at    TIMESTAMP,
    created_at      TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP NOT NULL DEFAULT NOW()
);

-- tags: 태그
CREATE TABLE tags (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(50) UNIQUE NOT NULL,
    slug        VARCHAR(50) UNIQUE NOT NULL,
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);

-- post_tags: 다대다 관계
CREATE TABLE post_tags (
    post_id     INTEGER NOT NULL REFERENCES posts(id),
    tag_id      INTEGER NOT NULL REFERENCES tags(id),
    PRIMARY KEY (post_id, tag_id)
);


# =========================================
# 3. Python 백엔드 코드 예시 (FastAPI 스타일)
#    - 클래스: PascalCase
#    - 함수/변수: snake_case
#    - 상수: UPPER_SNAKE_CASE
# =========================================

from typing import List, Optional
from datetime import datetime

from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel

# ------------------------------
# 상수 정의
# ------------------------------
MAX_RECENT_POST_COUNT = 10
DEFAULT_PAGE_SIZE = 20


# ------------------------------
# Pydantic 모델 (요청/응답용)
# ------------------------------
class PostCreate(BaseModel):
    title: str
    content_markdown: str
    tag_names: List[str]


class PostSummary(BaseModel):
    id: int
    title: str
    slug: str
    author_name: str
    is_published: bool
    published_at: Optional[datetime]


class PostDetail(PostSummary):
    content_html: str
    tag_names: List[str]


# ------------------------------
# 저장소 레이어 (Repository)
# ------------------------------
class UserRepository:
    def __init__(self, db_connection):
        self.db_connection = db_connection

    def get_user_by_id(self, user_id: int):
        # 네이밍 예시용 더미 코드
        cursor = self.db_connection.cursor()
        cursor.execute(
            "SELECT id, email, display_name, is_active FROM users WHERE id = %s",
            (user_id,),
        )
        row = cursor.fetchone()
        if row is None:
            return None
        return {
            "id": row[0],
            "email": row[1],
            "display_name": row[2],
            "is_active": row[3],
        }


class PostRepository:
    def __init__(self, db_connection):
        self.db_connection = db_connection

    def load_recent_posts(self, limit: int = MAX_RECENT_POST_COUNT) -> List[PostSummary]:
        cursor = self.db_connection.cursor()
        cursor.execute(
            """
            SELECT p.id,
                   p.title,
                   p.slug,
                   u.display_name AS author_name,
                   p.is_published,
                   p.published_at
            FROM posts p
            JOIN users u ON u.id = p.author_id
            WHERE p.is_published = TRUE
            ORDER BY p.published_at DESC
            LIMIT %s
            """,
            (limit,),
        )
        rows = cursor.fetchall()
        post_list: List[PostSummary] = []
        for row in rows:
            post = PostSummary(
                id=row[0],
                title=row[1],
                slug=row[2],
                author_name=row[3],
                is_published=row[4],
                published_at=row[5],
            )
            post_list.append(post)
        return post_list

    def get_post_by_slug(self, slug: str) -> Optional[PostDetail]:
        cursor = self.db_connection.cursor()
        cursor.execute(
            """
            SELECT p.id,
                   p.title,
           p.slug,
                   p.content_html,
                   u.display_name AS author_name,
                   p.is_published,
                   p.published_at
            FROM posts p
            JOIN users u ON u.id = p.author_id
            WHERE p.slug = %s
            """,
            (slug,),
        )
        row = cursor.fetchone()
        if row is None:
            return None

        post_id = row[0]

        cursor.execute(
            """
            SELECT t.name
            FROM tags t
            JOIN post_tags pt ON pt.tag_id = t.id
            WHERE pt.post_id = %s
            """,
            (post_id,),
        )
        tag_rows = cursor.fetchall()
        tag_names = [tag_row[0] for tag_row in tag_rows]

        return PostDetail(
            id=row[0],
            title=row[1],
            slug=row[2],
            content_html=row[3],
            author_name=row[4],
            is_published=row[5],
            published_at=row[6],
            tag_names=tag_names,
        )

    def create_post(
        self,
        author_id: int,
        post_create: PostCreate,
        slug: str,
        html_content: str,
    ) -> int:
        cursor = self.db_connection.cursor()
        cursor.execute(
            """
            INSERT INTO posts (author_id, title, slug, content_markdown, content_html, is_published)
            VALUES (%s, %s, %s, %s, %s, FALSE)
            RETURNING id
            """,
            (author_id, post_create.title, slug, post_create.content_markdown, html_content),
        )
        post_id = cursor.fetchone()[0]
        self.db_connection.commit()
        return post_id


# ------------------------------
# 서비스 레이어 (비즈니스 로직)
# ------------------------------
class MarkdownRenderer:
    def render_markdown_to_html(self, content_markdown: str) -> str:
        # 실제로는 markdown 라이브러리를 사용하겠지만,
        # 여기서는 네이밍 예시용으로 단순 처리
        return f"<p>{content_markdown}</p>"


class SlugGenerator:
    def generate_slug_from_title(self, title: str) -> str:
        # 예: "파이썬 네이밍 규칙" -> "python-naming-conventions"
        base_slug = (
            title.strip()
            .lower()
            .replace(" ", "-")
            .replace("—", "-")
            .replace("_", "-")
        )
        return base_slug


class BlogService:
    def __init__(
        self,
        post_repository: PostRepository,
        markdown_renderer: MarkdownRenderer,
        slug_generator: SlugGenerator,
    ):
        self.post_repository = post_repository
        self.markdown_renderer = markdown_renderer
        self.slug_generator = slug_generator

    def load_home_page_posts(self) -> List[PostSummary]:
        recent_post_list = self.post_repository.load_recent_posts()
        return recent_post_list

    def get_post_detail(self, slug: str) -> PostDetail:
        post_detail = self.post_repository.get_post_by_slug(slug)
        if post_detail is None or not post_detail.is_published:
            raise HTTPException(status_code=404, detail="Post not found")
        return post_detail

    def create_draft_post(self, author_id: int, post_create: PostCreate) -> int:
        slug = self.slug_generator.generate_slug_from_title(post_create.title)
        html_content = self.markdown_renderer.render_markdown_to_html(
            post_create.content_markdown
        )
        post_id = self.post_repository.create_post(
            author_id=author_id,
            post_create=post_create,
            slug=slug,
            html_content=html_content,
        )
        return post_id


# ------------------------------
# FastAPI 엔드포인트 (kebab-case URL)
# ------------------------------
app = FastAPI(title="My Tech Blog API")


def get_blog_service() -> BlogService:
    # 실제 코드에서는 DB 연결 주입 및 의존성 주입 사용
    db_connection = ...
    post_repository = PostRepository(db_connection=db_connection)
    markdown_renderer = MarkdownRenderer()
    slug_generator = SlugGenerator()
    blog_service = BlogService(
        post_repository=post_repository,
        markdown_renderer=markdown_renderer,
        slug_generator=slug_generator,
    )
    return blog_service


@app.get("/api/v1/blog-posts", response_model=List[PostSummary])
def get_blog_posts(blog_service: BlogService = Depends(get_blog_service)):
    recent_posts = blog_service.load_home_page_posts()
    return recent_posts


@app.get("/api/v1/blog-posts/{slug}", response_model=PostDetail)
def get_blog_post_detail(
    slug: str,
    blog_service: BlogService = Depends(get_blog_service),
):
    post_detail = blog_service.get_post_detail(slug=slug)
    return post_detail


@app.post("/api/v1/blog-posts", response_model=int)
def create_blog_post(
    post_create: PostCreate,
    blog_service: BlogService = Depends(get_blog_service),
):
    # 예제에서는 author_id를 1로 고정
    author_id = 1
    post_id = blog_service.create_draft_post(
        author_id=author_id,
        post_create=post_create,
    )
    return post_id

파이쎤 코딩 명명규칙은 프로젝트의 표준화를 위한 기본입니다.
파이쎤 코딩 명명규칙은 프로젝트의 표준화를 위한 기본입니다.

네이밍 규칙은 프로젝트 품질을 좌우한다

처음에는 네이밍 규칙이 어렵게 느껴질 수 있습니다.
하지만 프로젝트가 커지고 여러 사람이 함께 만들기 시작하면, 명확한 규칙의 필요성이 바로 느껴집니다.
좋은 네이밍은 코드를 더 읽기 쉽게 만들고, 문제를 빠르게 찾을 수 있게 도우며, 개발자의 생산성을 크게 높여줍니다.
오늘 소개한 규칙은 Python뿐만 아니라 대부분의 현대 개발 환경에서도 통용되므로, 지금부터 적용해 두면 앞으로 큰 도움이 될 것입니다.