본문 바로가기
카테고리 없음

React 음식 정보 렌더

by 김홍중 2022. 4. 24.

컴포넌트를 재활용하도록 만들 수 있을까?

 

처음 설계는 빨간색, 초록색 박스처럼 각각의 컴포넌트를 만들고자 하였습니다. (같은 색은 동일한 컴포넌트 입니다)

 

하지만 이 박스들도 재활용할 수 있도록 만들 수 있다고 생각하여 다음과 같이 하나의 컴포넌트로 만드는 대신에 공통되는 외의것인 카테고리 타이틀, 탭은 주입받을 수 있도록 하였습니다.

 

 

const MainCard = ({ mainCardData }) => {

  return (
    <CardContainer cardInfos={mainDatas}>
      <StyledTap>
        <CategoryTitle title={title} banner={banner} />
      </StyledTap>
      <Tab onClick={handleClickTab} />
    </CardContainer>
  );
};

 

const SubCard = ({ mainCardData }) => {

  return (
    <CardContainer cardInfos={mainDatas}>
      <CategoryTitle title={title} />
    </CardContainer>
  );
};

 

카테고리 타이틀, 탭의 컴포넌트 자체를 위와 같이 CardContainer에 주입하였습니다.
그리고 아래와 같이 CardContainer에서 children으로 태그들을 받도록 하였습니다.

 

const CardContainer = ({ cardInfos, children }) => {
  return (
    <StyledSection>
      {children}
      <StyledCardContainer hasBtn={hasButton}>
        {cardInfos.map((cardInfo, idx) => (
          <StyledCard key={idx}>
            <Card cardInfo={cardInfo} />
          </StyledCard>
        ))}
      </StyledCardContainer>
    </StyledSection>
  );
};

그런데 좌, 우 버튼도 전달해야하기 때문에 동일하게 CardContainer에 주입하는 방식을 고려하였습니다.

const CardContainer = ({ cardInfos, children, hasButton }) => {
  return (
    <StyledSection>
      {children[0]}
      <StyledCardContainer hasBtn={hasButton}>
        {children[1]} //'◀'버튼
        {cardInfos.map((cardInfo, idx) => (
          <StyledCard key={idx}>
            <Card cardInfo={cardInfo} />
          </StyledCard>
        ))}
        {children[2]} //'▶'버튼
      </StyledCardContainer>
    </StyledSection>
  );
};


하지만 위와 같이하면 좌, 우 버튼으로 네 개의 카드를 넘기는 리스트는 문제 없지만 최상단의 세 개의 카드가 나오는 컨테이너와 동일하게 children을 받지 않기 때문에 children의 인덱스로 하는경우 예외 처리가 필요하였고 예외처리를 작성하면 코드가 복잡해졌습니다.

 

const CardContainer = ({ cardInfos, children, hasButton }) => {
  return (
    <StyledSection>
      {children}
      <StyledCardContainer hasBtn={hasButton}>
        {hasButton && <Button icon={'◀'} />}
        {cardInfos.map((cardInfo, idx) => (
          <StyledCard key={idx}>
            <Card cardInfo={cardInfo} />
          </StyledCard>
        ))}
        {hasButton && <Button icon={'▶'} />}
      </StyledCardContainer>
    </StyledSection>
  );
};

 

그래서 hasButton이라는 값에 true를 상위에서 전달해준다면 CardContainer 내부에서 true일때만 Button 컴포넌트가 렌더되도록 `{hasButton && <Button icon={'◀'} />}`식의 코드를 작성하였습니다.

 

서버의 상태도 클라이언트의 상태로 두어야 할까?

 

const App = () => {
  const [mainCardData, setMainCardData] = useState([]);

  const getSideDishData = async (url, setData) => {
    const response = await fetchData(url);
    setData(response.data);
  };

  useEffect(() => {
    const mainCardUrl = 'data/mainCard.json';

    getSideDishData(mainCardUrl, setMainCardData);

  }, []);

  return (
    <>
      <Header />
      <MainCard mainCardData={mainCardData} />
    </>
  );
};

서버로 부터 받아오는 카드 정보는 서버의 상태라고 생각하였고 클라이언트와 사용자 간의 상호작용 사이에 발생한 상태가 아니기 때문에 처음에는 상태로 두지 않아야한다고 생각하였습니다.

하지만 '상태가 아닌 경우'에 대한 정리를 참고하여 다시 생각해보았습니다.

state가 아닌 경우

- 부모로 부터 props로 전달 받는 경우
- 시간이 지나도 변하지 않는 경우
- 컴포넌트 내 다른 state나 props로 구할 수 있는 경우

위의 정리를 참고하면 두 번째의 항목이 해당이 안되고, 서버가 보내는 데이터에 따라서 렌더하는 내용도 바뀌기 때문에 클라이언트의 상태로 두기로 결정하였습니다.

 

참고 :

React로 사고하기 - React

 

React로 사고하기 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

서버 요청 시점

 

첫 화면이랑 더보기 버튼을 눌렀을 때 UI가 다르다 보니
fetch하는걸 더보기 버튼을 눌렀을 때 필요한 데이터를 가져와야 하는지
아니면 한번에 다 가져온 후 더보기 눌렀을 때 필요한 데이터를 props로 전달해 줘야 하는지 어떤 방법이 나은지 궁금합니다

전자 방법으로 했을 때 useEffect안에 함수를 선언하는게 좋다고 생각해서 만약 전자의 방법으로 했을 때 getData 함수를 밖에다 한번 더 선언 해줘야 하는 단점이 있고

후자 방법으로 했을 때 불필요한 데이터를 미리 fetch하는게 아닌가 생각합니다 그리고 초기 페이지를 렌더 할 때 필요없는 데이터 까지 받아오기 때문에 느려질 수 있다고 생각합니다. 다만, 초기 렌더에 더 오래걸리더라도 이후 버튼 클릭시 빠르게 렌더할 수 있습니다.

 

더보기 버튼 대신에 사용자에게 더 자연스러운 UI는?

 

더보기 버튼 클릭시 카드 리스트가 펼쳐지고 다 펼쳐지면 펼친 것을 다시 올리는 버튼을 두어서 다시 펼친것을 닫을 수 있게 구현하였습니다. 하지만 댓글과는 다르게 음식 사진들을 살펴보는 것은 더보기 버튼을 클릭하는것이 부드러운 동작이라고는 생각이 안들었습니다. 그래서 이 보다는 무한 스크롤이 더 부드로운 동작일 수 있다고 생각하였습니다.

댓글