|
* 세미나 발표자료: AssociationProxyTutorial.pdf
투명한 '뿌락찌' 액티브레코드 연관 클래스는 ActiveRecord::Associations::AssociationProxy 클래스의 확장이다. 이프록시 클래스는 일종의 "투명 프록시(transparent proxy)"로서 개발자들 몰래 숨어서 맡은 역할을 수행한다.그렇지만, 이 프록시의 존재여부를 아는 것과 모르는 것은 차이가 있다. 예를 들어, 우리가 만약 User 모델에서has_many :articles를 선언하게 되면, 이제 우리는 user.articles 와 같이 액세스할 수 있는데, 이 때반환되는 객체가 실제로는 프록시 객체다. 아래 그림은 이 프록시 클래스의 계층구조다. (요즘도 "뿌락찌"라는 말을 아는 사람들이있을까?) ![]() 액티브 레코드의 소스코드를 보면 has_many, has_one, belongs_to, has_and_belongs_to_many와 같은 연관 메서드들이 정의되어 있는 것을 볼 수 있다. 그런데 이들 메서드의 내용을 유심히 보면, 메서드들마다 조금씩다르기는 하지만, 대체로 다음과 같은 로직이 들어 있다. 예를 들어, has_many의 경우를 보자. def has_many(association_id, options = {}, &extension)설명을 덧붙이자면, 우선 주어진 association_id와 옵션정보를 사용하여 리플렉션(Reflection)을 생성한다.리플렉션은 액티브레코드의 클래스나 객체에 대한 메타데이터 정보들을 담는 객체라고 보면된다. 연관 모델인 경우AssociationReflection 객체가 생성될 것이다. 이어서 생성된 리플렉션 객체를 가지고HasManyAssociation 프록시 객체를 생성하고, 리플렉션 정보를 이용하여 각각의 연관관계에서 필요한 헬퍼 메서드들을생성하게 된다. 예를 들어, User 모델에서 has_many :articles 라고 선언 하였다면, 이 메서드에 의해 User모델에 articles(), articles=() 등의 메서드가 만들어지고, 이들 메서드가 호출될 때에 실제로는 아래 그림처럼연관 프록시 객체를 대상으로 작업이 일어나게 되는 것이다. ![]() 연관 프록시를 사용하는 이점은 무엇인가? 그렇다면 이런 연관 프록시를 왜 사용할까? 연관 프록시를 사용하는 이점은 무엇일까? 연관 프록시를 사용하는 이점은 결국 연관(association)의 사용 사례를 살펴보면 알 수 있다. 연관을 사용하는 경우는크게 2가지인데, 하나는 액티브레코드 모델의 액세스 범위를 한정하는 것이고, 나머지 하나는 Custom Query를 생성하기위한 것이다. 예를 하나 들어보자. 어떤 사용자의 articles를 모두 찾는 기능을 만든다고 하자. Article 객체만을 사용하여 이 작업을 수행할 수도 있다. Article.find_all_by_user_id(user) 그렇지만 이 방법보다는 연관을 사용하여 다음과 같이 표현하는 것이 훨씬 간단하고 표현 또한 직관적이다. user.articles 더 편리한 것은 이 상태에서 뭔가 추가로 작업을 더 해 줘야 할 경우다. 후자의 경우 user.articles.find(...)와 같은 식으로 표현하여 특정 사용자로 범위가 한정된 모델객체에 대하여 일반적인 CRUD 조작을 할 수가 있다. 전자의 경우라면이들 경우마다 일일이 별개의 메서드를 만들어야 했을 것이다. 이것 외에 또 어떤 이점들이 있을까?
연관을 사용하는 또 다른 경우는 커스텀 질의문을 확장하는 경우다. 예를 들어, 최근의 article들을 찾는다고 하자. Article 모델에 다음과 같이 클래스 메서드를 정의할 수도 있을 것이다. class Article < ActiveRecord::Base그러나, 이 방법을 사용하면 매번 필요할 때 마다 이런 식으로 클래스 메서드들을 만들어 줘야 한다는 단점이 있다. 이제 액티브레코드 연관을 사용해 보자. 위와 똑같은 효과를 내기 위해서 아래와 같이 하면 된다. class User < ActiveRecord::Base 결과는 동일하다. 즉 위와 같이 하여 user.articles.recent를 호출한 것이나, 아래와 같이 하여user.recent_articles를 호출할 것이나 SQL 쿼리문장은 동일하게 "SELECT * FROM articlesWHERE (articles.user_id = 1 AND created_at > ...) " 과 같은 쿼리를 호출할것이다. 그렇다면, 차이점은 무엇일까? 장단점이 있다. 우선, 위의 경우는 연관관계가 아닌 경우에서도 사용할 수 있다는 장점이 있다. 즉,Article.recent 도 가능하며, 사실 이게 더 정상적인 사용 패턴일 것이다 (오히려 user.articles.recent가가능한 것은 연관프록시가 가지고 있지 않은 메서드에 대한 호출을 받은 경우 method_missing에서 실제 모델의 클래스메서드로 delegate하기 때문에 생기는 side-effect일 뿐이다.) 반면 아래의 경우는 반드시 연관관계 속에서만 사용이가능하다. 왜냐면 이 객체는 실제로연관 프록시 객체이며, 연관 프록시 객체는 연관관계 속에서만 생성되기 때문이다. 그렇지만 후자의 방법을 쓸 경우의 장점도 만만치않다. 우선 Scoped Access의 잇점을 누릴 수 있다. 즉, user.recent_articles.find(...) 와같은 식의 접근이 가능한 것이다. 또한 이 경우에도 마찬가지로 캐싱(caching)이 된다. 연관 프록시의 확장 연관 프록시는 확장이 가능하다. anonymous module을 사용할 수도 있고, 명시적으로 모듈을 만들어 extend하는 방법도 가능하다. 다음과 같은 식으로 연관을 확장하여 새로운 메서드를 추가하더라도 동일한 효과를 거둘 수 있다. class User < ActiveRecord::Base 실제로 위와 같이 한 다음 user.articles.recent를 호출하면 SELECT * FROM articles WHERE(articles.user_id = 1 AND (created_at > '2007-08-27 11:51:26')) 가호출된다. 결론적으로 보면, 아래 표와 같이 약 3가지의 사용 패턴이 나온다. 장단점을 간단히 비교해 보자.
연관 모델과 관련된 3rd Party 라이브러리들 꼭 연관과 관련된 것은 아니지만, 연관과 유사한 작용을 해 주는 라이브러리들이 많이 있다. 그 중 몇 가지만 소개해 본 Scope_out 플러그인 * http://code.google.com/p/scope-out-rails/ 이 플러그인은 제목 그대로 액세스 범위를 한정해 주는 플러그인이다. 내부적으로는 with_scope 문을 사용한다. 이 플러그인은 기본적으로 다음 3가지 메서드를 지원한다.
class Article < ActiveRecord::Base 따라서 위와 같이 scope_out이 정의되어 있을 경우 다음과 같이 사용할 수 있다. Article.find_recent(:all) user.articles.find_recent(:all) user.articles.calcuate_published(:count, :all) user.articles.with_published do user.articles end Scope_Proxy 플러그인 * http://www.jackchristensen.com/article/4/scopedproxy-plugin-for-ruby-on-rails 이 플러그인은 범위를 한정하는 것을 중복해서 가져갈 수 있게 해 주는 플러그인이다. 즉, Article.recent.published.find :all 과 같은 식의 액세스가 가능하다고 한다. 직접 사용해 보지는 않았다. HasFinder 젬 * http://www.pivotalblabs.com/articles/2007/09/02/hasfinder-its-now-easier-than-ever-to-create-complex-re-usable-sql-queries 이젬은 구현패턴#1과 연관된 젬이다. 다음과 같은 식으로 정의를 하면, Article.recent.popular와 같은 식으로개별 쿼리 메서드를 복합하여 사용할 수 있다는 점이 특색이다. user.articles.recent.popular와 같은 식으로연관관계 속에서도 사용할 수 있으며, user.articles.recent.popular.find(...)와 같은 식의 쿼리확장도 가능하다. 다만, 아직 안정화되지 않아서 생성되는 쿼리가 최적화되지 않았다. class Article < ActiveRecord::Base 기타 연관과 관련된 트릭과 기법들 연관 메서드 중 loaded?라는 것이 있다. 이 메서드를 사용하여 연관이 eager loading되었는지 여부를 판단, 조건부로 처리할 수 있다. * http://www.dcmanges.com/blog/24 class Team Conditional Association Loading Trick 연관 프록시의 loaded? 메서드를 오버로드하면 특정 조건에 부합하는 경우에만 재로드 하는 식의 조건부 로딩도 가능하다. * http://www.jroller.com/obie/entry/conditional_association_loading_trick has_many :neighborhoods do 참고자료 ※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.
|
by thinkr 카테고리
최근 등록된 덧글
음 제 경우 지금 메모리..
by 서울비 at 10/25 새 버전이신가 보네요,.. by 연안부두 at 10/22 CPU 사용량이 엄청나군.. by 오스카 at 10/22 드롭박스 깔고 팬이 자주.. by 백일몽 at 10/22 다른 개발자분이 작업하.. by GreatDG at 10/22 최소 3대는 해봐야겠더.. by 펭도 at 10/21 browsershots 같은 .. by thinkr at 10/21 예전에 그런 서비스를 .. by 몰아저씨 at 10/21 예. 물론 그 방법도 있.. by thinkr at 10/21 vmware나 virtual box .. by 이상훈 at 10/21 최근 등록된 트랙백
라지엘의 느낌
by laziel's me2DAY 우리는 언제나 창작과 .. by 시답잖은 지식과 개똥철학 월아, 알고리즘 by Read & Lead 우엉의 생각 by oldtype's me2DAY 졸음을 깨우기 위해 재미.. by jack in the box 이젠 업그레이드인가.. ra.. by Always Renewal 제로안의 생각 by zeroan's me2DAY 아샬의 생각 by ahastudio's me2DAY 구글사이트 접속차단 by P-camp & 대안언어축제.. 펭도의 생각 by pengdo's me2DAY 메모장
| |||||||||||||||||||||||