IT/development

[Vue.js] vue.js의 렌더링 방식과 기존 방식의 차이점(feat. ajax)

알 수 없는 사용자 2023. 7. 30. 12:20
반응형

목차

    ajax로 데이터 조회 시 reactivity를 활용한 lib나 framework를 사용하지 않는 기존 방식(javascript/jQuery)의 경우 화면에서 ajax로 데이터를 가져와서 그릴 부분의 태그만 미리 정의해 놓고 데이터를 가져온 후에 스크립트단에서 동적으로 태그를 만드는 로직을 작성해서 붙였다.(아래 참조)

    기존방식

    html

    <table>
            <caption>주소록</caption>
            <colgroup>
              <col width="60px">
              <col width="100px">
              <col width="150px">
              <col width="200px">
              <col width="100px">
              <col width="120px">
            </colgroup>
            <thead>
            <tr>
              <th scope="row">순번</th>
              <th scope="row">사원명</th>
              <th scope="row">직급</th>
              <th scope="row">직책</th>
              <th scope="row">전화번호</th>
              <th scope="row">이메일</th>
            </tr>
            </thead>
            <tbody class="center" id="addressbookBody">
            </tbody>
          </table>
    
    <!-- pagination -->
    <div class="paging tbl-paging">
    </div>

    script

    //table에 데이터 그리는 부분
    const targetTbody = "addressbookBody";
    if(data.list != null && data.list.length > 0) {
    // foreach start
        for (let i = 0; i < data.list.length; i++) {
            htmlData += "<tr>";
            htmlData += "<td>";
            htmlData += data.list[i].no;
            htmlData += "</td>";
            htmlData += "<td>" + data.list[i].empNm;
            htmlData += "</td>";
            htmlData += "<td>" + data.list[i].positionCd;
            htmlData += "</td>";
            if(data.list[i].roleCd != null) {
                htmlData += "<td>" + data.list[i].roleCd;
            } else {
                htmlData += "<td>" + "없음";
            }
            htmlData += "</td>";
            htmlData += "<td>" + data.list[i].empHp;
            htmlData += "</td>";
            htmlData += "<td>" + data.list[i].empEmail;
            htmlData += "</td>";
        }
    } else {
        htmlData += "<td colspan='6' style='text-align: center'>";
        htmlData += "검색결과가 없습니다.";
        htmlData += "</td>";
        htmlData += "</tr>";
    }
    htmlData += "</td>";
    htmlData += "</tr>";
    // foreach end
    // foreach end
    
    //tbody에 반영
    $("#" + targetTbody + "").html(htmlData);
    
    
    
    //페이지네이션 처리
    function drawPagination(paging) {
    
    	let pageHtml = "";
    	pageHtml += "<ul class='pagination'>";
    
    	//first
    	const first = parseInt(paging.firstPage);
    	pageHtml += "<li class='paginate_button page-item first'>";
    
    	if (paging.currentPage === paging.firstPage) {
    		pageHtml += "<button type='button' class='page-link' disabled>";
    	} else {
    		pageHtml += "<button type='button' class='page-link' style='cursor: pointer;' onclick='movePage("+ first +")'>";
    	}
    	pageHtml += "<span class='hidden'>first page</span>";
    	pageHtml += "</button>";
    	pageHtml += "</li>"
    
    	//prev
    	const prev = parseInt(paging.currentPage) -1;
    	pageHtml += "<li class='paginate_button page-item prev'>";
    
    	if (paging.firstPage === paging.currentPage) {
    		pageHtml += "<button type='button' class='page-link' disabled>";
    	} else {
    		pageHtml += "<button type='button' class='page-link' style='cursor: pointer;' onclick='movePage("+ prev +")'>";
    	}
    	pageHtml += "<span class='hidden'>prev page</span>";
    	pageHtml += "</button>";
    	pageHtml += "</li>";
    
    	//① ~ ⑩
    	for (let i = paging.firstPageNo; i <= paging.lastPageNo; i++) {
    		pageHtml += "<li class='paginate_button page-item";
    		//현재 페이지가 인덱스와 같으면 active class 추가
    		if (paging.currentPage === i) {
    			pageHtml += " active'>";
    			pageHtml += "<button type='button' class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
    			pageHtml += i;
    			pageHtml += "</button>";
    			pageHtml += "</li>"
    		} else {
    			pageHtml += "'>";
    			pageHtml += "<button type='button' class='page-link' style='cursor: pointer;' onclick='movePage("+ i +")'>";
    			pageHtml += i;
    			pageHtml += "</button>";
    			pageHtml += "</li>"
    		}
    	}
    
    	//next
    	const next = parseInt(paging.currentPage) + 1;
    	pageHtml += "<li class='paginate_button page-item next'>";
    
    	if (paging.currentPage === paging.lastPage) {
    		pageHtml += "<button type='button' class='page-link' disabled>";
    	} else {
    		pageHtml += "<button type='button' class='page-link' style='cursor: pointer;' onclick='movePage("+ next +")'>";
    	}
    	pageHtml += "<span class='hidden'>next page</span>";
    	pageHtml += "</button>";
    	pageHtml += "</li>"
    
    	//last
    	const last = parseInt(paging.lastPage);
    	pageHtml += "<li class='paginate_button page-item last'>";
    
    	if (paging.currentPage === paging.lastPage) {
    		pageHtml += "<button type='button' class='page-link' disabled>";
    	} else {
    		pageHtml += "<button type='button' class='page-link' style='cursor: pointer;' onclick='movePage("+ last +")'>";
    	}
    	pageHtml += "<span class='hidden'>last page</span>";
    	pageHtml += "</button>";
    	pageHtml += "</li>"
    
    	pageHtml += "</ul>";
    	//페이지네이션 영역에 반영
    	$(".tbl-paging").html(pageHtml);
    }

    이 경우 페이지 이동 시 DOM을 계속 다시 그리게 된다.(tr태그부분, 페이지네이션 처리 부분 계속 다시 그림)


    위 코드를 vue.js로 변경하면 아래와 같다.

    vue.js 방식

    App.vue(vue cli 프로젝트) 디자인 부분은 위와 다름

    <template>
      <div id="app">
        <table class="table">
          <thead>
            <tr>
              <th>순번</th>
              <th>사원명</th>
              <th>직급</th>
              <th>직책</th>
              <th>전화번호</th>
              <th>이메일</th>
            </tr>
          </thead>
          <tbody>
    		<!-- computed 속성값 데이터 사용해서 데이터 표시 -->
            <tr v-for="item in displayedItems" :key="item.empId">
              <td>{{ item.no }}</td>
              <td>{{ item.empNm }}</td>
              <td>{{ item.positionCd }}</td>
              <td>{{ item.roleCd }}</td>
              <td>{{ item.empHp }}</td>
              <td>{{ item.empEmail }}</td>
            </tr>
          </tbody>
        </table>
    
        <nav aria-label="Page navigation">
          <ul class="pagination">
            <li class="page-item" :class="{ 'disabled': currentPage === 1 }">
              <a class="page-link" href="#" @click="movePage(currentPage - 1)">Previous</a>
            </li>
            <li class="page-item" v-for="page in totalPageCount" :key="page" :class="{ 'active': currentPage === page }">
              <a class="page-link" href="#" @click="movePage(page)">{{ page }}</a>
            </li>
            <li class="page-item" :class="{ 'disabled': currentPage === totalPageCount }">
              <a class="page-link" href="#" @click="movePage(currentPage + 1)">Next</a> 
            </li>
          </ul>
        </nav>
      </div>
    </template>
    
    <script>
    //ajax 통신 하기 위한 lib import
    import axios from 'axios';
    
    export default {
      //데이터 세팅
      data() {
        return {
          items: [],
          currentPage: 1,
          totalPageCount: 0,
        };
      },
      //데이터를 기반으로 변경할 속성	
      computed: {
        displayedItems() {
          return this.items;
        } 
      },
      mounted() { 
        //DOM 최초 로드 후 서버에서 데이터 조회
        this.fetchData();
      },
      methods: {
          //API Server에서 데이터 조회
    	  fetchData() {
    	    const url = 'http://localhost:9000/api/user/addressbook';
    			//2번 째 parameter(queryString)
    	    const params = { params: { currentPage: this.currentPage } };
    	    axios.get(url, params)
    	      .then((response) => {
    	        this.items = response.data.list;  
    	        this.totalPageCount = response.data.paging.totalPageCount;
    	        console.log(response);
    	      })
    	      .catch((error) => {
                //에러 처리
    	        console.log(error);
    	      });
    	  },
          //페이지 이동
    	  movePage(page) {
    	    if (page >= 1 && page <= this.totalPageCount) {
    	      this.currentPage = page;
              //페이지 이동 시 데이터 조회 
    	      this.fetchData(); 
    	    }
    	  },
    }
    
    };
    </script>
    
    <style>
    /* 추가적인 스타일은 필요에 따라 적용합니다. */
    </style>

    API Server측만 준비된다면 위 코드는 정상 작동한다.

    또한 CORS 추가 설정해줘야 한다.(아래 참조)

    2023.07.23 - [IT/development] - [Vue.js] ajax로 데이터 조회 예제(feat. axios)

     

    [Vue.js] ajax로 데이터 조회 예제(feat. axios)

    Vue.js에서 ajax로 서버에서 데이터 받아와서 테이블에 뿌리는 간단한 예제 cli 환경 기반이라 npm install로 vue.js cli를 설치해야 함(미리 설치한 상태라 동영상에는 설치과정 누락됨) npm install -g @vue/cl

    yaga.tistory.com

    샘플 게시판 동영상

    vue.js 사용 시 코드도 훨씬 많이 줄고 성능면에서도 훨씬 이점이 있다.

    vue.js의 v-for로 그리는 tr은 최초 DOM 로드 시에만 수행하고 페이지 이동 시에는

    tr은 DOM을 다시 그리지 않고 computed 속성의 값인 displayedItems의 값만 변경

    (최초 DOM 로드 시에만 반복문 수행)

    페이지 이동 시 렌더링 된 화면은 그대로고 template의 data 변경 시 computed에서 이를 감지해서 테이블에 변경된 데이터만 반영한다.

    vue.js나 React의 장점은 Reactivity라고 한다.(DOM의 데이터 변경을 감지해서 자동으로 다시 렌더링해준다.)

    vue.js를 알게된 이상 이제 프론트엔드 개발은 무조건 vue.js를 이용해서 개발할 예정이다.

    React는 vue.js를 어느정도 능숙하게 사용하게 되면 스터디 예정

    vue.js 공부한지 얼마 안되었고 개인 스터디 기록을 메모하는 공간이라 틀린점이 있을 수 있습니다.

    틀린 점 있을 경우 댓글 부탁드립니다.

    반응형