기술 메모장

React Router를 직접 만들어보는 과정 (with 프리온보딩 FE 챌린지 10월)

ghoon99 2022. 10. 9. 17:57

 

8월에 참여했던 FE 프리온보딩 챌린지.. 

https://ghoon99.tistory.com/88

 

원티드 프리온보딩 챌린지 8월 참여 후기

원티드에서 운영하는 프로그램인 프리온보딩 챌린지, 8월 첫주 부터 2주동안 강의가 진행되었다.  마침 무언가 해볼 것들을 찾고있었는데 커리큘럼도 너무 좋아보였고, 이 기회에 리액트 쿼리

ghoon99.tistory.com

 

너무 만족했었고 10월에도 진행한다고 해서 바로 신청을 했다. 이번 주제는 CSR/SSR with Next JS 이다. 

첫 시간에서는 MPA , SSR , SPA , CSR 에 대한 내용을 다뤘다. 리액트만으로 라우팅을 구현해보는 것이 과제로 나왔다.

 

혹시 완성된 코드가 궁금하면 여기서 확인하면 된다.

https://github.com/GHooN99/vanilla-react-router

 

GitHub - GHooN99/vanilla-react-router: [history api 등을 이용한 Vaniila React Router 구현해보기] - 원티드 프리온

[history api 등을 이용한 Vaniila React Router 구현해보기] - 원티드 프리온보딩 FE 챌린지 3차 1회 과제 - GitHub - GHooN99/vanilla-react-router: [history api 등을 이용한 Vaniila React Router 구현해보기] - 원티드 프리온

github.com

 

 

과제를 진행하며 동시에 노션에 어떤 생각을 하면서 코드를 작성했는 지를 기록했다. 이 글은 그것을 다듬어서 정리한 글이다.

 

그래서 이 글은 어떤 개념을 설명하고자 하는 목적을 가진 글은 아니다. 한 사람이 어떤 기능을 구현할 때 이러한 사고 과정을 거쳤고

문제를 해결해 나가는 과정을 중심으로 보면 좋을 것 같다. 

 

특정 개념에 대해서 설명은 생략하지만 그 개념을 공부할 수 있는 링크로 대체하였다.

 

 

시작!

 

목표 설정

MPA(Multi Page Application) 는 요청 경로에 해당하는 다른 문서를 받아온다. SPA는 페이지는 1개 (Single Page Application), JS 로 처리한다. 한 페이지에서 다른 경로일때 다른 페이지를 보여지는 것 처럼 만들어야 한다.

 

⇒ 요청 경로에 맞는 컴포넌트를 보여주면 해결 될지도 모르겠다

 

목표 : 주소에 따라 다른(지정한) 컴포넌트를 보여준다.

 

과제 코드 구조 분석, Route 컴포넌트

과제 요구사항 중.. 꼭 이 형태는 아니여도 좋지만 이 형태로부터 컴포넌트 구조를 추측해 나가는 과정을 거쳤다.

과제에서 제시한 틀이 이미 있었다.

 

Router 컴포넌트 내부의 Route 컴포넌트는 요청경로(path)와 해당 컴포넌트(component)를 넘겨주어 어떤 기능을 수행하는 컴포넌트

라는 것이 선언적으로 나타나 있는 것 같다.

 

이 Route 컴포넌트의 기능은 아마도

요청경로(path=”/”)에 따라 해당 컴포넌트(component={<Root/>})를 렌더링한다 가 될 것 같다.

// Route.tsx
/* 지정한 path와 현재 경로가 같을 때 지정한 컴포넌트를 렌더링한다. */
const Route = ({ path, component }: RouterProps) => {
  if (window.location.pathname !== path) return null;

  return <>{component}</>;
};

window.location.pathname 으로 현재 pathname 을 가져올 수 있었다.

https://developer.mozilla.org/en-US/docs/Web/API/Window/location

 

Window.location - Web APIs | MDN

The Window.location read-only property returns a Location object with information about the current location of the document.

developer.mozilla.org

이것과 props 로 전달해준 path 과 같으면 컴포넌트를 렌더링하게 만들었다.

주소창에 입력하면 / 에서는 rootpage , /about 에서는 about page 가 잘 보여진다.

/ , /about 을 직접  주소창에 쳐서 들어가봄 

 

 

페이지 이동 Link 컴포넌트

그럼 페이지 이동을 구현해보자

 

MPA 에선 <a> 태그로 이동 ⇒ 해당 경로의 페이지 자체를 네트워크 통신으로 받아온다. (GET /about  about.html..?)

