HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🧚
[1기]최종 프로젝트 데브코스
/
💫
[팀16] YAS
/
😺
프롱이
/
👔
프로젝트 세팅
👔

프로젝트 세팅

요약1) create-react-app & Typescripttsconfig.json2) ESLint & Prettier 설정package.json.eslintrc.js.prettierrc타입스크립트 경로 설정추가한 패키지tsconfig.jsontsconfig.paths.jsoncraco.config.js스토리북 설정3) emotion, styledcraco(create-react-app-config-override)craco.config.jspackage.json4) Storybookstorybook 설치 후 start 오류5) Axios6) .env7) react-router-dom기술 스택1. Recoil사용 방법1) state.js ⇒ 상태들을 저장, 관리한다.2) 사용될 컴포넌트3) App.js2. formik + Yup기본 사용 방법3. moment jsdurationTime 구하기4. react-dnd5. emoji-picker최종 package.json
 

요약

  • Framework : React, React BoilerPlate(create-react-app)
  • Route: react-router-dom(5.3.0)
  • State Management Tool: redux
  • Network: Axios
  • Convention: ESLint, Prettier
  • Style: Emotion, styled component
  • etc: formik + Yup, momentjs, react-dnd, emoji-picker-react

1) create-react-app & Typescript

  • 기본: yarn 기반 패키지
  • CRA를 Typescript 버전으로 설치.
yarn create react-app yas --template typescript
 

tsconfig.json

{ "compilerOptions": { "target": "es6", // 기본값: es5 => es6로 수정 "lib": [ "dom", "dom.iterable", "esnext" ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx" }, "include": [ "src" ], }

2) ESLint & Prettier 설정

yarn add -D eslint prettier yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser // ESLint Rule 추가 플러그인 yarn add -D eslint-config-airbnb // airbnb ESLint 규칙 yarn add -D eslint-config-prettier eslint-plugin-prettier // prettier Rule 플러그인 yarn add -D eslint-plugin-react eslint-plugin-react-hooks // airbnb ESLint 규칙 yarn add -D eslint-plugin-jsx-a11y eslint-plugin-import // airbnb ESLint 규칙

package.json

  • 설치 완료 후 아래의 devDependencies가 추가된다.
// ... "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", "eslint": "^8.3.0", "eslint-config-airbnb": "^19.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.25.3", "eslint-plugin-jsx-a11y "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.27.1", "eslint-plugin-react-hooks": "^4.3.0", "prettier": "^2.5.0" } // ...

.eslintrc.js

  • 자동 생성된 .eslintrc.js 코드를 아래와 같이 수정하여 rule을 추가한다.
module.exports = { env: { browser: true, node: true, commonjs: true, es2021: true, }, extends: [ 'plugin:react/recommended', 'plugin:jsx-a11y/recommended', 'plugin:import/errors', 'plugin:import/warnings', 'plugin:@typescript-eslint/recommended', ], parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 2018, sourceType: 'module', }, plugins: ['react', '@typescript-eslint'], rules: { 'react/react-in-jsx-scope': 0, }, settings: { 'import/resolver': { node: { extensions: ['.js', '.jsx', '.ts', '.tsx'], }, }, }, };

.prettierrc

  • 루트 폴더에 .prettierrc 파일 생성 후 아래 코드를 작성한다.
{ "singleQuote": true, "semi": true, "useTabs": false, "tabWidth": 2, "trailingComma": "all", "printWidth": 80, "arrowParens": "always", "orderedImports": true, "bracketSpacing": true, "jsxBracketSameLine": false }
 

타입스크립트 경로 설정

추가한 패키지

tsconfig-paths-webpack-plugin
craco-alias

tsconfig.json

