엘리스 SW 엔지니어 트랙 8주차 (팀프로젝트 pagenation 기능 구현 중 생긴 문제)

ChanghyeonO·2023년 4월 26일
0
const productListUrl = `${URI}/api/product/list?category=MUG`;


async function fetchData() {
    try {
        const response = await fetch(productListUrl);
        const jsonData = await response.json();
        if (Array.isArray(jsonData.data)) {
            const links = jsonData.data.map(data => `
            <a href="../list-detail/list-detail.html?id=${data._id}" class="item-box">
            <img src="${data.imgSrc}" alt="${data.name}" class="item-image">
            <h5 class="item-name">${data.name}</h5>
            <p class="item-price">${data.price}원 (부가세포함)</p>
          </a>
        `);
            itemArea.innerHTML = links.join('');
            mainTitle.innerHTML = jsonData.data[0].category;
            itemCount.innerHTML = `${jsonData.data.length}`;

            const rowsPerPage = 15;
            const rows = jsonData.data;
            const rowsCount = jsonData.data.length;
            const pageCount = Math.ceil(rowsCount / rowsPerPage);
            // 페이지 버튼들을 담을 컨테이너
            const pagenationNumbers = document.querySelector('.pagenation-numbers');

            //페이지네이션 생성
            for (let i = 1; i <= pageCount; i++) {
                pagenationNumbers.innerHTML += `<li><a href="">${i}</a></li>`;
            }

            const numberBtn = pagenationNumbers.querySelectorAll('a');
            console.log(numberBtn);

            numberBtn.forEach((item, index) => {
                item.addEventListener('click', (e) => {
                    e.preventDefault();

                    // console.log(index);
                    // 테이블 출력 함수
                    displayRaw(index);
                });
            });

            function displayRaw(index) {

                let start = index * rowsPerPage;
                let end = start + rowsPerPage;

                let rowArray = rows.slice(0, 15);


                for (let ra of rowArray) {
                    ra.style.display = "none";
                }

                let newRows = rowArray.slice(start, end);
                for (let nr of newRows) {
                    nr.style.display = 'block';
                }
                for (let nb of numberBtn) {
                    nb.classList.remove('active');
                }
                numberBtn[index].target.classList.add('active');
            }

            displayRaw(0);

        }
    } catch (error) {
        console.log(error);
    }
}

서버에서 json데이터를 받아와 15개씩 나눠 페이지를 구성해주는 페이지네이션을 만들었다.
그런데 위 방식대로 하면 display-none 속성을 적용할 수 없다는 에러가 출력이 된다.

json 데이터는 DOM 엘리먼트가 아니기 때문에 위 속성을 적용할 수 없었다.
대신 displayRaw 함수에서는 itemArea 엘리먼트 내부의 a 태그 엘리먼트를 가져와서 처리해주면 된다.

아래는 수정된 코드다.

const productListUrl = `${URI}/api/product/list?category=MUG`;

