팀 프로젝트에서 팀원과의 코드스타일을 맞추는 것은 매우 중요합니다.
코드스타일을 맞춰 획일화를 하는 것은 단순히 가독성이 좋아진라는 장점 뿐만 아니라 버그와 오류를 방지하고 코드 품질을 향상시켜줍니다. react에서는 prettier와 eslint를 활용하여 코드의 일관성, 품질, 가독성을 향상시키고 개발자 간 협업을 더 쉽게 만들어 줄 수 있습니다. 우리 팀이 이를 지키기 위해 적용한 것들을 살펴보겠습니다.
import { roomState } from '../../recoil/recoil';
import { useCreateRoom } from '../../../hooks/api/rooms';
import { ErrorToastMessage } from '../../utils/toastMessage';
import { CreateRoomScreenProps } from './type/param/loginStack';
import EndCircle from '../../../images/Match/EndCircle.svg';
import StartCircle from '../../../images/Match/StartCircle.svg';
import EndGrayCircle from '../../../images/Match/EndGrayCircle.svg';
프로젝트를 진행하다보면 여러 파일들이나 라이브러리를 import해서 사용해야 하고 이렇게 상대경로로 파일들을 가져오는 것은 가독성을 해칩니다. 또한 파일의 위치를 변경했을때 해당 파일을 사용하고 있는 파일들에 경로를 수정해주어야 하여 유지보수를 어렵게 만듭니다.
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'module-resolver',
{
root: ['.'],
extensions: [
'.js',
'.jsx',
'.ts',
'.tsx',
'.android.js',
'.android.tsx',
'.ios.js',
'.ios.tsx',
],
alias: {
'@providers': './src/providers',
'@components': './src/components',
'@hooks': './src/hooks',
'@pages': './src/pages',
'@routes': './src/routes',
'@type': './src/type', // types는 불가능해서 type으로 대체
'@utils': './src/utils',
'@assets': './src/assets',
'@recoil': './src/recoil',
'@server': './src/server',
'@axios': './src/axios',
},
},
],
],
};
babel.config.js
의 root에 루트 폴더 경로를 설정해주고, alias에 경로들을 작성해 줍니다. 이상하게 types는 작동하지 않아 type으로 대체하였습니다.
{
"paths": {
"@providers/*": ["src/providers/*"],
"@components/*": ["src/components/*"],
"@hooks/*": ["src/hooks/*"],
"@routes/*": ["src/routes/*"],
"@type/*": ["src/type/*"], // types는 불가능해서 type으로 대체
"@utils/*": ["src/utils/*"],
"@assets/*": ["src/assets/*"],
"@server/*": ["src/server/*"],
"@recoil/*": ["src/recoil/*"],
"@axios/*": ["src/axios/*"]
}
}
typescript를 사용하고 있다면, typescript → javascript 컴파일 시에 상대경로를 파악해야하기 때문에 tsconfig.json
의 paths
부분에 똑같이 alias를 설정해 줍니다.
이렇게 하면 알아보기 힘들었던 상대경로로 적혀있던 파일들이 한눈에 알아보기 쉽게 정렬된 것을 확인할 수 있습니다.
module.exports = {
// 쌍따옴표 대신 홑따옴표 사용 (주로 홀따옴표 사용)
singleQuote: true,
// 모든 구문 끝에 세미콜론 출력 [default]
semi: true,
// 공백 대신 탭으로 줄을 들여씁니다. [default]
useTabs: false,
// 들여쓰기 공백 수 [default]
tabWidth: 2,
// 가능하면 후행 쉼표 사용 [default]
trailingComma: 'all',
// 줄 바꿈할 길이 [default]
printWidth: 100,
// 객체 괄호에 공백 삽입 [default]
bracketSpacing: true,
// 항상 화살표 함수의 매개 변수를 괄호로 감쌈 [default]
arrowParens: 'always',
// OS에 따른 코드라인 끝 처리 방식 사용 (윈도우 맥 등 병행 작업 때문에 설정)
endOfLine: 'auto',
};
Prettier
는 코드를 읽어 들여서 사용자가 지정한 '옵션'에 따라 코드를 다시 (정렬)하는 "코드 포맷터"입니다. 코드를 자동으로 포맷하여 일관된 스타일로 유지하는 것이 주된 목적입니다.
저장할때마다 자동으로 Prettier
가 적용되게 할 수 있는데요, save시에 prettier가 적용되게 하는 법은 다른곳에서 찾아서 적용해 봅시다 :)
Eslint는 코드의 품질을 검사하고 코딩 규칙을 강제하여 오류를 방지하고 일관된 코드 작성을 유도하는 것이 주된 목적입니다. Eslint는 코드를 자동으로 포맷하여 일관된 스타일을 유지하고, Eslint는 코드의 품질을 검사하고 코딩 규칙을 강제한다는 것에서 차이점이 있습니다.
"rules": {
"no-console": 1,
"camelcase": 0,
"no-use-before-define": 1,
}
.eslintrc
파일을 작성하고 rules
안에 규칙들을 작성해주면 됩니다. 사용하지 않는 변수들에 대해 경고를 표시해준다던지, camelcase를 사용하지 않는다던지에 대해서 경고를 표시해줄 수 있습니다.
import React, { useState } from 'react';
import { View, Text, Pressable } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context';
import DatePickerComponent from '@components/DatePicker';
import { NavigationProp, useNavigation } from '@react-navigation/native';
import HeaderComponent from '@components/Header';
import DescriptionComponent from '@components/Description';
import ButtonComponent from '@components/Button';
import DottedLineComponent from '@components/DottedLine';
import PassengerComponent from '@components/Match/Passenger';
import CategoryComponent from '@components/Match/Category';
import { LoginStackParamList } from '@type/ParamLists';
import { ErrorToastMessage } from '@utils/toastMessage';
import { useCreateMatch } from '@hooks/api/rooms';
다른 규칙에 대해서는 팀원들끼리 정하면 될 것이고, 우리가 사용해 볼 것은 import
관련입니다.
위에서 alias
를 적용해주었다 하더라도, 서로 다른 경로들에서 가져온 파일들이 뒤죽박죽 있다면 보기가 매우 힘듭니다. 같은 폴더에서 가져온 파일들은 그룹화를 시켜서 정렬시킨다면 매우 깔끔할 것 같습니다.
npm i --save-dev eslint-plugin-import
해당 문제를 해결하기 위한 라이브러리가 존재하는데요, eslint-plugin-import
를 설치해 줍시다.
"rules": {
'import/order': [
'error',
{
groups: [
'type',
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'unknown',
],
"pathGroups": [
{
"pattern": "react*",
"group": "external",
"position": "before"
},
{
"pattern": "@hooks/*",
"group": "internal",
"position": "after"
},
{
"pattern": "@pages/*",
"group": "internal",
"position": "after"
},
{
"pattern": "@components/*",
"group": "internal",
"position": "after"
}
],
"alphabetize": {
"order": "asc"
}
"newlines-between": "always",
}
]
}
그런다음 rules
에 import/order
을 추가하고, 정렬해줄 그룹들을 설정해주면 됩니다.
그룹은 type
, builtin
, external
, internal
, parent
, sibling
, index
, unknown
총 8개를 사용할 수 있습니다.
위에 저는 react가 붙은 파일들에 대해서는 external
그룹으로 묶고, 그 외 그룹들에 대해서는 internal
그룹으로 묶어주었습니다. 또한 alphabetize
을 asc
로 하여 오름차순이 되게끔 하였고, newlines-between
를 통해 그룹간 띄어쓰기를 보장해주었습니다.
import React, { useState } from 'react';
import { useRecoilState } from 'recoil';
import { View, Text, Pressable } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context';
import ButtonComponent from '@components/Button';
import HeaderComponent from '@components/Header';
import DatePickerComponent from '@components/DatePicker';
import DottedLineComponent from '@components/DottedLine';
import DescriptionComponent from '@components/Description';
import CategoryComponent from '@components/Match/Category';
import PassengerComponent from '@components/Match/Passenger';
import { roomState } from '@recoil/recoil';
import { useCreateRoom } from '@hooks/api/rooms';
import { ErrorToastMessage } from '@utils/toastMessage';
import { CreateRoomScreenProps } from '@type/param/loginStack';
그런 다음 eslint --fix
를 통해 정렬을 해주면 이렇게 그룹간에 알파벳 순서대로 정렬되어 가독성이 조금 더 좋아진 것을 확인할 수 있습니다.
근데 해당 라이브러리는 문제가 존재합니다. 위에서 말했듯 해당 라이브러리는 type
, builtin
, external
, internal
, parent
, sibling
, index
, unknown
8개만 사용할 수 있기 때문에 우리가 지정한 커스텀 그룹을 만들지 못하고 이렇게 되면 newlines-between
를 통해 그룹간 띄어쓰기를 보장해 주어도 @components
와 @utils
을 같은 그룹이기 때문에 위처럼 띄어 쓰기가 되지 않는 것을 확인할 수 있습니다.
import React, { useState } from 'react';
import { useRecoilState } from 'recoil';
import { View, Text, Pressable } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useChatContext } from 'src/providers/chatProvider';
import ButtonComponent from '@components/Button';
import HeaderComponent from '@components/Header';
import DatePickerComponent from '@components/DatePicker';
import DottedLineComponent from '@components/DottedLine';
import DescriptionComponent from '@components/Description';
import CategoryComponent from '@components/Match/Category';
import PassengerComponent from '@components/Match/Passenger';
import { roomState } from '@recoil/recoil';
import { useCreateRoom } from '@hooks/api/rooms';
import { ErrorToastMessage } from '@utils/toastMessage';
import { CreateRoomScreenProps } from '@type/param/loginStack';
import EndCircle from '@assets/images/Match/EndCircle.svg';
import StartCircle from '@assets/images/Match/StartCircle.svg';
우리가 원하는 형태를 이렇게 alias 별로 그룹화가되어 띄어쓰기가 보장 되는 것 입니다.
npm i --save-dev eslint-plugin-perfectionist
eslint-plugin-perfectionist 라이브러리를 사용하면 해당 문제를 해결할 수 있습니다. 설치를 해줍시다 :)
type CustomGroup = string
type Group =
| 'builtin'
| 'external'
| 'internal'
| 'parent'
| 'sibling'
| 'side-effect'
| 'side-effect-style'
| 'index'
| 'object'
| 'style'
| 'type'
| 'builtin-type'
| 'external-type'
| 'internal-type'
| 'parent-type'
| 'sibling-type'
| 'index-type'
| 'unknown'
| CustomGroup
eslint-plugin-perfectionist 은 기존 라이브러리에서 CustomGroup이 추가되어 우리가 임의로 그룹들을 설정해줄 수 있습니다.
"groups": [
["builtin", "external"],
"internal",
["parent", "sibling", "index"],
"object",
"unknown",
"custom-screens",
"custom-components",
"custom-routes",
"custom-recoil",
"custom-axios",
"custom-server",
"custom-hooks",
"custom-utils",
"custom-type",
"custom-assets",
],
"custom-groups": {
"value": {
"custom-screens": "@screens/**",
"custom-components": "@components/**",
"custom-routes": "@routes/**",
"custom-recoil": "@recoil/**",
"custom-axios": "@axios/**",
"custom-server": "@server/**",
"custom-hooks": "@hooks/**",
"custom-utils": "@utils/**",
"custom-type": "@type/**",
"custom-assets": "@assets/**",
},
},
custom-groups 에 설정해 주고 싶은 그룹들을 정의 해준다음에, groups 배열에 정렬을 원하는 순서대로 넣어주면 됩니다.
"rules": {
"perfectionist/sort-named-imports": [
1,
{
"order": "asc",
"type": "line-length",
},
],
"perfectionist/sort-named-exports": [
1,
{
"order": "asc",
"type": "line-length",
},
],
"perfectionist/sort-exports": [
1,
{
"order": "asc",
"type": "line-length",
},
],
"perfectionist/sort-imports": [
1,
{
"order": "asc",
"type": "line-length",
"newlines-between": "always",
"groups": [
["builtin", "external"],
"internal",
["parent", "sibling", "index"],
"object",
"unknown",
"custom-screens",
"custom-components",
"custom-routes",
"custom-recoil",
"custom-axios",
"custom-server",
"custom-hooks",
"custom-utils",
"custom-type",
"custom-assets",
],
"custom-groups": {
"value": {
"custom-screens": "@screens/**",
"custom-components": "@components/**",
"custom-routes": "@routes/**",
"custom-recoil": "@recoil/**",
"custom-axios": "@axios/**",
"custom-server": "@server/**",
"custom-hooks": "@hooks/**",
"custom-utils": "@utils/**",
"custom-type": "@type/**",
"custom-assets": "@assets/**",
},
},
"internal-pattern": ["src/**"],
},
],
},
perfectionist 관련 전체 코드입니다.
해당 eslint를 적용해준다음, fix 명령어를 수행해주면 우리가 원하는 결과가 나오는 것을 확인해 줄 수 있습니다.
npm i --save-dev eslint-plugin-tailwindcss
또한 우리는 css 스타일링에 tailwind
를 사용하고 있는데, 각자 스타일 입력 방식이 제각각이라 서로의 코드를 확인하는데 어려움을 겪었습니다. 이를 해결하기 위해 eslint-plugin-tailwindcss
라이브러리를 설치해주었습니다.
{
"root": true,
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"plugin:tailwindcss/recommended", <- 여기 추가
"prettier",
],
}
그리고 .eslintrc
파일의 extends에 plugin:tailwindcss/recommended
를 추가해주기만 하면 됩니다.
그런다음 eslint를 적용해보면 스타일들이 일관된 순서대로 적용된 것을 확인할 수 있습니다.