[Nextjs] Nextjs + twin.macro + styled-components/ Setup

velgmzz·2024년 1월 23일
0

Nextjs

목록 보기
3/3
post-thumbnail

시작하며

처음에는 간편한 인라인 스타일링을 제공하는 Tailwind가 마음에 들어 사용하게 되었습니다.
Tailwind는 많은 장점을 가지고 있지만, 동적 스타일링과 컴포넌트 내에서 가독성이 정말 떨어지는 className들이 불편했습니다. 이건 정말 아니다싶어 구글링중 twin.macro 라이브러리를 발견했고 기존에 사용하던 Nextjs와 twin.macro, styled-component를 함께 사용하기로 결정했습니다.

twin.macro github

<div className='flex bg-black justify-center items-center md:absolute md:right-0......'>안녕하세요</div>

기존 tailwind만 사용할때 md:absolute md:right-0 md:left-0 이런것들을 twin.macro를 사용하면 md:(absolute right-0 left-0) 묶어서 사용할 수 있습니다.. 감격

twin.macro?

twin.macro는 Tailwind를 사용 할 때 편리한 기능을 제공하는 매크로 패키지입니다.
이 패키지는 styled-components와 함께 사용하여 클래스를 동적으로 생성하고 적용 할 수 있도록 도와줍니다.

설치하기

1. twin.macro와 styled-componets 설치

npm i twin.macro styled-components

package.json 파일에 babelMacro 설정

//package.json
"babelMacros": {
	"twin": {
      "config": "tailwind.config.js",
      "preset": "styled-components"
    }
}

2. babel 설정

npm i -D babel-plugin-macros

.babelrc 파일을 생성하여 아래와 같은 설정을 추가해줍니다.

//babelrc
{
	"presets": ["next/babel"],
    "plugins": ["babel-plugin-macros"]
}

3. tailwind 기초 설정

기존의 tailwind global.css로 GlobalStyle 대체합니다.

//기존 tailwind 설정
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  * {
  	@apply m-0 p-0 box-border;
  }
  
  ol, ul, li {
  	@apply list-none;
  }
}
// _app.js
import './styles/globals.css';

function MyApp({ Component, pageProps}) {...}

Nextjs 12버전 이상

Next.js 12버전 이상에서 컴파일러를 babel에서 swc로 변경했습니다. Nextjs에서 .babelrc 파일이 있다면 컴파일러를 babel로 사용하게됩니다. twin.macro는 babel plugin을 사용하는데 둘 중 하나는 포기해야합니다. 그렇다고 방법이 없는건 아닙니다. styled-components와 같이 사용할 수 있는 방법을 알아보겠습니다.

1. babel plugin 설치

swc와 babel plugin을 같이 사용하려 합니다. 우선 패키지를 설치해주세요

npm i -D babel-loader babel-plugin-macros babel-plugin-styled-components

2. withTwin

최상위 폴더에 withTwin.js를 만들고 복사 해주세요.

const path = require('path');

// 사용할 폴더 경로
const includedDirs = [
  path.resolve( __dirname, 'components' ),
  path.resolve( __dirname, 'pages' ),
  path.resolve( __dirname, 'styles' ),
];

module.exports = function withTwin(nextConfig) {
  return {
    ...nextConfig,
    webpack(config, options) {
      const { dev, isServer } = options;
      const patchedDefaultLoaders = options.defaultLoaders.babel;
      patchedDefaultLoaders.options.hasServerComponents = false;
      patchedDefaultLoaders.options.hasReactRefresh = false;

      config.module = config.module || {};
      config.module.rules = config.module.rules || [];
      config.module.rules.push({
        test: /\.(jsx|js)$/,
        include: includedDirs,
        use: [
          patchedDefaultLoaders,
          {
            loader: 'babel-loader',
            options: {
              sourceMaps: dev,
              plugins: [
                require.resolve('babel-plugin-macros'), //설치 패키지
                [
                  require.resolve('babel-plugin-styled-components'), // 설치 패키지
                  { ssr: true }, //styled-components SSR을 위한 설정입니다.
                ],
              ],
            },
          },
        ],
      });

      if (!isServer) {
        config.resolve.fallback = {
          ...(config.resolve.fallback || {}),
          fs: false,
          module: false,
          path: false,
          os: false,
          crypto: false,
        };
      }

      if (typeof nextConfig.webpack === 'function') {
        return nextConfig.webpack(config, options);
      } else {
        return config;
      }
    },
  };
};

