반응형

리액트에서 styled components를 활용하여 전역 공통 css부분을 적용할 수가 있는데요.

이때 폰트를 적용하는 방법을 알아보고자 합니다.

 

프로젝트 로컬에 위치한 폰트 연결하기

먼저 프로젝트 내부에 존재하는 ttf, woff, woff2등의 웹 폰트 파일을 연결하는 방법입니다.

 

1. 폰트 파일 준비

- src/assets/fonts/Pretendard 디렉토리에 원하는 파일들을 위치시켰습니다.

 

이후, 해당 파일을 import하여 전역 스타일링을 할 GlobalStyle에서 사용을 합니다.

 

2. 전역 스타일링 파일 GlobalStyle.ts

import { createGlobalStyle } from "styled-components";
import PretendardExtraLight from "./assets/fonts/Pretendard/PretendardExtraLight.woff2";
import PretendardRegular from "./assets/fonts/Pretendard/PretendardRegular.woff2";
import PretendardBold from "./assets/fonts/Pretendard/PretendardBold.woff2";
import PretendardExtraBold from "./assets/fonts/Pretendard/PretendardExtraBold.woff2";

const GlobalStyle = createGlobalStyle`
    @font-face {
        font-family: 'Pretendard';
        font-weight: 200;
        src: url(${PretendardExtraLight}) format('woff2');    
        unicode-range: U+AC00-D7A3;
    }

    @font-face {
        font-family: 'Pretendard';
        font-weight: 400;
        src: url(${PretendardRegular}) format('woff2');    
        unicode-range: U+AC00-D7A3;
    }

    @font-face {
        font-family: 'Pretendard';
        font-weight: 700;
        src: url(${PretendardBold}) format('woff2');    
        unicode-range: U+AC00-D7A3;
    }

    @font-face {
        font-family: 'Pretendard';
        font-weight: 800;
        src: url(${PretendardExtraBold}) format('woff2');    
        unicode-range: U+AC00-D7A3;
    }
    body {
    	font-family: 'Poppins', 'Pretendard', sans-serif;
    }
`;

export default GlobalStyle;

- src/GlobalStyle.ts

 

import를 통해 폰트 파일 위치의 woff2 파일들을 import하고 @font-face에 적용을 해줍니다.

저는 한글에만 pretendard가 동작하도록 unicode-range를 설정하였습니다.

여기서 그대로 따라하다보면 import부분에서 woff2확장자에 대해 알 수 없기에 오류가 발생할 것입니다.

typescript에게 woff2확장자에 대해 인식할 수 있도록 파일을 추가 생성합니다.

 

3. fonts.d.ts 생성

declare module "*.woff2";
declare module "*.ttf";
declare module "*.woff";

- src/fonts.d.ts

 

ttf, woff도 사용 할 수도 있기에 추가해두었습니다.

저장을 하면 import 오류부분이 사라지는 것을 볼 수 있습니다.

 

4. GlobalStyle.ts 적용하기

최상단 컴포넌트인 index.tsx또는 App.tsx부분에 적용을 해주면 폰트가 바로 적용되는 것을 확인 할 수 있습니다.

import React from "react";
import styled, { ThemeProvider } from "styled-components";
import GlobalStyle from "./GlobalStyle";
function App() {

  return (
    <ThemeProvider theme={"mars"}>
      <GlobalStyle />      
    </ThemeProvider>
  );
}

export default App;

- src/App.tsx

반응형
반응형

 

원하는 동작 이미지


a태그에 View라는 텍스트가 들어간 동그란 버튼을 만들고 싶었다.

그 버튼 안에는 Hover 이벤트가 발생하면 파도가 차오르는 효과의 애니메이트 효과를 적용하였다.

 

이렇게 작성하면서 발생한 문제로 ::before 가상요소에 동그란 이미지가 빙글빙글 돌면서 파도현상으로 보이게 처리한건데 올라오면서 View라는 글씨가 덮어쓰여지게 되면서 보이지 않는 이슈가 발생하였다.

 

잘못된 동작... 가려진다

 

위 이미지처럼 백그라운드 요소가 올라오면서 가려지는 현상이였다.

 

이런 경우엔 z-index로 기존 요소를 높게주고, 가상요소인 before에 값을 처리한다고 하더라도 변화가 없다.

z-index는 같은 선상에 존재하는 요소들끼리의 보여주는 순서이기 때문이다.

 

이런 경우 같은 선상에 있는 after 가상요소를 추가하여 텍스트를 추가하여 처리하면 된다.

 

