인프런 타입스크립트  기초를 통해 공부중이며 제로초님의 유료강좌입니다.

코드를 통으로 가져오지는 않겠습니다.(내가 글을 쓰면서 복습하는게 목적이기때문에 필요한 부분만)

 

타입스크립트 (Typescript)

 

공식문서 읽기

https://www.typescriptlang.org/docs/home.html

 

 

.ts파일과 컴파일된 .js파일을 동시에 열면 에러가나는데, 설정을 해주면 에러가 안난다고 한다.

타입스크립트의 설정 파일은 tsconfig.json파일이다.

 

tsconfi.json 잘쓰는 옵션 몇가지

 

 

1.allowJS

js파일 사용 허용하는 옵션

타입스크립트는 .js확장자를 허용하지 않는다. 따라서 .js파일을 컴파일하는데, 이때 자바스크립트를 타입스크립트로 바꾸는 프로젝트가 진행중이라면 곤란함을 겪을 것이다.

점진적 도입을 위한 옵션이있는데, allowJS 옵션이다.

true일 경우 .js파일을 사용할 수 있다고 한다.

// tsconfig.json

"allowJS" : true

checkJs는 allowJS랑 같이 상요하는 옵션이라는데.... (뭐였더라)

 

 

2.bascURL

기본 경로 설정

bascURL


3.declaration

lib.dom.dts파일 생성(타입들만 작성되어있는 파일)

declaration

이 옵션을  true로 하면 dts파일이 생성된다.

객체랑 함수를 쓰다보면 타입을 커스텀하는 경우가 온다.

이때 타입들을 ts파일에 넣어도 되지만 용도가 다르기때문에 분리하는 것이 좋다.

그럴때 dts파일을 생성하는데 이때 이 옵션을 true로 바꾸면 된다.

 

 

4.이건 이해를 잘 못했다1

esModuleInterop
import React from 'react';
import * as React from 'react';

위의 import하는 코드가 같다고 생각하는데 완전히 다르다고 한다.

모듈시스템에서 엄청난 차이가 있는데, 원래는 공식적으로 아래처럼 써야하는게 맞다.

따라서 위에처럼 쓰게되면 에러가나는데, 이 에러를 없애는 방법이 esModuleInterop이다.

아래처럼 쓰기 싫어서 옵션을 쓰는사람들이 있다는데 잘못쓰면 위험하다.

 

 

5.이건 이해를 잘 못했다2

decorator를 쓴다면 확인해보자

emitDecoratorMetadata
experimentalDecorators

 

6.help

help는 명령어인데, 커맨드에 치지말고 공식문서를 보는것이 좋다.

help

 

 

7.init
npm init처럼 tsc init하면 ts.config.json 만들어내는것이다.

직접 만들어도 상관은 없다.

init

 

 

8.jsx

react를 쓰면 타입스크립트 파일인 tsx를 jsx로 변경한다.

jsx

 

 

9.lib

공식문서에 목록이 있는데 dts에 있는것이 불러와진다.

만약 최신문법을쓸때 에러가 나는 경우가 있다면 lib 옵션에 추가해줘야한다.

"lib" : ["DOM", "ES5", "ES2015"]

ES6 === ES2015

ES2015, ES2017은 중요하다

lib에 ES6에 넣으면 ES2020과 ES2019를 넣는게 좋다.

 

 

10.outDir

.ts와 같은 경로에 기본으로 .js 결과물을 생성하는데, 

outDir 옵션으로 경로를 수정할 수 있다.

outDir

 

 

11.target

기본적으로 타입스크립트가 ES3로 변환하는데(ie8)

es3지원하려고 타입스크립트를 쓰기도했다함.

하지만 너무 옛날코드이므로.. ie10부터 지원할거라면 ES5로 수정 하는게 좋음

타입스크립트가 var로 변환되는데, const나 let으로 변환하려면 ES6로 적어주면된다.

"target" : "ES6"

ES6로 작업을 한 후에 하위버전 호환하려면 바벨을 통해 컴파일하는 사람들이 있다.

타입스크립트 -> ES6 --바벨--> JS

 

 

12.types, typeRoots

내가 만든 커스텀 dts, 객체나 함수를 보통 dts로 만들어둔다.

그런 파일의 경로를 지정해주면 옵션을 사용해서 dts를 로딩하는 것이다

types 
typeRoots

 

 

