일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- useMutation error
- 고르드
- graphql with reactnative
- 예쁜술집 예술
- promise메서드
- apolloclient
- 비동기배열처리방법
- 잠실새내 도그존
- 잠실새내
- 토라비
- apollo react native
- 앙버터마카롱
- 신촌 소문난집
- graphql react native
- 홍대 토라비
- graphql with RN
- typescript
- 지보싶 신촌점
- graphql
- 비동기배열
- 화이트해커를 위한 웹 해킹의 기술
- 운정 소바동
- promise처리
- 화이트 해커를 위한 웹 해킹의 기술
- 도그존
- 홍대 카페 장쌤
- graphql mutation error
- graphql 400
- 금별맥주
- 홍대 예술
- Today
- Total
yehey's 공부 노트 \n ο(=•ω<=)ρ⌒☆
[Next js] AppRouter 프로젝트에 styled-component 적용하기 본문
Background
next js 를 app router 프로젝트로 시작했다. 그리고 styled-component로 스타일링 하는 것을 선호하기 때문에 설치 후 적용하면서 겪었던 모든 에러를 정리하고자... 쓴다.... (굉장히 다양하게 만남...^^)
Issue 1 : use client 에러
// app/layout.tsx
import styled, { ThemeProvider } from 'styled-components';
import { theme, GlobalStyle } from '@/styles';
import Header from '@/components/Header';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="kr">
<body>
<div id="portal" />
<ThemeProvider theme={theme}>
<GlobalStyle />
<Header title="Home" />
<Applayout>{children}</Applayout>
</ThemeProvider>
</body>
</html>
);
}
const Applayout = styled.div`
display: flex;
flex-direction: column;
min-height: 100%;
padding: 0 1.7rem;
width: 100%;
margin: 0 auto;
`;
레이아웃에 글로벌 스타일 적용하고, 커스텀 레이아웃을 사용하려고 하면
⨯ createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component
라는 에러가 계속 발생했다. 그냥 한마디로 'use client'
를 파일 최상단에 추가하라는 뜻인데,,, 이게 이해가 안갔던 이유는 아니 layout이 이해하기로는 최상단 껍데긴데.. 이 껍데기에 'use client'를 추가해서 클라이언트 컴포넌트 입니다. 라고 선언하면 그 아래도 클라이언트 컴포넌트가 되는 것이 아닌가.. 하면서 이건 해결방법이 아니라는 생각을 했다. 열심히 서치하고 박치기 한 결과, 서버 컴포넌트에서는 styled-component를 통해서 컴포넌트를 생성할 수 없다. 사용하려면 use client 를 명시해야한다. 그러면 layout.tsx는 어떻게 구성하냐!!!!
Solution
//utils/styledComponentsRegistry
'use client';
/* eslint-disable react/jsx-no-useless-fragment */
import React, { useEffect, useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import styled, { ServerStyleSheet, StyleSheetManager } from 'styled-components';
import { GlobalStyle } from '@/styles';
export default function StyledComponentsRegistry({ children }: { children: React.ReactNode }) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
const [isClient, setIsClient] = useState(false);
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return <>{styles}</>;
});
useEffect(() => {
setIsClient(true);
}, []);
// if (typeof window !== 'undefined') return <>{children}</>;
if (isClient) return <>{children}</>;
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
<GlobalStyle />
<Applayout>{children}</Applayout>
</StyleSheetManager>
);
}
const Applayout = styled.div`
display: flex;
flex-direction: column;
min-height: 100%;
width: 100%;
margin: 0 auto;
height: 100%;
`;
//app/layout.tsx
import type { Viewport, Metadata } from 'next';
import StyledComponentsRegistry from '@/lib/utils/StyledSheetManager';
import ReactQueryProvider from '@/lib/utils/ReactQueryProvider';
import { CustomIconDescriptorType } from '@/types/custom';
interface IRootLayoutProps {
children: React.ReactNode;
}
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1.0,
maximumScale: 1,
userScalable: false,
};
export default function RootLayout({ children }: IRootLayoutProps) {
return (
<html lang="kr">
<body>
<div id="portal" />
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
);
}
StyledComponentRegistry를 생성하고 layout.tsx에서는 그걸로 감싸주기만 하면 된다. 사실 이런다고 하위 컴포넌트는 use client가 안되는가? 에 대해 생각을 해봤는데, page.tsx
는 서버 컴포넌트더라 (styled-component 로 감쌌다가 같은 에러를 마주함..ㅎㅎ)
Issue 2 : 또..또..또!!! use client 에러
사실 이렇게 만들어둬도 styled-component만 쓰면 아주 에러를 뱉으면서 안해 안해를 외친다. 열받는다.
styled-comoponent를 써서만 error가 나는게 아니라, component에서 useState, useEffect 등등 리액트가 제공하는 무언가를 사용한 경우, use client
를 해야한다. 그럼 난 component 어떻게 불러오냐!! 라고 화를 낼 뻔 했지만...
우리의 친절한 next use client
를 어디에 쓰라고 힌트까지 주었다. 감사합니다.
Solution
간단하게 해결하자면 component 디렉토리의 index.ts
최상단에 use client
를 명시하면 된다.
사실 그것보다 진짜 사용하는 곳에서만 use client
를 명시하는게 좋을 것 같긴 하다. 그치만 해당 에러를 원천봉쇄하고 싶다면 index.ts
에 적는 것을 추천한다.
// components/index.ts
'use client';
export { default as LedgerItem } from './LedgerItem';
export { default as PostLedger } from './PostLedger';
Error Review
처음엔 영문도 모른채 use client
를 붙여가며 에러를 해결했지만 오히려 서버 컴포넌트, 클라이언트 컴포넌트에 대해 열심히 찾아보고 왜 이렇게 써야하는가에 대해 알아갈 수 있었다. 지금 index.ts에 적용한 use client를 리팩토링 과정에서 꼭 필요한 곳에만 적용할 수 있도록 수정해야지
그리고 page나 layout에 styled-component로 컴포넌트를 그때 그때 생성하는 습관은 지양해야겠다.
Page에서는 이미 완성된 컴포넌트들을 잘 조합할 수 있도록 하고 새로운 컴포넌트 생성은 하지 않는 것으로....!
참조:
https://github.com/vercel/next.js/discussions/50473