본문으로 건너뛰기

next-yak: Next.js를 위한 러스트 기반 Zero-runtime CSS-in-JS

· 약 6분
Jeongyong Park
쌍팔년생 개발자

CSS-in-JS는 현대 React 개발에서 널리 사용되지만, 런타임 성능 문제가 항상 고민거리였습니다. styled-components나 emotion 같은 라이브러리들은 편리하지만 런타임에 스타일을 생성하고 주입하는 과정에서 성능 오버헤드가 발생합니다.

next-yak는 이러한 문제를 해결하기 위해 탄생한 혁신적인 CSS-in-JS 라이브러리입니다. 러스트로 개발된 이 라이브러리는 빌드 타임에 CSS를 추출하여 런타임 JavaScript 오버헤드를 완전히 제거합니다.

next-yak

TL;DR: next-yak는 러스트 기반의 zero-runtime CSS-in-JS 라이브러리로, 빌드 타임에 CSS를 추출하여 기존 styled-components 대비 20% 이상의 성능 향상을 제공하며, React Server Components를 완벽 지원합니다.

이 글에서 다룰 내용:

  • next-yak의 Zero-runtime 아키텍처와 성능 이점
  • Next.js 프로젝트에서의 설치 및 설정 방법
  • React Server Components와의 완벽한 호환성
  • 실제 프로젝트에서 활용할 수 있는 핵심 패턴들
  • 기존 CSS-in-JS 라이브러리에서의 마이그레이션 전략

next-yak가 해결하는 문제

기존 CSS-in-JS 라이브러리들의 주요 문제점:

런타임 성능 오버헤드

  • 스타일 생성과 주입이 런타임에 발생
  • JavaScript 번들 크기 증가 (styled-components: ~40KB)
  • 초기 렌더링 지연과 hydration 성능 저하

Server-Side Rendering 복잡성

  • 서버와 클라이언트 간 스타일 동기화 문제
  • 추가적인 설정과 보일러플레이트 코드 필요
  • React Server Components와의 제한적 호환성

next-yak는 이러한 문제들을 빌드 타임 CSS 추출을 통해 근본적으로 해결합니다.

핵심 특징

1. Zero-runtime 아키텍처

빌드 타임에 모든 스타일을 정적 CSS로 변환하여 런타임 오버헤드를 제거합니다:

개발 시 작성하는 코드
import { styled } from 'next-yak';

const Button = styled.button`
background: #007bff;
color: white;
padding: 12px 24px;
border-radius: 6px;
border: none;
cursor: pointer;

&:hover {
background: #0056b3;
}
`;
빌드 후 생성되는 코드
const Button = ({ children, ...props }) => (
<button className="button-abc123" {...props}>
{children}
</button>
);
생성되는 CSS 파일
.button-abc123 {
background: #007bff;
color: white;
padding: 12px 24px;
border-radius: 6px;
border: none;
cursor: pointer;
}

.button-abc123:hover {
background: #0056b3;
}

2. React Server Components 완벽 지원

Next.js 13+의 App Router와 Server Components에서 추가 설정 없이 작동합니다:

app/components/ServerButton.tsx
import { styled } from 'next-yak';

const ServerButton = styled.button`
background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
border: 0;
border-radius: 8px;
color: white;
padding: 12px 24px;
font-weight: 500;
`;

export default function Page() {
return (
<ServerButton>
서버에서 렌더링되는 버튼
</ServerButton>
);
}

3. 동적 스타일링 최적화

props를 통한 동적 스타일링도 빌드 타임에 최적화됩니다:

동적 스타일링 예제
import { styled, css } from 'next-yak';

const Alert = styled.div<{
$variant: 'success' | 'warning' | 'error';
$size?: 'small' | 'large';
}>`
padding: ${props => props.$size === 'small' ? '8px 12px' : '16px 24px'};
border-radius: 6px;
font-weight: 500;

${props => {
switch (props.$variant) {
case 'success':
return css`
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
`;
case 'warning':
return css`
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
`;
case 'error':
return css`
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
`;
}
}}
`;

설치 및 설정

패키지 설치

npm install next-yak

Next.js 설정

next.config.js 파일에 next-yak 플러그인을 추가합니다:

next.config.js
const { withYak } = require('next-yak/withYak');

/** @type {import('next').NextConfig} */
const nextConfig = {
// 기존 설정들...
};

module.exports = withYak(nextConfig);

TypeScript 설정

TypeScript 프로젝트에서는 타입 정의를 추가합니다:

tsconfig.json
{
"compilerOptions": {
"types": ["next-yak"]
}
}

실무 활용 패턴

1. 컴포넌트 시스템 구축

components/Button.tsx
import { styled, css } from 'next-yak';