13.strict 옵션들

타입을 얼마나 엄격하게 할 것인가인데, 기본값이 false이기 때문에 true로 설정해준다.

strict 옵션들
"strict" : true

 

 

14.noImplicit 옵션들

얘네도 true로 해주는게 좋다.

strict와 noImplicit 시리즈들은 타입스크립트의 엄격한 장점을 사용할 수 있기때문에 켜두는게 좋다.

noImplicit 옵션들

 

 

15.module

모듈은 commonJS랑 ES 가있는데, 대부분은 CommonJS 가 될 것이고 최신문법만쓸거면 ES6도 된다.

commonJS할 때 조심해야하는 점은 맨 처음 import할때 * as가 붙은것과 아닌것의 차이를 알고 써야한다.

export default 할때 많은 차이가 있다고 한다.

ie를 지원해야하는 경우에는 commonJS를 많이 쓴다.

module

 

 

16.watch

변경사항 생겼을때 자동으로 컴파일해주는 옵션

watch

 

 

17.include, exclude, extends

ㄴ include는 어떤파일을 컴파일할지

ㄴ exclude는 반대로 컴파일 하지 않을 파일

ㄴ extends tsconfig.json을 다른것을 확장할 수 있는데, 보통 어떠한 폴더가 있는데 타입스크립트프로젝트가 여러개일때 tsconfig.json을 하나로 만들고 공통 tsconfig.json을 만든 후 다른 부분들이 있는 tsconfig.json에 확장할때 쓴다. 

"include" : ["lecture.ts"],
"exclude" : [],
"extends" : []

 

나머지는 프로젝트따라 사용하고 안하고가 달라서 공식문서 한번쯤 읽어보는게 좋다.

 

 


 

.js 파일과 .ts파일을 같이 열 수 있게하기 위해(점진적 도입) tsconfig.json 옵션

{
    "compilerOptions" : {
        "strict" : true,
    
        // 1. allowJS true로 설정
        "allowJS" : true
    },
    
        // 2. exclude로 모든 .js파일 제외
    "exclude" : ["*.js"]
}

 

자바스크립트 에러 체크

{
    "compilerOptions" : {
        "strict" : true,
    
        // 이거 없으면 자바스크립트 에러는 체크를 안하기 때문에 엄격하게 켜두면 좋다. 
        // 하지만 점진적도입중에 에러가 너무 많이 나면 checkJS안해도된다.
        "checkJS" : true,
 
        "allowJS" : true
    },
    "exclude" : ["*.js"]
}

 

그래서 이번 강좌 제로초님의 tsconfig.json 대략적인 결과물.

입문자는 strict true만 켜도 타입스크립트의 대부분의 기능을 사용해볼수 있다고 한다.

{
    "compilerOptions" : {
        "noImplicitReturns" : true,
        "strict" : true,
        "lib" : ["DOM", "ES5", "SE2015", "ES2020"],
        "target" : "ES6"
    },

    "include" : ["lecture.ts"],
    "exclude" : [],
    "extends" : []
}

 

 

인프런 타입스크립트  기초를 통해 공부중이며 제로초님의 유료강좌입니다. 

코드를 통으로 가져오지는 않겠습니다.(내가 글을 쓰면서 복습하는게 목적이기때문에 필요한 부분만)

 

타입스크립트 (Typescript)

타입스크립트는 자바스크립트의 superset(상위집합)이다.

자바스크립트가 언어고 타입스크립트가 라이브러리라서 반대로 생각하는데, 

타입스크립트자바스크립트의 모든 기능에 타입이라는 시스템이 추가된 것이므로 더 큰 존재이다.

따라서 기본적으로 타입스크립트를 사용하기 위해서는 자바스크립트를 알아야한다.

제로초님은 자바스크립트 강좌 (유튜브에 업로드하신 무료 es2020 / 자바스크립트강좌를 보고 오면 도움이 된다고했다)

 

제로초님은 실무에서 클라이언트가 요청하는 것을 제외하고는 보통 타입스크립트로 쓴다고 한다.

자바스크립트의 파일이 .js 확장자로 되어있다면 타입스크립트의 파일은 .ts 확장자로 되어있다.

자바스크립트 파일에서 .ts로 확장자 변경을 한다고 해서 타입스크립트를 사용할 수 있는 것은 아니다.

