본문으로 건너뛰기
AIDevOps
  • Learn
  • Learning Paths
  • Practice
  • Open Source
  • Books
  • Engineering

    AI DevOpsAI 서비스 개발·운영 전체 지도LLMOpsLLM 배포·평가·관측실전 프로젝트AI Agent 프로젝트 실습

    Knowledge

    Docs기술 문서 모음Blog엔지니어링 아티클Plogger개발 기록 피드

    Validate

    Certification3단계 역량 인증 · 준비 중
AI Models
LlamaMistralGemmaDeepSeekQwen
⚙️ Backend
Backend 입문 & 로드맵Python 기본FastAPIDjangoFlask|CGoGinNode.js
🤖 AI 실전 개발
AI 실전 입문 & 로드맵Hugging FaceLangChainLlamaIndexLLMOps|LangGraphMCPMulti-AgentAgent Evaluation
🧠 AI Core
AI 입문 & 로드맵ML FundamentalsLLM Fundamentals|Python AIC++|PyTorchTensorFlowJAX
🧠 AI Agent 개발
금융 AI AgentLLM API 서버주식 투자 AgentAIOps AI Agent교육 AI Agent코딩 AI Agent
🌱 Spring Cloud
Spring 입문 & 로드맵Spring Cloud GatewaySpring BootJava|Spring AISpring SecuritySpring BatchSpring JPA
🐳 DevOps
DevOps 입문 & 로드맵LinuxDockerCI/CD|Kubernetes 기본K8s 심화/실무PrometheusGrafana
🧱 인프라
인프라 입문 & 로드맵NginxRedis
☁️ 클라우드
클라우드 입문 & 로드맵AWSGCPAzureNCPCloudflare
🎨 Frontend
Frontend 입문 & 로드맵JavaScriptTypeScript|ReactNext.js|VueNuxt
📱 Mobile
Mobile 입문 & 로드맵KotlinAndroidFlutter
💾 Database
DB 입문 & 로드맵공통 SQLOracleMySQLPostgreSQL|MongoDB벡터 DB
🧪 검증
k6JMeternGrinder
AIDevOps

Engineering AI. From Code to Production.
AI와 AI Agent를 개발하고 운영하기 위한 엔지니어링 학습 플랫폼

Learn

  • 전체 가이드
  • Learning Paths
  • Practice
  • Books

Resources

  • AI DevOps
  • LLMOps
  • 실전 프로젝트
  • Docs
  • Blog
  • Plogger
  • Open Source
  • Certification (준비 중)

Start Here

  • AI Core 로드맵
  • AI 실전 개발 로드맵
  • Spring Cloud 로드맵
  • DevOps 로드맵
  • 인프라 로드맵

 

  • 클라우드 로드맵
  • Frontend 로드맵
  • Mobile 로드맵
  • Backend 로드맵
  • Database 로드맵
© 2026 AI DevOps Korea. All rights reserved.
이용약관개인정보처리방침Sitemaptestforge.kr
  1. Home
  2. Learn
  3. Backend
  4. Python 기본
Python 기본 + 중급 완전 가이드

🐍 Python 기본 가이드

Visitors

Python 설치부터 문법, 자료구조, 컴프리헨션/제너레이터, 데코레이터, 컨텍스트 매니저, 타입 힌트 심화, 동시성(threading/multiprocessing/asyncio), 패키지 관리(uv), 테스트까지 — 입문을 넘어 실무 중급 수준까지 한 번에 정리합니다.

  • Beginner · 입문
  • 업데이트 2026.09.23
  • 약 21분 읽기
  • 19개 섹션
  • 예제 코드 20개
  • 웹 IDE 실습 제공
🐍

Python 기본 웹 IDE

설치 없이 브라우저에서 코드를 실행하고 단계별 예제로 익혀보세요.

웹 IDE 열기 →
Python 입문 & 중급 도약동시성 프로그래밍 기초uv 기반 패키지 관리pytest 실무 테스트

관련 프레임워크 & 개발환경

⚡FastAPI→🟢Django→FLFlask→

목차

