메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

Ruby on Rails 속도 높이기

N+1 쿼리 문제점 삭제하기

David Berube, Proprietor, Berube Consulting
David Berube
David Berube is a consultant, speaker, and the author of Practical Rails Plugins, Practical Reporting with Ruby and Rails, and Practical Ruby Gems. You can reach him at info@berubeconsulting.com.

요약:  Ruby 프로그래밍 언어를 기반으로 하는 대중적인 웹 개발 프레임워크인 Ruby on Rails를 통해 데이터베이스에 쉽게 액세스할 수 있지만, 이는 그렇게 효율적으로 작동되지 않는 경우도 있습니다. Rails와 관련된 일반적인 성능 문제에 대해 알아보고, 이를 어떻게 수정할 수 있는지 살펴봅니다.

원문 게재일:  2010 년 7 월 27 일 번역 게재일:   2010 년 12 월 31 일
난이도:  중급 원문:  보기 PDF:  A4 and LetterGet Adobe® Reader®
페이지뷰:  2303 회
의견:  


Ruby 언어는 그 유연성으로 인해 자주 언급된다. Dick Sites가 "프로그램을 쓰도록 프로그램을 써라"라고 말한 것처럼 작업할 수 있다. Ruby on Rails는 핵심 Ruby 언어를 확장하지만, Ruby 자체가 그러한 확장가능성을 실현시킨다. Ruby on Rails는 그 언어의 유연성을 사용하여 더 많은 보일러판(boilerplate)이나 추가 코드 없이 고도로 구조화된 프로그램을 간편하게 쓸 수 있다. 다시 말해서, 추가 작업 없이 엄청난 표준 작동량이 수행된다. 이러한 무료 작동이 항상 완벽하지는 않지만, 작업을 많이 하지 않고도 애플리케이션에서 우수한 아키텍처를 많이 얻게 된다.

예를 들어, Ruby on Rails는 MVC(Model-View-Controller) 패턴을 기반으로 하는데, 이는 대부분의 Rails 애플리케이션이 세 부분으로 깔끔하게 분리되는 것을 의미한다. 모델에는 애플리케이션의 데이터를 관리하는 데 필요한 작동이 들어있다. 일반적으로 Ruby on Rails 애플리케이션에서는 모델과 데이터베이스 테이블 사이에 1:1의 관계가 있다. Ruby on Rails가 기본값으로 사용하는 ORM(object-relation mapping)인 ActiveRecord는 모델의 데이터베이스와의 상호 작용을 관리한다. 이는 평균적인 Ruby on Rails 프로그램에는 매우 적으며, 있는 경우에는 SQL 코딩임을 의미한다. 두 번째 부분인 보기는 사용자에게 전송하는 결과물을 작성하는 코드로 구성된다. 이는 대개 HTML, JavaScript 등으로 이루어진다. 마지막 부분인 컨트롤러는 사용자로부터의 입력을 정확한 모델로의 호출로 변환한 다음에 적절한 보기를 사용하여 응답을 렌더링한다.

Rails를 선호하는 사람들은 이 MVC 패러다임이 — Ruby and Rails의 다른 이점과 함께 — 편의성이 점점 높아지고, 적은 수의 프로그래머들로 더 짧은 시간 내에 더 많은 기능을 제작할 수 있다고 주장하면서 자주 언급한다. 물론 이는 각 소프트웨어 개발자 비용에 비해 더 높은 비즈니스 가치를 의미하기 때문에, Ruby on Rails 개발은 엄청나게 많이 사용되고 있다.

하지만 초기 개발 비용은 전체적인 그림이 아니다. 유지보수 비용과 하드웨어 비용과 같이 애플리케이션을 실행하기 위해 지속적인 기타 비용이 있다. Ruby on Rails 개발자들은 테스팅 및 다른 애자일 개발 기술을 자주 사용하여 유지보수 비용을 낮추도록 유지하지만, 대용량 데이터로 Rails 애플리케이션을 효율적으로 실행하는 데 관심을 덜 기울이는 것이 쉬울 것이다. Rails를 통해 데이터베이스에 쉽게 액세스할 수 있기는 하지만, 효율적으로 작동하지 않는 경우도 있다.