자바스크립트로 개발한 것을 타입스크립트로 변경하는 작업이 필요하다.

 

타입스크립트는 언어이면서 라이브러리이다.

언어와 라이브러리 특징으로는 하위 버전을 보장해줘야하는 것이 있다.

그렇기때문에 타입스크립트는 과거에 실수(잘못만들었던 것들)를 가지고 있다고 한다.

 

타입스크립트를 쓰려면 vscode에디터나 webstorm에디터를 쓰는것을 추천했다.

무료로는 vscode를 쓰면되는데, 타입스크립트와 vscode 에디터 모두 마이크로소프트가 개발해서 호환성이 좋다고한다.

이 두개가 아닐경우에는 지원하는 기능이 없어서 생산성이 낮아지기때문에 타입스크립트를 쓰는 의미가 줄어든다고 했다. 

 

자바스크립트는 약타입언어이며 타입스크립트는 강타입언어이다.

타입스크립트를 쓸 경우 안정적이며 에러를 방지해준다고 한다.

하지만 타입스크립트는 자바스크립트와는 다르게 컴파일이 필요하다.

자바스크립트의 실행기브라우저(자바스크립트 엔진이 있기때문)나 노드라고 한다.

타입스크립트의 실행기디노라는 게 있다는데 안정적인 버전이 아니라고 한다.

 

타입스크립트의 장점은 개발이 라이브러리식이라 빠른 릴리즈이며, 버그는 단점이라고 한다.

 

타입스크립트를 사용하려면 컴파일하는 모듈을 사용하기위해서 기본적으로 노드를 쓸수있어야한다.

브라우저에서 컴파일 없이 바로 타입스크립트를 실행하지 못하기 때문이다.

 

1.타입스크립트 모듈을 다운로드한다.

npm init
npm i typescript

 

파일을 생성하는데 확장자는 .ts이다.

 

npx를 사용하지 않으려면 아래와 같이 전역으로 타입스크립트를 설치해줘야 cmd에서 명령어를 쓸 수 있다.

npm i -g typescript

 

근데 전역으로 타입스크립트를 설치하는 것은 좋지않다고 한다.

전역으로 설치하게되면 전역에 깔린 타입스크립트와 프로젝트에 깔린 타입스크립트의 버전이 일치하지 않게 되는 경우가 생기고 그 경우 명령어의 버전이 달라질 수 있기 때문에 에러가 날 수 있다고 한다.

 

vscode 에디터 기준으로 터미널을 열었을 경우 powershell로 되어있으면 ctrl + shift + p 혹은 f1을 눌러서 select default shell을 검색하여 command prompt로 변경해주는게 좋다.

 

2.타입스크립트 컴파일 명령어는 아래와 같다.

tsc (파일명)
tsc test.ts

 

3.그 후 명령어를 매번 치면서 컴파일하기 번거로울때 watch 옵션을 설정해줘서 실시간으로 컴파일을 할 수 있도록 해준다. -w는 watch 옵션의 명령어이다.

watch 옵션을 켜주면 파일에 변경이 생길때마다 자동으로 컴파일해준다.

tsc (파일명) -w

 

보통 타입스크립트는 아래와같이 사용할 수 있는데, 쉬운 자료형같은경우 굳이 선언하지 않아도된다고한다.

let numberOne = Math.ceil(Math.random * 9);

// 굳이 이렇게 할 필요없다.
let numberOne : number = Math.ceil(Math.random * 9);

 

타입스크립트는 에러가 나면 에러코드를 cmd에 보여주는데, 

코드를 통해 구글링이 쉬워지기때문에 이것또한 장점이라고 한다. 

 

타입스크립트의 타입은 다양한것이 존재하며 커스텀도 가능하다.

(나중가면 내가 만들거나 사람들이 만들기도 한다고 한다.)

 

타입에는 number, string과 같은 것만 있는게 아니라 HtmlInputElement, HtmlDivElement, HtmlFormElement 등의 타입도 있다. 이건 브라우저에서 제공하는 타입으로 타입스크립트에서 만들어둔것이라고 한다.

 

자주쓰는 타입들을 보다가 모르는 타입이 나오면

[마우스 우클릭 > go to type definition 클릭] 혹은[F12]를 누르면 lib.dom.d.ts 파일이 열린다.

이 파일은 타입스크립트에서 작성해둔 모든 돔과 관련된 타입이 적혀있고 이 파일이 있기때문에 자동완성이 되는거라고 한다.

