데이터는 대부분의 소프트웨어 응용 프로그램의 핵심 부분입니다. 데이터베이스에서 데이터를 매핑하고 쿼리하는 것은 개발자의 삶에서 반복되는 작업입니다. 이 때문에 프로세스를 이해하고 작업을 단순화하는 추상화를 사용할 수 있는 것이 중요합니다.
두 시리즈 중 첫 번째 게시물인 이 게시물에서는 ActiveRecord(Ruby)와 Ecto(Elixir)를 비교한 것입니다. 두 도구를 모두 사용하여 개발자가 데이터베이스 스키마를 마이그레이션하고 매핑할 수 있는 방법을 살펴보겠습니다.
그래서 우리는 사과와 오렌지를 비교할 것입니다. (Original) 한 마디도 필요 없는 배트걸과 '나는 배트맨이다'라고 노골적으로 말하는 배트맨. 암시적, 구성보다 규칙, 대 명시적 의도. 1라운드. 파이팅!
액티브 레코드
출시된 지 10년이 넘었지만 Ruby on Rails 프로젝트와 함께 기본적으로 제공되는 유명한 ORM인 ActiveRecord에 대해 이미 들어보셨을 것입니다.
액티브 레코드 MVC의 M(모델)은 비즈니스 데이터와 논리를 나타내는 시스템 계층입니다. ActiveRecord는 데이터베이스에 영구 저장이 필요한 데이터가 있는 비즈니스 개체의 생성 및 사용을 용이하게 합니다. Object Relational Mapping 시스템에 대한 설명인 ActiveRecord 패턴의 구현입니다.
대부분 Rails와 함께 사용하는 것으로 알려져 있지만 ActiveRecord는 독립 실행형 도구로도 사용되어 다른 프로젝트에 포함시킬 수 있습니다.
엑토
ActiveRecord와 비교할 때 Ecto는 아주 새로운(현재로서는 유명하지 않은) 도구입니다. Elixir로 작성되었으며 기본적으로 Phoenix 프로젝트에 포함됩니다.
ActiveRecord와 달리 Ecto는 ORM이 아니라 Elixir를 사용하여 쿼리를 작성하고 데이터베이스와 상호 작용할 수 있도록 하는 라이브러리입니다.
엑토 Elixir에서 쿼리를 작성하고 데이터베이스와 상호 작용하기 위한 도메인 특정 언어입니다.
기본적으로 Ecto는 다른 Elixir 프로젝트에서 사용되며 어떤 프레임워크에도 연결되지 않은 독립 실행형 도구입니다.
사과와 오렌지를 비교하고 있지 않습니까?
네, 우리는! ActiveRecord와 Ecto는 의미상 다르지만 데이터베이스 마이그레이션, 데이터베이스 매핑, 쿼리 및 유효성 검사와 같은 공통 기능은 ActiveRecord와 Ecto에서 모두 지원됩니다. 그리고 두 도구를 사용하여 동일한 결과를 얻을 수 있습니다. Ruby 배경에서 나온 Elixir에 관심이 있는 분들을 위해 이것이 흥미로운 비교가 될 것이라고 생각했습니다.
인보이스 시스템
포스트의 나머지 부분에서 가상의 송장 시스템이 데모에 사용됩니다. 슈퍼 히어로에게 옷을 파는 가게가 있다고 가정해 봅시다. 간단하게 하기 위해 인보이스 시스템에 대해 users라는 두 개의 테이블만 사용합니다. 및 인보이스 .
다음은 필드 및 유형과 함께 해당 테이블의 구조입니다.
사용자
필드 | 유형 |
---|---|
이름 | 문자열 |
이메일 | 문자열 |
created_at(ActiveRecord) / 삽입된_at(Ecto) | 날짜/시간 |
업데이트된_at | 날짜/시간 |
인보이스
필드 | 유형 |
---|---|
사용자 ID | 정수 |
지불 방식 | 문자열 |
유료_at | 날짜/시간 |
created_at(ActiveRecord) / 삽입된_at(Ecto) | 날짜/시간 |
업데이트된_at | 날짜/시간 |
users 테이블에는 full_name이라는 4개의 필드가 있습니다. , 이메일 , 업데이트됨 사용된 도구에 따라 달라지는 네 번째 필드. ActiveRecord는 created_at Ecto가 inserted_at을 생성하는 동안 필드 레코드가 데이터베이스에 처음 삽입된 순간의 타임스탬프를 나타내는 필드입니다.
두 번째 테이블의 이름은 인보이스입니다. . user_id라는 5개의 필드가 있습니다. , 결제 수단 , 유료_at , 업데이트됨 users 테이블과 유사하게 created_at 또는 inserted_at , 사용된 도구에 따라 다릅니다.
사용자 및 인보이스 테이블에는 다음과 같은 연관이 있습니다.
- 사용자에게 많은 인보이스가 있습니다.
- 인보이스는 사용자 소유입니다.
이전
마이그레이션을 통해 개발자는 반복적인 프로세스를 사용하여 시간이 지남에 따라 데이터베이스 스키마를 쉽게 발전시킬 수 있습니다. ActiveRecord와 Ecto는 개발자가 SQL을 직접 처리하는 대신 고급 언어(각각 Ruby 및 Elixir)를 사용하여 데이터베이스 스키마를 마이그레이션할 수 있도록 합니다.
ActiveRecord 및 Ecto를 사용하여 사용자 및 인보이스 테이블을 생성하여 마이그레이션이 어떻게 작동하는지 살펴보겠습니다.
ActiveRecord:사용자 테이블 생성
이전
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :full_name, null: false
t.string :email, index: {unique: true}, null: false
t.timestamps
end
end
end
ActiveRecord 마이그레이션을 통해 create_table
을 사용하여 테이블 생성 가능 방법. created_at
및 updated_at
필드가 마이그레이션 파일에 정의되지 않은 경우, t.timestamps
사용 ActiveRecord를 트리거하여 둘 다 생성합니다.
생성된 테이블 구조
CreateUsers
실행 후 마이그레이션할 때 생성된 테이블의 구조는 다음과 같습니다.
Column | Type | Nullable | Default
------------+-----------------------------+----------+-----------------------------------
id | bigint | not null | nextval('users_id_seq'::regclass)
full_name | character varying | not null |
email | character varying | not null |
created_at | timestamp without time zone | not null |
updated_at | timestamp without time zone | not null |
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"index_users_on_email" UNIQUE, btree (email)
마이그레이션은 이메일 필드에 대한 고유 색인 생성도 담당합니다. 옵션 index: {unique: true}
이메일 필드 정의로 전달됩니다. 이것이 테이블에 "index_users_on_email" UNIQUE, btree (email)
가 나열된 이유입니다. 구조의 일부로 색인을 생성합니다.
Ecto:사용자 테이블 생성
이전
defmodule Financex.Repo.Migrations.CreateUsers do
use Ecto.Migration
def change do
create table(:users) do
add :full_name, :string, null: false
add :email, :string, null: false
timestamps()
end
create index(:users, [:email], unique: true)
end
end
Ecto 마이그레이션은 create()
기능을 결합합니다. 및 table()
사용자 테이블을 생성합니다. Ecto 마이그레이션 파일은 ActiveRecord와 매우 유사합니다. ActiveRecord에서 타임스탬프 필드(created_at
및 updated_at
)는 t.timestamps
에 의해 생성됩니다. Ecto에서 타임스탬프 필드(inserted_at
및 updated_at
)는 timestamps()
에 의해 생성됩니다. 기능.
인덱스가 생성되는 방식에 있어 두 도구 사이에는 약간의 차이가 있습니다. ActiveRecord에서 인덱스는 생성되는 필드에 대한 옵션으로 정의됩니다. Ecto는 create()
함수의 조합을 사용합니다. 및 index()
이를 달성하기 위해 조합이 테이블 자체를 생성하는 데 사용되는 방식과 일치합니다.
생성된 테이블 구조
Column | Type | Nullable | Default
-------------+-----------------------------+----------+-----------------------------------
id | bigint | not null | nextval('users_id_seq'::regclass)
full_name | character varying(255) | not null |
email | character varying(255) | not null |
inserted_at | timestamp without time zone | not null |
updated_at | timestamp without time zone | not null |
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"users_email_index" UNIQUE, btree (email)
Financex.Repo.Migrations.CreateUsers
실행 시 생성된 테이블 마이그레이션은 ActiveRecord를 사용하여 생성한 테이블과 동일한 구조를 가지고 있습니다.
ActiveRecord:invoices
생성 표
이전
class CreateInvoices < ActiveRecord::Migration[5.2]
def change
create_table :invoices do |t|
t.references :user
t.string :payment_method
t.datetime :paid_at
t.timestamps
end
end
end
이 마이그레이션에는 t.references
가 포함됩니다. 이전 방법에는 없었습니다. 사용자 테이블에 대한 참조를 만드는 데 사용됩니다. 앞서 설명한 바와 같이 사용자에게는 많은 인보이스가 있으며 인보이스는 사용자에게 속합니다. t.references
메소드는 user_id
를 생성합니다. 해당 참조를 보관하려면 인보이스 테이블의 열을 사용하십시오.
생성된 테이블 구조
Column | Type | Nullable | Default
----------------+-----------------------------+----------+--------------------------------------
id | bigint | not null | nextval('invoices_id_seq'::regclass)
user_id | bigint | |
payment_method | character varying | |
paid_at | timestamp without time zone | |
created_at | timestamp without time zone | not null |
updated_at | timestamp without time zone | not null |
Indexes:
"invoices_pkey" PRIMARY KEY, btree (id)
"index_invoices_on_user_id" btree (user_id)
생성된 테이블은 이전에 생성된 테이블과 동일한 패턴을 따릅니다. 유일한 차이점은 추가 색인(index_invoices_on_user_id
), ActiveRecord는 t.references
방법을 사용합니다.
Ecto:invoices
생성 표
이전
defmodule Financex.Repo.Migrations.CreateInvoices do
use Ecto.Migration
def change do
create table(:invoices) do
add :user_id, references(:users)
add :payment_method, :string
add :paid_at, :utc_datetime
timestamps()
end
create index(:invoices, [:user_id])
end
end
Ecto는 또한 references()
를 사용하여 데이터베이스 참조 생성을 지원합니다. 기능. 열 이름을 유추하는 ActiveRecord와 달리 Ecto는 개발자가 user_id
를 명시적으로 정의해야 합니다. 열 이름. references()
함수를 사용하려면 개발자가 참조가 가리키는 테이블을 명시적으로 정의해야 합니다. 이 예에서는 users 테이블입니다.
생성된 테이블 구조
Column | Type | Nullable | Default
----------------+-----------------------------+----------+--------------------------------------
id | bigint | not null | nextval('invoices_id_seq'::regclass)
user_id | bigint | |
payment_method | character varying(255) | |
paid_at | timestamp without time zone | |
inserted_at | timestamp without time zone | not null |
updated_at | timestamp without time zone | not null |
Indexes:
"invoices_pkey" PRIMARY KEY, btree (id)
"invoices_user_id_index" btree (user_id)
Foreign-key constraints:
"invoices_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
두 마이그레이션 모두 매우 유사합니다. references
방식에 관해서는 기능이 처리되지만 몇 가지 차이점이 있습니다.
-
Ecto는
user_id
에 대한 외래 키 제약 조건을 생성합니다. 필드("invoices_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
), 사용자와 인보이스 테이블 간의 참조 무결성을 유지합니다. -
ActiveRecord는
user_id
에 대한 색인을 자동으로 생성합니다. 열. Ecto는 개발자에게 이에 대해 명시적으로 요구합니다. 이것이 마이그레이션에create index(:invoices, [:user_id])
가 있는 이유입니다. 성명서.
ActiveRecord:데이터 매핑 및 연결
ActiveRecord는 "구성보다 규칙"이라는 모토로 유명합니다. 기본적으로 모델 클래스 이름을 사용하여 데이터베이스 테이블 이름을 유추합니다. User
라는 클래스 , 기본적으로 users
테이블을 소스로 사용합니다. ActiveRecord는 또한 테이블의 모든 열을 인스턴스 속성으로 매핑합니다. 개발자는 테이블 간의 연관을 정의하기만 하면 됩니다. 또한 ActiveRecord에서 관련 클래스와 테이블을 추론하는 데 사용됩니다.
ActiveRecord를 사용하여 사용자 및 인보이스 테이블이 매핑되는 방법을 살펴보십시오.
사용자
class User < ApplicationRecord
has_many :invoices
end
인보이스
class Invoice < ApplicationRecord
belongs_to :user
end
Ecto:데이터 매핑 및 연결
반면에 Ecto는 개발자가 데이터 소스와 해당 필드에 대해 명시적일 것을 요구합니다. Ecto에는 유사한 has_many
가 있지만 및 belongs_to
또한 개발자는 연결된 테이블과 해당 테이블 스키마를 처리하는 데 사용되는 스키마 모듈에 대해 명시적으로 설명해야 합니다.
이것이 Ecto가 사용자와 인보이스 테이블을 매핑하는 방법입니다:
사용자
defmodule Financex.Accounts.User do
use Ecto.Schema
schema "users" do
field :full_name, :string
field :email, :string
has_many :invoices, Financex.Accounts.Invoice
timestamps()
end
end
인보이스
defmodule Financex.Accounts.Invoice do
use Ecto.Schema
schema "invoices" do
field :payment_method, :string
field :paid_at, :utc_datetime
belongs_to :user, Financex.Accounts.User
timestamps()
end
end
마무리
이번 포스팅에서는 눈 깜짝할 사이에 사과와 오렌지를 비교해봤습니다. ActiveRecord와 Ecto가 데이터베이스 마이그레이션 및 매핑을 처리하는 방법을 비교했습니다. 은밀한 오리지널 배트걸과 노골적인 '나는 배트맨' 배트맨의 전투
"구성에 대한 규칙" 덕분에 ActiveRecord를 사용하면 일반적으로 쓰기 작업이 줄어듭니다. Ecto는 반대 방향으로 이동하여 개발자가 의도를 보다 명확하게 해야 합니다. 일반적으로 "코드가 적음"이 더 낫다는 점 외에 ActiveRecord에는 개발자가 모든 것을 결정하고 모든 기본 구성을 이해하지 않아도 되는 최적의 기본값이 있습니다. 초보자에게 ActiveRecord는 표준을 엄격히 준수하는 한 기본적으로 "충분히 좋은" 결정을 내리기 때문에 더 적합한 솔루션입니다.
Ecto의 명시적 측면은 코드 조각의 동작을 더 쉽게 읽고 이해할 수 있도록 하지만 개발자가 데이터베이스 속성과 사용 가능한 기능에 대해 더 많이 이해해야 합니다. Ecto가 언뜻 보기에 번거롭게 보일 수 있는 것은 장점 중 하나입니다. ActiveRecord와 Ecto 세계에 대한 제 개인적인 경험을 바탕으로 Ecto의 명시성은 ActiveRecord를 사용한 프로젝트에서 흔히 발생하는 "비하인드" 효과와 불확실성을 제거합니다. 개발자가 코드에서 읽는 것은 애플리케이션에서 일어나는 일이며 암시적인 동작은 없습니다.
몇 주 후 두 번째 블로그인 "ActiveRecord vs Ecto" 시리즈에서 ActiveRecord와 Ecto 모두에서 쿼리 및 유효성 검사가 작동하는 방식을 다룰 것입니다.
이 기사에 대한 귀하의 생각을 알고 싶습니다. 우리는 항상 다룰 새로운 주제를 찾고 있으므로 더 자세히 알고 싶은 주제가 있으면 주저하지 말고 @AppSignal로 알려주십시오!