결과 소스

.goLink {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    bottom: 4rem;
    right: 3rem;
    width: 6rem;
    height: 6rem;
    font-size: 1.6rem;
    font-weight: 600;
    text-align: center;
    border: 2px solid rgba(255, 255, 255, 0.8);
    border-radius: 50%;
    overflow: hidden;
    transition: 0.5s;
    background-color: transparent;
}
.goLink:hover {
    border: 2px solid rgba(114, 196, 226, 0.6);
}
.goLink:hover::before {
    content: "";
    position: absolute;
    top: 1rem;
    left: 50%;
    transform: translate(-50%);
    width: 32rem;
    height: 32rem;
    border-radius: 13rem;
    background-color: rgba(114, 196, 226, 1);
    animation: wave 4s infinite linear;
}
.goLink:hover::after {
	color: #fff;
}
.goLink::after {
    content: "View";
    bottom: 4rem;
    right: 3rem;
    font-size: 1.6rem;
    font-weight: 600;
    text-align: center;
    color: rgba(255, 255, 255, 0.8);
    z-index: 9;
}
반응형
반응형

타입스크립트 프로젝트를 나타내느 tsconfig.json파일에 대해 간략하게 알아보고자 합니다.

tsconfig.json의 파일명은 바뀔수 없습니다. 또한, 해당 파일이 존재한다는건 타입스크립트 프로젝트라는 뜻입니다.

 

1. include

{
 "include": ["src"] // src디렉토리의 모든 파일을 확인한다.
}

include는 타입스크립트의 컴파일이 대상이 될 디렉토리들을 배열형태로 넣어주면 됩니다.

위의 예시로는 src디렉토리의 모든 파일을 컴파일하게 됩니다.

 

2. complierOptions

타입스크립트 대부분의 옵션들을 담당하며 타입스크립트의 요소들을 어떻게 구동시킬 것인지 작성하게 됩니다.

 

 

2-1. outDir

outDir은 컴파일 후 빌드된 js파일들이 위치할 디렉토리를 설정합니다. 보통은 build로 작성합니다.

해당 디렉토리가 없다면 빌드후 디렉토리 생성 후 빌드된 파일들을 제공합니다.

{
  "compilerOptions": {
    "outDir": "build" // 보통 build 디렉토리에 생성한다.
  }
}

 

 

2-2. target

target은 컴파일을 진행 할 자바스크립트 버전을 뜻합니다.

ES3, ES5, ES6, ES2022 등 최신 문법까지 작성가능하며, 보통은 모든 브라우저에서 ES6를 지원하기때문에, ES6를 쓰는것을 추천합니다. 너무 낮은 버전인 ES3로 작성하게되면 컴파일 후 작성된 JS의 양이 많이질 수 있습니다. 하지만 서버용으로 개발하면서 낮은 버전의 모든것을 호환하려면 ES3를 생각해보는것도 괜찮습니다.

그렇다고 또 최신의 ES2022의 버전 또한 모든 브라우저가 제공되는게 아닐 수 있기때문에, 안정적인 버전으로 사용하는게 좋습니다.

{
  "compilerOptions": {
    "target": "ES6"
  }
}

 

 

2-3. lib

lib은 개발시 개발환경의 코드를 지정하는 옵션입니다. 웹개발을 통한 브라우저의 정보를 받고 싶다면, DOM을 입력하여 (document.~, window.~)의 정보들을 받아서 사용할 수도 있고, 또한 어떤 버전의 API의 정보들을 받아서 사용하는지 지정할 수 있습니다. 백엔드 작업이 아닌 프론트 & 브라우저 작업이라면 꼭 DOM을 입력하여 개발을 진행하고, 해당 옵션이 없다면 그저 JS 자체의 코드만 버전에 맞춰 제공됩니다.

가장 멋진점은 원하는 버전을 작성하면, Javascript이지만 Typescript에 맞춰 call signature를 제공해주기 때문에, 자주 사용하지 않는 메소드등을 사용하더라도 typescript의 도움을 받을 수 있습니다.

 

데이터 입력은 배열 형태로 입력하며, 문자열로 입력합니다.

{
  "compilerOptions": {
    "lib": ["es6", "DOM"] // ES6 문법과 브라우저 환경에서 사용된다고 지정됨
  }
}

 

 

2-4. strict

strict은 true, false boolean타입으로 옵션을 설정합니다.

엄격 모드로 프로그램의 정확성을 더 강력하게 보장하는 것을 제공하고 광범위하게 타입 검사를 하게 됩니다.

