저번 투두리스트에서 다소 원활하지 못했던 협업과정을 이번 크립토미터에서는 제대로 준비하고 들어가기로 했다. 최대한 현업과 유사한 환경을 만들기 위하여 디테일한 컨벤션부터 명확한 역할 분담을 하고, 정기적으로 회의를 하기로 했다. 이 모든 과정을 깃허브 위키와 깃허브 프로젝트, issue, 팀 노션에 기록하였다.
CryptoMeter GitHub
CryptoMeter 위키
브랜치 생성후 작업
- 현재 repo를 fork 딴다.
- 그럼 현재 repo가 upstream repo, 본인 repo에 있는 fork repo가 origin repo가 된다.
- fork한 origin repo를 로컬에 clone한 뒤
- dev 브랜치를 기준으로 feature 브랜치 등을 분기해서 작업을 끝내고
git push origin feature
후에
- dev 브랜치를 기준으로
git switch -c feature/delete_irrelevants/#3
후에git push origin feature/delete_irrelevants/#3
- origin feature 브랜치에서 바로 upstream dev로 PR 날린다. (개인 feature => 공용 dev로 PR)
- 코드리뷰를 받았다면,
- upstream repo의 dev와 origin repo의 dev 브랜치 싱크를 맞춰줘야 한다 (혹은 origin dev 브랜치에서 git pull upstream dev)
4-1. 브랜치 생성후 작업(보다 자세한 설명)
a.개인 로컬 dev
브랜치를공용 dev
브랜치와 sync
b.개인 로컬 dev
(hotfix의 경우로컬 main
)브랜치에서 작업할 새 브랜치 생성
git branch refactor/delete_irrelevants/#3
git checkout(or switch) refactor/delete_irrelevants/#3
OR
git checkout -b refactor/delete_irrelevants/#3
OR
git switch -c refactor/delete_irrelevants/#3
c. 작업 완료 후 push
git push origin refactor/delete_irrelevants/#3
PR날리기
작업한 브랜치 → 공용 레포의 dev 또는 main 브랜치
main
브랜치: 프로덕션 브랜치(여기에 push하면 netlify의 CI/CD가 실행됨)dev
브랜치: 버그 픽스가 아닌 모든 작업(꼭 새로운 기능이 아니라도 리팩토링, dev 브랜치 버그 픽스 등) + dev 브랜치도 netlify에 배포한다(개발모드) hotfix
브랜치: 버그 픽스 브랜치, 현재 main브랜치에 있는 버그들을 바로 고치고 merge하는 hotfix 브랜치 역할이렇게 3개의 브랜치로 운영한다. 당장은 dev 브랜치에서 모든 작업을 하므로, hotfix 브랜치를 쓸 일이 없지만, 이후 프로젝트가 끝나고 지속적인 유지/보수를 위해 일단 3개의 브랜치를 운영하는 것으로 한다. 현업처럼 release브랜치까지 나눠서 관리하기엔 규모가 작고 인원도 작아 오히려 유지보수하는데 어려울 것 같아서 과감하게 조금 더 가벼운 브랜치 전략으로 선택했다.
<category>/<description>/<issue-ref>
순으로 이름을 짓기로 한다.feature
, bug
, refactor
로 구분refactor/remove-unused-variables/#1
✅ 리팩토링 작업 중 불필요한 변수를 제거하는 작업을 진행한 브랜치이고, 대응하는 이슈번호는 1번이다.
예시2)`bug/register-form-not-working/#3`
✅ 회원가입 폼 관련 버그(dev브랜치에서 발견된 버그)를 고치는 작업을 진행한 브랜치이고, 대응하는 이슈번호는 3번이다.
예시3)`feature/hover-text/#4`
✅ 호버 텍스트 기능을 구현하기 위한 작업을 진행한 브랜치이고, 대응하는 이슈 번호는 4번이다.
⭐️⭐️ 작업 전후로 반드시 해야하는 upstream-fork sync 과정 ⭐️⭐️
dev
브랜치에서 feature/hover-text/#4
와 같은 이름의 새 브랜치를 만듭니다.dev
브랜치로 merge하는 PR을 생성합니다.hotfix
브랜치로 이동한후, 새 브랜치를 만들 필요 없이 버그 수정 작업을 진행합니다.main
으로 merge하는 PR을 생성합니다. dev
브랜치에서 refactor/remove-unused-variables/#5
와 같은 이름의 새 브랜치를 만듭니다.dev
브랜치로 merge하는 PR을 생성합니다. dev
브랜치에서 bug/register-form-not-working/#7
와 같은 이름의 새 브랜치를 만듭니다.dev
브랜치로 merge하는 PR을 생성합니다. 🚨 Merging Rules 주의
main
으로 머지하는 경우는 hotfix, 새 버전 출시 말고는 없습니다.
리뷰어가 코드리뷰가 끝나면 Approve 마지막에 리뷰한 리뷰어가 Merge하도록 합니다.
리뷰어가 바로 Approve하지 않고 변경사항을 Request하는 경우 해당 PR을 올린 Assignee가 코드를 수정한 뒤 리뷰를 재요청하도록 합니다.
case
- 일반변수:
camelCase
- 상수:
SCREAM_SNAKE_CASE
convention
- 명사로 작성한다.
- 예외) boolean 변수는 is~ has~ includes~ 등의 현재형 동사로 시작한다.
case
camelCase
convention
- 현재형 동사 + 명사로 작성한다.
- 핸들러함수:
handle~
로 시작한다.
case
PascalCase
convention
- 두 단어 이상의 조합으로 작성한다.
LoginPage.tsx
HomeButton.tsx
- Styled Component:
S~
로 시작한다.
export const Sdiv = styled.`
// ...
margin-top: ${({mgt}) => mgt ?? '0'};
${({ct}) => css`display: flex; justify-content: center, align-items: center`};
// ....
case
- 컴포넌트:
PascalCase.jsx
- 일반 파일 : 전부 소문자로 작성한다. 단어와 단어 사이는 dot으로 구분한다.
axios.instance.js
case
- 컴포넌트, 페이지 폴더:
PascalCase
- 일반 폴더: 소문자, 복수형,
kebab-case
(예: components, pages, hooks, …)
├─ src/
│ ├─ assets/
│ │ └─ favicon.svg
│ ├─ components/
│ │ ├─ ToggleButton
│ │ │ └─ ToggleButton.js
│ │ ├─ FilterChip
│ │ │ └─ FilterChip.js
│ │ └─ index.js
│ ├─ (contexts)
│ ├─ data/
│ ├─ services/
│ ├─ styles/
│ │ ├─ colors.js
│ │ └─ styled.components.js
│ ├─ utils/
│ ├─ App.css
│ ├─ App.jsx
│ └─ main.jsx
- assets: 이미지, 폰트 등
- components: 컴포넌트
- data: 정적인 고유 데이터 (mock.json, maps 등)
- services: api 관련 기능들
- styles: 공용 스타일 관련 폴더
- utils: 공용 유틸리티 함수들
- Main.jsx: 엔트리포인트
- App.jsx: 메인 앱
- App.css: 메인 앱 스타일 (reset 포함)
feat
: 새로운 기능(기능 구현)fix
: 버그 수정docs
: documentation 변경style
: 코드 의미에 영향을 주지 않는, 코드 스타일에 관련된 변경 사항(포맷, 공백, 빼먹은 세미콜론, 함수 이름 변경, 줄간격, 파일 이름, 의미없는 주석 삭제)refactor
: 리팩토링에 대한 커밋(버그를 수정하지 않고 기능을 추가하지 않는 코드 변경)chore
: 패키지 매니저 설정할 경우, 코드 수정 없이 설정을 변경(eslint, prettier…패키지 설정), 이외에도 특정 영역에 들어가기 애매한 잡다한 작업들Emoji | Category | Description |
---|---|---|
🎉 | init | Start a project. |
✨ | feat | Introduce new features. |
✏️ | fix | Fix typos. |
🐛 | fix | Fix a bug. |
📝 | docs | Add or update documentation. |
🎨 | style | Improve structure / format of the code. |
♻️ | refactor | Refactor code. |
🔥 | chore | Remove code or files. |
💩 | poop | Write bad code. |
🚚 | chore | Move or rename resources |
🍱 | chore | Add or update assets. |
🔖 | feat/fix | Release / Version tags. |
✨: 회원가입 기능 개발
🐛: 회원가입 시 사용자 이름이 안보이는 버그 수정
> npm i -g gitmoji-cli # 글로벌 주의
# or
> brew install gitmoji
git add . # (add 할 것들 스테이징에 올리고)
gitmoji -c # 입력해서 이모티콘 선택하고
#커밋 타이틀, 커밋 메시지 입력하고
git push ... #로 origin 브랜치에 넣기
- 화살표 함수를 사용합니다.
- if else 문 사용을 지양하고 early return 패턴을 사용합니다.
- 스타일드 컴포넌트는 컴포넌트 하단에 작성합니다.
- 이를 위해 .eslintrc의 rule에
"no-use-before-define": "off",
를 추가합니다- 조건부 렌더링 패턴
- undefined 배열 처리 패턴: 가독성을 위해 옵셔널 체이닝을 사용합니다.
array?.map(()=>{})
- 양자 택일 렌더링 패턴: else를 최대한 지양하고, 코드의 의미 전달을 높일 수 있게 early return 패턴을 사용합니다.
if (condition) return <ThisComponent /> return <ThatComponent />
- 부분 조건부 렌더링 패턴:
- 옵션이 2개 일 때는 삼항 연산자를 사용합니다.
- 옵션이 3개 이상일 때는 object mapping을 사용합니다.
- default value가 필요한 경우 nullish coallescing 문법을 사용합니다.
<span>{title ?? "default"}</span> // 값이 없는 상황에 default 값을 보여줄 때 <span>{isIncreased ? "going up!" : "going down hard.."}</span> // boolean일 때 <span>{messageMap[message]}</span> // object mapping 사용
- 비동기 함수를 사용할 때는 콜백 헬 방지를 위해 async/await를 사용합니다.
- import 경로
- 절대경로를 사용합니다.
- aliasing을 사용하여 src, components 등의 큰 폴더 경로의 별칭을 설정하여 사용합니다.
- 컴포넌트안에서 props 객체는 구조분해 할당하여 사용합니다.
- 커스텀 훅은 배열이 아닌 객체를 반환하도록 합니다.
- eslint 설정
- airbnb extends를 사용합니다.
- import문은 외부 모듈과 내부 모듈로 분리합니다.
- prettier 설정
// .prettierrc { "singleQuote": false, "semi": true, "useTabs": false, "tabWidth": 2, "printWidth": 80, "bracketSpacing": true, "arrowParens": "always" }
- CSS문 작성 규칙: 다음의 스타일 정의 순서를 지켜 가독성을 높이고 수정을 용이하게 합니다.
const SInput = styled.input` /* 레이아웃 */ display: .. visibility: .. .. /* Box model */ width: .. height:.. .. /* 폰트 */ font: .. color: .. .. /* 기타 속성 */ .. `; - 레이아웃 : display, visibility, overflow, float, clear, position, top, right, bottom, left, z-index - BOX : width, height, margin, padding, border - 폰트 : font, color, letter-spacing, text-align, text-decoration, text-indent, vertical-align, white-space
- String concatenation은 +연산자 대신 템플릿 리터럴을 사용합니다.
w + "px" // X `${w}px` // O
- 컴포넌트는 재사용성과 범용성을 위해 최대한 dumb하게 유지합니다.
- 컴포넌트 외부 스타일에 영향을 미치지 않습니다(전체 마진 등)
- 컴포넌트 안에서 api 호출을 하지 않습니다(app.js에서 호출)
- Semantic 태그 사용을 지향합니다.
- State로부터 파생된 state를 사용하는 경우, 원활한 동기화를 위해 reference를 사용합니다.
const [coins, setCoins] = useState([ {id: 1, name: "Bitcoin",}, {id: 2, name: "Dogecoin",}, ]); // 파생된 값을 가진 상태(X) const [selectedCoin, setSelectedCoins] = useState({...}); // 파생된 reference를 가진 상태 (○) const [selectedCoinId, setSelectedCoinId] = useState(2); ```
Language
- Javascript
- HTML5
- CSS3
Framework
- React v18
State Management
- Zustand
CSS
- styled components
Utils
- react-date-picker
- recharts
Ajax
- axios
공통 선정 기준
1. 대중적으로 사용되어 학습 자료가 많고 기술 습득이 쉬운가
2. 커스텀화가 가능한가
Chart
- [Rechart](https://recharts.org/en-US/storybook)
- 요구사항 기능과 매치되는 점이 많다.
- 툴팁 자동 생성
- vertical line 호버시 자동 생성
- 사용법이 쉬워 2주 프로젝트 기간에 사용하기 적합하다.
- prop이 직관적이고 많지 않다.
- https://dillonreedy.github.io/recharts-storybook/?path=/docs/recharts-area-chart--simple
- 무거운 번들 사이즈
CSS
- styled-components
- js 문법을 사용하여 props등의 자바스크립트 변수를 사용할 수 있어 생산성이 높아진다.
- props를 적극 활용하여 디자인 시스템과 비슷한 환경을 구축할 수 있다.
상태관리 툴
- Zustand
- recoil과 유사한 수준의 쉬운 난이도를 자랑한다.
- redux 다음으로 사용자가 많아 참고자료가 많다.
협업 툴
- GitHub
- 브랜치 관리, 백보드를 통한 task 상황 관리(Projects), 회의록 작성(Wiki) 등 다양한 기능을 사용할 수 있다.
- 위 기능을 한 플랫폼으로 통합하여 협업의 능률을 높일 수 있다.
AJAX
- Axios
- 커스텀 인스턴스를 만들어 추상화 및 서비스에 맞는 요청과 응답 세팅이 용이하다.
- 사용방법이 직관적이라 팀 프로젝트에 적합하다.
프로젝트 때 새로 알게 된 것들(혹은 이건 뭐지 싶은 것들) 모음집
문서 관리 담당 smart/dumb component architecture strategy (비즈니스 로직과 뷰 로직을 분리)
컴포넌트의 로직과 스타일을 분리시켜 효율적인 디버깅 및 수정을 가능하게 합니다.
export const HistoryButton = () =>{
// 로직을 통해 prop를 만들어
const handleClickSearchRecord = ()=>{...};
const containerProps = { onClick: handleClickSearchRecord, };
return (
<ButtonContainer {...containerProps}/>
)
}
export const ButtonContainer = ({onClick, isDown?})=>{
return (
<SDiv pdt={40} ct onClick={onClick}>
...
<SText>{isDown ? "down" : "up"}</SText>
</SDiv>
);
}