우선 개발자로서 필요한 역량이란 크게 두가지가 있다. 하드 스킬
과 소프트 스킬
이다. 하드 스킬
은 개발 업무에 있어 필요한 지식과 기술을 말하고, 소프트
스킬은 커뮤니케이션 능력, 협업 능력같은 팀으로서 일할 수 있는 능력을 말한다.
여기서 소프트 스킬
이 중요한 이유는, 개발자 혼자서는 하나의 프로덕트를 만들 수 없기 때문이다. 규모가 있는 프로젝트의 경우 개발자 뿐만 아니라 기획, 디자이너 등 여러 직군과의 협업을 통해 만들어 낼 수 있기 때문이다. 특히나 프론트엔드 직군은 타 직군 대비 더 많은 커뮤니케이션이 필요한 직군이다. 즉, 개발에 대한 이슈나 문제에 대해 비 개발직군에도 쉽게 이해할 수 있도록 설명할 수 있는 능력이 필요하다.
이러한 소프트스킬
은 나의 생각을 정리하여 쉽게 전달하는 것 뿐만 아니라 문서작성에도 영향을 끼친다. 그 중에서 commit
, pr
, 그리고 작성한code
를 통해 문서를통한 커뮤니케이션도 협업의 중요한 한 요소라고 할 수 있다. 이번 주제는 이와 연관해서 Git과 GitHub을 문서의 관점에서 바라보고 협업 능력, 효율적인 소통을 위해서 어떤 부분들을 시도할 수 있는지 그리고 개발자는 어떻게 팀으로 일하는지에 대해 좀 더 자세히 알아보자.
이제 Git & Github은 버전 관리 시스템
의 역할을 넘어서서 문서, 협업 툴의 역할 또한 수행하고 있다. 이 중에서 다른 개발자의 코드를 분석할 때, 코드가 언제, 어떤 목적을 가지고 작성되었는가를 확인할 때 보는 commit message
는 단순히 "수정"
, "완료"
등 목적과 의도를 알 수 없는 메시지로만 기록이 되어있다면 이 코드의 히스토리
를 파악하기 어려워진다. 따라서 팀이라면 팀만의 커밋 메세지 규칙을 설정하고, 혼자만의 저장소라도 언제든 내가 다시 볼때 쉽게 파악할 수 있도록 규칙을 설정하여 자세히 작성하는 것이 좋다.
commit message
도 중요하지만, 프로젝트의 전반적인 흐름도 알아보기 위해 전체 commit의 history를 확인할 필요도 있다. 이때, main 브랜치의 commit history
가 뒤죽박죽이며 진행중인 커밋과 같이 완성과 관계없는 커밋들이 혼재하고 있으면 전체 히스토리를 파악하기는 어려울 것이다.
즉 히스토리의 관리가 부족하면, 커밋들은 계속 쌓이게 될 것이고 점점 프로젝트의 흐름
을 파악하기 어려워질 것이다.
그렇다면, history를 깔끔하게 유지하기 위해 유의미한 단위(기능 완성)로만 해야할까? 이것 또한 중간에 커밋을 통해 진행중인 작업물의 기록을 남기지 않으면 작업물을 잃어버릴 가능성 또한 있고, 만약 진행중에 이전 기록으로 돌아가야 하는 상황이 발생하면 뒤로 돌아가는 시점도 멀어지게 될 것이다.
따라서, 원하는 만큼 중간에 커밋 기록을 작성하되, 최종적으로 한 브랜치의 작업이 완료되고, PR를 통해 develop(개발 메인브랜치)에 merge 요청을 하기 전 시점에 적절한 형태로 커밋을 정리하면 된다. Git 에서는 squash, rebase 명령어를 통해서 여러 커밋들을 하나로 합칠 수 있다. 자세한 방법은 아래 블로그에 설명되어 있다.
개발자마다 각자 다른 코딩 스타일을 가지고 있고, 그것이 코드에 드러난다면 통일성이 사라진 프로젝트를 제 3자가 읽기도 어려워지며, 팀원들끼리도 다른 팀원들이 작성한 코드를 읽고 이해하기가 힘들어진다. 결국 이러한 요소들은 비효율을 초래하며 유지보수에서도 부정적인 영향을 끼칠 것이다. 그래서 eslint를 사용하여 개발자들간의 코드 스타일을 일치시키기 위해 사용한다. 주로, JS에서 사용하는 linter는 eslint, code formatter는 prettier를 사용한다.
코딩 스타일은 팀끼리 지정하게 되며 eslint에서 설정하여 적용하게 되는데, 자동화 할 수 없는 컨벤션(eslint로 할 수 없는 규칙)은 최소한으로 하는 것이 좋다. 만약 그 규칙에 생소한 사람이 개발을 진행할 경우 기존에 몸에 밴 습관으로 인해 잘 지켜지지 않을 가능성이 있기 때문이다.
결론적으로, 이러한 eslint 초기 설정은 복잡해 보일 수 있으나, 한번 적용해 두면 추후에 다른 프로젝트에서도 적용하기 쉽고, 장기적인 관점에서 개발 생산성에 도움을 주기 때문에 반드시 설정하는 것이 좋다고 생각한다.
npm install eslint --save-dev
- CRA의 경우 내장되어 있기 때문에 따로 설치하지 않아도 된다.
npm install prettier --save-dev
- eslint는 linting 기능을, prettier는 formatting을 담당하는 구조가 이상적이다.
- 하지만, eslint에는 일부 formatting 관련된 rule도 포함되어 있기 때문에 서로 다른 설정을 가지고 있다면 설정 충돌이 발생한다.
- 따라서, eslint에서 formatting 관련 rule들을 모두 해제해야할 필요가 있으며, 직접 코드를 작성하여 진행할 수도 있지만, 이를 적용해주는 eslint plugin이 존재한다.
npm install eslint-config-prettier --save-dev
- package들을 설치하면 terminal에서 명령어를 통해서 eslint와 prettier를 실행할 수 있으며, IDE에서는 일반적으로 terminal 명령어로 실행하는 것 뿐만 아니라, 에디터 차원에서 파일을 저장할 때 formatting을 적용해주고, 에디터에서 eslint의 메세지들을 확인할 수 있게 해주는 기능들을 플러그인 형태로 제공하기에 원한다면 사용할 수 있다.
- 패키지는 사용할 수 있지만 아직 팀원들간의 일관적인 규칙을 적용하지는 않은 상태이므로 팀에 맞게 커스터마이징해서 사용하여 적용해야 한다.
- 프로젝트의 루트 디렉토리에
.prettierrc.확장자
파일을 통해서 설정을 할 수 있다.(JSON, JS, YAML ...ect)
예시 코드는 최근 프로젝트에서 prettier 커스텀 설정 옵션들이다.
// .prettierrc.json
{
"singleQuote": true, // 문자열에 싱글 쿼트(')를 사용
"semi": true, // 문장 끝에 세미콜론(;)을 사용
"useTabs": false, // 탭 대신 스페이스를 사용
"printWidth": 80 // 줄 바꿈을 수행할 프린트 마진의 너비를 80으로 설정
}
- eslint 설정은 언어별(js, ts 등), 환경별(web, node, react 등) 세팅을 해줘야 하는 부분이 많아서 다소 복잡할 수 있다.
- 처음부터 모든 rule 하나하나 설정하는 것이 불필요하거나 불편하다고 판단되는 경우와 다른 사람들이 이미 정의해둔
config
를 설치한 후 확장해서 사용할 수 있다.(airbnb, ...etc)- eslint에서 기본적으로 제공되지 않는 특정 환경을 위한 rule들을 추가하고 싶을 경우에는
plugin
을 이용할 수 있다.
예시 코드는 최근 프로젝트에서 eslint 커스텀 설정 옵션들이다.
// .eslintrc.json
{
"env": {
"browser": true, // 브라우저 전역 변수를 사용하도록 설정
"es2021": true, // ES2021 글로벌 변수와 문법을 사용하도록 설정
"node": true // Node.js 전역 변수와 Node.js 스코프를 사용하도록 설정
},
"extends": [
"eslint:recommended", // ESLint에서 권장하는 규칙을 사용
"plugin:react/recommended", // ESLint의 React 플러그인에서 권장하는 규칙을 사용
"plugin:@typescript-eslint/recommended", // TypeScript ESLint 플러그인에서 권장하는 규칙을 사용
"next/core-web-vitals", // Next.js의 Core Web Vitals 플러그인에서 권장하는 규칙을 사용
"airbnb", // Airbnb 스타일 가이드에서 권장하는 규칙을 사용
"airbnb/hooks", // Airbnb 스타일 가이드의 React Hooks 규칙을 사용
"airbnb-typescript", // Airbnb 스타일 가이드의 TypeScript 규칙을 사용
"prettier" // Prettier의 코드 스타일링 규칙을 사용
],
"overrides": [], // 규칙을 재정의 할 파일 패턴을 설정
"parser": "@typescript-eslint/parser", // TypeScript를 파싱하기 위한 ESLint 파서를 설정
"parserOptions": {
"ecmaVersion": "latest", // ECMAScript 버전을 최신으로 설정
"sourceType": "module", // 소스코드가 ECMAScript 모듈임을 설정
"project": ["tsconfig.json"] // TypeScript 프로젝트 설정 파일 위치
},
"plugins": [
"eslint-plugin-simple-import-sort", // import 정렬을 위한 플러그인
"unused-imports", // 사용하지 않는 import를 검출하는 플러그인
"react", // React 관련 규칙을 지원하는 플러그인
"@typescript-eslint" // TypeScript 관련 규칙을 지원하는 플러그인
],
"rules": {
"import/prefer-default-export": "off", // 파일 당 하나의 내보내기가 있는 경우에 기본 내보내기를 사용하도록 하는 규칙을 비활성화
"react/require-default-props": "off", // defaultProps를 필수로 요구하는 규칙을 비활성화
"react/react-in-jsx-scope": "off", // JSX를 사용할 때 'React'를 항상 import 해야하는 규칙을 비활성화
"react/jsx-props-no-spreading": "off", // JSX props spreading을 금지하는 규칙을 비활성화
"react/function-component-definition": "off", // 함수형 컴포넌트 정의 방식에 대한 규칙을 비활성화
"react/jsx-no-useless-fragment": "off" // 불필요한 React.Fragment를 사용하는 것을 금지하는 규칙을 비활성화
}
}
https://eslint.org/docs/latest/rules/
https://eslint.org/docs/latest/use/configure/configuration-files#using-a-shareable-configuration-package
https://eslint.org/docs/latest/use/configure/plugins