๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โญ Personal_Study/Javascript

AJAX

by ํฌ์ŠคํŠธ์‰์ดํฌ 2022. 11. 5.

AJAX

AJAX ๊ฐœ์š”

โœ” Asynchronous Javascript And XML (๋น„๋™๊ธฐ ํ†ต์‹  ์›น ๊ฐœ๋ฐœ ๊ธฐ์ˆ )
โœ” ๋น„๋™๊ธฐ ํ†ต์‹ ์„ ์ด์šฉํ•ด ํ™”๋ฉด ์ „์ฒด๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ ํ•˜์ง€ ์•Š๊ณ ๋„ ์„œ๋ฒ„ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ํ™”๋ฉด์˜ ์ผ๋ถ€๋ถ„๋งŒ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ๊ธฐ๋Šฅ

AJAX์˜ ํŠน์ง•

  1. ํŽ˜์ด์ง€ ์ƒˆ๋กœ ๊ณ ์นจ ์—†์ด ์„œ๋ฒ„์— ์š”์ฒญ
  2. ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์‘๋‹ต(๋ฐ์ดํ„ฐ)๋ฅผ ๋ฐ›์•„ ์ž‘์—…์„ ์ˆ˜ํ–‰

AJAX๋ฅผ ์ด์šฉํ•ด Django follow ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

์‚ฌ์ „์ค€๋น„

  1. ํ…œํ”Œ๋ฆฟ์— script ์ฝ”๋“œ ์ž‘์„ฑ์„ ์œ„ํ•œ block tag ์˜์—ญ ์ž‘์„ฑ
<!-- base.html -->

<body>
  ...
  {% block script %}
  {% endblock script %}
</body>
  1. axios CDN ์ž‘์„ฑ
<!-- accounts/profile.html -->

{% block script %}
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
  </script>
{% endblock script %}
  1. id ์†์„ฑ ์ง€์ • ๋ฐ ์„ ํƒ, ๋ถˆํ•„์š”ํ•œ action๊ณผ method ์‚ญ์ œ (์š”์ฒญ์€ axios๋กœ ๋Œ€์ฒด๋œ๋‹ค.)
<!-- accounts/profile.html -->

<form id="follow-form">...</form>
<!-- accounts/profile.html -->

<script>
  const form = document.querySelector("#follow-form");
</script>
  1. form ์š”์†Œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์ž‘์„ฑ ๋ฐ submit ์ด๋ฒคํŠธ ์ทจ์†Œ
<!-- accounts/profile.html -->

<script>
  const form = document.querySelector("#follow-form");

  form.addEventListener("submit", function (event) {
    event.preventDefault();
  });
</script>
  1. axios ์š”์ฒญ ์ค€๋น„
<!-- accounts/profile.html -->

<script>
  const form = document.querySelector('#follow-form')

  form.addEventListener('submit', function (event) {
    event.preventDefault()
    axios({
      method: 'post',
      url: `/accounts/${???}/follow/`,
    })
  })
</script>

1. url์— ์ž‘์„ฑํ•  user pk ์ž‘์„ฑ

  1. url์— ์ž‘์„ฑํ•  user pk ๊ฐ€์ ธ์˜ค๊ธฐ (HTML > JavaScript)
<!-- accounts/profile.html -->

<form id="follow-form" data-user-id="{{ person.pk }}">...</form>
<!-- accounts/profile.html -->

<script>
  const form = document.querySelector("#follow-form");

  form.addEventListener("submit", function (event) {
    event.preventDefault();

    const userId = event.target.dataset.userId;
  });
</script>
  1. url ์ž‘์„ฑ ๋งˆ์น˜๊ธฐ
<!-- accounts/profile.html -->

<script>
  const form = document.querySelector("#follow-form");

  form.addEventListener("submit", function (event) {
    event.preventDefault();
    axios({
      method: "post",
      url: `/accounts/${userId}/follow/`,
    });
  });
</script>

data-* attributes

โœ” ์‚ฌ์šฉ์ž ์ง€์ • ๋ฐ์ดํ„ฐ ํŠน์„ฑ์„ ๋งŒ๋“ค์–ด ์ž„์˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ HTML๊ณผ DOM ์‚ฌ์ด์—์„œ ๊ตํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•
โœ” ์‚ฌ์šฉ ์˜ˆ์‹œ

