Nextjs
2023-04-26 14:40
홈화면 하단에 최근에 작성한 Post와 진행 중인 프로젝트를 슬라이드 형식으로 만드려고 한다.
우선, post 데이터와 project 데이터를 받아오기 위한 코드를 작성한다.
pages/index.jsexport async function getStaticProps() {
// notion api를 통해서 데이터를 받아오기 위한 options를 정의
const options = {
method: "POST",
headers: {
Accept: "application/json",
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
Authorization: `Bearer ${TOKEN}`,
},
body: JSON.stringify({
page_size: 100,
}),
};
// posts 와 project의 database id를 작성해놓은 env파일에서 가져와서 fetch
const postsRes = await fetch(`https://api.notion.com/v1/databases/${POST_DATABASE_ID}/query`, options);
const projectRes = await fetch(`https://api.notion.com/v1/databases/${PROJECT_DATABASE_ID}/query`, options);
// 받아온 data json변환
const postsData = await postsRes.json();
const projectData = await projectRes.json();
// posts에는 일반 포스트도 있지만 project 진행하면서 작성한 포스트들도 있기 때문에
// filter 메소드로 일반 포스트들만 가져온다.
const posts = postsData.results.filter((v) => v.properties.project.checkbox !== true);
// results 부분만 필요하기 때문에 아래와 같이 작성
const projects = projectData.results;
const languages = languageData.results;
return {
props: { posts, projects },
};
}
javascript// 위에서 작성한 데이터를 받아온다
export default function Home({ posts, projects }) {
// 최근 포스트 리스트에 3개의 아이템만 노출시키기 위한 slice 사용
const slicePosts = posts.slice(0, 3)
return (
<Base>
<Layout>
...
// RecentPost에 data 전달
<RecentPost data={slicePosts} projects={projects} />
</Layout>
</Base>
)
}
javascriptcomponents/RecentPost.jsxconst RecentPost = ({ data, projects }) => {
// 슬라이드를 위한 useRef 사용
const slideRef = useRef();
// 슬라이드 wrapper의 넓이를 가져오기 위한 state
const [slideWidth, setSlideWidth] = useState(0);
// 슬라이드의 현재 번호를 저장하기 위한 state
const [slideIdx, setSlideIdx] = useState(0);
// 렌더링 될 때, slideRef의 현재 보여지는 넓이의 값을 아까 만들어둔 state에 저장
useEffect(() => {
setSlideWidth(slideRef.current.clientWidth);
}, []);
// 슬라이드 이전과 다음 메소드 정의
const handlePrev = () => {
setSlideIdx((prev) => prev - 1);
};
const handleNext = () => {
setSlideIdx((prev) => prev + 1);
};
return (
<Base>
<Header>
<Title>{slideIdx === 0 ? "최근 포스트" : "진행중인 프로젝트"}</Title>
<ArrowWrapper>
<PrevBtn onClick={handlePrev} disabled={slideIdx === 0}>
// 이전 버튼 svg 파일
</PrevBtn>
<NextBtn onClick={handleNext} disabled={slideIdx === 1}>
// 다음 버튼 svg 파일
</NextBtn>
</ArrowWrapper>
</Header>
<Wrapper>
<SlideWrapper ref={slideRef} move={slideIdx * slideWidth} minWidth={slideWidth}>
<SlideItem>
<RecentPostsList data={data} />
</SlideItem>
<SlideItem>
<RecentProjectList projects={projects} />
</SlideItem>
</SlideWrapper>
</Wrapper>
</Base>
);
};
export default RecentPost;
javascript구조를 보면 Wrapper 안에 SlideWrapper이 있고 그 안에 SlideItem이 있다.
Wrapper에 overflow:hidden 을 주어 넘어가는 요소를 숨기고
SlideWrapper에 move와 minWidth 값을 정의하고 props값으로 전달한다.
move 값은 slideIdx * slideWidthminWidth 값은 slideWidthconst Wrapper = styled.div`
overflow: hidden;
`;
const SlideWrapper = styled.div`
transform: translateX(0);
display: flex;
flex-wrap: nowrap;
transition: all 0.3s;
transform: ${({ move }) => (move ? `translateX(-${move}px)` : undefined)};
> div {
min-width: ${({ minWidth }) => (minWidth ? `${minWidth}px` : undefined)};
}
`;
javascript위 코드에 의하여 렌더링 될 때 slideWrapper의 넓이가 500px이라고 하면
move값은 0 * 500 으로 0이고, minWidth 값은 500이다.
그래서 첫번째 slideItem 만 보여질 것이다.
다음 버튼 클릭시, slideIdx가 0에서 1로 변하고 move 값은 1 * 500 으로 500이 된다.
move 값이 true가 되므로
transform: ${({ move }) => (move ? `translateX(-${move}px)` : undefined)};
javascriptSlideWrapper의 transform값이 translateX(-500px) 으로 변경되어 두 번째 슬라이드 아이템이 보이는 로직이다.
모바일 환경에서는 스크린의 넓이가 변경될 일이 많지 않지만 PC에서는 스크린의 넓이가 변경되는 것이 자유롭다.
스크린의 넓이가 변경될 시 위에서 정의한 변수인 minWidth 값이 변경되지 않아서 슬라이드가 어색하게 출력된다.
이를 해결하기 위해서, 스크린 변경을 감지하기 위한 코드 작성이 필요하다.
useEffect(() => {
if (typeof window !== "object") return;
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
const handleResize = () => {
setSlideWidth(slideRef.current.clientWidth);
};
javascriptresize 될 때마다, setSlideWidth 실행하면 해결 완료!