스택
JavaScript
State management (context API)
Package Manager (yarn)
React
StoryBook 사용
emotion
React
vite 사용 (노드 버전 제한 있음)
14.18+ 또는 16+ 의 Node.js를 요구
- 혜준: v16.19.0
- cra는 모듈 번들러로 webpack 사용 → 느림
- vite는 esbuild를 기반 → 100배 빠름

- 시작:
yarn create vite swithme —template react
- yarn으로 패키지들 설치:
yarn
- yarn으로 실행:
yarn dev
절대 경로 설정
vite.config.js
파일의defineConfig
에 아래 코드 추가
import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import * as path from "path"; // path import export default defineConfig({ plugins: [react()], // resolve 추가 resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, });
Emotion
yarn add @emotion/react @emotion/styled
- 앱을 cra로 만들지 않았으므로 CRACO(Create-React-App Configuration Override: CRA에 config 설정을 덮어쓰기 위한 패키지)를 설치하거나 할 필요가 없음
StoryBook
yarn add storybook
yarn sb init
Prettier / Eslint
- eslint vscode 확장 프로그램 설치
yarn add -D eslint prettier eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks
eslint-plugin-prettier
: prettier 의 설정 중 eslint 의 설정과 충돌이 나는 설정들을 비활성화하는 플러그인- 추천되지 않음?
eslint-config-prettier
: prettier의 규칙을 eslint에 적용시킬 수 있게 하는 플러그인
- .eslintrc.json
{ "env": { "browser": true, "es6": true }, "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "plugins": ["react"], "extends": ["react-app", "eslint:recommended", "plugin:react/recommended", "plugin:prettier/recommended", "prettier"], "rules": { "linebreak-style": 0, "object-curly-newline": 0, "no-unused-vars": "warn", "no-use-before-define": 1, "indent": ["error", 2], "react/react-in-jsx-scope": "off", "react/jsx-filename-extension": [1, { "extensions": [".jsx"] }], "import/prefer-default-export": "off", "jsx-quotes": ["error", "prefer-single"], "no-console": 0, "no-alert": 0, "react/jsx-props-no-spreading": 0, "react/prop-types": 0 } }
건열님이 예전에 사용하신 rules
- jsx-a11y/no-noninteractive-element-interactions는 적용이 잘 안돼서 제외
Definition for rule 'jsx-a11y/no-noninteractive-element-interactions' was not found.
"linebreak-style": 0, "object-curly-newline": 0, "no-unused-vars": "warn", "no-use-before-define": 1, "indent": ["error", 4], "react/jsx-indent": [2, 4, { "checkAttributes": true, "indentLogicalExpressions": true }], "react/jsx-indent-props": [2, 4], "react/react-in-jsx-scope": "off", "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], "import/prefer-default-export": "off", "jsx-quotes": [2, "prefer-single"], "no-console": 0, "no-alert": 0, "jsx-a11y/no-noninteractive-element-interactions": [ "error", { "handlers": [ "onClick", "onMouseDown", "onMouseUp", "onKeyPress", "onKeyDown", "onKeyUp" ] } ], "react/jsx-props-no-spreading": 0, "react/prop-types": 0
- .prettierrc
{ "singleQuote": true, // single 쿼테이션 사용 여부 "trailingComma": "all", // 여러 줄을 사용할 때, 후행 콤마 사용 방식 "semi": true, // 세미콜론 사용 여부 "useTabs": false, // 탭 사용 여부 "tabWidth": 2, // 탭 너비 "printWidth": 120, // 줄 바꿈 할 폭 길이 "arrowParens": "always", // 화살표 함수 괄호 사용 방식 "bracketSameLine": true // 객체 리터럴에서 괄호에 공백 삽입 여부 }
📌 약간 적용이 잘 안되는 부분이 좀 있는 것 같긴 하다..

차근차근 수정해봅시다..
글로벌 스타일, 폰트
yarn add emotion-reset
import { Global, css } from '@emotion/react'; import emotionReset from 'emotion-reset'; const style = css` ${emotionReset} html { font-size: 62.5%; } html, body { width: 100%; height: 100%; } * { box-sizing: border-box; } body, button, input { font-family: 'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, system-ui, Roboto, 'Helvetica Neue', 'Segoe UI', 'Apple SD Gothic Neo', 'Noto Sans KR', 'Malgun Gothic', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif; } select, input, button, textarea { border: 0; outline: 0; } a { text-decoration: none; } button { cursor: pointer; background-color: transparent; } `; const GlobalStyle = () => { return <Global styles={style} />; }; export default GlobalStyle;
폰트는 Pretendard 사용
index.html
head 태그에 추가<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.6/dist/web/static/pretendard-dynamic-subset.css" />
폴더 구조
📦src ┣ 📂assets ┣ 📂components ┃ ┣ 📂base ┃ ┣ 📂domain ┃ ┣ 📂templat ┣ 📂contex ┣ 📂hooks ┣ 📂page ┣ 📂stories ┃ ┣ 📂components ┃ ┣ 📂hooks ┣ 📂styles ┃ ┗ 📜globalStyle.jsx ┣ 📂utils ┃ ┗ 📜api.js ┣ 📜App.jsx ┗ 📜main.jsx
components
- base: input같은 기본적인 컴포넌트
- domain: postItem, postdetail과 같이 좀 더 디테일한 부분
- template: 항상 있어야 하는 부분(ex. 헤더)
import Menu from '@components/domain/Menu'; const DefaultTemplate = ({ children }) => { return ( <div> <Menu /> <main>{children}</main> </div> ); }; export default DefaultTemplate;
이슈 템플릿 / 깃허브 템플릿
- 이슈 템플릿 (Settings에서 설정 가능)

// 기능 템플릿 ## Description ## Progress - [ ] To do - [ ] To do - [ ] To do // 버그 픽스 템플릿 ## Reproduce > 버그가 어디에서 어떻게 발생했는지 작성 ## Screenshot ## Environment > 어떤 환경(OS), 브라우저
- 깃허브 템플릿
.github
폴더 밑에 pull_request_template.md
파일 생성 후 템플릿 작성## ⛓ 관련 이슈 - close #issue_number ## 📝 작업 내용 - [ ] 구현한 기능 - [ ] 스타일링 등 ## 📍 PR Point - 리뷰어가 알아야 하는 부분 - 우려되는 부분 - 궁금한 점 ## 📷 스크린샷
환경변수 설정
yarn add dotenv
.env
파일 생성API_END_POINT=http://kdt.frontend.3rd.programmers.co.kr PORT=5004
vite.cofig.js
코드 다음과 같이 수정import { defineConfig, loadEnv } from 'vite'; import react from '@vitejs/plugin-react'; import * as path from 'path'; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ''); return { define: { __APP_ENV__: env.APP_ENV, }, plugins: [react()], resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, }; });