지난 시간에 이어 북스토어 프로젝트를 계속 진행해 볼게요. 이번 시간에는 도서 상세 페이지의 세부 기능들을 구현하는 과정을 정리해 볼게요.
도서 상세 페이지는 서버에서 받아온 상품 정보를 화면에 렌더링하는 역할을 해요. 이때 단순히 화면을 구성하는 것을 넘어, 데이터 포맷팅 로직을 재사용할 수 있도록 분리하는 작업이 중요해요.
날짜 포맷 함수를 직접 구현해도 좋지만, dayjs 와 같은 기존 라이브러리를 활용하면 날짜 변환부터 다양한 유틸리티 함수를 쉽게 사용할 수 있어요.
좋아요 기능이나 장바구니 추가 같은 기능은 크게 두 가지 방식으로 구현할 수 있어요. 첫 번째는 서버에 요청을 보내고 응답을 받은 후 화면을 다시 그리는 방식이고, 두 번째는 서버 요청과 동시에 무조건 성공할 것이라 예상하고 화면을 먼저 변경하는 방식이에요.
서버 응답이 조금이라도 지연되면 사용자는 답답함을 느낄 수 있어요. 따라서 즉각적인 반응이 필요한 곳에서는 두 번째 방식인 낙관적 업데이트를 사용하는 것이 사용자 경험(UX) 측면에서 훨씬 유리해요.
컴포넌트를 언제 분리해야 할지 고민될 때는, 중복되는 렌더링 부분(TSX 코드)을 기준으로 삼으면 판단하기 편해요. 컴포넌트로 분리한 후에는 다양한 곳에서 재사용할 수 있도록 외부에서 제어 가능하게 설계하는 것이 중요해요.
스타일드 컴포넌트(styled-components )를 사용할 때, 기본 HTML 태그뿐만 아니라 이미 만들어진 커스텀 컴포넌트를 감싸서 스타일을 덧입히고 확장할 수 있어요.
import styled from "styled-components";
import Button from "./common/Button";
// 기존 Button 컴포넌트를 상속받아 스타일을 확장해요.
const LikeButtonStyle = styled(Button)`
background-color: red;
color: white;
border-radius: 8px;
`;
const LikeButton = () => {
return (
<LikeButtonStyle>
좋아요
</LikeButtonStyle>
);
};
장바구니 추가 기능도 낙관적 업데이트 로직을 적용하여 사용자의 클릭에 화면이 먼저 반응하도록 만들어요.
이때 스타일드 컴포넌트(styled-components )에서 조건부 스타일링을 위해 불리언(boolean) 타입의 props 를 넘겨주는 경우가 있어요. 이때 해당 속성이 실제 DOM 요소에 그대로 전달되면 표준 속성이 아니라는 이유로 에러가 발생할 수 있어요. 이럴 때는 변수명 앞에 $ 기호를 붙여 임시 속성으로 만들어주면 리액트가 DOM으로 속성을 전달하지 않아 에러를 깔끔하게 해결할 수 있어요.
import styled from "styled-components";
// $ 기호를 붙여 DOM으로 전달되지 않는 스타일 전용 속성임을 명시해요.
interface CartButtonProps {
$added: boolean;
}
const CartButton = styled.button<CartButtonProps>`
background-color: ${({ $added }) => ($added ? "gray" : "blue")};
color: white;
`;