Bucket Place/Ruby on Rails

[Ruby on Rails] Rails Database Table Relationships

Cloud Travel 2014. 5. 13. 19:33


  들어가면서 


 Rails를 하면서 신기하면서도 고생했던 부분은 데이터베이스에서 테이블간의 관계를 나타내는 부분이었다. 이 글에서는 간단하게 Rails에서 모델을 통해서 데이터 테이블들을 관계 맺는 방법(belongs_to, foreign_key, throught, ... 등)을 알아보고, 좀더 나아가 팔로잉 기능을 위한 데이터 모델을 셋팅하는 방법에 대해서 알아보겠다.


 여기서 모델에 집중을 하기 위해서 dependent와 validation에 관련된 내용은 언급하지 않겠다.




  의사와 환자의 관계 


 팔로잉에 대해서 설명하기 전에 의사와 환자의 관계에 대해서 간단하게 보면서 모델에서 테이블 관계를 표현하는 법에 대해서 보도록 하겠다. 여기서 다루게 되는 요구사항은 다음과 같다.

 

 병원에는 의사들과 환자들이 존재한다. 이 병원은 예약제를 통해서 진료를 한다. 환자는 예약을 하면서 언제 진찰을 받을지에 대해서 알려줘야 한다. 환자는 여러 의사선생님에게 예약을 할 수 있으며 또한 지정된 의사의 정보를 확인할 수 있어야 한다. 의사는 예약목록을 통해서 자신에게 진료받을 환자들의 정보를 봐야한다.

 

 본래에는 의사의 정보와 환자의 정보에는 여러가지 복잡한 내용이 들어가지만 여기서는 간단하게 이름만을 가지고 있다고 가정하고 일을 진행한다.


 의사와 환자는 예약을 통해서 관계를 갖고 있고, 이 관계를 통해서 의사는 환자의 정보를 얻고 환자는 예약을 할 것이다. 요구 사항을 정리하면 아래와 같이 될 것이다.

 - Table : 의사, 환자

 - Relation : 예약(의사와 환자사이)

 

  여기에 이제 추가적으로 테이블로 위의 관계를 요구사항에 빚대어 표현하면 아래와 같이 변화될 것이다.

 

각 테이블의 id값은 Rails에서 자동으로 추가되는 내용이기 때문에 같이 표시했다. (created_at, updated_at은 생략했다.)


 우리가 여기서 이제 해줘야 할일은 다음과 같다.

 - appointment 모델에서 FK 관계를 설정해줘야 한다.

 - physician 모델에서 의사가 다수의 진찰 예약을 가지고 있게 설정해줘야 한다.

 - physician 모델에서 의사가 진찰 예약을 한 patient 정보를 가지고 올 수 있게 설정해줘야 한다.

 - patient 모델에서 환자가 다수의 진찰 예약을 가지고 있게 설정해줘야 한다.

 - patient 모델에서 환자가 진찰 예약으로 배정된 physician의 정보를 자지고 올 수 있게 설정해줘야 한다.

 위에서 해줘야 할일에 대해서 순차적으로 진행을 해보도록 하자.


 1. appointment 모델에서 FK 관계를 설정해줘야 한다.

  - FK 관계를 작성할 때에는  belongs_to 키워드를 사용해야 한다.

  -  belongs_to :table_name 을 작성하면 자신의 테이블 Attribute에서 table_name_id를 찾아서 자동으로 연결한다.

  - 예를 들어, belongs_to :physician을 할 경우에는 physician 테이블의 id와 appointment 테이블의 physician_id가

    FK로서 자동으로 연결이 되게 된다. 이를 이용하여 FK관계를 설정하면 다음과 같다.


    app/model/appointment.rb