.ts 파일과 .d.ts 파일의 차이는 lib.dom.d.ts는 타입들만 작성되어있고 .ts파일은 우리가 실제 코드를 작성하는 파일이다.

 

타입을 사용자가 커스텀해야하는 경우가 생긴다고 말했었는데, 이때 d.ts를 만들어서 타입들을 적은 후 로딩하는 방법을 통해서 사용할 수 있다. (이건 뒤에서 알려준다고 한다)

 

타입을 상속받은 예시

버튼을 createElement하고 난 후에 확인해보면 accesskey가 없는데 자동완성이 되는 경우가있다.

button.accesskey

이럴때에는 d.ts파일에서 확인해보면 상위에서 상속되었다는 것을 알수있다.

interface는 객체, extends는 상위에서 상속받는다는 것을 뜻한다.

타입도 상속을 받을 수 있기때문에 부모타입으로 가서 찾아보면 확인할 수 있다.

HtmlButtonElement에는 없던 accesskey가 부모인 HtmlElement에 있다.

HtmlElement에 없으면 또 다시 extends를 보고 부모를 확인해보자.

 

타입스크립트는 가독성이 좋지는 않다고한다.

자바스크립트하다가 타입스크립트하면 가독성이 안좋다 느낄 수 있는 이유가 전반적으로 코드가 길어진다고 한다.

그래서 간단한 시스템에 도입하면 일이 더 커지기때문에 마이너스가 될 수 있다.  

대형 프로젝트나 추후의 확장성을 고려할 경우에 도입하면 좋다.

 

이건 타입스크립트와는 별개로 팁이라고한다.

-자바스크립트나 타입스크립트 모두 최신문법은 프로토타입을 안쓰며 클래스를 쓴다.

 

-package.json에 버전정보 앞에 "^3.33"과 같이 ^꺽쇠가 보이는데, 꺽쇠는 버전의 범위다.

3.33부터~ 4.0미만을 의미한다.(4를 포함하지 않는다, 미만!)

이 범위안에서 최신버전이 나왔을 경우 최신버전이 설치된다.

따라서 3뒤에 .3.3 숫자들은 큰 의미가 없기때문에 "^3.33"은 "^3"이되어도 상관없다.

 

-npx는 초반에 전역으로 타입스크립트를 설치하지않고 tsc 명령어를 사용하려고 할때 쓸 수 있다.

글로벌로 (-g) 타입스크립트를 설치하는 작업을 했었는데

이것은 타입스크립트를 프로젝트와 전역에 중복으로 설치하는 게 된다.

이때 버전정보가 달라질 수 있기 때문에 npx를 쓰는게 좋다.

(한참뒤에 프로젝트를 열고 로컬에 타입스크립트가 없을때 npm i -g 타입스크립트를 했다면 프로젝트버전은 3버전이 될 수 있고 나중에 돌아왔을떄 4버전이 될 가능성이 생긴다. 이때 전역에 설치된것으로 명령어를 실행하기때문에 프로젝트는 버전이 3이지만 전역명령어의 버전은 4가 되는 경우가 생길 수 있다. 이러면 오류가 생길가능성이 높다.)

 

-package.json은 꺽쇠로 시작하는 버전정보가 들어있는데, package.lock.json은 정확한 버전은 lock.json에 들어간다. lock.json파일은 프로젝트의 정확한 버전정보가 있기 때문에 무조건 git에 배포해야한다.

버전정보가 바뀌면 프로그램이 망가지는 경우가 생기기때문에 따라서 lock파일이있어야 똑같은 버전을 받을 수 있다.

 

 

 

드디어 리액트 기초강의끝!!

이젠 typescript를 보려고하는데 보는 매일 안쓰다보니 ㅠ 글이 엉망이다.

강의 볼때마다 글 쓰도록 해봐야지...

 

강의 유튜브 주소 :

https://www.youtube.com/watch?v=V3QsSrldHqI&list=PLcqDmjxt30RtqbStQqk-eYMK8N-1SYIFn



리액트 라우터

라우터를 쓰기위해서 모듈을 설치해준다.

필요한 것은 react-router와 react-router-dom이다.

npm i react-router react-router-dom

 

react-router는 웹, 앱 모두 사용할 수 있다고 한다.

