Category
개발  웹 개발

[Django / 블로그] Ajax를 이용한 댓글 업데이트

2023년 01월 29일 by Hwang

기존에는 포스트에 댓글을 작성했을 때 댓글을 생성하고, 업데이트 된 댓글을 보여주기 위해서 해당 포스트로 리다이렉트를 해줬다.

이 방식은 웹 페이지 전체를 로딩하므로 다시 로드가 필요없는 포스트 내용까지 로드하게 되었는데,내가 원하는 건 단지 ‘댓글’ 부분만 재갱신하는 것이기 때문에 이를 구현할 수 있는 방법을 찾아보았고, Ajax란 기법을 알게되었다

fetch api

javascript가 제공하는 fetch api를 통해 Ajax를 구현할 수 있다. fetch 메서드는 첫 번째 인자로 URL, 두 번째 인자로 옵션 객체를 받고 Promise 타입의 객체를 반환한다.

GET Request

fetch는 기본 값이 GET방식으로 설정되어있다. 일단 간단한 GET 방식의 예시를 보자

fetch('<http://example.com/movies.json>')
  .then((response) => response.json())
  .then((data) => console.log(data)); 

첫 번째 인자는 요청을 할 URL를 받는다. 이 요청에서 받은 응답 객체는 json()이란 매서드를 제공하는데 호출 시 JSON 본문 콘텐츠를 추출할 수 있다. 또한 추출한 데이터를 Promise 형태로 반환한다.

나는 댓글들의 데이터를 가져올 뿐만 아니라 생성을 위해 데이터를 전송해야하기 때문에 POST 방식을 알아야했다.

POST Request

fetch(URL, {
    method: 'POST',
    credentials: 'same-origin',
    headers:{
        'Accept': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRFToken': csrftoken,
    },
    body: JSON.stringify({'comment_data':'comment data'})
})
.then(response => {
        return response.json() //Convert response to JSON
})
.then(data => {
	console.log(data);
})

두 번째 인자로 옵션 객체가 있는 것을 볼 수 있는데, HTTP 방식, HTTP 요청 헤더, HTTP 요청 전문등을 설정할 수 있다.

이전에 말했듯이 fetch는 디폴트가 GET으로 설정되어 있기 때문에 method 옵션을 POST로 지정해야한다.

credentials 옵션의 ‘same-origin’은 요청 URL이 스크립트와 같은 출처일 때만 자격증명을 전송한다. 만약 전송하지 않도록 하려면 ‘omit’을 사용하면 된다.

다음은 헤더로, 댓글의 데이터를 JSON포멧으로 전송할 것이기 때문에 Accept 헤더를 ‘application/json’ 으로 설정했고, view에서 이 요청이 ajax요청인지 구분 할 수 있도록 해주기 위해 'X-Requested-With'헤더를 'XMLHttpRequest'로 세팅했다.

django에서 POST방식을 사용할 땐, csrf 토큰이 반드시 포함되어야한다. 따라서 'X-CSRFToken’ 헤더는 반드시 포함되어야고 여기서의 csrf 토큰은 django 문서에 얻을 수 있는 방법을 제공한다.

function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
const csrftoken = getCookie('csrftoken');

body에는 보낼 데이터를 포함한다. JSON 포멧으로 전달할 것이라 설정했으므로 객체를 JSON 문자열로 변환해서 body 옵션에 설정해준다.

이제 간단하게 사용법을 알았으니 구현을 해보자.

function summitComment(comment, user_pk, post_pk){
    URL = document.URL + 'new-comment/';
    fetch(URL,
        {
            method: 'POST',
            credentials: 'same-origin',
            headers:{
                'Accept': 'application/json',
                'X-Requested-With': 'XMLHttpRequest',
                'X-CSRFToken': csrftoken
            },
            body: JSON.stringify({
                'comment': comment,
                'user_pk': user_pk,
		'post_pk': post_pk 
            }),
        },
    ).then(response => {
        return response.json();
    }).then(data => {
        updateCommentList(data)
    });
}

댓글의 내용, 댓글을 쓴 유저와 댓글이 쓰인 포스트의 정보를 함께 전송한다.

from django.http import JsonResponse
import json

def new_comment(request):
    if request.headers.get('x-requested-with') == 'XMLHttpRequest':
        comment_info = json.load(request)
        post = get_object_or_404(Post, pk=comment_info['post_pk'])
        user = get_object_or_404(User, pk=comment_info['user_pk'])
        comment_content = comment_info['comment']
        Comment.objects.create(
            author=user,
            post=post,
            content=comment_content,
        )
        data = {
        'comments': [
            {
                'author': comment.author.username,
                'content': comment.content,
                'pub_date': comment.get_pub_date(),
                'comment_pk': comment.pk,
                'author_pk': comment.author.pk,
                'post_pk': post.pk
            } for comment in post.comment_set.all()]
	}
        return JsonResponse(data)
    else:
        raise PermissionDenied

ajax 요청일 때만 댓글을 생성하기 위해 'X-Requested-With' 헤더에 'XMLHttpRequest'로 설정해주었고 이를 이용해서 구분을 해주었다.

댓글을 생성하고 이제 업데이트된 포스트의 댓글들을 가져와야한다. 따라서 해당 포스트의 댓글들의 정보를 딕셔너리에 담고, JsonResponse 를 이용해서 데이터를 JSON 응답으로 클라이언트에 전달한다.

이렇게 받은 댓글 데이터들을 이용해서 댓글 리스트 부분만 재갱신 해주면 성공이다.


 Comment



  Hwang    Jan. 29, 2023

성공!