만약 SPA 에서 <a> 태그를 이용하면 , 해당경로의 다시 페이지를 요청할 것 ⇒ a 태그의 기본 동작을 취소 시켜야함

브라우저에 나타나는 주소만 바뀌게 하여 지정된 컴포넌트를 불러올 수 있게 해야한다.

 

SPA 에서 페이지 이동 할수있는 간단한 a 태그를 Link 컴포넌트로 만든다.

// Link.tsx
// 사용하는 쪽 <Link to="/path">text</Link>
const Link = ({ to, children }: LinkProps) => {
  const handleLinkClick = (event: MouseEvent<HTMLAnchorElement>) => {
    event.preventDefault();
    // TODO 서버로 요청을 보내는 기본동작은 막고 브라우저에 나타나는 주소만 바뀌게 해야함
    // ?
  };

  return (
    <a onClick={handleLinkClick} href={to}>
      {children}
    </a>
  );
};

 

https://developer.mozilla.org/en-US/docs/Web/API/History_API

 

History API - Web APIs | MDN

The DOM Window object provides access to the browser's session history (not to be confused for WebExtensions history) through the history object. It exposes useful methods and properties that let you navigate back and forth through the user's history, and

developer.mozilla.org

History api 페이지 방문 기록에 대한 접근 방법과 메서드를 제공한다. 여러 메소드들이 있는데..

window.history.pushState()를 이용하여 주소만 바꿔보자

// Link.tsx
const Link = ({ to, children }: LinkProps) => {
  const handleLinkClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
    event.preventDefault();
    // TODO 서버로 요청을 보내는 기본동작은 막고 브라우저에 나타나는 주소만 바뀌게 해야함 
	  window.history.pushState("","",to);
  };

Link 태그를 클릭하면 브라우저의 주소만 바뀐다. 네트워크 요청없이…!

하지만 주소(window.location.pathname) 이 바뀌었음에도 불구하고 화면은 바뀌지 않는다.

⇒ 화면 다시 렌더링 되지 않았다. ⇒ 값(pahname) 이 변하면 리렌더링이 발생해야 한다.

⇒ 리액트 상태 state 가 변하면 화면을 다시(리렌더링) 그린다.

 

결국 pathname 를 상태(react의 state) 로 관리해야 한다는 해답을 얻었다.

 

 

 

 

Router 컴포넌트

그럼 이 상태는 어디에서 관리하냐??

pathname , 최상단 (전역)에서 관리해줘야 하는 느낌이 든다 ⇒ 느낌적인 느낌.. 왜요? 더 구체적인 왜? 필요

⇒ 생각해보니 꼭 최상단이 아니였다. Route 내부에서 window.location.pathname 을 직접 접근하여 사용중이다.

 

각 Route 컴포넌트에서 이 상태를 이용해야 하니 Route 컴포넌트의 부모 컴포넌트인 곳에서 상태를 만들어 각 Route 에 내려주어도 된다.

 

아까 틀 구조를 다시 보자

root.render(
  <Router>
    <Route path="/" component={<Root />} />
    <Route path="/about" component={<About />} />
  </Router>
);

Router 에서 pathname 상태를 만들어 자식 컴포넌트에서 보내주는 것 같은 구조다.

Router 컴포넌트는 route 컴포넌트들을 감싼 컴포넌트이다. 현재 경로 상태 를 들고 있는 컴포넌트가 될 것 이다.

자식 컴포넌트라면 어디서든지 pathname 을 조회하고 수정을 가할 수 있어야 할 것 같다. ( ..왜?)

 

About 페이지 내부에는 여러 컴포넌트들이 모여 하나의 페이지를 만들어 내고 있을 것이다.

// About.tsx 중 .. 
<Header/>
<Navbar/> 
<Footer/>

