
✅Keupang Github
이제 각 페이지로 이동하는 Router를 생성하고, 이 페이지 파일들을 관리할 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 파일 하나만을 보고 이를 파악할 수 있다.
- 경로 간소화
- 구조적 관리
- 직관성
모든 기술들은 장점이 있으면 단점 또한 존재한다.
폴더 내부 파일들 끼리의 의존성이 강한 경우 배럴 파일로 인해 의존성에 관련된 문제가 발생할 수 있다.
배럴 파일은 파일 가져오기 프로세스에서 간접적인 중간 계층이 추가되는 것 이므로 특정 값의 출처를 이해하기 더 어려울 수 있다.
배럴 파일은 추가 파일을 로드하고 처리해야 하므로 응용 프로그램의 성능이 약간 저하될 수 있다.
또한, 폴더 구조가 계속 변경된다면 오히려 유지보수 및 코드 변경에 번거로움이 생길 수 있다.
- 의존성 문제
- 성능 최적화
- 폴더 구조에 대한 문제
그럼 내 프로젝트엔 배럴 파일을 도입해도 되는가? 는 중요하게 다루어야 하는 문제이다.
그럼 해당 기술의 문제점을 파악하고, 이 문제점들이 내 프로젝트에는 큰 영향이 없는가를 생각해보아야 한다.
배럴파일에 사용할 컴포넌트가 서로 긴밀하게 연결되어 사용하여야하는 경우는 배럴 파일 사용을 지양하여야 한다.
즉, 한 폴더의 컴포넌트가 독립적으로 수행될 수 있을 때는 사용을 고려할 수 있다.
예를들어, pages 폴더에서의 각 페이지 컴포넌트, hooks 폴더에서의 각 커스텀 훅 등에선 사용하기 충분하다고 판단할 수 있다.
프론트엔드를 개발하는 입장에서 성능 관련 이슈는 매우 민감하다.
사용자가 웹 사이트에 들어와 3초안에 로딩이 되지 않으면 이탈율이 70%가 되는 통계도 존재하는 등 성능에 관련된 문제는 민감하게 접근하여야 한다.
결국 페이지 컴포넌트 import 하는 간접적인 중간계층이 끼어들게 돼 이 컴포넌트가 많으면 성능이 저하될것이다. 라는 말은 머리로 이해할 수 있다.
그러면 '얼마나?'가 중요해진다.
성능이 저하되는 부분이 납득이 가능한 범위라면 이는 도입하기 타당한 기술이라 생각할 수 있다.
실제로 배럴 파일을 활용한 성능 테스트 관련 지표를 찾아서 확인해보았다.

위 출처에 적힌 글을 인용하면 아래와 같다.
보시다시피 모듈을 더 적게 로드하면 그만한 가치가 있습니다. 이러한 숫자를 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할 예정이기 때문에 도입하기 적합하다고 판단할 수 있다.
사소한 내용이라 포스트하진 않았지만, 이미 #4번 포스팅 이후에 폴더 구조에 대한 정립을 마쳤다.

Issue를 생성하고 이를 마쳤기 때문에 폴더 구조가 변경될 일은 거의 없다고 생각할 수 있다.
즉, 이 문제 또한 도입에 문제가 되지 않는다고 판단할 수 있다.
3가지의 단점을 모두 고려하였을 때, 배럴 파일은 내 프로젝트에 도입하기에 부족함이 없는 기술이라 판단할 수 있었다.
그래서, 이 기술을 이제 내 프로젝트에 적용해보려 한다.
우선, 라우터를 만들기 전에 페이지 스켈레톤 컴포넌트를 생성해야 한다.
총 9개의 페이지를 만들어야 한다.
하지만, 하나하나의 페이지를 적당히 만들기엔 너무 귀찮고 의미 없는 시간이 오래걸린다고 생각했다.
나는 vscode extension인 Reactjs code snippets를 사용하고 있었다.
하지만, Emotion과 TypeScript의 타입 및 컴포넌트의 기본 골격에 대한 스니펫은 위의 익스텐션에서 제공하지 않아 불편함을 느꼈다.
그래서 자체적으로 코드 스니펫을 만들었다.
ctrl+shift+p를 클릭하면 vscode의 상단에 검색창이 나오게 된다.
검색창에 snippets라고 입력하자.

위와 같은 옵션을 발견할 수 있다.
클릭해서 다시 typescript를 입력하자.

해당 json파일을 발견할 수 있고, 클릭해서 들어가면 큰 주석이 { }로 감싸진 코드를 발견할 수 있다.
위 사이트에서 본인이 원하는 스켈레톤 코드를 입력하면 위 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에서 가져와 사용할 수 있다.
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여도 잘 작동하는 모습을 볼 수 있다.