프론트엔드 엔지니어링 코드의 관심사를 분리하는 방법들입니다. 모두 훔쳐배운 것들입니다.
vercel에서 경험한 이런저런 에러
일단 이것부터 시작하겠습니다.
vercel Build Failed
ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY Broken lockfile: no entry for '/react/18.2.0' in pnpm-lock.yaml
어제 pnpm으로 설치했는데 vercel 배포환경에서 계속 에러가 발생했습니다. 왜 발생하고 누가 해결했는지 자료를 찾을 수 없었습니다.
해결: pnpm을 yarn으로 전환
학습: pnpm은 래퍼런스가 더 쌓였을 때 학습하고 활용합시다.
vercel 404 페이지
"routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }]
https://vercel.com/docs/concepts/projects/project-configuration#rewrites
관심사 분리하기
스타일링
인덱스 엔트리는 이렇게 작성할 수 있습니다.
export * from './flex';
export * from './position';
export * from './textStyle';
라우팅 처리
// App.tsx
import { useRoutes } from 'react-router-dom';
import { routes } from '@/routes/Routes';
export default function App() {
const routedElements = useRoutes(routes);
return <div>{routedElements}</div>;
}
// src/routes/Routes.tsx
import { lazy } from 'react';
import { ROUTE_PATHS } from '@/constants/config';
import GlobalLayout from './_globalLayout';
const SignUpPage = lazy(() => import('@/pages/SignUp'));
const SignInPage = lazy(() => import('@/pages/SignIn'));
const TodoPage = lazy(() => import('@/pages/Todo'));
const WelcomePage = lazy(() => import('@/pages/Welcome'));
export const routes = [
{
path: '/',
element: <GlobalLayout />,
children: [
{ index: true, element: <WelcomePage /> },
{ path: ROUTE_PATHS.signUp, element: <SignUpPage /> },
{ path: ROUTE_PATHS.signIn, element: <SignInPage /> },
{ path: ROUTE_PATHS.todo, element: <TodoPage /> },
],
},
];
레이지 로딩하는 전략이 상당히 흥미롭습니다. 실제 필요한 페이지 를 접근하기 전까지 import를 보류하고 있었습니다.
// src/routes/_globalLayout.tsx
import { Suspense } from 'react';
import { Outlet } from 'react-router-dom';
import Loading from '@/components/Loading';
import { Navbar } from '@/components/Navbar';
import useCheckAuth from '@/hooks/useCheckAuth';
export default function Layout() {
const isLoggedIn = useCheckAuth();
return (
<Suspense fallback={<Loading />}>
<Navbar isLoggedIn={isLoggedIn} />
<Outlet context={isLoggedIn} />
</Suspense>
);
}
로그인을 차러하기 전까지 Nav를 Suspense합니다. 내부도 또 Suspense 처리하는 전략입니다ㅏ.
src/router/Router.jsx / wanted-frontedend-team5
src/routes/Routes.tsx / WANTED-TEAM03
2개의 레포를 비교해보니까 createBrowserRouter
를 사용하는 것이 베스트 프렉티스 같습니다.
스타일링은 개별 모듈로 분리하는 것이 베스트 프렉티스입니다.
스타일링 분리
https://github.com/wanted-frontedend-team5/pre-onboarding-10th-2-5/tree/main/src/styles/utils
/Title
Title.tsx
Title.styles.tsx
index.ts
스타일링과 마크업 그리고 export 모두 분리합니다.
export * from './Title';
// src/components/layouts/AppLayout/AppLayout.styles.js
import styled from 'styled-components';
import { APP_MAX_WIDTH } from 'styles/constants/dimensions';
import { flex } from 'styles/utils';
export const AppLayout = styled.div`
${flex({ justifyContent: 'center' })}
min-height: 100vh;
padding: 16px;
`;
export const Main = styled.main`
width: 100%;
max-width: ${APP_MAX_WIDTH}px;
`;
// src/components/layouts/AppLayout/AppLayout.jsx
import * as Styled from './AppLayout.styles';
export const AppLayout = ({ children }) => {
return (
<Styled.AppLayout>
<Styled.Main>{children}</Styled.Main>
</Styled.AppLayout>
);
};
이런 패턴으로 사용합니다. 스타일링과 마크업을 분리하는 것은 좋습니다. 하지만 스타일이라는 접두어가 필요한지는 의문입니다. 저라면 그냥 import 했을 것입니다.
라우팅 constants
라우팅은 관심사를 묶어주시기 바랍니다.
export const BASE_URL = 'https://www.pre-onboarding-selection-task.shop';
export const API_URLS = {
todos: '/todos',
signIn: '/auth/signin',
signUp: '/auth/signup',
};
export const ROUTE_PATHS = {
welcome: '/',
signIn: '/signin',
signUp: '/signup',
todo: '/todo',
};
상수의 프로퍼티도 어퍼케이스 작성했을 것이지만 일단 좋은 것 같습니다. 그리고 타입스크립트 답게 as const
도 뒤에 추가할 것 같습니다. 진짜로 읽기 전용으로 만들 것 같습니다.