기존 프로젝트를 리액트 프로젝트로 만들기

Sharlotte ·2023년 1월 5일
0

Project D

목록 보기
1/3

새 웹사이트가 개발되었다

한 달간 휴식을 취하는 사이에 팀 프로젝트가 생각 이상으로 발전되었다. 시험을 마친 팀원의 퍼포먼스는 시험 이전과 비약적으로 큰 성능을 보여주었고 기획 / 디자인 지원을 받아 그것까지 수행하고 있었다.

그런데 문제가 하나 생겼다. 휴식을 취하는 사이 웹개발도 시작된건데, 이 친구가 웹개발이 처음이라 그런지 바닐라 html과 css 파일 하나로 모든걸 처리하려고 했던 것이다.

가독성을 고려하기 전에 이게 읽히기는 할지 의문이 들 지경의 원파일 css. html도 비슷한 상황이였고 인라인 스타일과 클래스, id가 난잡하게 섞인 혼종이 탄생했다.

해결책이 필요하다 - 리액트

왜 리액트인가? 뷰, 스벨트와 같은 다른 대안, 심하게 가자면 플러터웹이나 .Net, spring boot를 먼저 언급할 수도 있었으나 리액트는 본인 기술 스택이였고 이미 한번 이 프로젝트에서 Phaser라는 새 스택을 모험해봤기에 더이상의 모험을 하고 싶지 않았다.

리액트만으로 해결이 될까? - Emotion

리액트는 미완성된 자바스크립트 코드를 유연하고 고급지게 만들기 위해서, 그리고 난잡해진 html 코드를 정리하기 위해서 마련된 해결책이지 스타일링에 대한 해결책으론 부족하다.
그렇기에 스타일링을 위한 해결책이 필요하니, 그것이 바로 Emotion이다.
왜 Emotion인가? styled-component나 그냥 css 파일를 분리하는 다른 해결책이 있음에도 불구하고 Emotion를 선택한 이유는

  • 첫째로 본인 기술 스택이며, styled-component와 버금가는 성능이기 때문이다.
  • 둘째로 Css In Js 라이브러리의 장점, 즉
    • 높은 유지보수 및 모듈
    • 자바스크립트(타입스크립트) 환경을 통한 모든 부수적인 이득
    • 코드 경량화, 중복 위험성 제거
    • 컴포넌트 스타일링에 적절한 라이브러리

를 들 수 있다.

리액트로 포팅하기

리액트 공식 문서에선 리액트를 시작하는 방법의 다양한 길을 제공해준다. CRA, Next.js 등 각종 툴체인이나 기존 html 페이지에 <script>테그를 통한 CDN 불러오기가 있다. 하지만 이들은 기존 프로젝트를 완전히 리액트 프로젝트로 전환하는 솔루션이 되어주지 못한다.

리액트 프로젝트와 유사한 모습으로 직접 바꾸기

/Public

./public 디렉토리에선
탭바 아이콘을 위한 favion.ico,
루트 돔 컨테이너가 있는 index.html,
패비콘과 이름 등 다양한 메타데이터가 있는 manifest.json,
크롤링 및 검색앤진과 같은 웹 로봇을 위한 robots.txt,
가 있다.
지금 /public엔 이것들이 모두 없으므로 만들어줬다.

favion.ico는 png를 favion으로 전환하여 얻을 수 있다.

index.html는 아래와 같이 짯다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="user-scalable=no" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

root는 리액트의 진입점에서 루트 돔 컨테이너로써 역할할 것이다.

// index.html속 root id인 div로 루트 돔 컨테이너 생성 
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

manifest.json은 기본적으로 아래와 같이 되어있다.
MDN 문서를 참고하여 마음껏 커스터마이징하거나 속성들을 줄일 수 있다. favicon.io에서 파일을 받으면 favicon.io 외에 다른 이미지 파일들도 있는데, manifest.json의 icons에서 이들을 활용할 수 있다.

robots.txt는 웹 로봇에게 가능한 에이전트나 접근 불가 페이지를 알려줄 수 있다. 웹 로봇에겐 강제 사항이 아니므로 맹신해선 안된다.

User-agent: *
Disallow:

사전 설치 모듈

리액트를 제대로 굴리기 위해선 일부 모듈이 필요하다.

yarn add react react-dom react-scripts