왜 Rails 애플리케이션이 느리게 실행할 수 있는가?

Rails 애플리케이션은 몇 가지 기초적인 이유로 인해 느리게 실행할 수 있다. 첫 번째는 간단하다. Rails는 개발의 속도를 높이기 위해 가정을 만든다. 대개 이러한 가정은 정확하고 유용하다. 이는 성능에 유익하지 않은 경우도 있지만, 이는 자원— 특히 데이터베이스 자원의 비효율적인 사용을 초래할 수 있다.

예를 들어, ActiveRecordSELECT *에 상응하는 SQL문을 사용하여 기본 값으로 쿼리에서 모든 필드를 선택한다. 다수의 열이 있는 상황에서 — 특히 일부가 큰 VARCHAR 또는 BLOB 필드인 경우 — 이 작동은 메모리 사용과 성능 측면에서 중대한 문제가 될 수 있다.

또 다른 중대한 도전과제는 이 기사에서 자세히 살펴 보는 N+1 문제이다. 필수적으로 이는 하나의 대형 쿼리가 아니라 많은 수의 작은 쿼리가 수행되는 결과를 초래한다. 예를 들어, ActiveRecord는 하나의 하위 레코드가 상위 레코드의 각 세트에 대해 요청되는 것을 인식하는 방법이 없다. 따라서, 각 상위 레코드에 대해 하나의 하위 레코드 쿼리를 제작할 것이다. 쿼리당 오버헤드로 인해 이 작동은 중대한 성능 문제를 야기할 수 있다.

또 다른 도전과제는 Ruby on Rails 개발자의 개발 습관과 태도와 더 긴밀하게 관련이 있다. ActiveRecord가 많은 태스크를 매우 쉽게 만들기 때문에 Rails 개발자들은 "SQL은 좋지 않다"는 태도를 자주 가질 수 있기 때문에, SQL이 더 합리적일 수 있는 상황에서도 이를 피하는 경우가 종종 있다. 다량의 ActiveRecord 오브젝트를 작성하고 조작하면 속도가 느릴 수 있기 때문에, 일부 경우에 이는 오브젝트를 인스턴스화하지 않는 SQL 쿼리를 직접 쓰는 것이 훨씬 더 빠를 수 있다.

Ruby on Rails가 개발 팀의 규모를 줄이는 데 자주 사용되고, Ruby on Rails 개발자가 프로덕션에서 애플리케이션을 배치하고 유지보수하기 위해 필요한 일부 시스템 관리 태스크를 자주 수행하기 때문에, 그 환경에 대한 제한적인 지식으로 인해 문제가 일어날 수 있다. 운영 체제 및 데이터베이스 설정이 올바르게 설정되지 않을 수 있다. 예를 들어, MySQL my.cnf 설정은, 비록 최적은 아니라도 Ruby on Rails 배치에 기본 값으로 자주 남아있다. 게다가 성능의 장기적 그림을 개발하기 위해 모니터링하고 벤치마킹하는 도구가 충분하지 않을 수 있다. 이는 물론 Ruby on Rails 개발자를 비난하는 것이 아니라, 일부 경우에는 비전문화의 결과이며, Rails 개발자는 두 영역 모두에서 전문적일 수 있다.

