Better Ajax with Rails

5180
2016-08-03

웹개발을 하다보면 전체 페이지를 다시 로딩하지 않고 부분적으로 내용을 업데이트 해야할 일이 많이 생깁니다. Front-end 의 MVC framework를 쓰는 경우가 아니라면 보통 이런 상황에서 Jqeury 의 Ajax 기능을 생각하게 되는데요, 이번 글에서는 이 기능을 좀 더 세련된  rails 방식으로 구현하는 법을 소개해드릴까 합니다.

rails_ajax

예를 들어서 페이지 내에 한 부분에서 키워드를 사용해서 리스트를 검색한 후에 검색결과를 비동기 방식으로 부분적으로만 업데이트 하는 기능이 있다고 생각해 봅시다.

이런 경우에 다음과 같이 jquery의 ajax fuction을 사용해서 구현한 경우를 쉽게 접할 수 있습니다.

ajax 로 받아온 데이터를 미리 정해놓은 HTML element 안에 차곡차곡 쌓아 rendering 하게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$.ajax({
    type: "POST",
    url : 'some_request_url',
    data : 'some_keyword',
    contentType : "application/x-www-form-urlencoded; charset=UTF-8",
    timeout: 5000,
    success: function (data) {
        var $title = $('<h1>').text(data.results[0].title);
        var $description = $('<p>').text(data.results[0].description);
        $('#search_result')
            .append($title)
            .append($description)
    },
    error : function(data){
        $("#search_error").show();
    }
});

간단하게 html을 업데이트 하는 경우에는 jquery 방식이 간편하고 사용하기 편할 수 있는데요, 다음 예제와 같이 업데이트 하는 부분의 html 코드가 조금 복잡해 지기 시작하면 이부분을 string으로 조합해서 append 하는 일이 결코 단순한 작업은 아닐 수 있습니다.  여기에 class, style 등의 attribute  값들이 붙기 시작하면 코드의 가독성도 떨어지고 디버깅도 따라서 힘들어 지게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.ajax({
    type: "POST",
    url : 'some_request_url',
    data : 'some_keyword',
    contentType : "application/x-www-form-urlencoded; charset=UTF-8",
    timeout: 5000,
    success: function (data) {
        var image = "<img height='14' width='14' style='display:inline-block; float: right;' src='some_image.png'/>";
        var keyword = "<div class='keyword_text' style='display:table-cell'>" + data.results[0].keyword + "</div>"+image;
        $('#search_result').append("<li class='row postlink' id='row"+idx+"' " +
            "onclick='javascript:keyword_clicked("+idx+", \""+name+"\",\""+data.results[0].id+"\")'>"+keyword+"</li>");
    },
    error : function(data){
        $("#search_error").show();
    }
});

이 비동기 기능을 rails 방식으로 다음과 같이 풀어볼까 합니다.  

1) routes.rb

Rails 를 이용해서 비동기 부분을 구현하려고 하면 몇개의 파일에 간단한 수정사항이 필요한데요, 우선 routes 파일에

다음과 같은 요청이 오면 html template을 보내는 대신 js template를 return 할 것이라고 명시해 줍니다.

1
post 'search/search_by_keyword' => 'search#search_by_keyword', defaults: { format: 'js' }

2) controller

그렇게 되면 실제 controller에서는 할일이 특별하게 취해야 할 행동은 없습니다.

app/contollers/search_controller.rb

1
2
3
4
def search_by_keyword
   return [] unless params[:search_query]
   @results = List.get_by_keyword(params[:search_query])
end

3) view

routes 쪽에 명시된 내용으로 naming convention을 추측해보면

views/search라는 directory에 search_by_keyword 라고하는 파일을 연상하게 될텐데요, 이때 중요한건 우리가 js 로 받을 것이라고 명시했기 때문에 js.erb라는 파일을 생성해야 합니다.

search_by_keyword.js.erb  는 다음과 같이 간단합니다. 필요한 부분을 활성화 시키는 등 기본적인 jquery(javascript) 기능들을 여기에 정리하고, #searched_list 라고 하는 HTML element에 넘어온 데이터를 가지고 실제로 보여줘야 하는 리스트를 생성하면 됩니다.  실제로 #searched_list 라고 하는 element는 searched_list 라고 하는 partial에 담겨 있습니다.

1
2
$("#searched_list").html("<%= escape_javascript (render 'searched_list') %>");
$('#searched_list_area').show();

그럼  실제로 js.erb쪽에 명시된 partial에 들어가서 기존에 html template 작업하듯이 하심 됨니다.  이때 넘어간 데이터는 controller에서 넘어온 @results 입니다. 

/view/search/_searched_list.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%= @results.each do |result| %>
    <li>
        <a href="/result/<%=result[:id]%>" class="some_link">
        <span class="some_span">
        <img src="<%= result[:image] %>" class="thumb_nail" alt="<%= result[:title]%>">
        </span>
            <div class="class1">
                <div class="class2">
                    <div class="class3">
                        <strong class="class4"><%= result[:title] %></strong>
                        <span class="class5">"<%= result[:description]%>"</span>
                    </div>
                </div>
            </div>
        </a>
    </li>
<% end %>

4) action

앗 여기서 중요한 것 하나. ajax 로 부른다는 건 일반적으로 front end 쪽에서 user가 므슨 엑션을 취했다는 뜻일텐데요,

여기서는 검색버튼을 눌렀을때에 저런 일련의 엑션들이 생성이 되는 형태입니다.

검색 input field 하고 연관있는 form을 submit 했을때 일어나게 되어 있습니다.

views/search/_search_input_box.html.erb 

1
 <%= form_tag('/search/search_by_keyword', id: :search_form, method: 'post', remote: true) do %>

여기서 중요한건 remote:true —> 이 부분인데요,  이 remote 라고 하는 attribute 값이 ajax call을 사용하겠다는 뜻이 됩니다.

submit 버튼을 누르고 chrome dev tool에서 network flow를 보면요, 아래와 같이 ajax 콜이 생성된것을 볼수 있습니다.

Screen Shot 2016-07-18 at 4.25.19 PM

이상으로 간단하게 rails를 사용하여 ajax 기능구현 방법을 소개 해 드렸는데요, 위와 같은 rails 방법으로 좀 더 명시적인 흐름을 알 수 있게 구현한다면 가독성도 높일 수 있을 뿐 아니라 기본적인 rails의 "convention over configuration" 이라는 기본 사상을 고수하면서 유지보수/디버깅에도 효율적인 방법이 될것이라고 생각합니다.


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.