async function fetchData() {
    try {
        const response = await fetch(productListUrl);
        const jsonData = await response.json();
        if (Array.isArray(jsonData.data)) {
            const links = jsonData.data.map(data => `
            <a href="../list-detail/list-detail.html?id=${data._id}" class="item-box">
            <img src="${data.imgSrc}" alt="${data.name}" class="item-image">
            <h5 class="item-name">${data.name}</h5>
            <p class="item-price">${data.price}원 (부가세포함)</p>
          </a>
        `);
            itemArea.innerHTML = links.join('');
            mainTitle.innerHTML = jsonData.data[0].category;
            itemCount.innerHTML = `${jsonData.data.length}`;

            const rowsPerPage = 15;
            const rows = itemArea.querySelectorAll('.item-box');
            const rowsCount = rows.length;
            const pageCount = Math.ceil(rowsCount / rowsPerPage);
            // 페이지 버튼들을 담을 컨테이너
            const pagenationNumbers = document.querySelector('.pagenation-numbers');

            //페이지네이션 생성
            for (let i = 1; i <= pageCount; i++) {
                pagenationNumbers.innerHTML += `<li><a href="">${i}</a></li>`;
            }

            const numberBtn = pagenationNumbers.querySelectorAll('a');
            console.log(numberBtn);

            numberBtn.forEach((item, index) => {
                item.addEventListener('click', (e) => {
                    e.preventDefault();

                    // console.log(index);
                    // 테이블 출력 함수
                    displayRaw(index);
                });
            });

            function displayRaw(index) {

                let start = index * rowsPerPage;
                let end = start + rowsPerPage;

                for (let i = 0; i < rowsCount; i++) {
                    if (i >= start && i < end) {
                        rows[i].style.display = 'block';
                    } else {
                        rows[i].style.display = 'none';
                    }
                }

                for (let nb of numberBtn) {
                    nb.classList.remove('active');
                }
                numberBtn[index].classList.add('active');
            }

            displayRaw(0);

        }
    } catch (error) {
        console.log(error);
    }
}

페이지네이션 기능을 구현하고 나서 가격 높은 순, 낮은 순 각각 버튼을 만들고 sort 메서드를 사용해 구현했다.
버튼을 눌렀을 때 정렬 기능은 정상적으로 동작했지만 한 페이지에 15개씩 끊어서 표시해주는 기능이 동작하지 않아 모든 제품들이 한번에 표시되었다.아래는 문제의 코드다.

//상품 목록 불러오는 fetchData함수 선언
async function fetchData() {
    try {
        // fetch 함수를 사용해 상품 목록 데이터를 서버에서 가져옴
        const response = await fetch(productListUrl);
        const jsonData = await response.json();

        let jsonDataData = jsonData.data;

        document.querySelector('.sorting-low-price-button').addEventListener('click', function () {
            // 가격이 낮은 순서대로 정렬
            jsonDataData = jsonData.data.sort((a, b) => a.price - b.price);

            if (Array.isArray(jsonDataData)) {

                const links = jsonDataData.map(data => `
                    <a href="../list-detail/list-detail.html?id=${data._id}" class="item-box">
                    <img src="../../assets/product-imgs/${data._id}.jpeg" alt="${data.name}" class="item-image">
                    <h5 class="item-name">${data.name}</h5>
                    <p class="item-price">${data.price}원<br>(부가세포함)</p>
                </a>
            `);



                //상품 목록 UI 브라우저에 출력
                itemArea.innerHTML = links.join('');
                mainTitle.innerHTML = jsonData.data[0].category;
                itemCount.innerHTML = `${jsonData.data.length}`;
            }
        });

        document.querySelector('.sorting-high-price-button').addEventListener('click', function () {
            // 가격이 높은 순서대로 정렬
            jsonDataData = jsonData.data.sort((a, b) => b.price - a.price);

            if (Array.isArray(jsonDataData)) {

                const links = jsonDataData.map(data => `
                    <a href="../list-detail/list-detail.html?id=${data._id}" class="item-box">
                    <img src="../../assets/product-imgs/${data._id}.jpeg" alt="${data.name}" class="item-image">
                    <h5 class="item-name">${data.name}</h5>
                    <p class="item-price">${data.price}원<br>(부가세포함)</p>
                </a>
            `);



                //상품 목록 UI 브라우저에 출력
                itemArea.innerHTML = links.join('');
                mainTitle.innerHTML = jsonData.data[0].category;
                itemCount.innerHTML = `${jsonData.data.length}`;
            }
        });

        //상품 목록 데이터가 배열일 경우, 상품 목록 UI 생성
        if (Array.isArray(jsonDataData)) {

            const links = jsonDataData.map(data => `
                <a href="../list-detail/list-detail.html?id=${data._id}" class="item-box">
                <img src="../../assets/product-imgs/${data._id}.jpeg" alt="${data.name}" class="item-image">
                <h5 class="item-name">${data.name}</h5>
                <p class="item-price">${data.price}원<br>(부가세포함)</p>
            </a>
        `);

            //상품 목록 UI 브라우저에 출력
            itemArea.innerHTML = links.join('');
            mainTitle.innerHTML = jsonData.data[0].category;
            itemCount.innerHTML = `${jsonData.data.length}`;

            //페이지네이션 구현
            const rowsPerPage = 18;
            const rows = itemArea.querySelectorAll('.item-box');
            const rowsCount = rows.length;
            const pageCount = Math.ceil(rowsCount / rowsPerPage);

            // 페이지 버튼들을 담을 컨테이너
            const pagenationNumbers = document.querySelector('.pagenation-numbers');

            //페이지네이션 버튼 생성
            for (let i = 1; i <= pageCount; i++) {
                pagenationNumbers.innerHTML += `<li><a href="">${i}</a></li>`;
            }

            //페이지 버튼 선택
            const numberBtn = pagenationNumbers.querySelectorAll('a');

            //각 페이지 버튼을 클릭했을 때 발생하는 이벤트 등록
            numberBtn.forEach((item, index) => {
                item.addEventListener('click', (e) => {
                    e.preventDefault();

                    //페이지 번호를 인자로 받아, 해당 페이지에 해당하는 상품 목록 출력
                    displayRaw(index);
                });
            });

            //각 페이지에 해당하는 상품 목록 출력하는 함수
            function displayRaw(index) {

                let start = index * rowsPerPage;
                let end = start + rowsPerPage;

                //for문으로 rows 배열 내의 상품 목록중 해당 페이지에 해당하는 상품 목록 출력
                for (let i = 0; i < rowsCount; i++) {
                    if (i >= start && i < end) {
                        rows[i].style.display = 'block';
                    } else {
                        rows[i].style.display = 'none';
                    }
                }
                //페이지 버튼 활성화 표시를 위해 active 클래스 적용하고 이전 페이지 버튼의 활성화 클래스 제거
                for (let nb of numberBtn) {
                    nb.classList.remove('active');
                }
                numberBtn[index].classList.add('active');
            }

            displayRaw(0);


            return

        }
    } catch (error) {
        console.log(error);
    }
}