마지막 문제는 Ruby on Rails가 프로그래머에게 로컬 환경에서 개발하도록 장려하는 것이다. 이렇게 하여 수많은 이점이 있지만 — 예로는, 개발 지연이 줄어들고 분배가 늘어남 —, 이는 워크스테이션의 상대적으로 작은 규모로 인해 제한된 데이터세트로 작업할 수 있다는 것도 의미한다. 개발하는 방법과 코드를 배치할 장소 사이의 차이점이 큰 문제가 될 수 있다. 애플리케이션이 부하가 걸린 서버에서 상대적으로 큰 데이터 크기에 중대한 성능 문제가 있음을 발견하기 위해서만 로드되지 않은 성능이 우수한 로컬 서버에서 매우 오랜 기간 동안 작은 데이터 규모로 작업할 수 있다.

물론, Rails 애플리케이션이 성능에 문제가 있는 다양한 원인이 더 있을 수 있다. Rails 애플리케이션의 잠재적인 성능 문제가 무엇인지 발견하는 최상의 방법은 정확하고 반복 가능한 수치를 제공할 수 있는 진단 도구를 살펴보는 것이다.


성능 문제 발견하기

가장 우수한 도구는 Rails 개발 로그이며, 이는 log/development.log 파일의 각 개발 시스템에 있다. 사용 가능한 전체 측량은 요청으로 응답하는 데 걸리는 총 시간, 데이터베이스에서 걸리는 시간의 백분율, 보기를 생성하는 데 걸리는 시간의 백분율 등과 같이 다양하다. 도구는 development-log-analyzer와 같이 로그 파일을 분석하기 위해 사용 가능하다.

프로덕션 도중에 mysql_slow_log를 조사하여 가치 있는 정보를 찾을 수 있다. 전체 세부사항은 이 논의의 범위를 벗어나지만, 참고자료 섹션에서 자세한 내용을 찾아볼 수 있다.

가장 강력하고 유용한 도구 중 하나는 query_reviewer 플러그인(참고자료 참조)이다. 이 플러그인은 얼마나 많은 쿼리가 페이지에서 실행되는 것과 페이지가 이를 생성하는 데 얼마나 오래 걸리는 것을 보여 준다. 그리고 이는 잠재적인 문제를 위해 ActiveRecord가 생성하는 SQL 코드를 자동으로 분석한다. 예를 들어, 이는 MySQL 인덱스를 사용하지 않는 쿼리를 찾기 때문에, 중요한 열을 인덱스 처리하는 것을 잊어 버려서 성능 문제가 야기되는 경우, 이를 쉽게 찾을 수 있다(MySQL 인덱스에 대한 자세한 정보는 참고자료 참조). 플러그인은 팝업 <div>에서 이러한 모든 정보를 표시한다. 이는 개발 모드 도중에만 가시적이다.

마지막으로 성능 문제가 네트워크에서 발생하는지, 아니면 자산 로딩 문제에서부터 발생하는지를 발견하는 Firebug, yslow, Ping, 및 tracert과 같은 도구를 사용하는 것을 잊지 말자.

그 다음으로 일부 구체적인 Rails 성능 문제와 그 해결책에 대해 다루어보자.


N+1 쿼리 문제

N+1 쿼리 문제는 Rails 애플리케이션과 관련된 가장 중대한 문제 중 하나이다. 예를 들어, 목록 1에서 코드가 얼마나 많은 쿼리를 제작하는가? 이 코드는 가상의 포스트 테이블에서 모든 포스트를 통해 포스트의 카테고리와 본문을 표시하는 간단한 루프이다.


목록 1. 최적화되지 않은 Post.all 코드

<%@posts = Post.all(@posts).each do |p|%> 
	<h1><%=p.category.name%></h1>
	<p><%=p.body%></p>
<%end%>
 

답변: 코드는 @posts에서 행당 하나의 쿼리에 하나의 쿼리를 더하여 생성한다. 쿼리당 오버헤드로 인해 이는 심각한 도전과제가 될 수 있다. 그 원인은 p.category.name으로의 호출이다. 이 호출은 전체 @posts 배열이 아니라, 특정 포스트 오브젝트에만 적용한다. 다행히 선행 로딩(eager loading)을 사용하여 이를 수정할 수 있다.

