본문 바로가기
Web/JAVASCRIPT

LocalStorage를 활용해서 장바구니 추가, 삭제 기능만들어보기

by print_soo 2023. 8. 3.

[미리보기]

결과물

 

위의 기능을 작동 시키려면 아래의 로직대로 하면 좋을 것 같다.  하나씩 천천히 해보자!

로직

 

                      💡 로직💡                      

  1. 버튼이 클릭된 셀의 상품 정보를 가져오기
  2. 해당 A 키 값으로 로컬스토리지에 저장된 값이 있는지 확인하기
  3. 해당 A 키 값으로 저장된 것이 없다면 바로 저장하기
  4. 해당 A 키 값으로 저장된 것이 있다면 다른 방법으로 저장하기
    1. 저장된 값 가져와서 JSON 파싱해서 새로운 배열 변수에 저장하기
    2. 저장된 배열에 push로 추가할 상품의 정보를 추가하기 
    3. 추가된 배열을 JSON으로 만들어서 다시 로컬 스토리지에 저장하기
  5. 장바구니 HTML 만들기
  6. 장바구니 HTML 페이지가 로드가 되었을 때 A 키 값으로 저장된 값이 없을 때의 HTML 추가하기
  7. 장바구니 HTML 페이지가 로드가 되었을 때 A 키 값으로 저장된 값이 있을 때의 HTML 추가하기
    1. A 키 값으로 저장된 값을 가져와서 새로운 배열에 저장하기
    2. 카드 HTML 추가하기 + 데이터 입력
  8. 장바구니 삭제기능 만들기
    1. splice를 사용해서 선택된 배열의 요소 삭제하기
    2. 삭제된 요소가 있는 배열 로컬 스토리지에 저장하기
    3. 저장된 배열 다시 가져와서 HTML 추가하기

                                                                            

 

 

1. 버튼이 클릭된 셀의 상품 정보 가져오기

 

$('.row').on('click', function (e) { //버튼 클릭이벤트를 사용하지 않고 그 상위 버전인 row의 클릭 이벤트를 사용한 이유는 append를 통해서 html이 추가 될경우 추가된 html의 버튼은 먹지 않는 현상이 있어 이벤트 버블링을 이용해서 진행함
    for (let i = 0; i < products.length+1;i++) {
        if (e.target == document.getElementsByClassName('buy')[i]) { //클릭한 버튼에 따라서 해당 되는 상품에 대한 정보를 가져옴
        	var beforeCartArray = [{ id: i, price: $('.buy').siblings('.price')[i].outerText, title: $('.buy').siblings('.title')[i].outerText, img: $('.buy').siblings('.w-100')[i].src}];
        }
    } 
});

 

해당 셀의 buy버튼이 클릭되면 해당 셀의 상품명과 가격을 알아야하는데 이 부분은 sibilings 함수로 알 수 있다.

💡 sibilings함수는 선택한 요소의 형제(sibling) 요소 중에서 지정한 선택자에 해당하는 요소를 모두 선택한다.

따라서 sibilings 함수로 선택된 버튼과 형제요소를 찾고 거기서 상품명과 상품가격을 찾아 beforeCartArray에  저장한다.

이 글을 읽어보면 상위 요소에 이벤트를 달았는 이유 설명되므로 생략한다.


2. 해당 A 키 값으로 로컬스토리지에 저장된 값이 있는지 확인하기

 

if (localStorage.getItem('cart') == null){ // 기존에 cart라는 이름으로 값이 저장되지 않았다면 바로 저장
    
} else { // 기존에 cart라는 이름으로 값이 저장되어 있는경우

}

 

3. 해당 A 키 값으로 저장된 것이 없다면 바로 저장하기

 

if (localStorage.getItem('cart') == null){ // 기존에 cart라는 이름으로 값이 저장되지 않았다면 바로 저장
    localStorage.setItem('cart', cartArrayIntoJson);
} else { // 기존에 cart라는 이름으로 값이 저장되어 있는경우
    
}

