Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NDD-61]: createBrowserRouter를 통한 Router기능 추가 (8h / 3h) #19

Merged
merged 32 commits into from
Nov 8, 2023

Conversation

adultlee
Copy link
Collaborator

@adultlee adultlee commented Nov 8, 2023

NDD-61 Powered by Pull Request Badge

Why

createBrowserRouter는 React Router v6에서 새롭게 도입된 API 중 하나로, 라우터 객체를 생성하는 데 사용됩니다. 이 함수는 라우터 구성을 객체 형태로 선언적으로 만들 수 있게 해주며, 이를 BrowserRouter 컴포넌트에 전달하여 라우팅을 설정합니다. createBrowserRouter를 사용하면 프로그래매틱 라우트 정의와 라우트에 대한 여러 가지 추가 설정을 할 수 있습니다.

// React Router v6에서의 Route 사용 예
import { Route, Routes } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path="/" element={<HomePage />} />
      <Route path="about" element={<AboutPage />} />
      {/* 추가 Route 정의 */}
    </Routes>
  );
}

// React Router v6에서의 createBrowserRouter 사용 예
import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: "/",
    element: <HomePage />,
    // 여기에 하위 라우트나 로더(loader), 액션(action) 등을 추가할 수 있습니다.
  },
  {
    path: "about",
    element: <AboutPage />,
  },
  // 추가 라우트 정의
]);

function App() {
  return (
    <RouterProvider router={router} />
  );
}

다음과 같은 방식의 차이를 가지고 있습니다. 특별히 기능상 차이점이 없다고 느낄 수 있지만
createBrowserRouter를 사용하면 라우트 관련 설정을 한 곳에서 관리할 수 있으며, 라우트 로더(loaders), 액션(actions), 에러 핸들링(error handling) 등을 보다 세밀하게 설정할 수 있습니다.
아래의 예시를 통해서 추가 설명하겠습니다.

const router = createBrowserRouter([
  {
    path: '/',
    element: <HomePage />,
    // 여기서 로더를 정의하여 이 라우트가 활성화될 때 실행됩니다.
    loader: () => fetch('/api/home'),
  },
  {
    path: '/users',
    element: <UsersPage />,
    // 로더를 사용하여 UsersPage에 필요한 데이터를 미리 가져올 수 있습니다.
    loader: fetchUserData,
    errorElement: <ErrorPage />, // 로더에서 오류가 발생하면 이 요소가 렌더링됩니다.
  },
  // 추가 라우트 정의
]);

우리서비스는 유저의 flow가 굉장히 중요합니다. 예를들어 setting이 아직 진행이 끝나지 않았음에도 interview로 이동하는것을 처리해주어야만 합니다.
이때 여러 방법이 있지만, router 단계에서 해당 로직에 대해 처리하고 반환할 수 있다면, 책임분리가 가능하리라고 생각했습니다.

How

const routes: RouteObject[] = [
  {
    path: PATH.ROOT,
    element: <LandingPage />,
  },
  {
    path: PATH.INTERVIEW,
    element: <InterviewPage />,
  },
  {
    path: PATH.INTERVIEW_SETTING,
    element: <InterviewSettingPage />, // 여기를 기준으로 하위 컴포넌트를 추가로 사용합니다. 이때 Outlet 컴포넌트를 사용합니다.
    children: [
      {
        index: true,
        element: <QuestionSelectionBox />,
      },
      {
        path: PATH.CONNECTION,
        element: <VideoSettingBox />,
      },
      {
        path: PATH.RECORD,
        element: <RecordMethodBox />,
      },
    ],
  },
  {
    path: PATH.MYPAGE,
    element: <MyPage />,
  },
  {
    path: PATH.INTERVIEW_VIDEO, // ":videoId" is a URL parameter
    element: <InterviewVideoPage />,
  },
];

Result

2023-11-07.8.50.59.mov

Problem

react-router-dom의 Link 태그로는 잘 이동하지만 이동한 위치에서 새로 고침을하면 이동이 불가한 문제가 발생

이유

이 문제는 Single Page Applications (SPA)의 클라이언트 측 라우팅과 서버 측 라우팅 방식의 차이에서 발생합니다. react-router-dom 같은 클라이언트 측 라우팅 라이브러리는 브라우저의 주소 표시줄의 URL을 변경하지만, 실제로 서버에 새로운 요청을 보내지 않습니다. 대신, JavaScript를 사용하여 클라이언트 측에서 동적으로 콘텐츠를 변경합니다.

이와 대조적으로, 서버 측 라우팅은 URL이 변경될 때마다 서버로 새 요청을 보내 페이지를 새로고침합니다. SPA에서는 클라이언트 측 라우팅을 사용하므로, 서버는 어떤 특정 경로에 대한 요청을 처리하는 방법을 몰라서 404 Not Found 에러를 반환할 수 있습니다.

예를 들어, http://example.com/user 페이지를 보고 있다가 페이지를 새로고침하면, 브라우저는 서버에 /user 경로에 대한 새 페이지를 요청합니다. SPA를 호스팅하는 서버가 이 경로에 대해 설정되지 않았다면, 서버는 404 에러를 반환할 것입니다. 그 이유는, 클라이언트 측 라우터만이 /user 경로가 유효하다는 것을 알고 있고, 서버는 아닙니다.

이 문제를 해결하기 위해 historyApiFallback 설정이 사용됩니다. 이 설정을 활성화하면, Webpack Dev Server는 개발 중에 클라이언트 측 라우트에 대한 404 응답을 **index.html**로 리다이렉션합니다. 이는 **index.html**이 실제로는 SPA의 진입점이며, 클라이언트 측 JavaScript가 필요한 라우팅을 처리할 수 있게 해줍니다.