선행 로딩은 Rails가 지정된 하위 오브젝트의 오브젝트를 로드하는 필수 쿼리를 자동으로 수행할 것임을 의미한다. Rails는 다수의 쿼리가 수행되는 JOIN SQL문 또는 전략을 사용한다. 하지만 사용할 모든 하위를 지정한다고 가정하면 이는 절대 N+1 상황을 야기하지 않을 것이다. 이는 루프를 반복할 때마다 추가 쿼리를 만든다. 목록 2는 사용자가 N+1 문제를 방지하기 위해 선행 로딩을 사용하는 목록 1에서 코드의 버전이다.


목록 2. 선행 로딩으로 최적화된 Post.all 코드
	
<%@posts = Post.find(:all, :include=>[:category]
	@posts.each do |p|%> 
	<h1><%=p.category.name%></h1>
	<p><%=p.body%></p>
<%end%>

이러한 코드는 포스트 테이블에 있는 행의 개수와 관계 없이 많아 봐야 두 개의 쿼리를 생성한다.

물론 이렇게 간단하지 않은 경우도 있다. 더 복잡한 N+1 쿼리 상황을 처리하는 작업이 더 많다. 해 볼 만하지 않은가? 빠른 테스팅을 한 번 해보자.


N+1 테스트하기

목록 3에서의 스크립트를 사용하여 쿼리가 얼마나 느린지 — 또는 얼마나 빠른지 —를 알 수 있다. 목록 3은 데이터베이스 연결을 설정하고 테이블을 정의하며 데이터를 로드하도록 독립형 스크립트에서 ActiveRecord를 사용하는 방법을 시연한다. 그러면 Ruby의 내장 벤치마크 라이브러리를 사용하여 어느 접근방식이 더 빠르고 어느 정도로 차이가 나는지를 확인한다.


목록 3. 선행 로딩 벤치마크 스크립트

require 'rubygems'
require 'faker'
require 'active_record'
require 'benchmark'

# This call creates a connection to our database.

ActiveRecord::Base.establish_connection(
	:adapter  => "mysql",
	:host     => "127.0.0.1",
	:username => "root", # Note that while this is the default setting for MySQL,
	:password => "",     # a properly secured system will have a different MySQL
                            # username and password, and if so, you'll need to
                            # change these settings.
	:database => "test")

# First, set up our database...	
class Category <  ActiveRecord::Base
end

unless Category.table_exists?
	ActiveRecord::Schema.define do
		create_table :categories do |t|
				t.column :name, :string
		end
	end	
end

Category.create(:name=>'Sara Campbell\'s Stuff')
Category.create(:name=>'Jake Moran\'s Possessions')
Category.create(:name=>'Josh\'s Items')
number_of_categories = Category.count

class Item <  ActiveRecord::Base	
	belongs_to :category	
end

# If the table doesn't exist, we'll create it.

unless Item.table_exists?
	ActiveRecord::Schema.define do
		create_table :items do |t|
				t.column :name, :string
				t.column :category_id, :integer	
		end
	end	
end

puts "Loading data..."

item_count = Item.count
item_table_size = 10000

if item_count < item_table_size
	(item_table_size - item_count).times do
		Item.create!(:name=>Faker.name, 
                 :category_id=>(1+rand(number_of_categories.to_i)))
	end
end

puts "Running tests..."

Benchmark.bm do |x| 
	[100,1000,10000].each do |size|	
		x.report "size:#{size}, with n+1 problem" do 
			@items=Item.find(:all, :limit=>size)
			@items.each do |i| 
				i.category
			end	
		end	
		x.report "size:#{size}, with :include" do 
			@items=Item.find(:all, :include=>:category, :limit=>size)
			@items.each do |i| 
				i.category
			end	
		end	
	end	
end

이 스크립트는 :include 절을 사용하여 선행 로딩이 있을 때와 없을 때에 100, 1,000, 10,000의 오브젝트에 대해 루핑 속도를 테스트한다. 이 스크립트를 실행하기 위해 스크립트 맨 위 주변의 적절한 데이터베이스 연결 매개변수를 로컬 환경에 적절한 매개변수로 대체해야 할 수 있다. 테스트라는 MySQL 데이터베이스도 작성해야 할 것이다. 마지막으로, ActiveRecordfaker gem이 필요하며, 이는 gem install activerecord faker를 실행하여 얻을 수 있다.

시스템에서 그 스크립트를 실행하면 목록 4와 같은 결과가 제작된다.


목록 4. 선행 로딩 벤치마크 스크립트 결과물

-- create_table(:categories)
	 -> 0.1327s
-- create_table(:items)
	 -> 0.1215s
Loading data...
Running tests...
			user     system      total        real
size:100, with n+1 problem  0.030000   0.000000   0.030000 (  0.045996)
size:100, with :include  0.010000   0.000000   0.010000 (  0.009164)
size:1000, with n+1 problem  0.260000   0.040000   0.300000 (  0.346721)
size:1000, with :include  0.060000   0.010000   0.070000 (  0.076739)
size:10000, with n+1 problem  3.110000   0.380000   3.490000 (  3.935518)
size:10000, with :include  0.470000   0.080000   0.550000 (  0.573861)

모든 경우에 :include를 사용하는 테스트가 더 빠르다 — 구체적으로 각각 5.02배, 4.52배, 6.86배 더 빨라진다. 물론 정확한 결과는 특정 상황에 따라 다르지만, 선행 로딩은 명백하게 엄청난 성능 증가를 야기할 수 있다.


중첩된(nested) 선행 로딩

중첩된 관계 — 관계의 관계를 참조하려면 어떻게 하는가? 목록 5에서는 이러한 일이 발생한 일반적인 상황을 시연한다. 모든 포스트를 통해 루프하고 작성자 이미지를 표시하는데, 여기에서 AuthorImagebelongs_to 관계가 있다.


목록 5. 중첩된 선행 로딩 유스 케이스

@posts = Post.all	
@posts.each do |p|  
	<h1><%=p.category.name%></h1>
	<%=image_tag p.author.image.public_filename %> 
	<p><%=p.body%> 
 <%end%> 
 

이 코드는 이전과 동일한 N+1 문제를 겪지만, 관계의 관계를 사용하고 있기 때문에 수정사항에 대한 구문이 즉시 명백하지는 않다. 그러면 중첩된 관계를 어떻게 선행 로드하는가?

올바른 답변은 :include 절에 대한 해시 구문을 사용하는 것이다. 목록 6은 해시를 사용하는 중첩된 선행 로드의 예제를 제공한다.


목록 6. 중첩된 선행 로딩 솔루션
	
@posts = Post.find(:all, :include=>{ :category=>[],
                                       :author=>{ :image=>[]}} )
@posts.each do |p|  
	<h1><%=p.category.name%></h1>
	<%=image_tag p.author.image.public_filename %> 
	<p><%=p.body%> 
 <%end%> 

확인 가능한 대로 해시와 배열 리터럴을 중첩할 수 있다. 이 케이스에서 해시와 배열 사이의 유일한 차이점은 해시가 중첩된 하위 항목을 가질 수 있고 배열은 그럴 수 없다는 점을 주목하자. 그렇지 않은 경우, 이들은 서로 동등하다.


간접적인 선행 로딩

N+1 문제의 모든 인스턴스가 바로 인식되는 것은 아니다. 예를 들어, 목록 7은 얼마나 많은 쿼리를 제작하는가?


목록 7. 간접적인 선행 로딩 예제 유스 케이스

  <%@user = User.find(5)
    @user.posts.each do |p|%>   
      <%=render :partial=>'posts/summary',  :locals=>:post=>p
     %> <%end%>
 

물론 쿼리의 수를 결정하는 것은 부분적인 posts/summary 지식이 필요하다. 목록 8에서 그 부분을 볼 수 있다.


목록 8. 간접적인 선행 로딩 부분: posts/_summary.html.erb

  <h1><%=post.user.name%></h1>

불행히도 답변은 목록 7목록 8이 사용자의 이름을 검색하는 post에서 행당 하나의 추가 쿼리를 생성하는 것이다. — 내장 메모리인 User 오브젝트로부터 ActiveRecord가 자동으로 post 오브젝트를 생성하기도 한다. 즉, Rails는 현재까지 그 상위와 하위 레코드를 연관시키지 않는다.

수정사항은 자체 참조적인 선행 로딩을 사용하는 것이다. 필수적으로 Rails는 상위 레코드로 생성된 하위 레코드를 다시 로드하기 때문에, 전체적으로 별도의 관계인 것처럼 상위 레코드를 선행 로드해야 한다. 이는 목록 9의 코드와 모양이 같다.


목록 9. 간접적인 선행 로딩 솔루션

  <%@user = User.find(5, :include=>{:posts=>[:user]})
  ...snip...

비록 납득하기 어렵지만 이 기술은 위의 기술과 매우 비슷하게 작동한다. 불행히도, 특히 복잡한 계층 구조가 있는 경우, 이 기술을 사용하여 과도하게 중첩하는 것이 간편하다. 목록 9와 같이 간단한 유스 케이스는 문제가 없지만 복잡한 중첩은 문제를 야기할 수 있다. 일부 케이스에서 Ruby 오브젝트를 과도하게 로드하면 실제로 N+1 문제를 다루는 것보다 느려질 수 있다 — 특히 모든 오브젝트가 전체 트리에 걸쳐서 나타나지 않는 경우. 그러한 경우에 N+1 문제로의 다른 해결책이 더 적절할 수 있다.

이를 수행하는 하나의 방법은 캐싱 기술을 사용하는 것이다. Rails V2.1에는 내장된 간단한 캐시 액세스가 있다. Rails.cache.read, Rails.cache.write 및 관련 메소드를 사용하면 자체적인 간단한 캐싱 메커니즘을 쉽게 작성할 수 있고, 백엔드는 간단한 메모리 백엔드, 파일 기반 백엔드 또는 memcached 서버가 될 수 있다. 참고자료 섹션에서 Rails의 내장 캐싱 지원에 대해 더 자세한 정보를 찾을 수 있다. 자체적인 캐싱 솔루션을 작성하지 않아도 되지만, Nick Kallen의 cache money 플러그인과 같이 사전 내장 Rails 플러그인을 사용할 수 있었다. 이 플러그인은 쓰기를 통한 캐싱을 제공하며, 이는 Twitter에서 사용하는 코드를 기반으로 한다. 자세한 정보는 참고자료를 확인하기 바란다.

물론 모든 Rails 문제가 쿼리의 수와 연관되는 것은 아니다.


Rails 그룹화 및 집계 계산

데이터베이스에서 수행되어야 하는 작업을 Ruby에서 수행하는 것과 관련하여 하나의 문제점에 직면할 수 있다. 이는 Ruby가 얼마나 강력한지에 대한 증거이다. 사람들이 중요한 동기 없이 데이터베이스 코드의 일부를 C에서 자발적으로 다시 구현하는 것은 상상하기 어렵지만, Rails에서 ActiveRecord 오브젝트의 그룹에서 유사한 계산을 수행하는 것은 간편하다. 불행히도, Ruby는 언제나 데이터베이스 코드보다 속도가 느리다. 목록 10과 같이 순수 Ruby 접근방식을 사용하여 계산을 수행하지 마라.


목록 10. 그룹화 계산을 수행하는 잘못된 방법

  all_ages = Person.find(:all).group_by(&:age).keys.uniq
  oldest_age = Person.find(:all).max

그 대신에, Rails는 그룹화의 시리즈와 집계 함수를 제공한다. 목록 11과 같이 이를 사용하자.


목록 11. 그룹화 계산을 수행하는 올바른 방법

  all_ages = Person.find(:all, :group=>[:age])  
  oldest_age = Person.calcuate(:max, :age)

SQL을 혼합하기 위해 사용할 수 있는 ActiveRecord::Base#find로의 옵션이 많다. Rails 문서에서 더 자세히 찾아볼 수도 있다. calculate 메소드는 :min, :sum:avg와 같이 데이터베이스를 지원하는 유효한 집계 함수와 같이 작동하는 것을 참고하자. 추가적으로, calculate:conditions와 같이 많은 인수를 취할 수 있다. 세부사항은 Rails 문서를 확인하자.

하지만 SQL에서 할 수 있는 것 중 일부는 Rails에서 할 수 없다. 내장 항목이 충분하지 않으면 사용자 정의 SQL을 사용하자.


Rails를 통한 사용자 정의 SQL

사람과 그들의 직업, 연령 및 작년 한 해에 이들이 관련된 사고의 수의 목록이 있는 테이블이 있다고 가정하자. 그 정보를 검색하기 위해 목록 12와 같이 사용자 정의 SQL문을 사용할 수 있었다.


목록 12. ActiveRecord 예제를 통한 사용자 정의 SQL

sql = "SELECT profession,
              AVG(age) as average_age,  
              AVG(accident_count) 
         FROM persons 
        GROUP 
           BY profession"

Person.find_by_sql(sql).each do |row|   
  puts "#{row.profession}, " <<
       "avg. age: #{row.average_age}, " <<
       "avg. accidents: #{row.average_accident_count}"
end

이 스크립트는 목록 13과 같은 결과를 제작할 것이다.


목록 13. ActiveRecord 결과물을 통한 사용자 정의 SQL

  Programmer, avg. age: 18.010, avg. accidents: 9
  System Administrator, avg. age: 22.720, avg. accidents: 8

물론 이는 간단한 케이스이다. 이 예제를 복잡한 SQL문으로 어떻게 확장할 수 있는지 예상해 볼 수는 있다. 또한 목록 14와 같이 ActiveRecord::Base.connection.execute 메소드를 사용하는 ALTER TABLE 문과 같이 다른 유형의 SQL문도 실행할 수 있다.


목록 14. ActiveRecord를 통한 사용자 정의 비 파인더(non-finder) SQL

  ActiveRecord::Base.connection.execute "ALTER TABLE some_table CHANGE COLUMN..."

열 추가 및 제거와 같이 대부분의 스키마 조작은 Rails의 내장 메소드를 사용하여 수행할 수 있다. 하지만 임의의 SQL 코드를 실행하는 기능은 필요한 경우 사용 가능하다.


결론

모든 프레임워크와 마찬가지로, Ruby on Rails는 올바르게 처리하지 않고 주의를 기울이지 않으면 어느 정도 성능 문제를 겪을 수도 있다. 다행히 이러한 도전과제를 모니터링하고 정정하기 위한 올바른 기술은 상대적으로 간단하며 배우기 쉽다. 그리고 더 복잡한 문제도 인내심을 가지고 성능 문제가 어디에서 비롯되었는지를 알면 해결할 수 있다.


참고자료

교육

제품 및 기술 얻기

토론

필자소개

David Berube

David Berube is a consultant, speaker, and the author of Practical Rails Plugins, Practical Reporting with Ruby and Rails, and Practical Ruby Gems. You can reach him at info@berubeconsulting.com.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=오픈 소스
ArticleID=605625
ArticleTitle=Ruby on Rails 속도 높이기
publish-date=07272010
author1-email=info@berubeconsulting.com
author1-email-cc=

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.