{  "extends": "./tsconfig.paths.json", // 추가, 해당 파일에서 속성을 상속받는다.  "compilerOptions": {    "target": "es6",    "lib": [      "dom",      "dom.iterable",      "esnext"   ],    "allowJs": true,    "skipLibCheck": true,    "esModuleInterop": true,    "allowSyntheticDefaultImports": true,    "strict": true,    "forceConsistentCasingInFileNames": true,    "noFallthroughCasesInSwitch": true,    "module": "esnext",    "moduleResolution": "node",    "resolveJsonModule": true,    "isolatedModules": true,    "noEmit": true,    "jsx": "react-jsx" },  "include": [    "src" ],  "exclude": [ // 추가, 내용 더 찾아보기    "node_modules",    "**/node_modules/*" ] }
 

tsconfig.paths.json

tsconfig의 절대경로 별칭 지정
{  "compilerOptions": {    "baseUrl": "./src",    "paths": {      "@/*": ["./*"]   } } }
 

craco.config.js

craco의 절대경로 별칭을 지정
// eslint-disable-next-line const CracoAlias = require('craco-alias'); // 추가 module.exports = {  babel: {    presets: ['@emotion/babel-preset-css-prop'], },  plugins: [ // 추가, craco의 절대경로 별칭을 지정   {      plugin: CracoAlias,      options: {        source: 'tsconfig',        baseUrl: './src',        tsConfigPath: './tsconfig.paths.json',     },   }, ], };
 

스토리북 설정

  • index.css 추가
  • preview-head.html 추가
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset-css@5.0.1/reset.min.css" /> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet" />
  • mian.js 내용 추가
    • 스토리북 경로 alias 설정 (feat.tsconfig-paths-webpack-plugin 패키지)
    • const path = require('path'); const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); module.exports = { stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/preset-create-react-app', ], webpackFinal: async (config) => { config.resolve.plugins.push(new TsconfigPathsPlugin({})); return config; }, };
참고자료
Typescript로 만든 react 프로젝트에서 경로 별칭 사용하기..
[Typescript] storybook에서 절대 경로 설정하기
 
  • preview.js 내용 추가
    • 커스텀 뷰포트 추가
      • mobile
      • tablet
      • pc
    • MemoryRouter 추가
      • 스토리북상에서 링크 이동 관련 액션발생시 홈으로 라우팅
import './index.css'; import React from 'react'; import { addDecorator } from '@storybook/react'; import { MemoryRouter } from 'react-router-dom'; addDecorator((story) => ( <MemoryRouter initialEntries={['/']}>{story()}</MemoryRouter> )); const customViewports = { mobile: { name: 'Mobile', styles: { width: '320px', height: '100%', }, }, tablet: { name: 'Tablet', styles: { width: '768px', height: '100%', }, }, pc: { name: 'PC', styles: { width: '1920px', height: '100%', }, }, }; export const parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, date: /Date$/, }, }, viewport: { viewports: customViewports }, };
 

3) emotion, styled

  • styled component
  • javascript 파일 내부에서 스타일 적용 패키지
yarn add @emotion/react // emotion yarn add --dev @emotion/babel-plugin // babel yarn add @emotion/styled // styled
 
// ... "dependencies": { "@emotion/react": "^11.6.0", "@emotion/styled": "^11.6.0", // ...
 

craco(create-react-app-config-override)

  • 위의 패키지 3개만 추가하면 styled 패키지를 사용할 때 마다 /** @jsxImportSource @emotion/react */ fragma 주석을 추가해야 되는데, 이를 해결하기 위해 craco를 설치한다.
yarn add -D @craco/craco yarn add -D @emotion/babel-preset-css-prop
 

craco.config.js

  • 루트 폴더에 craco.config.js 파일을 생성하여 아래와 같이 추가한다.
module.exports = { babel: { presets: ["@emotion/babel-preset-css-prop"] } }
 

package.json

  • package.json 파일에서 scripts 부분을 react-scripts에서 craco로 변경한다.
// ... "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "craco eject" }, // ...
 
 

4) Storybook

  • UI 컴포넌트를 모아 문서화하고 보여주는 오픈소스 Tool
npx -p @storybook/cli sb init
notion image
 

storybook 설치 후 start 오류

  • storybook 설치 후 react-scripts와 storybook의 babel-loader 버전이 달라서 yarn start로 로컬 환경 Open이 되지 않는 현상이 발생한다.
  • package.json 파일에 아래 코드를 추가하여 해결할 수 있다.
// package.json // ... "resolutions": { "babel-loader": "8.1.0" } // ...
 

5) Axios

  • 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리
yarn add axios
 

6) .env

  • ESLint 설치 후 package.json과 yarn.lock에서의 package version 차이가 발생해서 yarn start 같은 로컬 환경이 Open되지 않는다.
  • 루트 폴더에 .env 파일을 생성 후 아래 코드를 추가한다.
SKIP_PREFLIGHT_CHECK=true

7) react-router-dom

  • react-router-dom을 설치하면 최신버전인 6버전으로 설치가 된다.
  • 하지만 6버전은 아직 문법이 많이 낮설기 때문에 아래와 같이 5버전으로 설치를 진행한다.
    • (5버전이랑 6버전 사용방법이 많이 다르다.)
yarn add react-router-dom@5.3.0
 