<div data-my-id="my-data"></div>
<script>
  const myId = event.targe.dataset.myId;
</script>

โœ” data-test-value๋ผ๋Š” ์ด๋ฆ„์˜ ํŠน์„ฑ์„ ์ง€์ •ํ–ˆ๋‹ค๋ฉด JavaScript์—์„œ๋Š” element.dataset.testValue๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
โœ” ์†์„ฑ๋ช… ์ž‘์„ฑ ์š”๋ น

  • ๋Œ€์†Œ๋ฌธ์ž ์—ฌ๋ถ€์— ์ƒ๊ด€์—†์ด xml๋กœ ์‹œ์ž‘ํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  • ์„ธ๋ฏธ ์ฝœ๋ก  ํฌํ•จ x
  • ๋Œ€๋ฌธ์ž ํฌํ•จ x

2. csrftoken ์ž‘์„ฑ

  1. hidden ํƒ€์ž…์œผ๋กœ ์ˆจ๊ฒจ์ ธ ์žˆ๋Š” csrf๊ฐ’์„ ๊ฐ€์ง„ input ํƒœ๊ทธ๋ฅผ ์„ ํƒํ•œ๋‹คimage
<!-- accounts/profile.html -->

<script>
  const form = document.querySelector("#follow-form");
  const csrftoken = document.querySelector("[name=csrfmiddlewaretoken]").value;
</script>
  1. AJAX๋กœ csrftoken ๋ณด๋‚ด๊ธฐ
<!-- accounts/profile.html -->

<script>
  const form = document.querySelector("#follow-form");

  form.addEventListener("submit", function (event) {
    event.preventDefault();
    axios({
      method: "post",
      url: `/accounts/${userId}/follow/`,
      headers: { "X-CSRFToken": csrftoken },
    });
  });
</script>
  1. ํŒ”๋กœ์šฐ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ณ€์ˆ˜๋ฅผ ๋‹ด์•„ JSON ํƒ€์ž…์œผ๋กœ ์‘๋‹ตํ•˜๊ธฐ
# accounts/views.py

from django.http import JsonResponse

@require_POST
def follow(request, user_pk):
    if request.user.is_authenticated:
        User = get_user_model()
        me = request.user
        you = User.objects.get(pk=user_pk)
        if me != you:
            if you.followers.filter(pk=me.pk).exists():
                you.followers.remove(me)
                is_followed = False
            else:
                you.followers.add(me)
                is_followed = True
            context = {
                'is_followed': is_followed,
            }
            return JsonResponse(context)
        return redirect('accounts:profile', you.username)
    return redirect('accounts:login')
  1. viewsํ•จ์ˆ˜์—์„œ ์‘๋‹ตํ•œ is_followed๋ฅผ ์‚ฌ์šฉํ•ด ๋ฒ„ํŠผ ํ† ๊ธ€ํ•˜๊ธฐ
<!-- accounts/profile.html -->

<script>
  axios({
  method: 'post',
  url: `/accounts/${userId}/follow/`,
  headers: {'X-CSRFToken': csrftoken,}
  })
  .then((response) => {
    const isFollowed = response.data.is_followed
    const followBtn = document.querySelector('#follow-form > input[type=submit]')
    if (isFollowed === true) {
      followBtn.value = '์–ธํŒ”๋กœ์šฐ'
    } else {
      followBtn.value = 'ํŒ”๋กœ์šฐ'
    }
  })
  1. ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋กœ ๊ฒฐ๊ณผ ํ™•์ธํ•ด๋ณด๊ธฐ
    image

    image

ํŒ”๋กœ์›Œ & ํŒ”๋กœ์ž‰ ์ˆ˜ ๋น„๋™๊ธฐ ์ ์šฉ

  1. ํ•ด๋‹น ์š”์†Œ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก span ํƒœ๊ทธ์™€ id ์†์„ฑ ์ž‘์„ฑ
<!-- accounts/profile.html -->

{% extends 'base.html' %}

