스무디 한 잔 마시며 끝내는 리액트 + TDD - 5장 나의 첫 리액트 프로젝트

728x90

한동안 팀프로젝트로 꽤 바쁜 일정을 소화하느라 스무디 한잔을 할 여유도 없었다... 😫
방학을 맞이하여 최소 한 챕터라도 보자는 마음으로 일단 책을 폈다. 그런데 이번장 너무 재밌겠잖어 ~? 😆
무려 시작부터 관심사였던 타입스크립트를 소개한다.

타입스크립트

리액트는 js이며, js는 동적 프로그래밍 언어(Dynamic Programming Language)이다. 동적 프로그래밍 언어는 런타임 시 변수의 타입이 결정된다. 그렇기 때문에 변수의 타입 때문에 발생한느 버그와 에러는 js를 실행해 보지 않으면 알 수가 없다. 리액트에서는 이런 문제를 해결하고자 플로우(Flow)라는 정적 타입 분석기를 사용할 수 있다.

플로우는 페이스북에서 만들었고, 리액트, 리액트 네이티브에서 변수의 타입을 미리 지정하여 변수의 타입으로 발생하는 문제를 해결한다.
하지만 이번에는 타입스크립트(Type Script, 이하 ts)를 사용하려고 한다. ts에 대해 더 자세히 공부하고 싶다면 공식 사이트를 참고하라.

타입스크립트를 권장하는 이유

ts는 js 전반에 걸쳐 사용할 수 있기 때문이다. 따라서 플로우보다 좀 더 범용적으로 사용할 수 있다. 또한 많은 js 라이브러리에서 이미 ts 타입 정의 파일(DefinitelyTyped)을 제공하고 있다. 우리는 타입 정의 파일을 통해 라이브러리를 사용하기 위한 올바른 데이터 타입, 매개변수를 쉽게 확인할 수 있다.

텍스트 에디터에서의 지원이 좋다. 특히 MS가 만든 VSCode 에디터는 기본적으로 TS를 지원하고 있으며, 이는 개발 생산성에 크게 도움이 된다. 물론 아톰, WebStorm, Sublime Text 등 많은 에디터에서도 사용할 수 있다. ts의 공식 사이트를 확인하여 자신의 에디터에 맞는 방법으로 에디터를 설정하길 바란다.

이제 create-react-app으로 생성한 리액트 프로젝트에 ts를 적용하기 위해 리액트 프로젝트를 준비해 보자.
생성한 리액트 프로젝트에 ts를 적용하기 위해서는 ts 라이브러리와 리액트의 타입이 정의된 타입 정의 파일을 설치할 필요가 있다. 다음 명령어를 사용하여 ts타입 정의 파일을 설치한다.

# cd my-app
npm install --save-dev typescript @types/node @types/react @types/react-dom @types/jest 

여기서 설치한 라이브러리와 타입 정의 파일은 다음과 같다.

  • typescript: ts 라이브러리
  • @types/node: 노드의 타입이 정의된 타입 정의 파일
  • @types/react: 리액트의 타입이 정의된 타입 정의 파일
  • @types/react-dom: react-dom의 타입이 정의된 타입 정의 파일
  • @types/jest: Jest의 타입이 정의된 타입 정의 파일

혹시나 위의 작업을 수행하는데 아래와 같은 경고가 뜬다면....
image
해당 참고 자료를 참고해서

이제 프로젝트에 ts를 설정하기 위해 tsconfig.json 파일을 프로젝트 루트 폴더(./my-app/tsconfig.json)에 만들고 다음 내용을 추가한다.

{
  "compilerOptions": {
    "target": "est5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSuntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCaseInSwitch": true,
    "module": "esnext",
    "moduleResolution": "noed",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
     "src",
     "custom.d.ts"
  ]
}

위의 스크립트를 일일이 수작업으로 작성했는데, 인텔리제이(얼티밋 버전)를 이용한다면, 자동완성이 되니까 적극 활용하자!(VSCode도 지원하지 않을까..? 👀)
image