4. 해당 A 키 값으로 저장된 것이 있다면 다른 방법으로 저장하기

 

if (localStorage.getItem('cart') == null){ // 기존에 cart라는 이름으로 값이 저장되지 않았다면 바로 저장
    localStorage.setItem('cart', cartArrayIntoJson);
} else { // 기존에 cart라는 이름으로 값이 저장되어 있는경우
    
    var getCartArray = JSON.parse(localStorage.getItem('cart'));
    for (let j = 0; j < getCartArray.length; j++){
        if (getCartArray[j].id == i){ //가져온 배열 상품의 id와 선택한 상품의 i 값이 같다면 추가하지 않음.
            alert('이미 추가된 상품입니다.')
            return
        } 
    }

    getCartArray.push({ id: i, price: $('.buy').siblings('.price')[i].outerText, title: $('.buy').siblings('.title')[i].outerText, img: $('.buy').siblings('.w-100')[i].src});
    localStorage.setItem('cart', JSON.stringify(getCartArray));
    //1. json 파싱을 해서 배열을 가져온다. 
    //2. 해당 배열에 선택된 정보를 추가로 요소로 저장한다.
    //3. 추가된 배열을 다시 json으로 바꿔서 로컬저장소에 저장한다. 
    
}

 

저장된 스토리지가 JSON 형태이기 때문에 파싱해서 배열로 변경시켜 새로운 변수에 저장한다.

 

무지성으로 장바구니에 추가하는 것이 아니라 중복이 없는지 확인하기 위해서 스토리지에 저장된 배열을 가져와 해당 배열 요소의 id와 추가하려 상품의 id가 겹치지 않는지 확인한다. 중복이라면 return

 

만약 중복이 아니라면 파싱된 배열에 push로 새로운 요소를 추가시킨다. 그리고 해당 배열을 JSON으로 만들어 다시 스토리지에 저장한다.


5. 장바구니 HTML 만들기

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="stylesheet" href="cart.css">
    <title>Document</title>
</head>

<body>
    <div class="nav">
        <button id="backBtn" type="button" onclick="document.location.href='index.html'">
            <</button>
                <h3 style="padding-top: 10px;"><b>장바구니</b></h3>
                <button id="fakeBtn">></button>
    </div>

    <div class="products">

    </div>

    <script src="https://code.jquery.com/jquery-3.7.0.min.js"
        integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>

    <script>
       
    </script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous">
        </script>
</body>

</html>

6. 장바구니 HTML 페이지가 로드가 되었을 때 A 키 값으로 저장된 값이 없을 때의 HTML 추가하기

 

window.onload = function () {


    var afterCartArray = JSON.parse(localStorage.getItem('cart'))

    if (afterCartArray.length == 0) {
        var template = `<p style="text-align: center; padding: 20px; color: gray;">장바구니 담긴 상품이 없습니다.</p>`
        $('.products').append(template);

    } else {
    }
};

7. 장바구니 HTML 페이지가 로드가 되었을 때 A 키 값으로 저장된 값이 있을 때의 HTML 추가하기

 

var afterCartArray = JSON.parse(localStorage.getItem('cart'))

if (afterCartArray.length == 0) {
    var template = `<p style="text-align: center; padding: 20px; color: gray;">장바구니 담긴 상품이 없습니다.</p>`
    $('.products').append(template);
} else {
    afterCartArray.forEach((a) => {
    	createTempale(a);
    })

    function createTempale(item) {
         var template = `<div class="container" style="padding: 40px 150px 0px 150px;">
                        <div class="cardArea">
                            <div class="card mb-3" style="max-width: 540px;">
                                <div class="row g-0">
                                    <div class="col-md-4">
                                        <img src="${item.img}" class="img-fluid rounded-start" alt="...">
                                    </div>
                                    <div class="col-md-8">
                                        <div class="card-body">
                                            <h5 class="card-title">${item.title}</h5>
                                            <p class="card-text"><small class="text-muted">${item.price}</small></p>
                                         </div>
                                    </div>
                                </div>
                            </div>
                            <button class="btn btn-danger">삭제</button>
                        </div>
                    </div>`
        $('.products').append(template);
    }
}

 

