[코드잇] 위클리 미션 수행기 (6주차)

woolee의 기록보관소·2023년 4월 28일

코드잇부트캠프0기

목록 보기
9/24

이번 주(6주차) 위클리 미션

이번 주는 별도로 요구사항이 존재하지 않았고, 기존 4-5주차에서 vanilla-js로 작성했던 코드들을 react로 옮기는 게 미션이었다.

CRA 대신 Vite로 빌드하기, SWC?

처음엔 CRA로 빌드를 했다.

하지만 구현해야 하는 요구사항에 비해 다소 무거웠다.
그리고 사실 이미 강의를 들으며 자주 접했던 탓에 create-react-app말고 다른 걸 사용해보고 싶었다. 그러던 중 케니의 추천으로 Vite를 알게 되었고, 이번 기회에 새로운 기술을 배워보고자 Vite로 다시 선택해서 만들어 보려고 한다.

기존 위클리 미션 폴더에서 .githubREADME.md 파일 때문에 제거한 뒤 git stash로 복구했다.

그냥 JavaScript만 쓰려다가 SWC로 새로 배워보고 싶어서 선택했다.

Vite 얕게 시작하기

환경 변수 사용법이 다르다.

REACT_APP_API_KEY => VITE_API_KEY

가져올 때는 import.meta.env로 가져온다.

진입점이 다르다.

index.js가 아니라 main.jsx다.

index.html의 위치가 다르다.

public/index.html이 아니라 루트 경로에 index.html이 존재한다.

port 번호가 5173다.

나는 3000번이 익숙해서 설정을 바꿔줬다.

// vite.config.js 

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
  },
});

alias 적용해보자.

/src 경로를 @로 변경해줬다.
타입스크립트면 tsconfig.json도 변경해줘야 하지만,
나는 js이므로 간단하게 설정할 수 있었다.

// vite.config.js 

export default defineConfig({
  ... 
  ,
  resolve: {
    alias: [{ find: "@", replacement: "/src" }],
  },
});

이제 이렇게 "@/~"로 편하게 불러올 수 있다.

.js도 허용하기

utils나 hooks에 들어가는 파일들은 굳이 jsx일 필요가 없을 것 같아서 js도 허용하도록 설정해줬다.

// vite.config.js

export default defineConfig({
  ...
  ,
  esbuild: {
    include: /\.(tsx?|jsx?)$/,
    exclude: [],
  },
});

vanillaJS => 리액트 마이그레이션

폴더 구조

폴더 구조는 다음과 같다.

