[Web] React와 Typescript 시작하기

2023. 1. 6. 17:22Developers 공간 [Basic]/Frontend

728x90
반응형

Web 개발을 위해서 다양한 방법(vue, react, svelte, pure html)이 존재하지만, 필수적으로 html 파일에 들어가는 Javascript 파일들을 하나의 자바스크립트 파일로 만들어주는 모듈 번들링은 꼭 필요합니다. 

** 모듈 번들링(Module Bundling) : web 개발에 필요한 html/css/js 등을 하나의 혹은 여러가의 파일로 병합&압축 하는 것으로, Webpack, Rollup, 그리고 Pacel등이 있습니다.

** Parcel(파셀) : Build tool로, Webpack의 설정은 까다롭고 시간적으로 비용이 많이 들기도 하는데 Parcel은 설정이 필요없는 zero-configuration 이기 때문에, 빌드를 위해 번들러를 학습하는 시간을 많이 줄일 수 있는 매력적인 번들러입니다.

 

또한 node.js를 활용해 Web 개발을 하는 경우, 위를 위해 webpack을 설치하는데 더해 Linter, Code Formatter, 혹은 다양한 extension 등을 직접 셋팅해주어야합니다.

** 린터(Linter) : 린트(lint) 또는 린터(linter)는 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류, 의심스러운 구조체에 표시(flag)를 달아놓기 위한 도구입니다. 예로 eslint가 있습니다.

** Code Formatter : 정한 코딩 컨벤션(코드 작성 스타일 규칙)에 따라 코드 스타일을 알아서 정리해주는 것입니다. 이에는 eslint 혹은 prettier가 있습니다.

 

이런 웹 개발을 위한 편의성 혹은 컴포넌트 단위의 설계가 가능하기 때문에 재사용이 가능한 프로젝트라는 장점으로 React 프레임워크가 주목을 받기 시작했습니다.

또한 React 프로젝트를 javacsript를 활용해 구축할 수도 있으나, typescript를 활용하는 경우가 많기 때문에 이번 글에서는 React와 typescript의 기본적인 특징을 설명하고자 합니다.

 

이 글에 앞서, node.js의 package manager인 npm과 yarn에 대한 글인 아래 글을 참조하면 편하실 수 있습니다.

https://tkayyoo.tistory.com/48

 

<구성>
1. React 설치 및 CRA 만들기 
   a. React 설치
   b. CRA(Create-React-App) 구조
   c. 기타 활용 명령어
2. React 의 특징
   a. 기본적인 특징
   b. hook 함수
   c. 기타 특징
3. Typescript의 특징
   a. 기본적인 특징
   b. React 외 예시

글효과 분류1 : 코드

글효과 분류2 : 폴더/파일

글효과 분류3 : 용어설명


1. React 설치 및 CRA 만들기

프로젝트를 시작하기 위해 React를 설치하고, CRA(Create-React-App) 프로젝트를 만들어야 합니다. 

기본적으로 CRA를 구성하는 과정을 살펴보고, 어떻게 작업을 시작할지 살펴봅니다.


a. React 설치
# STEP1
sudo apt-get install node # Ubuntu
brew install node # Mac
node -v

sudo apt-get install npm # Ubuntu
npm -v

sudo npm install -g npx # Ubuntu
npm install -g npx # Mac
npx -v

# STEP2
npm install –g yarn

# STEP3
yarn create react–app 폴더명 --template typescript

# STEP4
cd 폴더명
yarn start
  • STEP1. node.js가 있어야 합니다.
    • 위 코드와 같이 node, npm을 설치해야 합니다. 
    • STEP1에서 brew를 활용해 설치한 경우 npm도 함께 설치됩니다.
    • npm을 활용해 프로젝트를 구성하는 경우 npx도 설치해주어야 합니다.
  • STEP2. npm을 이용해 yarn을 설치합니다.
    • yarn 이란 npm과 같이 패키지를 설치하고 관리해주는 패키지 매니저입니다.
    • yarn을 사용하는 이유
      1. npm보다 더 dependency관리나 충돌을 관리하기 쉽습니다.
      2. 병렬 설치로 npm보다 설치 속도가 빠릅니다.
        npm은 여러 패키지를 설치할 때 순차적으로 진행하지만 yarn은 동시에 여러 패키지를 설치한다.
      3. 보안에 좀 더 강합니다.
        npm은 다른 패키지를 즉시 포함시킬 수 있는 코드를 자동으로 실행해 보안에 취약성이 있는 반면 yarn은 버전을 어디에서나 같게 만들어 버그를 줄이고 보안을 높였습니다.
  • STEP3. yarn create react–app 폴더명 --template typescript
    • CRA이 없이 프로젝트를 구한다면 하면, react-dom, babel, bundler(webpack) 등을 일일이 설치해야한다 
      ** 바벨(Babel) : 주로 ECMAScript 2015+ 코드를 이전 JavaScript 엔진에서 실행할 수있는 이전 버전과 호환되는 JavaScript 버전으로 변환하는 데 사용되는 무료 오픈 소스 JavaScript 트랜스 컴파일러 입니다.
      ** 트랜스필러(Transpiler) : 왜 인터프리터 언어에 컴파일러가 필요하지라는 생각이 들었을 것입니다. 정확히는 babel 은 javascript 로 결과물을 만들어주는 소스 대 소스 컴파일러로, transpiler라고 부릅니다. typescript 든 coffeescript 든 javascript 로의 compile 이 필수입니다.
      ** 폴리필(polyfill) : 개발자가 특정 기능이 지원되지 않는 브라우저를 위해 사용할 수 있는 코드 조각이나 플러그인입니다. 프로그램이 처음에 시작될 때 브라우저에서 지원하지 않는 기능들에 대한 호환성 작업을 채워 넣는다고 해서 polyfill이라 부릅니다.
    • React 는 기본적으로 javascript입니다. 하지만 이번 글에서는 typescript를 활용한 프로젝트를 구성할 것입니다. typescript를 활용한 이유는 이후에 typescript에 대한 설명에서 추가 설명하도록 하겠습니다.