해당 옵션을 키게되면 조금만 잘 못된 코드를 작성하게되면 타입스크립트는 오류가 발생했다고 알려주게 됩니다.

올바른 타입스크립트 작성을 위해 항상 true상태로 개발을 진행해야겠습니다.

{
  "compilerOptions": {
    "strict": true
  }
}

 

 

2-5. allowJs

allowJs는 js파일들을 타입스크립트에서 허용해준다는 뜻으로, true옵션이 되면 js파일의 함수들을 타입스크립트에서 바로 사용이 가능해집니다.

이것을 사용하게되면 declare파일이 없어도 함수를 추론해서 타입들을 정의해주는 것을 볼 수 있습니다.

{
  "compilerOptions": {
    "allowJs": true
  }
}

 

2-6. esModuleInterop

boolean값으로 true, false로 값을 가집니다.

true처리를 하게 되면, 이전에 개발되어 제공되는 CommonJS사양과 ES사양의 라이브러리를 가져와서 import로 사용할 때 ES모듈 사양으로 준수하여 CommonJS의 모듈도 정상적으로 가져올 수 있도록 도와줍니다.

이전에 개발된 CommonJS에는 export default의 처리가 없기 때문에 typescript에서 뭘 가져오라는건지 알 수가 없게되는데, 해당 모듈이 CommonJS사양이든 ES사양이든 일관성 있게 참조할 수 있도록 도와줍니다.

{
  "compilerOptions": {
    "esModuleInterop": true,
    "module": "CommonJS"
  }
}
import crypto from "crypto"; // 원래는 오류가 발생한다.

 

반응형
반응형

변수, 상수, 함수, 객체, 배열등 모든 정보의 데이터 타입이 무엇이고 리턴이 있다면 리턴타입은 무엇인지, 객체라면 어떤 정보로 이루어져 있는지 타입스크립트에선 모두 설명을 해주어야하고 그래야 타입스크립트로부터 데이터를 보호받고 자동완성 등의 혜택을 받을 수 있다.

 

type사용하기

const test: string = "hello";

type Message = string;
const test2: Message = "hi";

직접 자료형을 바로 적어줘도 되고, type에 정의해서 별칭을 정해주거나, 함수의 경우 call signature형태로 정의해서 직접 선언하는 함수는 깔끔 명료하게도 표현할수 있다.

 

 

call signature

//const add = (a:number, b:number) => a+b;

type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;

 

 

type, interface 차이점

여기서 자주 사용되는게 type키워드와 interface이다.

둘의 생김새는 매우 비슷하지만 약간의 차이점이 있다.

- type

//type으로 특정 값만 사용하도록 제한하기
type Team = "red" | "blue" | "yellow";
type Health = 1 | 5 | 10;

type Player = {
    nickname: string;
    team: Team; // 3가지의 값만 사용가능
    health: Health; // 3가지의 값만 사용가능
};

const nico: Player = {
    nickname: "nico",
    health: 10,
    team: "blue",
};

 

- interface

type Team = "red" | "blue" | "yellow";
type Health = 1 | 5 | 10;

interface IPlayer {
    nickname: string;
    team: Team; // 3가지의 값만 사용가능
    health: Health; // 3가지의 값만 사용가능
};

const shin: IPlayer = {
    nickname: "shin",
    health: 10,
    team: "blue",
};

일단 키워드명이 달라질 것이고, '=' 기호의 차이가 있다.

그리고 가장 큰차이는 interface의 경우 object, class의 모양을 특정하는데만 사용된다.

