HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
♥️
2기 최종 프로젝트 팀별 공간
/
🌳
[팀 05] Forest
/
🗑️
휴지통
/
🚢
(deprecated) FE WorkSpace
/
0725 스택 확립 회의

0725 스택 확립 회의

1. 기술스택 확정
2. 스펙 세부 논의
3. 폴더구조 및 파일 구조
2. eslint, prettier
4. 브랜치 룰 , 리뷰 및 PR 컨벤션 정하기
rebase merge VS commit push merge
5. Zenhub 세팅
6. 와이어 프레임 작성
 
 

1. 기술 스택 선정

📄
프론트엔드 1차 협의 내용 (1)

1. 스펙관련 Overview

✅ 1. 사용확정

  • React, Typescript ,Axios, EsLint + Prettier , React-Router, React-query + Recoil, Storybook, MaterialUI
 

✅ 2. 의논 필요

react-query + recoil 선호 VS contextAPI
  • MUI Basic
    • 아토믹 디자인 도입 여부
    • common 컴포넌트 + domain단위 컴포넌트
  • 스토리북 Basic
  • 테스트 Adv
 

⛔️ 3. 프론트 개발 포인트

💡
고민이 필요하다. > 우리의 목적 < - 1차 프로젝트 회고를 전부 읽어보면서, 사전에 도입하면 좋았던 부분들을 생각해보기
[후보]
  • 협업
    • 체계적인 능률 향상을 위한 방법론들 도입
    • 페어 프로그래밍
      • 좋은건 좋은건데, 단, 가능한 환경이냐?
  • 라이브러리 활용 최대화
    • react-query, recoil 등 공식문서를 온전히 이해하고, 제대로 사용하고자 함
  • UX 최대화
    • 사용자 입장에서 최적화 고려
      • 로딩, 스켈레톤 , …
      • 예외처리, 모든 입력에 대한 대응
      • 수치나 근거가 있어야하는데?
  • 컴포넌트 범위나 책임, 역할 분리 지향해야하는데 지킬 수 없는 약속에 가깝다. 매력적인선택지
    • 걱정되는 것은 거시적이다…
      • 고민한다고 되는 분야인가..
      • 시간과 일정이 넉넉치 않은 상황에서 가능한가?
      • basic first, refactor later 와 반대의 길?
  • 성능 최적화 부적합 나는 노력을 했어요 밖에 안되니까 , 선택할 수 없는 선택지
    • 리팩토링 할 때는 강점일 수 있는데, 기준점 정하기가 어렵기 때문에, 초기 프로젝트에서 포인트로 정하기 어렵다.
      • 라이트하우스 기준으로 ~점수의 ~측면이 낮아서 → 어떤 개선액션을 취했고 → ~한 개선효과를 측정했다.
      • 리렌더링 되는 모든 컴포넌트를 탐색 후 → 리렌더링 최소화 개선 액션 → ~~한 효과 측정
 

2. 🎤 스펙 관련 세부 논의

🎤 스택관련 세부 논의

[2.1. Next.js 보류]
  • 도메인 어울리긴 하는데, 시간적인 측면, 생산성 측면을 고려할 때, 프로젝트 이후 리팩토링 단계에 더 적합하다고 판단
[2.2 상태라이브러리 react-query+recoil]
  • server상태: react-query
    • ⚠️ 승범 제안, 인수 찬성, 채우 대기
    • react-query 도입 참고 자료
      • React-Query 도입을 위한 고민 (feat. Recoil) - 오픈소스컨설팅 테크블로그 - 강동희
        Web Frontend 개발을 할 때 React 를 사용하면서 마주하게 되는 여러 가지 문제점 중 하나는 state, 상태 관리에 관한 부분입니다. 프론트엔드 개발자라면 state 와 뗄 수 없는 인연을 맺고 있습니다.오늘 이 글이 작성되는 이유는 바로 state 와 깊은 관련이 있습니다. React 에서 상태 관리를 하기 위해서는 대부분 Redux 라는 상태관리 매니저를 사용합니다.
        React-Query 도입을 위한 고민 (feat. Recoil) - 오픈소스컨설팅 테크블로그 - 강동희
        https://tech.osci.kr/2022/07/13/react-query/
        React-Query 도입을 위한 고민 (feat. Recoil) - 오픈소스컨설팅 테크블로그 - 강동희
  • 전역상태: contextAPI || recoil
    • recoil 사용성 장점
    • contextAPI 가장 row한 단계라는 특징
  • 의견
    • 인수
      • react-query 안쓰고 Context API 하거나
      • react-query 쓴다면 Recoil 같이 사용하기