/// Navbar 컴포넌트 
navItems.map(item=><Menu item={item}/> 

// 그리고 Menu 컴포넌트
 여기서 pathname state 를 수정해야 한다면...? 
<li><<Link to="/">Home</Link></li>

(Link 컴포넌트 내부에는 currentPathname 을 수정해야 하는 로직이 들어가 있다.)

(페이지 깊숙한 곳에서 setCurrentPath(”/”) 를 해야 한다면… state , setState 들을 props 로 계속 내려줘야 할 것이다.)

props drilling 이 필연적으로 일어날 것이다.

그래서 context api 를 이용하여 관리하기로 해보았다. (바로 위 문장이 이유가 됨)

// Router.tsx
import { useState,createContext, type ReactNode } from "react";

interface RouterContextValue {
  currentPath: string;
  changePath: (to: string) => void;
}

export const RouterContext = createContext<RouterContextValue | null>(null);

interface RouterProps {
  children: ReactNode;
}

const Router = ({ children }: RouterProps) => {
  const [currentPath, setCurrentPath] = useState<string>(
    window.location.pathname
  );

  const changePath = (to: string) => {
    /* 브라우저의 주소를 바꿈 */
    window.history.pushState("", "", to);
    /* 리렌더링을 위한 path 상태 업데이트 */
    setCurrentPath(to);
  };

  const contextValue: RouterContextValue = {
    currentPath,
    changePath
  };

  return (
    <RouterContext.Provider value={contextValue}>
      {children}
    </RouterContext.Provider>
  );
};

export default Router;

context api 로 현재 pathname 을 관리해주었다.

 

 

아까 window.location.pathname, window.history.pushState() 으로 쌩으로 관리했던 곳을 변경한다.

// Link.tsx
  **const value = useContext(RouterContext);
  if (value === null) throw new Error("Router컴포넌트 내부에서 사용해주세요!");

  const { changePath } = value;**
  const handleLinkClick = (event: MouseEvent<HTMLAnchorElement>) => {
    event.preventDefault();
    // path를 to 로 변경시겨야 함
    **changePath(to);**
  };

// Route.tsx
**const value = useContext(RouterContext);
  if (value === null) throw new Error("Router컴포넌트 내부에서 사용해주세요!"); // 어? 겹치네?** 

  **const { currentPath } = value;**
  **if (currentPath !== path) return null;**

  **return <>{component}</>;**

이제 Link 를 눌러 페이지를 이동하면 , 주소도 변경되고 , 화면도 변경된다

 

 

 

 

뒤로가기 구현

이제 브라우저의 뒤로가기 버튼 , 앞으로 가기 버튼을 이용하여 페이지를 이동하는 것을 구현한다.

지금까지의 구현 결과는..

 

뒤로가기를 누르면 path 는 바뀌는데 화면은 안바뀜 ⇒ state 가 변경되지 않았기때문

path 가 변경되는것을 감지해야 함 ( 뒤로가기,앞으로가기 이벤트를 감지해야함) ⇒ 감지 하여 그때 current path 를 바꿔줘야함)

⇒ 바꿔주면 리렌더링, 화면에 반영

 

window.history.popState event 로  앞으로,뒤로 가기 이벤트를 감지 할 수 있었다.

https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event

 

Window: popstate event - Web APIs | MDN

The popstate event of the Window interface is fired when the active history entry changes while the user navigates the session history. It changes the current history entry to that of the last page the user visited or, if history.pushState() has been used

developer.mozilla.org

 

// Router.tsx 에 다음과 같이 수정 

// componentDidMount
useEffect(() => {
    const handlePopStateEvent = (event: PopStateEvent) => {
      console.log("변경 감지", event.state);
			// pushState 에서 보내준 state 꺼내오기 
      setCurrentPath(**event.state?.path** ?? "/");
    };
    window.addEventListener("popstate", handlePopStateEvent);

	// componentWillUnmount
    return () => {
      window.removeEventListener("popstate", handlePopEvent);
    };
  }, []);

const changePath = (to: string) => {
    /* 브라우저의 주소를 바꿈  첫번째 인자는 popState event 에서 state 를 꺼내갈 수 있게 */
    window.history.pushState(**{ path: to }**, "", to);
    /* 리렌더링을 위한 path 상태 업데이트 */
    setCurrentPath(to);
  };

여기까지 하니 요구사항 구현은 다 된것 같다..!

 

 

 

useRouter 커스텀 훅

context 에서 값을 빼온 후 리턴해준다.

// hooks/useRouter.ts
import { useContext } from "react";
import { RouterContext } from "@components/Router";

type useRouterHookResult = {
  push: (path: string) => void;
  pathname: string;
};

export const useRouter = (): useRouterHookResult => {
  const value = useContext(RouterContext);
  if (value === null) {
    throw new Error("Router 컴포넌트 내부에서 호출하세요!");
  }

  const { currentPath, changePath } = value;

  const pathname = currentPath;
  const push = (path: string) => {
    changePath(path);
  };

  return {
    push,
    pathname,
  };
};

그럼 아까 이부분은 훅으로 대체되어서 중복되는 코드가 줄더라..

