수요일부터 리액트 첫 프로젝트를 시작했다.
우리팀은 주제를 쇼핑몰로 정했고, 나는 상세페이지 메뉴 컴포넌트
, 리뷰 CRUD
기능 구현을 맡았다.
프로젝트 준비
두번의 프로젝트를 통해서 바로 코드를 작성하는 것보다, 디테일한 부분까지 미리미리 설계하는 게 중요하다는 것을 깨달았다.
다행히 다른 팀원분들도 비슷한 생각이었는지 우리팀은 거의 이틀동안 디자인, 데이터, 프로젝트 설계에 시간을 쏟았다. 다른팀들이 어디까지 구현했다더라~ 라는 말이 들려와 마음이 조급해지기도 했지만, 결과적으로 역시 잘한 선택이었다! 🤭
프로젝트 디자인
프로젝트 디자인이 기깔나서 너무 마음에 든다..! 💕
디자인 해주신 팀원분과는 첫 프로젝트도 같이 했는데, 그때도 프로젝트 컨셉이랑 디자인을 뚝딱뚝딱 만들어 오신 덕분에 정말정말 편하게 작업했던 기억이 있다.
리액트 프로젝트
상세페이지, 댓글 작성/유효성 검사 기능을 구현했다.
// Router.jsx
const Router = createBrowserRouter(
createRoutesFromElements(
<Route element={<Root />} errorElement={<NotFound />}>
<Route path="/" element={<Home />} />
<Route path="/categories" element={<Categories />} />
<Route path="/categories/:id" element={<Categories />} />
<Route path="/kimchis/:id" element={<ProductDetail />}>
<Route path="/kimchis/:id/description" element={<Description />} />
<Route path="/kimchis/:id/recipe" element={<Recipe />} />
<Route path="/kimchis/:id/review" element={<Review />} />
</Route>
</Route>,
),
);
React Router를 공부하다가 NavLink
컴포넌트에 대해 알게되었는데, 정말정말 꿀기능이다.
일반 Link
태그와 다른 점은 isActive
상태를 가지고 있어서, 조건부 스타일링이 가능하다는 것이다.
상품설명이 클릭된 상태일 경우 (경로가 /kimchis/:id/description
일 때) 다른 메뉴 (레시피, 후기)가 음영처리 되도록 스타일링 했다.
❗️ html
a
태그를 사용하면 안되는 이유
React에서 a 태그를 사용하면 페이지가 이동하면서 새로고침 된다.
새로고침 없이 컴포넌트만 re-render 하기 위해서는 React Router에서 제공하는 컴포넌트인 Link, NavLink 또는 useNavigate 등을 사용해야 한다.
// DetailMenus.jsx
import React from 'react';
import { NavLink, useParams } from 'react-router-dom';
export default function DetailMenus() {
const { id } = useParams();
return (
<ul style={menuStyle}>
<li style={listStyle}>
<NavLink
to={`/kimchis/${id}/description`}
style={({ isActive }) =>
isActive ? activeLinkStyle : inactiveLinkStyle
}
>
상품설명
</NavLink>
</li>
<li style={listStyle}>
<NavLink
to={`/kimchis/${id}/recipe`}
style={({ isActive }) =>
isActive ? activeLinkStyle : inactiveLinkStyle
}
>
레시피
</NavLink>
</li>
<li style={listStyle}>
<NavLink
to={`/kimchis/${id}/review`}
style={({ isActive }) =>
isActive ? activeLinkStyle : inactiveLinkStyle
}
>
후기
</NavLink>
</li>
</ul>
);
}
const menuStyle = {
width: '100%',
padding: '10px',
boxSizing: 'border-box',
display: 'flex',
margin: '30px 0',
};
const listStyle = {
border: '1px solid #c2c2c2',
textAlign: 'center',
flex: '1 1 auto',
};
const activeLinkStyle = {
display: 'block',
padding: '20px 0',
textDecoration: 'none',
color: 'black',
fontSize: '16px',
};
const inactiveLinkStyle = {
backgroundColor: '#eeeeee',
display: 'block',
padding: '20px 0',
textDecoration: 'none',
color: 'black',
fontSize: '16px',
};
상품설명 페이지의 경로 구조는 /kimchis/:id/description
으로 되어있다.
위 경로로 들어오면 상세페이지에서 Outlet
를 통해 Description
컴포넌트를 렌더링해주게 된다.
그런데 만약, /kimchis/:id
라는 경로로 들어온다면?
Outlet
에 아무것도 들어오지 않으니 아래 이미지처럼 메뉴바가 활성화되지 않고, 아래에 이미지가 보이지 않는다.
위 문제를 해결하기 위해서 /kimchis/:id
혹은 /kimchis/:id/
경로로 이동하면, 자동으로 description 페이지로 이동하는 함수를 작성했다.
이 로직은 상세페이지가 로딩(렌더링) 됐을 때 한 번만 실행되어야 하므로 useEffet
훅 내부에 작성했고, id
, currentPath
, navigate
에 의존성을 가진다.
// ProductDetail.jsx
import React, { useEffect } from 'react';
import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
import DetailMenus from '../components/DetailMenus/DetailMenus';
export default function ProductDetail() {
const { id } = useParams();
const location = useLocation();
const currentPath = location.pathname;
const navigate = useNavigate();
// /kimchis/:id 경로로 들어오면 description 페이지로 자동 이동
useEffect(() => {
if (currentPath === `/kimchis/${id}` || currentPath === `/kimchis/${id}/`) {
navigate(`/kimchis/${id}/description`);
}
}, [id, currentPath, navigate]);
return (
<>
<div style={containerStyle}>
{/* 상단 상품 상세정보 컴포넌트 */}
<div>
{/* 메뉴 바 */}
<DetailMenus />
<div style={divStyle}>
{/* 메뉴 바 밑에 보여줄 페이지 */}
<Outlet />
</div>
</div>
</div>
</>
);
}
const containerStyle = {
margin: '1.5rem',
};
const divStyle = {
width: '100%',
textAlign: 'center',
};
References