CSS-in-JS 솔루션 (styled-components)
CSS-in-JS는 JavaScript를 사용하여 컴포넌트의 스타일을 직접 정의하는 기법입니다.
이 절에서는 Next.js에서 대표적인 CSS-in-JS 라이브러리인 styled-components의 사용법, 장단점, 그리고 서버 컴포넌트와의 호환성에 대해 알아보겠습니다.
styled-components 설정
- 패키지 설치
npm install styled-components
- .babelrc 파일 생성 (SSR 지원을 위해)
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
- app/layout.js 파일 생성 (SSR 스타일 주입)
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
import { useServerInsertedHTML } from 'next/navigation';
export default function RootLayout({ children }) {
const sheet = new ServerStyleSheet();
useServerInsertedHTML(() => {
const styles = sheet.getStyleElement();
sheet.seal();
return <>{styles}</>;
});
return (
<html lang="en">
<head />
<body>
<StyleSheetManager sheet={sheet.instance}>
{children}
</StyleSheetManager>
</body>
</html>
);
}
기본 사용법
import styled from 'styled-components'
const Button = styled.button`
background-color: #0070f3;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #0051a2;
}
`
export default function MyComponent() {
return <Button>Click me</Button>
}
동적 스타일링
props를 사용하여 동적으로 스타일을 변경할 수 있습니다.
const Button = styled.button`
background-color: ${props => props.primary ? '#0070f3' : '#ffffff'};
color: ${props => props.primary ? 'white' : '#0070f3'};
padding: 10px 20px;
border: 2px solid #0070f3;
border-radius: 5px;
`
export default function MyComponent() {
return (
<>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</>
)
}
테마 적용
ThemeProvider를 사용하여 전역 테마를 적용할 수 있습니다.
import { ThemeProvider } from 'styled-components'
const theme = {
colors: {
primary: '#0070f3',
secondary: '#ff4081',
},
fonts: {
main: 'Arial, sans-serif',
}
}
const Button = styled.button`
background-color: ${props => props.theme.colors.primary};
font-family: ${props => props.theme.fonts.main};
`
export default function App({ Component, pageProps }) {
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
)
}
장단점
장점
- 컴포넌트와 스타일의 강력한 결합
- 동적 스타일링 용이
- 자동 벤더 프리픽싱
- 중복 스타일 제거
단점
- 런타임 오버헤드
- 서버 사이드 렌더링 설정 복잡성
- 기존 CSS 도구와의 호환성 문제
서버 컴포넌트와의 호환성
styled-components는 클라이언트 컴포넌트에서만 사용 가능합니다.
서버 컴포넌트에서는 직접 사용할 수 없으며, 클라이언트 컴포넌트로 래핑하여 사용해야 합니다.
// ServerComponent.js
import ClientComponent from './ClientComponent'
export default function ServerComponent() {
return <ClientComponent>Hello from server</ClientComponent>
}
// ClientComponent.js
'use client'
import styled from 'styled-components'
const StyledDiv = styled.div`
color: blue;
`
export default function ClientComponent({ children }) {
return <StyledDiv>{children}</StyledDiv>
}
7장 서버, 클라이언트 컴포넌트 연관성
CSS-in-JS 솔루션은 7장에서 다룬 클라이언트 컴포넌트와 밀접하게 연관됩니다.
styled-components와 같은 라이브러리는 클라이언트 컴포넌트에서만 사용 가능하므로, 서버 컴포넌트와 함께 사용할 때는 주의가 필요합니다.
스타일이 필요한 부분을 클라이언트 컴포넌트로 분리하고, 서버 컴포넌트에서는 이를 import하여 사용하는 패턴을 적용해야 합니다.
실습 : 다크 모드 전환 가능한 레이아웃 구현
다음 요구사항을 만족하는 다크 모드 전환 레이아웃을 구현해보세요.
- 라이트/다크 테마 정의
- 테마 전환 버튼 구현
- 테마에 따른 스타일 적용
구현 예시
// theme.js
export const lightTheme = {
body: '#ffffff',
text: '#000000',
toggleBorder: '#ffffff',
background: '#f4f4f4',
}
export const darkTheme = {
body: '#363537',
text: '#fafafa',
toggleBorder: '#6B8096',
background: '#999',
}
// ThemeToggle.js
'use client'
import { useState } from 'react'
import { ThemeProvider } from 'styled-components'
import { lightTheme, darkTheme } from './theme'
import styled from 'styled-components'
const ToggleButton = styled.button`
background: ${({ theme }) => theme.background};
color: ${({ theme }) => theme.text};
border: 2px solid ${({ theme }) => theme.toggleBorder};
padding: 10px;
border-radius: 5px;
`
const Layout = styled.div`
background: ${({ theme }) => theme.body};
color: ${({ theme }) => theme.text};
min-height: 100vh;
padding: 20px;
`
export default function ThemeToggle({ children }) {
const [theme, setTheme] = useState('light')
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light')
}
return (
<ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>
<Layout>
<ToggleButton onClick={toggleTheme}>
Toggle Theme
</ToggleButton>
{children}
</Layout>
</ThemeProvider>
)
}
// page.js
import ThemeToggle from '../components/ThemeToggle'
export default function Home() {
return (
<ThemeToggle>
<h1>Welcome to my app</h1>
<p>This is a paragraph with theme-based styling.</p>
</ThemeToggle>
)
}
이 실습을 통해 styled-components를 사용하여 테마를 정의하고, 동적으로 테마를 전환하는 방법을 경험할 수 있습니다.
이는 다크 모드 구현이나 사용자 정의 테마 기능을 개발할 때 유용한 패턴입니다.
styled-components를 비롯한 CSS-in-JS 솔루션은 동적 스타일링과 테마 적용에 강점을 가지고 있지만, 서버 컴포넌트와의 호환성 및 성능 최적화에 주의가 필요합니다.
프로젝트의 요구사항과 팀의 선호도를 고려하여 적절한 스타일링 방식을 선택하는 것이 중요합니다.