더보기

-----------------------------------------------------------------------------------------------------

<CRA 없이 프로젝트 구성하는 방법>

# 프로젝트 초기화 (package.json, src/index.js, public/index.html)

npm init -y

# babel 설치
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/preset-react

# .babelrc 생성

{  "presets": ["@babel/env", "@babel/preset-react"]   }

# webpack 설치
npm install --save-dev webpack webpack-cli webpack-dev-server

# 다양한 로더 및 플러그인 패키지 설치

npm install --save-dev html-webpack-plugin css-loader style-loader file-loader babel-loader

# webpack.config.js 라는 파일 생성

...

# react-dom 패키지 설치

npm install react react-dom

-----------------------------------------------------------------------------------------------------

  • STEP4. 실행
    • yarn start : 프로젝트 실행

 


 

위와 다르게 npx를 활용해 프로젝트를 구성하는 경우에 대해 간단히 설명하고 넘어가겠습니다.

# STEP1
npm install -g npx

# STEP2
npm install -g create-react-app

# STEP3
npx create-react-app {app 이름}

# STEP4
npm run start
  • STEP1. 위에서 언급한 것과 같이 npm으로 프로젝트를 구성하는 경우 npx가 필요합니다.
  • STEP2. npx를 활용해 실행할 때, creact-react-app이라는 패키지가 필요합니다.
    • npx 는 npm의 5.2.0 버젼부터 새로 추가된 도구입니다.
  • STEP3. 프로젝트를 만들 때  create-react-app을 활용해 만듭니다.
  • STEP4. 프로젝트를 실행합니다.

 


b. CRA(Create-React-App) 구조

아래는 CRA의 프로젝트 폴더 및 파일 구조를 나타냅니다. 주황색으로 표시된 부분은 아래 세가지 명령어를 활용해 프로젝트를 구성한 경우 각각 다른 부분들을 나타냅니다. 아래 보이는 것과 같이 typescript로 된 프로젝트는 tsx 확장자를 가지며, javascript로 된 프로젝트는 js 확장자를 가집니다.

설명은 typescript와 함께 yarn으로 만들어진 프로젝트로 진행하겠습니다.

# create CRA with yarn w/ javascript
yarn create react-app jstest

# create CRA with yarn w/ typescript
yarn create react-app jstest --template typescript

# create CRA with npx w/ javascript
npx create-react-app npmtest

[각각의 방법을 활용한 CRA의 구조]

 

  • package.json : 기본적으로 프로젝트에 대한 정보를 정리해놓은 파일입니다. 어떤 패키지들이 프로젝트에 포함되어있는지를 명시하고 script 명령어들을 명시해놓습니다.
  • tsconfig.json : typescript를 컴파일 할때 옵션들을 명시해놓는 곳입니다.
  • yarn.lock : : 패키지가 최초로 추가되는 시점에 어떤 버전이 설치되었는지를 기록하는 곳입니다. 직접 만질 필요는 없습니다.
  • node_modules : node.js로 설치된 dependency나 패키지들이 설치된 곳이며, 직접 만질 필요는 없습니다.
  • public : React 프로젝트의 static 파일들(HTML,CSS,JS,이미지)이 저장된 폴더 입니다. CRA를 배포했을 때 실제 서버에 배포되는 폴더이지만, 실제로 index.html을 직접 실행시 빈 화면이 나올 것입니다. 이유는 뒤에서 설명드리겠습니다.
    • index.html : 브라우저에 나타내는 static HTML 파일입니다.
    • manifest.json : PWA에 필수로 포함돼야하는 파일입니다.
      ** PWA(Progressive Web Apps) : HTML, CSS, JS와 같은 웹 기술로 만들었지만, 네이티브 앱의 특징이 가능합 앱입니다.
  • src : 앱이 컴파일 될 때 사용하는 모든 것입니다. 기본적으로 public폴더에 있는 index.html에 표현될 기능을 구현하는 공간입니다.
    • index.tsx : 아래의 모든 src폴더의 컴포넌트들을 public/index.html과 연결해주는 파일입니다.
      • 수정할 일이 없습니다. 
      • 자세한 내용은 아래 코드 예시에서 설명드리겠습니다.
    • index.css : index.tsx에 대한 CSS 파일. scss를 사용한다면 확장자를 scss로 변경해 사용해줍니다.
    • App.tsx : react component를 실제로 구현하는 위치입니다. 
      • TypeScript 를 사용 할때는 .ts 확장자를 사용하지만 리액트 컴포넌트의 경우에는 .tsx
    • App.css : App.tsx에 대한 파일.
    • logo.svg : 기본 예제 이미지로 삭제
    • reactappenv.d.ts : TypeScript의 타입 선언을 참조하는 파일 입니다. 이미지를 import 하기 위해 사용되기도 한다.
    • reportWebVitals.ts : React 프로젝트 성능을 측정하기 위한 파일로, index.tsxreportWebVitals함수와 연관됩니다. 
    • App.test.tsx : DOM을 테스트하기 위한 도구입니다.
    • setupTests.ts : 테스트를 실행하기 위한 설정 파일입니다.
더보기

-----------------------------------------------------------------------------------------------------

<public 폴더와 src 폴더의 구분>

