신규 프로젝트를 시작하면서 그 과정을 블로그에 정리해보기로 했다.
단순히 화면을 그리는 개발이 아닌 단 한 줄이라도 생각이 담긴 코드를 작성하는데 의미를 두고 과정에 집중하는데 초점을 맞춘 프로젝트이다.
오늘은 처음 시작하는 날로 프로젝트 초기 세팅하는 작업 을 했다.
npx create-next-app@latest
프로젝트 이름을 정해주고 그 외 설정을 하나씩 지정해준다.
TypeScript 와 ESLint 는 yes 로 설정했고, Tailwind CSS 는 styled-components 를 사용할 것이므로 no 로 설정했다.
1) 설치하기
TypeScript 을 사용하기에 아래와 같이 타입도 함께 설치해줬다.
npm i styled-components
npm i -D @types/styled-components
css 초기화를 위해 styled-components 의 reset 도 추가 설치해줬다.
npm i styled-reset
2) 세팅하기
Next.js 공식문서에도 나와있듯이 CSS-in-JS 라이브러리는 현재 Server Components 에서 지원되지 않고 있다.
Styled-Components 는 대표적인 CSS-in-JS 라이브러리로 따라서 먼저 next.config.js에서 styled-component를 활성화해야 한다.
next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
compiler: {
styledComponents: true,
},
};
export default nextConfig;
그 다음 styles 폴더에 registry.tsx 파일을 만들어줬다.
아래 코드는 Next.js 공식문서에 나와있다.
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
export default function StyledComponentsRegistry({ children }: { children: React.ReactNode }) {
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return <>{styles}</>;
});
if (typeof window !== 'undefined') return <>{children}</>;
return <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>{children}</StyleSheetManager>;
}
그리고 ClientComponentContainer 라는 컴포넌트를 하나더 생성해줬다.
이 컴포넌트에는 css 초기화를 위한 GlobalStyles
컴포넌트와 애플리케이션 전반에 사용될 폰트와 색상 등을 미리 지정한 theme.tsx 을 불러와 ThemeProvider
의 prop 으로 넣어줬다.
'use client';
import StyledComponentsRegistry from '@/styles/registry';
import { ThemeProvider } from 'styled-components';
import theme from '@/styles/theme';
import GlobalStyle from '@/styles/GlobalStyle';
interface IClientComponentContainerProps {
children: React.ReactNode;
}
function ClientComponentContainer({ children }: IClientComponentContainerProps) {
return (
<StyledComponentsRegistry>
<GlobalStyle />
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</StyledComponentsRegistry>
);
}
export default ClientComponentContainer;
그리고 RootLayout
컴포넌트에서 ClientComponentContainer 컴포넌트를 불러와 감싸주면, 'use client' 를 사용하지 않기 때문에 Metadata 를 사용할 수 있게 된다.
또한 ClientComponentContainer 컴포넌트로 children 을 감싸줬기 때문에 이하 컴포넌트에 reset 이 적용되고, theme.tsx 의 값들을 불러와 사용할 수 있게 된다.
import ClientComponentContainer from '@/components/ClientComponentContainer';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | omuk',
default: 'omuk',
},
description: 'The best way to find a good restaurant',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ko">
<body>
<ClientComponentContainer>{children}</ClientComponentContainer>
</body>
</html>
);
}
자세한건 공식문서 : https://nextjs.org/docs/app/building-your-application/styling/css-in-js#styled-components
라이브러리를 설치하기에 앞서 Next.js에서 react-query 가 필요한가? 에 대해 고민을 해보았다.
Next.js 만으로도 data fetch 및 캐싱 기능을 사용할 수 있기에 어느정도 React-query 를 대체할 수 있는 부분이 있지 않을까? 하는 생각이 들어서였다..
그러나 고민끝에 무한스크롤이나 loading, error 등을 처리하는데 있어 react-query 를 함께 사용하면 좀 더 효율적으로 처리할 수 있지 않을까 하는 나름대로의 결론이 나서 React-query 를 사용하기로 결정했다.
이 부분은 이번에 Next.js14 와 react-query 를 직접 사용하면서 부딫혀 보는 것이 좋을 것 같다는 생각이 든다.
1) 설치하기
tanstack-query 와 devtools 를 설치해줬다.
npm i @tanstack/react-query
npm i @tanstack/react-query-devtools
2) 세팅하기
React-query 를 전반에서 사용하려면 Provider 를 만들어 최상단에 감싸줘야한다.
따라서 context/Provider.tsx 파일을 생성해 Provider 를 작성해줬다.
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useState } from 'react';
const Providers = ({ children }: { children: React.ReactNode }) => {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
retry: 1, // API 요청 실패시 재시도 하는 옵션
},
},
})
);
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
};
export default Providers;
그리고 최상단에 위치한 Layout.tsx 에 Provider 를 불러와 아래와 같이 감싸줬다.
import ClientComponentContainer from '@/components/ClientComponentContainer';
import Providers from '@/context/Provider';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | omuk',
default: 'omuk',
},
description: 'The best way to find a good restaurant',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ko">
<body>
<Providers>
<ClientComponentContainer>{children}</ClientComponentContainer>
</Providers>
</body>
</html>
);
}
상태관리 라이브러리로는 Zustand 를 선택했다.
나는 리덕스를 좋아하지만, 비교적 작은 규모에서 리덕스를 사용할땐 작성해야 하는 코드가 과하다는 생각이 있었는데 zustand 는 리덕스에 비해 단순화된 구조로 특정 보일러플레이트 코드 없이 쉽게 구현이 가능하다는 점에서 이번 프로젝트에 적합하다는 생각이 들어 선택하게 됐다.
1) 설치하기
zustand는 타입스크립트로 작성된 라이브러리이기 아래 명령어로 간단하게 설치 가능하다.
npm i zustand
2) 세팅하기
zustand 는 redux 와 달리 컴포넌트를 provider로 감쌀 필요가 없기 때문에 별도의 세팅이 필요하지 않았다.
개발을 하다가 상태관리가 필요할때 store 를 만들어서 사용하면 될 것 같다.