0 / 21
  1. 가이드 사용법
  2. 구조 다이어그램
  3. 설치 & 실행 환경
  4. 변수와 기본 문법
  5. 자료구조
  6. 컴프리헨션 & 제너레이터
  7. 함수와 타입 힌트
  8. 데코레이터
  9. 모듈과 패키지
  10. 파일 입출력
  11. 컨텍스트 매니저 & 자원 관리
  12. 예외 처리
  13. 클래스와 객체지향
  14. 타입 힌트 심화
  15. 표준 라이브러리 활용
  16. 동시성 기초 (threading/multiprocessing/asyncio)
  17. 패키지 관리 심화 (uv)
  18. 테스트와 프로젝트 구조
  19. Python 기본 설계
  20. 운영 기준
  21. 검증 전략
목차 21개 섹션
  1. 가이드 사용법
  2. 구조 다이어그램
  3. 설치 & 실행 환경
  4. 변수와 기본 문법
  5. 자료구조
  6. 컴프리헨션 & 제너레이터
  7. 함수와 타입 힌트
  8. 데코레이터
  9. 모듈과 패키지
  10. 파일 입출력
  11. 컨텍스트 매니저 & 자원 관리
  12. 예외 처리
  13. 클래스와 객체지향
  14. 타입 힌트 심화
  15. 표준 라이브러리 활용
  16. 동시성 기초 (threading/multiprocessing/asyncio)
  17. 패키지 관리 심화 (uv)
  18. 테스트와 프로젝트 구조
  19. Python 기본 설계
  20. 운영 기준
  21. 검증 전략

가이드 사용법

읽는 방향

Python 기본를 실무 흐름으로 이해하기

Python 설치부터 문법, 자료구조, 컴프리헨션/제너레이터, 데코레이터, 컨텍스트 매니저, 타입 힌트 심화, 동시성(threading/multiprocessing/asyncio), 패키지 관리(uv), 테스트까지 — 입문을 넘어 실무 중급 수준까지 한 번에 정리합니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.

핵심 관점

백엔드 / 시스템 개발

문법보다 요청이 들어와 검증, 처리, 저장, 응답으로 이어지는 경계를 먼저 잡습니다.

Python 입문 & 중급 도약동시성 프로그래밍 기초uv 기반 패키지 관리pytest 실무 테스트

구조 다이어그램

글로 읽은 내용을 머릿속에 오래 남기려면 먼저 흐름을 그림으로 잡는 편이 좋습니다. 아래 두 그림은 Python 기본를 학습할 때 계속 되돌아볼 수 있는 기준 지도입니다.

학습 흐름

다이어그램 렌더링 중…

아키텍처 관점

다이어그램 렌더링 중…

설치 & 실행 환경

Python 기본를 처음 펼칠 때는 세부 명령보다 큰 그림이 먼저입니다. 이 섹션에서는 앞으로 배울 개념들이 어떤 문제를 풀기 위해 등장했는지부터 잡아봅니다.

Python은 운영체제 전역에 설치된 인터프리터를 그대로 쓰기보다 프로젝트별 가상환경을 분리하는 방식이 기본입니다. 초반에는 python.org 설치와 venv만으로 충분하고, 여러 프로젝트를 다루기 시작하면 uv로 의존성과 lock 파일을 관리하는 편이 좋습니다.
BASH
# 버전 확인
python --version

# 프로젝트 생성
mkdir python-basic && cd python-basic
python -m venv .venv

# macOS/Linux
source .venv/bin/activate

# Windows PowerShell
.venv\Scripts\Activate.ps1

# 패키지 설치
python -m pip install --upgrade pip
python -m pip install requests pytest

Tip

  • 프로젝트마다 가상환경을 분리하세요.
  • 실행 파일 이름을 python.py로 만들면 표준 모듈과 충돌할 수 있습니다.
  • 실무에서는 pyproject.toml과 lock 파일로 재현 가능한 환경을 남깁니다.

변수와 기본 문법