이미지와 같은 정적이미지는 public에 위치시킬 수 있으며, component와 함께 src에 위치시킬 수도 있습니다. 경우에 따라 선택하는 방법을 간단히 설명하겠습니다

  • src 폴더 : 컴포넌트 안에서 사용하는 이미지는 주로 src 폴더에 있어야 합니다.
    • 파일이 없다면 사용자에게 404 오류 대신 컴파일 오류가 발생합니다.
    • (예시) src/COMPONENT/file.tsx---> src/IMAGE/image.png
      import logo_exit from '../IMAGE/image.png'
      <img src={logo_exit} calssName='OK'\>
  • public 폴더 : 파비콘과 같이 앱 밖에서 사용하는 이미지는 주로 public 폴더에 있어야 한다.
    • 파일이 없다면 컴파일 단계가 아닌 접근시의 404 오류를 응답받습니다.
    • 파일을 받고 rendering하는 처리등이 필요하므로, 시간이 느리다는 이야기가 있습니다.
    • (예시) src/COMPONENT/file.tsx---> public/exit.png
      <link rel="icon" href="%PUBLIC_URL%/favicon.ico" \>

-----------------------------------------------------------------------------------------------------


c. 기타 활용 명령어

 

다음은 프로젝트를 실행하기 위한 나머지 명령어입니다. 

# package 설치
yarn add @types/react-dropzone
yarn add react-dropzone

# deploy with npm
yarn global add serve
yarn build
serve -s build

# deploy with npm
npm run build
serve -s build # or npx serve -s build

# deploy with firebase
npm install -g firebase-tools
firebase login
firebase init
firebase deploy
firebase hosting:disable
  • yarn과 함께 패키지 설치시 위와 같은 명령어를 사용합니다. typescript에 해당하는 패키지를 설치하고 싶다면 "@types/"를 앞에 붙여주면 됩니다.
    • import useDropzone from ‘react-dropzone’ 와 함께 패키지를 import할 수 있습니다
  • deploy하기
    • 뒤에서 react의 과정에 대해 자세히 설명하겠지만 package.json의 scripts로 담긴 "react-scripts start" 와 "react-scripts build"는 다릅니다.
      • react-scripts start : 개발 모드 빌드로, HMR(hot-module-replacement)가 동작해 수정과 동시에 화면이 변경됩니다. 코드에 Error 있을 경우에는 브라우저에 메시지를 출력하기도 합니다. https 인증서 없이도 임시로 https 형태로 url을 만들 수 있습니다.
      • react-scripts build :  배포용 빌드로, 압축 형태로 제공하며 build 된 정적 파일을 서비스 할 때 간단하게 사용하기 편리합니다. serve 패키지는 node.js 환경에서 동작하는 웹 서버 애플리케이션입니다. 
    • firebase를 활용해 deploy할 수도 있으며, 이외에 주로 github pages를 활용해 하기도 합니다.

2. React 의 특징

 

기본적으로 위 CRA 구조에서 React가 실행하는 단계는 아래와 같습니다.

  1. npm start 혹은 yarn start로 실행
  2. package.json 중 
    scripts: {
        start : react-scripts start
    }
    에있는 react-scripts start가 실행
  3. node_modules/react-scripts/scripts/start.js
    const devServer = new WebpackDevServer(compiler, serverConfig);
    // Launch WebpackDevServer.
        devServer.startCallback(() => {
    });
    WebpackDevServer가 브라우저가 접속할 개발서버에 접근해 bundle된 source를 받아와 실행합니다.
    ** WebpackDevServer :  실시간으로 리로딩을 제공하는 개발서버에 접근하기 위한 API
  4. React App의 모든 코드는 Browser-side Rendering합니다. 즉, react 프로젝트는 SPA(Single Page Application)이므로 모든 페이지가 public 폴더의 index.html로 모여 처리됩니다.
    하지만 public폴더에 있는 index.html파일을 보면, 아무것도 없습니다...
  5. React에서는 직접 HTML을 코딩하는 것이 아닌, src/index.tsx폴더에서 JSX라는 특별한 문법을 이용해서 동적으로 HTML뷰를 생성하고, index.html<div id="root"></div> 아래로 들어갑니다.
    ** JSX(Javascript + xml) : Javascript에 대한 확장 구문으로서, React에서 지원하는 포맷으로 함수의 마지막에 HTML 문법과 같이 return하는 구조를 가집니다.
  6. index.tsx파일 내용은 아래와 같습니다. (바꿀 일은 없습니다.)
// index.tsx

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

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
  • import React from 'react' : react 모듈을 부릅니다.
  • import ReactDOM from 'react-dom/client' : reactDOM 모듈은 react 코드 실제 HTML로 Rendering하기 위한 도구로, 자세히 모르셔도 될 것 같습니다. 
  • import App from './App' : 실제 구현될 App.tsx를 import 하는내용입니다.
  • import reportWebVitals from './reportWebVitals' : React의 성능 측정 도구로, React app의 잠재적인 문제를 알아내기 위한 도구입니다. UI를 따로 렌더링하지 않으며, child 검사와 경고등을 맡습니다.
  • document.getElementByID(‘root’) : public/index.html<div id="root"></div>를 불러온다
  • root.render() : 위에서 얻은 root div 안에 App을 추가합니다.
    • <React.StrictMode> : React App 내의 잠재적인 문제를 얻기 위한 도구 입니다.
  • reportWebVitals() : 위와 같습니다.
더보기

-----------------------------------------------------------------------------------------------------

