내 마음을 대변하는 누군가의 리플....^^;
classpick 개발은 꼭 NextJs로 해보고 싶어서 프로젝트 세팅을 하던 중, 저번달에 NextJs 13가 정식 출시되었고 어느정도 안정화 되었다는 소식이 있어서 꼭 사용해보고자 하여 기존에 Nextjs 12로 세팅해놨던 프로젝트를 NextJs 13으로 마이그레이션을 하였다.
사실 기존에 Nextjs12로 사용할때는 스토리북 세팅 해둔 것도 잘 되었고, emotion에 대한 에러가 하나도 뜨지 않아서 불편함을 잘 몰랐다.
그런데 세팅을 끝내고 개발환경을 싹 돌리는데 아래와 같은 에러가 뜨더구나...
두둥...
바로 emotion
관련 세팅 때문에 client 컴포넌트로 인식, 에러가 발생하는 것이다. 헉 하면서 설마 emotion을 쓰지 못하는건가? 라고 생각하며 이것저것 자료를 찾아봤는데 정답은 아래에 있었다.
https://github.com/emotion-js/emotion/issues/2928
(아니 2022년 10월에 올라온 이슈인데 아직도 해결을 안했냐고)
일해주세요 emotion 개발팀!! 🥹 emotion 이슈에 올라온 것을 보니까 NextJs13의 app디렉토리에서는 아직 emotion 관련된 해결이 안된 것으로 보인다.
https://nextjs.org/docs/app/building-your-application/styling/css-in-js
Next 공식문서를 보니까 styled-component
는 어느정도 해결이 되어 있으므로 emotion과 매우 유사한 방식으로 개선하고 싶다면 styled component를 이용해도 될 것으로 보인다.
하지만 이 글을 쓴 것은 단순히 해결책을 정리하고자 쓴 글이 아니고, 왜 NextJs 13부터는 무엇이 바뀌었길래 이를 사용할 수 없는지 !!! 정리해보고 싶어서 쓰게 된 글이다.
먼저 새롭게 바뀌게 된 Next 13의 app 디렉토리의 동작 방식에 대해서 이해해야 한다. 물론 Emotion컴포넌트가 들어가게 되는 모든 파일에 use client
라고 박아두면 마음은 편하겠으나..서버 컴포넌트를 공부해야하는 입장에서 이렇게 단순하게 해결하려고 하면 더이상 배울 게 없지 않을까 싶다.
root에 존재하는 Layout
파일에 emotion cache를 추가해주고 모든 파일에 use client
를 쓰면 코드 작성 시에 불필요하고 중복되는 코드를 작성할 뿐만 아니라, Next를 쓰는 의미도 사라지게 될 것이다.
사실, 클라이언트 컴포넌트를 제대로 잘 섞어쓰면 되는거 아냐?! 싶다. 그러나 app 컴포넌트를 제대로 쓰는 법을 배우고 싶기도 해서 조금 더 욕심 내서 tailwind
로 바꿔볼까 생각이 들기도 한다.
먼저 Next13의 app 디렉토리에 대해서 더 잘 이해하기 위해서는 아래 글을 꼼꼼히 읽어보아야 한다.
https://nextjs.org/docs/getting-started/react-essentials
서버 컴포넌트는 Next 13부터 실험적으로 새롭게 생긴 기능이다. 서버 컴포넌트를 사용하게 되면 개발자는 서버 컴포넌트의 장점과 클라이언트 컴포넌트의 장점을 아우르는 서비스를 제작할 수 있다. 즉,
클라이언트 사이드 앱의 풍부한 상호작용성과 전통적인 서버 랜더링의 개선된 성능을 개선할 수 있다.
보통 페이지를 만들 때 하나의 컴포넌트로 취급하지 않고 작은 컴포넌트 단위로 쪼개서 만들 것이다. 더 작은 상호작용이 필요한 UI일 경우에는 클라이언트 컴포넌트를 사용할 수 있다. 대부분의 컴포넌트는 interactive하지 않으므로, 서버컴포넌트로 작성해도 될 것이다.
장점이 있어야 이용할 만한 매력을 느끼지 않을까?! 서버 컴포넌트의 이점을 정리해보자.
서버 컴포넌트를 쉽게 이용하기 위해서 Next13부커는 app/
디렉토리에 있는 모든 컴포넌트는 기본적으로 서버 컴포넌트로 취급되는 것이다. 만약 클라이언트 컴포넌트를 사용해야 한다면 선택적으로 use client
를 사용하면 되는 것이다.
클라이언트 컴포넌트로 사용하고 싶다면 맨 앞 파일에 use client
를 덧붙이면 된다. 단, 주의해야 할 점은 해당 파일을 포함하여 import된 다른 모듈과 자식 컴포넌트들은 클라이언트 번들로 포함된다는 점이다.
서버에서는 React가 모든 서버 컴포넌트를 랜더링한 이후에 그 결과를 클라이언트에 전송한다.여기서 당연히 클라이언트 컴포넌트는 제외되고 전송될것이다.
이제 런타임에 클라이언트에서 클라이언트 컴포넌트 및 넘겨받은 서버 컴포넌트의 랜더링 결과를 화면에 그려낼 것이다.
이제 기본적으로 서버 컴포넌트를 지원하는 상황에서 클라이언트, 서버 컴포넌트를 적절히 잘 섞어 쓰는 것이 매우 중요해졌다고 볼 수 있다.
client/server 컴포넌트를 사용할 때의 꿀팁 모음이다. 🍯
위에서 말한것처럼 클라이언트 컴포넌트를 선언하게 되면 그에 포함되는 모듈과 함께 자식 컴포넌트들도 모두 클라이언트로 처리를 해버린다. 그렇기 때문에 로고, 링크 등 정적 요소를 포함하는 레이아웃과 대화형 검색 창이 있을 경우 전체 레이아웃을 최대한 서버 컴포넌트로 만들고, 상호작용과 관련한 컴포넌트만 클라이언트 컴포넌트로 작성해도 된다.
위에서 말한 것처럼 클라이언트 컴포넌트 안에 서버 컴포넌트를 작성하게 된다면 클라이언트 컴포넌트로 작성한 것과 같은 별 효과가(..)없을 것이다. 그렇기 때문에 아래와 같이 작성하는 것을 공식문서에서는 추천하더라.
'use client';
// This pattern will **not** work!
// You cannot import a Server Component into a Client Component.
import ExampleServerComponent from './example-server-component';
export default function ExampleClientComponent({
children,
}: {
children: React.ReactNode;
}) {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<ExampleServerComponent />
</>
);
}
'use client';
import { useState } from 'react';
export default function ExampleClientComponent({
children,
}: {
children: React.ReactNode;
}) {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
);
}
예시로 나온 ExampleClientComponent
는 children이 누구인지 알지 못한다. 해당 클라이언트 컴포넌트는 누구인지모를 자식 컴포넌트를 잘 배치해주기만 하면 되는 것이다. ㅎㅎ
import ExampleClientComponent from './ExampleClientComponent';
import ExampleServerComponent from './ExampleServerComponent';
function ParentServerComponent() {
return (
<ExampleClientComponent>
<ExampleServerComponent />
</ExampleClientComponent>
);
}
그럼 이제 이렇게 코드를 작성할 수 있다!
서버 컴포넌트인 ParentServerComponent
입장에서는 ExampleClientComponent
와 ExampleServerComponent
를 모두 가져와서 사용하게 되는데 그들의 랜더링이 독립적으로 각각 일어나, 서버 컴포넌트가 먼저 랜더링을 할 수 있게 된다.
일반적으로 이렇게 서버 컴포넌트는 작성하는 코드 패턴이 정해져 있고 이를 지켜서 코드를 작성하는 것이 중요하다고 판단될 수 있겠다.
현재 emotion 때문에 화면의 첫 페이지부터 오류를 마주하고 있다..ㅎㅎ
아직은 emotion에 대한 서버컴포넌트 관련한 뾰족한 수가 없는 상황이다. 있다고 해도,
/** @jsxImportSource @emotion/react */
이렇게 프로바이더를 줘 놓고, emotion을 사용하고 싶은 컴포넌트에는 use client
라고 언급을 하는 수 밖에 없다. 이렇게 코드를 작성하고 서버 컴포넌트와 클라이언트 컴포넌트를 유연하게 대처해도 되겠다.
그러나 이렇게 사용하게 되면 개발하다가 불필요한 프로바이더를 여러번 작성해야할 수도 있고 머리털 뽑힐 것 같기 때문에....
styled-component를 이용하여 스토리북을 완성하고 서버 관련된 컴포넌트에 스타일이 필요할 경우에는 Tailwind css를 이용해야겠다.
사실은 아직도 styled-component가 최적화 진행중이고, 완벽히 서버컴포넌트로 사용하기 어려운 면이 많다. 그렇기 때문에 정말 결국에는 tailwind를 배워야 할 것 같다는 생각도 든다.
개인적으로 클래스명이 너무 길어지는거 별로 안좋아해서, tailwind는 정말 사용하기 싫었는데 이렇게 css in js가 서버사이드랜더링이 발전함에 따라 사용하기 어려워지는 현상이 일어난다면 나도 배움에 있어서 생각을 바꿔야 하는게 아닌가 싶다.
고민하는 모습이 멋있습니다! 글 잘 읽었습니다!!