[Art-on] 스프린트1 -프로젝트 생성하기

김재만·2022년 10월 30일
0
post-thumbnail

프로젝트 목적

부트캠프에서 뵀던 백엔드 개발자분께서 실제 이용자가 있는 서비스를 목표로 프로젝트를 만들어보자고 제안해주셨다. 핵심 기능은 유저별 맞춤 정보 푸쉬 알림, 지도를 통한 인접지역 공연정보 제공이다.

개인적으로는 프로젝트의 시작부터 끝까지, 만족할만한 프로젝트 관리와 코드 퀄리티를 유지하는 것이 목표다. 그래서 초기 목표는 기술적인 챌린지를 최소화하는 방향으로 정했다.

프로젝트 생성

내 코드는 아직 구현하는데 급급하고, 회사 내 사수도 없다. 때문에, 사내 모바일 팀 리더께서 공유해주신 리액트 프로젝트 구조를 참조하기로 했다. 코드를 정형화시키고 내 것이 되었다 느끼면, 내가 생각하는 방향으로 개선해 나갈 예정이다.

Bulletproof React

https://github.com/alan2207/bulletproof-react

해당 코드에서는 React + Typescript로 개발한다. 나 역시 타입스크립트는 필수로 도입할 예정이었지만, React와 Next사이에서 고민을 했다. 프로젝트 내에서 내가 안드로이드 파트와 차별되는 가장 큰 부분이

  1. 검색엔진을 통한 접근
  2. 페이지 라우팅의 편이
  3. 디바이스 적응력(덕분에 빠른 개발속도)
    이라고 생각한다.

React와 Next를 얘기할 때 비중이 큰 부분도, 검색엔진 최적화와 같은 부분을 포함하므로 Next로 개발하고 싶은 생각도 컸다. 하지만, React를 제대로 작성하지 못하고 있고, React를 활용한 검색엔진 최적화를 경험해보는 것이 우선이라고 생각했다. 또, 사이드 프로젝트인 만큼 챌린징하는 부분을 최소화해야 내 의도대로 프로젝트를 만들어갈 수 있다는 판단도 React로 시작하는 선택에 기여했다.

프로젝트 관리

프로젝트 관리에 사용하는 도구들은 아래와 같다.

프로젝트 관리 도구

  • 사용언어 : TypeScript(JavaScript)
  • 프로그래밍 라이브러리 : React(v18.2.0)
  • 런타임 라이브러리 : Node.js (v16.15.0)
  • 패키지 매니저 : Yarn (v1.22.19)
  • 프로젝트 템플릿 및 생성 : Creacte React App
  • 린트도구 : ESLint
  • 코드포멧터 : Prettier
  • Git Hook 관리도구 : Husky
  • 임포트 경로 : 절대경로

1. 타입스트립트로 프로젝트 시작하기

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"
  ]
}

2. 린트 설정하기

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 설치 여부
입력 : y

2. 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. 그 외 방식
입력 : 1

4. Which framework does your project use?
React
Vue.js
None of these

내용 : 프로젝트에 사용하는 프레임워크
1. React
2. Vue.js
3. 그 외 도구
입력 : 1

5. Does your project use TypeScript?
No
Yes

내용 : 타입스크립트 사용 여부
1. 사용 안 함
2. 사용함
입력 : No

6. 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누른 후 엔터)

내용 : 스타일 가이드
1. 상용화 된 스타일
2. 스타일 커스텀
입력 : 1

8. 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

내용 : 스타일가이드 선택
입력 : 1

9. What format do you want your config file to be in?
JavaScript
YAML
JSON

내용 : 구성 파일 저장방식
입력 : JavaScript

10. Would you like to install them now with npm?
NO
Yes

내용 : 즉시 설치여부
입력 : Yes

11. 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": ["/"]

3. Prettier 설정

  1. .prettierc.json
    프로젝트 최상위 폴더에 .prettier.json파일 생성
//.prettier.json

{
  "singleQuote": true,
  "trailingComma": "es5",
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false
}
  1. prettier 확장 설치

  2. Format document 실행
    cmd + P > "> Formmat Document" 검색 > 클릭하여 실행

  3. Preitter 자동실행 설정하기
    cmd + , > "default formatter" 검색 > Prettier - Code formatter로 변경 후 VSCode 재부팅

4. Husky 설정

허스키 설정은 처음 접하는 내용이다. git hook을 적용시키기 위한 npm 라이브러리라고 한다. 커밋, 리베이스, 머지, 푸쉬 등의 깃과 관련한 동작이 발생할 경우 실행되는 기능들을 설정할 수 있다.


출처 : 가비아 라이브러리 - HUSKY로 GIT HOOK 하자

사용법

  1. 설치
    yarn add husky --dev //개발 모드에만 적용
    (yarn add pinst --dev)

  2. 허스키 활성화
    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 원상복구

typicode.github.io/husky

.git/hooks 디렉토리 안에 훅명을 딴 파일을 생성하여, 실행할 코드를 작성하면 된다.

  1. 새로운 훅 추가하기
    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 주소값을 안 바꾼 탓이었다..ㅠ 새로운 걸 처음 쓸 때는 항상 어설프다

아무튼 해결!

5. 절대경로 설정

절대경로를 설정하면, 해당 모듈, 컴포넌트, 파일의 위치가 비교적 직관적이게 된다.

//tsconfig.json

"compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }

마무리

와따..!

profile
듣는 것을 좋아하는 개발자입니다

0개의 댓글