// Link.tsx

//  ~~const value = useContext(RouterContext);
 // if (value === null) throw new Error("Router컴포넌트 내부에서 사용해주세요!");~~
 // ****~~const { changePath } = value;~~

  const router = useRouter();

  const handleLinkClick = (event: MouseEvent<HTMLAnchorElement>) => {
    event.preventDefault();
    // currentPath를 to 로 변경시겨야 함
    **router.push(to);**
  };

// Route.tsx**
// ~~const value = useContext(RouterContext);
//  if (value === null) throw new Error("Router컴포넌트 내부에서 사용해주세요!"); // 어? 겹치네?~~ 
// ~~const { currentPath } = value;

	const router = useRouter();
  if (router.pathname !== path) return null;

  return <>{component}</>;

 

 

 

 

보완해야 할 점 , 더 생각해볼 점

실제로는 더 복잡한 객체..

실제로 context api 로 관리하였던 curruntPath 는 주소 문자열 하나가 아니라

다양한 값을 가지고 있는 객체가 있을 것이다.

 

 

type Path = { ... }
interface RouterContextValue {
	pathObject : Path; 
  changePath: (nextPathState:Path) => void;
}

상태값이 실제로는 더 복잡하게 공유되고 있을 것이고 그에 따라 여러 커스텀 훅도 생길 것 같다.

 

 

Route 컴포넌트에서 렌더링 로직을 처리하는 것이 맞는 걸까?

Router 컴포넌트는 상태값을 공유하고, 페이지 이동 이벤트를 감지해 상태를 변경해주는 Provider 역할 컴포넌트

Route 컴포넌트는 요청경로(path)에 따라 해당 컴포넌트(component)를 렌더링하는 역할의 컴포넌트이다.

Route 컴포넌트 는 props 로 보내준 컴포넌트 혹은 null 을 리턴하는 상황이다.

// index.tsx 
root.render(
  <Router>
    <Route path="/" component={<Root />} />
    <Route path="/about" component={<About />} />
  </Router>
);

// 주소가 / 이면 
root.render(
  <Router>
    <Route path="/" component={<Root />} /> => Root 
    <Route path="/about" component={<About />} /> => null
  </Router>
);

// 주소가 /about 이면 
root.render(
  <Router>
    <Route path="/" component={<Root />} /> **=> null**
    <Route path="/about" component={<About />} /> => About
  </Router>
);

react devtools extension 으로 본 컴포넌트 트리?

react devtools extension 으로 본 컴포넌트 트리?

나는 지금 렌더링하고자 하는 Route 컴포넌트 1개만 보여주면 될 거같은데.. about 에서는 Route 가 2개가 보인다.

만약

<Router>
    <Route path="/" component={<Root />} /> => null
    <Route path="/about" component={<About />} /> => null
   ...100개 
		<Route path="/last" component={<Last />} />
</Router>

// 여기서 /last 에 들어가면 위에 null 이 100개 가 되는데..

이거 상관이 없나…????  찝찝하긴 하다.

또 페이지가 변경될 때마다 div id=”root” 부터 아래까지 전체가 깜빡인다. 뭔가 잘못된게 느껴진다..

잘못된게 맞을까? 왜 잘못되었을까? 어떻게 고쳐야 할까?

 

 

context api 의 리렌더링 이슈

context API 의 문제점이 존재(불필요한 리렌더링 발생 가능성 존재)한다고 한다.

렌더링 최적화가 안된다는것인데 이를 state Provider , setState(dispatch) Provider 로 쪼개서 불필요한 리렌더링을 줄이는 방법을 들어본 것 같다.. path 에 의한 꼭 필요한 리렌더링은 일어나되 , 불필요하게 딸려오는 리렌더링을 줄여 렌더링 최적화를 할 수도 있어보인다.

잘못된 경로의 요청 처리 필요

Route 컴포넌트로 지정하지않은 , 존재하지 않는 경로에 접속하면 404 페이지를 띄우게 처리도 추가해야 할 것이다.

 

 

 

 

 

강의 이후

강의 시간, 강사님께서 과제 피드백을 해주시면서  내가 의문을 가진 부분이였던  Route 컴포넌트에서 렌더링 로직을 처리하는 것이 맞는 걸까?  에 대한 나름대로 해답을 얻을 수 있었다.

강의 중. 다른 분의 과제 코드에서 아이디어를 얻었다.!

 

 

내 코드에 문제는 …

