노마드 코더 강의는 끝났지만 학습하는 개념으로 꾸준히 개선하고자한다.
챌린지 신청도 고려했지만 유료 강의를 먼저 수강해야한다고 한다.ㅠㅠ
자기 계발을 위해 유료 강의도 고려해 봐야겠다!
이번에는 메인페이지에 인기 영화 TOP10을 추가하고
이를 캐러셀로 한줄로 보이도록해보는 작업을 해보자!
sort
메서드를 활용할 수 있다.const voteAverage = movies.sort((a, b) => b.vote_average - a.vote_average).slice(10)
sort
는 두가지 인자를 받아 온다.a-b
는 내림차순, b-a
는 오름차순으로 값을 반환한다.slice(10)
해주었다.sort?
배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환합니다.
정렬은 stable sort가 아닐 수 있습니다. 기본 정렬 순서는 문자열의 유니코드 코드 포인트를 따릅니다.
-mdn정리하자면
sort
는 오름차순, 내림차순으로 정렬을 할 수 있다.단, 유니코드 기반으로 하기 때문에 간혹 제대로 된 결과 값을 가져오지 않기도 한다.
sort메서드 사용 법은 알지만 원리는 잘 모르는 것 같다.
시간 될 때 정독해 봐야겠다.
원리를 제대로 아는 사람이 거의 없는 Sort메서드
"use client"
import Image from "next/image";
import styles from "../styles/movie-hot.module.css";
import { useRouter } from "next/navigation";
interface MoviePropsT {
id: number;
title: string;
poster_path: string;
idx: number;
}
const MovieHot = ({ id, title, poster_path, idx }: MoviePropsT) => {
const router = useRouter();
const onClick = () => {
router.push(`/movie/${id}`);
}
return (
<div className={styles.movieHot}>
<span className={styles.hotNum}>{idx + 1}</span>
<div className={styles.imgWrap}>
<Image src={poster_path} fill alt={title} onClick={onClick} />
</div>
</div>
);
};
export default MovieHot;
MovieHot
이라는 컴포넌트로 분리했다.캐러셀?
- 캐러셀은 이미지나 텍스트의 슬라이드를 가로로 슬라이드시켜 여러 개를 표시하는 컴포넌트
캐러셀
return (
<section className={styles.carouselContainer}>
<div className={styles.carouselWrap}>
{voteAverage.map((movie: ImoviesT, idx) => (
<MovieHot key={movie.id} id={movie.id} title={movie.title} idx={idx} poster_path={movie.poster_path} />
))}
</div>
<button className={styles.prevBtn} onClick={handlerPrev}><Arrow type='left' /></button>
<button className={styles.nextBtn} onClick={handlerNext}><Arrow type='right' /></button>
</section>
);
figma
를 통해 화살표 아이콘을 제작하고 이를 svg로 내보냈다.type
속성을 추가해 하나의 컴포넌트만 사용해 위치를 변경할 수 있도록했다.const [currentIndex, setCurrentIndex] = useState<number>(0);
const carouselRef = useRef<HTMLDivElement>(null);
.
.
.
return (
<section className={styles.carouselContainer}>
<div ref={carouselRef} className={styles.carouselWrap}>
{voteAverage.map((movie: ImoviesT, idx) => (
<MovieHot key={movie.id} id={movie.id} title={movie.title} idx={idx} poster_path={movie.poster_path} />
))}
</div>
<button className={`${styles.prevBtn} ${prevDis}`} onClick={handlerPrev}><Arrow type='left' /></button>
<button className={`${styles.nextBtn} ${nextDis}`} onClick={handlerNext}><Arrow type='right' /></button>
</section>
);
ref
속성을 추가했다.DOM
에 스타일을 직접 추가하기 위해서다.const handlerPrev = () => {
if (currentIndex > 0) {
setCurrentIndex(currentIndex - 1);
}
}
const handlerNext = () => {
if (currentIndex <= 2) {
setCurrentIndex(currentIndex + 1);
}
}
handlerPrev
클릭 시-1
handlerNext
클릭 시+1
useEffect(() => {
if (carouselRef.current) {
carouselRef.current.style.transition = "all 0.6s";
if (currentIndex > 0) {
carouselRef.current.style.transform = `translateX(${currentIndex * 25}%)`;
}
if (currentIndex <= 2) {
carouselRef.current.style.transform = `translateX(-${currentIndex * 25}%)`;
}
}
}, [currentIndex])
currentIndex * 20%
을 준다.-currentIndex * 20%
를 준다.transform으로 캐러셀을 이동시킨 이유
transform
은 GPU로 동작하기에 position 보다 CPU 부하가 적다.margin
을 이용해서 구현할 수도 있다.
const nextDis = currentIndex >= 2 ? styles.disNone : styles.disBlock;
const prevDis = currentIndex > 0 ? styles.disBlock : styles.disNone;
.
.
.
return (
<section className={styles.carouselContainer}>
<div ref={carouselRef} className={styles.carouselWrap}>
{voteAverage.map((movie: ImoviesT, idx) => (
<MovieHot key={movie.id} id={movie.id} title={movie.title} idx={idx} poster_path={movie.poster_path} />
))}
</div>
<button className={`${styles.prevBtn} ${prevDis}`} onClick={handlerPrev}><Arrow type='left' /></button>
<button className={`${styles.nextBtn} ${nextDis}`} onClick={handlerNext}><Arrow type='right' /></button>
</section>
CSS-in-JS
로 props를 활용해 동적인 스타일을 줄 수 있었다.객체 형식
으로 굳이 className 내부 말고도 외부에서 작성할 수 있다.이 방법은 동적으로 스타일할 내용이 많아지만 코드도 길어질 것 같다. 아마 더 좋은 방법이 있지 않을까 싶다.
const nextDis = currentIndex >= 2 ? styles.disNone : styles.disBlock;
const prevDis = currentIndex > 0 ? styles.disBlock : styles.disNone;
${클래스 이름}
내부에 작성해 해결 할 수 있다.<button className={`${styles.prevBtn} ${prevDis}`} onClick={handlerPrev}><Arrow type='left' /></button>
<button className={`${styles.nextBtn} ${nextDis}`} onClick={handlerNext}><Arrow type='right' /></button>
"use client";
import { useEffect, useRef, useState } from "react";
import { ImoviesT } from "@/types/type";
import styles from "../styles/movie-carousel.module.css";
import Arrow from "./icons/arrow";
import MovieHot from "./movie-hot";
const MovieCarousel = ({ movies }: { movies: ImoviesT[] }) => {
const voteAverage = movies.sort((a, b) => b.vote_average - a.vote_average).slice(10)
const carouselRef = useRef<HTMLDivElement>(null);
const [currentIndex, setCurrentIndex] = useState<number>(0);
const nextDis = currentIndex >= 2 ? styles.disNone : styles.disBlock;
const prevDis = currentIndex > 0 ? styles.disBlock : styles.disNone;
const handlerPrev = () => {
if (currentIndex > 0) {
setCurrentIndex(currentIndex - 1);
}
}
const handlerNext = () => {
if (currentIndex <= 2) {
setCurrentIndex(currentIndex + 1);
}
}
useEffect(() => {
if (carouselRef.current) {
carouselRef.current.style.transition = "all 0.6s";
if (currentIndex > 0) {
carouselRef.current.style.transform = `translateX(${currentIndex * 25}%)`;
}
if (currentIndex <= 2) {
carouselRef.current.style.transform = `translateX(-${currentIndex * 25}%)`;
}
}
}, [currentIndex])
return (
<section className={styles.carouselContainer}>
<div ref={carouselRef} className={styles.carouselWrap}>
{voteAverage.map((movie: ImoviesT, idx) => (
<MovieHot key={movie.id} id={movie.id} title={movie.title} idx={idx} poster_path={movie.poster_path} />
))}
</div>
<button className={`${styles.prevBtn} ${prevDis}`} onClick={handlerPrev}><Arrow type='left' /></button>
<button className={`${styles.nextBtn} ${nextDis}`} onClick={handlerNext}><Arrow type='right' /></button>
</section>
);
};
export default MovieCarousel;
사실 넷플릭스의 인기순 처럼 이전, 다음 버튼 클릭 시 무한으로 이동되게 하고 싶었다.
아마 Node를 복제해서 붙이지 않았을까 싶다.
이점에 대해서는 좀 더 학습이 필요하다.
또한, 반응형인 경우를 고려하지 않고 작업을 진행했다.
만약 반응형으로 진행할 경우를 대비하여 스타일 및 코드가 수정될 것 같다.
현재는 어떤 식으로 동작하는지 등을 학습하는 개념으로 진행하고 있어, 리팩토링 등도 아직이다.
참고 - 더 있었지만.. 너무 많은 구글링을 했다..🥹
React에서 className 여러개 주기