따라서, 사용자가 새로고침을 하거나 직접 URL을 입력하여 특정 경로로 접근할 때, historyApiFallback 설정이 있는 서버는 요청을 **index.html**로 리다이렉트하고, 그 후에 **react-router-dom**이 URL을 확인하여 적절한 컴포넌트를 렌더링합니다. 이로 인해 클라이언트 측에서 정의한 라우트가 서버 측에서도 문제없이 작동하게 됩니다.

해결방법

image

다음 코드와 같이 webpack 설정을 추가해줌으로서 해결했습니다.

Link

Jira : https://milk717.atlassian.net/browse/NDD-61

adultlee and others added 29 commits November 7, 2023 23:36
@adultlee adultlee requested a review from milk717 November 8, 2023 06:27
@adultlee adultlee added FE 프론트엔드 코드 변경사항 feature 새로운 기능이 추가 된 경우 labels Nov 8, 2023
FE/src/components/interviewPage/InterViewCamera.tsx Outdated Show resolved Hide resolved
FE/src/page/interviewSettingPage/index.tsx Show resolved Hide resolved
FE/src/constants/path.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@milk717 milk717 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가장 번거로운 라우터 작업 처리하시느냐고 고생 많으셨습니다!!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-1] 이거 기존에 figma에 component안에 있는 폴더입니다!!! 폴더 이동 부탁드릴게요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영완료 b5e8135

display: flex;
flex-direction: ${direction};
width: ${full ? '100%' : 'auto'};
max-width: ${full ? 'none' : '750px'};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-1] 저희 모든 단위는 rem으로 통일하기로 정해서 750px -> 46.875rem으로 변경해 주세요!!

요거 이외에도 border도 1px로 되어있는데 요것도 확인 부탁드릴게요

Comment on lines 8 to 9
CONNECTION: 'connection',
RECORD: 'record',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-2] 요것도 '/connection' 등 으로 통일성 맞추는건 어떤가요?

path: PATH.INTERVIEW_VIDEO, // ":videoId" is a URL parameter
element: <InterviewVideoPage />,
},
];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-5] router관리를 좀 더 세세하게 할수 있는군요?!? 하나 배워갑니다

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-4] 저는 보통 파일명 === export 되는 명을 맞추는 편인데 이거에 대해서는 어떤 생각이신가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조금 고민이 되네요! 흐음 그렇지만 한번 맞추기는 해야할거 같아요
저도 비슷한 생각입니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p-5] box라는 네이밍 보다는 뭔가 하나의 세팅 페이지니 page로 끝나는건 어떤가요? 뭔가 box는 하나의 컴포넌트를 지정하는것 같아서 제 생각에는 그것보다는? 레이아웃을 제외한 본문의 내용이니 content? page? 이런 느낌이 더 강한것 같아요!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

당연히 언제든지 바뀔수 있습니다! 여기선 구역을 나누기 위해서 강제로 넣어놨어요!!

@adultlee adultlee merged commit da9de2b into dev Nov 8, 2023
@delete-merged-branch delete-merged-branch bot deleted the feature/NDD-61 branch November 8, 2023 08:11
@teihong93
Copy link

Data router 적용 훌륭하네요! 👍🏻
loader level 에서의 에러 발생 케이스와(즉 errorElement 로 보내지는 케이스) 랜더링 이후 & 혹은 error boundary level 에서의 에러 발생 케이스에서의 에러처리 정책들도 추후에 팀에서 고민해보면 좋을 것 같아요~ (loader 는 실패해도 일단 랜더링 시키고 컴포넌트 레벨에서 다시 시도해본다던지요!)

Yoon-Hae-Min pushed a commit that referenced this pull request Nov 11, 2023
* chore: react-dom & react-router-dom 설치

* chore: 불필요한 lint 설정 제거

* feat: layout 폴더에 대한 절대경로 설정

* feat: Layout 컴포넌트 정의

* feat: Layout 컴포넌트 기반 각 페이지별 Layout 컨포넌트 선언

* feat: route에 대한 상수화

* feat: MyPage 내부의 section을 나눌 컴포넌트 선언

* feat: InterviewPage 내부의 section을 나눌 컴포넌트 선언

* feat: InterviewSettingPage 내부의 section을 나눌 컴포넌트 선언

* feat: LandingPage 내부의 section을 나눌 컴포넌트 선언

* feat: 각 페이지별 기초적인 section 부여

* feat: AppRouter 함수를 통해서 Router 처리

* feat: 중첩된 경로에대한 server응답 구현

* fix: Typography export 설정 변경

* chore: react-dom & react-router-dom 설치

* chore: 불필요한 lint 설정 제거

* feat: layout 폴더에 대한 절대경로 설정

* feat: Layout 컴포넌트 정의

* feat: Layout 컴포넌트 기반 각 페이지별 Layout 컨포넌트 선언

* feat: route에 대한 상수화

* feat: MyPage 내부의 section을 나눌 컴포넌트 선언

* feat: InterviewPage 내부의 section을 나눌 컴포넌트 선언

* feat: InterviewSettingPage 내부의 section을 나눌 컴포넌트 선언

* feat: LandingPage 내부의 section을 나눌 컴포넌트 선언

* feat: 각 페이지별 기초적인 section 부여

* feat: AppRouter 함수를 통해서 Router 처리

* feat: 중첩된 경로에대한 server응답 구현

* fix: Typography export 설정 변경

* fix: 변수로 인해 재사용성 확대

* fix: layout 컴포넌트를 components 하위 폴더로 이동

* chore: layout 라우터 변경으로 발생한 파일병 변경
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FE 프론트엔드 코드 변경사항 feature 새로운 기능이 추가 된 경우
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants