SeaORM은 Rust에서 관계형 데이터베이스를 다루는 비동기 ORM이다. 테이블과 행을 엔티티·모델로 표현하고 조회·삽입·수정·삭제를 Rust API로 구성한다. 데이터베이스 서버 자체가 아니며, 애플리케이션과 서버 사이의 데이터 접근 계층에 해당한다.
엔티티와 모델의 역할
엔티티는 테이블·컬럼·관계에 대한 정보를 나타내고 모델은 조회한 한 행의 값을 나타낸다. 변경을 표현할 때는 ActiveModel을 사용해 어떤 값을 설정하는지 구분한다. SQL을 직접 작성하는 경우와 달리 컬럼과 조건을 코드의 타입으로 다룰 수 있고, 엔티티 생성 도구로 반복 선언을 줄일 수 있다.
장점은 흔한 CRUD를 일관되게 작성하고 런타임에 조건을 조합하기 쉽다는 것이다. 여러 지원 DB와 비동기 런타임을 선택할 수 있다. 한계는 생성된 SQL과 실제 DB의 동작을 여전히 이해해야 한다는 점이다. 복잡한 집계나 DB 고유 기능은 원시 SQL이 더 명확할 수 있으며, ORM을 쓴다고 인덱스 누락이나 과도한 왕복 조회가 자동 해결되지는 않는다.
대출 가능한 책 조회
아래는 SeaORM 2 계열의 조회 API를 쓰는 함수 조각이다. 연결된 DatabaseConnection과 book 엔티티가 이미 있다고 가정한다. book 테이블에는 id, title, available 컬럼이 있고 생성된 Column::Available과 Column::Title, Model 타입이 있어야 한다. 새 프로젝트 전체나 테이블 생성 코드를 대신하는 예제가 아니다.
use sea_orm::{ColumnTrait, DatabaseConnection, DbErr, EntityTrait,
QueryFilter, QueryOrder, QuerySelect};
use crate::entity::book;
pub async fn available_books(db: &DatabaseConnection)
-> Result<Vec<book::Model>, DbErr>
{
book::Entity::find()
.filter(book::Column::Available.eq(true))
.order_by_asc(book::Column::Title)
.limit(20)
.all(db)
.await
}호출 결과는 대출 가능 상태인 책을 제목순으로 정렬한 최대 20개의 모델이다. 결과가 없으면 빈 목록이며, 연결이나 질의가 실패하면 DbErr가 반환된다. 제목이 같은 행까지 일정하게 정렬하려면 id 같은 고유한 보조 정렬 기준을 더한다. 많은 행을 읽는 서비스에서는 제한 없는 all 호출보다 페이지 경계를 명시하는 편이 적절하다.
트랜잭션과 운영 판단
책 상태를 읽고 예약을 추가하는 여러 단계가 하나의 업무라면 트랜잭션과 동시 변경 규칙이 필요하다. ORM 모델을 읽었다는 사실만으로 다른 사용자의 변경을 막지는 못한다. 실제 SQL, 영향받은 행 수, 실행 계획을 함께 확인하고 스키마 변경은 별도 마이그레이션 절차로 관리한다.
한 목록의 각 행을 순회하며 연관 데이터를 다시 조회하면 N+1 문제가 생길 수 있다. 필요한 관계를 함께 읽거나 묶음 조회를 설계한다. 엔티티 코드가 현재 DB와 달라졌을 때의 오류, NULL과 타입 변환도 테스트 대상이다. 사용 버전의 엔티티 형식과 기능 플래그를 확인해 서로 다른 세대의 예제를 섞지 않는 것이 중요하다.