Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby on Rails 애플리케이션에서 structure.sql을 사용할 때의 장단점

오늘의 게시물에서는 structure.sql 사용의 중요한 차이점과 이점을 다룰 것입니다. 기본 schema.rb 대 Ruby on Rails 애플리케이션의 스키마 형식. 데이터 중심 세계에서 데이터베이스의 모든 풍부한 기능을 활용하는 방법을 아는 것은 기업의 성공과 실패를 가를 수 있습니다.

두 형식 간의 주요 차이점을 확인한 후 structure.sql로 전환하는 방법을 간략하게 설명합니다. 그렇지 않으면 보존할 수 없는 데이터베이스 기능과 데이터 무결성을 보장하는 데 이것이 어떻게 도움이 될 수 있는지 보여줍니다.

이 게시물에서는 structure.sql을 사용하는 Rails 앱의 예를 보여 드리겠습니다. PostgreSQL 데이터베이스와 함께 사용되지만 기본 개념은 다른 데이터베이스로도 전환될 수 있습니다. 실제 웹 응용 프로그램을 지원하는 신뢰할 수 있는 데이터베이스 없이는 진정으로 완전한 웹 응용 프로그램이 없습니다.

더 이상 고민하지 말고 바로 들어가 봅시다!

schema.rb와 structure.sql의 차이점

Ruby on Rails 프로젝트를 시작할 때 가장 먼저 해야 할 일은 데이터베이스 마이그레이션을 실행하는 것입니다. 예를 들어 사용자 모델을 생성하는 경우 Rails는 필연적으로 마이그레이션을 실행하도록 요청하여 schema.rb를 생성합니다. 그에 따라 파일:

rails g model User first_name:string last_name:string

Rails는 다음 마이그레이션을 생성합니다.

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :first_name
      t.string :last_name
 
      t.timestamps
    end
  end
end

마이그레이션이 실행되면 Rails가 schema.rb를 생성했음을 알 수 있습니다. 파일:

ActiveRecord::Schema.define(version: 2019_12_14_074018) do
 
  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"
 
  create_table "users", force: :cascade do |t|
    t.string "first_name"
    t.string "last_name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
 
end

schema.rb 파일은 비교적 기본적인 응용 프로그램 및 사용 사례에 적합합니다.

여기에서 주목해야 할 두 가지 주요 사항이 있습니다.

  1. 데이터베이스의 Ruby 표현입니다. schema.rb Ruby를 사용하여 데이터베이스를 검사하고 구조를 표현하여 생성됩니다.
  2. 데이터베이스에 구애받지 않습니다(즉, SQLite, PostgreSQL, MySQL 또는 Rails가 지원하는 기타 데이터베이스를 사용하든 구문과 구조는 거의 동일하게 유지됩니다)

그러나 이 전략이 성장하는 앱에 너무 제한적일 때가 올 수 있습니다.

예를 들어 수백 또는 수천 개의 마이그레이션 파일이 있다고 가정해 보겠습니다.

새 프로덕션 시스템을 빠르게 가동해야 하는 경우 모든 시스템을 순서대로 실행하는 데 너무 오래 걸리는 시나리오가 발생할 수 있습니다. 또는 일부 마이그레이션에 이전 버전의 데이터베이스에서 실행되어야 하지만 현재 버전에서는 더 이상 실행할 수 없는 코드가 포함되어 있는 상황에 직면할 수 있습니다. 더 이상 유효하지 않은 특정 데이터 가정으로 마이그레이션이 작성되어 마이그레이션이 실패하는 상황이 있을 수 있습니다.

이러한 모든 시나리오는 프로덕션 단계에서든 새로운 팀원을 위한 것이든 간단한 rails db:create db:migrate를 사용하여 애플리케이션의 새 인스턴스를 효율적으로 설정하는 것을 방해합니다. 명령. 이 경우 올바른 데이터베이스 스키마를 사용하여 속도를 높이려면 어떻게 하시겠습니까?

확실히 한 가지 방법은 돌아가서 모든 깨진 마이그레이션을 수정하는 것입니다. 그것은 결코 나쁜 생각이 아닙니다!

