preonboarding-internship / qna-12th

3 stars 0 forks source link

리다이렉트를 구현할 때 라우터로 구현하는 것과 useEffect로 구현하는 방법의 차이점과 관련하여 질문드립니다 #2

Closed NR0617 closed 1 year ago

NR0617 commented 1 year ago

리다이렉트를 '라우터로 했을 때'와 'TodoList 페이지에서 토큰이 없다면 Signin 페이지로 이동하도록 useEffect로 구현했을 때'의 차이점이 뭐가 있는지 비교하는 과정에서 질문이 생겼습니다. 라우터로 구현했을 때는 어플리케이션이 실행되자마자 Signin 페이지로 이동하면서 TodoList 페이지 컴포넌트는 렌더링 되지 않는다는 장점이 있고, useEffect로 구현했을 때는 페이지(함수)가 생성/실행된 다음 가장 마지막에 useEffect가 실행되기 때문에 한번 렌더링이 되고나서 이동이 되는 단점(불필요한 렌더링 발생)이 있다고 생각했어요. 그래서 불필요한 렌더링을 방지하기 위해 TodoList 페이지 컴포넌트에 useEffect로 리다이렉트를 구현할 때 if(!token) return;이라는 코드를 중간에 추가했고, 이렇게 하면 페이지 하단에 있는 부분이 렌더링 되지 않아서 비슷한 효과를 가질 수 있을 것 같았어요.

질문1. useEffect 를 사용해서 if(!token) return;을 추가하더라고 TodoList 페이지 컴포넌트(함수)는 1회 실행되고 있으니까 여전히 라우팅으로 리다이렉트를 구현하는게 더 큰 장점을 가지는게 맞을까요? 리다이렉트를 구현할 때 라우팅을 이용해서 만드는게 가장 좋은 방법인지 궁금합니다.

질문 2. 만약에 라우터와 useEffect 두 개를 모두 사용한다면 불필요하게 중복되는 기능을 만들게 되기 때문에 지양하는게 좋을까요?

//라우터로 구현
import {
  createBrowserRouter,
  createRoutesFromElements,
  Navigate,
  Route,
} from "react-router-dom";
import { Layout } from "../components";
import { NotFoundPage, SignInPage, SignUpPage, TodoListPage } from "../pages";
import { Authorized, UnAuthorized } from "../pages/Redirect";

const router = (
  <Route element={<Layout />}>
    <Route element={<UnAuthorized />}>
      <Route path="/signin" element={<SignInPage />} />
      <Route path="/signup" element={<SignUpPage />} />
    </Route>
    <Route element={<Authorized />}>
      <Route path="/" element={<Navigate to="/todo" replace />} />
      <Route path="/todo" element={<TodoListPage />} />
    </Route>
    <Route path="*" element={<NotFoundPage />} />
  </Route>
);

const rootRouter = createBrowserRouter(createRoutesFromElements(router));

export default rootRouter;
import { useContext } from "react";
import { Navigate, Outlet } from "react-router-dom";
import { useAuthContext } from "../../context/AuthContext";

export const UnAuthorized = () => {
  const { token } = useContext(useAuthContext);
  if (token !== null) {
    return <Navigate to="/todo" />;
  } else {
    return <Outlet />;
  }
};

export const Authorized = () => {
  const { token } = useContext(useAuthContext);
  if (token === null) {
    return <Navigate to="/signin" />;
  } else {
    return <Outlet />;
  }
};
// useEffect로 구현
import "./App.css";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

function App() {
  const navigate = useNavigate();
  useEffect(() => {
    navigate("/todo");
  }, []);
  return <div className="App"></div>;
}

export default App;
import Header from "../components/Header";
import { useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import ListItem from "../components/ListItem";
import { getTodoApi, createTodoApi } from "../utils/api";

const TodoList = () => {
  const navigate = useNavigate();
  const token = localStorage.getItem("JWT");
  useEffect(() => {
    if (!token) {
      navigate("/signin");
    }
  }, [token]);

  //...생략

  if (!token) return;

  return (
    <div>
      <Header />
       {/* ... 생략*/}
    </div>
  );
};

export default TodoList;
yeonuk-hwang commented 1 year ago

안녕하세요, 질문주신 사항에 대해서 제 생각은 다음과 같습니다

질문1. useEffect 를 사용해서 if(!token) return;을 추가하더라고 TodoList 페이지 컴포넌트(함수)는 1회 실행되고 있으니까 여전히 라우팅으로 리다이렉트를 구현하는게 더 큰 장점을 가지는게 맞을까요? 리다이렉트를 구현할 때 라우팅을 이용해서 만드는게 가장 좋은 방법인지 궁금합니다.

용어를 정확히 다시 정리하자면 라우팅을 이용한다고 하기 보다는 Navigate 컴포넌트를 바로 리턴해서 리다이렉트를 구현하는 방법으로 정리하겠습니다. 제 의견은 useEffect보다는 Navigate를 이용하는 방법이 좋아보입니다. 왜나하면 예시로 들어주신 useEffect에서 첫 렌더링에서 undefined를 리턴하는 코드를 사용하더라도 결국 1회 렌더링이 수행되고, 그 다음에 useEffect의 callback이 수행되면서 리다이렉트가 처리되는 과정은 동일하게 발생하는데 굳이 한번의 불필요한 렌더링과, useEffect를 사용해서 로직을 복잡하게 만들 필요가 없다고 생각되기에 저는 바로 Navigate를 이용해서 리다이렉트를 처리하는 방법을 사용할 것 같네요

추가적인 선택지로 React-Router-DOM을 사용할 경우에는 loader 기능을 이용해서 리다이렉트를 처리함으로서 컴포넌트에서는 리다이렉트와 관련된 관심사를 분리하는 방법도 있습니다. 이렇게 처리하게 된다면 내부 동작은 Navigate를 사용한 코드와 유사하겠지만 컴포넌트 내부에서 리다이렉트와 관련된 로직이 제거될 수 있기에 컴포넌트가 좀 더 본인의 역할에 집중할 수 있게 만들 수 있다는 장점을 얻을 수 있다고 판단됩니다.

질문 2. 만약에 라우터와 useEffect 두 개를 모두 사용한다면 불필요하게 중복되는 기능을 만들게 되기 때문에 지양하는게 좋을까요?

네, 동일한 기능을 하는 코드라면 중복이 발생하지 않도록 해주는게 좋으며 따라서, 동일한 기능을 수행하는 코드를 여러 방식으로 구현할 필요는 없다고 생각됩니다

답변이 도움이 됬기를 바랍니다 :)

NR0617 commented 1 year ago

loader 기능이 있었네요! 관심사를 분리할 수 있게 loader를 사용해서 다시 만들어보겠습니다 :) 많은 도움이 됐습니다!! 감사합니다 👍