Ecto.Schema를 래핑하는 프로젝트 전용 스키마 모듈. UUID, 마이크로초 타임스탬프 등 반복 설정을 중앙화.

핵심 내용

모든 Ecto 스키마에 동일한 3줄을 반복하는 대신, 래퍼 모듈을 만들어 use MyApp.Schema로 대체 (출처: The Modifications I Make to Every New Phoenix Project):

defmodule MyApp.Schema do
  defmacro __using__(_) do
    quote do
      use Ecto.Schema
      @primary_key {:id, :binary_id, autogenerate: true}
      @foreign_key_type :binary_id
      @timestamps_opts [type: :utc_datetime_usec]
    end
  end
end

utc_datetime_usec인가: 기본 utc_datetime은 초 단위 절삭 → 같은 초에 삽입된 레코드 정렬 불가. DateTime.truncate(:second) 호출도 불필요해짐.

Repo 설정도 함께: config/config.exs에서 migration 기본값도 맞춰줘야 스키마와 마이그레이션이 일관됨:

config :my_app, MyApp.Repo,
  migration_primary_key: [type: :binary_id],
  migration_foreign_key: [type: :binary_id],
  migration_timestamps: [type: :utc_datetime_usec]

Embedded Schema와의 관계

커스텀 스키마 모듈은 DB 연결 스키마에 주로 적용되지만, embedded schema에도 동일한 원리가 적용될 수 있다 (출처: Structs and Embedded Schemas in Elixir Beyond Maps AppSignal Blog). embedded_schema는 내부적으로 schema 매크로를 source: nil로 호출하므로, use Ecto.Schema 대신 use MyApp.Schema를 사용하면 embedded schema에도 동일한 기본값(UUID primary key 등)이 자동 적용된다.

단, embedded schema는 DB 테이블 없이 동작하므로 @primary_key@foreign_key_type 설정이 실질적 의미가 없는 경우가 많다. API 입력 검증용 Command 패턴이나 LiveView form 전용 스키마처럼 DB와 무관한 용도에는 별도의 경량 래퍼를 고려할 수 있다.

자세한 내용은 Ecto Embedded Schema 참조.

관련 링크