2023. 7. 7. 12:34ㆍ기타
이번 과제를 해결하기 위해 고민했던 흔적들을 남겨보려고 한다.
1. react-router-dom 뜯어보기
react-router-dom 이라는 라이브러리는 많이 사용했었다. 다만 어떻게 돌아가는지 구조에 대해서는 단 한번도 생각해본 적이 없었는데 이번 과제를 통해서 처음으로 뜯어봤다.
https://github.com/remix-run/react-router
위의 사이트가 바로 react-router의 코드를 볼 수 있는 github 주소이다.
일단 packages로 들어갔다.
여기서 react-router - lib 까지 들어갔다.
일단은 Router, Route에 대해 살펴보기 위해 component.tsx 로 들어갔다.
먼저 Router 검색
export function Router({
basename: basenameProp = "/",
children = null,
location: locationProp,
navigationType = NavigationType.Pop,
navigator,
static: staticProp = false,
}: RouterProps): React.ReactElement | null {
// 생략
return (
<NavigationContext.Provider value={navigationContext}>
<LocationContext.Provider children={children} value={locationContext} />
</NavigationContext.Provider>
);
}
이런 내용을 찾았다. context.provider 를 이용해서 만드는 것 같다.
다음으로 Route 검색
export function Route(_props: RouteProps): React.ReactElement | null {
invariant(
false,
`A <Route> is only ever to be used as the child of <Routes> element, ` +
`never rendered directly. Please wrap your <Route> in a <Routes>.`
);
}
Route는 단순하게 컴포넌트를 그려주는 용도인 것 같다. 그리고 invariant 내용을 보니 Route를 사용하려면 Routes가 필요한 모양이다.
그럼 Routes 검색
export function Routes({
children,
location,
}: RoutesProps): React.ReactElement | null {
return useRoutes(createRoutesFromChildren(children), location);
}
Routes는 자식과 경로를 useRoutes라는 hook으로 보낸다.
그럼 hooks.tsx 폴더에서 useRoutes를 찾아보자.
export function useRoutes(
routes: RouteObject[],
locationArg?: Partial<Location> | string
): React.ReactElement | null {
return useRoutesImpl(routes, locationArg);
}
흠........ useRoutesImpl 도... 찾아보자.......
export function useRoutesImpl(
routes: RouteObject[],
locationArg?: Partial<Location> | string,
dataRouterState?: RemixRouter["state"]
): React.ReactElement | null {
// 생략
if (locationArg && renderedMatches) {
return (
<LocationContext.Provider
value={{
location: {
pathname: "/",
search: "",
hash: "",
state: null,
key: "default",
...location,
},
navigationType: NavigationType.Pop,
}}
>
{renderedMatches}
</LocationContext.Provider>
);
}
return renderedMatches;
}
Context API로 상태 관리하고 Router - Routes - Route ? 이런 식으로 구조가 짜여지는 거 같다는 생각이 들었다.
2. 과제 진행
- 진짜 막막했다. 어디서부터 시작해야할 지 생각도 못하다가 예전부터 사용했던 react-router 를 생각했다.
일단 Router랑 Route 부터 구현을 시작했다.
App.tsx
import {Router} from "./components/Router"
import {Route} from "./components/Route";
import Root from "./pages/Root";
import About from "./pages/About";
function App() {
return (
<>
<Router>
<Route path="/" element={<Root />} />
<Route path="/about" element={<About />} />
</Router>
</>
)
}
export default App;
늘 봐왔던 구조로 만들고 Router랑 Route를 만들었다. 거의 베끼다시피 했다..........
Router.tsx
import React, { useState, useEffect } from "react";
import { RouteProps } from "./Route";
interface RouterProps {
children: React.ReactNode;
}
export const Router = ({ children }: RouterProps) => {
const [path, setPath] = useState(location.pathname);
const routes = React.Children.toArray(children) as React.ReactElement<RouteProps>[];
useEffect(() => {
const handleSetPath = () => {
setPath(window.location.pathname)
};
window.addEventListener('popstate', handleSetPath);
return () => window.removeEventListener('popstate', handleSetPath);
}, []);
return routes.find((route) => route.props.path === path);
}
- Router 안에 자식 요소들을 받고, 현재 경로의 주소값을 state로 만들었다. 그리고 popstate 이벤트가 발생하면 path의 값이 변경되도록 했다.
- 이후 현재 경로와 path의 값이 동일하다면 값을 돌려준다!
Route.tsx
export interface RouteProps {
path: string;
element: React.ReactNode;
}
export const Route = ({ path, element }: RouteProps) => {
return window.location.pathname == path? <>{element}</> : null;
}
- 현재 경로랑 path의 값이 동일하다면 element를 렌더링한다.
useRouter.tsx
export const useRouter = () => {
const push = (path: string):void => {
window.history.pushState(null, '', path)
window.dispatchEvent(new PopStateEvent("popstate"));
};
return { push };
}
- 경로를 받아 history 에 넣어주고, popstate 이벤트가 발생하면 dispatch를 진행하도록 했다.
3. 과제 후 느낀점
나만 못하는 건 아닐지도??
한 두명씩 과제 제출을 하는데 다들 잘하는 사람들인가보다 라는 생각도 들고 나만 못하나 라는 생각도 들었다.
근데 첫 강의 때 참여한 사람이 거의 천명 가까이? 들었던 거 같은데 제출한 사람은 50명도 안되었다. 막바지에 가면 늘기야 하겠지만 생각보다 엄청 적은 사람이 제출했다.
그리고 다른 분들의 코드를 보니 뒤로 갈수록 조금씩 비슷해진다는 느낌이 들었다. 어쩌면 나처럼 참고해서 작성하거나 거의 베끼지 않았을까란 생각도 들었다.
한번 더 작성해봐야지!!!
위의 코드는 context API 를 사용하지 않은 코드다. 좀 시간이 지난 다음에 이번 과제를 다시 한번 Context API를 이용해서 작성해볼 예정이다. 작성하고 난 다음에 다시 블로그에 올려야겠다.
앞으로의 공부방법
이번 강의를 통해서 어떤 방식으로 프론트엔드 개발에 대해 공부해야 하는가에 대해서 생각해볼 수 있었다. 궁금한 내용이 있다면 검색해보고, github 자료가 있다면 코드 까보고, 다시 검색하고.... 무한 반복!!
중간중간 블로그에 정리해놓으면 될 거 같다.
진짜 어렵다.
근데 나만 어려운 건 아닌 듯 싶다.
4. 사용한 함수 정리
window.history.pushState(state, unused, url)
- state : 브라우저 이동 시 넘겨줄 데이터
- unused : 변경할 브라우저 제목(필요없다면 null 이나 '')
- url : 변경할 주소
window.history.pushState() 을 이용하면 페이지 이동 없이 주소만 바꿔준다. (뒤로가기 버튼 활성화)
브라우저 페이지가 이동하게 되면 window.onpopstate 이벤트 발생하는데 pushState() 는 이벤트가 발생하기 않고 앞/뒤 가기를 클릭했을 때만 popstate 발생.
window.dispatchEvent(event)
- dispatchEvent(event)는 event를 실행시키는 함수다.
new PopStateEvent(event)
- popstate는 윈도우 history가 변경되면 이벤트가 발생한다.
※ 참고사이트
- https://kwangsunny.tistory.com/28
- https://ko.javascript.info/dispatch-events
- https://developer.mozilla.org/ko/docs/Web/API/Window/popstate_event
- https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent
5. 작성한 github 주소
작성한 github 주소 : https://github.com/taehankim-dev/preonboarding_fe_challange/tree/main
'기타' 카테고리의 다른 글
Vite + Firebase 로 만든 프로젝트를 Github 배포하면서 생긴 일. (2) | 2023.10.17 |
---|---|
플로이드 와샬 알고리즘 (0) | 2023.07.26 |
Next.js로 마크다운 블로그 만들기 (0) | 2023.07.11 |
[원티드 프리온보딩 프론트엔드 챌린지] 사전과제 (0) | 2023.06.14 |