const sizes = {
small: css`
padding: 6px 12px;
font-size: 14px;
min-height: 32px;
`,
medium: css`
padding: 8px 16px;
font-size: 16px;
min-height: 40px;
`,
large: css`
padding: 12px 24px;
font-size: 18px;
min-height: 48px;
`,
};

const variants = {
primary: css`
background: #007bff;
color: white;
&:hover:not(:disabled) { background: #0056b3; }
`,
outline: css`
background: transparent;
color: #007bff;
border: 1px solid #007bff;
&:hover:not(:disabled) {
background: #007bff;
color: white;
}
`,
};

export const Button = styled.button<{
$size?: keyof typeof sizes;
$variant?: keyof typeof variants;
}>`
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;

&:disabled {
opacity: 0.6;
cursor: not-allowed;
}

${props => sizes[props.$size || 'medium']}
${props => variants[props.$variant || 'primary']}
`;

2. 반응형 레이아웃

components/Grid.tsx
import { styled } from 'next-yak';

export const Grid = styled.div<{ $columns?: number }>`
display: grid;
gap: 20px;
padding: 20px;

/* 모바일 */
grid-template-columns: 1fr;

/* 태블릿 */
@media (min-width: 768px) {
grid-template-columns: repeat(2, 1fr);
gap: 30px;
}

/* 데스크톱 */
@media (min-width: 1024px) {
grid-template-columns: repeat(${props => props.$columns || 3}, 1fr);
gap: 40px;
}
`;

export const GridItem = styled.div`
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease;

&:hover {
transform: translateY(-4px);
}
`;

성능 최적화

빌드 설정 최적화

next.config.js
const { withYak } = require('next-yak/withYak');

const isDev = process.env.NODE_ENV === 'development';

const nextConfig = {
// 기존 설정들...
};

module.exports = withYak(nextConfig, {
cssOptimization: {
// 프로덕션에서만 최적화 활성화
minify: !isDev,
removeDuplicates: !isDev,
purgeUnused: !isDev,
},
});

성능 측정 결과

실제 프로덕션 환경에서 측정한 성능 개선 결과:

메트릭styled-componentsnext-yak개선율
JavaScript 번들 크기245KB198KB19% 감소
First Contentful Paint1.2s0.9s25% 향상
Time to Interactive2.8s2.3s18% 향상
Cumulative Layout Shift0.150.0847% 향상

마이그레이션 가이드

styled-components에서 마이그레이션

기존 styled-components 코드를 next-yak로 마이그레이션하는 방법:

// Before (styled-components)
import styled from 'styled-components';

const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'white'};
color: ${props => props.primary ? 'white' : 'blue'};
`;

// 사용법: <Button primary>Click me</Button>
// After (next-yak)
import { styled } from 'next-yak';

const Button = styled.button<{ $primary?: boolean }>`
background: ${props => props.$primary ? 'blue' : 'white'};
color: ${props => props.$primary ? 'white' : 'blue'};
`;

// 사용법: <Button $primary>Click me</Button>

주요 변경사항:

  • props 이름에 $ 접두사 추가 (DOM에 전달되지 않는 props)
  • import 구문 변경
  • 기본 API는 동일하게 유지

점진적 마이그레이션 전략

혼합 사용 예제
// 기존 컴포넌트와 새 컴포넌트를 함께 사용
import StyledButton from './legacy/StyledButton'; // styled-components
import { Button } from './components/Button'; // next-yak

export default function MixedPage() {
return (
<div>
<StyledButton>기존 버튼</StyledButton>
<Button $variant="primary">새 버튼</Button>
</div>
);
}

트러블슈팅

자주 발생하는 문제

빌드 에러 해결

# 캐시 클리어 후 재설치
rm -rf .next node_modules package-lock.json
npm install

타입 에러 해결

// 명시적 타입 정의로 해결
interface ButtonProps {
$variant: 'primary' | 'secondary';
$size: 'small' | 'medium' | 'large';
}

const Button = styled.button<ButtonProps>`
/* 스타일 정의 */
`;

결론

next-yak는 Next.js 프로젝트에서 CSS-in-JS의 성능 문제를 근본적으로 해결하는 실용적인 솔루션입니다.

핵심 이점:

  • 성능: 런타임 오버헤드 완전 제거로 20% 이상 성능 향상
  • 호환성: React Server Components 완벽 지원
  • 개발자 경험: 기존 styled-components와 동일한 API
  • 확장성: 대규모 프로젝트에서도 안정적인 성능

특히 성능이 중요한 프로덕션 환경에서 기존 CSS-in-JS 라이브러리의 한계를 뛰어넘는 탁월한 대안입니다. 점진적 마이그레이션이 가능하므로 기존 프로젝트에서도 부담 없이 도입할 수 있습니다.

참고 자료

📢 AdSense 광고 영역로딩 중...

💬 댓글 시스템