이렇게 타입스크립트 설정을 끝냈다면 js 파일들을 ts 파일로 변경해야 한다.

  • ./src/App.js 파일을 ./src/App.tsx로 변경
  • ./src/App.test.js 파일을 ./src/App.test.tsx로 변경
  • ./src/index.js 파일을 ./src/index.tsx로 변경
  • ./src/reportWebVitals.js 파일을 ./src/reportWebVitals.ts로 변경
  • ./src/setupTests.ts 파일을 ./src/setupTests.ts로 변경

여기서 .tsx 파일은 TypeScript JSX 파일을 의미하며 .ts 파일은 TypeScript JavaScript 파일을 의미한다. 이렇게 ts를 사용함을 알리기 위해서는 위와 같이 파일 확장자명을 변경해야 한다.

다음으로 ts를 사용하여 js 코드를 ts에 맞게 변경해야 한다. 우선 App.tsx 파일과 App.test.tsx 파일 상단에 다음과 같이 추가한다.

ts 혹은 tsx 파일로 모든 파일 확장자를 변경한 모습
image

App.tsx와 App.test.tsx에 import React from 'react';를 추가하고 reportWebVitals.ts 파일을 열어 아래와 같이 수정한다.
(책의 내용을 직관적으로 이해하기 어려웠는데... IntelliJ가 오류 표시를 해줘서 바로 이해했다. 또한 자동 import도 해주기 때문에 더더욱... 갓텔리제이 짱짱 👍)
image

마지막으로 ts 파일에서 svg 파일을 ts에서 불러올 수 있게 하도록 ./src/custom.d.ts 파일을 생성하고 다음과 같이 수정한다. 여기서 생성하는 d.ts 파일은 타입 정의 파일로 ts 인식하지 못하는 타입이나 ts 내에서 사용할 타입들을 정의할 때 사용한다.

declare module '*.svg' {
  import * as React from 'react';

  export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement> & { title?: string }>;

  const src: string;
  export default src;
}

이렇게 모든 설정이 끝났다면 App.test.tsx 파일을 열어 앞의 장에서 작성한 테스트 코드를 다음과 같이 복붙하여 npm run test로 테스트 코드를 실행해본다.

image

앗... 실패했다.
에러 로그를 읽어보니 error TS6046: Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'esnext'. 라고 한다. 이와 관련된 설정은 처음에 tsconfig.json에서 해줬던 것 같다는 기억에 찾아가보니...

image
'est5'라니...ㅎㅎ 오타 주의합시다... 이 외에도 noFallthroughCaseInSwitch라는 컴파일러 옵션이 없다고한다. (Did you mean 'noFallthroughCasesInSwitch'? 이라며 자신의 똑똑함을 과시한다... 눈이 침침해서 그런가... 's' 하나 빠진걸 한참을 찾지 못했다 😂)

image

스크립트를 올바르게 고쳐주니 정상적으로 동작한다!

이 테스트로 우리는 리액트 컴포넌트를 js에서 ts로 리팩토링했음에도 아무 문제 없이 우리가 표시하고자 하는 화면이 잘 표시되었음을 알 수 있다. 이렇게 테스트 코드는 우리가 안심하고 코드를 변경할 수 있게 도와준다.

npm start 명령어를 실행해보니 리액트 프로젝트가 정상적으로 실행되는 것도 확인햇다!

이로써 create-react-app을 사용하여 생성한 리액트 프로젝트에 ts를 설정하는 방법을 알아봤다. 하지만 너무 복잡하다.. 이런 복잡함을 개선하고자 create-react-apptemplate이라는 옵션을 제공하고 있다. 다음 명령어를 사용하여 새로운 리액트 프로젝트를 생성해 보자.

npx create-react-app my-app-typescript --template=typescript

위의 명령어로 생성된 프로젝트를 확인해보면 우리가 앞에서 template 옵션 없이 생성한 리액트 프로젝트에 ts를 적용한 것과 같다는 것을 알 수 있다. 이렇게 우리는 create-react-apptemplate 옵션으로 ts가 적용된 리액트 프로젝트를 간단하게 생성할 수 있다.