여기서는 변수와 기본 문법을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Python은 들여쓰기로 블록을 구분하고, 변수에 타입을 명시하지 않아도 됩니다. 다만 중급 이상 코드에서는 타입 힌트를 함께 사용해 IDE 자동완성, 리뷰, 테스트 품질을 높이는 것이 좋습니다.
basics.pyPYTHON
name = "AI DevOps"
score = 87
active = True

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
else:
    grade = "C"

for index in range(3):
    print(index, name, grade)

def format_user(name: str, score: int) -> str:
    return f"{name}: {score}점"

print(format_user(name, score))
개념예시실무 기준
문자열`str`, f-string문자열 조합은 f-string을 기본으로 사용합니다.
숫자`int`, `float`금액 계산은 float 대신 Decimal을 검토합니다.
조건문`if / elif / else`복잡한 조건은 함수로 분리합니다.
반복문`for`, `while`인덱스가 필요하면 enumerate를 사용합니다.

자료구조

여기서는 자료구조을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Python의 생산성은 list, tuple, dict, set을 자연스럽게 다루는 데서 나옵니다. 데이터의 순서, 중복 허용 여부, 키 기반 조회가 필요한지를 먼저 판단하면 적절한 자료구조를 고를 수 있습니다.
collections_demo.pyPYTHON
users = [
    {"id": 1, "name": "Ada", "role": "admin"},
    {"id": 2, "name": "Linus", "role": "developer"},
    {"id": 3, "name": "Grace", "role": "developer"},
]

names = [user["name"] for user in users]
developers = [u for u in users if u["role"] == "developer"]
user_by_id = {user["id"]: user for user in users}
roles = {user["role"] for user in users}

print(names)
print(user_by_id[2]["name"])
print(sorted(roles))
자료구조특징대표 용도
list순서 있음, 중복 가능목록, 정렬, 필터링
tuple변경 불가좌표, 고정된 반환값
dict키-값 조회설정, 인덱스, JSON 형태 데이터
set중복 제거태그, 권한, 교집합/차집합

컴프리헨션 & 제너레이터

여기서는 컴프리헨션 & 제너레이터을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

컴프리헨션은 반복문을 한 줄로 압축하는 문법이고, 제너레이터는 결과를 한 번에 메모리에 올리지 않고 필요한 만큼만 지연 생성(lazy evaluation)합니다. 대량 데이터를 다룰 때 list 대신 제너레이터를 쓰면 메모리 사용량을 크게 줄일 수 있습니다.
generators_demo.pyPYTHON
# list/dict/set 컴프리헨션 — 즉시 전체를 메모리에 생성
squares = [n * n for n in range(10)]
even_squares = {n: n * n for n in range(10) if n % 2 == 0}

# 제너레이터 함수 — yield를 만날 때마다 하나씩 반환하고 멈춤
def read_large_file(path: str):
    with open(path, encoding="utf-8") as f:
        for line in f:
            yield line.strip()

# 제너레이터 표현식 — []대신 ()를 쓰면 지연 생성
total = sum(n * n for n in range(1_000_000))  # 리스트를 만들지 않고 즉시 합산

# itertools로 무한/대용량 스트림 다루기
from itertools import islice

def counter():
    n = 0
    while True:
        yield n
        n += 1

first_five = list(islice(counter(), 5))
print(first_five)
구분평가 시점메모리재순회
리스트 컴프리헨션 `[...]`즉시 전체 생성전체 데이터 크기만큼 사용여러 번 가능
제너레이터 표현식 `(...)`값을 요청할 때마다 하나씩 생성항목 1개 분량만 사용1회성 (소진되면 재사용 불가)

Tip

  • 제너레이터는 한 번 소진하면 다시 순회할 수 없습니다 — 여러 번 순회해야 한다면 list로 변환하거나 제너레이터 함수를 다시 호출하세요.
  • 대용량 파일을 처리할 때는 file.readlines()로 전체를 읽지 말고, 파일 객체 자체가 이미 한 줄씩 지연 반환하는 이터레이터이므로 `for line in f:`로 순회하세요.

함수와 타입 힌트

여기서는 함수와 타입 힌트을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

함수는 Python 코드의 가장 중요한 분리 단위입니다. 입력과 출력 타입을 명시하면 작은 스크립트도 유지보수 가능한 모듈로 성장시킬 수 있습니다.
functions.pyPYTHON
from dataclasses import dataclass