돌아가서 많은 마이그레이션을 수정하는 데 너무 많은 비용이 든다면 다른 방법은 rails db:setup을 실행하는 것입니다. 직무. 이 작업은 schema.rb에서 데이터베이스 스키마를 생성합니다. 파일. 그러나 데이터베이스에 schema.rb에 표시되지 않는 복잡한 논리가 포함되어 있으면 어떻게 될까요? 데이터베이스의 표현은 무엇입니까?

다행히도 Rails는 structure.sql 대안을 제공합니다.

structure.sql schema.rb와 다름 다음과 같은 방법으로:

  • 데이터베이스 구조의 정확한 사본을 허용합니다. 이것은 팀과 함께 작업할 때뿐만 아니라 rails db:setup에서 프로덕션 환경에서 새 데이터베이스를 빠르게 생성해야 하는 경우에도 중요합니다. 작업.
  • 고급 데이터베이스 기능의 정보를 보존할 수 있습니다. 예를 들어 PostgreSQL을 사용하는 경우 보기, 구체화된 보기, 함수, 제약 조건 등을 사용할 수 있습니다.

애플리케이션이 특정 성숙도 수준에 도달하면 효율성을 높이고 데이터 정확성을 유지하며 초고속 성능을 보장하기 위해 책의 모든 트릭을 사용해야 합니다. structure.sql 사용 Rails 데이터베이스의 동작을 관리하려면 사용자가 그렇게 할 수 있습니다.

schema.rb에서 전환 structure.sql

schema.rb에서 변경하기 structure.sql로 비교적 간단한 과정이다. config/application.rb에서 다음 줄을 설정하기만 하면 됩니다. :

module YourApp
  class Application < Rails::Application
    config.load_defaults 6.0
 
    # Add this line:
    config.active_record.schema_format = :sql
  end
end

그런 다음 rails db:migrate를 실행합니다. db/structure.sql에 파일이 표시되어야 합니다. . 짜잔! Rails는 사용 중인 데이터베이스에 특정한 도구를 사용하여 데이터베이스 구조를 덤프합니다(PostgreSQL의 경우 해당 도구는 pg_dump , MySQL 또는 MariaDB의 경우 SHOW CREATE TABLE 출력이 포함됩니다. 각 테이블 등). 나머지 팀이 동일한 데이터베이스 구조를 갖도록 이 파일이 버전 제어 하에 있는지 확인하는 것이 좋습니다.

schema.rb 파일을 처음 보면 어려울 수 있습니다. 파일은 25줄에 불과했지만 structure.sql 파일은 무려 109줄입니다! 이러한 대용량 파일이 앱 개발에 어떤 이점을 추가할 수 있습니까?

데이터베이스 수준 제약 조건 추가

ActiveRecord는 Rails를 사용할 때 가장 좋아하는 부분 중 하나입니다. 거의 구어체 언어처럼 자연스럽게 느껴지는 방식으로 데이터베이스를 쿼리할 수 있습니다. 예를 들어 Dan이라는 회사의 모든 사용자를 찾으려면 ActiveRecord를 사용하면 다음과 같은 쿼리를 간단히 실행할 수 있습니다.

company = Company.find(name: 'Some Company')
 
# Reads just like in a natural language!
company.users.where(first_name: 'Dan')

그래도 ActiveRecord가 부족한 경우가 있습니다. 예를 들어 사용자 모델에 대해 다음과 같은 유효성 검사가 있다고 가정합니다.

class User < ApplicationRecord
  validate :name_cannot_start_with_d
 
  private
 
  def name_cannot_start_with_d
    if first_name.present? && first_name[0].downcase == 'd'
      errors.add(:first_name, "cannot start with the letter 'D'")
    end
  end
end

이름이 'Dan'인 사용자를 만들려고 하면 유효성 검사가 실행될 때 오류가 표시되어야 합니다.

User.create!(first_name: 'Dan')
Traceback (most recent call last):
ActiveRecord::RecordInvalid (Validation failed: First name cannot start with the letter 'D')

이것은 괜찮지만 귀하 또는 귀하의 팀원 중 한 명이 ActiveRecord의 유효성 검사를 우회하여 데이터를 변경했다고 가정합니다.

u = User.create(first_name: 'Pan')
 
