© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
💾
클린 코드를 위한 기동팀의 규칙 eslint 설정 개인 취향을 적어보자. 지성 import/order : import 순서 조정 (자동으로 줄바꿈 진행)
@typescript-eslint/no-unnecessary-type-assertion
: 불필요한 타입 단언을 금지합니다.
유진 기본적인 것 말고도 참고하면 좋은 라이브러리들 목록 Simple-import-sort
:이 라이브러리는 자동으로 임포트 순서를 정렬해줍니다function-component-definition
: 함수 형식을 강제로 고정할 때 사용(화살표 함수 등)typescript-eslint/naming-convention
: 변수 함수 인터페이스 작성 등 네이밍 컨벤션 강제화react/jsx-curly-brace-presence
: props로 넘겨줄때 중괄호로 넘길건지 문자열로 넘길건지 결정 react/no-unknown-property
: 올바르지 않은 프로퍼티가 넘어왔을 때 에러처리를 해줄 수 있습니다.import/newline-after-import
: import 아래에 엔터칠건지 안칠건지..unicorn/filename-case
: 파일명을 통일시켜줍니다. (대문자로 할건지 아닌지 등)
지성팀 eslint~ ^^ (최종본) prettier 설정 지성 “semi” : 무조건false…. “Single Quote” : true, (’) Tab width : 2 or 4 아무거나 상관없습니다! Trailling Comma : none(에러가 자주 나더라구요…) printWidth : 80 희석 "semi": true, "tabWidth": 4, "arrowParens": "always", "singleQuote": true, 유진 Semi : false Tab width 2 Single quote true Pritnwidth 80
클린 코드를 위한 래퍼런스 변수명, 함수명, 일관적인 코드 작성을 위해 서로 고려했으면 하는 점 지성 저도 희석님과 같은 레퍼런스를 참고 했습니다 컴포넌트 이름 : 대문자 + 파일과 컴포넌트 이름 겹쳤으면 좋겠습니다 ex) Header > Header.tsx( index.tsx ❌) 자주 쓰이는 접두사 함수 되도록이면 순수함수로… (사이드 이펙트는 최대한 없었으면 좋겠습니다) 저도 arrow function이었으면 좋겠습니다! 클린코드 문서를 좀 보다가.. 함수 파라미터로 boolean type은 지양해야한다는데 조금 더 실펴보겠습니다 함수 화살표 함수 사용 함수는 한 가지의 일만 수행하기 유진 함수 화살표 함수가 좋아요~~ 함수는 되도록 리턴값이 존재하는 형식으로 작성하기!! (리턴값이 없으면 state 느낌으로 관리하도록 노력해주기) api작성 객체로 정리하여 api들이 한 곳에 모여 있을수 있도록 하기 (빨리 찾을 수 있음) Mutation 중복되는거는 커스텀 훅으로 만들수있으면 빼기 ⇒ ‘authmutation’ 컴포넌트 분리 page.tsx
PostList.page ⇒ 300줄 넘어가는 경우가 ..
포스트 제목 포스트 사진 포스트 글 포스트 정보 포스트 댓글 댓글 작성하기 변수명 관련
todo 기동팀 클린 코드 문서 작성 변수명 동일한 유형의 변수는 동일한 단어로 사용하기
배열 이름은 되도록 복수로 설정하기 Booleans 접두사(최대한 여기 있는거 참고하기) 코드 스타일 etc 함수 작성방법 클린코드 문서를 좀 보다가.. 함수 파라미터로 boolean type은 지양해야한다는데 조금 더 실펴보겠습니다 변수 작성방
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:import/recommended',
'prettier',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh', 'import'],
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: true,
},
},
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'import/order': [
'error',
{
'newlines-between': 'always',
groups: [
'builtin',
'external',
'internal',
['parent', 'sibling'],
'index',
],
pathGroups: [
{
pattern: 'react*',
group: 'builtin',
position: 'before',
},
{
pattern: '@/stores/*',
group: 'internal',
position: 'after',
},
{
pattern: '@/contexts/*',
group: 'internal',
position: 'after',
},
{
pattern: '@/hooks/*',
group: 'internal',
position: 'after',
},
{
pattern: '@/components/*',
group: 'internal',
position: 'after',
},
{
pattern: '@/assets/*',
group: 'internal',
position: 'after',
},
{
pattern: '@/public/*',
group: 'internal',
position: 'after',
},
],
pathGroupsExcludedImportTypes: [],
alphabetize: {
order: 'asc',
},
},
],
'import/no-unresolved': ['error', { ignore: ['.svg'] }],
},
};
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:prettier/recommended',
'plugin:storybook/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
};
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'plugin:react/recommended',
'prettier',
],
plugins: ['react', 'prettier', '@typescript-eslint', 'simple-import-sort'],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
'linebreak-style': ['error', require('os').EOL === '\r\n' ? 'windows' : 'unix'],
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'react/no-unknown-property': ['error', { ignore: ['css'] }],
'react/prop-types': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/strict-boolean-expressions': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'react/react-in-jsx-scope': 'off',
'react/jsx-curly-brace-presence': ['error', { props: 'always', children: 'always' }],
'react/function-component-definition': [
'error',
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
},
],
'@typescript-eslint/naming-convention': [
'error',
{
format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
selector: 'variable',
leadingUnderscore: 'allow',
},
{
format: ['camelCase', 'PascalCase'],
selector: 'function',
},
{
format: ['PascalCase'],
selector: 'interface',
},
{
format: ['PascalCase'],
selector: 'typeAlias',
},
],
},
parser: '@typescript-eslint/parser',
}
- `function-component-definition` : 화살표 함수
- `typescript-eslint/naming-convention` : 변수 함수 인터페이스 작성 등 네이밍 컨벤션 강제화
- `react/jsx-curly-brace-presence` : props로 넘겨줄때 중괄호(확정❗️)
- `react/no-unknown-property`: 올바르지 않은 프로퍼티가 넘어왔을 때 에러처리를 해줄 수 있습니다.
- `import/newline-after-import`: import 아래에 엔터칠건지 안칠건지..
- `import/order` : import 순서 조정 (자동으로 줄바꿈 진행)
- `unicorn/filename-case` : 파일명을 통일시켜줍니다. (대문자로 할건지 아닌지 등)
- `@typescript-eslint/no-unnecessary-type-assertion`**: 불필요한 타입 단언을 경고❗️합니다
//기본적인 세팅
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'react/no-unknown-property': ['error', { ignore: ['css'] }],
'react/prop-types': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/strict-boolean-expressions': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
----
'@typescript-eslint/naming-convention': [
'error',
{
format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
selector: 'variable',
leadingUnderscore: 'allow',
},
{
format: ['camelCase', 'PascalCase'],
selector: 'function',
},
{
format: ['PascalCase'],
selector: 'interface',
},
{
format: ['PascalCase'],
selector: 'typeAlias',
},
'react/jsx-curly-brace-presence': ['error', { props: 'always', children: 'always' }],
{
"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"
}
],
"pathGroupsExcludedImportTypes": ["@tanstack*"],
"alphabetize": {
"order": "asc"
}
}
]
},
"unicorn/filename-case": [
"error",
{
"cases": {
"camelCase": true,
"pascalCase": true
}
}
],
- `@typescript-eslint/no-unnecessary-type-assertion`**: 불필요한 타입 단언을 금지합니다.
semi : false
"trailling Comma" : none(에러가 자주 나더라구요…)
"arrowParens": "always",
"singleQuote": true,
"tab width" : 2,
"printwidth" : 80
{
// prettier전체 옵션이라고하는데 혹시 빼먹은거 있으면 말씀해주세요!
"arrowParens": "avoid", // 화살표 함수 괄호 사용 방식
"bracketSpacing": false, // 객체 리터럴에서 괄호에 공백 삽입 여부
"endOfLine": "auto", // EoF 방식, OS별로 처리 방식이 다름
"htmlWhitespaceSensitivity": "css", // HTML 공백 감도 설정
"jsxBracketSameLine": false, // JSX의 마지막 `>`를 다음 줄로 내릴지 여부
"jsxSingleQuote": false, // JSX에 singe 쿼테이션 사용 여부
"printWidth": 80, // 줄 바꿈 할 폭 길이
"proseWrap": "preserve", // markdown 텍스트의 줄바꿈 방식 (v1.8.2)
"quoteProps": "as-needed" // 객체 속성에 쿼테이션 적용 방식
"semi": true, // 세미콜론 사용 여부
"singleQuote": true, // single 쿼테이션 사용 여부
"tabWidth": 2, // 탭 너비
"trailingComma": "all", // 여러 줄을 사용할 때, 후행 콤마 사용 방식
"useTabs": false, // 탭 사용 여부
"vueIndentScriptAndStyle": true, // Vue 파일의 script와 style 태그의 들여쓰기 여부 (v1.19.0)
"parser": '', // 사용할 parser를 지정, 자동으로 지정됨
"filepath": '', // parser를 유추할 수 있는 파일을 지정
"rangeStart": 0, // 포맷팅을 부분 적용할 파일의 시작 라인 지정
"rangeEnd": Infinity, // 포맷팅 부분 적용할 파일의 끝 라인 지정,
"requirePragma": false, // 파일 상단에 미리 정의된 주석을 작성하고 Pragma로 포맷팅 사용 여부 지정 (v1.8.0)
"insertPragma": false, // 미리 정의된 @format marker의 사용 여부 (v1.8.0)
"overrides": [
{
"files": "*.json",
"options": {
"printWidth": 200
}
}
], // 특정 파일별로 옵션을 다르게 지정함, ESLint 방식 사용
}
//bad
function getUserInfo(): User;
function getUserDetails(): User;
function getUserData(): User;
//good
function getUser(): User;
// 여러 개 가져올 때 vs 다 가져올 때
const = getusers() vs const getAllUsers() => {...}
// bad
const fruit = ['apple', 'banana', 'cucumber'];
// okay
const fruitArr = ['apple', 'banana', 'cucumber'];
// good
const fruits = ['apple', 'banana', 'cucumber'];
// great
const fruitNames = ['apple', 'banana', 'cucumber'];
const fruits = [
{ name: 'apple', genus: 'malus' },
{ name: 'banana', genus: 'musa' },
{ name: 'cucumber', genus: 'cucumis' },
];
// bad
const open = true;
const write = true;
const fruit = true;
const equal = true;
const visible = true;
// good
const isOpen = true;
const canWrite = true;
const hasFruit = true;
const areEqual = true;
const isVisible = trueBODY: $PR_BODY"
ISSUE_NUMBER=$(echo $PR_BODY | grep -oE "close #[0-9]+" | tr -d 'close #')
echo "ISSUE_NUMBER: $ISSUE_NUMBER"
if [[ ! -z "$ISSUE_NUMBER" ]]; then
curl -s -H "Authorization: token ${{ secrets.ACTION_TOKEN }}" -X PATCH "https://api.github.com/repos/${{ github.repository }}/issues/$ISSUE_NUMBER" -d '{"state": "closed"}'
fi
shell: bash
name: Close associated issue
on:
pull_request:
branches:
- dev
types:
- closed
jobs:
close-issue:
runs-on: ubuntu-latest
steps:
- name: Close associated issue
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
PR_URL="https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER"
echo "PR_URL: $PR_URL"
PR_BODY=$(curl -s -H "Authorization: token ${{ secrets.ACTION_TOKEN }}" $PR_URL | jq -r '.body')
echo "PR_BODY: $PR_BODY"
ISSUE_NUMBER=$(echo $PR_BODY | grep -oE "close #[0-9]+" | tr -d 'close #')
echo "ISSUE_NUMBER: $ISSUE_NUMBER"
if [[ ! -z "$ISSUE_NUMBER" ]]; then
curl -s -H "Authorization: token ${{ secrets.ACTION_TOKEN }}" -X PATCH "https://api.github.com/repos/${{ github.repository }}/issues/$ISSUE_NUMBER" -d '{"state": "closed"}'
fi
shell: bash