Ajax와 Django

Jingi·2024년 4월 24일

Web

목록 보기
29/40
post-thumbnail

Ajax와 서버

Ajax

  • 비동기적인 웹 애플리케이션 개발에 사용하는 기술

Ajax를 활용한 클라이언트 서버 간 동작

  • XML 객체 생성 및 요청 -> Ajax 요청 처리 -> 응답 데이터 생성 -> JSON 데이터 응답 -> Promise 객체 데이터를 활용해 DOM 조작(웹 페이지의 일부분 만을 다시 로딩)

Ajax with follow

Ajax 적용

    1. 프로필 페이지에 axios CDN 작성
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
  </script>
    1. form 요소 선택을 위해 id 속성 지정 및 선택
    1. action과 method 속성은 삭제
    • 요청은 axios로 대체되기 때문
      <!-- accounts/profile.html -->
      <form id = "follow-form">
      ...
      </form>
      <!-- accounts/profile.html -->
      // 2. submit 이벤트가 발생하는 form 태그 선택
      const formTag = document.querySelector('#follow-form')
    1. form 요소에 이벤트 핸들러 할당
    1. submit 이벤트의 기본동작 취소
      // 3. 선택한 form 태그에 이번트핸들로 할당
      formTag.addEventListener("submit", function (event){
          event.preventDefault()
      })
    1. axios 요청 작성
    • csrftoken 처리?
    • url에 작성할 user pk는?
      const formTag = document.querySelector('#follow-form')
      formTag.addEventListener("submit", function (event){
      event.preventDefault()
      axios({
          method:'post',
          url:`/accounts/${}/follow` ,
      })
      console.log(event)
      })
    1. 요청 url 작성 마무리
    • data-* 속성
      - 사용자 지정 데이터 특성을 만들어 임의의 데이터를 HTML과 DOM 사이에서 교환 할 수 있는 방법

      formTag.addEventListener("submit", function (event){
          event.preventDefault()
      
          const = userID = event.currentTarget.dataset.userId
          axios({
              method:'post',
              url:`/accounts/${userID}/follow` ,
          })
      })
    1. 문서상 input hidden 타입으로 존재하는 csrf token 데이터를 이제는 axios로 전송해야 함
    • csrf 값을 가진 input 요소를 직접 선택 후 axios에 작성하기

      const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value
      
      formTag.addEventListener("submit", function (event){
          event.preventDefault()
      
          const = userID = event.currentTarget.dataset.userId
          axios({
              method:'post',
              url:`/accounts/${userID}/follow`,
              headers: {'X-CSRFToken': csrftoken,},
          })
      })
    1. 팔로우 버튼을 토글하기 위해서는 현재 팔로우 상태인지 언팔로우 상태인지에 대한 상태 확인 피료
    • Django의 view 함수에서 팔로우 여부를 파악할 수 있는 변수를 추가로 생성해 JSON타입으로 응답하기
    1. 팔로우 상태 여부를 JavaScript에서 전달할 데이터 작성
    • 응답은 더이상 HTML 문서가 아닌 JSON 데이터 작성

      from django.http import JsonResponse
      @login_required
      def follow(request, user_pk):
          me = request.user
          you = get_user_model().objects.get(pk=user_pk)
      
          if me != you:
              if me in you.followers.all():
                  you.followers.remove(me)
                  is_followed = False
              else:
                  you.followers.add(me)
                  is_followed = True
              context = {
                  'is_followed': is_followed,
                  'followings_count': you.followings.count(), 
                  'followers_count': you.followers.count()
              }
              return JsonResponse(context)
          return redirect('accounts:profile', you.username)
    1. 팔로우 요청 후 django 서버로 부터 받은 데이터 확인하기

      const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value
      
      formTag.addEventListener("submit", function (event){
          event.preventDefault()
      
          const = userID = event.currentTarget.dataset.userId
          
          axios({
              method:'post',
              url:`/accounts/${userID}/follow`,
              headers: {'X-CSRFToken': csrftoken,},
          }).then((response) => {
              console.log(response)
              console.log(response.data)
          })
      })
    1. 응답 데이터 is_followed에 따라 팔로우 버튼을 토글하기

      const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value
      
      formTag.addEventListener("submit", function (event){
          event.preventDefault()
      
          const = userID = event.currentTarget.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('input[type=submit]')
              if (isFollowed === true){
                  followBtn.value = 'Unfollow'
              }
              else{
                  followBtn.value = 'Follow'
              }
              console.log(response)
              console.log(response.data)
          })
      })
    1. 팔로잉 수와 팔로워 수 비동기 적용
    • 해당 요소를 선택할 수 있도록 span 태그와 id 속성 작성
      <div>
          팔로잉 : <span id = "followings-count">{{ person.followings.all|length }}</span>/
          팔로워 : <span id = "followers-count">{{ person.followers.all|length }}</span>/
      </div>
    1. 각 span 태그 선택
      .then((response) => {
          const followingsCountTag = document.querySelector('#followings-count')
          const followersCountTag = document.querySelector('#followers-count')
      })
    1. Django view 함수에서 팔로워, 팔로잉 인원 수 연산을 진행하여 결과를 응답 데이터로 전달
      def follow(request, user_pk):
          ...
              context = {
                  'is_followed' : is_followed,
                  'followings_count' : person.followings.count(),
                  'followers_count' : person.followers.count(),
              }
              return JsonResponse(context)
          return redirect('accounts:profile', person.username)
    1. 응답 데이터를 받아 각 태그의 인원수 값 변경에 적용

      .then((response) => {
          const followingsCountTag = document.querySelector('#followings-count')
          const followersCountTag = document.querySelector('#followers-count')
      
          followingsCountTag.textContent = response.data.followings_count
          followersCountTag.textContent = response.data.followers_count
      })