로컬 스토리지에 저장된 배열을 파싱해서 새로운 배열에 넣고 해당 배열의 데이터를 바탕으로 새로운 HTML을 만들어 추가한다.

 


8. 장바구니 삭제기능 만들기

 

$('.products').on('click', function (e) {
    for (let i = 0; i < afterCartArray.length; i++) {
        if (e.target == document.getElementsByClassName('btn')[i]) {
            afterCartArray.splice(i, 1)
            localStorage.setItem('cart', JSON.stringify(afterCartArray));
                
            $('.products').html('');
            afterCartArray.forEach((a) => {
                createTempale(a);
            })

            if (afterCartArray.length == 0) {
                var template = `<p style="text-align: center; padding: 20px; color: gray;   ">장바구니 담긴 상품이 없습니다.</p>`
                $('.products').append(template);
            }
        } 
    }
});

삭제 버튼이 눌려지면 해당 버튼이 눌려진 id를 가진 배열의 인덱스를 찾아서 해당 요소를 splice로 삭제하고 삭제된 배열을 다시 로컬 스토리지에 저장한다. 그후 원래 있던 HTML을 삭제하고 새로운 HTML을 추가한다. 만약 모든 요소가 삭제되었다면 장바구니에 담긴 상품이 없다는 HTML을 추가해준다.

 

 

 

전체 코드

 

index.html

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="stylesheet" href="main.css">

    <title>Hello, world!</title>
</head>

