부트캠프에서 뵀던 백엔드 개발자분께서 실제 이용자가 있는 서비스를 목표로 프로젝트를 만들어보자고 제안해주셨다. 핵심 기능은 유저별 맞춤 정보 푸쉬 알림, 지도를 통한 인접지역 공연정보 제공이다.
개인적으로는 프로젝트의 시작부터 끝까지, 만족할만한 프로젝트 관리와 코드 퀄리티를 유지하는 것이 목표다. 그래서 초기 목표는 기술적인 챌린지를 최소화하는 방향으로 정했다.
내 코드는 아직 구현하는데 급급하고, 회사 내 사수도 없다. 때문에, 사내 모바일 팀 리더께서 공유해주신 리액트 프로젝트 구조를 참조하기로 했다. 코드를 정형화시키고 내 것이 되었다 느끼면, 내가 생각하는 방향으로 개선해 나갈 예정이다.
Bulletproof React
해당 코드에서는 React + Typescript로 개발한다. 나 역시 타입스크립트는 필수로 도입할 예정이었지만, React와 Next사이에서 고민을 했다. 프로젝트 내에서 내가 안드로이드 파트와 차별되는 가장 큰 부분이
React와 Next를 얘기할 때 비중이 큰 부분도, 검색엔진 최적화와 같은 부분을 포함하므로 Next로 개발하고 싶은 생각도 컸다. 하지만, React를 제대로 작성하지 못하고 있고, React를 활용한 검색엔진 최적화를 경험해보는 것이 우선이라고 생각했다. 또, 사이드 프로젝트인 만큼 챌린징하는 부분을 최소화해야 내 의도대로 프로젝트를 만들어갈 수 있다는 판단도 React로 시작하는 선택에 기여했다.
프로젝트 관리에 사용하는 도구들은 아래와 같다.
프로젝트 관리 도구
yarn create-react app 프로젝트명 --template typescript
CRA와 Yarn을 통해 타입스크립트 리액트 템플릿을 활용해 프로젝트를 생성하였다. 템플릿 구조에 익숙해져서 템플릿을 변형하거나, 템플릿 없이 작성하고 싶지만 아직은 시기상조다.
cra 명령어의 뒤에 --template typescript를 작성하면 기존 jsx파일이 tsx로 생성된다. 또한 타입스크립트를 컴파일하는 과정에서 필요한 내용을 작성할 수 있다.
//tsconfig.json
{
"compilerOptions": {
"target": "es5",
"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"
]
}
yarn eslint --init
1. You can also run this command directly using 'npm init @eslint/config'. Need to install the following packages: @eslint/create-config
Ok to proceed? (y)내용 : @esling/create-config 설치 여부
입력 : y2. How would you like to use ESLint?
To check syntax only
To check syntax and find problems
To check syntax, find problems, and enforce code style내용 : ESLint 사용 목적
1. 문법 검사
2. 문법 + 에러검사
3. 문법 + 에러검사 + 코드스타일 강제
입력 : 3(해당 행 선택 후 엔터)3. What would you like to use ESLint?
JavaScript modules (import/export)
CommonJS (require/export)
None of these내용 : 자바스크립트 모듈 이용방식
1. 자바스크립트 모듈(import/export)
2. CommonJs 모듈(require/export)
3. 그 외 방식
입력 : 14. Which framework does your project use?
React
Vue.js
None of these내용 : 프로젝트에 사용하는 프레임워크
1. React
2. Vue.js
3. 그 외 도구
입력 : 15. Does your project use TypeScript?
No
Yes내용 : 타입스크립트 사용 여부
1. 사용 안 함
2. 사용함
입력 : No6. Where does yout code run? (Press
<space>
to select,<a>
to toggle all,<i>
to invert selection)
Browser
Node내용 : 코드 실행 도구
1. 브라우저
2. Node
3. 모두(a)
입력 : 모두 (a누른 후 엔터)7. Which style guide do you want to follow?
Use a popular style guide
Answer question about style내용 : 스타일 가이드
1. 상용화 된 스타일
2. 스타일 커스텀
입력 : 18. Which style guide do you want to follow?
standard: https://github.com/standard/eslint-config-standard-with-typescript
XO: https://github.com/xojs/eslint-config-xo-typescript내용 : 스타일가이드 선택
입력 : 19. What format do you want your config file to be in?
JavaScript
YAML
JSON내용 : 구성 파일 저장방식
입력 : JavaScript10. Would you like to install them now with npm?
NO
Yes내용 : 즉시 설치여부
입력 : Yes11. Which package manager do you want to use?
npm
yarn
pnpm내용 : 패키지 매니저 선택
입력 : yarn
eslint설정이 끝나면 아래와 같은 json파일이 생성된다.
//.eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'plugin:react/recommended',
'standard-with-typescript'
],
overrides: [
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: [
'react'
],
rules: {
}
}
bulletproof에 .eslintrc.js 파일의 내용으로 교체했다.
.eslintrc.js
module.exports = {
root: true,
env: {
node: true,
es6: true,
},
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
ignorePatterns: ['node_modules/*'],
extends: ['eslint:recommended'],
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
settings: {
react: { version: 'detect' },
'import/resolver': {
typescript: {},
},
},
env: {
browser: true,
node: true,
es6: true,
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:prettier/recommended',
'plugin:testing-library/react',
'plugin:jest-dom/recommended',
],
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@/features/*/*'],
},
],
'linebreak-style': ['error', 'unix'],
'react/prop-types': 'off',
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
'import/default': 'off',
'import/no-named-as-default-member': 'off',
'import/no-named-as-default': 'off',
'react/react-in-jsx-scope': 'off',
'jsx-a11y/anchor-is-valid': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/explicit-function-return-type': ['off'],
'@typescript-eslint/explicit-module-boundary-types': ['off'],
'@typescript-eslint/no-empty-function': ['off'],
'@typescript-eslint/no-explicit-any': ['off'],
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
},
},
],
};
- 린트와 관련하여 아래 오류 메세지가 나타났다.
[eslint] Failed to load plugin 'prettier' declared in '.eslintrc.js#overrides[0]': Cannot find module 'eslint-plugin-prettier'
eslint플러그인이 .eslintrc.js파일의 경로를 인식하지 못하여 발생한 문제이다. settings.json의 최상단에 아래의 코드를 작성하여 해결했다.
"eslint.workingDirectories": ["/"]
//.prettier.json
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"tabWidth": 2,
"useTabs": false
}
prettier 확장 설치
Format document 실행
cmd + P > "> Formmat Document" 검색 > 클릭하여 실행
Preitter 자동실행 설정하기
cmd + , > "default formatter" 검색 > Prettier - Code formatter로 변경 후 VSCode 재부팅
허스키 설정은 처음 접하는 내용이다. git hook을 적용시키기 위한 npm 라이브러리라고 한다. 커밋, 리베이스, 머지, 푸쉬 등의 깃과 관련한 동작이 발생할 경우 실행되는 기능들을 설정할 수 있다.
- 설치
yarn add husky --dev //개발 모드에만 적용
(yarn add pinst --dev)
- 허스키 활성화
yarn husky install
2-2. git hook 자동 활성화 방법// package.json { ("private": true) "scripts": { "postinstall": "husky install" } }
2-3. 제거
yarn remove husky && git config --unset core.hooksPath
- package.json 원상복구
.git/hooks 디렉토리 안에 훅명을 딴 파일을 생성하여, 실행할 코드를 작성하면 된다.
- 새로운 훅 추가하기
yarn husky add .husky/pre-push
master로 직접 push 방지하기
// .git/hooks/pre-push
#!/bin/sh
FORBIDDEN_HTTPS_URL="https://github.com/gabia/git-hooks-study.git" # insert your remote url (https)
FORBIDDEN_SSH_URL="git@github.com:gabia/git-hooks-study.git" # insert your remote url (ssh)
FORBIDDEN_REF="refs/heads/master" # insert branch ref
remote="$1"
url="$2"
if [ "$url" != "$FORBIDDEN_HTTPS_URL" -a "$url" != "$FORBIDDEN_SSH_URL" ]
then
exit 0 # Forked Project 에서는 제한하지 않음
fi
if read local_ref local_sha remote_ref remote_sha
then
if [ "$remote_ref" == "$FORBIDDEN_REF" ]
then
echo "DO NOT PUSH it master"
exit 1 # 금지된 ref 로 push 를 실행하면 에러
fi
fi
exit 0
url 주소값을 안 바꾼 탓이었다..ㅠ 새로운 걸 처음 쓸 때는 항상 어설프다
아무튼 해결!
절대경로를 설정하면, 해당 모듈, 컴포넌트, 파일의 위치가 비교적 직관적이게 된다.
//tsconfig.json
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
와따..!