-
[루비온레일즈] 댓글 기능 구현하기 (부모-자식 관계)Ruby on Rails 2021. 3. 11. 15:39
이전 포스팅까지 MVC 패턴으로 CRUD를 구현하고 레일즈의 Scaffold를 활용해 게시판을 만들어보았습니다.
이번에는 게시글에 댓글 기능을 구현해보도록 하겠습니다!
1. 부모-자식 관계
댓글 기능을 구현하기 전에 댓글과 게시글 간의 관계를 먼저 이해해야 합니다.
우리가 보통 게시글에 댓글을 쓰면, 해당 댓글은 해당 게시물에서만 읽고, 쓰고, 수정, 삭제 즉 CRUD가 가능합니다. 다른 게시글에서는 또다른 게시글에 달린 댓글을 읽거나 쓰고 수정하고 삭제할 수 없습니다.
이는 다시 말해 특정 댓글이 특정 게시글에 속해 있다고 이해할 수 있습니다.
이러한 게시글과 댓글 사이의 관계를 부모-자식 관계라고 하며, 이 관계는 게시글 모델과 댓글 모델의 관계 설정을 통해 구현할 수 있습니다.
이 관계를 통해 댓글 모델을 게시글 모델의 자식으로 만들어 특정 게시물에서는 그 게시물에 달린 댓글만 읽고, 쓰고, 수정하고 삭제할 수 있도록 만드는 것입니다.
그리고 게시글이 부모가 되는 이유는 한 개의 게시글이 여러 개의 댓글을 가질 수 있고, 반대로 여러 개의 댓글이 한 개의 게시글에 속하기 때문입니다.
또한, 데이터베이스에서 데이터 관리 측면에서 게시글이 모든 댓글을 알고 있기보다 여러 개의 댓글이 자신의 부모가 누구인지 알고 있는 것이 관리하기 효율적일 것입니다.
이를 구현하기 위해 게시글과 댓글의 관계를 설정하고, 댓글 모델에 게시글의 id 를 넣어주면 그 댓글은 항상 자신의 부모가 누구인지 알 수 있을 것입니다.
추가적으로 유저와 게시글, 댓글 간의 관계도 생각해볼 수 있습니다. 여러 개의 게시글과 댓글이 한 명의 유저에게 속하기 때문입니다.
결국, 유저가 할머니, 게시글이 부모, 댓글이 자식이 되는 관계로 이해할 수 있습니다.
오른쪽 그림을 보면 user_id를 post 모델과 comment 모델이 알고 있고, post_id를 comment 모델이 알고 있습니다.
이런 식으로 관계 설정을 해준다면, 특정 유저가 작성한 게시글과 댓글은 그 유저만 수정, 삭제할 수 있도록 할 수 있고, 특정 게시글에 작성된 댓글은 그 게시글에서만 읽고, 쓰고, 수정, 삭제하도록 할 수 있습니다.
이제 댓글 기능 구현을 하기 전에 어떻게 하면 좋을지 설계해보겠습니다!
2. 댓글 기능 구현하기 전에
우선, 크게 보면 이전에 게시글의 CRUD를 구현했던 것과 같은 원리로 댓글도 CRUD를 구현하면 될 것 같은데요.
컨트롤러의 액션을 구성할 기능에 대해 생각해보면 게시글과는 조금 차이점이 있어 보입니다.
1. index 액션 측면에서, 댓글은 전체 댓글 목록을 불러올 필요가 없습니다. 즉, index 가 필요 없다는 것입니다.
2. new 액션에서, 댓글은 새 댓글 작성 기능 및 폼이 게시글의 show 페이지 안으로 들어가야 합니다. 우리는 댓글을 새 창을 열어서 작성하는 것이 아니라 기존에 작성된 게시글에 작성하기 때문입니다.
3. create 액션과 update 측면에서 댓글은 자신의 부모 즉, 특정 게시글의 id를 알고 있어야 합니다. update 시 이전 내용을 불러와야 할 것입니다.
4. show 액션에서는 댓글이 게시글 하단에 보이면 좋을 것입니다.
이외에도 더 생각해볼 것들 중에, 이제 게시글과 댓글을 부모-자식 관계로 묶어줄텐데 그 과정에서 댓글은 게시글의 id를 알게 됩니다. 이 때, 보안을 위해 댓글이 알고 있는 게시글의 id는 뷰 쪽에서는 숨겨주는 것이 좋을 것입니다.
지금은 여기까지만 생각해보고 본격적으로 댓글 기능을 만들어보겠습니다!
3. 댓글 작성 기능 구현하기
resource comment 만들기
댓글을 기능을 만들기 위해 우선, 상단 Tools 메뉴 - Run Rails Generator 에서 rails g resource를 클릭해 resource를 만들어줍니다.
안에 내용은 resource이름, 필드명:필드값으로 Scaffold와 유사한 형식입니다.
저는 comment content:text board:references 로 지정하겠습니다.
❗️여기서 references 는 영어 사전에서는 참조하다는 뜻인데, board:references 하면 comment 모델을 board에 속하도록 하겠다는 의미입니다. 즉 여기에서부터 부모-자식 관계 맺기가 시작입니다. references의 복수형에 주의해주세요.
이 방식은 맥 터미널에서
rails g resource comment content:text board:references
라고 입력해도 동일한 결과가 나옵니다.
명령을 실행하면 하단 실행 창에 다음과 같이 여러 파일들이 생성됩니다.
Scaffold에서처럼 이 파일들도 살펴보겠습니다.
우선, comment의 마이그레이션 파일과 모델 파일(comment.rb)이 생성되었습니다.
이 파일의 5번째 줄의 코드를 보면, board를 referneces로 하고 foreign_key를 갖고 있다고 되어있습니다. 이 foreign_key를 통해 부모-자식 관계를 확인할 수 있습니다.
마이그레이션 파일을 보면 모델의 테이블 모양이 잘 잡힌 것 같으니 rake db:migrate 해줍니다.
comment 마이그레이션 파일 comment.rb 에서 모델 파일을 보면, belongs_to :board 라는 코드가 작성되어 있습니다.
아까 앞에서 board:references를 설정했기 때문에 comment 모델이 board 모델에 속해있다는 코드가 자동으로 들어간 것입니다.
comment.rb 다음, comments_controller.rb 즉 댓글 컨트롤러가 생성되었는데, scaffold에서 만든 컨트롤러와 달리 안에 아무런 메소드가 정의되지 않았습니다.
이 점이 resource와 scaffold의 차이점입니다. scaffold는 crud에 필요한 모든 액션을 만들어주지만, comment는 index나 show와 같은 불필요한 액션들이 있기 때문에 우리가 필요한 기능들만 코딩해주면 됩니다.
나머지 test, helper, scss는 지금 댓글 기능 구현과 크게 관련이 없으니 넘어가겠습니다.
이제 comment.rb에는 belongs_to 로 부모가 누군지 알려주었으니, board.rb에 가서 자식이 누군지 알려주고 관계설정을 마무리하겠습니다.
board.rb에는 2번째 줄과 같이
has_many :comments
라는 코드를 삽입해 여러 댓글을 가진다고 알려줍니다. (belongs_to 와 has_many를 작성할 때, 단수 및 복수형에 주의해주세요)
라우트 설정하기(routes.rb)
다음으로 라우트를 설정해주겠습니다.
지금까지 만든 라우트들 말고 2번째 줄에 resources :comments 라는 한 줄이 생겼습니다.
boards 가 부모이고 comments 가 자식이므로 라우트도 다음과 같이 작성해서 관계를 설정해줍니다.
resources :board do
resources :comments
end터미널 창에서
rails routes | grep comment
라고 입력해보면 comment 와 관련된 경로들을 전부 확인할 수 있습니다.
여기에서 살펴보니 /boards/:board_id/comments 이런 식으로 comments의 경로 앞에 전부 board와 board_id가 들어가 있네요.
즉, 경로에서도 부모-자식 관계가 제대로 설정되었다는 것입니다.
댓글 뷰 만들기
이제 화면에 댓글을 보여주는 뷰를 만들겠습니다.
댓글은 각 게시물의 show 페이지에서 보는 것이기 때문에 board 컨트롤러의 show.html.erb 파일에서 게시글 하단에 코드를 작성합니다.
<h3>Comments</h3>
<div id="comments">
<%= render @board.comments %>
</div>
<div>
<%= render 'comments/new', board: @board %>
</div>render를 통해 각 게시물(@board)의 comments를 불러와서 보여주고, 새 comments 작성하는 폼을 불러오라는 의미입니다.
댓글 보기 및 작성 폼 만들기
이제 댓글을 보여주는 폼과 새 댓글을 작성하는 폼을 만들겠습니다.
이전에 render를 사용해서 폼을 만들 때, html파일 앞에 _(언더바)를 붙이는 규칙을 이야기했었습니다. 그리고 폼 파일은 보통 Partial에 만들어 주기 때문에 comments 컨트롤러의 Partial 에 _comment.html.erb 파일과 _new.html.erb 파일을 생성합니다.
파일 생성하는 방법은 파일을 생성하고자 하는 디렉토리를 우클릭하고 New - File 로 파일 생성 창을 불러와 만들 수 있습니다.
우선, _comment.html.erb 파일에 댓글 내용을 보여주는 폼을 작성하겠습니다.
<p><%= comment.content %> <%= comment.created_at.to_s(:long) %></p>
이 코드는 우선 댓글의 content 즉 내용을 보여주고, 그 옆에 댓글이 작성된 시간을 알려주는 코드입니다.
created_at은 생성 시간으로 댓글이나 게시물 작성 시 자동으로 모델에 들어가는 데이터이며 .to_s는 이를 문자열로 바꿔주는 코드입니다.
(:long) 이라는 인수는 시간을 보여주는 형식의 하나로 .to_s, .to_s(:short), .to_s(:long) 세 가지 형태로 활용할 수 있습니다.
다음으로 _new.comment.erb에 새 댓글을 작성하는 폼을 만들겠습니다.
이전에 사용했던 form_for를 이용해서 댓글 작성 폼을 만드는데, 이 코드를 살펴보면 do ~ end 사이에서 각 form은 각 게시물(@board)의 새로운 댓글을 작성하는 폼이며, 댓글 내용을 작성하고 제출하는 모양입니다.
첫번째 줄의 remote: true는 true로 설정하면 새로운 댓글 작성 시 페이지를 새로고침하지 않고 바로 댓글이 작성되도록 하는 기능입니다. 만약 false로 설정하면 새로운 댓글이 작성될 때 페이지가 새로고침하게 됩니다.
false로 설정할 경우, 웹페이지와 서버가 통신하는 시간이 늘어나게 되는 비효율이 발생하게 됩니다. 따라서, remote: true 로 설정해줍니다. 이와 관련한 사항은 다른 포스팅에서 작성하겠습니다.
댓글 작성 후 저장 기능 만들기(comments controller)
이제 컨트롤러에서 create 액션을 만들어 폼에 작성한 댓글을 테이블에 저장해주는 기능을 만들겠습니다.
이전에 MVC패턴에서 create 액션을 만든 것처럼 comments_controller.rb 에 아래 코드를 작성해줍니다.
class CommentsController < ApplicationController
before_action :set_board
def create
comment = @board.comments.new(comments_params)
comment.save
redirect_to @board
end
private
def set_board
@board = Board.find(params[:board_id])
end
def comments_params
params.require(:comment).permit(:content)
end
end이 코드들의 의미는 위에서부터 보면 scaffold에서와 같이 before_action을 통해 액션을 수행하기 전에 set_board를 하라는 의미인데 set_board는 private에서 보면, 각 게시물의 파람스의 board_id 를 찾으라는 뜻입니다.
그리고 create 액션에서는 각 게시물의 코멘트의 파람스에 따라 게시물을 create하고 다시 게시물로 이동하라는 의미입니다. @board.comments 라고 적는 이유는 comment 모델이 board 모델에 종속되어 있고, 한 게시물에 여러개의 댓글을 생성할 수 있기 때문에 복수형으로 작성합니다.
다음 private 에서는 before_action 에 들어가는 set_board를 정의해줍니다. 특정 게시물을 찾아서 댓글을 생성해야 하기 때문에 board_id를 찾으라는 의미입니다.
그리고 내용을 작성해야 하기 때문에 comment 파람스의 content만을 받아오라는 코드입니다.
이제까지 댓글 작성 기능을 구현하기 위해 board - comment 모델의 부모-자식 관계를 맺어주었고, 라우트 설정, 뷰 생성, 작성 폼 생성, create 액션까지 만들었으니 댓글이 잘 작성되는지 확인해보겠습니다.
이렇게 댓글 작성 기능이 잘 동작하는 것을 확인할 수 있습니다. Yeah :) 👍
지금까지 댓글 기능을 구현하기 위해 부모-자식 관계, 댓글 구현 시 생각할 것들을 토대로 댓글 작성 기능을 구현해봤습니다.
'Ruby on Rails' 카테고리의 다른 글
[루비온레일즈] devise gem 회원정보 추가하기 (0) 2021.03.12 [루비온레일즈] devise gem 이용해 회원가입, 로그인 기능 만들기 (0) 2021.03.12 [루비온레일즈] Scaffold 생성하고 뜯어보기 (0) 2021.03.11 [루비온레일즈] MVC 패턴(업데이트, 삭제 기능) (0) 2021.03.07 [루비온레일즈] MVC 패턴(모델, CR 기능 만들기 2) (0) 2021.03.07