
ํ๋ก์ ํธ ์์๋งํฌ ๐ผ
https://www.youtube.com/watch?v=v78D9IcYdU8
1. Styled Component ํ์ฉ์ด์ ํ๋ก์ ํธ์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ Styled Component๋ฅผ ํ์ฉํด ๋ณด์๋ค๋ ์ ์ด๋ค.
Styled Component๋ฅผ ํ์ฉํ๋ฉด ์ฝ๋๊ฐ ๊น๋ํ๊ฒ ์ ๋ฆฌ๋๊ณ ์ฝ๋ ์คํฌ๋กค์ด ์ค์ด๋ ๋ค.
html/css๊ฐ ์ฝ๋์ ๋ถ๋ฆฌ๋์ด jsxํ์ผ์๋ ์์ ํ ๊ธฐ๋ฅ์ ์ํ ์ฝ๋๋ง์ด ๋ถ๋ฆฌ๋์ด ๊ด๋ฆฌ๋๋ฏ๋ก
ํ์ธ์ด ํธ๋ฆฌํ๊ณ ๊ตฌ์กฐ ํ์ธ์ ์ฉ์ดํ๋ค. ๋ํ ํด๋์ค ๋ช
์ ๋ํ ๊ณ ๋ฏผ์ ์ค์ผ ์ ์์๊ณ ,
์ค์๋ก ์ค๋ณต๋ ํด๋์ค๋ช
์ ๊ฐ๊ฒ ๋๋ ๊ฒฝ์ฐ๊ฐ ์์ด ์๋ชป๋ css๊ฐ ์ ์ฉ๋์ด ์ค์ผ๋๋ ๊ฒฝ์ฐ๊ฐ ์์๋ค.
2. EventCard Component1. type{type !== 'wishlist' ? (
''
) : (
<S.Check
src={
checkList.includes(event_id)
? '/images/common/check-true.png'
: '/images/common/check-false.png'
}
onClick={() => handleChecked(event_id)}
/>
)}
EventCard ์ปดํฌ๋ํธ๋ ํ๋์ ์ปดํฌ๋ํธ ์ด์ง๋ง, wishlist์ ๊ทธ ์ธ ํ์ด์ง์์ ํ์ฉ๋๊ฐ ๋ค๋ฅด๋ค.
์๋ฅผ ๋ค์ด checklist๊ธฐ๋ฅ์ wishlist์๋ง ์๋ค. ์ด๋ฌํ ๊ธฐ๋ฅ๋ค์ ์ฐจ์ด๋ฅผ ๋ฐ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก
๊ฐ ํ์ด์ง์์ EventCard ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฌ์ฌ ๋ type์ ๋ถ์ฌํ๋ค.
์ด type์ด wishlist ์ธ์ง, ๊ทธ ์ธ์ธ์ง๋ฅผ ๊ตฌ๋ถํ์ฌ ๊ฐ๊ฐ ๋ค๋ฅด๊ฒ ๋ ๋๋งํ ์ ์๋๋ก ์์
ํ์๋ค.
์์ ์ ํ์ญ์ ๋ฅผ ์ํ ์ฒดํฌ๋ฆฌ์คํธ ๋ฟ๋ง ์๋๋ผ UX์ ์ธ ๊ด์ ์์๋,
๋ค๋ฅธ ํ์ด์ง์์๋ ์ด๋ฒคํธ ์์ฒด์ ์ ๋ณด๋ฅผ ์ ๋ฌํ๋ ๋ฐ ์ฃผ๋ ฅํ๊ณ
wishlist ํ์ด์ง์์๋ ์ด๋ฏธ user๊ฐ ์ด๋ฒคํธ์ ๋ํ ์ ๋ณด๋ฅผ ๊ฐ๊ณ ํฅ๋ฏธ๋ฅผ ๊ฐ์ง ์ํ์ด๋ฏ๋ก
'์
์ฐฐ ๋ง๊ฐ ๋จ์ ์๊ฐ'๊ณผ '๋จ์ ํฐ์ผ ์'๋ฅผ ํ๊ธฐํ๋๋ก ํ์ฌ ๊ตฌ๋งค ์๊ตฌ๋ฅผ ์ฆ๋ํ ์ ์๊ฒ ๊ณ ์ํ์๋ค.
2. fetchLiked๋ชจ๋ ํ์ด์ง์์ ๋ ๋๋ง ๋ eventCard์ wishlist ํฌํจ ์ฌ๋ถ์ ์ํ๋ฅผ ํ๋ฒ์ ๊ด๋ฆฌํ ์ ์๋๋ก
๊ธฐ๋ฅ์ ๋ฐ๋ก fetchLiked ํจ์๋ก ๋ง๋ค์ด ๊ด๋ฆฌํ์๋ค.
์ด ํจ์๋ฅผ ๋ง๋ค ๋
recoil์ฌ์ฉ๊ณผ ๊ณ ๋ฏผ์ ํด๋ณด๊ฒ ๋์๋ค.
ํ์ง๋ง ๋จ์ํ ํ๋ก ํธ ๋จ์์ ์ ์ฅ๊ณผ์ ์์ด ์ฌ์ฉํ ๋ฐ์ดํฐ๊ฐ ์๋๊ณ ,
์๋ก๊ณ ์นจ์ด๋ ์์์น ๋ชปํ ํ์ด์ง์ ๋ซํ ๋ฑ์ด ์๊ฒผ์ ๋์๋ ์ ์ง๊ฐ ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์
๋ฐฑ ๋จ๊ณผ ๋ฐ๋ก ๋ฐ๋ก ํต์ ํ๋ฉด์user data์์์ ๊ด๋ฆฌ๋์ด์ผ ํ๋ค๊ณ ์๊ฐํ์๋ค.
๊ทธ๋์ user๊ฐ liked(heart)๋ฅผ ๋๋ฅผ ๋ ๋ง๋ค ํต์ ์ด ์ผ์ด๋ ์ ์์ด์ผ ํ๊ณ
๋ก๊ทธ์ธ ๋ user๋ง ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฅ, ์ฆ TOKEN์ ๋ถ์ฌ ๋ฐ์ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋ค์๋ค.
TOKEN, url, wishlist(data), event_id๋ฅผ ์ฃผ์๋ก ์ ๋ฌ๋ฐ๋๋ก ํ๊ณ
const isExistData =
wishlist &&
wishlist.filter(el => el.event_id === Number(event_id)).length > 0;
wishlist data์ ๋ค์ด์๋ event_id์ data์์ ๋ฐ์ event_id๋ฅผ filter๋ก ๋น๊ตํ์ฌ 1๊ฐ๋ผ๋ wishlist์ data๊ฐ ์กด์ฌํ๋ฉด DELETEํต์ ์ ์งํํ๊ณ ์กด์ฌํ์ง ์์ผ๋ฉด event_id๋ฅผ ๊ธฐ์ค์ผ๋ก POST ํต์ ์ ์งํํ๋ค.
์ด ํต์ ๊ณผ ํจ๊ป eventCard Component ๋ด๋ถ์์๋ wishlist ID ์ฌ๋ถ๋ฅผ includes๋ก ์ฒดํฌํ์ฌ ์ปดํฌ๋ํธ ๋ด๋ถ์ heart๋ฅผ ์ํ ๋ณํํ๋ค.
3. fetchLiked ํ์ฉ๋ชจ๋ ํ์ด์ง์์ fetchLiked๋ฅผ ํธ์ถํ์ฌ EventCard ๊ฐ ๋ ๋๋ง ๋ ๋ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ค.
const setId = (data, event_id) => {
fetchLiked(TOKEN, APIS.wishlist, data, event_id, setWishlist, getWishList);
};
์ฌ๊ธฐ์ getWishlist๋ wishlist๋ฅผ ๋ถ๋ฌ์ค๋ ํต์ ์ผ๋ก ๋ฐ๋ก ์ ์ธ๋ ๋ณ์์ ๋ด์๋๊ณ ์คํํ๋๋ก ํ๋ค.
3. Wishlistย ๐ wishlist ํต์ ์ ํญ์ TOKEN ๊ฐ์ ๊ธฐ๋ณธ์ผ๋ก ๊ฐ๋๋ก ํ์ฌ ํ์ ๊ฐ๊ฐ์ wishlist ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ๊ด๋ฆฌํ ์ ์๋๋ก ํ๋ค.
1. emptyBoxUX์ ์ธ ๊ด์ ์์ wishlist๊ฐ ๋น์ด์๋ค๊ณ ์๋ฌด๊ฒ๋ ๋
ธ์ถ๋์ง ์์ผ๋ฉด ์ฌ์ฉ์๋ ์์ง ๋ ๋๋ง์ด ์งํ ์ค์ธ์ง, wishlist๊ฐ ๋น์ด์๋์ง ํ์
ํ๊ธฐ๊ฐ ์ด๋ ต๋ค. ๊ทธ๋์ ํ ๋์ ์ ์ ์๋๋ก wishlist.length๋ฅผ ํ์ฉํ์ฌ 0์ด๋ผ๋ฉด ๋ฏธ๋ฆฌ ์ง์ ํด๋ ๋ฌธ๊ตฌ (์ด๋ฒคํธ ์นด๋๋ฅผ ๋ด์์ฃผ์ธ์!) ๋ฅผ ๋ ๋๋ง ํ ์ ์๋๋ก ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ ์ฌ์ฉํ์๋ค.
2. handleCheckedconst handleChecked = event_id => {
if (checkList.includes(event_id)) {
setCheckList(checkList.filter(el => el !== event_id));
} else {
setCheckList([...checkList, event_id]);
}
};
wishlist์์๋ง ๋ ๋๋ง ๋๋ check icon์ ํด๋ฆดํ ๋๋ง๋ค handleChecked๊ฐ ์คํ๋๋ค.
checklist ๊ฐ ์ด๋ฏธ ํด๋น event_id๋ฅผ ํฌํจํ๊ณ ์์ ๊ฒฝ์ฐ ๋ฐฐ์ด ๋ด์์ ์ญ์ ํ๊ณ , ํฌํจ๋์ง ์์์ ๊ฒฝ์ฐ ์ถ๊ฐํ๋ค.
3. handleDeleteCheckedconst handleDeleteChecked = id => {
if (checkList !== []) {
let copy = [...wishlist];
setWishlist(copy.filter(el => !checkList.includes(el.id)));
setType('checked');
}
const url = `${APIS.wishlist}`;
const deleteCards = checkList.join(',');
fetch(`${url}/${deleteCards}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json;charset=urf-8',
Authorization: TOKEN,
},
}).then(response => {
if (response.ok) {
getWishList();
}
});
};
์์ handleChecked๋ก ๋ง๋ checklist ๋ฐฐ์ด์ด ๋น๋ฐฐ์ด์ด ์๋ ๊ฒฝ์ฐ setWishlist๋ฅผ ํ์ฉํ์ฌ wishlist์์ checklist( ์ญ์ ๋ฅผ ์ํด ์ ํ๋ ์ด๋ฒคํธ ๋ค์ id๊ฐ ํฌํจ๋ ๋ฐฐ์ด ) ์ ํฌํจ๋ id ๋ฐฐ์ด์ joinํ์ฌ endpoint์ ๋ด์ ๋ณด๋์ผ๋ก์จ ์ ํ๋ event๋ฅผ wishlist์์ ์ญ์ ํ๋ค.
์ด ๋ type์ checked๋ก ์ง์ ํจ์ผ๋ก์จ ์ญ์ ๋ชจ๋ฌ์ด ๋ด์ ๋ ์ ํ ์ญ์ ๋ฅผ ์งํํ ์ง ์ ์ฒด ์ญ์ ๋ฅผ ์งํํ ์ง ์ ๋ฌํ๋ค.
4. handleDeleteAllwishlist ๋ฐฐ์ด ๋ด ๋ชจ๋ ๊ฐ์ฒด๋ฅผ joinํด์ endpoint์ ๋ด์ ๋ณด๋์ผ๋ก์จ ๋ชจ๋ wishlist๋ฅผ ์ญ์ ํ๋ค.
5. ๋ฌดํ๋ ๋๋ง (๋๋ณด๊ธฐ)let limit = searchParams.get('limit');
const handleMoreEvent = () => {
searchParams.set('limit', Number(limit) + 6);
setSearchParams(searchParams);
};
limit์ ๋ฐฑ์๋์์ ๊ธฐ๋ณธ๊ฐ 6์ผ๋ก ์ ๋ฌ๋ฐ๊ณ ์๋ค. ๊ทธ๋์ ๋จผ์ limit ์ searchParams.get()์ผ๋ก ๋ฐ์์ค๋๋ก ํ๋ค. ๊ทธ๋ฐ ๋ค์ ํ๋ฉด ํ๋จ์ ๋๋ณด๊ธฐ ๋ฒํผ์ ํด๋ฆญํ ๋ ๋ง๋ค limit์ 6์ ๊ณ์ํด์ ๋ํ ์ ์๋๋ก ์์
ํ์๋ค.
EventList1. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ฉ ( calendar/range filter )1. calendarReact-calendar ํ์ฉ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ ๊ฒ์ ์ฒ์์ด๋ผ ์ฒ์ ์์
ํ ๋๋ ๋งค์ฐ ์์ ๊ฑฐ ๋ถํฐ ๋ง์ด ํค๋งธ๋ค. ์๋ก์ด ํ์ผ์ ์ถ๊ฐํด์ CSS๋ฅผ ์ ์ฉํด ์ปค์คํฐ๋ง์ด์ง์ ํ ํ์ ๊ธฐ๋ฅ ์์
์ ๋ค์ด๊ฐ๋๋ฐ, ์ฒ์์ onClick ์์ฒด๊ฐ ๋์์ ํ์ง ์์ ๋๋ฌด ๋นํฉ์ค๋ฌ์ ๋ค. ๊ทธ๋์ ๊ทธ ๋ค์์ onChange๋ฅผ ์ ์ฉํด ๋ณด๋ ์๋์ ์ ๋๋ก ํ์ง๋ง, ํ ํ
ํฌ ๋๋ฆฌ๊ฒ ์๋ํ๋ ๊ฒ์ด ๋ง์์ ๋ค์ง ์์๋ค.
๋ค์ ๊ณต์ ๋ฌธ์๋ฅผ ๊ผผ๊ผผํ ์ดํด๋ณด๋ onClickDay๋ผ๋ ๋ฉ์๋๊ฐ ๋ฐ๋ก ์ ๋ฆฌ ๋์ด ์์๋ค. ์ด๊ฑธ ํ์ฉํ๋ ์ ์์ ์ผ๋ก ์ํ๋ ๋๋ก ์๋ํ๋ค. ์ด๋ ๊ฒ ์ถ๋ ฅํ userDate๋ฅผ
const dateFormat = userDate => {
const year = userDate.getFullYear();
const month = String(userDate.getMonth() + 1).padStart(2, '0');
const date = String(userDate.getDate()).padStart(2, '0');
return `${year}-${month}-${date}`;
};
๋ฐฑ์๋์์ ํต์ ์ ์๋ง๋ ํํ๋ก ๋ง๋ค๋๋ก ํ๋ค. ์ฒ์์ ํ ์๋ฆฌ ์๋ง ์์ 0์ ๋ํด์ฃผ๋ ๊ฒ์ ๋ํด ์กฐ๊ธ ํค๋งธ๋๋ฐ padStart๋ฅผ ํ์ฉํด์ ํด๊ฒฐํ๋ค.
padStart(size, '๋น์๋ฆฌ๋ฅผ ์ฑ์ธ string')
๊ทธ๋ฆฌ๊ณ ๋ค์ ์ด fomattedDate๋ฅผ searchParams์ ํ์ฉํ์ฌ ํด๋น ๋ ์ง์ ํด๋นํ๋ ์ด๋ฒคํธ๋ง ์ถ๋ ฅํ ์ ์๋๋ก ํ์๋ค.
2. Range Filtermaterial-ui (MUI) ํ์ฉmin๊ฐ๊ณผ max๊ฐ์ value๋ก ์ ๋ฌ๋ฐ์ query์ ํ์ฉํ ์ ์๋๋ก ํ์๊ณ ,
๊ณต์ ๋ฌธ์๋ฅผ ํตํด step์ ์ด์ฉํด ํ์ํ ๊ฐ๊ฒฉ(10๋จ์)์ผ๋ก๋ง ์ด๋ํ ์ ์๋๋ก ์ง์ ํ์๋ค.
์ด๋ค ํน์ ๊ฐ์ด ์ถ๊ฐ๋์ด ํํฐ๋ง ๋๋ ๊ฒ์ด ์๋๋ผ ํน์ ๊ฐ ๋ด์ ๋ฒ์๊ฐ ๊ณ์ ๋
ธ์ถ๋ ์ ์๋๋ก ํด์ผํ๊ธฐ ๋๋ฌธ์
min๊ฐ๊ณผ max๊ฐ์ url์ minPrice, maxPrice๋ก ๊ธฐ๋ณธ์ผ๋ก ์ ๋ฌ๋๋๋ก ํ๋ค.
2. Category Component
- data ๊ตฌ์กฐ
{ "id": 1, "mainCategory": "์นดํ ๊ณ ๋ฆฌ", "subCategory": ["ํ์คํฐ๋ฒ", "์ฝ์ํธ", "ํผํฌ๋จผ์ค", "ํด๋์"], "key": "categoryId" }, { "id": 2, "mainCategory": "๋์ด", "subCategory": ["์ ์ฒด", "์ฑ์ธ"], "key": "ageRange" }, { "id": 3, "mainCategory": "๊ตญ๊ฐ", "subCategory": ["๊ตญ๋ด", "๋ฏธ๊ตญ", "ํธ์ฃผ", "์ผ๋ณธ"], "key": "countryId" }, { "id": 4, "mainCategory": "์ ์ฐฐ๊ฐ", "subCategory": "" }
๋ฉ์ธ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ธ๋ถ ์นดํ
๊ณ ๋ฆฌ๊ฐ ๋๋์ด ์ง๋ ํํ์๋๋ฐ, ์ ์ฒด ์นดํ
๊ณ ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ํ๋์ ํ์ผ๋ก ๋ง๋๋ ๊ฒ ์ข์ง ์์๊น? ๋ผ๊ณ ์๊ฐํด์ data ๊ตฌ์กฐ๋ฅผ ์์ ๊ฐ์ด ๋ง๋ค์๋ค. ์ฌ๊ธฐ์ key๋ query๋ฅผ ๋ฃ์ด์ฃผ์๋ค. ๊ทธ๋ฆฌ๊ณ mainCategory๋ฅผ ๊ธฐ์ค์ผ๋ก componentํ ํด์ map() ๋ฉ์๋๋ฅผ ํ์ฉํด์ ๊ตฌํํ์๋ค.
๋จผ์ subCategory๊ฐ array๊ฐ ์๋ ๊ฒฝ์ฐ๋ฅผ ๋นผ๊ธฐ ์ํด Array.isArray(subCategory)๋ฅผ ํ์ฉํ์๋ค.
* Array.isArray(subCategory ) - subCategory๊ฐ array ์ธ์ง ํ์ธ ํ ์๋๋ฉด subCategory๋ฅผ ๊ทธ๋๋ก ์ถ๋ ฅ - Array ์ผ ๊ฒฝ์ฐ : key ๊ฐ์ผ๋ก subcategory ๋ถ์ฌ (subcategory = ์ธ์ === subCategory ๋ฐฐ์ด ๋ด ๋ฌธ์ ๊ทธ์์ฒด) -> query๋ฅผ ์ํจ
<S.SubCategories key={subcategory} onClick={() => { handleCheckedCategory(String(idx + 1)); handleCategoryState(subcategory, data.key); }}
์ฌ๊ธฐ์ idx์ +1 ์ ํ๋ ์ด์ ๋ ๋ฐฐ์ด์ idx๊ฐ 0 ๋ถํฐ ์์ํ๊ณ ,
์ด idx๋ฅผ ์ดํ data์ id ๊ฐ๊ณผ ๋น๊ตํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค !
3. DropBox Component
- data ๊ตฌ์กฐ
export const DROP_MENU = [ { id: 0, name: '๋ ์ง์', key: 'dateAsc' }, { id: 1, name: '๊ฐ๊ฒฉ ๋ฎ์ ์', key: 'priceAsc' }, { id: 2, name: '๊ฐ๊ฒฉ ๋์ ์', key: 'priceDesc' }, ];
์์ ์นดํ
๊ณ ๋ฆฌ data ๊ตฌ์กฐ์ ๋์ผํ๊ฒ id๋ฅผ index๋ก ํ์ฉํ์ฌ key๊ฐ์ searchParams๋ฅผ ํ์ฉํด orderBy์ ์ ๋ฌํ ์ ์๋๋ก ํ๋ค.
4. handleCategoryStateconst handleCategoryState = (category, key, categoryData) => {
const categoryIndex = categoryData.subCategory.indexOf(category) + 1;
const categoryIdArr = searchParams.getAll(key);
if (categoryIdArr.includes(String(categoryIndex))) {
const newArr = categoryIdArr.filter(el => el != categoryIndex);
searchParams.delete(key);
newArr.forEach(el => {
searchParams.append(key, el);
});
} else {
searchParams.append(key, categoryIndex);
}
setSearchParams(searchParams);
};
๋ฐฐ์ด์ index๊ฐ 0๋ถํฐ ์์ํ๋ฏ๋ก id ์ ๋ง์ถ๊ธฐ ์ํด +1์ ํด์ค๋ค.
categoryIdArr ๋ searchParams.getAll(key)๋ฅผ ํตํด key์ ์ผ์นํ๋ ํ๋ผ๋ฏธํฐ๋ฅผ array๋ก ๊ฐ์ ธ์จ๋ค. ๊ทธ ๋ค includes๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ categoryIdArr์ categoryIndex๊ฐ ์ด๋ฏธ ํฌํจ๋์ด ์๋ ์ง ํ์ธํ๊ณ ์๋ค๋ฉด ํด๋น ์นดํ
๊ณ ๋ฆฌ ์ธ๋ฑ์ค๋ฅผ ์ ์ธํ ์๋ก์ด ๋ฐฐ์ด (newArr)๋ฅผ ๋ง๋ค๊ณ ๊ธฐ์กด ์ฟผ๋ฆฌ๋ฅผ ์ง์ฐ๊ณ ๋ค์ appendํ์ฌ ์ค๋ณต๋ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ญ์ ํ๋ค.
๋ง์ฝ ์ค๋ณต๋ ์ธ๋ฑ์ค๊ฐ ์๋ค๋ฉด append ํ๋ค.
๋ ๋ง์ง๋ง ์ค์ setSearchParams(searchParams)๋ฅผ ํตํด ์๋ก๊ณ ์นจ ์์๋ ์ฟผ๋ฆฌ๊ฐ ์ ์ง๋ ์ ์๋๋ก ๊ตฌํํ์๋ค.
* value(min/max), limit, location.search, checkLiked ์์กด์ฑ ๋ฐฐ์ด
5. API fetchuseEffect(() => {
const url = `${APIS.events}${location.search}&minPrice=${value[0]}&maxPrice=${value[1]}`;
fetch(url)
.then(response => response.json())
.then(result => {
setCardData(result.data);
});
}, [value, limit, location.search, checkLiked]);
fetch์ ์์กด์ฑ ๋ฐฐ์ด์๋ value(mixPrice / maxPrice), location.search(์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ), checkLiked(๊ฐ๋ณ ์ข์์ ํต์ ) ๋ฅผ ๊ฐ๋๋ค.
5. goToTop Component์๋จ ์ด๋ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ๋์๋ UX/UI ๋ถ๋ถ์์ ๊ณ ๋ฏผ์ ์ข ํด๋ณด์๋ค.
ํ๋ฉด์ ํํ๊ฐ ์ข/์ฐ๋ก ๋ช
ํํ๊ฒ ๋๋์ด์ ธ ์๊ณ , ์ข์ธก ์นดํ
๊ณ ๋ฆฌ ์ฒดํฌํ๋ ๋ถ๋ถ์ ๋ง์ฐ์ค๊ฐ ์์ฃผ ์ด๋ํ ์ ์๋ค๋ ์ ์
๊ฐ์ํ์ฌ ๋ค๋ฅธ ํ์ด์ง๋ ์ฐ์ธก ํ๋จ์๋ง ์๋ ์๋จ ์ด๋ ์ปดํฌ๋ํธ๋ฅผ ์ข์ธก ํ๋จ์๋ ํด๋น ๋ถ๋ถ์ ๋ง๊ฒ ๋์์ธ์ ์์ ํ์ฌ ๊ตฌํํ๋ค. ์ด ๊ฒ๋ ์์ Eventcard component๊ฐ wishlist์์๋ง ๋ค๋ฅด๊ฒ ๋ํ๋ ์ ์๋๋ก ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ ํ๋ ๊ฒ ์ฒ๋ผ type์ ๋ถ์ฌํด์ ํ์ด์ง๋ฅผ ๊ตฌ๋ถํ์ฌ ํ์ด์ง์ ๋ง๋ ๋ค๋ฅธ ๋์์ธ์ด ๋์ฌ ์ ์๋๋ก ๊ตฌํํ์๋ค.