공시자료 리스트 페이지 페이지네이션 + 검색 필터링 + 페이지네이션
깃헙에 올리니 api 커스텀 부분으로 인해 데이터를 못가져오는 거 같다.
❗❗❗ 만들어진 전체 코드에서 부분부분 가져온 내용이라 전체적인 스크립트 내용 확인이 필요하다면 깃에서 확인하면 된다 [dejeong 깃허브 주소]
연습해보고 싶다면 링크를 참고하여 연습해보자 [참고]
데이터 기본 세팅
// OpenDart API 사용을 위한 스크립트
const openDartApi = (page) => {
// API 요청에 필요한 파라미터
const crtfc_key = "키값";
const corp_code = "회사코드";
const bgn_de = startDate;
const end_de = endDate;
const page_count = itemsPerPage;
const page_no = page || 1;
const pblntf_ty = selectedPblntfTy && selectedPblntfTy !== "none" ? `&pblntf_ty=${selectedPblntfTy}` : '';
// API 요청 URL
const localUrl = `https://cors-anywhere.herokuapp.com/https://opendart.fss.or.kr/api/list.json?crtfc_key=${crtfc_key}&corp_code=${corp_code}&bgn_de=${bgn_de}&end_de=${end_de}&page_count=${page_count}&page_no=${page_no}${pblntf_ty}`;
// Axios를 사용하여 API 요청
axios
.get(localUrl)
.then((response) => {
const object = JSON.parse(response.data.response);
if (object.message === "정상") {
// 정상적인 응답인 경우 테이블과 페이지네이션을 업데이트
displayTable(object.list, object.total_count);
displayPagination(object.total_page);
} else {
// 정상적이지 않은 경우 경고 메시지 출력 및 홈페이지로 이동
if (object.message !== "조회된 데이타가 없습니다.") {
alert("비정상적인 접근입니다.");
location.href = "/";
} else {
// 검색 결과가 없는 경우
displayTable([], 0);
displayPagination(0);
}
}
})
.catch((error) => {
console.log(`error ${error}`);
});
}
표 형식으로 출력하기
let headerSecLang; // headerSecLang 전역 변수로 선언
// 데이터 표시 테이블 업데이트 함수 > innderHTML 사용하게 되면 보안에 취약해서 사용 지양.
const displayTable = (data, totalCount) => {
const table = document.createElement("table");
const headerRow = table.insertRow(0);
headerSecLang = document.querySelector('.hd_select_lang').textContent;
// 각 언어에 따른 헤더 텍스트 정의
let headers;
if (headerSecLang.indexOf('KOR') != -1) {
headers = ['번호', '제목', '제출인', '등록일', '비고'];
} else if (headerSecLang.indexOf('ENG') != -1) {
headers = ['Number', 'Title', 'Submitter', 'Date', 'Remarks'];
} else if (headerSecLang.indexOf('CHN') != -1) {
headers = ['编号', '标题', '提交者', '日期', '备注'];
} else if (headerSecLang.indexOf('JPN') != -1) {
headers = ['番号', 'タイトル', '提出者', '日付', '備考'];
} else if (headerSecLang.indexOf('VTN') != -1) {
headers = ['Số thứ tự', 'Tiêu đề', 'Người gửi', 'Ngày', 'Ghi chú'];
} else if (headerSecLang.indexOf('RUS') != -1) {
headers = ['Номер', 'Заголовок', 'Податель', 'Дата регистрации', 'Примечания'];
} else if (headerSecLang.indexOf('SPN') != -1) {
headers = ['Número', 'Título', 'Remitente', 'Fecha', 'Observaciones'];
} else {
// 기본 언어 설정
headers = ["번호", "제목", "제출인", "등록일", "비고"];
}
// 테이블 헤더 생성
headers.forEach(headerText => {
const th = document.createElement("th");
th.textContent = headerText;
headerRow.appendChild(th);
});
// 데이터 표시
if (data.length > 0){
data.forEach((item, index) => {
const row = table.insertRow(index + 1);
const number = (currentPage - 1) * itemsPerPage + index + 1; // 전체 개수 기준으로 번호 계산
const cells = [
number,
item.report_nm, // 보고서 제목
item.flr_nm, // 제출인
formatDateString(item.rcept_dt), // 날짜 형식 변경
item.rm, // 비고
];
// 행에 셀 추가
cells.forEach((cellData, cellIndex) => {
const cell = row.insertCell(cellIndex);
cell.textContent = cellData;
// "제목" 셀에 클릭 이벤트 리스너 추가
if (cellIndex === 1) {
cell.style.cursor = "pointer";
cell.addEventListener("click", () => {
openDartLink(item.rcept_no);
});
}
});
});
} else { // 검색 결과가 없을 때
const noResultRow = table.insertRow(1);
const noResultCell = noResultRow.insertCell(0);
noResultCell.colSpan = headers.length;
// 각 언어에 따른 텍스트 정의
if (headerSecLang.indexOf('KOR') != -1) {
noResultCell.textContent = "검색 결과가 없습니다.";
}else if (headerSecLang.indexOf('ENG') != -1) {
noResultCell.textContent = "No results found.";
} else if (headerSecLang.indexOf('CHN') != -1) {
noResultCell.textContent = "没有找到结果。";
} else if (headerSecLang.indexOf('JPN') != -1) {
noResultCell.textContent = "検索結果はありません。";
} else if (headerSecLang.indexOf('VTN') != -1) {
noResultCell.textContent = "Không có kết quả nào.";
} else if (headerSecLang.indexOf('RUS') != -1) {
noResultCell.textContent = "Результаты поиска отсутствуют.";
} else if (headerSecLang.indexOf('SPN') != -1) {
noResultCell.textContent = "No se encontraron resultados.";
} else {
// 기본 언어 설정
noResultCell.textContent = "검색 결과가 없습니다.";
}
}
// 기존 테이블 삭제 및 새로운 테이블 추가
const dataTable = document.getElementById("data-table");
dataTable.innerHTML = "";
dataTable.appendChild(table);
}
페이지네이션 추가
// 페이지네이션 함수 innerHTML 사용 X -> 문자열 파싱이 없어 효율적임
const displayPagination = (totalPages) => {
const pagination = document.getElementById("pagination");
pagination.classList.add("list_pagination");
while (pagination.firstChild) {
pagination.removeChild(pagination.firstChild);
}
const maxButtonsToShow = 5;
let startPage = Math.max(1, currentPage - Math.floor(maxButtonsToShow / 2));
let endPage = Math.min(totalPages, startPage + maxButtonsToShow - 1);
// startPage 및 endPage 계산을 조정합니다.
if (endPage - startPage + 1 < maxButtonsToShow) {
startPage = Math.max(1, endPage - maxButtonsToShow + 1);
}
// 마지막 페이지가 표시된 버튼에 포함되도록 보장합니다.
if (endPage === totalPages) {
startPage = Math.max(1, endPage - maxButtonsToShow + 1);
}
// "<<" (첫 페이지) 버튼 추가
const firstPageButton = document.createElement("li");
firstPageButton.classList.add("page_prev", "page_btn");
firstPageButton.addEventListener("click", () => {
currentPage = 1;
openDartApi(currentPage);
});
pagination.appendChild(firstPageButton);
// "<" (이전 페이지) 버튼 추가
const previousPageButton = document.createElement("li");
previousPageButton.classList.add("page_first", "page_btn");
previousPageButton.addEventListener("click", () => {
if (currentPage > 1) {
currentPage--;
openDartApi(currentPage);
}
});
pagination.appendChild(previousPageButton);
// 페이지 버튼 생성
for (let i = startPage; i <= endPage; i++) {
const pageButton = document.createElement("li");
pageButton.textContent = i;
// 현재 페이지에 "on" 클래스 추가
if (i === currentPage) {
pageButton.classList.add("on");
}
pageButton.addEventListener("click", () => {
currentPage = i;
openDartApi(currentPage);
});
pagination.appendChild(pageButton);
}
// ">" (다음 페이지) 버튼 추가
const nextPageButton = document.createElement("li");
nextPageButton.classList.add("page_next", "page_btn");
nextPageButton.addEventListener("click", () => {
if (currentPage < totalPages) {
currentPage++;
openDartApi(currentPage);
}
});
pagination.appendChild(nextPageButton);
// ">>" (마지막 페이지) 버튼 추가
const lastPageButton = document.createElement("li");
lastPageButton.classList.add("page_last", "page_btn");
lastPageButton.addEventListener("click", () => {
currentPage = totalPages;
openDartApi(currentPage);
});
pagination.appendChild(lastPageButton);
}
기간 검색 + 데이터피커 날짜 검색
$(function() {
$( "#startDate" ).datepicker({
dateFormat: 'yy.mm.dd'
});
$( "#endDate" ).datepicker({
dateFormat: 'yy.mm.dd'
});
$('select').niceSelect(); // 나이스 셀렉트 세팅
});
// 다른 옵션인 경우 startDate 설정을 담당하는 함수(셀렉트 옵션에 따른 날짜 계산)
const setStartDateByOption = (option, today) => {
switch (option) {
case "1":
return new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
case "3":
return new Date(today.getFullYear(), today.getMonth() - 3, today.getDate());
case "6":
return new Date(today.getFullYear(), today.getMonth() - 6, today.getDate());
case "12":
return new Date(today.getFullYear() - 1, today.getMonth(), today.getDate());
case "all":
return new Date("1999-01-01");
default:
return today;
}
};
// 선택한 기간에 따라 시작일과 종료일 설정
const searchByPeriod = () => {
const selectedBox = document.getElementById("period");
const selectedOption = selectedBox.value;
if (selectedOption === "custom") {
// "직접 선택" 옵션을 선택한 경우
const selectedStartDate = $("#startDate").datepicker("getDate");
const selectedEndDate = $("#endDate").datepicker("getDate");
// 밖에 있는 datepicker 설정 지우기
$("#startDate").datepicker("destroy");
$("#endDate").datepicker("destroy");
// "직접 선택" 옵션인 경우에만 onSelect 함수 추가
$("#startDate").datepicker({
dateFormat: 'yy.mm.dd',
onSelect: function () {
searchByPeriod();
}
});
$("#endDate").datepicker({
dateFormat: 'yy.mm.dd',
onSelect: function () {
searchByPeriod();
}
});
$('.selectDateBox').addClass('on'); //직접 선택인 경우에만 on 추가
if (selectedStartDate && selectedEndDate) {
startDate = formatDateToYyyymmdd(selectedStartDate);
endDate = formatDateToYyyymmdd(selectedEndDate);
} else {
alert("시작일과 종료일을 선택하세요.");
return; // 날짜가 선택되지 않았을 경우 함수 종료
}
}else{
// 다른 옵션인 경우
const today = new Date();
endDate = formatDateToYyyymmdd(today);
startDate = formatDateToYyyymmdd(setStartDateByOption(selectedOption, today));
// 날짜 형식 변환 (YYYYMMDD -> YYYY.MM.DD)
startDateStr = startDate.replace(/(\d{4})(\d{2})(\d{2})/, '$1.$2.$3');
endDateStr = endDate.replace(/(\d{4})(\d{2})(\d{2})/, '$1.$2.$3');
// 변환된 값 input 노출
$("#startDate").val(startDateStr);
$("#endDate").val(endDateStr);
$('.selectDateBox').removeClass('on'); //직접 선택인 경우에만 on 추가
}
// 캘린더 초기화
$("#datepicker").datepicker("setDate", null);
}
날짜 형식 변환
파라미터 값 변경과 등록일 테이블 노출로 인해 2가지 형식으로 변경
// url에 들어가는 날짜를 yyyymmdd 형식으로 변환하는 함수 > 파라미터 값 변경을 위함
const formatDateToYyyymmdd = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}${month}${day}`;
}
// 등록일 날짜 형식 변경 함수 > 등록일 출력을 위함
const formatDateString = (dateString) => {
// "yyyymmdd" 형식의 날짜를 "yyyy.mm.dd" 형식으로 변경
const year = dateString.substring(0, 4);
const month = dateString.substring(4, 6);
const day = dateString.substring(6, 8);
return `${year}.${month}.${day}`;
}
공시유형 선택에 따른 파라미터 값 변경
선택한 값에 따른 파라미터를 변수로 받아와 값을 변경
// 공시유형 선택 값 가져오기
const selectedPblntfTy = $("#periodCate").val();
// 검색 버튼을 눌렀을 때만 pblntf_ty 값을 설정 + 공시유형 선택 option일 때는 변수 빈칸
const pblntf_ty = selectedPblntfTy && selectedPblntfTy !== "none" ? `&pblntf_ty=${selectedPblntfTy}` : '';