@dataclass
class Order:
    id: int
    amount: float
    paid: bool = False

def total_paid(orders: list[Order]) -> float:
    return sum(order.amount for order in orders if order.paid)

def normalize_name(value: str) -> str:
    return value.strip().title()

orders = [Order(1, 12000, True), Order(2, 8000, False)]
print(total_paid(orders))

Tip

  • 함수는 한 가지 역할만 하도록 작게 유지합니다.
  • 기본값 인자에 빈 리스트 같은 mutable 객체를 직접 넣지 않습니다.
  • 반환값이 복잡하면 dict보다 dataclass나 Pydantic 모델을 고려합니다.

데코레이터

여기서는 데코레이터을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

데코레이터는 함수를 감싸서 실행 전후에 공통 로직(로깅, 캐싱, 권한 체크, 재시도)을 끼워 넣는 문법입니다. `functools.wraps`로 원본 함수의 이름·문서를 보존하는 것을 잊지 마세요.
decorators.pyPYTHON
import time
import functools

def retry(times: int = 3):
    """인자를 받는 데코레이터는 함수를 한 겹 더 감싼 팩토리 구조입니다."""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_error: Exception | None = None
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as exc:
                    last_error = exc
                    print(f"[재시도 {attempt}/{times}] {func.__name__} 실패: {exc}")
            raise last_error
        return wrapper
    return decorator

@retry(times=3)
def call_flaky_api() -> str:
    if time.time() % 2 < 1:
        raise ConnectionError("일시적 네트워크 오류")
    return "ok"

# 내장 데코레이터
class Circle:
    def __init__(self, radius: float) -> None:
        self._radius = radius

    @property
    def area(self) -> float:
        return 3.14159 * self._radius ** 2

    @staticmethod
    def unit() -> "Circle":
        return Circle(1)

@functools.lru_cache(maxsize=None)
def fibonacci(n: int) -> int:
    return n if n < 2 else fibonacci(n - 1) + fibonacci(n - 2)
데코레이터용도
`@property`메서드를 속성처럼 접근하게 만듦 (getter)
`@staticmethod`self 없이 클래스 네임스페이스만 빌려 쓰는 함수
`@classmethod`첫 인자로 클래스 자체(cls)를 받는 대체 생성자 등
`@functools.lru_cache`동일 인자 호출 결과를 캐싱해 재계산 방지
`@functools.wraps`커스텀 데코레이터 안에서 원본 함수의 메타데이터 보존

Tip

  • 데코레이터 안에서 예외를 잡았다면 로그만 남기고 조용히 삼키지 말고, 처리 후 반드시 다시 raise하거나 명시적으로 실패를 표현하세요.
  • 인자를 받는 데코레이터는 함수를 3중으로 감싸는 구조(팩토리 → 데코레이터 → wrapper)가 됩니다 — 처음엔 헷갈리지만 위 retry 예시를 템플릿 삼아 익혀두면 됩니다.

모듈과 패키지

여기서는 모듈과 패키지을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

파일 하나가 커지면 기능별 모듈로 나눕니다. import 경로가 명확해야 테스트와 배포가 쉬워지며, 실행 진입점은 `if __name__ == "__main__"` 아래에 두는 습관이 좋습니다.
project layoutTEXT
python-basic/
  pyproject.toml
  src/
    app/
      __init__.py
      calculator.py
      cli.py
  tests/
    test_calculator.py
src/app/calculator.pyPYTHON
def add(a: int, b: int) -> int:
    return a + b
src/app/cli.pyPYTHON
from app.calculator import add

def main() -> None:
    print(add(2, 3))

if __name__ == "__main__":
    main()

파일 입출력

여기서는 파일 입출력을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

파일을 다룰 때는 인코딩, 경로, 예외 처리를 함께 생각해야 합니다. 텍스트는 UTF-8을 기본으로 두고, 경로 조합은 pathlib을 사용하면 운영체제 차이를 줄일 수 있습니다.
files.pyPYTHON
from pathlib import Path
import json

