[TIL] 2022. 12. 23. 리액트 쇼핑몰 프로젝트 - 상세페이지 구현

suno·2022년 12월 23일
1
post-thumbnail
post-custom-banner

수요일부터 리액트 첫 프로젝트를 시작했다.
우리팀은 주제를 쇼핑몰로 정했고, 나는 상세페이지 메뉴 컴포넌트, 리뷰 CRUD 기능 구현을 맡았다.

✅ 오늘 한 일


  1. 프로젝트 준비
    두번의 프로젝트를 통해서 바로 코드를 작성하는 것보다, 디테일한 부분까지 미리미리 설계하는 게 중요하다는 것을 깨달았다.
    다행히 다른 팀원분들도 비슷한 생각이었는지 우리팀은 거의 이틀동안 디자인, 데이터, 프로젝트 설계에 시간을 쏟았다. 다른팀들이 어디까지 구현했다더라~ 라는 말이 들려와 마음이 조급해지기도 했지만, 결과적으로 역시 잘한 선택이었다! 🤭

  2. 프로젝트 디자인
    home
    프로젝트 디자인이 기깔나서 너무 마음에 든다..! 💕
    디자인 해주신 팀원분과는 첫 프로젝트도 같이 했는데, 그때도 프로젝트 컨셉이랑 디자인을 뚝딱뚝딱 만들어 오신 덕분에 정말정말 편하게 작업했던 기억이 있다.

  3. 리액트 프로젝트
    상세페이지, 댓글 작성/유효성 검사 기능을 구현했다.


🏃‍♀️ 학습할 것

  • React Thunk - 어떨 때 쓰는지, 지금 프로젝트에 적용할 수 있을지
  • Styled Component - 깔끔하게 쓰는 법
  • function을 컴포넌트 외부/내부에 작성하는 기준을 더 명확히 할 것.

👾 구현할 것

  • DetailMenus.jsx 반복되는 로직 최소화하기 (map 메소드 사용)
  • Styled Component 적용

💡 오늘 배운 것

상세페이지 구현

Router

// 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

profile
Software Engineer 🍊
post-custom-banner

0개의 댓글