{% block content %}
  <h1>{{ person.username }}๋‹˜์˜ ํ”„๋กœํ•„</h1>
  <div>
    ํŒ”๋กœ์›Œ : <span id="followers-count">{{ person.followers.all|length }}</span> / ํŒ”๋กœ์ž‰ : <span id="followings-count">{{ person.followings.all|length }}</span>
  </div>
  1. ์ง์ „์— ์ž‘์„ฑํ•œ span ํƒœ๊ทธ ๊ฐ๊ฐ ์„ ํƒ
<!-- accounts/profile.html -->

<script>
  axios({
  method: 'post',
  url: `/accounts/${userId}/follow/`,
  headers: {'X-CSRFToken': csrftoken,}
  })
  .then((response) => {
    ...
    const followersCountTag = document.querySelector('#followers-count')
    const followingsCountTag = document.querySelector('#followings-count')
  })
  1. ํŒ”๋กœ์›Œ, ํŒ”๋กœ์ž‰ ์ธ์› ์ˆ˜ ์—ฐ์‚ฐ์€ view ํ•จ์ˆ˜์—์„œ ์ง„ํ–‰ํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์‘๋‹ต์œผ๋กœ ์ „๋‹ฌ
# accounts/views.py

from django.http import JsonResponse

@require_POST
def follow(request, user_pk):
    ...
            context = {
                'is_followed': is_followed,
                'followers_count': you.followers.count(),
                'followings_count': you.followings.count(),
            }
            return JsonResponse(context)
        return redirect('accounts:profile', you.username)
    return redirect('accounts:login')
  1. view ํ•จ์ˆ˜์—์„œ ์‘๋‹ตํ•œ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ ํƒœ๊ทธ์˜ ์ธ์› ์ˆ˜ ๊ฐ’ ๋ณ€๊ฒฝํ•˜๊ธฐ
<!-- accounts/profile.html -->

<script>
  axios({
  method: 'post',
  url: `/accounts/${userId}/follow/`,
  headers: {'X-CSRFToken': csrftoken,}
  })
  .then((response) => {
    ...
    const followersCountTag = document.querySelector('#followers-count')
    const followingsCountTag = document.querySelector('#followings-count')
    const followersCount = response.data.followers_count
    const followingsCount = response.data.followings_count
    followersCountTag.innerText = followersCount
    followingsCountTag.innerText = followingsCount
  })

์ตœ์ข… ์ฝ”๋“œ

  1. HTML ํ…œํ”Œ๋ฆฟ
{% extends 'base.html' %}

{% block content %}
  <h1>{{ person.username }}๋‹˜์˜ ํ”„๋กœํ•„</h1>
  <div>
    ํŒ”๋กœ์›Œ : <span id="followers-count">{{ person.followers.all|length }}</span> / ํŒ”๋กœ์ž‰ : <span id="followings-count">{{ person.followings.all|length }}</span>
  </div>


  {% if request.user != person %}
  <div>
    <form id="follow-form" data-user-id="{{ person.pk }}">
      {% csrf_token %}
      {% if request.user in person.followers.all %}
        <input type="submit" value="์–ธํŒ”๋กœ์šฐ">
      {% else %}
        <input type="submit" value="ํŒ”๋กœ์šฐ">
      {% endif %}
    </form>
  <div>
  {% endif %}
  1. Python ์ฝ”๋“œ
@require_POST
def follow(request, user_pk):
    if request.user.is_authenticated:
        User = get_user_model()
        me = request.user
        you = User.objects.get(pk=user_pk)
        if me != you:
            if you.followers.filter(pk=me.pk).exists():
                you.followers.remove(me)
                is_followed = False
            else:
                you.followers.add(me)
                is_followed = True
            context = {
                'is_followed': is_followed,
                'followers_count': you.followers.count(),
                'followings_count': you.followings.count(),
            }
            return JsonResponse(context)
        return redirect('accounts:profile', you.username)
    return redirect('accounts:login')
  1. JavaScript ์ฝ”๋“œ