data_dir = Path("data")
data_dir.mkdir(exist_ok=True)

users = [{"id": 1, "name": "Ada"}, {"id": 2, "name": "Linus"}]
path = data_dir / "users.json"

path.write_text(json.dumps(users, ensure_ascii=False, indent=2), encoding="utf-8")
loaded = json.loads(path.read_text(encoding="utf-8"))

for user in loaded:
    print(user["id"], user["name"])

컨텍스트 매니저 & 자원 관리

여기서는 컨텍스트 매니저 & 자원 관리을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

`with` 문은 파일, 네트워크 연결, 락(lock)처럼 반드시 정리(cleanup)가 필요한 자원을 예외가 발생해도 안전하게 해제합니다. `__enter__`/`__exit__`를 직접 구현하거나, `contextlib.contextmanager`로 제너레이터 함수 하나만으로 간단히 만들 수 있습니다.
context_managers.pyPYTHON
import time
from contextlib import contextmanager

# 1) 클래스 기반 컨텍스트 매니저
class Timer:
    def __enter__(self):
        self._start = time.perf_counter()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        elapsed = time.perf_counter() - self._start
        print(f"소요 시간: {elapsed:.3f}s")
        return False  # 예외를 삼키지 않고 그대로 전파

with Timer():
    time.sleep(0.1)

# 2) 제너레이터 기반 컨텍스트 매니저 — 훨씬 짧게 같은 역할 구현
@contextmanager
def timer():
    start = time.perf_counter()
    try:
        yield
    finally:
        print(f"소요 시간: {time.perf_counter() - start:.3f}s")

with timer():
    time.sleep(0.1)

# 3) 여러 자원을 동시에 정리
with open("in.txt", encoding="utf-8") as src, open("out.txt", "w", encoding="utf-8") as dst:
    dst.write(src.read().upper())

Tip

  • `__exit__`가 True를 반환하면 발생한 예외가 조용히 억제(swallow)됩니다 — 의도한 게 아니라면 항상 False나 None을 반환하세요.
  • "무시해도 되는 예외"는 `try/except: pass`보다 `contextlib.suppress(FileNotFoundError)`처럼 의도를 코드로 명시하는 편이 읽기 좋습니다.

예외 처리

여기서는 예외 처리을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

예외 처리는 오류를 숨기는 도구가 아니라 실패를 명확히 표현하는 장치입니다. 너무 넓은 except는 문제를 감추므로, 예상 가능한 예외만 잡고 호출자에게 의미 있는 메시지를 전달해야 합니다.
errors.pyPYTHON
from pathlib import Path

def read_config(path: str) -> dict[str, str]:
    file = Path(path)
    if not file.exists():
        raise FileNotFoundError(f"config not found: {path}")

    result: dict[str, str] = {}
    for line in file.read_text(encoding="utf-8").splitlines():
        if not line.strip() or line.startswith("#"):
            continue
        key, value = line.split("=", 1)
        result[key.strip()] = value.strip()
    return result

try:
    config = read_config("app.env")
except FileNotFoundError as exc:
    print(f"설정 오류: {exc}")

Tip

  • 예외를 잡았다면 로그나 메시지로 원인을 남깁니다.
  • 복구할 수 없는 오류는 조용히 삼키지 말고 다시 raise합니다.
  • 외부 API 호출에는 timeout과 재시도 정책을 함께 둡니다.

클래스와 객체지향

여기서는 클래스와 객체지향을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Python에서 클래스는 상태와 행위를 함께 묶어야 할 때 사용합니다. 단순 데이터는 dataclass로 충분하고, 복잡한 상속보다 조합과 작은 객체를 우선하는 편이 유지보수에 유리합니다.
oop.pyPYTHON
from dataclasses import dataclass

@dataclass
class Account:
    owner: str
    balance: int = 0

    def deposit(self, amount: int) -> None:
        if amount <= 0:
            raise ValueError("amount must be positive")
        self.balance += amount

    def withdraw(self, amount: int) -> None:
        if amount > self.balance:
            raise ValueError("insufficient balance")
        self.balance -= amount

