

서버 컴포넌트는 서버에서 렌더링되고 클라이언트로 전달되며, 클라이언트에서는 JavaScript가 실행되지 않습니다.
반면 클라이언트 컴포넌트는 클라이언트에서 렌더링되고 JavaScript가 실행됩니다.
서버 컴포넌트 내부에 클라이언트 컴포넌트가 있는 것은 허락이 되나,
클라이언트 컴포넌트 내부에 서버 컴포넌트를 둘 수는 없습니다.
(사실상 클라이언트 컴포넌트 내부에 서버 컴포넌트랍시고 "use client"를 선언하지 않고 컴포넌트를 내부에 둬도
결국 클라이언트 컴포넌트로 동작하게 되는 것 같았습니다.
그래서 제가 서버 컴포넌트라고 생각한 곳에서 async, await를 사용하여 데이터 패칭을 진행하려고 했으나,
클라이언트 컴포넌트 내부에서는 async, await를 사용할 수 없다는 에러가 떴습니다.)

그런데 현재,
next.js 14 App Router에서는 서버 컴포넌트를 클라이언트 컴포넌트에 프롭으로 전달하기를 지원한다는 얘기를 들었습니다.
일반적인 패턴은 React children 프로퍼티를 사용하여 클라이언트 컴포넌트에 "슬롯"을 만드는 것입니다.
https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#supported-pattern-passing-server-components-to-client-components-as-props
Rendering: Composition Patterns | Next.js
Recommended patterns for using Server and Client Components.
nextjs.org
'use client'
import { useState } from 'react'
export default function ClientComponent({
children,
}: {
children: React.ReactNode
}) {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
)
}
아래 예시에서 <ClientComponent>는 자식 프로퍼티를 받습니다 :
<ClientComponent>는 서버 컴포넌트의 결과에 의해 자식이 채워질 것이라는 것을 알지 못합니다.
<ClientComponent>가 가진 유일한 책임은 자식이 최종적으로 배치될 위치를 결정하는 것입니다.
상위 서버 컴포넌트에서 <ClientComponent>와 <ServerComponent>를 모두 가져와서
<ServerComponent>를 <ClientComponent>의 하위로 전달할 수 있습니다
// This pattern works:
// You can pass a Server Component as a child or prop of a
// Client Component.
import ClientComponent from './client-component'
import ServerComponent from './server-component'
// Pages in Next.js are Server Components by default
export default function Page() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
)
}
이 접근 방식을 사용하면 <ClientComponent>와 <ServerComponent>가 분리되어 독립적으로 렌더링될 수 있습니다.
이 경우 <ClientComponent>가 클라이언트에서 렌더링되기 훨씬 전에
서버에서 자식 <ServerComponent>가 렌더링될 수 있습니다.
알아두면 좋습니다 :
부모 컴포넌트가 다시 렌더링될 때 중첩된 자식 컴포넌트가 다시 렌더링되지 않도록 하기 위해
"콘텐츠 위로 올리기" 패턴이 사용되었습니다.
자식 프로퍼티에만 국한되지 않습니다. 모든 props를 사용하여 JSX를 전달할 수 있습니다 :
이것에 대해서 왜 고민을 시작하였는가? :
현재 진행 중인 프로젝트에서,
layout.tsx(가장 상위 RootLayout이 아닌 특정 layout)가 클라이언트 컴포넌트로 이뤄져 있습니다.
그리고 이 내부에는 Sidebar component, children 등이 위치하고 있습니다.
Sidebar component 내부에는 데이터를 패칭하는 코드가 있습니다.
저는 이걸 서버 컴포넌트를 사용하여 데이터 패칭을 진행하여 프리렌더링이 되도록 하고 싶었습니다.(렌더링시 빈 공백이 보이거나 loading 화면이 보이는 걸 원치 않았기 때문)
layout.tsx(main) - Sidebar - MyLibrary - MyLibraryData
현재 이런 식으로 이어지는데, 제가 처음에 선택한 방식은MyLibraryData component를 서버 컴포넌트로 만들어서 거기서 데이터 패칭을 하는 것이었는데, 위에서 말한 대로 이 컴포넌트가 속한 최상위(layout.tsx이나 page.tsx)가 클라이언트 컴포넌트로 이뤄져 있어 클라이언트 컴포넌트에서는 async, await를 사용할 수 없다는 문구가 떴습니다.
너무 당황스러웠습니다... 지금까지는 패칭을 진행하는 그 컴포넌트만 서버 컴포넌트이면 서버 컴포넌트로서 동작을 할 거라고 생각했는데, 그게 아니었던 것입니다.
하지만 전 너무 Sidebar를 프리랜더링 되게끔 만들고 싶었습니다. 그런데 지금 상황에서는 가장 최상위에 있는 layout.tsx를 서버 컴포넌트로 만드는 게 최선... 저는 절대 layout.tsx를 서버 컴포넌트로 만들 수 없었기 때문에 포기해야 하나 생각하고 있었습니다.
그러던 중, 위의 서버 컴포넌트를 클라이언트 컴포넌트에 프롭으로 전달하기를 지원한다는 글을 보게 된 것입니다. 이 방법을 사용하면 클라이언트 컴포넌트와 서버 컴포넌트가 분리되어 독립적으로 렌더링될 수 있다는 문구. 이거라면 최상위 부모 컴포넌트가 클라이언트 컴포넌트 레이아웃이어도 이에 영향을 받지 않고 독단적으로 서버 컴포넌트로 동작하지 않을까? 하고 MyLibrary, MyLibraryData 두 컴포넌트를 위의 방식처럼 구성해 사용해보았습니다. 응 결과는 실패했습니다^^
왜 실패한 건지는... 아직도 잘 모르겠네요.(;) 최상위 컴포넌트가 클라이언트 컴포넌트라는 것에서 벗어나지 못하여 서버 컴포넌트로 전환하고 데이터를 패칭했음에도 이게 통하지 않는 걸지도 모릅니다.(그냥 제 추측입니다. 누구든지 정답 좀 알려도)
최상위 클라이언트 컴포넌트인 layout.tsx 바로 아래에 있는 Sidebar를 서버 컴포넌트로 만들어 데이터 패칭을 진행한 다음 그렇게 탄생한 데이터를 MyLibraryData까지 넘겨주지 않는 이상 절대 불가능한 얘긴가 봅니다. 최상위 클라이언트 컴포넌트 바로 아래에 있는 컴포넌트를 위의 방식을 사용하여 서버 컴포넌트로 만들면 최상위 클라이언트 컴포넌트의 영향을 안 받지 않을까요...?라는 막연한 생각을 가지고 시도해보려고 했으나 Layout component를 가지고 이를 시도하는 것은 불가능한 얘기였기에모든 걸 내려놨습니다. 그냥 Suspense 연동해서 Sidebar component에서는 로딩 화면이 뜨게 하렵니다...(여러분 제가 잘못 생각하고 있는 거라면 제발 정답을 알려줘)
'Next.js' 카테고리의 다른 글
Next.js 14) 서버 컴포넌트에서 데이터 패칭 (0) | 2024.02.24 |
---|---|
Next.js 14) Suspense를 사용한 로딩 UI (0) | 2024.02.24 |
Next.js 14) 대충 내가 헷갈려서 적어본 url 받는 방법. (0) | 2024.02.24 |
Next.js 14) 현재 URL의 쿼리 문자열 얻어내기 (+Link (0) | 2024.02.23 |
구글 소셜 로그인 구현하기 (0) | 2024.02.06 |
github : https://github.com/dnjfht
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!