오늘은 헤더와 네비게이션 바를 공통 컴포넌트로 만들고, 심리테스트 목록에서 각 아이템을 클릭하면 아이템의 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>
);
}
이제 다음에 해야할 것은 테스트 상세 정보와 테스트 결과 컴포넌트 꾸미기이다! 이게 끝나면 컴포넌트가 어느정도 다 만들어지는 것이 되어 더미데이터를 버리고 json server를 사용하는 방식으로 리팩토링할 예정이다.
공통 컴포넌트 적용하는데 참고한 블로그)
https://geonlee.tistory.com/182
동적 라우팅 관련 참고 강의)
https://www.udemy.com/course/best-react/learn/lecture/28518403#overview
'프로젝트 > 오심테(오늘의 심리테스트)' 카테고리의 다른 글
[NextJS & Firestore] NextJS에 Firestore 연동해보기 (0) | 2023.05.23 |
---|---|
[Tailwind CSS] 테스트 상세/결과 컴포넌트(feat. SweetAlert2 & MUI Icon) (0) | 2023.05.22 |
[Tailwind CSS] 전체 화면 스크롤 바 없애기(Header 고정) (0) | 2023.05.19 |
[Tailwind CSS] nextjs is missing required "width" property & Error: Invalid src prop "" on `next/image` (0) | 2023.05.19 |
[NextJS & Tailwind CSS] 헤더 & 네비게이션 바 만들기 (0) | 2023.05.18 |