[TypeScript/Keupang] #5 Barrel File을 활용한 Router 생성하기

HoneyCode Lab·2024년 12월 4일

프로젝트

목록 보기
5/8
post-thumbnail

개요


✅Keupang Github
이제 각 페이지로 이동하는 Router를 생성하고, 이 페이지 파일들을 관리할 Barrel File에 대해 알아보고 '이 기술을 현 프로젝트에 적용하는게 맞는가?' 에 대한 고민을 하려고 한다.

본격적인 페이지 개발 전, 마지막 세팅 작업이다.

 


배럴 파일 (Barrel File) ?


배럴 파일(Barrel File)이란 폴더 내 여러 파일들을 하나의 진입점(entry point) 으로 묶어주는 역할을 한다.

이를 통해 import 경로를 간소화하고, 관련 파일들을 쉽게 관리할 수 있다.

예를 들어, pages 폴더에 여러 컴포넌트 파일이 있을 경우, 각각 import하지 않고, index.ts라는 배럴 파일 하나로 관리할 수 있다.

배럴 파일 사용 전

import MainPage from './pages/MainPage';
import LoginPage from './pages/LoginPage';
import SignupPage from './pages/SignupPage';

배럴 파일 사용 후

import { MainPage, LoginPage, SignupPage } from './pages';

 

장점


위의 예제 코드에서 볼 수 있듯이, index.ts에서 페이지 컴포넌트를 import하여 관리할 경우 가독성이 월등히 증가하고 코드 또한 간결해지는 것을 확인할 수 있다.

모든 페이지 컴포넌트를 하나의 파일에서 관리하기 때문에 유지보수 및 관리하기에도 용이해진다.

추 후 코드 리뷰등을 진행하는 등 협업 과정에서도 존재하는 pages들을 확인하기 위해선 index.ts 파일 하나만을 보고 이를 파악할 수 있다.

  1. 경로 간소화
  2. 구조적 관리
  3. 직관성

 

단점


모든 기술들은 장점이 있으면 단점 또한 존재한다.

폴더 내부 파일들 끼리의 의존성이 강한 경우 배럴 파일로 인해 의존성에 관련된 문제가 발생할 수 있다.

배럴 파일은 파일 가져오기 프로세스에서 간접적인 중간 계층이 추가되는 것 이므로 특정 값의 출처를 이해하기 더 어려울 수 있다.

배럴 파일은 추가 파일을 로드하고 처리해야 하므로 응용 프로그램의 성능이 약간 저하될 수 있다.

또한, 폴더 구조가 계속 변경된다면 오히려 유지보수 및 코드 변경에 번거로움이 생길 수 있다.

  1. 의존성 문제
  2. 성능 최적화
  3. 폴더 구조에 대한 문제

 


Keupang에 배럴 파일 적용하기


그럼 내 프로젝트엔 배럴 파일을 도입해도 되는가? 는 중요하게 다루어야 하는 문제이다.

그럼 해당 기술의 문제점을 파악하고, 이 문제점들이 내 프로젝트에는 큰 영향이 없는가를 생각해보아야 한다.

1) 의존성 문제

배럴파일에 사용할 컴포넌트가 서로 긴밀하게 연결되어 사용하여야하는 경우는 배럴 파일 사용을 지양하여야 한다.

즉, 한 폴더의 컴포넌트가 독립적으로 수행될 수 있을 때는 사용을 고려할 수 있다.

예를들어, pages 폴더에서의 각 페이지 컴포넌트, hooks 폴더에서의 각 커스텀 훅 등에선 사용하기 충분하다고 판단할 수 있다.

 

2) 성능 최적화

프론트엔드를 개발하는 입장에서 성능 관련 이슈는 매우 민감하다.

사용자가 웹 사이트에 들어와 3초안에 로딩이 되지 않으면 이탈율이 70%가 되는 통계도 존재하는 등 성능에 관련된 문제는 민감하게 접근하여야 한다.

결국 페이지 컴포넌트 import 하는 간접적인 중간계층이 끼어들게 돼 이 컴포넌트가 많으면 성능이 저하될것이다. 라는 말은 머리로 이해할 수 있다.

그러면 '얼마나?'가 중요해진다.

성능이 저하되는 부분이 납득이 가능한 범위라면 이는 도입하기 타당한 기술이라 생각할 수 있다.

실제로 배럴 파일을 활용한 성능 테스트 관련 지표를 찾아서 확인해보았다.

