HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🐣
프론트엔드 데브코스 4기 교육생
/
🎽
성기동팀
/
💾
자료실
/
💾
클린 코드를 위한 기동팀의 규칙
💾

클린 코드를 위한 기동팀의 규칙

Tags
규칙
Created
Oct 5, 2023 04:36 AM

eslint 설정

개인 취향을 적어보자.

  • 지성
    • import/order : import 순서 조정 (자동으로 줄바꿈 진행)
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 }, ], }, };
  • @typescript-eslint/naming-convention: 변수, 함수, 클래스 등의 명명 규칙을 정의합니다.
    • @typescript-eslint/naming-convention: ["error", { "format": ["camelCase"] }],
  • @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 : 파일명을 통일시켜줍니다. (대문자로 할건지 아닌지 등)
      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`**: 불필요한 타입 단언을 경고❗️합니다

지성팀 eslint~ ^^ (최종본)

//기본적인 세팅 '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`**: 불필요한 타입 단언을 금지합니다.

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
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 방식 사용 }
 

클린 코드를 위한 래퍼런스

변수명, 함수명, 일관적인 코드 작성을 위해 서로 고려했으면 하는 점

  • 지성
    • 저도 희석님과 같은 레퍼런스를 참고 했습니다
    • 컴포넌트 이름 : 대문자 + 파일과 컴포넌트 이름 겹쳤으면 좋겠습니다
      • ex) Header > Header.tsx( index.tsx ❌)
    • 자주 쓰이는 접두사
      • https://jobkae-function-naming.netlify.app/
    • 함수
      • 되도록이면 순수함수로… (사이드 이펙트는 최대한 없었으면 좋겠습니다)
      • 저도 arrow function이었으면 좋겠습니다!
      • 클린코드 문서를 좀 보다가.. 함수 파라미터로 boolean type은 지양해야한다는데 조금 더 실펴보겠습니다
  • 희석
    • 레퍼런스 : https://738.github.io/clean-code-typescript/
  • 파일명 : camelCase
  • 컴포넌트 파일명 : PascalCase
  • 변수명 : 카멜 케이스
    • ex) getUser
  • 상수명 : 대문자 + 스네이크 케이스
    • ex) SNAKE_CASE
  • 함수
    • 화살표 함수 사용
    • 함수는 한 가지의 일만 수행하기
  • 될 수 있으면 부정 조건문 사용하지 않기
    • ex) if(!data)
  • 유진
    • 함수
      • 화살표 함수가 좋아요~~
      • 함수는 되도록 리턴값이 존재하는 형식으로 작성하기!! (리턴값이 없으면 state 느낌으로 관리하도록 노력해주기)
    • api작성
      • 객체로 정리하여 api들이 한 곳에 모여 있을수 있도록 하기 (빨리 찾을 수 있음)
      • Mutation 중복되는거는 커스텀 훅으로 만들수있으면 빼기 ⇒ ‘authmutation’
    • 컴포넌트 분리 page.tsx
      • PostList.page ⇒ 300줄 넘어가는 경우가 ..
      • 포스트 제목
      • 포스트 사진
      • 포스트 글
      • 포스트 정보
      • 포스트 댓글
      • 댓글 작성하기
    • 변수명 관련
      • 희석님께서 너무 좋은 예시를 가져와주셔서
      • Javascript 네이밍(naming)과 클린 코드(clean code)
        네이밍과 클린코드의 규칙에 대해 정리했습니다.
        Javascript 네이밍(naming)과 클린 코드(clean code)
        https://www.datoybi.com/naming-clean-code-javascript/
        Javascript 네이밍(naming)과 클린 코드(clean code)
 

todo

husky 설치

기동팀 클린 코드 문서 작성

변수명

동일한 유형의 변수는 동일한 단어로 사용하기
//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' }, ];

Booleans

// 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

접두사(최대한 여기 있는거 참고하기)

잡캐헨리 함수명 접두사 살펴보기
유튜버 잡캐헨리와 함께하는 함수명 짓기
잡캐헨리 함수명 접두사 살펴보기
https://jobkae-function-naming.netlify.app/

코드 스타일

etc

  • 될 수 있으면 부정 조건문 사용하지 않기
    • ex) if(!data)

함수 작성방법

  • 클린코드 문서를 좀 보다가.. 함수 파라미터로 boolean type은 지양해야한다는데 조금 더 실펴보겠습니다
  • 화살표 함수 사용하기
  • 함수의 매개변수는 2개 이하로 작성하기
  • 하나의 함수는 한가지만 동작만 수행하기

변수 작성방

  • 파일명 : camelCase
  • 컴포넌트 파일명 : PascalCase
  • 변수명 : 카멜 케이스
    • ex) getUser
  • 상수명 : 대문자 + 스네이크 케이스
    • ex) SNAKE_CASE