├─ public/assets
├─ src
	├─ components/
	├─ hooks/*/index.js
	├─ pages/
	├─ utils/index.js
	├─ App.jsx
	└─ main.jsx
└─ index.html  

hooks 폴더에는 유저 데이터를 가져오는 훅들을 작성하고
utils 폴더 안에 기타 함수들을 작성했다.

/shared 페이지 구현

/shared 페이지만 구현하면 되므로 생각보다 간단했다.

라우팅은 다음과 같이 구성했다.

/shared 페이지는 다음과 같이 구성했다.

import Header from "@/components/Header";
import FolderContents from "@/components/FolderContents";
import Footer from "@/components/Footer";

const SharedPage = () => {
  return (
    <>
      <Header />
      <FolderContents />
      <Footer />
    </>
  );
};

export default SharedPage;

components 폴더

@/components/FolderContents에서는 다음과 같이 코드를 작성했다.

데이터를 받아오는 건 커스텀 훅으로 빼고,

내부에서 searchBar, Card 컴포넌트에 데이터를 prop으로 전달했다.

// @/components/FolderContents/index.jsx

import { useCallback } from "react";
import styles from "./folderContents.module.css";
import SearchBar from "@/components/SearchBar";
import Card from "@/components/Card";
import useUserFolder from "@/hooks/useUserFolder";

const FolderContents = () => {
  const [userFolder] = useUserFolder();

  const toggleCardAsterisk = useCallback((e) => {
    const cardAsterisk = e.target.closest(".cardAsterisk");
    if (!cardAsterisk) return;

    if (cardAsterisk.getAttribute("src") === "/assets/card-asterisk.svg") {
      cardAsterisk.setAttribute("src", "/assets/card-asterisk-check.svg");
    } else if (
      cardAsterisk.getAttribute("src") === "/assets/card-asterisk-check.svg"
    ) {
      cardAsterisk.setAttribute("src", "/assets/card-asterisk.svg");
    }
  }, []);

  return (
    <main className={`${styles.main}`}>
      {userFolder && (
        <div className={`${styles.hero} ${styles.inner}`}>
          <div className={`${styles.codeitAvatar}`}>
            <img
              src={userFolder?.owner?.profileImageSource}
              alt="Owner Avatar"
            />
          </div>
          <span className={`${styles.atsign}`}>@{userFolder?.owner?.name}</span>
          <span className={`${styles.marks}`}>{userFolder?.name}</span>
        </div>
      )}
      <div className={`${styles.contents}`}>
        <SearchBar />
        <div
          onClick={toggleCardAsterisk}
          className={`${styles.cardContainer} ${styles.inner}`}
        >
          {userFolder?.links?.map((link) => (
            <Card key={link.id} link={link} />
          ))}
        </div>
      </div>
    </main>
  );
};

export default FolderContents;

propTypes

CRA를 쓸 때와 달리 Vite를 쓰니까 Card 컴포넌트에서 에러가 발생했었는데, 타입 체크를 요구했다.

  • PropTypes는 부모로부터 전달받은 prop의 데이터 type을 검사한다.

CRA에서는 알아서 다 해주던 걸 Vite로 넘어와서 그런지 이런 부분을 세심하게 고려하면서 개발해야 할 것 같다고 느꼈다.

// @/components/Card/index.jsx

...
import PropTypes from "prop-types";

const Card = ({ link }) => {
  ...
};

Card.propTypes = {
  link: PropTypes.object,
};

export default Card;

env에서 가져오는 방식도 다르다. import.meta.env.로 가져와야 한다.

참고로 여기서는는 useEffect에서 바로 async 함수를 반환하지 않고 간접적인 방식으로 함수를 호출했다.

effect hook이 아무것도 반환하지 않거나 메모리 누수를 방지하기 위한 clean-up 함수만을 반환해야 한다고 배웠다. useEffect에서 async를 직접적으로 사용하면 resolved promise를 반환하므로 저렇게 간접적인 방식으로 사용했다.
(물론 맞는 건지 코드 리뷰로 멘토분께 질문드려야겠다..)

// src/hooks/useUserProfile.js 

import { useEffect, useState } from "react";

const useUserProfile = () => {
  const [userProfile, setUserProfile] = useState({});

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const res = await fetch(import.meta.env.VITE_USER_URL);
        const result = await res.json();
        setUserProfile(result.data);
      } catch (e) {
        console.dir(e);
      }
    };
    fetchUser();
  }, []);

  return [userProfile, setUserProfile];
};

export default useUserProfile;

기타 환경 설정들

[Lint] ESLint + Prettier 설정하기
Vite, React, TypeScript, ESlint, Prettier 환경 세팅하는 법

처음 vite로 빌드를 하고 나면, .eslintrc.cjs로 되어 있는데, .js로 변경했다.

eslint: ESLint 코어
eslint-config-airbnb: Airbnb의 eslint 스타일 가이드
eslint-plugin-import: ES2015+의 import/export 구문을 지원
eslint-plugin-react: 리액트 지원
eslint-plugin-jsx-a11y: 접근성 지원
eslint-plugin-react-hooks: 리액트 hooks 지원


자동으로 설치되는 것 제외하고 추가적으로 설치해줬다.

Lint는 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류 등을 표시해주는 도구를 가리킨다. ESLint가 요즘 많이 쓰이고 있는 린트 중 하나다.

airbnb 스타일 가이드의 코드 규칙을 적용할 수 있는 eslint-config-airbnb 패키지는 ECMAScript 6+와 (자바스크립트란 소리) 리액트를 포함하는 airbnb ESLint 규칙을 제공하고 아래 5개의 패키지들을 필수로 한다.

  • eslint
  • eslint-plugin-import
  • eslint-plugin-react
  • eslint-plugin-react-hooks
  • eslint-plugin-jsx-a11y

prettier도 설정해준다.

npm i -D eslint-config-prettier eslint-plugin-prettier

// .prettierrc

{
  "printWidth": 120,
  "tabWidth": 2,
  "singleQuote": true,
  "trailingComma": "all",
  "semi": true
}

약간의 컨벤션들

  • 이벤트 핸들러를 받는 prop 이름은 on__으로
  • 이벤트 핸들러는 handle__으로
  • 커스텀 훅은 use__로 (커스텀 훅에서는 JSX 지양하고 리액트 훅이나 JS 로직 위주)
  • 페이지 이름은 __Page

Netlify에 배포

Page Not Found가 떠서 public 폴더 안에 _redicrects 파일을 추가했다.

netlify에서 내가 설정한 환경변수를 왜 못 읽지 했는데,
URL을 string으로 적어서 그랬던 거였다. 코드 작성해뒀던 걸 그대로 복사했는데 string으로 넣으면 안 되는 거였다..

profile
https://medium.com/@wooleejaan

0개의 댓글