231010(ํ) ์ฑ์ฅ
๐ค ์ฑ์ฅ์ผ์ง 7.0
์ฑ
ํ๋ณตํ ์ด๊ธฐ์ฃผ์์(์จ์ธ ๋ค์ด์ด)์ ๋ด์ฉ์ ์๊ทน๋ฐ์ ์์ํ๋ ์๋ฐํ ์ฑ์ฅ๊ธฐ๋ก
์ด์์๋ ๊ฝ๊ณผ ์ฃฝ์ ๊ฝ์ ์ด๋ป๊ฒ ๊ตฌ๋ณํ๋๊ฐ?
์ฑ์ฅํ๊ณ ์๋ ๊ฒ์ด ์ด์ ์๋ ๊ฒ์ด๋ค.
์๋ช ์ ์ ์ผํ ์ฆ๊ฑฐ๋ ์ฑ์ฅ์ด๋ค!
โ (7.0)<์์ ๊ฐํธ>
ํ์ธ๋ง ํ์ต๋ฒ์ ์๊ฒ ๋๋งํผ, ์ฑ์ฅ์ผ์ง๋ ์ ๋ง ๊ทธ ๋ ์ ํค์๋ ์ค์ฌ์ผ๋ก ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๋๋ก ํ๋ค.
Swiper ์ฌ์ฉ ํ๊ธฐ
๊ธฐ์กด react๋ก ํ๋ก์ ํธ๋ฅผ ํ ๋๋ ์ด์ฌํ ์ ์ฉํด๋ณด๋ ค๋ค๊ฐ ๊ณ์ ui๊ฐ ๊นจ์ ธ์ ํฌ๊ธฐํ๊ณ ์ง์ Carousel ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ๋ค. ์ง์ ๊ตฌํํ ๊ฒ๊น์ง ์ข์๋ฐ, ์๋ฌด๋๋ touch slide๋ ํ์ฅ์ฑ๋ฉด์์ ์์ฑ๋๊ฐ ๋ง์ด ๋จ์ด์ก๋ค.(์กฐ๊ธ๋ง ๊ฑด๋๋ ค๋ ๋ถ์์ง ๊ฒ๋ง ๊ฐ์ ์ปดํฌ๋ํธโฆ) ๊ทธ๋์ ์ด๋ฒ์ ๋ค์ ๊ฐ์ก๊ณ !!! swiper๋ฅผ ์ด์ฌํ ๋ฏ์ด๋ณด๋ฉด์ ์ ์ฉํด๋ณด์๋ค. ํโฆ ์ด๋ ๊ฒ ํธํ๊ณ ์ฌ์ด ๊ฑด ์ค ์์๋๋ผ๋ฉดโฆ ํํ
์ฐ์ swiper์์๋ ๋ค์ํ ๊ธฐ๋ฅ์ ๊ฐ์ง module๋ค์ ์ง์ํ๊ณ ์๋ค. ์๋์ ๊ฐ์ด ์ฌ์ฉํ๋ฉด ๋๋ค.
์ถ๊ฐ 1) ๋ณดํต slide๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ฝ๋ฐฑ์ ์คํํ๋ ค๊ณ onSlideChange๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด๊ฑด slide๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์คํ๋๋ ๊ฒ์ด ์๋๋ผ ์ ์ ๊ฐ slide๋ฅผ ๋ณ๊ฒฝํ๊ณ ๋ง์ฐ์ค๋ฅผ ๋ผ๋ ์๊ฐ์๋ง ์คํ๋๋ค. ๊ทธ๋์ onRealIndexChange๋ฅผ ์ฌ์ฉํ๋ค.
// ์๋ต
<Swiper
className="h-full w-full"
modules={[Autoplay, A11y]}
autoplay={{ delay: SWIPER.DELAY, disableOnInteraction: false }}
spaceBetween={bodyWidth < 390 ? bodyWidth : SWIPER.SPACE_BETWEEN}
slidesPerView={SWIPER.SLIDES_PER_VIEW}
loop={true}
loopedSlides={SWIPER.LOOPED_SLIDES}
onRealIndexChange={swiper => {
setCurrentSlideIndex(swiper.realIndex);
}}
>
// ์๋ต๊ทธ๋ฆฌ๊ณ ๋ ํ๋ ๊ธฐ์ตํ๋ฉด ์ข์ ๊ฒ! swiper ์ปดํฌ๋ํธ์ tailwindcss์ className์ผ๋ก display ์์ฑ์ ์ ์ฉํ๋ ๊ฑด ๋์ง ์๋๋ค. ์๋ง ๊ธฐ๋ณธ์ ์ธ ์ค์ ์์ฒด๊ฐ block์ผ๋ก ๋์ด์๋ ๊ฒ ๊ฐ๋ค. ๊ทธ๋์ display ์์ฑ์ ์ ์ฉํ๊ณ ์ถ๋ค๋ฉด style ์์ฑ์ผ๋ก ์ง์ ์ ์ฉํด์ผ ํ๋ค.
<SwiperSlide
key={index}
className="flex h-full w-full items-center justify-center"
// NOTE: Swiper ์ปดํฌ๋ํธ์ display๋ ํ
์ผ์๋ ํด๋์ค๋ฅผ ์ ์ฉํ ์ ์๋ค.
style={{ display: 'flex' }}
onClick={() => {
if (currentSlideIndex !== index) {
return;
}
if (clickedSlideIndex !== index) {
setClickedSlideIndex(index);
return;
}
setClickedSlideIndex(-1);
}}
>
// ์๋ตhttpClient๋ก ํ์ฅ์ฑ ์๊ฐํ๊ธฐ(์ถํ ์์กด์ฑ ์ฃผ์ ๋ ๊ณ ๋ คํด๋ณด๊ธฐ)
์๋์ ๊ฐ์ด api ํต์ ํ๋ ๋ก์ง์ ๊ด๋ฆฌํ๋ฉด ๋ ๊น๋ํ๊ฒ ์ถ์ํ๊ฐ ๋ ๋ฟ๋ง ์๋๋ผ ๋์ค์ ๋ค๋ฅธ ์์กด์ฑ์ ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ๊ธฐ ์ข์์ง๋ค.
export class HttpClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
fetch(path: string, options = {}) {
return fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
},
});
}
}์๋๋ ์์ HttpClient ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ์์์ด๋ค.
import { HttpClient } from '../http';
export async function getRecommendations() {
if (!process.env.NEXT_PUBLIC_BASE_API_URL) throw new Error('์กด์ฌํ์ง ์๋ ํ๊ฒฝ๋ณ์์
๋๋ค.');
const httpClient = new HttpClient(process.env.NEXT_PUBLIC_BASE_API_URL);
const response = await httpClient.fetch('/recommendations');
const json = await response.json();
return json.body;
}๊ณ ๋ฏผ: ์ฌ๋ฌ ๊ธฐ๊ธฐ์ ๋์ํ๊ธฐ
swiper๋ฅผ ํตํด Carousel ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ๋๋ฐ, ํ์ฌ slide์ ์ ์ slide์ ๋ํ style์ ๋ณ๊ฒฝํ๋ ์์ ์ ํ๋ค. ๊ทธ๋ฐ๋ฐ ๋ค์ํ ํ๋ฉดํฌ๊ธฐ์ ๋ฐ๋ผ Carousel ์ปดํฌ๋ํธ์ ui๋ฅผ ์ธ๋ฐํ๊ฒ ์กฐ์ ํ๋ ๊ฒ ์ ๋ง ์ด๋ ค์ ๋ค.(์ง๊ธ๋ ์ด๋์ ๋๋ง ๋์ํ์งโฆ ์๋ฒฝํ๊ฒ๋ ๋ชปํ๋ค.) ๊ธฐ๊ธฐ๋ง๋ค ๋ค๋ฅธ ํ๋ฉด์ ๋ํด์๋ ์ค์ ํ์ ์์ ์ด๋ป๊ฒ ๋์ํ ์ง ๋๋ฌด ์๊ณ ์ถ๋ค. ๊ทธ์ media query๋ฅผ ์ฌ์ฉํ๊ธฐ์๋ ์ค๋งํธํฐ์ ํ๋ฉด์ด ๋๋ฌด ๋ค์ํ๋ฐโฆ ํ ๊ณ์ ์ด ๋ถ๋ถ์ ์ด๋ป๊ฒ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ง ์ฐพ์๋ด์ผ๊ฒ ๋ค.
๐ ํ๊ณ
์ฌ์ค ์์ ๋ด์ฉ๋ง๊ณ ๋ Carousel์์ ์ฌ์ฉ๋๋ image ํฌ๊ธฐ ์กฐ์ ๋ถํฐ ์ง์ํ tailwind ์ค๋ฅ๊น์งโฆ ๋ง์์ง๋ง ์ด๊ฑด Carousel ์ปดํฌ๋ํธ๋ฅผ ์ ๋ฆฌํ ๋ ํ๋ฒ์ ์์ฑํด์ผ๊ฒ ๋ค. ๊ทธ๋์ ๋ ์ค๋์ ์ง์ง ์ผ์ฐ ์๋ คํ๋๋ฐโฆ ์ผ๋ฅธ ์์ผ์ง!!!