<용어 설명>

  • SPA(Single Page Application) : 요청시마다 서버로부터 리소스들과 데이터를 해석하고 화면에 렌더링하는 기존 방식과 다르게, 브라우저에 최초에 페이지를 한번 전체를 로드하고, 이후부터는 특정 부분만 데이터를 바인딩하는 방식입니다.
    • SPA를 배포하기 위해 Hosting Server가 있어야 합니다.
  • Static Website(정적 웹사이트) :  다른 Server-side 코드가 없이 HTML, CSS, Browser-side JavaScript 코드로만 구성되어 있는 웹사이트입니다. 
    • React를 포함한 모든 SPA는 Static Website입니다.
    • build 폴더에 존재하는 JavaScript 코드들은 Server-side 코드가 아니고 Client-side 혹은 browser-side 코드입니다. 즉, 브라우저에서 실행되며, 서버에서 실행되기 위한 코드(Node.js, PHP 등)가 전혀 없습니다.
  • CSR(Client Side Rendering) : 서버가 요청을 받으면 HTML과 JS를 보내고, Rendering이 Client에서 일어납니다.
    1. User가 Website 요청
    2. CDN이 HTML, JS로 접근할 수 있는 링크를 Client로 보냅니다.
    3. Client는 HTML과 JS를 다운로드 받는다.
    4. 다운이 완료된 JS가 실행되어, 서버로 다시 데이터를 위한 API 를 보냅니다.
    5. 서버가 API를 받고 data를 받아 회신합니다.
  • SSR(Server Side Rendering) : 서버에서 Rendering을 마치고 Client에 전달합니다.
    1. User가 Website 요청
    2. 서버는 바로 렌더링 가능한 html파일을 만들어 Client로 보냅니다.
    3. Client는 즉시 Rendering합니다.
    4. Client가 Javascript를 순서에 따라 다운받고, 컴파일해서 실행합니다.
  • CDN(Content Delivery Network) : 다양한 Resource를 저장해 제공하는 시스템.
    • 지리적으로 여러개가 분산되어 있는 서버입니다.
    • Resource를 사용자와 가까운 곳에서 바로 전송함으로써 전송 속도를 높입니다.
    • CDN은 Web page, image video 등의 Resource를 사용자의 물리적 위치와 가까운 프록시 서버에 캐싱합니다. 
  • React Router를 사용한 React App의 모든 코드는 Browser-side 패키지이며 브라우저에서 실행됩니다. React App이 브라우저에 로드되고, React Router가 URL을 확인하고 화면에 그에맞는 컴포넌트를 조건부 렌더링하기도 합니다. 
    즉, 브라우저가 index.html을 응답을 받은 뒤에 React Router가 경로를 확인하고 그에 맞는 컴포넌트를 렌더링하도록 해야합니다.

** Cross-Origin Resource Sharing(CORS) : 추가적인 HTTP header를 사용해서 App이 다른 origin의 리소스에 접근할 수 있도록 하는 메커니즘 이지만, 다른 origin에서 내 리소스에 함부로 접근하지 못하게 하기 위해 주로 사용됩니다. 즉, 다른 사람들이 자유롭게 resource 사용을 하지 못하도록 막는 기능입니다.

-----------------------------------------------------------------------------------------------------

위에 따라 실질적으로 구현할 App.tsx파일부터 설명을 시작해보겠습니다. 예시 파일은 위에서 보여드렸습니다.


a. 기본적인 특징
// App.tsx

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
  • 하나의 Component 를 만들고 재활용  : 외부에서 만들고 export 해주면 import 해서 사용가능합니다.
  • JSX(JavaScript XML)의 특징 : 기존의 Javascript의 특징에 XML을 추가한 확장 문법
    • component를 return 하는 구조입니다.
    • className : HTML의 class 와 같은 역할이지만, class 는 예약어이기 때문에 다른 표현으로 쓰입니다.
  • React최적화 : 뒤에서 또 설명드리겠지만, 조건에 따라 화면이 Re-Rendering되는 경우가 많아 이를 줄이는 것이 중요합니다. 최적화 방법 여러개를 소개합니다.
    • 1.  React.memo : 컴포넌트의 상태가 변경되어 re-rendering하는 경우, 하위 컴포넌트까지 모두 재평가하지 않도록 하위 컴포넌트를 만드는 함수입니다. React.memo는 HOC로, 컴포넌트를 return 합니다.
      ** HOC(Higher Order Component) : 인수로 컴포넌트를 전달받아 컴포넌트를 반환하는 컴포넌트
      const MyComponent = React.memo(function MyComponent(props) {
        /* render using props */
      });
    • 2. useCallback : Javascript에서 Object 타입을 가지는 값이 props로 component에 전달된다면, 이 props가 재평가될 때마다 매번 component를 생성하기 때문에 React.memo를 사용하더라도 사용하는 의미가 없어집니다. 따라서 component가 재평가 되더라도 함수의 re-rendering을 막을 수 있는 useCallback()을 사용해 최적화합니다. 이는 아래 hook 함수에 대한 설명에서 자세히 하겠습니다.
      const func = useCallback(function, [...dep]);
    • 3. useMemo : 위와 비슷하지만 함수를 저장하는 것이 아닌, 값을 저장하는 방법이며, 역시나 아래 hook 함수에 대한 설명에서 자세히 하겠습니다.
    • 4. Lazy Loading : 해당 코드가 필요한 경우에만 로딩하는 것으로, React Route를 활용해 구현합니다.
      • SPA 를 사용하게 되면 결국 하나의 큰 Javascript Bundle 일을 다운로드해, Rendering 하게 되는데 web 사용자가 모든 bundle을 받는 것이 시간이 오래 걸립니다.
      • 따라서, 모두 다운로드 하지 않고, 최초의 bundle을 받고 나머지는 필요한 경우에만 다운로드 하도록 하는 것입니다. 
      • React Route : 하기의 component들은 해당 URL 경로가 호출되면 해당 component에 필요한 bundle을 다운받아 실행합니다.
import { Routes, Route } from 'react-router-dom';

//import Page1 from './COMPONENT/page1';
//import Page2 from './COMPONENT/page2';
//import NotFound from './COMPONENT/NotFound';
const page1 = React.lazy(() => { import('./COMPONENT/page1') });
const page2 = React.lazy(() => { import('./COMPONENT/page2') });
const NotFound = React.lazy(() => { import('./COMPONENT/NotFound') });