이미지 출처 : https://github.com/yeonjuan/dev-blog/blob/master/JavaScript/speeding-up-the-javascript-ecosystem-the-barrel-file-debacle.md

위 출처에 적힌 글을 인용하면 아래와 같다.

보시다시피 모듈을 더 적게 로드하면 그만한 가치가 있습니다. 이러한 숫자를 100개의 테스트 파일이 있는 프로젝트에 적용하고, 각 테스트 파일에 새로운 자식 프로세스를 생성하는 테스트 실행 도구를 사용한다고 가정해 봅시다. 여기서는 테스드 러너가 4개의 테스트를 병렬로 실행할 수 있다고 가정해 보겠습니다.

모듈 500 개: 0.15s * 100 / 4 = 3.75s 오버헤드
모듈 1000 개: 0.31s * 100 / 4 = 7.75s 오버헤드
모듈 10000 개: 3.12s * 100 / 4 = 1:18m 오버헤드
모듈 25000 개: 16.81s * 100 / 4 = ~7:00m 오버헤드
모듈 50000 개: 48.44s * 100 / 4 = ~20:00m 오버헤드

즉, 적은 수의 모듈을 로드하면 이 기술은 충분한 활용가치가 있다고 설명하고 있고, 현재 내 프로젝트는 404NotFound 페이지를 포함하여 총 9개 정도의 페이지만 import할 예정이기 때문에 도입하기 적합하다고 판단할 수 있다.

 

3) 폴더 구조에 대한 문제

사소한 내용이라 포스트하진 않았지만, 이미 #4번 포스팅 이후에 폴더 구조에 대한 정립을 마쳤다.

Issue를 생성하고 이를 마쳤기 때문에 폴더 구조가 변경될 일은 거의 없다고 생각할 수 있다.

즉, 이 문제 또한 도입에 문제가 되지 않는다고 판단할 수 있다.

결론

3가지의 단점을 모두 고려하였을 때, 배럴 파일은 내 프로젝트에 도입하기에 부족함이 없는 기술이라 판단할 수 있었다.

그래서, 이 기술을 이제 내 프로젝트에 적용해보려 한다.

 

page 컴포넌트 생성하기


우선, 라우터를 만들기 전에 페이지 스켈레톤 컴포넌트를 생성해야 한다.

총 9개의 페이지를 만들어야 한다.

하지만, 하나하나의 페이지를 적당히 만들기엔 너무 귀찮고 의미 없는 시간이 오래걸린다고 생각했다.

나는 vscode extension인 Reactjs code snippets를 사용하고 있었다.

하지만, Emotion과 TypeScript의 타입 및 컴포넌트의 기본 골격에 대한 스니펫은 위의 익스텐션에서 제공하지 않아 불편함을 느꼈다.

그래서 자체적으로 코드 스니펫을 만들었다.

 

vscode 코드 스니펫 만들기

ctrl+shift+p를 클릭하면 vscode의 상단에 검색창이 나오게 된다.

검색창에 snippets라고 입력하자.

위와 같은 옵션을 발견할 수 있다.

클릭해서 다시 typescript를 입력하자.

해당 json파일을 발견할 수 있고, 클릭해서 들어가면 큰 주석이 { }로 감싸진 코드를 발견할 수 있다.

✅snippet generator

위 사이트에서 본인이 원하는 스켈레톤 코드를 입력하면 위 json파일에 넣어야 하는 코드로 만들어준다.

나는 아래와 같이 작성해서 사용중이다.

{
	"TypeScript Functional Components": {
  "prefix": "!tsc",
  "body": [
    "import { useEffect } from 'react';",
    "import styled from '@emotion/styled';",
    "",
    "interface ${TM_FILENAME_BASE}Props {",
    "}",
    "",
    "// 스타일 정의",
    "const ${TM_FILENAME_BASE}Container = styled.div`",
    "  display: flex;",
    "  flex-direction: column;",
    "  align-items: center;",
    "  justify-content: center;",
    "  padding: 16px;",
    "  background-color: ${({ theme }) => theme.colors.background};",
    "  color: ${({ theme }) => theme.colors.text};",
    "`;",
    "",
    "const ${TM_FILENAME_BASE}: React.FC<${TM_FILENAME_BASE}Props> = () => {",
    "  useEffect(() => {",
    "    console.log(`useEffect를 설정하세요`);",
    "  }, []);",
    "",
    "  return (",
    "    <${TM_FILENAME_BASE}Container>",
    "      컴포넌트 작성하기",
    "    </${TM_FILENAME_BASE}Container>",
    "  );",
    "};",
    "",
    "export default ${TM_FILENAME_BASE};",
    ""
  ],
  "description": ""
}
}

