프론트엔드/Next.js

[Next.js] Next.js 환경에서 MUI integration 방법 + MUI 전역 스타일링 방법

순코딩 2024. 12. 3. 16:59

이 글에서는 Next.js 15버전 환경에서 MUI 테마를 적용하는 방법에 대해 스텝 바이 스텝 형식으로 설명한다.

 

문제 상황

Next.js 15 버전에서 MUI를 활용한 UI 개발 중 반복되는 UI 스타일링상속 중첩에 따른 가독성 저해 문제가 발생했다.

(쉽게 말하자면 일일히 UI 스타일링 하는 것과 다른 UI를 상속 받아 스타일링을 추가했을 때 부모 요소 스타일링을 추적하는 것이 지겨웠다.)

필자는 MUI 전역 스타일링(테마)을 활용하여 위 문제들을 해결하려 했지만 Next.js 환경 특성(서버 컴포넌트, 클라이언트 컴포넌트의 분리 등)에 따라 테마 적용에 어려움을 겪었다.

 

해결 방법
종속성 설치
npm install @mui/material-nextjs @emotion/cache

 

 

app/layout.tsx 수정
// AppRouterCacheProvider 임포트
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';



// <body> 태그 내부의 모든 요소들을 <AppRouterCacheProvider>으로 랩핑한다.
<body>
    <AppRouterCacheProvider>
        {children}
    </AppRouterCacheProvider>
</body>

app/layout.tsx 에 AppRouterCacheProvider 컴포넌트를 적용합니다.

 

전역 테마 파일 생성(app/styles/MuiTheme.ts)
import { createTheme } from '@mui/material/styles';

// MUI 테마 생성
const MuiTheme = createTheme({
    components: {
      // MUI의 TextField 컴포넌트에 대한 스타일 오버라이드
      MuiTextField: { // MUI의 TextField 컴포넌트 지정
        styleOverrides: { // MuiTextField의 내부 스타일을 덮어쓰기
          root: { // MuiTextField의 최상위 DOM 요소를 대상으로 스타일을 적용
            // 포커스 상태에서 입력 필드의 보더(border) 색상 변경
            "& .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline": {
              borderColor: "var(--main-color)", // 포커스 상태의 보더 색상 (CSS 변수 사용)
            },
          },
        },
      },
    },
});

export default MuiTheme;

위 테마 파일을 통해 MUI의 모든 TextField 컴포넌트에 해당 스타일이 적용됩니다.

 

전역 테마 컴포넌트 생성(app/components/ThemeRegistry.tsx)
"use client";

import { ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import MuiTheme from "@/styles/MuiTheme";

export default function ThemeRegistry({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <ThemeProvider theme={MuiTheme}>
      <CssBaseline />
      {children}
    </ThemeProvider>
  );
}

ThemeRegistry.tsx 컴포넌트는 MUI의 ThemeProvider 컴포넌트를 랩핑한 컴포넌트입니다.

layout.tsx에 ThemeProvider를 곧바로 적용하지 않고 ThemeRegistry.tsx 컴포넌트로 랩핑한 이유는 아래와 같습니다.

1. 서버 컴포넌트와 클라이언트 컴포넌트의 분리
Next.js의 App Router에서는 기본적으로 layout.tsx가 서버 컴포넌트로 동작합니다.
하지만 MUI의 ThemeProvider는 클라이언트에서만 동작해야 합니다.
따라서 ThemeRegistry.tsx를 use client로 선언해 클라이언트 전용 컴포넌트로 분리해야 합니다.


2. Emotion 캐시 처리 (SSR 스타일 동기화)
Next.js는 서버 사이드 렌더링(SSR)을 기본으로 합니다.
MUI의 Emotion 스타일을 서버와 클라이언트 간 동기화해야 합니다.
ThemeRegistry에서 Emotion 캐시(CacheProvider)를 함께 설정하면, 서버에서 생성된 스타일이 클라이언트로 제대로 전달되어 hydration mismatch 문제를 방지할 수 있습니다.

 

app/layout.tsx 수정

 

// AppRouterCacheProvider 임포트
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
// ThemeRegistry 임포트
import ThemeRegistry from "@/components/ThemeRegistry";



// <AppRouterCacheProvider>으로 랩핑된 요소를 <ThemeRegistry>로 중첩랩핑한다.
<body>
    <AppRouterCacheProvider>
    	<ThemeRegistry>
            {children}
        </ThemeRegistry>
    </AppRouterCacheProvider>
</body>

 

서버 재시작
npm run dev

 

 

결과

모든 TextField 컴포넌트에 테두리 스타일이 적용된 것을 확인할 수 있다.

 

참고자료

https://mui.com/material-ui/integrations/nextjs/

 

Next.js integration - Material UI

Learn how to use Material UI with Next.js.

mui.com

 

끝.