개발 기록/Javascript

[Javascript] 페이징 소스코드 - jQuery, Ajax, and Bootstrap5

JasonM 2023. 5. 22. 00:35
반응형

jQuery ajax를 통해 호출한 API의 JSON 응답 데이터를 jQuery로 html을 만들고 화면에 페이지 번호를 그려주는 javascript 공통 함수를 만들어 봤다. 현재까지 3개 웹사이트에 적용했는데 복사 붙여넣기만으로 아직까지는 별다른 수정 없이 잘 돌아가고 있다. 

 

동작방식

  1. Backend에서 데이터 목록의 페이징과 관련된 정보를 보내준다.
  2. 받은 정보와 html을 그려줄 target 등 필요한 정보를 initPagination javascript 함수에 parameter로 넣어서 호출한다.
  3. 화면이 그려진 이후 page 번호 클릭 시 원하는 페이지로 이동한다. (또는 ajax로 Backend에 다른 페이지의 데이터 요청) 

 

Backend - Java Spring Framework Controller 

@GetMapping("/board/{boardType}/list")
public ResponseEntity<Map<String, Object>> boardNoticeListAPI(@RequestParam(value="pg", required=true, defaultValue = "1") Integer pg, @PathVariable String boardType) {
    Map<String, Object> rtnMap = new HashMap<String, Object>();

    boolean isValidType = false;
    for (BoardType type : BoardType.values()) { 
        if(type.toString().equals(boardType)) {
            isValidType = true;
            break;
        }
    }

    if(!isValidType) 
        throw new RestResponseExcpetion("MsAP100", "입력값 오류", "No Board Definition");


    Page<Map<String, Object>> result = commonService.findAllByBoardType(pg, boardType);

    rtnMap.put("page_total", result.getTotalPages());
    rtnMap.put("total_element", result.getTotalElements());
    rtnMap.put("page_size", result.getSize());
    rtnMap.put("data", result.getContent());

    return new ResponseEntity<Map<String, Object>>(rtnMap, HttpStatus.OK);
}

SpringBoot로 Java 앱을 하나 만들고, 공지사항, FAQ, Q&A 등 비슷한 형태의 게시판 구조를 정의하고 게시판 타입에 따라 Dynamic 하게 처리하도록 구현했다.
 
 
 

JSON Response Body - 서버에서 보내주는 데이터 (예시)

{
    "data": [
        {
            "member_name": "테스터1",
            "member_id": 10044,
            "title": "게시물 제목",
            "board_id": 13,
            "board_type": "notice",
            "created_date": "2023-04-29T08:46:23.000+00:00",
            "updated_date": "2023-04-29T08:46:23.000+00:00",
            "view_count": 0,
            "status": 1
        }
    ],
    "page_total": 1,
    "total_element": 7,
    "page_size": 10
}

Spring Framework의 Page를 사용해서 DB 데이터를 조회했기 때문에, 글 목록 외 전체 페이지 수,  전체 글 개수, 한 페이지 당 출력되는 글 개수를 쉽게 받아 올 수 있다.


 

반응형


 

HTML 영역

<script>
var pg = 1;			//메인 리스트의 초기 page 번호
var pg_modal = 1;	//모달 창 내부 리스트의 초기 page 번호

$(document).ready(function(){
	loadBoardList();  //HTML이 로드되면 AJAX로 데이터 리스트를 요청한다.
});

function loadBoardList(){
	var rqBoardList = {};
	rqBoardList.pg = pg;
	rqBoardList.board_type = "notice";
			
	sendXMLHttpRequest("POST", "[API endpoint URL]", JSON.stringify(rqBoardList), sBoardList, COM_AjaxFailCallBack);
}

var sBoardList = function sUserListCallBack(list){
	$("#board_list").html("");
			
    for(var i = 0 ; i < list.data.length; i++){
        var listHTML = "";

        listHTML += "<tr class='pointer boardListRow bg-white' board_id='" + list.data[i].board_id + "' id='boardListRow_" + list.data[i].board_id + "' idx='" + i + "'>";
        listHTML += "	<td class='mb-3 align-middle'>" + list.data[i].board_id + "</td>";
        listHTML += "	<td class='mb-3 align-middle'>" + list.data[i].title + "</td>";
        listHTML += "	<td class='mb-3 align-middle'>" + list.data[i].name + "</td>";
        listHTML += "	<td class='mb-3 align-middle td-view-count'>" + list.data[i].view_count + "</td>";
        listHTML += "	<td class='mb-3 align-middle'>" + getKSTDateYYYYMMDDHHMM(list.data[i].created_date) + "</td>";
        listHTML += "</tr>";
        $("#board_list").append(listHTML);
    }
    
	if(list.data.length == 0){
		$("#board_list").append("<tr><td colspan=" + $("#thead").children().length + " class='text-center'>조회된 결과가 없습니다.</td></tr>");
	}

	initPagination(pg, list.page_total, 3, "pagination", "#");
}

//페이지 번호 클릭 시 실행하게 될 함수
function COM_PAGINATION_LINK(param_pg, target){
	if(target == "modal_pagination"){
		pg_modal = Number(param_pg);
    	loadSchoolList() //선택된 페이지 번호로 AJAX 요청
    }else{
    	pg = Number(param_pg);
    	loadBoardList() //선택된 페이지 번호로 AJAX 요청
    }
}