account = Account("Ada")
account.deposit(10000)
account.withdraw(3000)
print(account.balance)

타입 힌트 심화

여기서는 타입 힌트 심화을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

기본 문법에서 다룬 단순 타입 힌트를 넘어, `typing` 모듈의 도구로 더 정교한 계약을 표현할 수 있습니다. mypy나 pyright 같은 정적 타입 검사기를 CI에 연결하면 실행하지 않고도 타입 오류를 미리 잡을 수 있습니다.
typing_advanced.pyPYTHON
from typing import TypedDict, Protocol, Literal

# 값이 없을 수도 있음을 명시 (Python 3.10+에서는 Optional[str] 대신 str | None)
def find_user(user_id: int) -> str | None:
    return "Ada" if user_id == 1 else None

# 여러 타입 중 하나
def parse_amount(value: int | str) -> float:
    return float(value)

# JSON처럼 구조가 정해진 dict 타입
class UserPayload(TypedDict):
    id: int
    name: str
    role: Literal["admin", "developer", "viewer"]

def greet(user: UserPayload) -> str:
    return f"{user['name']}({user['role']})님 환영합니다"

# 상속 없이 "이 메서드가 있으면 된다"는 구조적 타이핑
class SupportsTotal(Protocol):
    def total(self) -> float: ...

def print_total(item: SupportsTotal) -> None:
    print(f"합계: {item.total()}")
도구용도
`X | None` (`Optional[X]`)값이 없을 수도 있음을 명시
`A | B` (`Union[A, B]`)여러 타입 중 하나를 허용
`TypedDict`dict의 키·값 타입을 JSON 구조체처럼 명시
`Protocol`상속 없이 필요한 메서드만 있으면 되는 구조적 타이핑
`Literal["a", "b"]`정해진 값들 중 하나로 제한

Tip

  • Python 3.10+ 코드베이스에서는 `Optional[str]`, `Union[int, str]`보다 `str | None`, `int | str` 표기가 더 널리 쓰입니다.
  • 타입 힌트는 런타임에 강제되지 않습니다 — 외부 입력(API 요청, 파일)처럼 실제로 값을 검증해야 하는 경계에서는 Pydantic 같은 런타임 검증 라이브러리를 함께 사용하세요.

표준 라이브러리 활용

여기서는 표준 라이브러리 활용을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Python 표준 라이브러리는 자동화와 데이터 처리의 기본 도구입니다. datetime, pathlib, json, csv, argparse, logging만 익혀도 작은 운영 스크립트를 안정적으로 만들 수 있습니다.
report.pyPYTHON
import argparse
import csv
import logging
from pathlib import Path

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

parser = argparse.ArgumentParser()
parser.add_argument("--file", default="orders.csv")
args = parser.parse_args()

path = Path(args.file)
total = 0

with path.open(encoding="utf-8", newline="") as f:
    for row in csv.DictReader(f):
        total += int(row["amount"])

logging.info("total amount: %s", total)

동시성 기초 (threading/multiprocessing/asyncio)

여기서는 동시성 기초 (threading/multiprocessing/asyncio)을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Python은 GIL(Global Interpreter Lock) 때문에 한 프로세스 안에서 여러 스레드가 동시에 파이썬 바이트코드를 실행하지 못합니다. 그래서 "지금 무엇을 기다리는가"에 따라 도구를 다르게 골라야 합니다 — I/O를 기다리는 작업은 asyncio나 threading, CPU를 많이 쓰는 계산은 multiprocessing입니다.
concurrency_demo.pyPYTHON
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

# I/O bound: 동시에 여러 스레드로 대기
def download(url: str) -> str:
    time.sleep(0.5)  # 네트워크 대기를 흉내
    return f"downloaded: {url}"

with ThreadPoolExecutor(max_workers=4) as pool:
    results = list(pool.map(download, ["a.com", "b.com", "c.com"]))

# CPU bound: 여러 프로세스로 진짜 병렬 계산
def heavy_square(n: int) -> int:
    return sum(i * i for i in range(n))

with ProcessPoolExecutor(max_workers=4) as pool:
    results = list(pool.map(heavy_square, [10_000_000] * 4))