<body>
    
    <div class="logo" style="text-align: center; padding-top: 10px;">
        <h2><b>무씬사</b></h2>
    </div>

    <form class="container my-5 form-group"">
        <button id="cartBtn" type="button" onclick="document.location.href='cart.html'">장바구니</button>
        <div class="input-group mb-3">
            <input type="number" class="form-control" placeholder="구매 가능한 최대 가격을 작성해주세요."
                aria-label="Recipient's username" aria-describedby="button-addon2">
            <button class="btn btn-outline-secondary" type="button" id="buttonSort">보기</button>
        </div>
        <p><b>정렬</b></p>
        <select class="form-select mt-2">
            <option>기본</option>
            <option>이름(오름차 순)</option>
            <option>이름(내림차 순)</option>
            <option>가격(오름차 순)</option>
            <option>가격(내림차 순)</option>
        </select>
    </form>

    <div class="container">
        <div class="row">

        </div>
    </div>
    <div class="container" style="text-align: center; padding-top: 30px;">
        <button class="btn btn-danger" id="moreBtn"">더보기</button>
    </div>


    <!-- JavaScript -->

    <script src="https://code.jquery.com/jquery-3.7.0.min.js"
        integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>


    <script>
        // PART: 변수
        var products = [
            { id: 0, price: 70000, title: 'Blossom Dress', img: 'https://via.placeholder.com/600' },
            { id: 1, price: 50000, title: 'Springfield Shirt', img: 'https://via.placeholder.com/600' },
            { id: 2, price: 60000, title: 'Black Monastery', img: 'https://via.placeholder.com/600' }
        ];

        var originalProduct = [
            { id: 0, price: 70000, title: 'Blossom Dress', img: 'https://via.placeholder.com/600' },
            { id: 1, price: 50000, title: 'Springfield Shirt', img: 'https://via.placeholder.com/600' },
            { id: 2, price: 60000, title: 'Black Monastery', img: 'https://via.placeholder.com/600' }
        ];

        // PART: 사용자의 입력값에 따라 정렬

        $('#buttonSort').on('click', function () {
            let maxPrice = $('.form-control').val()
            var underfive = products.filter(function (a) {
                return a.price < maxPrice
            })

            if (underfive.length == 0) {
                alert('해당 조건에 해당되는 상품이 없습니다.')

            } else {
                $('.row').html('');
                underfive.forEach((a) => {
                    createTempale(a);
                });
            }


        });


        // PART: 정렬 드롭다운 선택 옵션에 따라 정렬\
        $('.form-select').eq(0).on('input', function () {
            var selectedSort = $('.form-select').val();

            if (selectedSort == '이름(오름차 순)') {
                products.sort(function (a, b) {
                    if (a.title > b.title) return 1;
                    if (a.title < b.title) return -1;
                    if (a.title === b.title) return 0;
                });
                $('.row').html('');
                products.forEach((a) => {
                    createTempale(a);
                });


            } else if (selectedSort == '이름(내림차 순)') {
                products.sort(function (a, b) {
                    if (a.title < b.title) return 1;
                    if (a.title > b.title) return -1;
                    if (a.title === b.title) return 0;
                });
                $('.row').html('');
                products.forEach((a) => {
                    createTempale(a);
                });


            } else if (selectedSort == '가격(오름차 순)') {
                products.sort(function (a, b) {
                    return a.price - b.price;
                });
                $('.row').html('');
                products.forEach((a) => {
                    createTempale(a);
                });


            } else if (selectedSort == '가격(내림차 순)') {
                products.sort(function (a, b) {
                    return b.price - a.price;
                });
                $('.row').html('');
                products.forEach((a) => {
                    createTempale(a);
                });

            } else if (selectedSort == '기본') {
                $('.row').html('');
                originalProduct.forEach((a) => {
                    createTempale(a);
                });
            }
        })

        // PART: 더보기 버튼
        var moreBtnClickCount = 0;

        products.forEach((a) => {
            createTempale(a);
        });



        $('#moreBtn').on('click', function () {
            moreBtnClickCount += 1;
            $.get('https://codingapple1.github.io/js/more' + moreBtnClickCount + '.json').done(function (data) {

                data.forEach((a) => {
                    createTempale(a);
                    products.push(a);
                    originalProduct.push(a);
                });
                console.log('데이터 통신 성공')

            }).fail(function () {
                console.log('데이터 통신 실패')
                alert('데이터 통신에 실패하였습니다.')
            })
            if (moreBtnClickCount > 1) {
                $('#moreBtn').css('display', 'none');
            }
        });

        // PART: HTML 생성

        function createTempale(item) {
            var template = `<div class="col-sm-4" style = "padding-bottom: 20px;">
                    <img src="https://via.placeholder.com/600" class="w-100">
                    <h5 class="title">${item.title}</h5>
                    <p class="price">가격: ${item.price}</p>
                    <button class="buy" >buy</button>
                    </div>`
            $('.row').append(template);
        }

        // PART: Buy 버튼
        $('.row').on('click', function (e) { //버튼 클릭이벤트를 사용하지 않고 그 상위 버전인 row의 클릭 이벤트를 사용한 이유는 append를 통해서 html이 추가 될경우 추가된 html의 버튼은 먹지 않는 현상이 있어 이벤트 버블링을 이용해서 진행함
            for (let i = 0; i < products.length+1;i++) {
                if (e.target == document.getElementsByClassName('buy')[i]) { //클릭한 버튼에 따라서 해당 되는 상품에 대한 정보를 가져옴
                    var beforeCartArray = [{ id: i, price: $('.buy').siblings('.price')[i].outerText, title: $('.buy').siblings('.title')[i].outerText, img: $('.buy').siblings('.w-100')[i].src}];
                    var cartArrayIntoJson = JSON.stringify(beforeCartArray);
                    if (localStorage.getItem('cart') == null){ // 기존에 cart라는 이름으로 값이 저장되지 않았다면 바로 저장
                        localStorage.setItem('cart', cartArrayIntoJson);
                    } else { // 기존에 cart라는 이름으로 값이 저장되어 있는경우
                        
                        var getCartArray = JSON.parse(localStorage.getItem('cart'));
                        for (let j = 0; j < getCartArray.length; j++){
                            if (getCartArray[j].id == i){ //가져온 배열 상품의 id와 선택한 상품의 i 값이 같다면 추가하지 않음.
                                alert('이미 추가된 상품입니다.')
                                return
                            } 
                        }

                        getCartArray.push({ id: i, price: $('.buy').siblings('.price')[i].outerText, title: $('.buy').siblings('.title')[i].outerText, img: $('.buy').siblings('.w-100')[i].src});
                        localStorage.setItem('cart', JSON.stringify(getCartArray));
                        //1. json 파싱을 해서 배열을 가져온다. 
                        //2. 해당 배열에 선택된 정보를 추가로 요소로 저장한다.
                        //3. 추가된 배열을 다시 json으로 바꿔서 로컬저장소에 저장한다. 
                    }
                
                }
            } 
        });

        

    </script>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous">
        </script>

