Ruby on Rails : has_and_belongs_to_many Association 의 마법
Ruby on rails 에서는 모델간의 관계를 정의하기 위해 has_many , belongs_to 등의 관계선언을 하게 된다.
이 본문에서 쓰고자 하는건 다대다 관계를 나타내는 :has_and_belongs_to_many 에 관해서다. 이 선언을 통해 다대다(N:N) 관계에서 Rails 가 제공하는 놀라운 마법을 경험하게 될것이다.
menus 와 posts 라는 두 테이블이 있다고 가정하자. 한개의 포스트(post)는 여러 메뉴에 속할 수 있고, 메뉴(menu)는 여러 post 들과 관련되어 있다면, 이 둘의 다대다 관계에 놓이게 된다. 이 두 테이블의 관계를 맺어주기 위해선 이들 사이에 join table 이 필요한데, Rails 의 네이밍 관습을 따르기 위해서 menus_posts 라는 이름으로 join table 을 생성해야 한다.
이제 각 테이블을 나타내는 모델 클래스에서 관계를 맺어주자.
class Menu < ActiveRecord::Base
has_and_belongs_to_many :posts
end
class Post < ActiveRecord::Base
has_and_belongs_to_many :menus
end
class MenusPost < ActiveRecord::Base
# 선언없음.
end
MenusPost 모델에는 아무 설정도 하지 않았으며 또 이 모델 class 를 별도로 생성할 필요도 없는데, 이는 join table 이기에 Menu 와 Post 모델에서의 선언만으로 이 세 테이블의 관계를 Rails 는 이미 인식하고 있기 때문이다.
자, 이제 Rails 의 마법을 경험해보자.
Post 를 등록할 때, 여러 menu 들을 선택할 수 있도록 하기 위해선 일반적으로 입력폼에 각 메뉴들의 값을 checkbox 로 뿌려주어 사용자가 다중 선택할 수 있도록 제공될 것이다. 다음과 같이 말이다.
<input type="checkbox" name="post[menu_id][]" value="7" />
<!-- name 속성값에 "post[menu_id]" 뒤에 반드시 "[]" 를 붙여주어야 폼섭밋시 check 된 값들이 배열문자로 전달된다는 점에 유의하자. -->
만약 위와 같이 checkbox 를 제공하였다면, 생성(Create)시 menu_id 라는 속성이 존재하지 않는다는 에러메세지를 보게 될것이다. 이는 분명 당연한 결과이다. post 테이블에는 menu_id 라는 필드가 존재하지 않기 때문이다.
하지만 menu_id 에 s 를 붙여 다음과 같이 checkbox 를 제공하면,
<input type='checkbox' name='post[menu_ids][]' value='7' />
Create 가 성공적으로 수행되게 되는데, 여기에 우리가 모르는 Rails 의 마법이 발휘되었기 때문이다.
menu_id 라는 필드는 존재하지 않기에 에러를 발생시켰는데, menu_ids 라는 필드 역시도 존재하지 않음에도 Rails 는 (다대다 관계에 의해) post 가 여러 개의 menu_id 를 가질 수 있음을 알고 있었기에 에러를 발생시키지 않았다.
더욱 놀라운 점은, menus_posts Join table 의 내용을 들여다 보면 자동으로 입력된 값을 보게 된다는 것이다.
sql> select * from menus_posts;
+-----------------------+
+ menu_id | post_id +
+-----------------------+
+ 7 | 1 +
+-----------------------+
+ 9 | 1 +
+-----------------------+
위 쿼리의 결과에서 post_id 가 1 인 것은 방금 생성한 post 를 가리키고, menu_id 값 7, 8 은 post 생성시 체크한 menu 의 값들이다.
join talbe 에 값을 입력하는 코드를 넣지 않았음에도 Rails 가 모델의 관계를 인지하여 자동으로 값을 넣어준 것이다!
자, 그럼 수정(update)시에는 또 어떤 마법을 부리는지 살펴보자.
수정화면에서 생성시 선택했던 menu checkbox 들을 해제하고 다른 값들로 체크한 후 update 를 하면, Rails 는 자동으로 두가지 일을 수행한다.
첫째, join table 에서 기존에 입력된 값(post_id 가 1인 레코드)들을 모두 지운다.
둘쨰, 새로 선택한 값을 입력한다.
수행후 결과는 다음과 같다.
sql> select * from menus_posts;
+-----------------------+
+ menu_id | post_id +
+-----------------------+
+ 2 | 1 +
+-----------------------+
+ 4 | 1 +
+-----------------------+
어떤가? 놀랍지 않은가?
라벨: has_and_belongs_to_many, Model, Ruby on Rails