front-end/Nextjs

[NextJS] 동적 라우팅 (with React-Query)

hojung 2022. 7. 6.
728x90
반응형

1. 무엇을 하려고 하는가?

나는 개인 블로그를 만들고 있다. 이 중에 블로그 글을 작성하여 업로드를 하면 /Album페이지에 작성된 블로그 글을 

React-Slick을 이용해 carousel형태로 보여주고 이 Carousel에 들어있는 이미지를 누르게 되면 해당하는 블로그 글 상세 페이지로 이동하는 기능을 구현하려고 한다. 

이 기능을 NextJS에서는 dynamic Routing 즉, 동적 라우팅이라는 기능으로 제공하고 있는데 거의 프론트 엔드 업무를 진행할 때는 필수적인 기능이라 기록해두고자 한다. 

 

React에서는 useParams훅을 이용해 query의 파라미터를 받아오고 이를 통해 다른 데이터 페칭 요청을 보냈다. (axios등 이용)

 

NextJS에서는 pages폴더 안에 [변수명].tsx , [변수명].jsx 등으로 파일이름을 지정하면 자동으로 동적 라우팅을 제공해준다. 

 

2. 어떻게 하는가?

1. Detail Component제작 

우선 위에서 설명한 대로 상세페이지를 지정할 파일을 만들어주겠다. 

상세 페이지를 담당할 파일

나는 블로그 포스트의 postNum이라는 데이터 별로 axios요청을 다르게 보내서 데이터를 받아올 것이므로 알아보기 쉽게 [postNum].tsx라는 파일 명으로 만들어 주었다. 

그 후 Detail component 틀을 만들어주었다. 이 틀은 보여지는 레이아웃이나 스타일은 다르고 안에 있는 데이터들만 바꾸는 그런 형식이다. 예를 들면 네이버 쇼핑에서 다음과 같이 상품을 눌렀을 때 아래와 같이 안에 있는 내용만 변하지 기본적인 레이아웃은 변하지 않는다. 동적라우팅이란 그런것이다. 

그리고 두 페이지를 비교해보면 요청하는url이 다음과 같음을 알 수 있다. 

두 페이지 모두 product라는 url까지는 똑같으나 그 뒤에 있는 파라미터로 인해 데이터가 다르게 들어온다는 것을 알 수 있다. 따라서 나는 이 레이아웃에 해당하는 Detail Component를 만들어주었다. 

import {
	SingleAlbumContentContainer,
	SingleAlbumDetailContainer,
	SingleAlbumDetailDescription,
	SingleAlbumDetailDescriptionContainer,
	SingleAlbumDetailImage,
	SingleAlbumDetailImageContainer,
	SingleAlbumDetailTitle,
	SingleAlbumDetailTitleContainer,
} from "./styled";

type DetailProps = {
	title: string;
	content: string;
	filepath: string;
};

const SingleAlbumDetail = ({ title, content, filepath }: DetailProps) => {
	return (
		<SingleAlbumDetailContainer>
			<SingleAlbumDetailImageContainer>
				<SingleAlbumDetailImage src={`http://localhost:5000/${filepath}`} alt={title} width={300} height={400} />
			</SingleAlbumDetailImageContainer>

			<SingleAlbumContentContainer>
				<SingleAlbumDetailTitleContainer>
					<SingleAlbumDetailTitle>{title}</SingleAlbumDetailTitle>
				</SingleAlbumDetailTitleContainer>
				<SingleAlbumDetailDescriptionContainer>
					<SingleAlbumDetailDescription>{content}</SingleAlbumDetailDescription>
				</SingleAlbumDetailDescriptionContainer>
			</SingleAlbumContentContainer>
		</SingleAlbumDetailContainer>
	);
};

export default SingleAlbumDetail;

위의 코드를 보면 알 수 있듯이 Image태그 안에 filepath가 들어가고 title에는 {title} description에는 {content}가 들어가는 것을 확인할 수 있다. 

 

2. AlbumList를 모아둔 페이지에서 Link를 이용한 연결 

그러면 이렇게 만든 Detail Component를 어떻게 적용해야 하는가?

나는 Album 리스트가 모여있는 /Album주소에서 이 블로그 포스트들을 모아서 보여준다음 해당 포스트를 누르면 Detail page로 이동하고 싶다. 따라서 NextJS에서 제공하는 Link컴포넌트는 /Album페이지에 존재해야한다. 

 

따라서 리스트를 모아둔 페이지로 이동했다. 

export type singleAlbumPost = {
	title: string;
	content: string;
	filePath: string;
	postNum: string;
};