3. next.config.js

원래 styled-components SSR 설정을 하기위해서는 별도의 설정이 필요했는데요. Next.js 공식 문서에서 babel-plugin-styled-components를 이식하기 위해 노력했다고 합니다. 생각보다 내용이 짧으니 한번 읽어보시는걸 추천 드립니다.

Nextjs styled-components

//next.config.js
const withTwin = require( './withTwin' );

const nextConfig = withTwin({ // withTwin 적용
	reactStrictMode: true,
  	swcMinify: true,
  	//...그 외 설정
})

module.exports = nextConfig;

twin.macro 사용

tw 사용

import tw from 'twin.macro';

const TestComponent = tw.div`
	flex
	w-full
	h-full
`

styled components 사용

import { styled } from 'twin.macro';

const TestComponent = styled.div`
	${tw`flex justify-center items-center rounded-md text-white`}

	${props => props.rounded && tw`rounded-full`}
	${props =>
		props.size === 'medium' && tw`h-8`
	}
`

const Test = ({ size, rounded, children }) => {
  return(
  	<TestComponent
    	size={size}
    	rounded={rounded}
    >
          {children}
    </TestComponent>
  )
}

공식문서 styled component guide

공식문서 가이드에서 자주 사용하게 될 방법들입니다.

Basic styling

import tw from 'twin.macro'

const Wrapper = tw.section`flex w-full`
const Column = tw.div`w-1/2`
const Component = () => (
	<Wrapper>
  		<Column></Column>
  		<Column></Column>
  	</Wrapper>
)

Conditional styling

import tw, { styled } from 'twin.macro'

const Container = styled.div(({ hasBg }) => [
  tw`flex w-full`,
  hasBg && tw`bg-black`,
])
const Column = tw.div`w-1/2`

const Component = ({ hasBg }) => (
  <Container {...{ hasBg }}>
    <Column></Column>
    <Column></Column>
  </Container>
)

Variants with many values

import tw, { styled } from 'twin.macro'

const containerVariants = {
  light: tw`bg-white text-black`,
  dark: tw`bg-black text-white`,
  crazy: tw`bg-yellow-500 text-red-500`,
}

const Container = styled.section(() => [
  tw`flex w-full`,
  ({ variant = 'dark' }) => containerVariants[variant],
])
const Column = tw.div`w-1/2`

const Component = () => (
  <Container variant="light">
    <Column></Column>
    <Column></Column>
  </Container>
)

Overriding styles

import tw from 'twin.macro'

const Text = tw.div`text-white`

const Component = () => <Text tw="text-black">Has black text</Text>

Extending components

import tw, { styled } from 'twin.macro'

const Container = tw.div`bg-black text-white`

const BlueContainer = tw(Container)`bg-blue-500`

const RedContainer = styled(Container)(({ hasBorder }) => [
  tw`bg-red-500 text-black`,
  hasBorder && tw`border`,
])


const BlueContainerBold = tw(BlueContainer)`font-bold`

const Component = () => (
  <>
    <Container />
    <BlueContainer />
    <RedContainer hasBorder />
  </>
)

Custom selectors

import tw from 'twin.macro'

const Button = tw.button`
  bg-black
  [> i]:block
  [> span]:(text-blue-500 w-10)
`

const Component = () => (
  <Button>
    <i>Icon</i>
    <span>Label</span>
  </Button>
)
profile
정리하며 공부하는 블로그, React/Next를 공부합니다

0개의 댓글