본문 바로가기

프로젝트/오심테(오늘의 심리테스트)

[Nextjs] 공통 컴포넌트(헤더, 네비게이션 바) 적용 및 동적 라우팅

반응형

오늘은 헤더와 네비게이션 바를 공통 컴포넌트로 만들고, 심리테스트 목록에서 각 아이템을 클릭하면 아이템의 id에 따른 동적 라우팅이 될 수 있도록 구현하였다.

 

먼저 헤더와 네비게이션 바를 공통 컴포넌트로 만들기 위해 Components라는 폴더에 layout.js라는 새로운 파일을 추가해주었다. 

import Header from "./header";
import NavBar from "./navbar";

export default function Layout(props) {
  return (
    <div className="flex flex-col h-screen">
      <Header className="sticky top-0" />
      <NavBar className="sticky top-0" />
      <div className="flex-1 overflow-auto">{props.children}</div>
    </div>
  );
}

그런 다음 index.js 파일과 testId의 index.js 파일에서 보여주고 싶은 컴포넌트를 Layout 컴포넌트로 감싸주었다.

export default function Home() {
  return (
    <Layout>
      <TestList testList={DUMMY_TEST} />
    </Layout>
  );
}

이렇게 하면 테스트 목록을 볼 때도 헤더와 네비게이션 바가 보이고, 테스트 상세 내용을 볼 때도 동일하게 적용된다.

번외로 오늘 item 컴포넌트 디자인을 손봤다. title은 두 문장까지만 보이게 했고, title 길이와 상관없이 모든 컴포넌트의 높이가 동일하며 버튼의 위치가 고정될 수 있도록 하였다. Nextjs는 반응형 쪽이 잘 지원되어있어 내친김에 반응형도 적용시켜보았다. 두 시간은 사용한 것 같다. 아직 UI 구성(퍼블리싱)은 쉽지 않다. 위치 정렬과 반응형.. 이 두 가지가 제일 까다롭다. 하다보면 늘겠지.

참고로 반응형은 chatGPT에게 물어보았다. chatGPT에게 질문을 바꿔가며 다양하게 물어보아 원하는 대답을 얻었을 때의 그 짜릿함이란...

return (
    <div className="col-span-6 md:col-span-4 lg:col-span-3 w-full bg-purple-200">
      <div className="aspect-w-2 aspect-h-1">
        <Image
          src={image}
          alt={title}
          width={400}
          height={200}
          className="object-cover"
        />
      </div>
      <div className="p-3 flex flex-col justify-between h-32">
        <div className="text-xl font-semibold mb-2 text-gray-50 line-clamp-2">
          {title}
        </div>
        <div className="flex justify-center">
          <button
            className="bg-purple-400 text-white px-4 py-2 rounded-full"
            onClick={handleShowDetails}
          >
            테스트 하러가기
          </button>
        </div>
      </div>
    </div>
  );
}

테스트 상세 페이지는 아직 더 다듬어야 한다. 이건 다음에 하기로 하자. 위에서 시간을 꽤 썼기 떄문에 쉬고싶다.

 

 

다음으로는 동적 라우팅이다.

앞의 이미지와 같이 테스트 별 동적 라우팅을 적용시켜주기위해 [testId]라는 폴더를 만들어주고 그 안에 index.js 파일을 만들어주었다. 이 index.js 파일 안에 테스트 상세 내용과 테스트 결과를 분기처리하여 보여줄 예정이다.

useRouter 훅을 사용해 테스트 하러가기 버튼을 누르면 라우팅이 되도록 처리해준다. 

import Image from "next/image";
import { useRouter } from "next/router";