기술 스택

1. Recoil

  • React 상태관리 라이브러리이다.
  • 부모 트리(보통 App.js)에 RecoilRoot 태그로 한 번 감싸준 후 Atom(State) 등을 통해 상태를 관리, 사용한다.
 

사용 방법

1) state.js ⇒ 상태들을 저장, 관리한다.

import { atom } from "recoil"; // atom은 상태를 나타낸다. export const textState = atom({ key: "textState", // default: "", });
 

2) 사용될 컴포넌트

import { useRecoilState } from "recoil"; import { textState } from "./state"; const RecoilTest = () => { const [value, setValue] = useRecoilState(textState); // ... }
 

3) App.js

import { RecoilRoot } from "recoil"; import "./App.css"; function App() { return ( // 사용될 컴포넌트를 RecoilRoot로 감싸주면 자식 컴포넌트에서 전역으로 사용할 수 있다. <RecoilRoot> // ... </RecoilRoot> ); } export default App;

2. formik + Yup

  • form 태그 내부에서 사용되는 input 태그의 입력 값의 Change, Blur, Submit 등의 폼 기능을 간편하게 사용할 수 있다.
  • Yup 라이브러리와 함께 사용해서 입력 값의 유효성 검사를 쉽게 작성할 수 있다.
    • Yup을 사용하지 않고 validation을 직접 작성할 수 있다.
  • 회원가입, 로그인 페이지에서 사용될 수 있다.
 

기본 사용 방법

const formik = useFormik({ initialValues: { userEmail: "", userPassword: "", }, validationSchema: Yup.object({ userEmail: Yup.string() .email(invalidErrorMessage.email) .required(invalidErrorMessage.email), userPassword: Yup.string() .min(8, invalidErrorMessage.password) .max(15, invalidErrorMessage.password) .matches(/^[a-zA-Z0-9]+$/, invalidErrorMessage.password) .required(invalidErrorMessage.password), }), onSubmit: (value) => { alert(value); }, }); // {...} <form onSubmit={formik.handleSubmit}> <input id="userEmail" name="userEmail" type="email" onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.userEmail} placeholder="이메일" /> {formik.errors.userEmail} <input id="userPassword" name="userPassword" type="password" onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.userPassword} placeholder="비밀번호" /> {formik.errors.userPassword} </form> // {...}
 

3. moment js

  • 날짜 계산을 쉽게 할 수 있다.
  • 각 루틴, 미션의 startTime, endTime을 받아서 durationTime을 구할 때
    • 그 외 날짜와 관련된 기능을 쉽게 사용할 수 있다.
 

durationTime 구하기

const startTime = moment(); // 미션이 시작될 때 startTime 변수 생성 const endTime = moment(); // 미션이 끝났을 때 endTime 변수 생성 const durationTime = moment.duration(endTime.diff(startTime)).asSeconds(); // 초(second) 단위 Number type으로 변환된다. // duration값에서는 asDays(), asHours() 등으로 원하는 값을 얻을 수 있다.

4. react-dnd

https://velog.io/@dowon938/react-dnd-이용하여-drag-drop-구현하기
 
 

5. emoji-picker

https://www.npmjs.com/package/emoji-picker-react
 

최종 package.json

{ "name": "yas", "version": "0.1.0", "private": true, "dependencies": { "@emotion/react": "^11.6.0", "@emotion/styled": "^11.6.0", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.15", "@types/node": "^12.0.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "axios": "^0.24.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3", "typescript": "^4.1.2", "web-vitals": "^1.0.1" }, "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "craco eject", "storybook": "start-storybook -p 6006 -s public", "build-storybook": "build-storybook -s public" }, "resolutions": { "babel-loader": "8.1.0" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ], "overrides": [ { "files": [ "**/*.stories.*" ], "rules": { "import/no-anonymous-default-export": "off" } } ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "@craco/craco": "^6.4.2", "@emotion/babel-plugin": "^11.3.0", "@emotion/babel-preset-css-prop": "^11.2.0", "@storybook/addon-actions": "^6.3.12", "@storybook/addon-essentials": "^6.3.12", "@storybook/addon-links": "^6.3.12", "@storybook/node-logger": "^6.3.12", "@storybook/preset-create-react-app": "^3.2.0", "@storybook/react": "^6.3.12", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", "eslint": "8.3.0", "eslint-config-airbnb": "^19.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.25.3", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.27.1", "eslint-plugin-react-hooks": "^4.3.0", "prettier": "^2.5.0" } }