TIL 231120 - 펜레터 프로젝트 회고

송용승·2023년 11월 20일
2

TIL

목록 보기
16/29
post-thumbnail

Today I Learned

오늘 정오로 펜레터 프로젝트가 끝났다. 어렵진 않았지만 낯설었고 많이 배울 수 있는 기회였다. 오늘은 좀 쉬면서 며칠간 쓴 코드를 다시 보고, 자잘한 문제를 수정하고 배포를 했다. 오늘 포스팅에서는 이번 개인 프로젝트를 진행한 과정과 진행하며 배운 것들을 간략하게(강조) 정리해보려고 한다.

컴포넌트 및 라우트 구조를 미리 짜두자

프로젝트 시작이 늦은 편이었다. 특별히 게으름을 피운 건 아닌데 알고리즘 스터디에 뭐에 이것저것 하다보니 어느덧 마감기한을 사흘 앞두고 있었다. 조금 초조했지만 다른 캠퍼들의 고충과 시행착오를 들어 둔 덕택에(?) 전체적인 프로젝트의 레이아웃이 머릿속에 그려져 있었다.

머릿속에 이런 구조를 이루고 있었고, 이 구조에 수정 없이 그대로 프로젝트를 마쳤다.
(context API, Redux 적용 제외)

📦public
┗ 📜fakedata.json
📦src
┣ 📂assets
┣ 📂components
┃ ┣ 📜Footer.jsx
┃ ┣ 📜Header.jsx
┃ ┣ 📜Letter.jsx
┃ ┣ 📜LetterForm.jsx
┃ ┗ 📜LetterList.jsx
┣ 📂pages
┃ ┣ 📜Detail.jsx
┃ ┗ 📜Home.jsx
┣ 📜App.js
┗ 📜index.js
const [letters, setLetters] = useState({});
const [selectedMember, setSelectedMember] = useState("");

이렇게 state 도 어느 정도 정해두고 프로젝트에 착수했다.

하지만 당연히 계획대로 되지 않는다

컴포넌트의 주요 기능을 순조롭게 개발해가고 있던 도중, 두 가지 부분에서 막히고 말았다.

경로지옥

프로젝트 요구사항에 json 포멧의 목데이터를 이용해서 내용을 채워야하는 부분이 있었다. 이를 위해 렌더링시에 useEffect hook 을 이용해서 localStorage에 데이터가 없을 시 목데이터를 가져오도록 코드를 짰다.

useEffect(() => {
    if (!localStorage.getItem("letters")) {
      fetch("../public/fakeData.json")
        .then((res) => res.json())
        .then((data) =>
          data.forEach((item) => {
            setLetters((prevState) => ({
              ...prevState,
              [item.id]: { ...item, editedAt: "" },
            }));
          })
        );
      const stringifiedLetterMap = JSON.stringify(letters);
      localStorage.setItem("letters", stringifiedLetterMap);
    } else {
      const storageData = JSON.parse(localStorage.getItem("letters"));
      setLetters(storageData);
    }
  }, []);

그런데 계속해서 데이터를 가져오지 못하는 것이었다.

  1. 경로상 이렇게 접근을 하는 게 맞는 것 같고
  2. fetch 또한 맞게 쓰이고 있는데

계속해서 파일을 찾지 못하는 것이었다. 여기에서 몇 시간을 날린 건지... 결국 두 손 두 발 다 들고 다른 캠퍼한테 물어봤더니 본인도 같은 문제를 겪었고 그래서 리액트의 작동 방식을 뜯어본 결과

프로젝트 빌드 전과 빌드 후의 경로 구성이 vscode의 탐색기에서 보는 것과 차이가 있는 것 같다는 것이다 (!)

정확히 규명을 해보지는 못했지만 리액트는 빌드를 하면 babelwebpack을 사용해서 소스코드를 찢고 붙이고 다시 꿰매서 빌드용 js 파일을 만들고, 이걸 바탕으로 앱을 빌드한다.

그래서 그냥 루트 디렉토리에서 바로 접근하게끔 fetch("fakeData.json") 으로 경로를 잡았더니 잘 불러오는 것이었다... 여기서 한 쪽 무릎을 꿇었다. 깨달은 건... CRA를 사용해서 앱을 만들다가 경로가 안 잡히면 일단 루트 디렉토리로 한번 잡아보자. 정도였다.

구조분해지옥(feat. redux)

순조롭게 props-drilling 버전과 context API 적용 버전을 끝내고 redux 적용 버전을 작성하고 있었는데, 리듀서에서 각각의 액션 타입에 대한 리턴값을 정의하다가 여기서 또 한차례 막히고 말았다.
원래 존재하는 객체에 수정한 객체의 내용을 덮어씌우는 작업이었는데, 나는 공연히 어렵게 어렵게 풀어서 작성한 것이다.

// React Component
dispatch(
	editLetters({
		[params.id]: {
		  ...selectedLetter,
          content: updatedContent,
          editedAt: dayjs().toJSON(),
        },
        id: params.id,
      })
    );
// 리듀서
case EDIT_LETTER:
      return {
        ...state,
        letters: {
          ...state.letters,
          [action.payload.id]: {
            ...state.letters[action.payload.id],
            content: action.payload.updatedContent,
            editedAt: action.payload.editedAt
          }
        },
      };

이렇게 작성한 EDIT_LETTER action을 dispatch 하는 부분에서 계속 에러가 발생하는 것이다. 에러가 안 나면 오히려 이상할 생김새긴 했는데... 전날 밤을 새워서 그런지 머리도 어질어질하고 원래 구조분해할당에는 조금 자신이 없는 편이기도 해서 어떻게 할 지 몰라 한참을 갈팡질팡 하다가 다른 캠퍼분에게 질문을 했다. 이 캠퍼분의 해결 방법이 정말 좋았는데, 이렇게 써 둔 걸 실제로 임의의 데이터를 하나 가정해서 대입을 해 보는 것이다. 그렇게 해 보고 나니 간단히 문제를 알 수 있었고, 다음과 같이 리듀서를 수정했다.

case EDIT_LETTER:
      return {
        ...state,
        letters: {
          ...state.letters,
          ...action.payload,
        },
      };

사실 이렇게만 해도 원래 존재하는 id 에 덮어씌워지기 때문에 수정이 완료되는 것이다. 구조분해와 객체의 개념을 다시 공부해야 할 것 같다. 그리고 이 자리를 빌려 다시 지노님에게 감사를 전한다...

고지는 생각보다 가까이에 있다

물론 막힌 부분은 있었지만 그건 프로젝트 자체의 난이도라기보다 내가 선택한 방식과 어쩔 수 없이 겪어야 하는 어려움들로 인한 것이었다. 프로젝트 전체적으로 보면 React hook을 실습하는 것에 가까웠고, 그 결과 이전보다 hook들과 많이 친해질 수 있었다. 익힌 게 녹슬기 전에 어서 다음 프로젝트로 또 몸에 익히고 싶다.

야호!

조심해야 할 점

  • 경로를 항상 재검토하자
  • Styled-components 에 변수를
profile
웹 프론트엔드 개발을 익히고 있습니다.

0개의 댓글