적당히 useEffect를 import해주었고, interface와 emotion styled 스켈레톤 코드를 만들었다.

${TM_FILENAME_BASE}는 해당 파일의 이름이다.

prefix는 스켈레톤 코드를 생성하는 명령어이다.

이제 페이지 컴포넌트 파일에서 사용해보자.

NotFoundPage에서 !tsc를 입력하니 내가 설정한 코드 스니펫을 볼 수 있었다.

Tab을 클릭하니 아래와 같이 나온다.

import { useEffect } from 'react';
import styled from '@emotion/styled';

interface NotFoundPageProps {
}

// 스타일 정의
const NotFoundPageContainer = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background-color: ${({ theme }) => theme.colors.background};
  color: ${({ theme }) => theme.colors.text};
`;

const NotFoundPage: React.FC<NotFoundPageProps> = () => {
  useEffect(() => {
    console.log(`useEffect를 설정하세요`);
  }, []);

  return (
    <NotFoundPageContainer>
      컴포넌트 작성하기
    </NotFoundPageContainer>
  );
};

export default NotFoundPage;

이제 컴포넌트 스켈레톤 코드를 사용하는데 귀찮은 일을 크게 줄일 수 있다 !

이와 같은 방법으로 9가지의 페이지 모두 스켈레톤 코드를 만들었다.

이제 App.tsx에서 import할 수 있다.

 


배럴 파일 만들기


이제 배럴 파일 index.ts를 만들어 페이지 컴포넌트를 import하여 export하면 된다.

export { default as MainPage } from './MainPage';
export { default as LoginPage } from './LoginPage';
export { default as SignupPage } from './SignupPage';
export { default as ProductListPage } from './ProductListPage';
export { default as ProductDetailPage } from './ProductDetailPage';
export { default as CartPage } from './CartPage';
export { default as OrderHistoryPage } from './OrderHistoryPage';
export { default as MyPage } from './MyPage';
export { default as NotFoundPage } from './NotFoundPage';

배럴 파일은 위와 같이 만들었다.

이제 이를 App.tsx에서 가져와 사용할 수 있다.

 

router 생성하기


App.tsx에 배럴파일을 import하고 page들을 라우터에 연결하면 라우터 연결이 끝난다.

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import {
	MainPage,
	LoginPage,
	SignupPage,
	ProductListPage,
	ProductDetailPage,
	CartPage,
	OrderHistoryPage,
	MyPage,
	NotFoundPage,
} from './pages';

라우터를 import하고, 배럴파일에 저장된 페이지를 가져온다.

const App = () => {
	const [isDarkMode, setIsDarkMode] = useState(() => {
		const savedTheme = localStorage.getItem('theme');
		return savedTheme ? JSON.parse(savedTheme) : false;
	});

	const toggleTheme = () => {
		setIsDarkMode((prevMode: Boolean) => {
			const newMode = !prevMode;
			localStorage.setItem('theme', JSON.stringify(newMode));
			return newMode;
		});
	};

	return (
		<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
			<GlobalStyles />
			<Router>
				<StyledDiv>
					<h1>현재 테마: {isDarkMode ? '다크 모드' : '라이트 모드'}</h1>
					<Button onClick={toggleTheme}>Toggle Theme</Button>
					<Routes>
						<Route path='/' element={<MainPage />} />
						<Route path='/login' element={<LoginPage />} />
						<Route path='/signup' element={<SignupPage />} />
						<Route path='/products' element={<ProductListPage />} />
						<Route path='/products/:id' element={<ProductDetailPage />} />
						<Route path='/cart' element={<CartPage />} />
						<Route path='/orders' element={<OrderHistoryPage />} />
						<Route path='/mypage' element={<MyPage />} />
						<Route path='*' element={<NotFoundPage />} /> {/* 404 페이지 */}
					</Routes>
				</StyledDiv>
			</Router>
		</ThemeProvider>
	);
};

export default App;

이제 App.tsx를 위와 같이 구성하면 라우터 연결도 끝이 난다.

상단의 url이 /가 아닌 /mypage여도 잘 작동하는 모습을 볼 수 있다.

 


Summary


  • 배럴 파일?
  • 장/단점
  • keupang에 적용하기
  • 라우터 생성하기
profile
“왜?”라는 질문을 멈추지 않고 본질에 집중하는 개발자입니다.

0개의 댓글