export default function TestItem({ id, image, title }) {
  const router = useRouter();
  const handleShowDetails = () => {
    router.push(`/${id}`);
  };

그 후 index.js 안에 간략하게 컴포넌트 구조를 짜주고 리스트에서 첫번째 아이템을 누르면, 다음과 같이 라우팅이 잘 되는 것을 볼 수 있다.

nextjs를 사용하기 이전처럼 직접 라우팅 시켜주지 않아도 되는 점은 무지 편하다.  비교를 위해 이전 프로젝트(맛피)의 app.tsx 일부를 가져왔다. 크 많다 많아..

아직 nextjs를 사용해본지 몇일되지 않았지만, 라우팅면에서는 매우 편리한 것 같다. tailwind CSS도 반응형이나 간편함? 면에서는 매우 만족스럽고!

return (
    <RecoilRoot>
      <AppContainer toggle={visible}>
        <Sidebar />
        <div className={`feed_container_${visible ? "hidden" : ""}`}>
          <Header />
          <Routes>
            <Route path="/" element={<Domain />} />
            <Route path="/pickers" element={jwtToken ? <MatPicker /> : <LoginPage />} />
            <Route path="/pickers/:id" element={jwtToken ? <MatPickerDetail /> : <LoginPage />} />
            <Route path="/search" element={<SearchPage />} />
            <Route path="/search/:name" element={<SearchDetailPage />} />
            <Route path="/newplaces" element={jwtToken ? <MatPlacePostPage /> : <LoginPage />} />
            <Route path="/places/:placeId" element={<MatPlacePage />} />
            <Route path="/mypage" element={jwtToken ? <MyPage /> : <LoginPage />} />
            <Route path="/people/:id" element={<MatPeople />} />
            <Route path="/matPickers" element={<MatPeoplePickerPage />} />
            <Route path="*" element={<NotFoundPage />} />
            <Route path="/login" element={<LoginPage />} />
          </Routes>
        </div>
        <HeaderFeedHide visible={visible} setVisibility={setVisibility} />
        <KakaoMap />
      </AppContainer>
    </RecoilRoot>
  );

간단히 분기처리도 해주었다.

컴포넌트 구조를 짜기 위해 데이터는 하드코딩해주었다. 사용자가 선택지에서 고르면 결과가 나올 수 있도록 상태를 하나 만들고 그 상태에 따라 보일 컴포넌트를 구분해주었다.

import Image from "next/image";
import Layout from "../../components/layout/Layout";
import { useEffect, useState } from "react";

export default function TestDetails() {
  const option = ["들어간다", "들어가지 않는다"];
  const answer = ["당신은 용감한 사람!", "당신은 겁이 많은 사람!"];
  const [isClicked, setIsClicked] = useState(false);
  const [result, setResult] = useState(0);
  const handleShowResult = (idx) => {
    setResult(idx);
    setIsClicked(true);
  };

  useEffect(() => {
    console.log(isClicked);
  }, [isClicked]);

  return (
    <Layout>
      {!isClicked ? (
        <div className="flex flex-col">
          <div>
            <div>깊은 숲 속에 성을 발견했다. 들어갈까 말까?</div>
          </div>
          <div>
            <Image
              src="https://a.cdn-hotels.com/gdcs/production143/d584/9d0b3a81-4f8a-4f26-8aad-9c165dff75fa.jpg"
              alt="깊은 숲 속에 성을 발견했다. 들어갈까 말까?"
              width={400}
              height={200}
              className="object-cover"
            />
          </div>
          <div>
            {option.map((el, idx) => (
              <button
                key={idx}
                className="bg-purple-400 text-white px-4 py-2 rounded-full"
                onClick={() => handleShowResult(idx)}
              >
                {el}
              </button>
            ))}
          </div>
        </div>
      ) : (
        <div>{answer[result]}</div>
      )}
    </Layout>
  );
}

일단은 선택지의 idx에 따른 결과만 간단히 보여주도록 짜놓았다.

이제 다음에 해야할 것은 테스트 상세 정보와 테스트 결과 컴포넌트 꾸미기이다! 이게 끝나면 컴포넌트가 어느정도 다 만들어지는 것이 되어 더미데이터를 버리고 json server를 사용하는 방식으로 리팩토링할 예정이다.

 

공통 컴포넌트 적용하는데 참고한 블로그)

https://geonlee.tistory.com/182

 

[Next.js] 공유된 컴포넌트 사용하기

이 글은 Next.js의 공식 튜토리얼을 번역한 글입니다. 오역 및 오탈자 발견시 댓글로 제보해주시면 감사하겠습니다. Next.js는 결국 페이지라는 것을 알 것입니다. 리액트 컴포넌트를 내보내고 해당

geonlee.tistory.com

동적 라우팅 관련 참고 강의) 

https://www.udemy.com/course/best-react/learn/lecture/28518403#overview

 

반응형