Ecto 연관 관계 및 고유 제약 조건 문제 해결: PostgreSQL DEFERRABLE 활용

Ecto, on_replace and deferred checks

발행일
2025년 07월 03일

핵심 요약

  • 1 Ecto에서 연관된 레코드의 순서를 변경할 때 발생하는 고유 제약 조건 위반 문제를 다룹니다.
  • 2 Ecto의 개별 업데이트 방식이 중간 상태에서 PostgreSQL 고유 인덱스 제약 조건을 트리거하여 오류가 발생함을 설명합니다.
  • 3 PostgreSQL의 DEFERRABLE INITIALLY DEFERRED 제약 조건을 사용하여 트랜잭션 커밋 시점까지 고유성 검사를 지연시켜 문제를 해결합니다.

도입

본문은 프로세스와 단계(steps)를 모델링하는 일반적인 시나리오에서 시작됩니다. 단계의 순서를 `order` 필드로 관리하고, `process_id`와 `order`에 고유 인덱스를 설정하여 데이터 무결성을 보장하려 합니다. Ecto 스키마에서는 `has_many`와 `on_replace: :delete` 옵션, 그리고 `cast_assoc`를 사용하여 부모 프로세스를 통해 단계들을 관리합니다. 그러나 이러한 설정에도 불구하고, 단계의 순서를 변경하려 할 때 `Ecto.ConstraintError`가 발생하는 예상치 못한 문제에 직면하게 됩니다.

Ecto 연관 관계 업데이트 시 고유 제약 조건 문제

문제 발생 원인 분석

Ecto를 사용하여 연관된 레코드(단계)의 순서를 변경할 때 Ecto.ConstraintError가 발생했습니다. 이 오류는 steps_process_id_order_index라는 고유 제약 조건 위반으로 인해 발생합니다. 예를 들어 (step 1, order: 1)(step 2, order: 2)(step 1, order: 2)(step 2, order: 1)로 변경하려 할 때, Ecto는 다음 과정을 거쳐 개별적으로 레코드를 업데이트합니다.

  • step 1order2로 업데이트

이 시점에서 데이터베이스는 (step 1, order: 2)(step 2, order: 2)라는 중간 상태가 됩니다. 이는 process_idorder 컬럼에 설정된 고유 인덱스(unique_index(:steps, [:process_id, :order]))를 즉시 위반하게 되어 오류가 발생합니다. 데이터베이스는 트랜잭션의 최종 상태가 아닌 현재 시점의 상태를 기준으로 제약 조건을 검사하기 때문에, 비록 다음 업데이트로 step 2order1로 변경될 예정이라 하더라도 중간 상태에서의 위반을 허용하지 않습니다.

해결책: PostgreSQL의 DEFERRABLE 제약 조건 활용

이 문제를 해결하기 위해 PostgreSQL의 DEFERRABLE INITIALLY DEFERRED 제약 조건을 활용합니다. 이 제약 조건은 고유성 검사를 즉시 수행하는 대신, 현재 트랜잭션이 커밋되는 시점까지 검사를 지연시킵니다. 이를 통해 중간 상태에서 발생하는 고유 제약 조건 위반을 회피할 수 있습니다.

기존의 create unique_index 대신 다음 SQL 명령어를 마이그레이션에 추가하여 제약 조건을 생성합니다:

sql execute """ ALTER TABLE steps ADD CONSTRAINT unique_step UNIQUE (process_id, "order") DEFERRABLE INITIALLY DEFERRED """

ALTER TABLE 문은 steps 테이블에 process_idorder 컬럼에 대한 고유 제약 조건을 추가하며, 이 제약 조건은 기본적으로 트랜잭션 커밋 시점까지 검사가 지연되도록 설정됩니다. 이 변경을 적용함으로써 Ecto의 연관 관계 업데이트 메커니즘이 중간 제약 조건 위반 없이 완벽하게 작동하게 됩니다.

결론

본 사례는 겉보기에는 간단해 보이는 레코드 순서 변경 작업이 데이터베이스의 고유 제약 조건과 ORM(Ecto)의 동작 방식이 결합될 때 얼마나 복잡한 엣지 케이스를 발생시킬 수 있는지 보여줍니다. 이는 데이터베이스가 단순한 저장소가 아니며, 그 기능과 미묘한 차이를 깊이 이해하는 것이 매우 중요하다는 교훈을 강조합니다. 복잡한 ORM 로직을 직접 구현하려 시도하는 대신, PostgreSQL의 `DEFERRABLE` 제약 조건과 같은 데이터베이스의 강력한 기능을 활용함으로써 더 효율적이고 견고한 해결책을 찾을 수 있음을 시사합니다. 데이터베이스 지식의 중요성을 다시 한번 상기시켜주는 사례입니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!