react-router-dom이 웹브라우저에서 사용하기 위해 필요한것들이 있다고 한다.

react-router만 설치해도 종속성에 의해 react-router-dom이 설치된다고 한다.

 

staticRouter와 hashRouter, browserRouter가 있다고 한다.

import React from 'react';
import { BrowerRouter, hashRouter, staticRouter }  from 'react-touter-dom';

제일많이쓰는건 browserRouter.

 

import React from 'react';
import { BrowerRouter }  from 'react-touter-dom';

const Test () => {
    retern (
        <BrowserRouter>
            //이렇게 감싸줘야한다.
        </BrowserRouter>
    )
};

혹은 ReactDOM으로 render감싸주는 곳에서 컴포넌트를 감싸줘도 된다고 한다.

// app.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(<BrowerRouter><App/></BrowserRouter, document.querySelector('#root'));

 

라우터는 페이지로 만들고 싶은 것들의 경로를 임의로 만들어 작업해준다.

가상의 페이지주소이기때문에 사용자가 주소로 접속할 경우 에러가 난다.

import React from 'react';
import { BrowerRouter, Route }  from 'react-touter-dom';
import TestChild from './TestChild';

const Test () => {
    retern (
        <BrowserRouter>
            <Route path="./경로" component={TestChild}></Route>
        </BrowserRouter>
    )
};

 

가상의 주소로 넘어갈 수 있도록 앵커 태그들이 필요한데, a태그를 사용할 경우에는 에러가 난다.

리액트 라우터는 페이지가 존재하는게 아니라 페이지가 이동하는 것처럼 흉내내는 것이기 때문이다.

따라서 브라우저에서 제공하는 a태그를 사용할 경우 라우터가 인식하지 못한다.

리액트 라우터에서 제공하는 <Link>태그를 사용해준다.

// (X)
retern (
        <BrowserRouter>        	
            <a href="주소"></a>
            <Route path="./경로" component={TestChild}></Route>
        </BrowserRouter>
)
import React from 'react';
import { BrowerRouter, Route, Link }  from 'react-touter-dom';
import TestChild from './TestChild';

const Test () => {
    retern (
        <BrowserRouter>
            <Link to="주소">라우터보기</Link>
            <Route path="./경로" component={TestChild}></Route>
        </BrowserRouter>
    )
};

 

<Link>태그는 페이지의 이동이 아니라 보여지게 흉내내는 것이다.

라우터에게 보여져야할 주소값을 전달한다.

브라우저상에서는 Link가 a태그로 표현된다.

이때 Link태그를 클릭하여 주소를 이동한 후에 새로고침을 하게되면 에러가 발생한다.

또한 주소창에 주소를 입력하고 접근해도 에러가 발생한다.

이것은 주소창으로 접근시에(새로고침도 포함) 서버에 요청이 가기 때문인데, 가상의 주소는 프론트에서만 알고 있기 때문에 서버에서는 에러를 반환한다.

이 부분은 실제 페이지가 여러개가 아니기때문이다.

 

hashRouter

browserRouter와 거의 비슷하지만 주소사이에 hash값이 들어가게 된다.