[2.3 스타일 emotion + MaterialUI + storybook]
  • emotion 결정 ✅
  • 디자인 프레임워크
    • MUI(link) , antD (ant.d)
      • https://moonsupport.oopy.io/post/3
    • 시간
      • 썼을 때: 학습 초기 비용 단, 사용 시 시간 절감 효과
      • 안썼을 때: 디자인이 와이어프레임 이상으로 나왔을 경우, 시간 단축 가능
    • 메인 디자이너가 없는 상황에서 어떤 게 더 이득이냐
      •  
    • 장점) 추후 사용을 위해 필요한 경험이지 않을까?
    • 단점) 전체적인 스택의 학습 곡선도 고려해봐야할 것 같다.

3. 폴더 구조

 
src ┣ api ┣ components // 공통 컴포넌트 ┃ ┗ Header // 컴포넌트 ┃ ┃ ┣ Header.jsx // jsx file ┃ ┃ ┣ Header.style.js //style file ┃ ┃ ┗ index.js // export file ┣ hooks // 공통 hooks ┣ recoil // recoil 로직 ┣ utils // 기타 js로직 폴더 ┣ @type// typescript type 관련 폴더 ┣ constants ┗ pages // page 폴더 ┃ ┣ components // page에서만 쓰이는 컴포넌트 ┃ ┗ hooks // page 내에서만 쓰이는 hooks
 

4. 패키지 설치

  • react-router
  • react-query
  • recoil
  • storybook
  • axios
  • feather-icons
    • [설치]
      • npm install feather-icons --save
      [실제 사용시]
      • <Icon name="triangle" size={30} rotate={270} fill />
      [Icon Component]
      code
      function Icon({ name, size = 16, strokeWidth = 2, color = '#222', rotate = 0, fill, addStyle, ...props }: propsType) { const iconStyle = { 'stroke-width': strokeWidth, stroke: color, width: size, height: size, fill: fill ? color : 'none', }; const shapeStyle = { width: size, height: size, transform: rotate ? `rotate(${rotate}deg)` : undefined, }; const icon = icons[name]; const svg = icon ? icon.toSvg(iconStyle) : ''; const base64 = Buffer.from(svg, 'utf8').toString('base64'); return ( <IconWrapper style={{ ...shapeStyle, ...addStyle }} {...props}> <img alt={name} src={`data:image/svg+xml;base64,${base64}`} /> </IconWrapper> ); } export default Icon;
       
       

5. ESLint 및 Prettier