const App = () => {
    return (
    {/*Suspense is added*/}
    <Suspense fallback={<p>Loading...</p>}>
        <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/page" element={<Page1 />} />
            <Route path="/page/:pagedetail" element={<Page2 />} />
            <Route path="/*" element={<NotFound />} />
        </Routes>
    </Suspense>
    );
};

export default App;
  • React 에서 MVC MVP MVVM 중 어떤 패턴이라고 생각하고 구현하면 좋을까?
    • React는 단방향 데이터 바인딩으로 따르는 것을 기본으로 합니다.(부모에서 자식 component로)
    • 초기에는 MVC 패턴을 주로 활용했는데, MVC는 기본적으로 view와 model간의 양방향 Data Binding을 필요로 합니다. 즉, 대규모 SPA에서는 View와 Model이 많아지고, 서로 간의 데이터 흐름이 너무 많아집니다.
    • 따라서 이의 한계에 따라 Flux 패턴이 등장하게 됩니다.(Facebook)
      ** Flux 패턴 : Action이 Dispatcher로 들어와, Model을 업데이트하고, View는 Model을 이용해 화면을 나타냅니다. 하지만, MVC와 다르게 데이터가 단방향으로 흐르게 했습니다.
    • Redux는 Flux패턴을 구현한 상태 관리 라이브러리이며 아래에서 보실 수 있습니다.
    • 정리하면 React에서 우리가 구현하는 코드는 주로 View에 대한 것이며, state를 어떻게 관리하느냐에 따라 MVC와 flux 등의 패턴을 만들 수 있습니다. 
더보기

-----------------------------------------------------------------------------------------------------

<MVC, MVP, MVVM 이란?>

- MVC(Model, View, Controller) 

  • Model : 데이터 처리 부분
    View : 사용자 UI (Out)
    Controller : 입력 처리 부분 (In)
  • Action이 Controller로 들어와, Model을 업데이트하고, View는 Model을 이용해 화면을 나타냅니다.

- MVP(Model, View, Presenter)

  • Model : 데이터 처리 부분
    View : 사용자 UI (In) (Out)
    Presenter : View와 Model 사이 전달부
  • Action이 View로 들어와, Presenter에 전달, Model에서 받아 데이터를 처리, Presenter가 View 응답

- MVVM(Model, View, ViewModel) 

  • Model : 데이터 처리 부분
    View : 사용자 UI (In)
    View Model : View와 매칭되는 Model
  • Action이 View로 들어와, View Model은 View에 Binding, Model에서 받아 데이터를 처리, View Model은 View와 Binding

-----------------------------------------------------------------------------------------------------

 


b. hook 함수

use가 앞에 붙어 함수형 component가 class형 component의 상태값과 생명주기함수를 사용하게 해주는 명령어를 hook 함수라고 한다.

** class형 component : 상태값을 가지고 있으며, 생명주기함수를 작성할 수 있습니다.(render() 함수를 사용해서 JSX를 return하며, this.state를 활용해 생명주기 만든다)

** 함수형 component : 상태값과 생명주기함수를 가질 수 없습니다. 

// function component
import React from 'react';

function NAME({name}) {
  return (
    <div></div>
  );
}

export default NAME;

// class component
import React, { Component } from 'react';

class NAME extends Component {
  render() {
    const {name} = this.props;
    return (
      <div></div>
    );
  }
}

export default NAME;

기본적으로는 useState, useEffect, useContext가 있으며, 추가로 useCallback, useMemo, useRef, useRecoil이 있습니다.

메모리 자원을 함부로 사용하면 오히려 성능저하가 되므로 잘 이해하고 사용하는 것이 중요합니다.

 

//=========================useState
const [display, setDisplay] = useState<string[]>([])
const [isOpen, setIsOpen] = useState<bool>(true)

const openSidebar=()=>{
	if(!isOpen){
    	setIsOpen(true);
    }
}
<Sidebar isOpen={isOpen}>

//=========================useEffect

useEffect(()=>{
    fetch('http://abcd.com:1234/api',   {
        method : 'GET',
    })
    .then(res=>res.json())
    .then(res=>{
            console.log("START: See version : " + res.version);
    });
},[]);
    
useEffect(() => {
    console.log("LISTVIEW ; not come - double check");
}, [keyword, latitude, longitude]);

useEffect(() => {
	const event = ({clientX, clientY}:MouseEvent) =>{
    }
    window.addEventListener("mousemove",event);
    return ()=>window.removeEventListener("mousemove",event)
}, []);

//=========================useRef
const divRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);

<input type="file" ref={inputRef} />
<div className='outOfInput'
	onClick={()=>{inputRef.current?.click()}}
</div>

//=========================useCallback & useMemo

const memoizedCallback = useCallback(
  () => { doSomething(a, b); },
  [a, b],
);

const memoizedValue = useMemo(
  () => doSomething(a, b), 
  [a, b]
);

//=========================useRecoil
// (DECLARE)
import { atom, RecoilState } from "recoil";

export const searchKeyword: RecoilState<string> = atom({
    key: 'keyword',
    default: ''
});

// (RENDER)
import { RecoilRoot } from "recoil";

function App() {
	return(
		<RecoilRoot>
			<SearchBar/>
    	</RecoilRoot>
    );
}

// (USE)
import { useRecoilState } from "recoil";