주로 react에서 자주 봤던 이유이다.(api등이나 그리려는 props들의 정보를 object형태로 자주 담아서 사용했다.

 

즉, 바로 아래 표현한거처럼 interface는 간단한 자료형 정의와 같은 행위는 불가능하다.

interface Hello = string; //Error

 

 

상속 표현 차이점

- type

type User = {
    name: string
}

type Player = User & {

}

const nico: Player = {
    name: "nico"
}

 

- interface

interface User {
    name: string
}

interface Player extends User {

}

const shin: Player = {
    name: "shin"
}

부모 속성으로 부터의 상속도 가능한데 아래와 같은 차이가 있다.

1. type은 &(and) 기호를 통해 가능하다.

2. interface는 extends 키워드로 상속을 받아서 사용한다.

 

같은 이름의 interface가 존재하면?

또한 interface에서만 가능한 기술로 같은 파일시스템 내에서 같은 이름의 interface를 중복으로 선언하면, 하나의 인터페이스로 합쳐준다.

interface User {
    nickname: string,
}

interface User {
    hp: number,

}

interface User {
    mp: number,
}

const warrior: User = {
    nickname: "shin",
    hp: 100,
    mp: 40
}

알아서 3가지의 타입을 합쳐서 가지고 있다. 이런 표현방식은 interface에서만 가능하다.

 

 

interface를 class에 상속하기

abstract을 통해 상속받는 class모습을 추상적으로 그려주고 필수적으로 작성해야하는 함수를 지정하거나 할수 있는데, 이런 형태를 interface를 통해서도 가능하다.

interface User {
    nickname: string,
    level: number,
    myInfo(): string,
    sayHi(name: string): string,
}

interface Human{
    health: number
}

class Player implements User,Human {
    constructor(
        public nickname: string,
        public level: number,
        public health: number
    ) { };

    myInfo() {
        return `${this.nickname}이며,  ${this.health}의 체력을 가지고 있습니다.`;
    }

    sayHi(name: string) {
        return `hello ${name}`;
    }
}

다만 상속받는 class의 생성자부분에서 접근제한자는 public으로만 생성 가능하게 됩니다.

자바스크립트로 변환될때의 이점으로 interface로 정의한 User, Human의 정보의 추적이 불가능합니다. interface라는 형태는 typescript에서만 존재하기 때문에 좀 더 가벼운 소스를 만들수 있고, extends는 하나의 추상클래스만 상속가능하지만 implments는 여러개의 interface를 상속받을수 있습니다.

반응형
반응형

javascript에서도 class는 자주 사용해보진 않았지만, 해당 개념은 알고 있어야하니 정리를 해봅니다.

객체지향의 자바를 해봅셨다면 아주 어렵게 느껴지진 않을겁니다. 거의 똑같은 느낌입니다.

 

 

class

typescript에서 class를 정의하는 방법은 간단합니다.

constructor인 생성자부분에서 변수의 접근 제한자 변수명: 타입; 형태로 작성합니다.

class Players {
  constructor(
    private firstName: string,
    private lastName: string,
    public nickname: string
  ) {}
}

const p1 = new Players("shinwoo", "park", "shin");

자바에서 많이 하던짓과 비슷합니다.

다만 javascript에서는 파라미터부분에 파라미터명들을 넣어주고, this.var1 = var1; 등을 해줘야하지만, typescript는 정의만으로 끝입니다.

 

이후 생성된 인스턴스를 사용해보면 public인 nickname만 접근 가능한 것을 볼 수 있습니다.

 

 

private값을 호출하거나 변경하고 싶다면?

firstName과 lastName은 private이므로 선언한 클래스 내부인 Players에서만 접근이 가능합니다.

해당 값을 가져오거나 변경하고 싶다면 setter, getter를 활용합니다.

class Players {
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string
    ) { }

    setFName(name: string) {
        this.firstName = name;
    }
    getFName() {
        return this.firstName;
    }
    getLName() {
        return this.lastName;
    }
}

const p1 = new Players("shin", "p", "shin");
p1.setFName("변경")
console.log(p1.getFName());
console.log(p1.getLName());

 

 

abstract

추상클래스는 다른 클래스들이 상속받는 용도로 사용되는 클래스입니다.

직접 인스턴스 생성은 불가능합니다.

(인스턴스 new형태로 클래스를 생성하는것을 말합니다.)

abstract class User {
  constructor(
    private firstName: string,
    private lastName: string,
    public nickname: string
  ) {}

  getFullname() {
    return `${this.firstName}${this.lastName}`;
  }
}

class Player extends User {}

const shin = new Player("shinwoo", "park", "shin");
shin.nickname = "shin2";
console.log(shin);
console.log(shin.getFullname());

getFullname인 추상클래스의 메소드도 상속받아졌기때문에 Player클래스에서 별다른 정의가 하나도 없는데도 자동으로 사용할수 있는것을 볼 수 있습니다.

 

주로 라이브러리등을 전달할때 해당 방식을 많이 사용합니다.

 

 

abstract method

추상메소드는 추상클래스 내의 추상메소드들을 의미합니다.

해당 메소드들은 추상클래스를 상속받는 클래스내에서 무조건 구현되어야하는 함수들을 의미합니다.

abstract class User {
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string
    ) { }
    abstract getName(): void;

    getFullname() {
        return `${this.firstName}${this.lastName}`;
    }
}