class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient
end

 


  - 만약, appointment 테이블에서 physician_id의 이름이 doctor_id라고 정의되서 Rails가 자동으로 연결을 못할

   경우  class_name파라미터 를 이용하여 어떤 테이블과 연결이 된건지 명시해주면된다. 여기서 주의할 점은 테이블 명이

   아닌 Rails에서 자동으로 생성한 클래스 이름을 이용하기 때문에 맨 앞을 대문자로 해줘야 한다는 점이다.

    >> 예시 : belongs_to: doctor, class_name: "Physician"

  


 2. physician 모델에서 의사가 다수의 진찰 예약을 가지고 있게 설정해줘야 한다.

  - 다수를 갖고 있는 관계를 나타낼 때에는  has_many 키워드를 사용해야 한다.

  -  has_many:table_name의 복수형 을 작성하면 다수를 갖고 있다는 관계를 표현할 수 있다.

    

     app/model/physician.rb

class Physician < ActiveRecord::Base
  has_many :appointments
end

   - 위의 표현으로 의사는 여러개의 진료예약을 갖을 수 있다는 것을 나타내게 된다.


 3. physician 모델에서 의사가 진찰 예약을 한 patient 정보를 가지고 올 수 있게 설정해줘야 한다.

  - 의사는 자신과 예약관계가 있는 환자의 정보를 가져오고 싶어 한다.

  - 즉, 진찰 예약을 통해서 환자의 정보에 접근을 해야 한다는 것이다.

  - "~을 통하여"라는 뜻을 가진  through 가 키워드로 동작을 하게 된다.

 

     app/model/physician.rb

class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, through: :appointments
end

   - 위의 표현 through를 통해서 의사는 자신에게 진찰 예약이 되있는 환자의 정보를 가져올 수 있게 된다.

   - 만약, 하나의 클래스로 두 개이상이 연결될 경우(follow 기능시 발생) FK가 무엇인지 명백히 해줘야 한다.

   >> 예시 : has_many :appointments, foreign_key :"patient_id"


 4. 5. 단계는 2와 3에서 했던 것을 patient 모델 파일에서 해주면 된다.




  팔로잉 기능 개발


 팔로잉 기능은 여러분이 너무 잘 알고 있을 것이라고 생각하기 때문에 별도의 요구사항은 적지 않겠다. 또한, 팔로잉 기능은 모델에서도 관계 표현을 제외하고도 해줘야 할 일이 많다. 이글은 모델의 관계표현에 중점을 두고 있다고 한번더 밝힌다. 팔로잉 기능에 대한 전체적은 이해는 맨아래의 참조 링크를 따라가주기 바란다.


 그럼 바로 요구사항을 간략하게 정리했을 때의 모습을 보도록 하자.

 


 테이블의 모습을 보면 아래와 같이 될 것이다.

 사용자(user)에 대한 기타 상세한 사항(비밀번호, 이메일주소,... 등)은 생략하였다.

 


 following기능을 완성하기 위해서 우리가 해줘야 할 일은 다음과 같다.

  - relationship 모델에서 FK설정하기

  - user 모델에서 여러명의 follower를 가지고 있다는 것을 표현하기

  - user 모델에서 follower된 관계를 통해서 자신이 follow한 사람의 정보를 가져올 수 있다는 것을 표현

  - user 모델에서 여러명의 followed를 가지고 있다는 것을 표현하기

  - user 모델에서 followed된 관계를 통해서 자신을 follow한 사람의 정보를 가져올 수 있다는 것을 표현

 위 의사와 환자를 표현한 관계에서 했던 일과 같이 할 일을 순차적으로 실행해보자.


 1. relationship모델에서 FK설정하기

   -  belongs_to 를 이용하여 FK를 설정해보자.

   - relationship모델을 보면, 각각의 특성의 이름이 follower, followed로 user와 다른 이름을 가지고 있다.

   - 이때 사용해야 하는 것이 바로  class_name파라미터 이다.

     

      app/model/relationship.rb

class Relationship < ActiveRecord::Base
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end

 2. user 모델에서 여러명의 follower를 가지고 있다는 것을 표현하기

   - 기본적인 표현방법은 위에서 밝혔듯이  has_many 를 이용하면 된다.

   - 여기서 주의해야 할점이 많이 발생한다. 일단 relationship 테이블에는 user_id 라는 특성이 없다. 따라서  foreign_key

     파라미터를 이용하여 어떤 키에 연결되는지 명시해줘야 한다.


      app/model/user.rb