const [keyword, setKeyword] = useRecoilState<string>(searchKeyword);
  • useState() : state가 여러번 변경되는 경우 만들어 줍니다. (하나만 바꾸는 경우엔 그냥 함수만 실행해도 됩니다)
    • [변수 이름, set변수이름] 으로 구성됩니다.
    • 리스트.push 하면 절대안된다 함수를 통해서 건드려야 한다
  • useEffect() : 보통 이벤트를 추가할 때 사용합니다.
    • useEffect(() => {}); :  Component가 렌더링(mount) 될 때 마다 실행
    • useEffect(() => {},[]);Component가 처음 렌더링 될 떄 한번 만 실행
    • useEffect(() => {},[name]); : 특정값이 업데이트될 때만 실행
    • useEffect(() => {return  ()=>{}}); : 함수를 반환하는 것을 넣어준다면, unmount될 때 실행할 cleanup 함수를 명시해주게 됩니다.
  • useRef() : return에 해당 state가 활용되는 경우, 해당 state가 변할때마다 re-rendering하게 될 수 있습니다.
    • react에서는 id를 잘 활용하지 않기 때문에 getElementByID나 querySelector 대신 useRef() 함수를 주로 활용합니다.
    • React 컴포넌트는 기본적으로 내부 상태(state)가 변할 때 마다 다시 랜더링(rendering)
      • return (
            <div>
              <p>{count}번 클릭</p>
              <button onClick={() => setCount(count + 1)}>클릭</button>
            </div>
          );
      • 위의 경우 count가 변할 때 마다 re-rendering
    • Re-rendering 하더라도 다른 상태들이 초기화되거나 없어지지 않도록 하기 위해서 사용합니다.
더보기

-----------------------------------------------------------------------------------------------------

<Re-Rendering하는 조건 및 과정>

  • 조건
    • props(input argument)가 변경되었을 때
    • state(내부 변수)가 변경되었을 때
    • 부모 component가 렌더링되었을 때
    • forceUpdate() 를 실행하였을 때
  • 과정 (함수 Component)
    1. 위 조건에 따라 기존 component 를 재사용할 지 확인하고 Re-Rendering을 시작합니다.
    2. 함수를 호출합니다.
    3. return 값을 통해 VirtualDOM을 생성합니다.
    4. 이전 VirturalDOM과 새로운 VirtualDOM을 서로 비교해 실제 변경된 부분만 실제 DOM에 반영합니다.
    5. CRP를 진행합니다.

** virtual DOM : 기존의 DOM을 그대로활용 하는 것이 아닌, 추상화한 Javacsript Object를 구성하고 DOM의 상태를 메모리에 저장해 변경 전과 변경 후의 상태를 비교해 최소한의 내용만 반영 하는 기능입니다. 
각종 rendering 과정에서의 문제와 성능 개선을 위해 등장했으며,
상태를 메모리 위에 계속 올려두고, DOM에 변경이 있을 경우 해당 변경을 반영하는 것이 가장 큰 특징입니다.

** CRP(Critical Rendering Path) : HTML, CSS, JavaScript를 로드 해 화면에 픽셀 페인팅을 하는 과정입니다.

-----------------------------------------------------------------------------------------------------

  • useCallback() : 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용합니다.
    • Re-Rendering 시에 함수를 매번 함수를 새로 선언하며 만들어내기 때문에, 매번 다시 만들지 않고 함수를 참조해 이전에 기억하던 함수 자체와 비교해 다른 경우에만 Re-Rendering 하게 하도록 합니다.
  • useMemo() : 특정 값을 새로 만들지 않고 재사용하고 싶을 때 사용합니다. Memo는 Memoization의 약자입니다.
    • useCallback()과 비슷하지만, 함수자체를 기억하는것이 아닌 함수의 return 값을 기억하는 것이 다릅니다.
  • useRecoil() : Redux와 비슷한 Recoil 상태관리 라이브러리 입니다.
    • 앞서 설명한 것과 같이 기존의 MVC 패턴의 양방향성을 개선하기 위해, React에서 추구하는 단방향 패턴과 일치하는 Flux 패턴이 등장했다고 말씀드렸습니다. 이를 구현하는 것이 Redux입니다.
    • 하지만, Redux는 Flow(데이터 흐름)나 Pattern에 대한 이해와 복잡성을 요구하기 때문에 이를 단순화한 Recoil을 주로 활용하게 됩니다.
    • 설치 : yarn add node-sass recoil react-icons

 

 

 

 

 

 


c. 기타 특징
  • HTTP API 보내기
const apiGet = async(type:string, param: string) => {
    const apiUrl: string = 'http://www.abc.com/api/'+type+'get';
    await fetch(apiUrl, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body : JSON.stringify({
            query : param, 
            geometry :[longitude, latitude]
        }),
    })
    .then((resp: Response) => resp.json())
    .then((resp: IHttpRespRestaurantsList) => {
        if(resp != null){
            setRestaurants(resp);
        }
    });
};
  • JSX에서의 조건부 렌더링에서의 특징
    • Case0. 주석처리 : 아시다시피 Javascript와 Typescript에서의 주석은 ///**/ 입니다. 하지만 React의 jsx, tsx에서 return DOM의 주석작성 방법은 바로 {}로 주석을 랩핑해주어야 합니다
      • ex)  {/**/}
    • Case1. &&로 나누기 : return 문 안에서 if 문 쓰기 어려우므로 && 를 사용하기도 합니다.
      • {}로 포장해 어떤 DOM을 낼지 선택할 수 있다.
      • 어떤 경우에 class 내용의 string을 넣어주는 것을 선택할 때도 쓰인다.
      • ex) {state && <div></div>}
    • Case2. ?:(삼항연산자)로 나누기 : return 문 안에서 if 문 쓰기 어려우므로 삼항연산자를 사용하기도 합니다.
      • {}로 포장해 어떤 DOM을 낼지 선택할 수 있다.
      • ex) {state ? 'succeed' : 'failed'}
      • 어떤 경우에 class 내용의 string을 넣어주는 것을 선택할 때도 쓰인다.
      • ex) <div style={ border : 'solid $(selected ? 6:1)px' }>
    • Case3. map : 여러개의 component를 list에 따라 render하는 경우 map을 활용하기도 합니다
      • {}로 포장해 어떤 DOM을 낼지 선택할 수 있다.
      • 함수를 연결하므로 map 함수 내에서는 if문 쓸 수도 있다 
      • ex) imageList.map(el => {}) : 리스트마다 를 return하도록
    • Case4. 미리 정의 된 List로 rendering : List로 상태를 만들어 놓고 Rendering
    • Case5 . const 함수로 정의 된 rendering : 미리 정의된 const 함수를 사용해 rendering할 수도 있다.