class Player extends User {
    //무조건 상속받는 클래스내에서 작성해야하는 메소드
    getName() {
        return this.getFullname(); //딱히 구현할게 없으니 그냥 같은 fullname을...작성함
    }
}

const shin = new Player("shinwoo", "park", "shin");
shin.nickname = "shin2";
console.log(shin);
console.log(shin.getName()); //이전과 동작은 동일하다. 같은걸 뱉으니까...

abstract class User내부의 abstract getName이 그런 경우입니다.

 

User를 상속받고 있는 Player클래스는 getName을 무조건 지정된 속성으로 작성해줘야합니다.

 

 

🧷 접근 제한자들의 접근 가능한 위치들

  선언 클래스 내부 상속받은 클래스 내부 인스턴스(new 사용)
private
protected
public
반응형
반응형

니콜라스 선생님의 타입스크립트 강의를 듣다가 제네릭을 추가적으로 정리해봅니다.

 

Generic

먼저 제네릭이란 다양한 모습의 함수를 지원하는 다형성을 만족시키기 위해서 나온 개념이다.

다양한 파라미터 자료형(컨크리트 타입)와 다양한 리턴값을 해당 함수에서 제공해야한다면 어떻게 작성해야 할까?

(*concrete type : 컨크리트 타입은 기본적으로 제공되는 타입들로 number, string, boolean, void, unknown등이 있다.)

 

제네릭을 사용하지 않는다면 아래와 같은 모습이 될 것이다.

 

 

- 일반 타입스크립트로 다형성을 만족하는 함수 만들기

type SuperPrint = {
    (arr: number[]): void
    (arr: boolean[]): void
    (arr: string[]): void
    (arr: (number|boolean)[]): void
}

const superPrint: SuperPrint = (arr) => {
    arr.forEach(i => console.log(i))
}

superPrint([1,2,3,4,5]); // 1번째
superPrint([true, false, true]); // 2번째
superPrint(["a", "B", "c"]); // 3번째
superPrint([1, 2, true, false]); // 4번째

각각 다양한 타입의 파라미터를 제공하는 모습이 생길때마다 call signature 정의부가 늘어난다.

number배열과 그것을 리턴하는 자료형, string 배열과 자료형, number, boolean이 혼합된 자료형 등등...

종류가 많아지면 점점 별칭이 많아질텐데 이런 경우가 많지는 않겠지만, 제네릭을 사용함으로써 함축시킬 수 있다.

(* call signature: 함수의 파라미터 구성과 return자료형을 표현한 type을 말한다.)

 

- Generic 사용해보기

<T>, <V> 형태로 주로 사용된다.

type SuperPrint = {
  <TypePlaceholder>(arr: TypePlaceholder[]): TypePlaceholder;
};

const superPrint: SuperPrint = (arr) => arr[0];

superPrint([1, 2, 3, 4, 5]); // 1번째
superPrint([true, false, true]); // 2번째
superPrint(["a", "B", "c"]); // 3번째
superPrint([1, 2, true, false]); // 4번째

각 배열의 첫번째 값을 리턴하도록 간단하게 작성해보았다.

 

여기서 이상한점으로 그럼 any랑 뭐가 다른거지? 라는 생각이 들 수도 있다.

그래서 바로 any로 작성해보고 차이점을 살펴보았다.

 

 

any와 Generic의 차이점은 무엇인가?

type SuperPrint = {
  (arr: any[]): any;
};

const superPrint: SuperPrint = (arr) => arr[0];

superPrint([1, 2, 3, 4, 5]); // 1번째
superPrint([true, false, true]); // 2번째
superPrint(["a", "B", "c"]); // 3번째
superPrint([1, 2, true, false]); // 4번째

똑같이 동작은 되지만, vs code에서 마우스 오버를 통해 각 메소드의 정보를 보면 바로 차이점이 눈에 보인다.

 

 

any는 모든 타입의 정보를 잃는다.

모든 메소드들 위에 올려보면 타입에 대한 정보가 존재하지 않는다 모든것이 any로 표현된다.

 

 

하지만 제네릭의 경우는 어떻게 보일까?

Generic은 각각 파라미터와 return타입을 표현해준다.

입력한 배열 정보에 의해 파라미터값과 return값의 타입의 정보를 보여주는걸 볼 수 있다.

 

 

마지막의 복잡한 정보도 마찬가지이다.

Generic은 각각 파라미터와 return타입을 표현해준다.

파라미터는 number, boolean이며 return또한 number, boolean임을 명시해준다.

 

