export default function Cart() {
return (
<div>
<h4 className="title">Cart</h4>
<div className="cart-item">
<p>상품명</p>
<p>$40</p>
<p>1개</p>
</div>
<div className="cart-item">
<p>상품명</p>
<p>$40</p>
<p>1개</p>
</div>
</div>
);
}
이렇게 생긴 cart-item 하나를 컴포넌트로 만들어 쉽게 반복 사용할 수 있다.
function CartItem() {
return (
<div className="cart-item">
<p>상품명</p>
<p>$40</p>
<p>1개</p>
</div>
);
}
이렇게 만들어주고,
export default function Cart() {
return (
<div>
<h4 className="title">Cart</h4>
<CartItem />
<CartItem />
<CartItem />
</div>
);
}
이렇게 추가해주면
원하는 개수만큼 생성할 수가 있다.
위에 만든 것처럼 아무데나 대충 만든건 server component가 된다.
server component는 html안에 자바스크립트 기능(예를 들면 onClick={}
같은..)을 넣을 수가 없다.
useEffect, useState 등을 사용할 수 없다.
Error: Event handlers cannot be passed to Client Component props.
<button onClick={function} children=...>
If you need interactivity, consider converting part of this to a Client Component.
근데 왜 server component 불편하게 쓰냐? 장점이 있으니깐!
일단 로딩이 빠르당 검색엔진 노출 등에도 유리하다.
server component와 다르게 파일 상단에 'use client'라고 쓰고 만든 건, client component가 된다.
client component 안에서는 html 내 자바스크립트 기능이라든지, useEffect, useState 등을 사용할 수 있다.
client component는 자바스크립트가 많이 필요해 로딩 속도가 느리고, 또 hydration이 필요하기 때문에 또 느리다.
hydration : html 유저에게 보낸 후에 자바스크립트로 html 다시 읽고 분석하는 일
큰 페이지는 server component,
자바스크립트 기능들이 필요한 페이지에만 client component를 이용!
다른 파일에서 생성한 변수를 가져와 보자!
let age = 20;
export default age;
import age from "./data";
...
<p>상품명 {age}</p>
...
import와 export를 적절히 해주면 쉽게 사용할 수 있다.
let price = 20;
let name = "바나나";
export { price, name };
export 하고 싶은 변수가 두 개 이상이면, 중괄호{}
를 사용하여 export 한다.
import { price, name } from "./data";
...
<div className="cart-item">
<p>{name}</p>
<p>${price}</p>
<p>1개</p>
</div>
...
그리고 불러올 때도 중괄호를 사용해 준다.
export default function Cart() {
let shoppingCart = ["Tomatoes", "Pasta"];
...
상품의 data를 하나 만들어주고, 이를 출력하고 싶으면
아까 만들었던 컴포넌트에서 {product[0]}
를 해주면 될 것 같지만,
fucntion에서 만든 변수는 그 함수 내에서만 사용할 수 있다.
부모컴포넌트->자식컴포넌트로 변수를 사용흐고 싶으면 props를 이용해주기!
<CartItem product={shoppingCart[0]}/>
// <자식컴포넌트 작명={전해줄데이터} />
...
function CartItem(props) {
return (
<div className="cart-item">
<p>{props.product}</p>
<p>${price}</p>
<p>1개</p>
</div>
);
}
이렇게 해주면 잘 가져올 수 있음.
- props는
<CartItem 이런거={이런거} 저런거={저런거}>
이렇게 많이 전송가능- 일반 문자데이터 전송하려면 중괄호 없이
<CartItem 어쩌구="어쩌구">
해도 가능
let [count, setCount] = useState(0);
useState를 이용해서 변수에 담아줬다.
일반변수는 변경되어도 변수가 들어있는 html에 자동으로 반영되지 않으나, state가 변경되면 state가 들어있는 html도 자동으로 재렌더링이 된다.
위에서 설명했듯 상단에
"use client";
를 꼭 써주어야 함! 왜냐면 useState는 client component에서만 쓸 수 있기 때문!
그리고 버튼에 onClick 함수를 추가해 화면에 수량 + - 가 실행되게 해주었다.
<button
onClick={() => {
count <= 0
? (0, alert("더 줄일 수 없습니다!"))
: setCount(count - 1);
}}
>
-
</button>
<span>{count}</span>
<button
onClick={() => {
setCount(count + 1);
}}
>
+
</button>
map으로 했더니 얘네 하나 누르니까 나머지 다 눌러진다...
이거 이런식으로 장사하면 순 사기꾼이 되니 수정해주어야 함.
그러려면 일단 state 부터 각각 넣어주어야 한다.
하지만 그러면 귀찮으니 이렇게 해보도록 함.
let [count, setCount] = useState([0, 0, 0]);
{grocery.map((a, i) => {
return (
<div className="food" key={i}>
<img src={"food" + i + ".png"} className="food-img" width={50} />
<h4>{a} $40</h4>
<button
onClick={() => {
let copy = [...count];
count <= 0
? (0, alert("더 줄일 수 없습니다!"))
: (copy[i]++, setCount(copy));
}}
>
-
</button>
<span> {count[i]} </span>
<button
onClick={() => {
let copy = [...count];
copy[i]++;
setCount(copy);
}}
>
+
</button>
</div>
);
})}
state를 ...문법(spread operator)으로 복사하고 state의 [i]번 항목을 +1 해준 다음 setCount로 넣어주면 된다.
왜 ...를 이용해서 복사를 먼저 하냐면, set어쩌구()를 하게 되면 컴퓨터는 기존state와 신규state의 일치여부 부터 검사해본다.
검색해보고 같으면 state를 변경해주지 않음.
그러니까,
count[0]++
setCount[count]
이렇게 해봤자 복사해주지 않는다는 것이다.
array/object자료를 let arr = [1,2,3]
요런식으로 하나 만들면
RAM에다가 몰래 저장이 되고, let arr 변수는 그 자료가 어디있는 거다~ 하는 화살표만 담겨있는 것이다.
그러니 그 자료를 수정한다고 해서 화살표를 타고 들어간 RAM값이 수정될 뿐 그 변수에 담긴 화살표는 변하지 않기 때문에,
기존state와 신규state를 비교하면 (==로 비교하기 때문에) 변수에 저장된 화살표만 비교해주기 때문에 같다고 나오는 것이다.
let arr = [1,2,3]
let arr2 = arr
arr2[0]++
arr에 있던 걸 arr2에 복사를 한다고 해도 화살표만 복사될 뿐이라,
콘솔창에 입력해보면
++ 된 값이 아닌 그대로 1이 출력될 뿐더러
arr == arr2 는 true가 나온다.
참고 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
근데 여기서 만약에 스프레드 연산자를 이용하면,
let arr = [1,2,3]
undefined
let arr2 = [...arr]
완전 독립적인 복사본을 생성해주는 것~
깊은 복사에서 자세히 알아볼 수 있는 부분이다.
아무튼 이렇게 해주었는데 0 이하로 내려갔을 때 개수가 -가 되었다.
처음에 삼항연산자에서 간단히 copy부분만 수정해줘서
0 으로 입력했던게 사실상 아무 의미가 없게 된 것.
삼항연산자를 뜯어고쳐야 했다.
<button
onClick={() => {
let copy = [...count];
copy[i] <= 0
? ((copy[i] = 0), alert("더 줄일 수 없습니다!"))
: (copy[i]--, setCount(copy));
}}
>
-
</button>