(React) App.js에 무분별하게 작성했던 라우팅 설정 코드를 분리하고 Protected Route 전략 도입하기 (1) | 라우팅 설정 코드 분리, App.js와 index.js의 역할

kidstone·2025년 2월 4일
0

외개인 프로젝트

목록 보기
7/10
post-thumbnail

들어가며

어느덧 개발자로 일한지 10개월차가 되었다. 회사에서 운영하는 서비스를 유지보수하고 신규 개발에도 참여하면서 많은 것을 배웠던 것 같다. 그 중에서 서비스가 크지는 않지만 체계와 규칙이 잡혀있는 것이 그동안 내가 해왔던 프로젝트 경험과는 큰 차이점이라고 볼 수 있다.

회사 서비스에서는 App.js를 깔끔하게 두고 있었다. 아니, 그동안 내가 더럽게 두고 있었는 지 모르겠다. App.js에서 라우팅 설정하는 것은 당연하지만, 과거에 페이지 몇 안되는 프로젝트만 진행하다가 꽤 페이지 수가 많은 프로젝트를 처음 해봐서 그런가 무지성으로 라우팅 설정을 했던 것 같다. 그래서 나는 App.js 다이어트를 결심했다!


App.js와 index.js

우선 App.js 파일이 무엇인지 확실하게 머릿속에 정리하고 싶었다.

App.js가 무엇일까? App.js는 보통 싱글 페이지 애플리케이션 프레임워크에서 사용되는 최상단 컴포넌트이다. 최상단 컴포넌트로서 모든 하위 컴포넌트를 관리하는 중심적인 역할을 수행한다.

App.js의 역할

  • 라우팅 설정: React Router와 같은 라이브러리를 사용하여 SPA의 페이지 라우팅을 관리한다. 경로에 대해 렌더링될 컴포넌트를 설정한다.
  • 상태 관리: 전역 상태를 정의하고, 이를 하위 컴포넌트들에게 전달한다. Redux나 Recoil 등 다양한 상태 관리 라이브러리를 사용한다.
  • UI 구성: 최상위 컴포넌트이기 때문에 전체적인 UI 구조를 설정할 수 있다. 또는 전역적으로 상태에 따라 서로 다른 컴포넌트를 렌더링할 수 있다.
App.js

import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">   
      <h1>프론트엔드 앞잡이입니다.</h1>
    </div>
  );
}

export default App;

index.js의 역할

App.js에 대해 이야기하고 나니 문득 떠오른 것이 있다. index.js는 그럼 뭐지?? 뭔가 App.js와 비슷한 것 같긴 한데 두개가 따로 있으니 어떻게 다른지 궁금해졌다.

index.js는 애플리케이션의 진입점이다. 즉, 애플리케이션이 처음 로드될 때 가장 먼저 실행되는 파일이다. 이 파일에서 루트 컴포넌트(App.js)를 DOM에 렌더링한다.

index.js

import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

ReactDOM.render() 메서드를 사용하여 App.js 컴포넌트를 DOM에 렌더링한다. 또한 전역 스타일이나 전역 상태도 index.js에서 설정하고 초기화할 수 있다.

아무튼 정리하자면!

  • index.js는 애플리케이션의 진입점으로, DOM에 루트 컴포넌트를 렌더링하고 전역 설정을 담당한다.
  • App.js는 애플리케이션의 주요 컴포넌트로, UI 구성, 상태 관리, 라우팅 등의 로직을 포함한다.

라우팅 설정 코드 분리하기

그렇다. App.js는 라우팅 설정을 하는 역할이 분명 있다. 하지만 이러한 설정들을 모두 App.js에 노출시키는 것은 코드의 가독성을 떨어뜨릴 수 있다. 따라서, 라우팅 관련 설정을 별도의 폴더로 분리하여 관리하는 것이 더 깔끔하고 명확한 구조를 제공할 것이라고 생각했다. 이러한 접근은 코드 유지보수성을 높이고, 프로젝트의 전반적인 구조를 한층 더 체계적으로 만들어 줄 것이다!



"아니, App.js가 얼마나 더럽길래 이러는거야?"
밑의 코드를 한번 보시라.



기존 App.js

App.js

import React from 'react';
import './App.css';
import {
	BrowserRouter as Router,
	Routes,
	Route,
	useLocation,
	Outlet,
} from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import 'react-toastify/dist/ReactToastify.css';
//pages
import LandingPage from '@pages/LandingPage/LandingPage';
import OnboardingPage from '@pages/OnboardingPage/OnboardingPage';
...
import LikePage from '@pages/MyPage/LikePage/LikePage';
import { StyledToastContainer } from '@components/toastify/Toast';

const queryClient = new QueryClient();

