미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
정의를 참조하면 DSL(도메인 특정 언어)은 특정 애플리케이션 도메인에 특화된 컴퓨터 언어입니다. 즉, 특정 요구 사항을 충족하기 위해 개발되었습니다.
이 글을 읽으면 DSL이 무엇이며 루비와 어떤 공통점이 있는지 알게 될 것입니다.
정의를 참조하면 DSL(도메인 특정 언어)은 특정 애플리케이션 도메인에 특화된 컴퓨터 언어입니다. 즉, 특정 요구 사항을 충족하기 위해 개발되었습니다. DSL에는 두 가지 유형이 있습니다:
An 외부 자체 구문 분석기가 필요한 DSL. 잘 알려진 예로 SQL 언어를 들 수 있는데, 이 언어를 사용하면 데이터베이스가 생성되지 않은 언어로 데이터베이스와 상호 작용할 수 있습니다.
An 내부 자체 구문이 없는 대신 특정 프로그래밍 언어의 구문을 사용하는 DSL입니다.
짐작할 수 있듯이 두 번째 DSL 유형에 계속 집중할 것입니다.
기본적으로 루비 메타프로그래밍을 활용하면 자신만의 미니 언어를 만들 수 있습니다. 메타프로그래밍은 프로그래밍 기법입니다. 코드 런타임에 동적으로(즉석에서) 설정할 수 있습니다. 잘 모르실 수도 있지만, 여러분은 매일 다양한 DSL을 사용하고 있을 것입니다. DSL의 기능을 이해하기 위해 아래의 몇 가지 예를 살펴보겠습니다. 이 모든 예에는 공통된 요소가 하나씩 있는데, 이를 가리킬 수 있을까요?
Rails.application.routes.draw do
루트로 'home#index'
resources :users do
get :search, on: :collection
end
end
```
Rails를 사용해 본 사람이라면 누구나 알고 있습니다. config/routes.rb 파일에서 애플리케이션 경로를 정의합니다(HTTP 동사와 URL을 컨트롤러 동작에 매핑). 하지만 어떻게 작동하는지 궁금한 적이 있으신가요? 사실 루비 코드일 뿐입니다.
FactoryBot.define do
factory :사용자 do
company
sequence(:email) { |i| "user_#{i}@test.com" }
sequence(:first_name) { |i| "사용자 #{i}" }
last_name 'Test'
role 'manager'
end
end
테스트를 작성하려면 종종 객체를 제작해야 합니다. 따라서 시간 낭비를 피하려면 프로세스를 최대한 단순화하는 것이 좋습니다. 이것이 바로 FactoryBot 는 기억하기 쉬운 키워드와 사물을 설명하는 방법을 제공합니다.
'sinatra/base' 필요
클래스 WebApplication < Sinatra::Base
get '/' do
'Hello world'
end
end
```
시나트라 는 웹 애플리케이션을 처음부터 만들 수 있는 프레임워크입니다. 요청 방법, 경로, 응답을 더 쉽게 정의할 수 있을까요?
다른 DSL 예는 다음과 같습니다. 레이크, RSpec 또는 활성 레코드. 각 DSL의 핵심 요소는 블록 사용.
이제 후드 아래에 무엇이 숨어 있는지, 그리고 구현이 어떤 모습일 수 있는지 이해할 차례입니다.
다양한 제품에 대한 데이터를 저장하는 애플리케이션이 있다고 가정해 봅시다. 사용자 정의 파일에서 데이터를 가져올 수 있는 기능을 추가하여 애플리케이션을 확장하고 싶습니다. 또한 필요한 경우 파일에서 값을 동적으로 계산할 수 있어야 합니다. 이를 위해 DSL을 만들기로 결정했습니다.
간단한 제품 표현은 다음과 같은 속성을 가질 수 있습니다(product.rb):
클래스 제품
attr_accessor :이름, :설명, :가격
end
실제 데이터베이스를 사용하는 대신 작업을 시뮬레이션할 것입니다(fake_products_database.rb):
클래스 FakeProductsDatabase
def self.store(product)
puts [product.name, product.description, product.price].join(' - ')
end
end
이제 제품 데이터가 포함된 파일을 읽고 처리하는 클래스를 만들겠습니다(dsl/data_importer.rb):
모듈 Dsl
데이터 임포터 클래스
모듈 구문
def add_product(&block)
FakeProductsDatabase.store product(&block)
end
private
def product(&block)
ProductBuilder.new.tap { |b| b.instance_eval(&block) }.product
end
end
포함 구문
def self.import_data(file_path)
new.instance_eval File.read(file_path)
end
end
end
```
클래스에는 다음과 같은 메서드가 있습니다. import_data 인자로 파일 경로를 기대합니다. 파일이 읽혀지고 그 결과가 인스턴스_평가 메서드가 클래스 인스턴스에서 호출됩니다. 어떤 기능을 하나요? 인스턴스 컨텍스트 내에서 문자열을 루비 코드로 평가합니다. 이는 다음을 의미합니다. self 의 인스턴스가 될 것입니다. 데이터 임포터 클래스. 원하는 구문/키워드를 정의할 수 있기 때문입니다(가독성을 높이기 위해 구문은 모듈로 정의됩니다). 언제 add_product 메서드가 호출되면 메서드에 주어진 블록은 다음과 같이 평가됩니다. 제품 빌더 인스턴스를 빌드하는 제품 인스턴스입니다. 제품 빌더 클래스는 아래에 설명되어 있습니다(dsl/product_builder.rb):
모듈 Dsl
제품 빌더 클래스
속성 = %i[이름 설명 가격].freeze
ATTR_READER :제품
def initialize
@product = Product.new
end
ATTRIBUTES.each do |attribute|
define_method(attribute) do |arg = nil, &block|
value = block.is_a?(Proc) ? block.call : arg
product.public_send("#{attribute}=", value)
end
end
end
end
```
이 클래스는 다음과 같이 허용되는 구문을 정의합니다. add_product 블록을 추가합니다. 약간의 메타프로그래밍을 통해 제품 속성에 값을 할당하는 메서드를 추가할 수 있습니다. 이러한 메서드는 직접 값 대신 블록을 전달하는 것도 지원하므로 런타임에 값을 계산할 수 있습니다. 속성 판독기를 사용하면 마지막에 빌드된 제품을 얻을 수 있습니다.
이제 가져오기 스크립트(import_job.rb):
필수 'dsl/데이터임포터' 요구사항
필수 'dsl/productbuilder'
리퀘이러티브 '가짜제품데이터베이스'
필수 '제품'
Dsl::DataImporter.import_data(ARGV[0])
```
마지막으로 DSL을 사용하여 제품 데이터가 포함된 파일(dataset.rb)을 가져옵니다:
```ruby
add_product do
name '충전기'
설명 '생명을 구하는'
가격 19.99
end
add_product do
name '자동차 전복'
description { "#{Time.now.strftime('%F %T')}에서 전복됨" }
price 0.01
end
add_product do
name 'Lockpick'
설명 '문은 닫히지 않습니다'
price 7.50
end
```
데이터를 가져오려면 명령어 하나만 실행하면 됩니다:
ruby import_job.rb dataset.rb
그 결과는...
충전기 - 인명 구조 - 19.99
자동차 난파 - 2018-12-09 09:47:42에 난파 - 0.01
자물쇠 따기 - 문이 닫히지 않음 - 7.5
..성공!
위의 모든 예시를 보면 DSL이 제공하는 가능성을 어렵지 않게 알 수 있습니다. DSL을 사용하면 필요한 모든 로직을 숨기고 가장 중요한 키워드만 사용자에게 노출함으로써 일부 일상적인 작업을 단순화할 수 있습니다. 이를 통해 더 높은 수준의 추상화를 달성할 수 있고 유연한 사용 가능성(재사용성 측면에서 특히 가치 있는 기능)을 제공합니다. 반면에 DSL을 추가하면 프로젝트 메타프로그래밍을 사용한 구현은 확실히 이해하고 유지 관리하기가 훨씬 더 어렵습니다. 또한 동적이기 때문에 견고한 테스트 스위트가 필요합니다. DSL을 문서화하면 더 쉽게 이해할 수 있으므로 반드시 문서화할 가치가 있습니다. 자신만의 DSL을 구현하는 것은 보람된 일이지만 반드시 보상을 받아야 한다는 점을 기억하는 것이 좋습니다.
이 주제에 관심이 있으신가요? 그렇다면 알려주세요 - 최근 프로젝트 중 하나에서 요구 사항을 충족하기 위해 만든 DSL에 대해 알려드리겠습니다.