Ajax with likes

버블링

  • 한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작하는 현상
  • 가장 최상단의 조상요소를 만날 때까지 이과정이 반복되면서 요소 각각에 할당된 핸들러가 동작

currentTarget & target

  • currentTarget 속성
    • 현재 요소
    • 항상 이벤트 핸들러가 연결된 요소만을 참조하는 속성
    • this와 같음
  • target 속성
    • 이벤트가 발생한 가장 안쪽의 요소를 참조하는 속성
    • 실제 이벤트가 시작된 요소
    • 버블링이 진행 되어도 변하지 않음

비동기 좋아요 구현

    1. 모든 좋아요 form 요소를 포함하는 최상위 요소 작성
      <article class ="article-container">
          {% for article in articles %}
          ..
          {% endfor %}
      </article>
    1. 최상위 요소 선택
    • 이벤트 핸들러 할당

    • 하위 요소들의 submit 이벤트를 감지하고 submit 기본 이벤트를 취소

      const articleContainger = document.querySelector('.article-container')
      
      articleContainer.addEventListener('submit', function(event){
          event.preventDefault()
      })
    1. axios 작성

      const articleContainger = document.querySelector('.article-container')
      
      articleContainer.addEventListener('submit', function(event){
          event.preventDefault()
          axios({
              method:'post',
              url:`/articles/${}/likes`,
              headers:{'X-CSRFToken':csrftoken,},
          })
      })
    1. 각 좋아요 form에 article.pk를 부여 후 HTML article.pk 값을 JavaScript에서 참조

      <form data-article-id = "{{ article.pk }}">
      ...
      </form>
      const articleContainger = document.querySelector('.article-container')
      
      articleContainer.addEventListener('submit', function(event){
          event.preventDefault()
          const articleId = event.target.dataset.articleId
          axios({
              method:'post',
              url:`/articles/${}/likes`,
              headers:{'X-CSRFToken':csrftoken,},
          })
      })
    1. url 완성 후 요청 및 응답 확인

      <form data-article-id = "{{ article.pk }}">
      ...
      </form>
      const articleContainger = document.querySelector('.article-container')
      
      articleContainer.addEventListener('submit', function(event){
          event.preventDefault()
          const articleId = event.target.dataset.articleId
          axios({
              method:'post',
              url:`/articles/${articleId}/likes`,
              headers:{'X-CSRFToken':csrftoken,},
          })
          .then((response) => {
              console.log(response)
          })
          .catch((error) => {
              console.log(error)
          })
      })
    1. 좋아요 버튼을 토글하기 위해서는 현재 사용자가 좋아요를 누른 상태인지 좋아요를 누르지 않은 상태인지에 대한 상태 확인이 필요
    • django의 view 함수에서 좋아요 여부를 파악 할 수 있는 변수 추가 생성
    • JSON 타입으로 응답하기
    1. 좋아요 상태 여부를 JavaScript에게 전달할 데이터 작성 및 JSON 데이터 응답

      form django.http import JsonResponse
      
      @login_required
      def likes(request, article_pk):
          article = Article.objects.get(pk=article_pk)
          if request.user in article.like_users.all():
              article.like_users.remove(request.user)
              is_liked = False
          else:
              article.like_users.add(request.user)
              is_liked = True
          context = {
              'is_liked': is_liked,
              'liked_count': article.like_users.count(),
          }
          return JsonResponse(context)
    1. 응답 데이터 is_liked를 받아 isLiked 변수에 할당
      axios({
          method:'post',
          url:`/articles/${articleId}/likes`,
          headers:{'X-CSRFToken':csrftoken,},
      })
      .then((response) => {
          console.log(response)
          const isLiked = response.data.is_liked
      })
      .catch((error) => {
          console.log(error)
      })
    1. isLiked에 따라 좋아요 버튼을 토글하기
    • 그런데 어떤 좋아요 버튼을 선택 했는지 확인하기 위한 값이 필요
      axios({
          method:'post',
          url:`/articles/${articleId}/likes`,
          headers:{'X-CSRFToken':csrftoken,},
      })
      .then((response) => {
          console.log(response)
          const isLiked = response.data.is_liked
          const likeBtn = ??
      })
      .catch((error) => {
          console.log(error)
      })
    1. 문자와 article의 pk 값을 혼합하여 id 속성 값을 설정
      {% 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 %}
    1. 각 좋아요 버튼을 선택 후 isLiked에 따라 버튼을 토글
.then((response) => {
    console.log(response)
    const isLiked = response.data.is_liked
    const likeBtn = document.querySelector(`#like-${articleId}`)
    if (isLiked === true){
        likeBtn.value = '좋아요 취소'
    } else{
        likeBtn.value = '좋아요'
    }
})

버블링을 활용하지 않은 경우

  • querySelectorAll()을 사용해 전체 좋아요 버튼을 선택

  • forEach()를 사용해 전체 좋아요 버튼을 순회하면서 진행

  • querySelectorAll() 선택을 위한 class 적용

    {% for article in articles %}
        ...
        <form class ="like-forms' data-article-id="{{ article.pk }}">
        {% csrf_token %}
        {% if request.user in article.like_users.all %}
            <input type="submit" value="좋아요 취소">
        {% else %}
            <input type="submit" value="좋아요">
        {% endif %}
        </form>
        <hr>
    {% endfor %}
  • forEach()를 사용해 전체 좋아요 버튼을 순회하면서 진행

profile
데이터 분석에서 백엔드까지...

1개의 댓글

comment-user-thumbnail
2024년 4월 24일

비동기 좋아요 구현한 거 캡쳐해주면 안돼요???

답글 달기