react-router
SPA을 위해 React의 Client-side routing을 위해 필요한 녀석이다.
대표적으로 BrowserRouter, Switch, Route 그리고 Link 정도를 import 해서 자주 쓴다.
오늘은 이 중에서 Route와 관련하여, 함께 쓸 수 있는 useParams 를 살펴보려 한다.
useParams는 Route 컴포넌트에서 path에 정의된 param의 이름에 req.param 을 매핑해주는 녀석이다.
아래와 같은 코드를 가정하면,
useParams()를 통해, Route 컴포넌트에서 path에 정의된 catId의 실제 req.param값을 Object로 받아올 수 있다.
// 'src/Router.tsx'
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Cat from "./routes/Cat";
import Dog from "./routes/Dog";
function Router() {
return (
<BrowserRouter>
<Switch>
<Route path="/cat/:catId">
<Cat />
</Route>
<Route path="/dog/:dogID">
<Dog />
</Route>
</Switch>
</BrowserRouter>
);
}
export default Router;
// 'src/routes/Cat.tsx'
import { useParams } from "react-router";
interface RouteParams {
catId: string;
}
function Cat() {
const { catId } = useParams<RouteParams>();
console.log(useParams<RouteParams>())
return <h1>Cat: {catId}</h1>;
}
export default Cat;
이 때, useParams를 뜯어보면 아래와 같은 코드를 볼 수 있다.
export function useParams<Params extends { [K in keyof Params]?: string } = {}>(): Params;
위 코드를 이해하기 위해서 먼저 알아야할 것은 keyof 의 의미이다. keyof는 말그대로 어떤 Type이 지니고 있는 key 그 자체를 의미한다.
interface Person {
gender: number;
name: string;
}
type PersonKeys = keyof Person; // "gender" | "name"
자 그럼 in keyof 를 알아보자.
A in keyof B는 B라는 Type에 존재하는 Key 각각을 A로 받아오는 것을 의미한다. 이러한 in keyof는 사실 Partial<T> 라는 official utility type 의 구현에 쓰이고 있다. 이를 좀 더 알아보자.
interface God {
name: string;
gender: number;
location: string;
}
interface PartialGod {
name?: string;
location?: string;
}
const demiGod:PartialGod = {
name : 'Jupyter'
}
위와 같이, 이미 존재하는 interface God의 key를 optional로 처리하고 싶은 경우, 아래와 같이 작성할 수 있다.
interface God {
name: string;
gender: number;
location: string;
}
type PartialGod = Partial<God>;
const demiGod : PartialGod = {
name:'Jupyter'
}
extends나 implements가 기존 interface를 그대로 받아와서 뭔가를 덧대는 느낌의 확장이라면, 위의 Partial은 기존의 인터페이스를 optional로 처리해주는 녀석이다. 근데 이때, Partial에 쓰이는 녀석이 바로 in keyof 이다.
type Partial<T> = {
[P in keyof T]?: T[P];
};
위 코드를 해석하자면, Partial<T> 라는 타입을 하나 선언하는데, T는 Generic 이므로 다른 Type이 올 수 있다. 따라서, Partial<God>으로 입력하면, 아래의 코드와 같이 작동하게 된다.
type Partial<God> = {
[P in keyof God]?: God[P];
};
따라서, God 이라는 Type에 있는 key 각각을 P로 받아온 뒤, 이 P를 프로퍼티 Key로 갖고, 그 프로퍼티 Value의 타입은 God의 해당 P 프로퍼티 Value의 Type과 동일하다는 의미가 된다.
객체의 type 지정에서 프로퍼티 Key 부분에 Array가 지정되는 문법인 아래 코드와 함께 천천히 생각해보길 바란다.
type ArryKey = {
[key : string] : string
}
따라서 결과적으로 Partial<God>은 아래와 같은 코드로 작동한다.
type Partial<God> = {
name?: string;
gender?: string;
location?: string;
};
자 그럼 다시 useParams로 돌아오자.
export function useParams<Params extends { [K in keyof Params]?: string } = {}>(): Params;
위 코드는 결국, Params Type을 { [K in keyof Params]?: string } = {}로부터 상속받아온 뒤, useParams의 return 값을 Params로 정해주는 코드이다.
그렇다면 { [K in keyof Params]?: string } = {} 이 코드는 무엇을 의미할까?
먼저 짚고 넘어갈 것은 상속이란 결국 제약을 의미한다는 점이다. 좋은 것만 쏙 골라서 상속 받을 수는 없다. 현실 세계에서도 그런 상속은 존재하지 않는다. 즉, 부모의 모든 요소를 자식이 구현해야만 한다.
그런 의미에서 Params에 존재하는 key에 대해서는 optional key를 가지며, 그 value는 string인 임의의 익명Type이 곧 Params의 부모 Type이 된다. 하지만 어쨌거나 이 복잡한 관계(?)를 지닌 부모Type의 값은 { }이다. 즉, 우리가 정해주는 Type은 결국 { }를 확장하는 셈이다.
굳이 이런 코드가 필요한 이유는, Params로 string이 아닌 다른 Type이 오지 못하도록 제한하기 위함이다.
또한, 이러쿵 저러쿵해서 결국 부모Type의 값이 { } 인 것은 아래와 같이 어떠한 Type도 전달되지 않았을 때 { }를 전달해주기 위함이다.
'Learning-Log > Computer Science' 카테고리의 다른 글
[TS] Typescript의 enum, const enum, as const 에 대해 알아보자 (0) | 2022.07.18 |
---|---|
[WSL2] Vmmem의 RAM 점유율 해결 방법 (0) | 2022.07.17 |
[JS] 모듈을 받아오는 import와 모듈을 내보내는 export (0) | 2022.07.15 |
[ReactJS] styled-components와 함께 하는 즐거운 ReactJS (0) | 2022.07.12 |
[NestJS] NestJS, 한 번 빠지면 벗어날 수 없는 마성의 늪(3) : NestJS를 이해하기 위한 First Class Function, Closure 그리고 Decorator (0) | 2022.07.11 |