231011(์) ์ฑ์ฅ
๐ค ์ฑ์ฅ์ผ์ง 7.0
์ฑ
ํ๋ณตํ ์ด๊ธฐ์ฃผ์์(์จ์ธ ๋ค์ด์ด)
์ ๋ด์ฉ์ ์๊ทน๋ฐ์ ์์ํ๋ ์๋ฐํ ์ฑ์ฅ๊ธฐ๋ก
์ด์์๋ ๊ฝ๊ณผ ์ฃฝ์ ๊ฝ์ ์ด๋ป๊ฒ ๊ตฌ๋ณํ๋๊ฐ?
์ฑ์ฅํ๊ณ ์๋ ๊ฒ์ด ์ด์ ์๋ ๊ฒ์ด๋ค.
์๋ช ์ ์ ์ผํ ์ฆ๊ฑฐ๋ ์ฑ์ฅ์ด๋ค!
โ (7.0)<์์ ๊ฐํธ>
ํ์ธ๋ง ํ์ต๋ฒ
์ ์๊ฒ ๋๋งํผ, ์ฑ์ฅ์ผ์ง๋ ์ ๋ง ๊ทธ ๋ ์ ํค์๋ ์ค์ฌ์ผ๋ก ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๋๋ก ํ๋ค.
Swiper UX ๊ฐ์
Swiper๋ฅผ ์ด์ฉํด์ Carousel์ ๊ตฌํ ํ, ๋ฐฐํฌํ์ฌ ํธ๋ํฐ์์ ๋์์์ผ๋ณด์๋ค. ๊ทธ๋ฐ๋ฐ ์ฌ๊ฑธ ์ฌ๋ผ์ด๋ UX๊ฐ ๋๋ ๋๊ธฐ๋ฉด์ ์๋ํ๋ค. ์ด ๋ถ๋ถ์ ํน์ ๊ณต์๋ฌธ์์์ ์ง์ํ๋ ํด์ ์ฐพ์๋ณด์๋๋ฐ ๋คํํ cssMode
๋ผ๋ ์ต์
์ ์ ๊ณตํ๊ณ ์์๋ค. ์ด ์ต์
์ ์ ์ฉํ๋ ์ฌ๋ผ์ด๋ UX๊ฐ ๋งค๋๋ฝ๊ฒ ๋์ํ๋ค.
ํ์ฑํํ๋ฉด ์ต์ CSS ์คํฌ๋กค ์ค๋
API๋ฅผ ์ฌ์ฉํฉ๋๋ค. Swiper์ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ง์ํ์ง๋ ์์ง๋ง ๊ฐ๋จํ ๊ตฌ์ฑ์์ ํจ์ฌ ๋ ๋์ ์ฑ๋ฅ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
์ ์ฉํ๊ณ ๋์ ์ฌ๋ผ์ด๋๊ฐ ์์ฃผ ๋งค๋๋ฝ๊ฒ ๋์ํ๊ฒ ๋์๋ค. ๋ค๋ง ๋ฌธ์ ๋ ๋ฐ์คํฌํ์์ ํฐ์น ์ฌ๋ผ์ด๋๊ฐ ๋์ง ์๋๋ค๋ ๊ฒ์ด์๋ค. ๊ตฌ๊ธ๋ง๋ ํด๋ณด๊ณ ์ด๊ฒ์ ๊ฒ ๋ค ํด๋ดค์ง๋ง cssMode
์ ์ฉ ํ์๋ ์ด์ฉ ์ ์๋ ๊ฒ ๊ฐ๋ค. ๊ทธ๋๋ ์ ์ ์๊ฒ ์ฌ๋ผ์ด๋๋ฅผ ๋๊ฒจ์ค ์ ์๋ UX๋ฅผ ์ ๊ณตํ๊ธฐ ์ํด Navigation ๋ฒํผ์ ์ถ๊ฐํ๋ค.
return (
<Swiper
className="h-full w-full"
modules={[Autoplay, A11y, Navigation, Keyboard]}
autoplay={{ delay: SWIPER.DELAY, disableOnInteraction: false }}
freeMode={true}
navigation={isDesktop}
keyboard={{ enabled: true }}
lazyPreloadPrevNext={SWIPER.LAZY_LOADED_PREV_NEXT}
spaceBetween={bodyWidth < STANDARD.WIDTH ? bodyWidth : SWIPER.SPACE_BETWEEN}
slidesPerView={SWIPER.SLIDES_PER_VIEW}
loop={true}
loopedSlides={SWIPER.LOOPED_SLIDES}
loopPreventsSliding={true}
onRealIndexChange={swiper => {
setCurrentSlideIndex(swiper.realIndex);
}}
cssMode={true}
>
Github Issue ์๋ close๋๋ ๋ฌธ์
๋ง์ด๊ทธ๋ ์ด์ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ๊ณ์ ๋ด๊ฐ ๋ง๋ ์ด์๊ฐ ์๋์ผ๋ก close๊ฐ ๋์ด์ ์ ์ด๋ฌ๋ ํ๋ค.(์ง์ ์ฐพ์๋ณผ๊ฑธโฆ ๋ฐ์ฑ) ์ฒ์์ ์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๋ซํ๋๊ฑด๊ฐ ํ๋๋ฐ ์๊ณ ๋ณด๋ ์ปค๋ฐ ๋ฉ์์ง์ ์๋์ ๊ฐ์ ๋จ์ด์ ์ด์ ๋๋ฒ๋ฅผ ์ ์ด์ฃผ๋ฉด ์๋์ผ๋ก close๊ฐ ๋๋ค๊ณ ํ๋ค.
- close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved + #{issue number}: ์ด์๋ฅผ ์๋์ผ๋ก closeํ๋ค.
zustand๋ฅผ ํตํ ์ ์ญ ์ํ ๊ด๋ฆฌ
์ฌ๋ฌ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค ์ด์ํ๊ฒ zustand๊ฐ ๋ก๊ธด๋ค ํํฃโฆ ์ผ๋จ ์ฌ์ฉ๋ฒ์ด ์ ๋ง ๊ฐ๋จํ๋ฉด์ Redux Devtools๋ ์ง์ํ๊ณ ๋ฌด์๋ณด๋ค demo ์ฌ์ดํธ๊ฐ ์ ๋ง ๋๋ฌด ์์๋คใ ใ ใ Jotai๋ฅผ ๊ฐ๋ฐํ์ ๋ถ์ด๋ ๋์ผ์ธ๋ฌผ์ด๋ผ๊ณ ์๊ณ ์๋๋ฐ, ๋ญ๊ฐ Zustand์ ๋ ๊ณต์ ๋ค์ด์๋ ๋๋..? (๋, npm trends๋ฅผ ๋ณด๋ฉด zustand์ ์ฌ์ฉ๋ ์ฆ๊ฐ๊ฐ ์ ๋ง ์ด๋ง์ด๋งํ๊ฒ ์ฆ๊ฐํ๊ณ ์๋ค..! ๊ณง Redux ๋ฐ๋ผ์ก์๋ฏ..?) ์ด๋ฒ์ Next์ ์ฒ์์ผ๋ก ๊ฐ์ด ์ฌ์ฉํด๋ดค๋๋ฐ client ์ปดํฌ๋ํธ์์ ์ ๋ง ๊น๋ํ๊ฒ ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ํ ์ ์์ด์ ๋์ด์ด์ด์ด๋ฌด ์ข์ ๊ฒ ๊ฐ๋ค. ์๋๋ ๊ฐ๋จํ๊ฒ ์ฌ์ฉํด๋ณธ ์ฝ๋์ด๋ค.(์ ์ ์ ์ ํ์ ์ ์ฅํ๋ ์คํ ์ด)
import { create } from 'zustand';
import { UserSelect } from '@/types';
type State = {
userSelect: UserSelect;
};
type Action = {
setUserSelectSize: (sizeId: number) => void;
setUserSelectIngredientIds: (ingredientIds: number[]) => void;
reset: () => void;
};
const initialState: State = {
userSelect: {
sizeId: 0,
ingredientIds: [],
},
};
export const useUserSelectStore = create<State & Action>()((set) => ({
...initialState,
setUserSelectSize: (sizeId: number) =>
set((state) => ({ userSelect: { ...state.userSelect, sizeId } })),
setUserSelectIngredientIds: (ingredientIds: number[]) =>
set((state) => ({ userSelect: { ...state.userSelect, ingredientIds } })),
reset: () => set(initialState),
}));
์์ ์ ๋์ถฉ ๊ณต์๋ฌธ์ ์ฝ๊ณ ํผ์์ ์งฐ๋๋ฐ, ๋ค์ ๊ณต์๋ฌธ์๋ฅผ ๊ผผ๊ผผํ๊ฒ ๋ณด๋ฉด์ ์ข๋ ํธํ๊ฒ ์์ฑํ ์ ์์๋ค. ์ถํ์ Immer์ ํจ๊ป ์ฌ์ฉํด์ ์ข๋ ๊น๋ํ๊ณ ํธํ๊ฒ ์ง๋ด์ผ๊ฒ ๋ค. ์ง๊ธ state ์ ๋์์๋ Immer๋ฅผ ๊ตณ์ด ์ ์ฉํ ํ์๊ฐ ์๋ ๊ฒ ๊ฐ๋ค.
๐ ํ๊ณ
๊ทธ๋๋ ์ค๋๋ ํ์ด์ง ํ๋ ๋ง์ด๊ทธ๋ ์ด์ ์ด ๋๋ฌ๋ค. ์ ์ฉํ๊ณ ์ถ์๋ zustand๋ ๋ด ๊ฐ์ธ์ ์ผ๋ก Next์ ์ ๋ง๋ ๋๋์ด๋ผ ์ฌ๋ฏธ์๊ฒ ์ ์ฌ์ฉํ ์ ์์๋ค. ์๊ฐ๋ณด๋ค ์ด์ ์ ๊ตฌํํ๋ Carousel ์ชฝ์์ ๋ฌธ์ ๊ฐ ๋ง์ด ๋ฐ์ํด์ ์กฐ๊ธ ๊ท์ฐฎ์์ง๋งโฆ ์ผ๋จ์ UX ์ ์ผ๋ก๋ ๋ฌธ์ ์์ด ์ ๋ง๋ฌด๋ฆฌํ ๊ฒ ๊ฐ๋ค!