</body>

</html>

 

cart.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="stylesheet" href="cart.css">
    <title>Document</title>
</head>

<body>
    <div class="nav">
        <button id="backBtn" type="button" onclick="document.location.href='index.html'">
            <</button>
                <h3 style="padding-top: 10px;"><b>장바구니</b></h3>
                <button id="fakeBtn">></button>
    </div>


    <div class="products">

    </div>

    <script src="https://code.jquery.com/jquery-3.7.0.min.js"
        integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>

    <script>
        window.onload = function () {


            var afterCartArray = JSON.parse(localStorage.getItem('cart'))

            if (afterCartArray.length == 0) {
                var template = `<p style="text-align: center; padding: 20px; color: gray;">장바구니 담긴 상품이 없습니다.</p>`
                $('.products').append(template);
            } else {
                afterCartArray.forEach((a) => {
                createTempale(a);

                 })

                function createTempale(item) {
                var template = `<div class="container" style="padding: 40px 150px 0px 150px;">
                                    <div class="cardArea">
                                        <div class="card mb-3" style="max-width: 540px;">
                                            <div class="row g-0">
                                                <div class="col-md-4">
                                                    <img src="${item.img}" class="img-fluid rounded-start" alt="...">
                                                </div>
                                                <div class="col-md-8">
                                                    <div class="card-body">
                                                        <h5 class="card-title">${item.title}</h5>
                                                        <p class="card-text"><small class="text-muted">${item.price}</small></p>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                        <button class="btn btn-danger">삭제</button>
                                    </div>
                                </div>`
                $('.products').append(template);
                }

            }

            $('.products').on('click', function (e) {
                for (let i = 0; i < afterCartArray.length; i++) {
                    if (e.target == document.getElementsByClassName('btn')[i]) {
                        afterCartArray.splice(i, 1)
                        localStorage.setItem('cart', JSON.stringify(afterCartArray));
                            
                        $('.products').html('');
                        afterCartArray.forEach((a) => {
                            createTempale(a);
                        })

                        if (afterCartArray.length == 0) {
                            var template = `<p style="text-align: center; padding: 20px; color: gray;   ">장바구니 담긴 상품이 없습니다.</p>`
                            $('.products').append(template);
                        }
                    } 
                }
            });
        };




    </script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous">
        </script>
</body>

</html>

 

main.css

.buy {
    border: 0;
    padding: 5px 10px;
    border-radius: 5px;
    background-color: tomato;
    color: white;
    float: right;

}

#cartBtn {
    border: 0;
    border-radius: 5px;
    padding: 5px 10px;
    background-color: gray;
    color: white;
    margin-bottom: 10px;
    float: right;
}

 

cart.css 

.nav{
    display: flex;
    justify-content: space-between;
}

.cardArea {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

#backBtn {
    border: 0;
    background-color: transparent;
    color: black;
}
#fakeBtn {
    border: 0;
    background-color: transparent;
    color: white;
}