Route 컴포넌트에서 현재 경로와 비교하는 로직이 들어있었고..

/* 지정한 path와 현재 경로가 같을 때 컴포넌트를 렌더링한다. */
const Route = ({ path, component }: RouterProps) => {
  const router = useRouter();
  if (router.pathname !== path) return null;

  return <>{component}</>;
};

강의 전 내 코드에 대한 의문점..

결국 이부분이 문제가 있었던 것이다.

 

null 을 리턴하는 하나의 컴포넌트가 100개 찍히는 것... 

null 을 리턴하는 컴포넌트라도 그 안에 useEffect 가 돌아간다고 한다. (컴포넌트가 마운트는 됨..?)

 

아예  컴포넌트를 마운트를 시키지 않으려면…. 어디선가 조건에 맞는 컴포넌트를 골라서 렌더링 할 컴포넌트만 뱉어줘야할 것 같다.

 

 

 

그럼 Router 컴포넌트에서 해볼까?

그래도 될거같지만 Router 컴포넌트는 Context Provider 컴포넌트로서 남겨놔야

한 컴포넌트에 다른 로직들이 쌓이는 걸 막을 수 있을 거 같다. (관심사의 분리측면)

 

Router 는 이벤트를 감지하고 , path 를 update 해주고, 하는 역할

Routes 는 Route 컴포넌트들 중에 지금 렌더링 해야 하는 것만 렌더링 해주는 역할

 

Route 는 내부에는 아무것도 하지 않음 ( 컴포넌트를 받아 컴포넌트를 리턴) 심지어 props 로 준 path 는 내부에서 사용하지도 않음

아무것도 안하는 껍데기 일 뿐이지만

 <Route path="/" component={<Root />} />

/ 라는 곳에서 <Root/> 라는 컴포넌트를 보여준다. 라고 선언적으로 나타낼 수 있는 코드 조각이 될 수 있겠다.

 

 

 

이렇게

<Router /> → <Routes /> → <Route />

라는 구조가 완성되는구나!

 

Router as BrowserRouter 로 import 하면 이렇게 구조가 나온다..!

 

 

Routes  컴포넌트를 작성해보자

Route 에 존재하였던 로직 (currentPath 와 props.path 가 같으면 보여주자!) 을 옮겨오면 된다.

 

그리고 Routes 자식들을 돌면서 저 로직을 하나하나 검사해주고

최종적으로 조건이 맞는 , 렌더링할 컴포넌트들만 리턴해주면 된다!

interface RoutesProps {
  children: ReactNode; // ReactNode | ReactNode[]
}

원래 처럼 ReactNode 로 줬지만 얘들이 여러개가 될 수도 있고.. 그걸 나타내야 한다.. ReactNode[] 가 됨

ReactNodeArray 라는 것도 있었지만 deprecated 되었다.

 

/* 자식 컴포넌트들을 찾아간다.*/
/* 지정한 path와 현재 경로가 같을 때 컴포넌트를 렌더링한다. */
const Routes = ({ children }: RoutesProps) => {
  const { pathname: currentPath } = useRouter();

  const currentRenderComponent = (component: ReactNode): boolean => {
			// TODO Route 컴포넌트에 있던 로직을 여기에 
  };

	// 조건에 맞는 것들만 리턴해준다! 쓸모없이 null 을 리턴하는 컴포넌트를 마운트 할 필요가 없어짐 
  return <>{children.find(currentRenderComponent)}</>;
};

export default Routes;

 

children 을 직접 접근해서 Routes 컴포넌트 하위의 Route 컴포넌트를 객체? 로서 직접 접근을 해야 했다.

하지만 ReactNode 타입으로 지정된 component 변수에서 component.props 에 접근을 할 수 없었다. 

그러다 발견한 것이... 

 

Accessing React component children props from the parent

 

Accessing React component children props from the parent

I am currently in the process of designing the interface for a game engine that is written in JavaScript and ReactJS. If a game object has a texture, the source of the texture will show up on the ...

stackoverflow.com

function isValidElement<P>(object: {} | null | undefined): object is ReactElement<P>

if 문 안쪽은 ReactElement 로 잡힘 , props 에 접근가능하다.
제네릭을 넣어주면 그 props 는 타입까지 잡힌다..

이렇게 component 의 props 에 접근 할 수 있게 되었다.

 

최종적으로 Routes 컴포넌트는 이렇게 생겼다.

interface RoutesProps {
  children: ReactNode | ReactNode[];
}