그럼에도 이상함을 못느낀다면 아래와같은 예시는 어떨까?

type SuperPrint = {
	(arr: any[]): any;
};

const superPrint: SuperPrint = (arr) => arr[0];
const test = superPrint([1, 2, true, false]);
test.toUpperCase(); // ???! 1이라는 number한테 대문자로 변경하라는건 말이 안된다...

any는 저런 말이 안되는걸 오류로 감지하지 않는다.

Typescript의 보호를 받으면서 다형성을 유지하려면 generic 형태로 꼭 함수를 작성해주자.

generic은 잘못된 코드를 보호해주게 된다.

 


함수의 파라미터가 늘어날때 제네릭 표현법

파라미터가 늘어난다면 아래처럼 제네릭 이름만 정해서 늘려주면 된다.

type SuperPrint = {
  <T, V>(arr: T[], param?: V): T|V;
};

const superPrint: SuperPrint = (arr, param) => {
  if (param) {
    return param;
  } else {
    return arr[0];
  }
};

const test = superPrint([1,2,3,4], "test");
const test2 = superPrint(["hi", "hello"]);
console.log(test);
console.log(test2);

결과값

 

 

제네릭의 형태는 앞으로 react등의 라이브러리를 사용하다보면 이런 형태의 다양한 자료형을 제공하는 함수를 사용하면 볼 수 있을 것이다.

반응형
반응형
function myInfo(name: string): { name: string; age?: number } {
  return {
    name,
  };
}

myInfo라는 이름의 함수가 있을때, return값까지 정의하는 방법을 알아보겠습니다.

파라미터에는 (파라미터명 : 자료형) 형태로 사용합니다.

함수의 리턴값은 파라미터가 끝나고 : return 자료형을 입력합니다.

 

즉, 빨간부분은 파라미터 정의

초록부분은 return타입 정의부분입니다.

 

return타입의 object key, value가 많아진다면  복잡하게 보일텐데 별칭을 통해 아래와같이 처리도 가능합니다.

type IPlayer = {
  name: string;
  age?: number;
};

function myInfo(name: string): IPlayer {
  return {
    name,
  };
}

const shin = myInfo("shin");
console.log(shin);
console.log((shin.age = 13));

파라미터가 필수값이 아니라면 ?를 꼭 붙여서 optional처리를 해줍니다.

반응형
반응형

react 강의를 학습하면서 최근에 제일 많은 차이점이 발생한 것이 router-dom으로 보였다. 5버전과 6버전간에 버전 차이도 꽤 크고 사용법이나 사라진 컴포넌트 함수등이 발생해서 차이점을 알아보고자 한다.

 

React Router Dom V6

react-router-dom 6.4 설치

npm i react-router-dom@6.4

 

 

BrowseRouter, Routes, Route 기본적인 구조

- Router.tsx

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Header from "./components/Header";
import About from "./screen/About";
import Home from "./screen/Home";

function Router() {
  return (
    <BrowserRouter>
      <Header />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

export default Router;

v5와 달라진점

1. Switch대신 Routes를 사용한다.

2. Route 컴포넌트 element prop에 컴포넌트 명을 입력한다.

 

 

createBrowserRouter

기존 v5와 비슷하게 생긴 BrowseRouter를 사용하여 라우터 표현을 해도 되지만, 아주 새로운 방식으로 라우터를 작성할 수 있다.

 

Router.tsx

import { createBrowserRouter } from "react-router-dom";
import About from "./screen/About";
import Home from "./screen/Home";
import Root from "./Root";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    children: [
      {
        path: "",
        element: <Home />,
      },
      {
        path: "about",
        element: <About />,
      },
    ],
  },
]);

export default router;

JSX문법 형태로 사용하지 않는다.

createBrowserRouter 함수 파라미터에 Router를 array형태로 표현한다.

 

index.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import Router from "./Router";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <RouterProvider router={Router} />
  </React.StrictMode>
);

먼저 RouterProvider를 index.tsx에서 추가해주고 router prop에 생성한 Router 함수를 넣어준다.

 

Root.tsx

import React from "react";
import { Outlet } from "react-router-dom";

function Root() {
  return (
    <div>
      <h1>Root입니다.</h1>
      <Outlet />
    </div>
  );
}

export default Root;

또한 Root에서 자식노드를 보여줄 수 있도록 최상단 부모에서 Outlet을 선언해준다.

Outlet덕에 자식 컴포넌트가 표현되어 Home이 보인다.
About경로도 잘 노출이 된다.

 

반응형