경험·후기

메가테라 과제 해설 코드에 기여한 썰 푼다✌

Jiwoo 2023. 5. 15. 21:55

🌳 해설 코드 뜯어보기

메가테라 프론트엔드 생존코스의 4주차 과제를 진행했다.

기본적인 기능 구현은 끝마쳤지만 과연 이게 최선의 코드일지 고민스러웠다.

컴포넌트의 구조, 상태의 위치, hook과 함수의 분리 기준,

그리고 컴포넌트 명 짓는 방법 등 풀리지 않는 궁금증이 있었다.


다행히 생존 코스는 모범 해설 코드를 제공하기에 궁금증을 어느정도 해소할 수 있다.

나와 어떻게 다르게 했는지 궁금했기에 조각조각 뜯어서 의도를 파악했고

더 최선의 코드라고 생각되면 내 코드도 함께 수정했다.

🌳 이 부분이 정말 최선일까?

과제를 하다가 가장 애를 먹은 부분이 뭐였냐면...

메뉴 주문을 누르면 영수증이 나타났다가 5초 후에 사라지는 기능 구현이었다.

그래서 해당 코드를 꼼꼼히 보고 있었다.

그러던 중, 이해 되지 않는 부분을 발견했다.

// before
export default function App() {
  const { value, setTrue, setFalse } = useBoolean(false);

  const [receipt, setReceipt] = useLocalStorage('receipt', emptyReceipt);

  const restaurants = useFetchRestaurants();

  useEffect(() => {
    setFalse();

    if (receipt.id) {
      setTrue();
    }
  }, [receipt]);

  useInterval(() => {
    if (receipt.id) {
      setReceipt(emptyReceipt);
      setFalse();
    }
  }, value ? 5000 : null);

  // ...
}

모범 코드는 useEffect로 receipt.id의 유무를 boolean으로 저장한 다음,
boolean에 따라 useInterval이 실행되는 구조였다.
그런데 왜 useEffect를 거쳐 boolean으로 실행하는지 이해되지 않았다.

// after
export default function App() {
  const { value, setTrue, setFalse } = useBoolean(false);

  const [receipt, setReceipt] = useLocalStorage('receipt', emptyReceipt);

  const restaurants = useFetchRestaurants();

   useInterval(() => {
      setReceipt(emptyReceipt);
      // setFalse();
    }, receipt.id ? 5000 : null);

  // ...
}

receipt에 변경이 생기면 전체가 리렌더링되니
useInterval에 receipt.id 로 바로 유무를 판단해도 될 것 같았다.
이렇게 useEffect를 사용하지 않고 receipt.id로 검사하는 방식도 정상적으로 작동했다.

그래서 디스코드에 질문을 올렸다.

그리고 노아님의 답변!!!

해설 코드지만 리팩터링의 여지가 있기 때문에

이런 의견은 얼마든지 환영이라고 하셨다.

해설 코드를 단순히 보고 넘어가지 않고 직접 리팩토링 해본

좋은 사례이자 이상적인 학습이라며 칭찬도 받았다👍

그리고 해당 부분을 직접 고쳐 PR을 보냈다.

비록 오픈 소스는 아니지만 많은 수강생들이 보는 소스코드에

기여하게 된 좋은 경험이었다.

메가테라를 이끄는 홀맨님, 노아님,

그리고 내 이름이 함께 있는 낯선 광경....ㅋㅋㅋ

🌳 추가로 배운 점

과제를 하던 중 setInterval을 어느 컴포넌트에 넣을지 고민했다.

receipt 데이터를 직접적으로 사용하는 receiptPrinter 컴포넌트에 해당 함수를 넣었다.

receipt 데이터를 변경하는 일이니까 해당 컴포넌트에서 처리하고 싶어서

props로 setReceipt까지 받아서 동작했건만... 예상대로 동작하지 않았다.

지연 간격을 보장하고 사이드 이펙트를 없애기 위해

custom hook인 useInterval을 사용해도 마찬가지였다.


결국 한 단계 상단 컴포넌트로 옮기니 잘 작동했다.

옮기고 보니 굳이 receiptPrinter에서 작동시킬 필요가 없다는 것을 알았다.

불필요하게 receipt을 수정하는 setReceipt를 props로 내려주는 과정도 필요 없었다.

구현을 완료하고 useInterval을 receipt 컴포넌트로 옮겨도 잘 작동했기에

발생했던 에러의 원인이 단순히 컴포넌트 위치 때문은 아니었지만...

결론적으로 더 좋은 구조를 구현하게 됐다.

상태 변경 함수의 위치 결정하기

  • 상태값이 있는 컴포넌트
  • 더 연관성 있는 컴포넌트

뭐가 더 나을지는 중점적으로 보는 요소에 따라 달라지기 때문에

하나에 갇혀있지 말고 넓게 보면서 구조를 만드는게 좋을 것 같다.

🌳 결론: 좋은 코드를 많이 보는 것은 역시 옳다!

단계별 좋은 코드를 보고 학습할 수 있다는 것이 생존코스를 듣게 된 1순위 이유였다.

막상 진행하다보니 과제만 하기도 급급하지만

최대한 강의 속 코드와 해설 코드를 곱씹어보는 과정은 역시 중요하다.

이번 경험을 통해 더욱 깨달았기 때문에...

남은 7주동안 더 열심히 달려보려 한다! 🏃‍♀️