# asyncio: 단일 스레드에서 수많은 대기를 동시에 처리
async def fetch(session_id: int) -> str:
    await asyncio.sleep(0.5)  # 반드시 asyncio용 sleep — time.sleep은 이벤트 루프를 멈춤
    return f"session {session_id} done"

async def main() -> None:
    results = await asyncio.gather(*(fetch(i) for i in range(10)))
    print(results)

asyncio.run(main())
상황적합한 도구이유
네트워크 요청을 대량으로 동시 호출 (I/O bound)asyncio단일 스레드로 수천 개를 동시 대기, 컨텍스트 스위칭 비용이 가장 작음
기존 동기 라이브러리로 파일·DB I/OthreadingI/O 대기 중에는 GIL이 풀리므로 여러 스레드가 동시에 대기 가능
이미지 처리, 수치 계산 (CPU bound)multiprocessing프로세스마다 별도의 인터프리터·GIL을 가져 진짜 병렬 실행

Tip

  • I/O bound 작업에 multiprocessing을 쓰면 프로세스 생성 오버헤드만 커지고 이득이 없습니다 — 먼저 "이 작업이 기다리는 것인가, 계산하는 것인가"부터 구분하세요.
  • asyncio 코드 안에서 `time.sleep`이나 동기 requests 호출을 그대로 쓰면 이벤트 루프 전체가 멈춰버립니다 — `asyncio.sleep`, `httpx`의 async client처럼 반드시 비동기 버전을 사용하세요.

패키지 관리 심화 (uv)

여기서는 패키지 관리 심화 (uv)을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

venv + pip만으로 시작했다면, 프로젝트가 커지면서 재현 가능한 의존성 잠금(lock)과 빌드 표준화가 필요해집니다. `pyproject.toml`은 이제 `setup.py`를 대체하는 표준 설정 파일이고, `uv`는 그 위에서 의존성 설치·가상환경·lock 관리를 하나의 매우 빠른 CLI로 통합해줍니다.
pyproject.tomlTOML
[project]
name = "python-basic"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "requests>=2.32",
]

[dependency-groups]
dev = ["pytest>=8.0"]
uv commandsBASH
# 프로젝트 초기화 (venv + pyproject.toml 자동 생성)
uv init python-basic && cd python-basic

# 의존성 추가 (설치 + pyproject.toml + uv.lock 동시 갱신)
uv add requests
uv add --dev pytest

# lock 기준으로 정확히 동일한 환경 재현
uv sync

# venv를 직접 activate하지 않고 lock된 환경에서 바로 실행
uv run python -m app.cli
uv run pytest
도구특징
pip + requirements.txt가장 단순하지만 정식 lock 파일이 없음 (pip freeze로 흉내)
pip-toolsrequirements.in → requirements.txt로 lock 파일 생성
Poetrypyproject.toml 기반, lock 파일과 빌드·배포까지 통합
uvRust로 작성되어 매우 빠름, venv·설치·lock을 하나의 CLI로 통합 — 최근 표준으로 빠르게 확산

Tip

  • lock 파일(uv.lock, poetry.lock)은 반드시 git에 커밋하세요 — "내 컴퓨터에서는 되는데" 문제의 대부분은 잠기지 않은 의존성 버전 차이에서 비롯됩니다.
  • 새 프로젝트를 시작한다면 특별한 이유가 없는 한 uv로 바로 시작하는 것을 권장합니다 — pip+venv보다 설치 속도가 훨씬 빠르고 lock 관리까지 한 번에 해결됩니다.

테스트와 프로젝트 구조

여기서는 테스트와 프로젝트 구조을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

기본기를 실무 수준으로 끌어올리는 가장 빠른 방법은 테스트를 같이 작성하는 것입니다. 계산 로직, 파싱 로직, 예외 케이스를 테스트로 고정하면 리팩터링이 쉬워지고, fixture와 mock을 익히면 외부 의존성이 있는 코드도 안전하게 검증할 수 있습니다.
tests/test_calculator.pyPYTHON
import pytest
from app.calculator import add