var pageUI = { 
  page1 : <p>page1</p>,
  page2 : <p>page2</p>,
  page3 : <p>page3</p>
}

const starRender = (e:number) => {
    const result = [];
    for (var i = 0; i < 5; i++){
        if(i < e){
            result.push(<img src={logo_star}/>);
        }else{
            result.push(<img src={logo_empty_star}/>);
        }
    }
    return result;
};
function Sidebar(){
	return (
        <div className="title">
        	{/*Case1. Render with &&*/}
            {imageList.length === 0 &&
            	<div clasName='text'>
                	No Image
                </div>
            }
        	{/*Case1. Render with && : string case*/}
			{sidebarInfo && (
                {/*Case2. Render with ?*/}
                (sidebarInfo.place_name === '') ? 
                'Nothing' 
                : sidebarInfo.place_name
			)} 
            
        </div>
        {/*Case2. Render with ? : string case*/}
        <div className={"title2" + (isOpen? 'open' : '')}></div>
        
        {/*Case3. Render with map*/}
        {sidebarInfo && (sidebarInfo.scores.map((item, index) => (
            <div className={`__item ${activeIndex === index ? 'active' : ''}`} key={`score-${index}`}>
                <div className="__item __text2">
                    {item.score}/5.0   
                </div>
                <div className="__item __icon2">
                	{/*Case5. Render with another function*/}
                    {starRender(Math.round(Number(item.score)))}  
                </div>
            </div>
        )))
        }
        {/*Case4. Render with List*/}
        {
        	pageUI['page1']
      	}
        {/*Case5. Render with another function*/}
        {sidebarInfo && (sidebarInfo.scores.map((item, index) => {
        	if(item != null){
            	return <div className={`__item ${activeIndex === index ? 'active' : ''}`} key={`score-${index}`}>
            </div>
            }
        }))
        }
    )
}




3. Typescript의 특징

Typescript는 javascript의 superset 언어로, javascript에 type을 명시 하는 것이 가장 큰 특징입니다. 많은 앱들이 react로 작성이되는데, 큰 앱들을 react로 작성하다보면 유지보수가 힘들어서 type이 존재하는 typescript를 활용하는 것이 이점이 되는 일이 많습니다. 따라서 react 프로젝트를 typescript로 구성하는 경우가 많습니다.


a. 기본적인 특징
  • javascript와 typescript의 가장 핵심적인 차이는 type에 대한 명시
    • 종류 : boolean, null, undefined, number, string, symbol, object, array, enum, tuple, any, never, void (12가지)
      • any : 이걸 최대한 쓰지 않는게 핵심입니다.
      • unknown : any가 가지고 있는 타입불안전성을 조금이나마 해소하기 위해 나온 대체자
      • never 타입은 모든 타입의 subtype이며, 모든 타입에 할당 할 수 있습니다. 하지만, never 에는 그 어떤 것도 할당할 수 없습니다.
  • Type을 명시하는 syntax 예시
// Variable Example
let ATC:number = 12345;

// Function Example
function NAME(abc:ABC):DEF{
	return;
}
  • import와 export (javascript와 동일)
    • export의 경우
      • export let a = ['a_1', 'a_2', 'a_3']; : 선언과 동시에 export하는 경우
      • export { a2, a3 as a4 }; : 여러개를 export하는 경우, as를 활용해 export  이름을 바꿔줄 수도 있습니다.
      • export const c = 3; : 상수를 export하는 경우
      • export default b; : '해당 모듈 코드엔 개체 하나만 있다’는 사실을 명확히 나타내기 위해 사용합니다. 따라서, 파일하나엔 export default가 하나밖에 없습니다.
    • import A와 import {B} : bracket이 포함된 것에 대해 다른 점
      • default 로 export를 한 경우 : 괄호를 사용하지 않아도 import할 수 있으며, 변수명도 마음대로 할 수 있습니다. (b로 export하더라도 d로 import할 수 있습니다.)
      • 나머지 경우 괄호 안에서 불어와야 하며, 반드시 export할 때의 이름을 그대로 import해야 합니다. 다른 이름으로 import 하고 싶은 경우 as를 활용합니다.
import React from 'react';

import d, { a, c as e } from './Example';

import SearchBar from './COMPONENT/search'
import {searchFunction} from './COMPONENT/search'

function App(){
	return <>;
}
export default App;
  • interface : 함수에서 처리할 property를 미리 추가하는 방법
    • 아래 다양한 예시가 있습니다.
    • Case1. List type을 명시하는 방법으로, type을 아래와 같이 명시하는 방법이있습니다.
    • Case2. Index Signature : property의 이름을 모르고 있지만 type을 선언해야 할때 사용합니다
      ex) [key: string]: any; 
    • 파일을 따로 두는 경우 반드시 export type을 해주어야 합니다.
interface IRestaurantFeature {
    id: string;
    getFunction: ()=>void;
}

interface IRestaurant {
    place_name: string;
    features:IRestaurantFeature[] | null;
}

interface IRestaurant2 extends IRestaurant, IRestaurantadd {}

// Case1
type IRestaurantsList = IpRestaurant[];

// Case2
interface IRestaurantsList2{
    [index:number]:IRestaurants;
}

export type{
    IRestaurantFeature,
    IRestaurant,
    IRestaurantsList
}
더보기

-----------------------------------------------------------------------------------------------------