fetchData();

아직까지 기능구현에만 중점을 둔 나머지, 중복되는 코드들도 많고, 때문에 가독성이 너무 떨어진다.
여튼 현재 문제는 그것보다 기능이 정상동작이 안되는 거니, 일단 문제를 살펴보자,,, 정말 간단한 문제인데 한참동안 고민했던 것 같다.
정렬 버튼 또한 페이지네이션 코드를 넣어줬어야 했는데, 기본 출력 값에만 페이지네이션 코드를 넣어주었다. 이것가지고 몇시간 동안 끙끙대면서 고민한 나 제법 바보일지도,,,,?😇
여튼 해당 코드를 이벤트마다 다 붙여넣어줬더니 정상동작했다. 다만, 위에서 얘기한 것 처럼 중복되는 코드들이 많았는데, 페이지네이션 코드를 두번이나 더 넣어줬더니 정말 더러운 코드가 완성되어버렸다.... 누덕쓰~
보기 너무 싫어 중복되는 코드는 함수로 묶어 호다닥 빼버렸다. 아래는 오류도 고치고 나름 깔끔해진 코드.

//상품 목록 불러오는 fetchData함수 선언
async function fetchData() {
    try {
        // fetch 함수를 사용해 상품 목록 데이터를 서버에서 가져옴
        const response = await fetch(productListUrl);
        const jsonData = await response.json();

        let jsonDataData = jsonData.data;

        function innerData() {
            const links = jsonDataData.map(data => `
            <a href="../list-detail/list-detail.html?id=${data._id}" class="item-box">
            <img src="../../assets/product-imgs/${data._id}.jpeg" alt="${data.name}" class="item-image">
            <h5 class="item-name">${data.name}</h5>
            <p class="item-price">${data.price}원<br>(부가세포함)</p>
        </a>
          `);
            //상품 목록 UI 브라우저에 출력
            itemArea.innerHTML = links.join('');
            mainTitle.innerHTML = jsonData.data[0].category;
            itemCount.innerHTML = `${jsonData.data.length}`;
            // 페이지네이션 구현
            const rowsPerPage = 15; // 한 페이지에 보여줄 상품 개수
            const rows = itemArea.querySelectorAll('.item-box'); // 상품 목록 영역에서 'item-box' 클래스를 가진 요소들을 모두 가져와서 배열로 저장
            const rowsCount = rows.length; // 상품 목록의 총 개수
            const pageCount = Math.ceil(rowsCount / rowsPerPage); // 총 페이지 수 계산
            // 페이지 버튼들을 담을 컨테이너
            const pagenationNumbers = document.querySelector('.pagenation-numbers');
            // 이전 페이지네이션 버튼 삭제
            pagenationNumbers.innerHTML = '';
            // 페이지네이션 버튼 생성
            for (let i = 1; i <= pageCount; i++) {
                pagenationNumbers.innerHTML += `<li li > <a href="">${i}</a></li > `; // 페이지네이션 버튼을 생성하고, 버튼의 숫자는 i 값으로 할당
            }
            // 페이지 버튼 선택
            const numberButton = pagenationNumbers.querySelectorAll('a');
            // 각 페이지 버튼을 클릭했을 때 발생하는 이벤트 등록
            numberButton.forEach((item, index) => {
                item.addEventListener('click', (e) => {
                    e.preventDefault();
                    // 페이지 번호를 인자로 받아, 해당 페이지에 해당하는 상품 목록 출력
                    displayRaw(index);
                });
            });
            // 각 페이지에 해당하는 상품 목록 출력하는 함수
            function displayRaw(index) {
                let start = index * rowsPerPage; // 해당 페이지의 시작 인덱스 계산
                let end = start + rowsPerPage; // 해당 페이지의 끝 인덱스 계산

                // for문으로 rows 배열 내의 상품 목록 중 해당 페이지에 해당하는 상품 목록 출력
                for (let i = 0; i < rowsCount; i++) {
                    if (i >= start && i < end) {
                        rows[i].style.display = 'block'; // 해당 페이지의 상품 목록은 보이도록 설정
                    } else {
                        rows[i].style.display = 'none'; // 해당 페이지가 아닌 상품 목록은 숨기도록 설정
                    }
                }

                // 페이지 버튼 활성화 표시를 위해 active 클래스 적용하고 이전 페이지 버튼의 활성화 클래스 제거
                for (let nb of numberButton) {
                    nb.classList.remove('active');
                }
                numberButton[index].classList.add('active'); // 현재 페이지 버튼에 active 클래스 적용
            }

            displayRaw(0); // 첫 번째 페이지의 상품 목록을 먼저 보여줌
        }

        document.querySelector('.sorting-low-price-button').addEventListener('click', function () {
            // 가격이 낮은 순서대로 정렬
            jsonDataData = jsonData.data.sort((a, b) => a.price - b.price);
            if (Array.isArray(jsonDataData)) {
                innerData()
            }
        });

        document.querySelector('.sorting-high-price-button').addEventListener('click', function () {
            // 가격이 높은 순서대로 정렬
            jsonDataData = jsonData.data.sort((a, b) => b.price - a.price);
            if (Array.isArray(jsonDataData)) {
                innerData()
            }
        });

        //상품 목록 데이터가 배열일 경우, 상품 목록 UI 생성
        if (Array.isArray(jsonDataData)) {
            innerData()
        }
    } catch (error) {
        console.log(error);
    }
}

fetchData();
profile
꾸준한 기록을 통해, 좋은 개발자가 되겠습니다.

2개의 댓글

comment-user-thumbnail
2023년 5월 4일

와우 프로젝트 진행하면서 깨달음까지!

1개의 답글