const Albums = ({ singleAlbums }: InferGetServerSidePropsType<{ singleAlbums: singleAlbumPost[] }>) => {
	const { data, isLoading } = useGetSingleAlbumList();
	const handleContentLength = (content: string) => {
		if (content.length > 15) {
			return content.slice(0, 15) + "...";
		} else {
			return content;
		}
	};
	return (
		<AlbumListContainer>
			<Link href="Album/SingleAlbumWrites">
				<SingleAlbumWritesButton>싱글 앨범 작성</SingleAlbumWritesButton>
			</Link>

			{isLoading ? (
				<div>is Loading...</div>
			) : (
				<AxiosSlider title="Single Albums">
					{data?.data.singleAlbumList.map((postitem) => (
						<LinkCard
							key={postitem.filePath}
							src={`http://localhost:5000/${postitem.filePath}`}
							title={postitem.title}
							alt={postitem.content}
							year={handleContentLength(postitem.content)}
							width={300}
							height={400}
							linkurl={`/Album/singleAlbum/[postNum]`}
							query={postitem.postNum}
						></LinkCard>
					))}
				</AxiosSlider>
			)}
		</AlbumListContainer>
	);
};

export default Albums;

리스트를 모아둔 페이지는 다음과 같았다. 보면 react-query훅을 이용해서 데이터 리스트들을 받아온 후 map함수를 이용해서 LinkCard에 데이터를 넣어주고 있다. 이렇게 하면 Carousel컴포넌트 안에 데이터가 주입된 카드 컴포넌트들이 들어가게 될 것이다. 

 

따라서 페이지를 확인해보면 다음과 같다. 

Express 서버에 저장된 이미지들과 MongoDB에 저장된 제목 그리고 사진 설명들이 잘 불러져 오는 것을 확인할 수 있다. 

 

이 AlbumList를 모아둔 페이지에서 중요한 것은 Link컴포넌트이다. 

보면

LinkURL

Linkurl속성으로 /Album/singleAlbum/[postNum]을 전달해주는 것을 볼 수 있다. 

참고로 LinkCard 컴포넌트는 다음과 같이 생겼다. 

type LinkCardProps = {
	src: StaticImageData | string;
	title: string;
	alt: string;
	year: string;
	width: number;
	height: number;
	linkurl: string;
	query: number | string;
};

const LinkCard: React.FC<LinkCardProps> = ({ src, title, alt, year, width, height, linkurl, query }) => {
	return (
		<Link
			href={{
				pathname: `${linkurl}`,
				query: { postNum: query },
			}}
		>
			<CardContainer>
				<CardImageWrapper>
					<Image
						src={src}
						alt={alt}
						layout="responsive"
						quality={100}
						width={width}
						height={height}
						unoptimized={true}
					/>
				</CardImageWrapper>
				<CardInfo>
					<CardTitle>{title}</CardTitle>
					<CardKeyWord>{year}</CardKeyWord>
				</CardInfo>
			</CardContainer>
		</Link>
	);
};

보면 props로 전달받은 linkurl을 NextJS의 기본 컴포넌트 Link에서 pathname으로 사용하고 있는 것을 볼 수 있다. 또한 query속성에서 위에서 전달받은 postNum이 query라는 것을 명시해주어야한다. 

 

3. 데이터 불러오기 

기본 axios요청을 보내도 되지만 나는 데이터 페치 라이브러리 react-query를 사용했다. 

react-query를 사용해서 detail component에 필요한 정보를 불러오는 훅은 다음과 같다. 

import { singleAlbumPost } from "pages/Album";
import axios, { AxiosResponse, AxiosError } from "axios";
import { useQuery } from "react-query";

export type singleAlbumDetailAxios = {
	singleAlbumDetail: singleAlbumPost;
};

const getSingleAlbumDetail = (postNum: string | string[]) =>
	axios.get(`http://localhost:5000/singleAlbum/getDetail/${postNum}`);

const useGetSingleAlbumDetail = (postNum: string | string[]) => {
	const queryFn = () => getSingleAlbumDetail(postNum);
	return useQuery<AxiosResponse<singleAlbumDetailAxios>, AxiosError>(["singleAlbumDetail", postNum], queryFn);
};

export default useGetSingleAlbumDetail;

다음과같이 axios요청을 ${postNum}에 따라 다르게 보낸다. 서버에서도 다르게 처리해주어야하지만 여기서는 front-end만 설명하겠다. 백엔드에서의 처리는 간단하다. 다음 포스트에서 설명하겠다. 

 

그럼 이 훅을 DetailComponent에 적용해준다. 

아까 만든 동적라우팅 파일에서 아까 만든 axios요청을 react-query를 통해 하는 훅을 만들어준 후 받아온 데이터를 아까만든 detail component에 props로 전달해주면 된다. 그러면 다음과 같이 list가 모여있는 페이지에서 card를 누르면

다음과 같이 상세 페이지가 불러져온다. 

728x90
반응형

댓글