# The update_attribute method bypasses ActiveRecord validations
u.update_attribute :first_name, 'Dan'
u.first_name
=> "Dan"

입증된 바와 같이 검증을 우회하는 것은 매우 쉽습니다.

이것은 우리의 응용 프로그램에 치명적인 결과를 초래할 수 있습니다. ActiveRecord는 축복이자 저주가 될 수 있습니다. 매우 깨끗하고 자연스러운 DSL을 가지고 있어 함께 작업하는 것이 즐겁지만 모델 수준 유효성 검사를 시행할 때는 종종 지나치게 관대합니다. 이미 알고 있는 것처럼 솔루션은 데이터베이스 수준 제약 조건을 추가하는 것입니다.

rails g migration AddFirstNameConstraintToUser

이렇게 하면 문자 'D'로 시작하는 이름을 허용하지 않는 논리로 편집할 수 있는 파일이 생성됩니다.

class AddFirstNameConstraintToUser < ActiveRecord::Migration[6.0]
  def up
    execute "ALTER TABLE users ADD CONSTRAINT name_cannot_start_with_d CHECK (first_name !~* '^d')"
  end
 
  def down
    execute "ALTER TABLE users DROP CONSTRAINT IF EXISTS name_cannot_start_with_d"
  end
end

매우 마이그레이션을 성공적으로 되돌리는 코드를 추가하는 것이 중요합니다. 위의 예에서 나는 updown 지시. up 마이그레이션이 실행될 때 메소드가 실행됩니다. down 마이그레이션이 롤백될 때 실행됩니다. 데이터베이스 구조를 제대로 되돌리지 않으면 나중에 수동으로 집 청소를 해야 할 수도 있습니다. 항상 up에서 모두 실행할 수 있는 마이그레이션 파일을 보유하는 것이 좋습니다. 및 down 미래의 두통을 피하기 위해.

이제 마이그레이션을 실행하고 해당 제약 조건을 우회할 수 있는지 확인하십시오.

rails db:migrate
user = User.create first_name: 'Pan'
user.update_attribute :first_name, 'Dan'
 
ActiveRecord::StatementInvalid (PG::CheckViolation: ERROR:  new row for relation "users" violates check constraint "name_cannot_start_with_d")
DETAIL:  Failing row contains (2, Dan, null, 2019-12-14 09:40:11.809358, 2019-12-14 09:40:41.658974).

완벽한! 제약 조건이 의도한 대로 작동합니다. 이유가 무엇이든 ActiveRecord의 검증을 우회하더라도 데이터 무결성을 유지하기 위해 우리는 여전히 우리의 궁극적인 골키퍼인 데이터베이스⁠에 의존할 수 있습니다.

이것이 structure.sql과 어떤 관련이 있습니까? ?

살펴보면 다음과 같이 추가된 것을 알 수 있습니다.

CREATE TABLE public.users (
    id bigint NOT NULL,
    first_name character varying,
    last_name character varying,
    created_at timestamp(6) without time zone NOT NULL,
    updated_at timestamp(6) without time zone NOT NULL,
    CONSTRAINT name_cannot_start_with_d CHECK (((first_name)::text !~* '^d'::text)));

제약 조건은 스키마 자체 내에 있습니다!

동안 schema.rb 또한 데이터베이스 수준 제약 조건을 지원하지만 트리거, 시퀀스, 저장 프로시저 또는 검사 제약 조건과 같이 데이터베이스가 지원할 수 있는 모든 것을 표현하지 않는다는 점을 기억하는 것이 중요합니다. 예를 들어, 정확히 동일한 마이그레이션(AddFirstNameConstraintToUser ) schema.rb를 사용하려는 경우 :

ActiveRecord::Schema.define(version: 2019_12_14_074018) do
 
  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"
 
  create_table "users", force: :cascade do |t|
    t.string "first_name"
    t.string "last_name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
 
end

파일이 변경되지 않았습니다! 제약 조건이 추가되지 않았습니다.

프로젝트 작업을 위해 새 개발자를 온보딩하는 경우 잠재적으로 다른 데이터베이스 규정에 따라 운영될 수 있습니다.

structure.sql 커밋 버전 관리를 통해 팀이 같은 페이지에 있는지 확인하는 데 도움이 됩니다. rails db:setup을 실행한다면 structure.sql 파일에서 데이터베이스의 구조에는 위의 제약 조건이 포함됩니다. schema.rb 사용 그런 보장은 없습니다.

생산 시스템에 대해서도 마찬가지입니다. 새로운 데이터베이스로 애플리케이션의 새 인스턴스를 빠르게 가동해야 하고 모든 마이그레이션을 순차적으로 실행하는 데 오랜 시간이 걸리는 경우⁠— structure.sql에서 데이터베이스를 설정합니다. 파일이 훨씬 빠를 것입니다. structure.sql 다른 인스턴스와 똑같은 구조로 데이터베이스를 생성합니다.

성장통

간결한 schema.rb 관리 팀 전체에 파일을 작성하는 것이 장황한 structure.sql을 관리하는 것보다 훨씬 쉬운 작업입니다. 파일.

structure.sql로 마이그레이션할 때 가장 큰 문제 중 하나 필요한 변경 사항만 해당 파일에 커밋되도록 하는 것입니다. 이는 때때로 수행하기 어려울 수 있습니다.

예를 들어, 누군가의 브랜치를 가져와서 해당 브랜치에 특정한 마이그레이션을 실행한다고 가정해 보겠습니다. 귀하의 structure.sql 이제 일부 변경 사항이 포함됩니다. 그런 다음 자체 분기 작업으로 돌아가서 새 마이그레이션을 생성합니다. 귀하의 structure.sql 파일에는 이제 지점과 다른 지점의 변경 사항이 모두 포함됩니다. 이는 처리하기가 다소 번거로울 수 있으며 이러한 충돌을 관리하는 데 약간의 학습 곡선이 있음은 의심할 여지가 없습니다.

이 접근 방식을 사용하여 절충안을 만들고 있습니다. 데이터베이스의 고급 기능을 보존할 수 있는 약간의 코드 복잡성을 미리 처리해야 합니다. 결과적으로 우리는 더 간단한 스키마 표현을 처리해야 할 뿐만 아니라 데이터베이스의 모든 기능을 손끝에서 사용할 수 없습니다. db:setup에서 백업을 설정하려는 경우 직무. 프로덕션 시스템에서 손상되거나 잘못된 데이터를 수정하거나 데이터베이스가 제공하는 모든 고급 기능을 사용할 수 없는 것보다 약간의 버전 제어 번거로움을 참는 것이 가장 좋습니다.

일반적으로 structure.sql 파일에는 특정 분기에 필요한 변경 사항만 포함되어 있습니다.

  • 마이그레이션이 포함된 브랜치에서 작업을 마치면 rails db:rollback STEP=n을 실행해야 합니다. 여기서 n 해당 분기의 마이그레이션 수입니다. 이렇게 하면 데이터베이스 구조가 원래 상태로 되돌아갑니다.
  • 지점에서 작업한 후 롤백하는 것을 잊어버릴 수 있습니다. 이 경우 새 브랜치에서 작업할 때 깨끗한 structure.sql 파일을 가져와야 합니다. 새 마이그레이션을 만들기 전에 마스터에서 파일을 가져옵니다.

일반적으로 structure.sql 파일에는 마스터에 병합되기 전에 분기와 관련된 변경 사항만 포함되어야 합니다.

결론

일반적으로 Rails 애플리케이션이 작거나 데이터베이스가 제공하는 고급 기능이 필요하지 않은 경우 schema.rb를 사용하는 것이 안전합니다. , 매우 읽기 쉽고 간결하며 관리하기 쉽습니다.

그러나 응용 프로그램의 크기와 복잡성이 증가함에 따라 데이터베이스 구조를 정확하게 반영하는 것이 중요합니다. 이를 통해 팀은 그렇지 않으면 불가능했을 올바른 제약 조건, 데이터베이스 모듈, 기능 및 연산자를 유지할 수 있습니다. 잘 관리된 structure.sql과 함께 Rails를 사용하는 방법 배우기 파일은 더 간단한 schema.rb 단순히 할 수 없습니다.

추신 Ruby Magic 게시물이 언론에 공개되는 즉시 읽고 싶다면 Ruby Magic 뉴스레터를 구독하고 게시물을 놓치지 마세요!