has_many :relationships, foreign_key: "follower_id"

   - 위 표현을 통해서 사용자는 여러명의 follower를 가질 수 있게 된다. 이 이야기는 followed된 사람이 여러명 발생하게 되고

     사용자는 자신이 follow한 사람의 목록을 보고 싶을 것이다. 이는 아래의 단계이다.


 3. user 모델에서 follower된 관계를 통해서 자신이 follow한 사람의 정보를 가져올 수 있다는 것을 표현

   - 사용자가 여러명을 follow했다는 이야기는 이 관계를 통해서 다른 사용자들은 follow 당했다는 것이다.

   - 이를 표현하게 되면 has_many :followeds, through: :relationships이 되겠지만, followeds라는 표현이 어색하여

     followeds를 followed_users로 바꾸고 이것의 본채가 누군지  source 파라미터 를 이용하여 표현해준다.


      app/model/user.rb

has_many :relationships, foreign_key: "follower_id"
has_many :followed_users, through: :relationships, source: :followed

   - 위 표현을 보면 자신이 follow한 것에 의한 결과로 follow당한 사람들의 목록을 가져 오는 것이 가능하게 된다.


4. user 모델에서 여러명의 followed를 가지고 있다는 것을 표현하기

   - 이 것은 2번에서 하는 것과 동일하지만 같은 이름의 관계를 서로 다른 용도로 사용할 수 없기 때문에 이름을 새로 지어 준다.

   - 관계의 이름을 임의적으로 바꾸었기 때문에  class_name 을 이용하여 어떤 테이블클래스를 사용했는지 명시해야 한다.


      app/model/user.rb

has_many :reverse_relationships, foreign_key: "followed_id",
                                   class_name:  "Relationship"

   - 2에서 했던 것처럼 어떤 FK와 연관되는지도 명시해주는 것을 잊지말자.


 5. user 모델에서 followed된 관계를 통해서 자신을 follow한 사람의 정보를 가져올 수 있다는 것을 표현

   - 이 것은 3번의 과정과 완벽히 동일하기 때문에 소스코드만 밝힌다.

 

      app/model/user.rb

has_many :reverse_relationships, foreign_key: "followed_id",
                                   class_name:  "Relationship",
has_many :followers, through: :reverse_relationships, source: :follower

  - 위 표현을 통해서 자신이 누군가에게 follow당했을 경우 누가 follow를 해준 것인지에 대한 정보를 얻을 수 있게 된다.




   마치며


 필자도 아직 follow기능에 대해서는 2번 밖에 제작해보지 않았기 때문에 아직 미숙한점이 있을 수 있지만, 데이터베이스 테이블 모델에서 관계를 표현하는 것에 대해서 거의 90% 이상은 다뤘다고 생각한다.


 belongs_to, has_many ~ class_name ~ source ~ foreign_key등의 자주쓰이는 키워드에 대해서도 모두 정리했다. 요즘 소셜엡에서 왠만하면 모두 사용하고 있는 following기능에 대해서도 구현하는 법에 대해서 정리하였다. 만약, 위 글을 읽고선 아직 following 기능 구현에 대해서 감이 오지 않는다면 follow를 하는 사람과 당하는 사람의 관계를 잘 생각해보길 바란다.


 Rails에서 데이터베이스와 연관된 모델에서는 관계표현에서 추가적으로 dependent를 설정해줘야 한다. 위 글에서는 그냥 생략했지만(다른 글에 설명이 되있을 것이기 때문에), 반드시 고려해줘야 한다. 추가적으로 Rails의 모델에 관련된 클레스에는 데이터 수정에 밀접한 행동(팔로우, 언팔로우 등)을 정의해줄 수 있고, 데이터를 저장하고 가져오는데 필요한 행동(암호화 등)을 정의해줄 수 있다.


 MVC 중 Model은 어플리케이션의 뼈대이기 때문에 잘 공부하고 또 공부하기를 바란다. 


 참고 페이지 : http://railstutorial.jp/chapters/following-users?version=4.0#sec-the_relationship_model