이 프로젝트는 타입스크립트이므로 타입도 설치한다.

yarn add react react-dom react-scripts
yarn add @types/react @types/react-dom --dev

덧붙여 스타일링을 위한 이모션도 필요하다.

yarn add react react-dom react-scripts @emotion/styled @emotion/react
yarn add @types/react @types/react-dom --dev

Src

./src 에선 본격적인 리액트 웹앱 프로그래밍이 시작되는 루트 소스 디렉토리다.
진입점인 index.tsx에선 아래와 같이 코드를 짠다.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './global.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
  • document가 element를 못찾을 수도 있어 getElementById의 리턴 타입엔 유니온으로 null이 들어가 있다. 하지만 이건 불가능하며 만일 그러더라도 리액트가 걸러주니 as를 해야 한다
  • React.StrictMode는 리액트의 잠재적 문제를 콘솔 경고와 에러로 나타내주는 강력한 검사 도구다. 가끔 컴포넌트가 왜 두번 랜더링되는지 의문이 생길 때 이놈이 원인이곤 했다

global.css를 불러오려 했으므로 파일을 만들어준다. CIJ 라이브러리인 emotion를 가져다쓸거면서 굳이 css파일을 하는 이유는 emotion이 컴포넌트 스타일링엔 적절할지라도 전역 스타일링엔 딱히 필요하지 않기 때문이다. global.css에선 이러한 전역 스타일링에 대해 다룰 것이다.

a {
  text-decoration: none;
  color: inherit;
}

가령 이와 같이 흉물스러운 <a/> 태그의 퍼렇고 밑줄난 스타일을 지운다던지.

App.tsx를 불러오려 했으므로 응당 해당 컴포넌트도 있어줘야 한다.
App 컴포넌트는 진입점에 불러와지는 루트 컴포넌트이며 따로 페이지에 따라 다른 렌더링, 즉 페이지를 위해선 react-router-dom과 같은 라우팅 라이브러리가 필요하다.

const App: React.FC = () => <></>;
export default App;

일단 돌리게 하는게 급선무니 더미로 만들어둔다.

tsconfog

이대로 src에 파일들을 추가하면,
모듈 './App'이(가) '~~/App.tsx'(으)로 확인되었지만 '--jsx'가 설정되지 않았습니다.
'--jsx' 플래그를 제공하지 않으면 JSX를 사용할 수 없습니다.
와 같은 에러가 터진다. 이는 tsconfig.json에서 컴파일 옵션의 jsx를 설정해야 하기 때문이므로

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

와 같이 속성을 추가한다.

실행하기

앞서 설치한 react-scripts가 활약할 순간이다!...
그런데 사실 이 친구는 CRA 툴체인을 사용하면 자동적으로 설치되는 모듈이다. 만약 기존 번들러나 cli가 딱히 없다면 react-scripts start를 직접 터미널에 입력하거나 package.jsonscripts에 추가하는 방식으로 해결하면 된다. react-scripts 스크립트 목록에서 start 외의 스크립트 명령어를 볼 수 있다.

하지만 지금 프로젝트에선 vite라는 번들러가 사용되고 있으므로 이 친구를 이용하는 방식으로 이어나가본다.
react-scripts는 vite가 있어 필요가 없으니 제거한다.

yarn remove react-scripts

vite with react and typescript

놀랍게도 vite 공식 문서의 샘플 데모에서 react-ts가 있어 손쉽게 따라할 수 있다.

보아하니 vite.config.ts에서 리액트 플러그인을 추가하는게 핵심인듯 하다.

yarn add @vitejs/plugin-react

로 플러그인을 추가하고 콘피그 속성에 plugin를 추가한다.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
});

또한 CRA에선 index.html가 public/index.html에 있었는데, vite에선 /index.html로 루트로 내려갔으므로 이에 맞춰 파일을 이동한다.
팀원이 만든 ./index.html이 있어서 public.index.html에서 리액트와 매니페스트, 아이콘과 관련된 태그만 가져와 ./index.html에 추가했다.
+ vite에선 %PUBLIC_URL%가 필요없는 것 같다.

  <body>
    <div id="root"></div>
    <script type="module" src="/src/index.tsx"></script>
  </body>

루트 돔 컨테이너 아래에 script 태그로 진입점을 직접 불러와야 한다.


잘 돌아간다.

profile
샤르르르

0개의 댓글