<Generic type : 정의해 놓은 인터터페이스를 변환하는 방법>

  • Partial : 특정 타입의 부분 집합도 가능한 타입을 정의
    type Email = Partial<Address>; // Address 에 email, name 등등 여러가지 property가 있는 경우 
    const john : Email = { email: "abc@gmail.com" }; 
  • Pick : 특정 타입에서 몇 개의 속성을 선택하여 타입을 정의
    type shoppingItem = Pick<Product, "id" | "name">; // id와 name property만 사용
  • Omit : 특정 속성만 제거한 타입을 정의합니다. pick의 반대
    type shoppingItem = Omit<Product, "stock">; // stock property 제거

-----------------------------------------------------------------------------------------------------

  • 함수의 argument Props 
    • 초기화할 때 javascript 기본문법으로 Parameter에 ({selected=false } )해주기도 함
    • (abc : ABC, def:DEF) : abc의 타입 ABC, def의 타입 DEF
    • (abc : ABC, def?:DEF) : abc의 타입 ABC, def의 타입 DEF(선택적 매개변수)
      ** 선택적 매개변수 : 선택적 매개 변수 def?:DEFdef:DEF|undefined와 같습니다. 필수 매개 변수가 앞으로, 선택 매개 변수는 뒤로갑니다.
    • ({abc,def}:{abc:ABC, def:DEF}) : abc의 타입 ABC, def의 타입 DEF을 named parameter로 정의
      ** named parameter : 함수 argument를 객체로 감싸주고, type을 따로 정의해주기
    • {abc}: INTERFACE : INTERACE의 destructing assignment
      ** destructing assignment(비구조할당, 구조분해할당) : 해당 props의 type을 전부쓰는 것이 아닌 일부를 분해해서 할당하는 것
const RestaurantRow = ({place_list, whenClick}: {place_list:IRestaurant[], whenClick:any}) => {  
    return (
        <li>
            <div className="RowTitle" onClick={()=>{
                    whenClick('', place_list)
                }}>
                <span className="RowAuthor">{place_list[0].place_name}</span>
            </div>
        </li>
    )
};
const RestaurantRow = ({place_name, whenClick}: IRestaurant[] & any) => {  
    return (
        <li>
            <div className="RowTitle" onClick={()=>{
                    whenClick('', place_list)
                }}>
                <span className="RowAuthor">{place_list[0].place_name}</span>
            </div>
        </li>
    )
}
  • Logical &&와 ||를 일반 Object에 적용하기
    • && : 첫항이 true인 경우 두번째항을 반환하고, 그렇지 않은 경우 첫항을 반환
      'Cat' && 'Dog' :  T && T returns "Dog"
      false && 'Cat' :  F && T returns false
      ''    && false :  F && F returns ""
      false && '' : F && F returns false
    • || : 첫항이 true인 경우 첫번째항을 반환하고, 그렇지 않은 경우 두번째항을 반환
      'Cat' || 'Dog' : T || T returns "Cat"
      false || 'Cat' : F || T returns "Cat"
      'Cat' || false : T || F returns "Cat"
      ''    || false : F || F returns false
      false || '' : F || F returns ""
  • @ts-expect-error, @ts-ignore, @ts-nocheck
    • Typescript 에러를 무시하기 위해 사용 (Type등의 명시가 어려운 상황 등)
    • @ts-nocheck를 파일 맨 앞에 붙여 줄 수도 있습니다.
    • @ts-ignore를 코드 앞에 붙여줄 수도 있습니다.
    • @ts-ignore-start@ts-ignore-end로 범위를 지정해줄 수도 있습니다.
// @ts-nocheck

// @ts-ignore
if ([undefined, null].includes(data)) {return '?';}

// @ts-ignore-start
// @ts-ignore-end
  • ===와 == (Javascript와 동일)
    • == (Equal Operator) : 값이 같은지, 다른지 확인
    • === (Strcit Equal Operator): 값과 데이터 type이 모두 같은 지를 확인
  • 문자열 Literal 타입 :  string의 확장으로, 사용자가 정의한 타입에 의한 문자열만 사용 가능
type Animal = "rabbit" | "dog" | "Cat" | "human";

 


b. React 외 예시
  • React에서 활용한 것이 아닌 typescript 파일예시
// greeter.ts

function greeter(person:string) {
    return "Hello, " + person;
}

let user = "Jane User";

document.body.textContent = greeter(user);
  • 설치하고 컴파일 하기 
# install
npm install -g typescript

# compile (will be greeter.js)
tsc greeter.ts

 


https://tech.osci.kr/2022/06/16/recoil-state-management-of-react/

https://www.daleseo.com/react-hooks-use-ref/

https://typescript-kr.github.io/pages/tutorials/typescript-in-5-minutes.html

https://cocoon1787.tistory.com/796

https://codingapple.com/unit/react-if-else-patterns-enum-switch-case/
https://bobbyhadz.com/blog/typescript-interface-type-array-of-objects

https://ssungkang.tistory.com/entry/React-React-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-create-react-app

https://webruden.tistory.com/919

https://velog.io/@cjy9306/create-react-app%EC%97%90%EC%84%9C-react%EA%B0%80-%EC%8B%A4%ED%96%89%EB%90%98%EB%8A%94-%EA%B8%B0%EB%B3%B8-%EC%9B%90%EB%A6%AC-yjk56rqzlk
https://ljh86029926.gitbook.io/coding-apple-react/1/where-is-html
https://hahahoho5915.tistory.com/52
https://velog.io/@kim98111/useMemo
https://velog.io/@kim98111/React-App-Deploy

https://velog.io/@seong-dodo/React-%ED%81%B4%EB%9E%98%EC%8A%A4%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-vs-%ED%95%A8%EC%88%98%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8

https://beomy.tistory.com/43

 

728x90
반응형