</script>

<body>
	<!-- 메인 리스트 영역 -->
	<div class="row bg-white">
		<div class="table-responsive">
            <table class="table mb-0">
                <caption id="total_element" class="text-secondary"></caption>
                <thead>
                    <tr id="thead" class="bg-white">
                        <th>번호</th>
                        <th style="min-width:40vw;">제목</th>
                        <th>작성자</th>
                        <th>조회수</th>
                        <th>상태</th>
                        <th>등록일</th>
                    </tr>
                </thead>
                <tbody id='board_list'>
                </tbody>
            </table>	
        </div>
    </div>
    <div class="row bg-white">
        <ul class="pagination justify-content-center" id="pagination"></ul>
    </div>
    
    
    <!-- Modal 리스트 영역 -->
	<div class="modal" id="schoolModal">
		<div class="modal-dialog modal-dialog-centered modal-lg modal-dialog-scrollable">
			<div class="modal-content">

				<!-- Modal Header -->
				<div class="modal-header">
					<h4 class="modal-title">학교 검색</h4>
					<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
				</div>
	
				<!-- Modal body -->
				<div class="modal-body text-center">
					<div class="input-group mb-3">
						<input type="text" class="form-control" placeholder="학교를 입력하세요." id="modal_search_school_name">
						<button class="btn btn-secondary btn-search-school" type="button">검색</button>
					</div>
					
					<div class="table-responsive">
						<table class="table table-hover">
							<thead>
								<tr>
									<th>학교명</th>
									<th>학교코드</th>
									<th>학교등급</th>
								</tr>
							</thead>
							<tbody id='school_list'>
							</tbody>
						</table>
					</div>
					
					<div class="row bg-white">
						<ul class="pagination justify-content-center" id="modal_pagination"></ul>
					</div>
				</div>
	
				<!-- Modal footer -->
				<div class="modal-footer">
					<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Close</button>
				</div>
	
			</div>
		</div>
	</div>
</body>
  • 서버에 API 요청하는 sendXMLHttpRequest 함수의 사용 방법은 Access Token 만료 시 자동 갱신해주는 소스 참고
  • 페이지 그려주는 함수 (initPagination) 호출
  • 페이지 번호 눌렀을 때 실행할 함수 정의 (이 부분은 각 페이지마다 다를 수 있으니, html에 작성하였음) 
  • 한 페이지에 페이징 처리가 2개 이상 필요 한 경우를 고려해서 각각의 page 번호를 별도로 관리. (pg 및 pg_modal) 

 

 

Javascript/jQuery (공통 함수)

var curPage = 1; //현재 페이지 번호
var totalPage; //전체 페이지 수
var pageGroup; //그룹당 페이지 수
var curPageGroup; //현재 페이지 그룹 번호

function initPagination(p_curPage, p_totalPage, p_pageGroup, target, url){
	curPage		= p_curPage;
	totalPage	= p_totalPage;
	pageGroup	= p_pageGroup;

	curPageGroup = Math.ceil(curPage / pageGroup);
	var st = ((curPageGroup-1) * pageGroup)+1;
	var ed = Math.min( (curPageGroup * pageGroup), totalPage);
	
		
	var pageHtml = "";
    
	pageHtml  += "<li class='page-item'><a class='page-link pointer text-secondary' tgt='" + target + "' pg='" + (curPage > 1 ? curPage-1 : 1) + "' style='padding:3.5px;'><span class='material-icons-outlined align-middle'>navigate_before</span></a></li>"
		
	for(var i = st; i <= ed; i++){
		if(i == curPage){
			pageHtml  += "<li class='page-item active'><a class='page-link text-white bg-secondary border-secondary'>" + i + "</a></li>"
		}else{
			pageHtml  += "<li class='page-item'><a class='page-link pointer text-secondary' tgt='" + target + "' pg='" + i + "'>" + i + "</a></li>"
		}
	}
		
	pageHtml  += "<li class='page-item'><a class='page-link pointer text-secondary' tgt='" + target + "' pg='" + (curPage < totalPage ? curPage+1 : totalPage) + "' style='padding:3.5px;'><span class='material-icons-outlined align-middle'>navigate_next</span></a></li>"
	
	$("#"+target).html(pageHtml);
}



$(document).on("click", ".page-link", function(){
	var pg = $(this).attr("pg");
	var tgt = $(this).attr("tgt");
	
	if(pg != undefined){
		COM_PAGINATION_LINK(pg, tgt);
	}
});
  • 위 부분을 공통(common.js) 스크립트로 등록
  • initiation 부분과 페이지 번호 클릭 이벤트 구현 

 

 

결과물

페이징 처리 결과 화면

 

 

사용된 라이브러리

jQuery + Bootstrap5 + Google Material(icon)

 

 

 

[Javascript] - Access Token 만료 시 자동 갱신해주는 소스

 

Access Token 만료 시 자동 갱신해주는 소스

세션을 이용하지 않고 JWT Access Token을 이용해서 서버와 클라이언트의 소스가 완전히 분리되어 있는 웹 사이트의 경우 웹 화면에서 서버로 API를 보낼 때마다 Access Token을 담아서 보내야 하는데,

jsonm.tistory.com

 

 

반응형