<script>
    const form = document.querySelector('#follow-form')
    const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value

    form.addEventListener('submit', function (event) {
      event.preventDefault()

      const userId = event.target.dataset.userId

      axios({
        method: 'post',
        url: `/accounts/${userId}/follow/`,
        headers: {'X-CSRFToken': csrftoken,}
      })
        .then((response) => {

          // ๋ฒ„ํŠผ ํ† ๊ธ€
          const isFollowed = response.data.is_followed
          const followBtn = document.querySelector('#follow-form > input[type=submit]')

          if (isFollowed === true) {
            followBtn.value = '์–ธํŒ”๋กœ์šฐ'
          } else {
            followBtn.value = 'ํŒ”๋กœ์šฐ'
          }

          // ํŒ”๋กœ์šฐ, ํŒ”๋กœ์›Œ ์ธ์› ์ˆ˜
          const followersCountTag = document.querySelector('#followers-count')
          const followingsCountTag = document.querySelector('#followings-count')
          const followersCount = response.data.followers_count
          const followingsCount = response.data.followings_count
          followersCountTag.innerText = followersCount
          followingsCountTag.innerText = followingsCount
        })
        .catch((error) => {
          console.log(error.response)
        })
    })
  </script>

์ข‹์•„์š”(like)

โœ” ํŒ”๋กœ์šฐ์™€ ๋™์ผํ•œ ํ๋ฆ„ + forEach() & querySelectorAll()

  • index ํŽ˜์ด์ง€ ๊ฐ ๊ฒŒ์‹œ๊ธ€๋งˆ๋‹ค ์ข‹์•„์š” ๋ฒ„ํŠผ
  1. HTML ์ฝ”๋“œ
<!-- articles/index.html -->

{% extends 'base.html' %}

{% block content %}
  <h1>Articles</h1>
  {% if request.user.is_authenticated %}
    <a href="{% url 'articles:create' %}">CREATE</a>
  {% endif %}
  <hr>
  {% for article in articles %}
    <p>
      <b>์ž‘์„ฑ์ž : <a href="{% url 'accounts:profile' article.user %}">{{ article.user }}</a></b>
    </p>
    <p>๊ธ€ ๋ฒˆํ˜ธ : {{ article.pk }}</p>
    <p>์ œ๋ชฉ : {{ article.title }}</p>
    <p>๋‚ด์šฉ : {{ article.content }}</p>
    <div>
      <form class="like-forms" data-article-id="{{ article.pk }}">
        {% csrf_token %}
        {% if request.user in article.like_users.all %}
          <input type="submit" value="์ข‹์•„์š” ์ทจ์†Œ" id="like-{{ article.pk }}">
        {% else %}
          <input type="submit" value="์ข‹์•„์š”" id="like-{{ article.pk }}">
        {% endif %}
      </form>
    </div>
    <a href="{% url 'articles:detail' article.pk %}">์ƒ์„ธ ํŽ˜์ด์ง€</a>
    <hr>
  {% endfor %}
{% endblock content %}
  1. Python ์ฝ”๋“œ
# articles/views.py

from django.http import JsonResponse

@require_POST
def likes(request, article_pk):
    if request.user.is_authenticated:
        article = Article.objects.get(pk=article_pk)

        if article.like_users.filter(pk=request.user.pk).exists():
            article.like_users.remove(request.user)
            is_liked = False
        else:
            article.like_users.add(request.user)
            is_liked = True
        context = {
            'is_liked': is_liked,
        }
        return JsonResponse(context)
    return redirect('accounts:login')
  1. JavaScript ์ฝ”๋“œ
<!-- articles/index.html -->

<script>
  const forms = document.querySelectorAll(".like-forms");
  const csrftoken = document.querySelector("[name=csrfmiddlewaretoken]").value;

  forms.forEach((form) => {
    form.addEventListener("submit", function (event) {
      event.preventDefault();
      // onsole.log(event.target.dataset)

      const articleId = event.target.dataset.articleId;

      axios({
        method: "post",
        url: `http://127.0.0.1:8000/articles/${articleId}/likes/`,
        headers: { "X-CSRFToken": csrftoken },
      })
        .then((response) => {
          // console.log(response)
          // console.log(response.data)

          const isLiked = response.data.is_liked;

          const likeBtn = document.querySelector(`#like-${articleId}`);
          if (isLiked === true) {
            likeBtn.value = "์ข‹์•„์š” ์ทจ์†Œ";
          } else {
            likeBtn.value = "์ข‹์•„์š”";
          }
          // likeBtn.value = isLiked ? '์ข‹์•„์š” ์ทจ์†Œ' : '์ข‹์•„์š”'
        })
        .catch((error) => {
          console.log(error.response);
        });
    });
  });
</script>

๋Œ“๊ธ€