이전 예시와 똑같이 test 코드들을 App.test.tsx 파일에 복붙하여 실행해보자.
테스트 결과는 앞의 결과와는 달리 테스트가 실패한다.

콘솔창에 표시된 에러 내용을 자세히 살펴보면 <p /> 태그의 설명문에서 에러가 난 것을 확인할 수 있다. 따라서 App.tsx 파일을 열어 <p /> 태그의 설명문 부분을 확인해보면 설명문 내용이 src/App.js가 아닌 src/App.tsx로 표시된 것을 확인할 수 있다.
바뀐 텍스트에 맞게 테스트 코드 또한 수정한다. 그러면 테스트 코드가 정상적으로 동작한다!

또한 리액트 프로젝트 또한 정상적으로 실행된다.

styled-components

리액트는 웹 애플리케이션 개발에 사용하는 라이브러리이다. 그러므로 스타일링에는 물론 웹 개발과 동일한 CSS(Cascading Style Sheets)를 사용할 수 있다.

우선 리액트 스타일링을 사용하는 방법을 이해하기 위해 다음의 명령어를 사용하여 ts 새로운 리액트 프로젝트를 생성해보자.
npx create-react-app my-app-style --template=typescript

이번에는 인텔리제이로 새로운 ts기반 react 프로젝트를 생성해본다.
image

내용은 확인해보면 동일하다!

우리는 리액트 프로젝트의 폴더와 파일 구조를 확인했다. 리액트도 웹 서비스이므로 기본적으로 HTML 파일이 필요하고 public/index.html 파일이 이 역할을 한다고 설명했다.

그러므로 보통의 웹 페이지처럼 CSS 파일을 생성하고 <link /> 태그를 추가하여 웹 서비스의 스타일링을 할 수 있다. 그렇다면 ./public/temp.css 파일을 생성하고 다음과 같이 수정한다.

.App-header {
  background-color: red !important;
}

우리가 만든 CSS가 제대로 적용되는지 확인하기 위해 .App-header 클래스의 배경에 강제적(import)으로 빨간색을 표시하도록 했다. 그리고 ./public/index.html 파일을 열어 다음과 같이 수정한다.

<link rel="stylesheet" href="%PUBLIC_URL%/temp.css">
<title>React App</title>  <!--기존에 있던 내용-->

보통의 웹 서비스처럼 <link /> 태그를 사용하여 우리가 만든 temp.css 파일을 추가하였다. create-react-app으로 만든 프로젝트의 public 폴더는 위와 같이 %PUBLIC_URL%을 사용하여 지정한다. 이렇게 추가했다면 실제로 우리가 만든 CSS가 제대로 적용되는지 확인해보자.

그 결과는 엄청났다(리액트 납량특집...)
image

하지만 리액트는 보통의 웹 서비스 개발과는 다르게 컴포넌트 중심으로 개발한다. 그러므로 이렇게 모든 CSS를 한 곳에서 관리하게 되면 어떤 컴포넌트에서 어떤 스타일을 활용하는지 쉽게 알 수 없다. 그래서 리액트에서는 CSS 파일을 리액트 컴포넌트 파일에서 import하는 방식으로 스타일도 컴포넌트 중심으로 설계할 수 있도록 하고 있다.

우리가 create-react-app 명령어로 생ㅅ어한 프로젝트의 리액트 컴포넌트인 ./src/App.tsx 파일을 열어보면 다음과 같이 CSS 파일을 import하는 것을 확인할 수 있다.

...
import './App.css';
...

리액트는 위와 같이 JSX 파일에서 직접 CSS를 import하는 것으로 해당 리액트 컴포넌트가 어떤 스타일을 사용하는지 알 수 있도록 하고 있다. 해당 스타일 파일이 적용되고 있는지 확인하기 위해 ./src/App.css 파일을 열어 다음과 같이 배경 색상을 강제적으로 되돌리도록 수정한다. (동일하게 !important를 뒤에 붙인다)

다시 리액트 프로젝트를 실행해보면 빨간 배경이 원래대로 돌아왔다. 이렇게 리액트에서는 HTML에 <link /> 태그를 통해 CSS를 사용할 수도 있고, 컴포넌트에서 CSS를 import해서 사용할 수도 있다.