[ localhost:8080/#/주소 ] 이런형식으로 보여진다.

import React from 'react';
import { HashRouter, Route, Link }  from 'react-touter-dom';
import TestChild from './TestChild';

const Test () => {
    retern (
        <HashRouter>
            <Link to="주소">라우터보기</Link>
            <Route path="./경로" component={TestChild}></Route>
        </HashRouter>
    )
};

 

hashRouter의 장점은 주소를 이동한 후에 새로고침을 해도 에러가 나지 않고 화면이 나타난다.

이유는 해쉬값(#)때문이다.

browserRouter는 주소가 깔끔한 대신에 새로고침시 서버에 요청이 들어가게되는데 hashRouter는 해쉬값때문에 서버에 요청이 가지않는다. 단점은 서버가 주소의 이동을 모르는 것이다(페이지 존재유무)

이렇게 되면 SEO에 불이익을 받게되고 검색엔진에 접근이 안되게 되면.... 사이트 검색이 안될수도 있다.

이것은 실무에서 엄청난 불이익이다.

따라서 보통 실무에서는 해쉬라우터를 사용안하고 브라우저라우터를 사용한다.

 

관리자페이지나 SEO에 전혀 상관이없는 사이트같은경우에는 해쉬라우터를 사용하기도 한다.

해쉬라우터는 배포시에 편하다고 한다. 새로고침때 브라우저만 동작하기때문에 어떤 브라우저든 잘 동작한다고 한다.

 

브라우저 라우터를 쓰면 검색엔진에는 검색이 되나 새로고침이나 주소값 접근의 문제를 해결해야한다.

이것은 서버에 추가적인 세팅으로 해결할 수 있다.

페이지가 존재하는 것을 서버에 알려줘야한다고한다.

서버쪽 세팅시 검색엔진도 신경써야한다.

 

라우터가 100개가 되고 200개가 되고 방대해질 경우에는 동적 라우터매칭을 사용한다.

:name 값은 파라미터로 동적으로 변경가능하다.

import React from 'react';
import { HashRouter, Route, Link }  from 'react-touter-dom';
import GameMatcher from './GameMatcher';
import TestChild from './TestChild';

const Test () => {
    retern (
        <HashRouter>    
            <Link to="/game/number">게임1</Link>
            <Link to="/game/lottory">게임2</Link>
            <Link to="/game/paper">게임3</Link>

            <Link to="/game/index">게임 매처</Link>
            <Route path="/game/:name" component={GameMatcher}></Route>
        </HashRouter>
    )
};

동적 라우터를 사용해서 라우터 하나로 여러개를 처리한다.

this.props.history.match는 라우터 to에 담았던 경로값이 포함되어있다.

 

history, location, match가 undefined가 된다면 라우터 컴포넌트에 cameMatcher를 넣어준다.

라우터랑 연결이 안되어있는 라우터의 경우 withRouter를 사용해서 hoc형식으로 감싸주게되면 사용할 수 있다.

import React, {component} from 'react';
import {withRouter} from 'react-router-dom';

class GameMatcher extends Component{
    render(){
        return(
            <div>
            </div>
        );
    }
}

export default withRouter(GameMatcher);

 

location, match, history

history에는 앞으로가기, 뒤로가기 등의 페이지 이동에 대한 이력이 남아있다.

제공하는 메서드들이 있어서 go나 go back등 (앞으로가기)의 기능을 제공할 수 있다.

그래서 history를 통해 눈속임으로 페이지의 이동을 구현할 수 있다.

이때 브라우저가 제공하는 기본 메서드를 사용하면 안된다.(라우터는 브라우저 기본기능을 통해 이 기능을 구현하고 있다)

 

match는 path가 실제 라우트에 등록한 값을 알 수 있다.

:name 파라미터에 실제 어떠한 값이 들어왔는지 알 수 있다.

이 값은 match의 params에 들어가있다.

 

location은 주소에 대한 정보나 서치, 해쉬값등이 들어있다.

라우터에서 import하던것들을 똑같이 import해준다.

그후에 분기처리한다.

import React, {component} from 'react';
import {withRouter} from 'react-router-dom';

import Number from './number';
import RSP from './RSP';
import Lotto from './Lotto'

class GameMatcher extends Component{
    render(){
        if(this.props.match.params.name === 'number'){
            return <Number />    
        }else if(this.props.match.params.name === 'RSP'){
            return <RSP />
        }else if(this.props.match.params.name === 'Lotto'){
            return <Lotto />
        }else{
            return <div>일치하는 게임이 없다</div>;
        }
    }
}

export default withRouter(GameMatcher);

 

길어지는것은 어쩔수없다고 하며 어느 부분이던 길어지는 부분이 존재하기 때문에 선택사항이라고 한다.

history.pushAPI를 사용할 경우 주소를 변경할 수 있다.

3번째 인자값으로 주소값을 전달한다.

history.push('', '', '변경할 주소값');

 

this.props.history와 history.push는 다르며 브라우저의 historyAPI를 사용하면 안된다.

리액트라우터에서 제공하는 것을 사용해야 에러가 나지 않는다.

import React from 'react';
import { HashRouter, Route, Link }  from 'react-touter-dom';
import GameMatcher from './GameMatcher';
import TestChild from './TestChild';

const Test () => {
    retern (
        <HashRouter>    
            <Link to="/game/number?query=10&data=1">게임1</Link>
            <Link to="/game/lottory">게임2</Link>
            <Link to="/game/paper">게임3</Link>

            <Link to="/game/index">게임 매처</Link>
            <Route path="/game/:name" component={GameMatcher}></Route>
        </HashRouter>
    )
};

to 뒤의 값에 쿼리스트링값을 넘길 수 있는데 

?키=값&키=값 형식으로 작성할 수 있으며  &를 통해 연장할 수 있다.

쿼리스트링값은 서버에서도 확인할 수 있다.

이 쿼리스트링은 location의 search에서 확인할 수 있다.

import React, {component} from 'react';
import {withRouter} from 'react-router-dom';

import Number from './number';
import RSP from './RSP';
import Lotto from './Lotto'

class GameMatcher extends Component{
    render(){

        // slice해서 물음표를 제거한뒤 URLSearchParams에 담아줘야하다.
        let urlSearchParams = new URLSearchParams(this.props.location.search.slice(1));
    
        if(this.props.match.params.name === 'number'){
            return <Number />    
        }else if(this.props.match.params.name === 'RSP'){
            return <RSP />
        }else if(this.props.match.params.name === 'Lotto'){
            return <Lotto />
        }else{
            return <div>일치하는 게임이 없다</div>;
        }
    }
}

export default withRouter(GameMatcher);

쿼리스트링을 사용하려면 URLSearchParams가 필요하다.

slice해서 물음표를 제거한뒤 URLSeatchParams는 빈객체가 아니며 URLSearchParams에 담아줘야하다.

보통 쿼리스트링은 게시판 작업시에 쓰인다.

 

해쉬라우터에는 뒤에 #abc=1234 식으로 붙일 수 있는데, 이건 서버는 모르고 브라우저만 안다.

실제적으로 많이 쓰이지 않는다.

서버가 모르면 쿼리스트링을 딱히 쓸때가 별로 없기때문이다.

 

함수 컴포넌트

props자리에 들어있다. 클래스컴포넌트는 this.props로 접근한다.

다만 라우터에 연결되어있지 않다면 withRouter안에 넣어줘야 사용할 수 있다,

const Games = ({ match, location, history }) => {
	// ....
}

export default withRouter(Games);

 

props를 넘길 수도 있다.

부모컴포넌트의 props를 자식컴포넌트에 넘기는게 목적이라면 render를 쓰는게 좋다.

<Route path="/game/:name" component={() => <GameMatcher props="1234" />} ></Route>
<Route path="/game/:name" render={(props) => <GameMatcher props={props}>} ></Route>

실제로 작업을 하다보면 동적 라우터가 있어도 주소고정라우터가 필요할 경우가 있다.

이때 하나만 렌더링 시키고 싶다면 switch를 사용한다.

<Switch>
    <Route path="/game/:name" render={(props) => <GameMatcher {...props} />} ></Route>
    <Route path="/game/Number" render={(props) => <GameMatcher {...props}/>} ></Route>
</Switch>

switch안에 있을경우 여러개가 렌더링이 되지 않으며 일치하는 하나만 렌더링이된다.

exact는 정확하게 일치하는 경우에만 렌더링을 시키는 경우인데 switch와 함께 쓸수도 있다.

아래와 같은 경우에 쓰는데 주소가 겹치게되는 경우에 사용된다.

만약 exact가 '/'값에 없다면 '/game/Number/' 접근하게 될 경우 switch로 인해 하나만 렌더링이 되어야하기때문에 '/'로 ㄷ등록된 컴포넌트가 렌더링이되는 현상을 겪을 수 있다. 

<Switch>
    <Route exact path="/" render={(props) => <GameMatcher {...props} />} ></Route>
    <Route path="/game/Number" render={(props) => <GameMatcher {...props}/>} ></Route>
</Switch>

강의 유튜브 주소 :

https://www.youtube.com/watch?v=V3QsSrldHqI&list=PLcqDmjxt30RtqbStQqk-eYMK8N-1SYIFn



성능최적화

구글확장플러그인 devtools로 확인하여 테스트한 후에 최적화가 필요하면 진행한다.

최적화작업은 작업의 마지막에 한다.

 

useEffect와 useRef로 재렌더링이 되는 이유를 디버깅할 수 있는 방법이 있다.

ref를 만들어서 props나 state들을 넣고 비교하면서 검사하면된다,

import React, {useCallback, useEffect, useRef}from 'react';

const Test = () => {
    const Ref = uesRef([]);

    useEffect(() =>{
    
    	// 콘솔에 찍어보자
        console.log(a === ref.current[0], a === ref.current[1]);
        ref.cuffrent = [a, b, c, d];
        
    // a, b, c, d가 변경될때 useEffect가 실행
    },[ a, b, c, d ]);

}

 

React.memo는 컴포넌트를 기억할 수 있다.

컴포넌트자체를 기억시킬수 있다.

return(
    <>
        useMemo(() => <Test />, [ data[i] ]);
    </>
);

 

근데 이 전에 memo를 먼저 진행해보고 안되면 useMemo를 쓴다.

import React, {memo} from 'react'l

const Test = memo(() => {
    // .... 
});

자식 컴포넌트부터 진행하여 위로 올라오면서 확인하는것이 더 쉽다.

useMemo는 memo를 쓰고도 재렌더링이 될때 최후의 수단으로 쓸 수 있다.(이건 추후에 다시 공부)

 

contextAPI

useReducer는 redux에서 차용했으며 state가 여러개일때 하나로 관리하고 dispatch의 action을 통해 state를 변경할 수 있따. redux와의 차이점은 동기이나 비동기이냐의 차이이다.

react와 useReducer는 비동기이며 redux는 동기이다.

 

부모컴포넌트가 자식컴포넌트에게 props를 전달할때, 다중 관계를 해결하기 위해 contextAPI를 사용한다.

return(
    <>
        <Text dispatch={dispatch}/>
    </>   
)

이렇게 dispatch를 props로 내려주는 부분을 contextAPI를 통해 바로 접근할 수 있도록 해준다.

 

createContext Provider

contextAPI에 접근하고 싶은 컴포넌트들을 provider에 감싸주면된다.

import React, { createContext } from 'react';

const TestContext = creatContext({
    // 여기에 기본 값을 넣을 수 있다.
});
import React, { createContext } from 'react';

const TableContext= creatContext({
    // 여기에 초기값을 넣어야한다.
    tableData : [],
    dispatch : () => {}
    // 초기값에 의미가 없어서 데이터형태만 넣어줬다.
});

const Test = () => {
    return (
        <TextContext.Provider>
            <Test />
        </TextContext.Provider>
    );
};

 

데이터는 value에 넣어준다.

value는 자식컴포넌트에서 바로 접근할 데이터들이며 가져올때에는 useContext를 사용한다.

return (
        <TextContext.Provider value={{ tableData : state.tableData, dispatch:dispatch }}>
            <Test />
        </TextContext.Provider>
);
// 자식
import React, { useContext } from 'react';
import { TableContext } from './createContext한 컴포넌트';

const Test = () => {    
    const value = useContext(TableContext);

    // tableContext는 createContext한 것이고 
}

// 부모
export const TableContext= creatContext({
    // 여기에 초기값을 넣어야한다.
    tableData : [],
    dispatch : () => {}
    // 초기값에 의미가 없어서 데이터형태만 넣어줬다.
});

이렇게 해두면 value.dispatch로 접근할 수 있다.

const Test = () => {    
    const value = useContext(TableContext);
    
	// 구조분해해도된다
    const { dispatch } = useContext(TableContext);

 

contextAPI가 성능최적화에서 가장 힘들다.

아래처럼 쓰면 컴포넌트가 재렌더링이 될때마다 value 객체가 새로 재렌더링이 되는데, 이럴경우 성능에 문제가 생길수 있어서 캐싱을 해줘야한다.

useMemo를 사용해준다.

참고로 dispatch는 항상 안바뀌고 유지가되므로 2번째 인자로 전달하지 않아도 된다.

return (
        <TextContext.Provider value={{ tableData : state.tableData, dispatch:dispatch }}>
            <Test />
        </TextContext.Provider>
);
import React, { useMemo } from 'react';

conts Test = () => {
    const value = useMemo (() => {
        tableData : state.tableData, dispatch:dispatch
    },[ state.tableData ]);

    return (
        <TextContext.Provider value={value}>
            <Test />
        </TextContext.Provider>
    );
};
dispatch({ action을 생성하면된다 });

 

contextAPI를 쓰려면 value가 다 바뀌게된다.(재렌더링)따라서 value를 useMemo로 감싸준다.

memo를 쓰려면 하위컴포넌트 모두에게 적용해야한다.

const value = useMemo (() => {
        tableData : state.tableData, dispatch:dispatch
},[ state.tableData ]);

 

+ Recent posts