/* 자식 컴포넌트들을 찾아간다.*/
/* 지정한 path와 현재 경로가 같을 때 컴포넌트를 렌더링한다. */
const Routes = ({ children }: RoutesProps) => {
  const { pathname: currentPath } = useRouter();

	// 1개의 Route 만 있는 경우..
  const childArray = Array.isArray(children) ? children : [children];

  const currentRenderComponent = (component: ReactNode): boolean => {
    //  isValidElement,  type predicate 되어있음 이 안에서는 props 접근 가능!  제네릭을 넣으면 props 타입도 지정 가능!
    if (!isValidElement<RouteProps>(component)) return false;
			// **Route 컴포넌트가 아닌 경우** path 는 undefined 인거 같음 
			// **이를 드러내야 하나?** 아님 암묵적으로 가져가도 상관없을까? 
    return component.props.path === currentPath;
  };
	// 읽기 좋게 {**childArray 에서 찾는다! 조건에 맞는 렌더될 컴포넌트를!**} 을 나타내고 싶었다. 함수명이 괜찮나..?  
  return <>{childArray.find(currentRenderComponent)}</>;
};

export default Routes;

 

 

Routes 의 Child가 Route 컴포넌트가 아닐때..!

 if (!isValidElement<RouteProps>(component)) return false;
 	// 여기서부터 component 는 진짜 ReactElement,그리고 props 로 <RouteProps> 를 가진..
      // component 가 컴포넌트가 맞으나 Route 컴포넌트가 아니면..???
    
	// **Route 컴포넌트가 아닌 경우** path 는 undefined 인거 같음 
	// **이를 드러내야 하나?** 아님 암묵적으로 가져가도 상관없을까? 
    return component.props.path === currentPath;
  };

 

isValidElement 함수의 역할은  이 객체가 리액트 컴포넌트인지만 판단하지 , 어떤 리액트 컴포넌트인지를 판단하는게 아니다.

 

TS 상에서는 RouterProps  제네릭으로 주면 if문 안쪽은 RouterProps 를 가진 컴포넌트가 된다.

이 코드는 Routes 의 자식이 무조건 Route 컴포넌트라는 것을 가정하고 짠 코드다.

 

하지만 자식이 만약 Route 컴포넌트가 아니면..?

런타임 상에서는 childComponent.props 까진 존재하지만 거기에 RouteProps 가 존재 하지 않을 수 있고  (undefined) 

undefined === currentPath 는 어차피 무조건 false 라 문제없이 동작은 한다.

 

일단 동작하니까 그냥 넘어갔다면 놓칠 수 있었던 부분이다. 이건 어떻게 해결해야 할까..?

컴포넌트가 유효하고 => 그것이 Route 컴포넌트 인지도 체크를 해줘야 할 것이다. (타입 체크도 함께) 

 

const isRouterComponent = (component:ReactNode):component is ReactElement<RouterProps> = {
  // valid Element 이고 Router 컴포넌트 인지 확인 
}

이렇게 좁히면 될려나..?

너무 길어지니까 일단 여기서 마무리 하기로 했다.

 

화면엔 보이지 않지만 Root 컴포넌트 까지 찍히는 이전 상황
필요한 컴포넌트만 존재하게 수정하였다.

 

마무리

과제를 진행하면서 SPA 에서 어떻게 라우팅이 일어나는지 원리를 공부할 수 있었다.

역시 이번 챌린지 강의도 정말로 많은 것을 알아가는 것 같아 너무너무 좋았다.

 

 

요즘은  react hook 이 어떤 식으로 동작하는 지 ( useState 와 closure 의 관계..?) 

react 에서 SSR 을 어떻게 구현 할 수 있는지 (Next JS 가 어떻게 SSR 을 하고 있는지..?)

이런 내용들을 알아보고 있다. 

closure 와 react hook ..

기술을 그냥 사용했을때보다 , 왜 이렇게 구현을 했고 어떤 원리로 돌아가는지 배경을 공부하는 것에 더 재미를 느낄 수 있었다.

또 원리를 어느정도 알고 있으니 그것을 근거로 들어 어떤 기술에 대해 나의 견해를  자신있게 이야기 할 수 있을 것 같다. 

 

또 이번 과제를 진행하면서 그냥 코드를 작성해보는 것이 아니라 

의식적으로 이런 생각을 머리속에 떠올리면서 문제 해결을 해보려 노력을 해보았다.

요즘 느끼는건데 항상 왜? 라는 질문을 던지는 것이 너무나도 중요한 것 같다.