인수 ESLint
{ "env": { "browser": true, "node": true, "es6": true }, "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "airbnb", "plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:prettier/recommended" ], "plugins": [ "prettier" ], "rules": { "linebreak-style": "off", "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "react/jsx-filename-extension": [ 1, { "extensions": [ ".js", ".jsx", "tsx" ] } ], "import/no-unresolved": "off", "react/react-in-jsx-scope": "off", "react/jsx-props-no-spreading": "off", "react/require-default-props": "off", "import/no-extraneous-dependencies": [ "error", { "devDependencies": true } ], "import/extensions": "off", "@typescript-eslint/no-non-null-assertion": "off", "prettier/prettier": ["error", { "endOfLine": "auto" }], "no-underscore-dangle": "off", "import/prefer-default-export": "off", }, "overrides": [ { "files": [ "*.ts", "*.tsx" ], "rules": { "no-undef": "off", "no-unused-vars": "off" } }, { "files": [ "*.config.js" ], "rules": { "@typescript-eslint/no-var-requires": "off" } } ], "parser": "@typescript-eslint/parser" }
승범 ESLint
{ "extends": ["airbnb", "plugin:prettier/recommended"], "plugins": ["react", "jsx-a11y", "import"], "parserOptions": { "ecmaVersion": 2021 }, "rules": { "prettier/prettier": ["error", { "endOfLine": "auto" }], "react/jsx-filename-extension": [ "error", { "extensions": [".js", ".jsx"] } ], "jsx-a11y/img-has-alt": "off", "react/function-component-definition": "off", "react/jsx-props-no-spreading": "off", "consistent-return": "off", "no-undef": "off", "import/no-unresolved": "off", "no-underscore-dangle": "off" } }
채우 ESLint
module.exports = { env: { browser: true, es2021: true, }, extends: [ 'plugin:react/recommended', 'plugin:react-hooks/recommended', 'airbnb', 'plugin:prettier/recommended', ], parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 'latest', sourceType: 'module', }, plugins: ['react'], rules: { 'prettier/prettier': [ 'error', { endOfLine: 'auto', }, ], 'react/jsx-filename-extension': ['warn', { extensions: ['.js', 'jsx'] }], 'linebreak-style': 0, 'react/react-in-jsx-scope': 0, 'comma-dangle': 0, 'no-unused-vars': 1, 'import/no-unresolved': 0, 'no-unused-expressions': ['error', { allowShortCircuit: true }], 'react/jsx-props-no-spreading': 0, 'react/forbid-prop-types': 0, 'consistent-return': 0, 'no-shadow': 0, 'no-underscore-dangle': 0, }, };
 
Prettier
code
{ "singleQuote": true, "semi": true, "useTabs": false, "tabWidth": 2, "trailingComma": "all", "printWidth": 80 }
"eslint-plugin-prettier": "^4.0.0", "eslint-config-prettier": "^8.5.0",
 
 

6. 브랜치 룰 리뷰 및 PR 컨벤션

6.1 브랜치 룰

main : 배포용
develop : 개발
feat : 기능 구현
💡
feat/[이슈번호] ex) feat/123
 

6.2 PR컨벤션 및 리뷰 룰

[리뷰]
  • PR 단위
    • issue 단위
    • issue 생성 시 고려하기
  • 리뷰 강도
    • 작성자의 PR 포인트 위주로
  • 리뷰 시간
    • 급함: 30분 이내 리뷰시작
    • 일반: 다음 코탐 전 까지
 
PR Template
예시

📌 PR 설명

아주 기본적인 Card 컴포넌트를 만들었습니다.
  • CheQuiz의 대부분 컴포넌트는 사각형 구조로 되어 있습니다. 이런 구조 Container를 만들 때, 바로 가져와서 쓰도록 Base 컴포넌트로서 Card를 구현하였습니다.

💻 요구 사항과 구현 내용

e684479 Card 컴포넌트를 구현했습니다.

✔️ PR 포인트 & 궁금한 점

일단 아주 기본적이지만 필요했던, Card 컴포넌트만 구현해 놨습니다.
  • 각 파트에서 사용하고 있는 Card 비슷한 컴포넌트도 만들어 드리고 싶었는데, 스타일드 컴포넌트로만 구현하기에는 어려움이 있어 일단 보류했습니다.
  • 일단 기본적인 제가 맡은 컴포넌트는 여기까지인데, 있으면 좋겠다 하는 컴포넌트가 혹시 있을까요?close [Feat] 카드 컴포넌트 구현 #163
  • 라벨
    • 급함: 다음작업이 연속적일 때
      • PR 후 슬랙 메세지 보내기
      • PR 후 30분 이내 보기
    • 일반
      • 다음 코탐 이전까지
  • rebase merge 사용

Todo

PR Template 만들어 등록하기
Creating a pull request template for your repository - GitHub Docs
For more information, see " About issue and pull request templates." You can create a PULL_REQUEST_TEMPLATE/ subdirectory in any of the supported folders to contain multiple pull request templates, and use the template query parameter to specify the template that will fill the pull request body.
https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository
Creating a pull request template for your repository - GitHub Docs