👉 프로젝트의 코드 단위를 명확하게 분리하고 구성된다.
👉 클로저와 즉시 실행함수가 합쳐져 싱글턴같은 기능을 할 수 있다.
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} //객체로 다이렉트로 리턴
<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를 이용해서 모듈패턴과 같은 효과를 낼 수 있다.
<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
}
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값과 비교하는것임.
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와 같은 코드가 사용가능하다.
<!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 배열이 들어가게된다.
📌 화면(HTML) : 컴포넌트의 웹 페이지인 시각적 부분을 정의. 보통 HTML과 CSS를 사용하여 레이아웃과 스타일을 설정한다.
📌 이벤트 처리 : 사용자와의 상호작용을 처리하는 이벤트 핸들러를 포함한다. 예를 들어, 버튼클릭, 입력 변경등 이벤트를 처리하여 사용자 입력에 응답한다. 보통 javascript를 사용하여 구현한다.
📌 상태 데이터 : 상태는 컴포넌트의 데이터와 그 데이터가 어떻게 변하는지 정의하고, 사용자 입력, 서버응답등 다양한 소스에서 변경이 될 수 있다.
🌳 현재 우리 코드에서는 UI 컴포넌트가 HTML 내부의 menuDiv, cartDiv가 담당.
🌳 로직 컴포넌트중 데이터처리에 해당되는 product.js, cart.js에서 메뉴 데이터와 장바구니 데이터를 관리
🌳 로직 컴포넌트중 이벤트 및 UI 업데이트는 HTML내부의 script태그가 사용자 이벤트 처리기능.
👉 코드가 한 번에 하나씩 순차적으로 출력되는것이 아닌, 여러 작업을 동시에 진행할 수 있도록 유동적으로 사용가능한 프로그래밍
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 순서로 출력이된다.