function App() {
	return (
		<div className="App">
			<QueryClientProvider client={queryClient}>
				{/* <React.StrictMode> */}
				<Router>
					<Routes>
						<Route path="/" element={<MainLayout />}>
							<Route index element={<HomePage />} />
							<Route path="callback" element={<CallBack />} />
							<Route path="home" element={<HomePage />} />
							<Route path="home/dormnews" element={<DormNewsPage />} />
							<Route path="home/ending-soon" element={<EndingsoonPage />} />
							<Route
								path="home/best-roommates"
								element={<BestRoommatesPage />}
							/>
							<Route path="notification" element={<NotificationPage />} />
							<Route path="search" element={<SearchPage />} />
							<Route path="roommate" element={<RoommatePage />} />
							<Route path="roommate/filter" element={<RoommateFilterPage />} />
							<Route path="landing" element={<LandingPage />} />
							<Route path="onboarding" element={<OnboardingPage />} />
							<Route path="login" element={<LoginPage />} />
							<Route path="mypage" element={<MyPage />} />
							<Route
								path="mypage/come-matchingrequests"
								element={<ComeMatchingListPage />}
							/>
							<Route
								path="mypage/roommate-applylist"
								element={<RoommateApplyListPage />}
							/>
							<Route path="mypage/like" element={<LikePage />} />
							<Route path="mypage/mypost" element={<MyPostPage />} />
							<Route path="my-profile/edit" element={<MyProfileEditPage />} />
							<Route path="user/:memberId" element={<UserPage />} />
							<Route path="user/:memberId/posts" element={<UserPostPage />} />
							{/* <Route
								path="user/:memberId/reviews"
								element={<RoommateReviewPage />}
							/> */}
							<Route path="/setting" element={<BasicInfoSetting />} />
							<Route path="/post-roommate" element={<Post />} />
							<Route
								path="/post-roommate/edit/:postId"
								element={<PostRoommateEdit />}
							/>
							<Route path="/post-detail/:postId" element={<PostDetail />} />
							<Route
								path="/comment-detail/:postId"
								element={<CommentDetail />}
							/>
							<Route path="/chat" element={<Chat />} />
							<Route
								path="/chat/chatroom/:subscribeID"
								element={<ChatRoom />}
							/>
							<Route path="/alarm" element={<Alarm />} />
							<Route path="/delivery" element={<Delivery />} />
						</Route>
					</Routes>
				</Router>
			</QueryClientProvider>
		</div>
	);
}

function MainLayout() {
	const location = useLocation();
	return (
		<div className="main-layout">
			<div className="content">
				<StyledToastContainer
					position="top-center"
					limit={10}
					closeButton={true}
					autoClose={4000}
					hideProgressBar
				/>
				<Outlet />
			</div>
			...

export default App;

import 부분만 살짝 중략했고, Route 태그는 일부러 안지우고 다 복붙해보았다. 수많은 경로 설정과, 전역 상태 설정, 외부 라이브러리 사용 등 App.js는 제 기능을 하는 것 같지만 뭔가 더럽다. 아니, 확실히 더럽다. 이제 라우팅 설정을 분리해보자!

src 하위 경로에 routes라는 폴더를 만들었고, 그 아래에 AppRoutes.jsx 파일을 만들었다. 그리고 이 파일에 라우팅 설정 부분을 모두 옮겼다.

라우팅 설정 기능만 하는 AppRoute.jsx

//  routes/AppRoutes.jsx
import React from 'react';
import {
	Routes,
	Route,
	useLocation,
	Outlet,
} from 'react-router-dom';

//pages
import LandingPage from '@pages/LandingPage/LandingPage';
import OnboardingPage from '@pages/OnboardingPage/OnboardingPage';
...

const AppRoutes = () => {
  return (
    <Routes>
      <Route path="/" element={<MainLayout />}>
        <Route index element={<HomePage />} />
        <Route path="callback" element={<CallBack />} />
        <Route path="home" element={<HomePage />} />
        <Route path="home/dormnews" element={<DormNewsPage />} />
        <Route path="home/ending-soon" element={<EndingsoonPage />} />
        <Route
          path="home/best-roommates"
          element={<BestRoommatesPage />}
        />
        ...
          
          
        중략


		...
        <Route
          path="/chat/chatroom/:subscribeID"
          element={<ChatRoom />}
        />
        <Route path="/alarm" element={<Alarm />} />
        <Route path="/delivery" element={<Delivery />} />
      </Route>
    </Routes>
  );
};

function MainLayout() {
	const location = useLocation();
	return (
		<div className="main-layout">
		    ...
		</div>
	);
}


export default AppRoutes;

그리고 이 AppRoutes 파일을 App.js에서 import해서 사용하면? 끝이다.

변경된 App.js

//  App.jsx
import React from 'react';
import './App.css';
import {
	BrowserRouter as Router,
} from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import AppRoutes from 'routes/AppRoutes';
import 'react-toastify/dist/ReactToastify.css';

const queryClient = new QueryClient();

function App() {
	return (
		<div className="App">
			<QueryClientProvider client={queryClient}>
				<Router>
					<AppRoutes/>
				</Router>
			</QueryClientProvider>
		</div>
	);
}


export default App;

어떤가. App.js의 본연의 기능인 라우팅 설정, 상태 관리 등이 명확하게 보이지 않는가? 이것으로 첫번째 챕터 App.js 다이어트는 끝이났다. 다음 포스팅에서는 이제 Protected Route 전략을 도입하여, 인증된 사용자만 특정 페이지에 접근할 수 있도록 해보겠다.

profile
안녕하세요. 웹 프론트엔드 개발자 앞잡이 '꼬마돌' 입니다.

0개의 댓글