보통 리액트는 컴포넌트를 기반으로 개발하게 되며 컴포넌트별로 CSS를 갖는 형식으로 스타일을 관리하게 된다. 하지만 모든 CSS를 한 곳에서 관리하지 않다 보면 CSS의 클래스 명이 중복되어 잘못된 스타일이 적용될 수 있다. 만약 한 곳에서 모든 스타일을 관리하다 보면 어떤 스타일이 컴포넌트에 적용되고 있는지 한눈에 알 수 없다.

이런 문제를 해결하고자 styled-components 라이브러리가 탄생했으며, 리액트에서 이 styled-components를 사용하여 스타일을 적용할 수 있다. 리액트에서 styled-components를 사용하여 스타일링을 하게 되면 다음과 같은 장점이 있다.

  • 클래스 이름 버그 해결
    보통 CSS에 클래스 이름을 생성하고 스타일을 작성한 다음 해당 이름을 HTML 태그에 적용함으로써 스타일을 적용한다. 하지만 이런 방식은 클래스명의 중복, 겹침 또는 철자 오류가 발생할 수 있다. styled-components는 스타일을 컴포넌트에 직접 적용함으로써 이런 문제를 해결하고 있다.

  • 더 쉬운 CSS관리
    일반적인 방식으로 스타일을 적용하면 해당 스타일의 클래스가 코드의 어디에서 사용되는지 쉽게 알 수 없다. styled-components는 모든 스타일이 특정 컴포넌트에 연결되기 때문에 더 명확히 사용되는 스타일을 알 수 있다. 또한, styled-components는 모든 스타일이 특정 컴포넌트에 연결되어있기 때문에 사용되지 않은 불필요한 스타일을 쉽게 제거할 수 있다.

  • 간단한 동적 스타일 적용
    동적인 스타일을 관리하기 위해 여러 클래스를 만들 필요가 없으며 컴포넌트의 상태에 따라 쉽고 직관적으로 동적 스타일을 적용할 수 있다.

  • CSS 자동 구성
    styled-components는 페이지에 렌더링되는 컴포넌트를 추적하여 해당 스타일을 완전히 자동으로 추가한다. 또한, 코드 분할(Code splitting)과 결합하여 사용자가 필요한 최소한의 코드를 자동으로 추가한다.

실무에서도 이런 장점 때문에 styled-components를 많이 사용하며 이 책에서도 앞으로 styled-components를 사용하여 스타일링을 할 예정이다. 처음에는 styled-components를 사용하면 이런 장점이 잘 체감되지 않지만, 자주 사용하다 보면 앞에서 소개한 장점들이 점차 이해될 것이다.

styled-components를 사용하기 위해 다음 명령어를 실행하여 앞에서 만든 리액트 프로젝트에 styled-components를 설치한다.

cd my-app-style
npm install --save styled-components
npm install --save-dev @types/styled-components jest-styled-components

설치가 완료되었다면 styled-components를 사용하여 현재 페이지를 리팩토링해 보자. 일단 styled-components를 사용하기 위해 App.tsx 파일을 열어 다음과 같이 라이브러리를 import 한다.

...
import './App.css';
import Styled from 'styled-components';
...

그리고 styled-components를 사용하여 .App 클래스를 대체할 새로운 컴포넌트를 생성하기 위해 App.tsx 파일에 다음과 같은 코드를 추가한다.

import Styled from 'styled-component';

const Container = Styled.div`
`;
...

styled-components를 사용하여 리액트 컴포넌트를 생성하기 위해서는 Styled.[HTML 태그] 형식과 js의 템플릿 리터럴(Template literals, `)을 사용한다. 이 템플릿 리터럴 기호 안에 다음과 같이 스타일링을 작성함으로써 컴포넌트의 스타일링을 하게 된다.

...
const Container = Styled.div`
  text-align: center;
`;
...

추가한 스타일은 App.css 파일의 .App 클래스명 스타일 내용을 복사/붙여넣기 한 것이다.

.App {
  text-align: center;
}
...

이제 styled-components로 생성한 리액트 컴포넌트를 사용하기 위해 .App 클래스를 사용하는 부분을 찾아 다음과 같이 수정한다.

...
function App() {
  return (
    // <div className="App">
    <Container>
      ...
    </Container>
    // </div>
  );
}
...

기조에 있던 <div /> 태그 부분은 삭제해도 되지만, 비교하기 쉽게 하려고 주석 처리했다. 우리가 만든 styled-components를 사용하기 위해 <div /> 태그의 CSS 클래스 명을 제거하고 <div />태그 대신 우리가 만든 <Container /> 컴포넌트를 사용했다. 참고로 리액트에서는 HTML 태그에 class 대신 className을 사용하여 클래스를 지정한다.

이렇게 App.tsx 파일을 수정하고 저장한 후 브라우저를 확인하면 처음 화면과 똑같은 것을 확인할 수 있다. 즉, 우리가 styled-components를 사용하여 만든 리액트 컴포넌트가 이전의 CSS 방식의 스타일링을 잘 대체 했음을 알 수 있다.

이제 <header /> 태그를 styled-components로 변경해보자. <header /> 태그를 styled-components로 변경하기 위해 App.tsx 파일을 다음과 같이 수정한다.

코드는 아래와 같고 결과는 여전히 똑같다!

image

이제 애니메이션이 포함되어조금 복잡한 .App-logo 클래스를 styled-components로 변경해 보자. .App-logo 클래스를 styled-components로 변경하기 위해 App.tsx 파일을 열어 다음과 같이 수정한다.

...
const AppLogo = Styled.img`
    height: 40vmin;
    pointer-everts: none;
`;
...

image

<img /> 태그 대신 우리가 styled-components로 만든 AppLogo 컴포넌트를 사용했다.

이렇게 수정하고 저장한 후 웹 브라우저를 확인하면 스타일이 잘 적용된 것을 확인할 수 있다. 하지만 아직 회전 애니메이션을 추가하지 않았기 때문에 로고가 회전하지는 않는다.

이제 우리가 styled-components을 사용하여 만든 컴포넌트에 회전 애니메이션을 추가하기 위해 App.tsx 파일을 열어 다음과 같이 회전 애니메이션을 추가한다.

...
const AppLogo = Styled.img`
    hieght: 40vmin;
    pointer-events: none;

    @media (prefers-reduced-motion: no-preference) {
        animation: App-logo-spin infinite 20s linear;
    }

    @keyframes App-logo-spin {
        from {
            transform: rotate(0deg);
        }
        to {
            transform: rotate(360deg);
        }
    }
`;

이 역시 App.css 파일의 내용을 복사하여 붙여넣은 것이다. 다만, 차이가 있다면 CSS에서는 애니메이션을 사용할 클래스명을 다음과 같이 지정하였다.

...
@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}
...

하지만 styled-components에서는 해당 컴포넌트가 직접 애니메이션을 수행하므로 클래스명을 특별히 지정하지 않아도 된다.

이렇게 수정한 후 App.tsx 파일을 저장하고 브라우저를 확인해보면 이전과 같이 로고 이미지가 잘 회전하고 있는 것을 확인할 수 있다.

이처럼 애니메이션이 한 곳에서만 사용되는 경우, 하나의 컴포넌트에 전부 선언하여도 된다. 하지만 만약 여러 곳에서 같은 애니메이션을 사용한다면 다음과 같이 애니메이션을 분리하여 사용할 수 있다.

애니메이션을 분리하기 위해서 styled-components의 keyframes을 사용할 필요가 있다. styled-components의 keyframes을 사용하기 위해 App.tsx 파일을 열어 다음과 같이 수정한다.

...
import Styled, {keyframes} from 'styled-components';
...

그리고 다음과 같이 styled-components의 keyframes을 사용하여 우리가 로고 이미지에서 사용할 회전 애니메이션을 선언한다.

...
const spin = keyframes`
        from {
            transform: rotate(0deg);
        }
        to {
            transform: rotate(360deg);
        }
`;

마지막으로, 실제 애니메이션을 사용하느 부분에 다음과 같이 styled-components의 keyframes를 사용해 생성한 애니메이션을 추가한다.

...
const AppLogo = Styled.img`
    height: 40vmin;
    pointer-events: none;

    @media (prefers-reduced-motion: no-preference) {
        animation: ${spin} infinite 20s linear;
    }

    @keyframes App-logo-spin {
        from {
            transform: rotate(0deg);
        }
        to {
            transform: rotate(360deg);
        }
    }
`;
...

styled-components는 js의 탬플릿 리터럴을 사용하기 때문에 위와 같이 문자열 중간에 js 변수를 사용할 수 있다. 이제 이렇게 수정한 App.tsx 파일을 저장하고 브라우저를 확인하면 여전히 로고의 애니메이션이 잘 동작하는 것을 확인할 수 있다. 이처럼 자주 반복되어 사용되는 애니메이션은 styled-components의 keyframes를 사용하여 미리 정의하고 필요한 부분에서 정의된 애니메이션을 사용하면 된다.

설명문인 <p /> 태그에는 어떤 스타일도 적용되어 있지 않으므로 그대로 <p /> 태그를 유지하도록 한다. 마지막으로 .App-link 클래스를 styled-components로 변경해 보자.

.App-link 클래스를 styled-components로 만들기 위해 App.tsx 파일을 다음과 같이 수정한다.

...
const AppLink = Styled.a`
    color: #61dafb;
`;
...

이처럼 App.test.tsx 파일을 수정한 후, 테스트 코드를 실행하면 이전과는 다르게 다음과 같은 에러를 확인할 수 있다.

image

우리는 styled-components를 사용하여 모든 HTML 요소에서 클래스를 제거하였기 때문에 너무도 당연한 에러가 발생하였다.
글면 이 에러를 수정하기 위해 테스트 코드를 수정해 보자. App.test.tsx파일을 열어 다음과 같이 에러가 나는 부분을 수정해준다.

...
const AppLogo = screen.getByAltText('logo');
expect(appLogo).toBeInTheDocument();
expect(appLogo).toHaveAttribute('src', 'logo.svg');
...

수정한 내용을 살펴보면 react-testing-library의 screen을 활용하여 화면에서 logo라는 alt 속성을 가진 HTML 요소를 찾은 다음, 해당 요소가 화면에 표시되었는지를 Jest의 toBeInTheDocument를 사용하여 확인하였다. 또한, 해당 요소가 우리가 원하는 로고 이미지를 제대로 표시하는지 확인하기 위해 HTML의 src 속성을 확인하여 logo.svg 이미지가 제대로 표시되는지 확인하였다.

이렇게 수정하니 테스트가 정상적으로 통과한 것을 확인할 수 있다. 이를 통해 우리가 styled-components를 사용하여 만든 리액트 프로젝트도 문제없이 동작하는 것을 확인할 수 있다.

절대 경로로 컴포넌트 추가

우리는 리액트 프로젝트를 개발할 때 수많은 리액트 컴포넌트를 제작하고 제작한 컴포넌트를 조합하여 페이지를 제작하게 된다. 이처럼 리액트 컴포넌트를 조합할 때 보통은 상대경로를 사용하여 컴포넌트를 불러오게 된다.

몇 개 안되는 컴포넌트를 추가하고 관리할 때에는 큰 문제가 안되지만, 프로젝트가 커지고 수많은 컴포넌트가 추가되고 프로젝트의 폴더 구조가 복잡해지면 이 상대 경로 추가방식은 어떤 경로를 지정하고 있는지 명확하게 파악하기 어려운 단점이 있다.

이런 문제점을 ts의 설정으로 간단히 해결할 수 있다. 우선 테스트하기 위해 다음의 명령어로 새로운 프로젝트를 생성하자.

이제 ts 설정 파일인 tsconfig.json을 열어 다음과 같이 수정한다.

"compilerOptions": {
  ...
  "jsx": "react-jsx",
  "baseUrl": "src"
},
...

이제 ts 설정 파일인 tsconfig.json에 baseUrl을 설정하면 절대 경로로 컴포넌트를 추가할 수 있다. 물론, 상대 경로 추가는 기본적으로 지원하므로 상대 경로와 절대 경로를 동시에 사용할 수 있다.

이제 절대 경로로 컴포넌트를 추가해 보기 위해 src 폴더 하위에 Component라는 폴더를 생성하고 App 컴포넌트와 관련이 있는 파일인 App.css, App.test.tsx, App.tsx, logo.svg을 이동시킨다.

그리고 src/index.tsx 파일을 열어 다음과 같이 절대 경로로 App 컴포넌트를 추가해 본다.

...
import App from './Component/App';
...

현재는 하나의 컴포넌트만 있고 폴더 구조가 많이 복잡하지 않으므로 필요성이 크게 느껴지지 않지만, 앞으로 예제를 진행하면서 이 절대 경로 추가가 얼마나 중요한 역할을 하게 되는지 알게 될 것이다.

리액트 프로젝트를 실행해보면 정상적으로 작동한다.

이제 우리가 만든 컴포넌트를 테스트하기 위해 ./src/Component/App.test.tsx 파일을 열어 이전에 작성한 테스트코드를 테스트 코드를 다음과 같이 복붙한다. 큰 문제없이 테스트 코드 또한 정상적으로 통과한다.

Prettier

Prettier는 js, CSS, JSON 등을 지원하는 코드 포맷터(Code formatter)이다. Prettier는 미리 정의한 코드 스타일에 맞춰 자동으로 코드의 형식을 수정해주는 도구이다. (Prettier: https://prettier.io/)

그렇다면 절대 경로 컴포넌트 추가에서 만든 리액트 프로젝트에 Prettier를 적용하기 위해 다음의 명령어를 실행하여 Prettier를 설치한다.

npm install --save-dev husky lint-staged prettier

Prettier를 활용하기 위해 huskylint-staged도 함께 설치하였다. husky와 lint-staged의 역할은 다음과 같다.

  • husky
    package.json 파일에서 githook을 사용할 수 있게 해준다. githook은 깃에 특정 이벤트(소스 코드 커밋, 푸시 등)를 감지하여 이벤트를 실행하기 전에 특정 동작을 수행할 수 있도록 도와준다.

  • lint-staged
    깃의 stage된 파일들에 특정 동작을 수행할 수 있도록 해준다.

필요한 라이브러리 설치가 완료되었다면 Prettier의 설정 파일을 만들 필요가 있다. 프로젝트의 루트 폴더에 .prettierrc.js 파일을 생성하고 다음과 같이 수정한다.

image

Prettier에서 사용할 기본적인 규칙을 설정하였다. JSX 문법에서 괄호를 같은 라인에 표시하게 하고 싱글쿼터(')를 주로 사용하도록 설정하는 등의 내용이다.

이렇게 설정이 완료되었다면 lint-staged와 husky를 사용하여 깃 이벤트에 Prettier 명령어를 연동해 보자. lint-staged와 husky를 설정하기 위해서 package.json 파일을 열어 script 하단을 다음과 같이 수정한다.

"scripts": {
  ...
},
"husky": {
  "hooks": {
    "pre-commit": "lint-staged"
  }
},
"lint-staged": {
  "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
    "prettier --write"
  ]
},

조금 자세히 살펴보면 husky를 사용하여 깃의 커밋 이벤트 전에(pre-commit) lint-staged 명령어를 실행하도록 설정한 것을 알 수 있다.

그리고 lint-staged에서는 src 폴더 하위 파일 중 JSX, CSS, json 등과 관련 있는 파일들이 있는 경우 Prettier의 명령어를 실행하도록 하였다. 여기서 사용한 write 옵션은 Prettier를 사용하여 파일을 직접 수정하도록 하는 옵이다.

이제 Prettier는 소스 코드를 깃에 커밋할 때 우리가 정의한 규칙을 기반으로 소스 코드의 형식을 자동으로 수정해 줄 것이다.

실무에서는 husky와 lint-staged는 보조적인 도구로 사용, 개발에 사용하는 IDE에 플러그인 등을 추가하여 파일을 수정하고 저장할 때마다 코드의 형식을 자동으로 맞추도록 설정한다!

728x90