파이썬 SQLAlchemy ORM Declarative 매핑 registry mapped_column 기본 정리
🚀 파이썬 ORM 프로그래밍의 핵심 개념을 쉽게 이해하는 방법을 알려드립니다
데이터베이스를 다루는 개발 과정에서 SQL문을 직접 작성하는 대신 객체 지향적으로 데이터를 관리할 수 있다면 훨씬 편리하다고 느끼실 겁니다.
특히 파이썬에서는 SQLAlchemy ORM이 널리 사용되며, 데이터베이스와 객체 모델을 연결하는 다양한 기능을 제공합니다.
그중에서도 Declarative 매핑, registry, mapped_column은 ORM 구조를 이해하는 데 꼭 필요한 핵심 개념입니다.
단순한 예제로 접근하기보다는 개념과 원리를 명확히 파악하면 실제 프로젝트에서 활용도가 크게 높아집니다.
이 글에서는 SQLAlchemy ORM의 기본 철학을 바탕으로, Declarative 매핑을 이용해 클래스를 테이블에 매핑하는 방법과 registry 객체의 역할, 그리고 각 컬럼을 정의하는 mapped_column 속성에 대해 차근차근 알아보겠습니다.
처음 접하시는 분들도 따라갈 수 있도록 개념을 풀어 설명하고, 중급 개발자라면 한 단계 더 깊이 이해할 수 있도록 실제 동작 원리와 코드 사용법을 함께 다루겠습니다.
📋 목차
🧩 Declarative 매핑이란 무엇인가
SQLAlchemy ORM의 핵심 기능 중 하나는 바로 Declarative 매핑입니다.
이는 클래스 정의와 동시에 데이터베이스 테이블 구조를 함께 설계할 수 있도록 도와주는 방식입니다.
즉, 클래스를 선언하는 순간 해당 클래스가 곧 데이터베이스의 테이블과 연결되며, 속성은 컬럼으로 매핑됩니다.
이런 구조 덕분에 SQL문을 직접 작성하지 않아도 객체 단위로 데이터를 관리할 수 있습니다.
기존 SQLAlchemy에서는 Table 객체와 mapper()를 별도로 정의하는 방식이 흔했습니다.
하지만 Declarative 매핑은 이를 간소화하여 코드 가독성과 유지보수성을 크게 높여줍니다.
따라서 실무 프로젝트에서는 대부분 이 방식을 사용합니다.
또한, 클래스 상속 구조를 통해 공통 속성을 쉽게 정의할 수 있어 확장성이 뛰어납니다.
📌 Declarative 매핑의 장점
- 🛠️클래스와 테이블을 한 곳에서 정의할 수 있어 구조가 단순해집니다.
- ⚙️속성 정의 시 자동으로 컬럼 매핑이 이뤄져 추가 설정이 최소화됩니다.
- 🚀상속 구조를 활용해 공통 필드를 효율적으로 관리할 수 있습니다.
- 📚코드가 가독성 있게 정리되어 협업 시 유리합니다.
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
위 예시는 DeclarativeBase를 상속받아 User 클래스를 정의하는 모습입니다.
클래스 속성에 mapped_column()을 사용하여 데이터베이스 컬럼을 매핑하고 있으며, 이 과정을 통해 객체와 테이블 간 자연스러운 연결이 이뤄집니다.
📚 registry 객체의 역할
SQLAlchemy ORM에서 registry는 매핑 정보를 관리하는 핵심 객체입니다.
클래스와 데이터베이스 테이블 간의 연결을 기록하고 추적하여 ORM이 정상적으로 작동할 수 있도록 돕습니다.
즉, 하나의 중앙 저장소 역할을 하며, Declarative 매핑 기반 클래스들이 어떤 테이블과 연결되는지를 효율적으로 관리합니다.
과거에는 declarative_base() 함수를 통해 매핑 기반 클래스를 생성했지만, SQLAlchemy 2.0에서는 registry()와 DeclarativeBase를 사용하는 방식이 권장됩니다.
이를 통해 코드의 구조가 명확해지고, ORM 구성이 직관적으로 바뀌었습니다.
📌 registry 객체의 특징
| 특징 | 설명 |
|---|---|
| 중앙 관리 | 모든 매핑 클래스와 테이블 정보를 하나의 객체에 저장 |
| 유연성 | 여러 개의 registry 인스턴스를 사용하여 독립적인 ORM 환경 구성 가능 |
| 호환성 | DeclarativeBase와 함께 사용해 최신 SQLAlchemy 스타일을 지원 |
from sqlalchemy.orm import registry
mapper_registry = registry()
@mapper_registry.mapped
class Product:
__tablename__ = "products"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
위 예제에서는 registry()를 생성하고 @mapper_registry.mapped 데코레이터를 사용해 클래스를 매핑하고 있습니다.
이처럼 registry 객체는 매핑의 기반이 되며, ORM 환경 전반을 안정적으로 유지하는 핵심 요소입니다.
📝 mapped_column 기본 사용법
SQLAlchemy 2.0에서 가장 자주 접하게 되는 기능 중 하나가 mapped_column()입니다.
이는 클래스 속성을 데이터베이스의 컬럼으로 매핑하기 위해 사용하는 함수로, 컬럼의 타입, 제약 조건, 기본값 등을 정의할 수 있습니다.
즉, 클래스의 속성이 데이터베이스 필드로 어떤 형태로 저장될지를 지정하는 역할을 합니다.
예를 들어, 정수형 기본키, 문자열, 날짜, 불리언 값 등 다양한 타입을 선언할 수 있으며, 각 필드마다 nullable, unique, default 같은 옵션을 줄 수 있습니다.
또한, ForeignKey를 지정하여 테이블 간 관계를 설정할 수도 있습니다.
📌 기본 예제
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, Integer, DateTime, func
class Base(DeclarativeBase):
pass
class Blog(Base):
__tablename__ = "blogs"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
title: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
content: Mapped[str] = mapped_column(String(500))
created_at: Mapped[str] = mapped_column(DateTime, server_default=func.now())
위 예제에서 id는 기본키이며 자동 증가 속성을 가집니다.
title은 고유 값이어야 하고 반드시 입력해야 하며, created_at은 데이터가 생성될 때 자동으로 현재 시간이 기록됩니다.
이처럼 mapped_column을 이용하면 데이터베이스 스키마를 손쉽게 객체 속성으로 표현할 수 있습니다.
📌 자주 사용하는 옵션
💎 핵심 포인트:
mapped_column에서는 다음과 같은 옵션을 자주 사용합니다.
- 🔑primary_key : 기본키 여부 지정
- 🔄autoincrement : 자동 증가 여부 설정
- 📌nullable : NULL 허용 여부
- 🔒unique : 고유 값 설정
- 🕒server_default : DB 서버에서 기본값 자동 설정
이러한 옵션을 적절히 조합하면 데이터 무결성을 유지하고, 개발자가 의도한 구조에 맞춰 테이블이 생성되도록 설계할 수 있습니다.
⚙️ Declarative 매핑과 클래스를 활용한 예제
Declarative 매핑과 registry, 그리고 mapped_column을 실제 코드에 적용하면 객체 지향적으로 데이터베이스를 다룰 수 있습니다.
아래의 예제는 사용자(User)와 게시글(Post) 테이블을 정의하고, 두 테이블 간 관계를 매핑하는 방식입니다.
이를 통해 ORM이 어떻게 객체와 테이블을 연결하는지 한눈에 확인할 수 있습니다.
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy import String, Integer, ForeignKey
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(50), nullable=False)
posts: Mapped[list["Post"]] = relationship("Post", back_populates="author")
class Post(Base):
__tablename__ = "posts"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
title: Mapped[str] = mapped_column(String(100), nullable=False)
content: Mapped[str] = mapped_column(String(500))
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
author: Mapped["User"] = relationship("User", back_populates="posts")
위 예제에서는 User 클래스와 Post 클래스가 정의되어 있습니다.
User는 여러 개의 게시글(Post)을 가질 수 있으며, Post는 반드시 하나의 User에 속합니다.
이때 relationship()을 통해 양방향 관계를 선언하여 객체 간 탐색이 가능해집니다.
📌 예제의 실행 흐름
💡 TIP: 실제 실행 시에는 엔진(engine)을 생성하고, Base.metadata.create_all()을 통해 테이블을 데이터베이스에 반영해야 합니다.
1. DeclarativeBase를 상속한 User와 Post 클래스가 선언됩니다.
2. 각 클래스는 __tablename__ 속성을 통해 매핑할 테이블명을 정의합니다.
3. mapped_column을 통해 id, name, title, content 등 컬럼이 선언됩니다.
4. relationship()을 통해 User와 Post 간의 관계가 설정됩니다.
5. 최종적으로 Base.metadata.create_all(engine)을 실행하면 데이터베이스에 실제 테이블이 생성됩니다.
이러한 과정은 객체 지향적인 접근을 가능하게 하여, SQL 쿼리를 직접 작성하지 않고도 객체 단위로 데이터를 조작할 수 있게 해줍니다.
따라서 프로젝트 규모가 커지더라도 코드 구조를 깔끔하게 유지할 수 있다는 장점이 있습니다.
💡 ORM 구조 이해를 위한 실전 팁
SQLAlchemy ORM을 단순히 문법 차원에서 배우는 것에 그치지 않고, 실제 프로젝트에서 어떻게 활용할 수 있는지 고민하는 것이 중요합니다.
특히 Declarative 매핑, registry, mapped_column은 ORM의 기초 뼈대를 이루는 개념이므로 반드시 정확하게 이해하고 넘어가야 합니다.
다음은 ORM 구조를 효과적으로 이해하고 활용하기 위한 몇 가지 실전 팁입니다.
이 팁들을 적용하면 데이터 모델링과 유지보수에서 큰 차이를 체감할 수 있습니다.
📌 실무에서 꼭 기억해야 할 포인트
- 📂모델 분리 : 큰 프로젝트에서는 모델 클래스를 별도의 파일로 분리해 관리하는 것이 좋습니다.
- 🔗관계 정의 : relationship()은 반드시 양쪽 모델에 선언해 상호 참조가 가능하도록 설계하는 것이 이상적입니다.
- ⚠️제약 조건 : nullable, unique, ForeignKey와 같은 제약 조건을 꼼꼼히 지정해야 데이터 무결성이 보장됩니다.
- 📝마이그레이션 도구 : Alembic과 같은 도구를 사용해 스키마 변경을 체계적으로 관리하세요.
💬 ORM은 SQL을 감추는 것이 목적이 아니라, 객체 지향적으로 데이터베이스를 다루기 위해 SQL을 더 직관적으로 표현하는 방법입니다.
📌 학습 시 주의사항
⚠️ 주의: 초보자의 경우 ORM만 학습하고 SQL 자체를 소홀히 하면 안 됩니다.
ORM은 결국 SQL 위에서 동작하기 때문에, 기본적인 SELECT, JOIN, WHERE 같은 SQL 구문에 대한 이해가 반드시 필요합니다.
Declarative 매핑, registry, mapped_column은 ORM의 핵심 기초이자 필수 개념입니다.
이 세 가지를 확실히 이해하면 SQLAlchemy를 활용한 데이터베이스 프로그래밍에서 흔히 부딪히는 어려움들을 훨씬 쉽게 해결할 수 있습니다.
❓ 자주 묻는 질문 (FAQ)
Declarative 매핑과 registry는 어떤 차이가 있나요?
mapped_column을 사용할 때 반드시 타입을 지정해야 하나요?
DeclarativeBase와 declarative_base()는 무엇이 다른가요?
registry를 여러 개 생성해도 되나요?
mapped_column의 server_default와 default는 어떻게 다른가요?
ORM만 사용하면 SQL을 몰라도 되나요?
Declarative 매핑을 사용하면 모든 SQL 쿼리를 대체할 수 있나요?
Alembic 같은 마이그레이션 도구는 꼭 사용해야 하나요?
🧭 SQLAlchemy ORM 핵심 개념 총정리
이번 글에서는 파이썬 ORM 라이브러리인 SQLAlchemy의 중요한 개념인 Declarative 매핑, registry, mapped_column을 다뤘습니다.
Declarative 매핑은 클래스를 정의하면서 곧바로 데이터베이스 테이블과 연결할 수 있게 해주며, registry는 이러한 매핑 정보를 중앙에서 관리하는 역할을 담당합니다.
또한 mapped_column은 컬럼 속성, 타입, 제약 조건을 선언적으로 지정할 수 있어 데이터 모델링을 훨씬 직관적으로 할 수 있습니다.
실무에서 ORM을 활용할 때는 단순히 문법을 외우는 것보다 구조적 이해를 바탕으로 활용하는 것이 중요합니다.
관계형 데이터베이스의 기본적인 개념과 SQL 구문을 알고 있다면 ORM을 사용할 때 훨씬 유연하고 강력하게 응용할 수 있습니다.
특히 프로젝트 규모가 커질수록 모델 분리, 관계 정의, 마이그레이션 관리 같은 부분이 중요해지므로, ORM을 기초부터 탄탄히 이해하는 것이 필요합니다.
Declarative 매핑을 통해 객체 지향적으로 모델을 정의하고, registry로 전체 매핑 구조를 관리하며, mapped_column으로 세부적인 스키마를 지정하는 방식은 SQLAlchemy 2.0의 핵심적인 패턴입니다.
이 글에서 다룬 원리와 예제를 기반으로 실제 프로젝트에 적용한다면, 데이터베이스 프로그래밍을 훨씬 효율적이고 안정적으로 수행할 수 있을 것입니다.
🏷️ 관련 태그 : 파이썬, SQLAlchemy, ORM, Declarative, registry, mapped_column, 데이터베이스프로그래밍, 파이썬ORM, SQLAlchemy2.0, 데이터모델링