위코드에서 공부하며 정리한 내용입니다.
하나의 코드로 작성된 카드 UI 를 컴포넌트로 분리하고 카드뷰와 리스트뷰로 구분해 사용할 수 있게 만듭니다.
우선 반복되는 카드 디자인을 Mock 데이터와 map 메서드로 만듭니다. Mock 데이터는 U에서 변경되는 요소를 고려해 json 파일로 만듭니다.
[
{
"id": 1,
"tag": "NEW",
"img": "https://t1.kakaocdn.net/friends/prod/main_tab/home/home_20201103134054_kr.jpg?type=thumb&opt=R329x247@2xa",
"title": "촉촉함을 원해요",
"desc": "재채기는 콜록 콜록 눈,코,입 모두 간질 간질. 이게 다 건조함때문이라구! 건조함을 해결하러 온 꿀꿀꿀귀탱 가습기들, 구경해볼까요?"
},
{
"id": 2,
"tag": "Good",
"img": "https://t1.kakaocdn.net/friends/prod/main_tab/home/home_20201106164745_kr.jpg?type=thumb&opt=R329x247@2xa",
"title": "통통미 폭발 구름 폭신 필로우",
"desc": "통통하고 말랑 말랑한 너희에게 내 하루 시작과 끝에서 힐링을 부탁해!"
},
{
"id": 3,
"tag": "Theme",
"img": "https://t1.kakaocdn.net/friends/prod/main_tab/home/home_20201111183631_kr.jpg?type=thumb&opt=R335x187@2xa",
"title": "올 겨울엔 프렌즈랑 메리 화이트 크리스마스!",
"desc": "올해 크리스마스엔 눈이 올까요? 안오면 어때요. 이미 프렌즈들이 내 마음에 하얀 눈을 내려주고 있는걸!"
}
]
useState 와 useEffect, fetch 를 사용해 데이터를 가져와 상태값에 저장합니다.
const [cardInfoList, setCardInfoList] = useState([]);
useEffect(() => {
fetch("/data/cardInfo.json") // 데이터 위치 신경쓰기
.then((res) => res.json())
.then((result) => setCardInfoList(result));
}, []); // 의존성 배열 비워두기
가져온 데이터를 map 메서드로 돌려 반복되는 카드 UI 만듭니다. 카드 UI 는 Card 컴포넌트를 만들어서 구현하고 props 로 데이터를 전달합니다.
{cardInfoList.map((cardInfo) => {
return (
<Card
key={cardInfo.id} // 키 값 지정해야 함
id={cardInfo.id}
tag={cardInfo.tag}
img={cardInfo.img}
title={cardInfo.title}
desc={cardInfo.desc}
/>
);
})}
Card 컴포넌트는 props 로 전달받은 데이터로 카드 UI 를 그립니다.
const Card = ({ id, tag, img, title, desc}) => {
return (
<article className="card">
<div>
<img alt="card" src={img} />
</div>
<div className="cardMain">
<div>
<span className={tag.toLowerCase()}>{tag}</span>
</div>
<h3>{title}</h3>
<div>
<p>{desc}</p>
</div>
</div>
</article>
);
};
이제 카드 하단에 제품 목록이 들어가는 컴포넌트를 만듭니다. 우선 제품 정보를 담은 Mock 데이터를 json 형태로 준비하고
[
{
"id": 1,
"img": "https://t1.daumcdn.net/friends/prod/product/20201020152815054_8809721505908_AW_00.jpg?type=thumb&opt=R103x103@2xa",
"title": "구름폭신필로우_라이언",
"price": "39,000원"
},
{
"id": 2,
"img": "https://t1.daumcdn.net/friends/prod/product/20201020152804391_8809721505915_AW_00.jpg?type=thumb&opt=R103x103@2xa",
"title": "구름폭신필로우_어피치",
"price": "42,000원"
},
{
"id": 3,
"img": "https://t1.daumcdn.net/friends/prod/product/20201020152905998_8809721505922_AW_00.jpg?type=thumb&opt=R103x103@2xa",
"title": "구름폭신필로우_무지",
"price": "15,000원"
}
]
이 데이터 역시 useEffect, fetch 함수로 호출한 후 상태값에 넣어줍니다.
const [itemInfoList, setItemInfoList] = useState([]);
useEffect(() => {
fetch("/data/itemInfo.json")
.then((res) => res.json())
.then((result) => setItemInfoList(result));
}, []);
해당 데이터를 Card 컴포넌트에 props로 전달하고,
{cardInfoList.map((cardInfo) => {
return (
<Card
key={cardInfo.id}
id={cardInfo.id}
tag={cardInfo.tag}
img={cardInfo.img}
title={cardInfo.title}
desc={cardInfo.desc}
itemInfoList={itemInfoList} // 여기 추가
/>
);
})}
Card 컴포넌트에서 tag 이름에 따라 리스트뷰, 카드뷰 컴포넌트를 화면에 보여지게 만듭니다. {tag === "Good" && } 이렇게 입력하면 tag 이름에 따라 해당 컴포넌트가 화면에 그려집니다. 아래 공식은 해당 컴포넌트를 바로 넣지 않고 map 함수로 반복되는 구조를 리턴했습니다.
<ul>
{tag === "Good" &&
itemInfoList.map((itemInfo) => {
return <ItemListView key={itemInfo.id} itemInfo={itemInfo} />;
})}
{tag === "Theme" &&
itemInfoList.map((itemInfo) => {
return <ItemCardView key={itemInfo.id} itemInfo={itemInfo} />;
})}
</ul>
리스트 뷰와 카드뷰의 구조는 클래스 이름만 다르게 해서 다른 scss 를 적용하면 되지만, 이후 사용성을 고려해 컴포넌트 파일과 각각의 scss 파일을 두개로 분리했습니다. 아래는 리스트뷰 컴포넌트 입니다.
const ItemListView = ({ itemInfo }) => {
const { img, title, price } = itemInfo;
return (
<li className="item list"> // 카드뷰는 "item card"
<img alt="item" src={img} />
<div>
<div>
<p>{title}</p>
<p className="price">{price}</p>
</div>
<button className="button" />
</div>
</li>
);
};