def test_add():
    assert add(2, 3) == 5

@pytest.mark.parametrize("a,b,expected", [
    (1, 2, 3),
    (-1, 1, 0),
    (0, 0, 0),
])
def test_add_cases(a: int, b: int, expected: int):
    assert add(a, b) == expected
tests/test_client.pyPYTHON
import pytest
from app.weather import WeatherClient

@pytest.fixture
def client() -> WeatherClient:
    # 각 테스트마다 새로 생성되는 재사용 가능한 준비물
    return WeatherClient(api_key="test-key")

def test_parses_temperature(client: WeatherClient, monkeypatch: pytest.MonkeyPatch):
    def fake_get(url: str) -> dict:
        return {"temp": 21.5}

    # 실제 네트워크 호출 대신 가짜 함수로 대체
    monkeypatch.setattr(client, "_get", fake_get)

    assert client.current_temperature("Seoul") == 21.5
개념역할
`fixture`테스트마다 필요한 준비물(DB 연결, 객체)을 만들고 뒷정리까지 담당
`monkeypatch` / `unittest.mock`외부 API·시간·환경변수처럼 통제하기 어려운 의존성을 가짜로 대체
`pytest.mark.parametrize`같은 로직을 여러 입력값으로 반복 검증
`pytest --cov=app`coverage.py 연동으로 테스트가 커버하지 못한 코드 라인 확인

Tip

  • 테스트 파일은 tests/ 아래에 모으고, 버그를 수정할 때는 같은 문제가 재발하지 않도록 재현 테스트를 먼저 추가하세요.
  • 진짜 네트워크·DB를 호출하는 테스트는 느리고 불안정합니다 — 단위 테스트에서는 monkeypatch/mock으로 외부 의존성을 대체하고, 실제 연동 확인은 별도의 통합 테스트로 분리하세요.
  • FastAPI, Django로 넘어가기 전에 fixture와 mock을 포함한 함수 단위 테스트 습관을 잡아두면 프레임워크의 TestClient도 훨씬 쉽게 이해됩니다.

Python 기본 실무 설계

Python 기본 실무 설계은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

Python 기본 가이드는 문법을 나열하는 데서 끝나면 안 됩니다. 입력 처리, 자료구조 선택, 함수 분리, 예외 계약, 모듈 구조까지 연결해 작은 스크립트를 유지보수 가능한 프로그램으로 확장하는 흐름이 중요합니다.
결정 지점확인 질문실무 기준
경계Python 기본 코드에서 바뀌기 쉬운 부분은 어디인가?입출력, 설정, 외부 연동, 핵심 규칙을 분리합니다.
상태상태가 어디서 생성되고 어디서 사라지는가?상태 소유자와 수명 주기를 코드로 드러냅니다.
장애실패했을 때 호출자는 무엇을 받는가?timeout, fallback, error contract를 먼저 정합니다.

Python 기본 운영 기준

이 섹션은 Python 기본 운영 기준을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.

실무 활용에서는 실행 환경, 의존성 버전, 로그, 설정 파일, 파일 경로, 인코딩 문제를 먼저 안정화해야 합니다. 자동화 스크립트도 timeout, 재시도, 실패 로그를 남겨야 운영 도구가 됩니다.

Tip

  • venv or uv environment
  • type hints
  • pytest coverage
  • pathlib/json/logging

Python 기본 검증 전략

Python 기본 검증 전략은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

함수 단위 테스트, 예외 케이스, 파일 입출력 샘플, 타입 힌트, lint/format을 함께 적용하면 기본 문법 학습을 넘어 실무 코드 품질로 이어질 수 있습니다.
품질 축검증 방법완료 기준
정확성정상/실패 케이스를 자동화합니다.핵심 시나리오가 재현 가능하게 통과합니다.
회귀 방지버그 수정 시 동일 케이스를 테스트로 남깁니다.같은 장애가 다시 배포되지 않습니다.
운영성로그, 메트릭, 알림을 확인합니다.문제가 생겼을 때 원인 추적 경로가 있습니다.
← 이전 가이드Backend 입문 & 로드맵다음 가이드 →FastAPI