모듈패턴 개발

이동언·2024년 8월 14일

new world

목록 보기
27/62
post-thumbnail

8.14(수)

1. 모듈패턴

👉 프로젝트의 코드 단위를 명확하게 분리하고 구성된다.
👉 클로저와 즉시 실행함수가 합쳐져 싱글턴같은 기능을 할 수 있다.

1-1. sample2.js

const sampleService = (function() { //즉시 실행함수 (예전버전) - 무조건 한번은 실행

console.log("-----------------")

let count = 0 //클로저 때문에 외부에서 접근할수가 없다.

const inc = () => { //inc와 dec만 count를 접근할수 있다.
    console.log("inc")
    count++
    return count
}

const dec = () => {
    console.log("dec")
    count --
    return count
}

return {inc,dec} //객체로 다이렉트로 리턴

1-2. index.html

<script src="sample2.js"></script>

<script>

const service = sampleService

console.log(service.inc())
console.log(service.dec())

const service2 = sampleService
// 여기서 service2를 한번더 만들더라도 새롭게 생성이 아닌 기존의 service와 값이 공유된다.
// 이 말뜻이 즉시 실행함수는 한번밖에 사용할수 없다.

console.log(service2.inc())
console.log(service2.dec())

</script>

👉 하지만 이렇게 즉시 실행함수와 클로저를 사용하는것은 옛날 방식으로 export를 이용해서 모듈패턴과 같은 효과를 낼 수 있다.




1-3. html / js

<script type="module">

import {inc,dec} from './sample2.js'

console.log(inc())
console.log(inc())

</script>

let count = 0 // export를 하지 않아서 외부에서 접근할수 없다.

export const inc = () => { //inc와 dec만 count를 접근할수 있다.
console.log("inc")
count++
return count
}

export const dec = () => {
console.log("dec")
count--
return count
}




2. 모듈형개발 menu cart프로젝트

2-1. product.js


const list = [
    {pno:1, pname:'아메리카노', price:1500},
    {pno:2, pname:'라떼', price:2500},
    {pno:3, pname:'아이스', price:2000},
]

export const getList = () => list
// list를 가져오는 함수

export const findMenu = (pno) => list.find(menu => menu.pno === pno)
// 매개변수 (pno)와, 해당 코드 제일 마지막에있는 pno는 html에서 이벤트가 발생할때 전달되는 pno와 같은것.
// menu는 const list의 객체라고 임의로 정해놓은것, 결국에  list의 pno값과 비교하는것임.




2-2. cart.js

    let cartArr=[]

    export function addCart(product,callback) { // product에는 내가 선택한 객체의 정보들이 들어간다.

        const findProduct = cartArr.find(cart => cart.pno === product.pno) // cartArr 배열내부에 있던 pno와 지금 내가 선택한 pno가 같은게 있는지

        if(findProduct){
            findProduct.qty += 1 // cart내부에 기존에 있던 물건이 들어온다면 수량만 증가
        }else{
            cartArr.push({...product, qty:1}) // cart 내부에 새로운 물건이 들어온다면, product객체 그대로 넣고, 수량을 1로 해서 넣는다.
        }
        if(callback){ //함수 호출할때 addCart(매개변수1,매개변수2) 가 있으면 callback이 실행되고, addCart(매개변수1) 만 해당하면 callback이 호출 안됨.
                        // callback 매개변수에서는 cartArr를(현재상태) 전달해줄게
            callback(cartArr)
        }
    }

    export function changeQty(pno, job, callback){
        const findProduct = cartArr.find(cart => cart.pno === pno) // 내가 선택한 pno와 cart에 담겨있는 pno가 같은지 확인

        if(job === 'plus'){
            findProduct.qty += 1 
          // 내가 확인했던 pno의 물건에 대한 개수를 변경
         
        }else if(job === 'minus'){
            findProduct.qty -= 1
            cartArr = cartArr.filter(cart => cart.qty>0) // cartArr에서 개수가 양수인 것만 filter하자
        }
    if(callback){
        callback(cartArr)
    }
}

📌 상태저장 : 장바구니의 상태를 저장하는 cartArr배열

📌 cart.qty+=1 를 하지않는 이유
👉 cart는 배열이므로 length와 같은 속성은 가질수 있지만, pno, pname와 같은 요소에 대한 직접적인 속성은 가지고 있지 않다.
고로, cart.qty 같은 코드는 존재하지 않는다.

👉 findProduct는 cart배열요소의 참조객체이므로 객체의 속성을 직접 수정할 수 있으므로 findProduct.qty와 같은 코드가 사용가능하다.



2-3. index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <div id="app">
      <div class="menuDiv">
        <ul></ul>
      </div>
      <div class="cartDiv">
        <ul></ul>
      </div>
    </div>



    <script type="module">
      import {getList, findMenu} from './product.js'
      import {addCart, changeQty} from './cart.js'

      const menuList = getList()


      const menuUL = document.querySelector('.menuDiv ul')
      const cartUL = document.querySelector('.cartDiv ul')

      menuUL.innerHTML = //함수로 만들지 않고 맨처음 list로 보여줘야 하므로 direct로 넣어줌
              menuList.map(menu =>
                      `<li>
                       <div data-pno="${menu.pno}"> <!-- 클릭했을때 해당 menu.pno값을 data-pno 속성에 넣기 위함 -->
                          ${menu.pno}
                          ${menu.pname}
                          ${menu.price}
                       </div>
                          </li>`).join('')

      menuUL.addEventListener('click',e=>{
        const target = e.target
        const pno = target.dataset.pno //data-pno속성에 저장된 pno의 번호를 읽기 위함
        const menu = findMenu(parseInt(pno)) // 만들어놓은 findMenu함수에 pno값을 넣어서 pno에 해당되는 pname, price를 알 수 있다.
                                            // 변수 menu에는 선택한 pno에 대한 list들의 전체 객체값들을 알 수 있다.
        if(!menu){
          return
        }
        addCart(menu,refreshCart) // addCart함수를 선언할때 addCart(product,callback) 이라고 선언했는데, 여기서 menu가 product의 매개변수로 가게되고,
                                  // refreshCart가 callback이 있다는것을 알려주는 역할이 되는것이다. 만약 함수호출시 addCart(menu)만 있다면 callback이 없는것.
      },false)

     const refreshCart = (cartList) =>{ // callback을 통해서 cartArr을 인자로 받아서 cartList에 cartArr이 들어가는것. 그러므로 우리는 cartList = getCart()와 같은 함수를 쓸 필요가 없다.
        cartUL.innerHTML = cartList.map( //map을 통해 cartList(cartArr)에 해당되는 배열을 새로 구성하는 역할
                cart => `<li>
                         <div data-pno=${cart.pno}>
                         ${cart.pno}
                         ${cart.pname}
                         ${cart.price}
                         ${cart.qty}
                         </div>
                         <div>
                         <button data-pno="${cart.pno}" data-job="plus">+</button>
                         <button data-pno="${cart.pno}" data-job="minus">-</button>
                         </div>
                         </li>`).join('')

     }

     cartUL.addEventListener('click',e=>{
       const target = e.target
       const pno = target.dataset.pno
       const job = target.dataset.job

       changeQty(parseInt(pno),job,refreshCart)

     },false)

    </script>


  </body>
</html>

📌 렌더링 : refreshCart함수를 통해 장바구니의 상태를 반영하여 HTML을 업데이트
👉 cartList라는 매개변수에 들어가는 값은 cart.js에서 선언한 callback함수의 return값인 cart 배열이 들어가게된다.



3. 컴포넌트

📌 화면(HTML) : 컴포넌트의 웹 페이지인 시각적 부분을 정의. 보통 HTML과 CSS를 사용하여 레이아웃과 스타일을 설정한다.

📌 이벤트 처리 : 사용자와의 상호작용을 처리하는 이벤트 핸들러를 포함한다. 예를 들어, 버튼클릭, 입력 변경등 이벤트를 처리하여 사용자 입력에 응답한다. 보통 javascript를 사용하여 구현한다.

📌 상태 데이터 : 상태는 컴포넌트의 데이터와 그 데이터가 어떻게 변하는지 정의하고, 사용자 입력, 서버응답등 다양한 소스에서 변경이 될 수 있다.

🌳 현재 우리 코드에서는 UI 컴포넌트가 HTML 내부의 menuDiv, cartDiv가 담당.
🌳 로직 컴포넌트중 데이터처리에 해당되는 product.js, cart.js에서 메뉴 데이터와 장바구니 데이터를 관리
🌳 로직 컴포넌트중 이벤트 및 UI 업데이트는 HTML내부의 script태그가 사용자 이벤트 처리기능.




4. 비동기프로그래밍

👉 코드가 한 번에 하나씩 순차적으로 출력되는것이 아닌, 여러 작업을 동시에 진행할 수 있도록 유동적으로 사용가능한 프로그래밍

console.log("Start");

function asyncOperation() {
    setTimeout(() => {
        console.log("Inside setTimeout");
    }, 1000); // 1초 후에 실행
}

asyncOperation();

console.log("End");

👉 일반적 함수호출시에는 start -> inside setTimeout -> end 순서로 출력이 될텐데, 비동기 함수인 setTimeout을 사용하여 start -> end -> inside setTimeout 순서로 출력이된다.

0개의 댓글