홈화면에 최근 Post와 프로젝트 보여주기

Nextjs

  • slide

2023-04-26 14:40

코드

홈화면 하단에 최근에 작성한 Post와 진행 중인 프로젝트를 슬라이드 형식으로 만드려고 한다.

우선, post 데이터와 project 데이터를 받아오기 위한 코드를 작성한다.

pages/index.js

export 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>
  )
}
javascript

components/RecentPost.jsx

const 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에 moveminWidth 값을 정의하고 props값으로 전달한다.

  • move 값은 slideIdx * slideWidth
  • minWidth 값은 slideWidth
const 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)};
javascript

SlideWrapper의 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);
  };
javascript

resize 될 때마다, setSlideWidth 실행하면 해결 완료!