{"id":787,"date":"2026-05-18T23:02:56","date_gmt":"2026-05-18T15:02:56","guid":{"rendered":"https:\/\/junai.ai\/blog\/react-server-components-23\/"},"modified":"2026-05-19T20:26:31","modified_gmt":"2026-05-19T12:26:31","slug":"react-server-components-23","status":"publish","type":"post","link":"https:\/\/junai.ai\/blog\/react-server-components-23\/","title":{"rendered":"React Server Components \uc815\ub9ac (Ch.23)"},"content":{"rendered":"\n<!-- WordPress REST API \ubc1c\ud589\uc6a9 HTML (\uc790\ub3d9 \uc0dd\uc131) -->\n<!-- WP-FEATURED-MEDIA-ID: 761 -->\n<div style=\"max-width:800px;margin:0 auto;\">\n<style>\n:root {--color-primary:#0891b2;--color-accent:#06b6d4;--color-bg:#f8fafc;--color-bg-card:#fff;--color-text:#0f172a;--color-text-muted:#64748b;--hero-start:#0f172a;--hero-end:#0891b2;}\n*{box-sizing:border-box;}\n.container{max-width:760px;margin:0 auto;padding:0 24px 80px;}\n.hero{background:linear-gradient(135deg,var(--hero-start) 0%,var(--hero-end) 100%);color:#fff;padding:80px 24px 60px;text-align:center;}\n.hero .eyebrow{display:inline-block;font-size:14px;color:#67e8f9;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;margin-bottom:14px;}\n.hero h1{font-size:36px;margin:0 0 16px;line-height:1.3;font-weight:800;}\n.hero p{color:#cffafe;font-size:18px;max-width:640px;margin:0 auto;line-height:1.6;}\n.hero img{width:100%;max-width:640px;height:auto;margin:32px auto 0;border-radius:10px;display:block;}\narticle{padding-top:48px;}\narticle h2{font-size:26px;margin:56px 0 20px;padding-left:14px;border-left:5px solid var(--color-accent);line-height:1.4;}\narticle h3{font-size:19px;margin:32px 0 12px;color:var(--color-primary);}\narticle p{margin:16px 0;}\narticle strong{color:var(--color-primary);font-weight:700;}\narticle code{background:#ecfeff;padding:2px 8px;border-radius:4px;font-family:'SF Mono',Menlo,Consolas,monospace;font-size:14px;color:#0e7490;}\n.databox{background:#ecfeff;border-left:4px solid var(--color-accent);padding:16px 20px;margin:24px 0;border-radius:0 8px 8px 0;font-size:15.5px;}\n.databox strong{color:var(--color-primary);}\n.warnbox{background:linear-gradient(135deg,#fef3c7 0%,#fde68a 100%);padding:16px 20px;margin:24px 0;border-radius:8px;font-size:15.5px;}\n.tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:22px 0;}\ntable{width:100%;border-collapse:collapse;font-size:15px;background:var(--color-bg-card);}\nth,td{padding:11px 12px;text-align:left;border-bottom:1px solid #e2e8f0;vertical-align:top;}\nth{background:#f1f5f9;font-weight:700;color:#0f172a;}\ntd:first-child,th:first-child{font-weight:700;}\n@media (max-width:560px){.tablewrap table,.tablewrap thead,.tablewrap tbody,.tablewrap tr,.tablewrap th,.tablewrap td{display:block;width:auto;}.tablewrap thead{display:none;}.tablewrap tr{margin:0 0 14px;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;}.tablewrap td{border:none;border-bottom:1px solid #f1f5f9;padding:9px 14px;}.tablewrap td:first-child{background:#f1f5f9;font-weight:800;font-size:15.5px;}.tablewrap td:last-child{border-bottom:none;}.tablewrap td[data-label]::before{content:attr(data-label) \" \u2014 \";font-weight:700;color:var(--color-primary);}}\n.code-block{background:#0f172a;color:#e2e8f0;padding:16px 20px;border-radius:8px;font-family:'SF Mono',Menlo,Consolas,monospace;font-size:14px;line-height:1.6;margin:20px 0;overflow-x:auto;white-space:pre;}\n.cta{background:linear-gradient(135deg,#0891b2 0%,#06b6d4 100%);color:#fff;padding:28px 24px;border-radius:12px;margin:48px 0 0;text-align:center;}\n.cta h3{color:#fff;margin:0 0 8px;font-size:20px;}\n.cta p{color:#cffafe;margin:0;font-size:15.5px;}\n.footer-nav{margin-top:32px;padding-top:20px;border-top:1px solid #e2e8f0;font-size:14px;color:var(--color-text-muted);}\n.footer-nav a{color:var(--color-primary);text-decoration:none;}\n@media (max-width:480px){.hero h1{font-size:26px;}.hero p{font-size:16px;}article h2{font-size:21px;}article h3{font-size:17px;}body{font-size:16px;}}\n<\/style>\n<section class=\"hero\">\n  <span class=\"eyebrow\">React \uad50\uc7ac \u00b7 \uace0\uae09 23\ud3b8<\/span>\n  <h1>React Server Components \uc815\ub9ac<\/h1>\n  <p>&#8220;useEffect \uc548\uc5d0\uc11c fetch&#8221; \uc2dc\ub300\ub97c \ub05d\ub0b8 \ud328\ub7ec\ub2e4\uc784. RSC \uac00 \ubc14\uafb8\ub294 \uac83\uacfc \uc548 \ubc14\uafb8\ub294 \uac83.<\/p>\n  <img decoding=\"async\" src=\"https:\/\/junai.ai\/blog\/wp-content\/uploads\/2026\/05\/hero-5-44.jpg\" alt=\"\uc11c\ubc84\uc640 \ube0c\ub77c\uc6b0\uc800 \uc0ac\uc774 \ub370\uc774\ud130 \ub77c\uc778 \uc77c\ub7ec\uc2a4\ud2b8 \u2014 RSC \ucee8\uc149\">\n<\/section>\n\n<div class=\"container\">\n<article>\n\n<p>React Server Components (RSC) \ub294 React 18 (2022) \uc5d0\uc11c \ub3c4\uc785\ub3fc Next.js 13 App Router (2023) \ubd80\ud130 \ubcf8\uaca9\ud654. 2026 \ub144 \ud604\uc7ac Next.js \uc758 \uae30\ubcf8 \ubaa8\ub378 + \ub2e4\ub978 \uba54\ud0c0 \ud504\ub808\uc784\uc6cc\ud06c (Remix\u00b7Waku) \ub3c4 \ucc44\ud0dd \uc911. 1~22\ud3b8\uc758 \ubaa8\ub4e0 \ud328\ud134\uc740 \uc5ec\uc804\ud788 \uc720\ud6a8\ud558\uc9c0\ub9cc, RSC \uac00 \ucd94\uac00\ub418\uba74\uc11c &#8220;\uc5b4\ub514\uc11c \ubb34\uc5c7\uc744 \ud560\uc9c0&#8221; \uac00 \ub2e4\uc2dc \uc815\ub9ac\ub410\ub2e4.<\/p>\n\n<p>\uc774\ubc88 23\ud3b8\uc740 RSC \uc758 \uc815\ud655\ud55c \uc758\ubbf8 + &#8216;use client&#8217; \/ &#8216;use server&#8217; \uacbd\uacc4 + \ub370\uc774\ud130 fetch \uac00 \ucef4\ud3ec\ub10c\ud2b8 \uc548\uc73c\ub85c \ub4e4\uc5b4\uc624\ub294 \ubaa8\ub378 + \uc2e4\uc804 \ub3c4\uc785 \uac00\uc774\ub4dc.<\/p>\n\n<h2>1. \ub450 \uc885\ub958\uc758 \ucef4\ud3ec\ub10c\ud2b8 \u2014 \uc11c\ubc84 vs \ud074\ub77c\uc774\uc5b8\ud2b8<\/h2>\n\n<p>RSC \ub3c4\uc785 \ud6c4 \ucef4\ud3ec\ub10c\ud2b8\uac00 2 \uc885\ub958\ub85c \uba85\ud655\ud788 \ub098\ub25c\ub2e4.<\/p>\n\n<div class=\"tablewrap\">\n<table>\n<thead><tr><th>\uad6c\ubd84<\/th><th>Server Component<\/th><th>Client Component<\/th><\/tr><\/thead>\n<tbody>\n<tr><td>\uc2e4\ud589 \uc704\uce58<\/td><td data-label=\"Server\">\uc11c\ubc84 (Node\u00b7Edge)<\/td><td data-label=\"Client\">\ube0c\ub77c\uc6b0\uc800 (\uae30\uc874\uacfc \ub3d9\uc77c)<\/td><\/tr>\n<tr><td>\ud45c\uc2dc \ubc29\uc2dd<\/td><td data-label=\"Server\">\uae30\ubcf8\uac12. &#8216;use client&#8217; \uc5c6\uc73c\uba74 server<\/td><td data-label=\"Client\">\ud30c\uc77c \uccab\uc904 &#8216;use client&#8217;<\/td><\/tr>\n<tr><td>useState\u00b7useEffect<\/td><td data-label=\"Server\">\u274c \uc0ac\uc6a9 \ubd88\uac00<\/td><td data-label=\"Client\">\u2705 \uac00\ub2a5<\/td><\/tr>\n<tr><td>DB\u00b7\ud30c\uc77c \uc9c1\uc811 \uc811\uadfc<\/td><td data-label=\"Server\">\u2705 async \ud568\uc218\ub85c \uc790\uc720\ub86d\uac8c<\/td><td data-label=\"Client\">\u274c API \ud1b5\ud574\uc11c\ub9cc<\/td><\/tr>\n<tr><td>\ubc88\ub4e4\uc5d0 \ud3ec\ud568<\/td><td data-label=\"Server\">\u274c HTML \ub9cc \uc804\uc1a1<\/td><td data-label=\"Client\">\u2705 JS \ubc88\ub4e4\uc5d0 \ud3ec\ud568<\/td><\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n\n<h2>2. \ub370\uc774\ud130 fetch \uac00 \ucef4\ud3ec\ub10c\ud2b8 \uc548\uc73c\ub85c<\/h2>\n\n<p>\uac00\uc7a5 \ud070 \ubcc0\ud654. 17\ud3b8 TanStack Query \uac00 \ud074\ub77c\uc774\uc5b8\ud2b8 fetch \uc758 \ud45c\uc900\uc774\uc5c8\ub2e4\uba74, RSC \ub294 <strong>\uc11c\ubc84\uc5d0\uc11c \uc9c1\uc811 DB \ud638\ucd9c<\/strong>.<\/p>\n\n<div class=\"code-block\">\/\/ app\/users\/page.tsx (Server Component \u2014 &#8216;use client&#8217; \uc5c6\uc74c)\nimport { db } from &#8216;@\/lib\/db&#8217;;\n\nexport default async function UsersPage() {\n  \/\/ \u2190 \ucef4\ud3ec\ub10c\ud2b8\uac00 async \ud568\uc218!\n  const users = await db.users.findMany();   \/\/ DB \uc9c1\uc811 \uc811\uadfc\n  return (\n    &lt;ul&gt;\n      {users.map(u =&gt; &lt;li key={u.id}&gt;{u.name}&lt;\/li&gt;)}\n    &lt;\/ul&gt;\n  );\n}<\/div>\n\n<p>\uc774 \ud55c \ucef4\ud3ec\ub10c\ud2b8\uac00 \u2192 \uc11c\ubc84\uc5d0\uc11c DB \ucffc\ub9ac \u2192 HTML \uc0dd\uc131 \u2192 \ube0c\ub77c\uc6b0\uc800\ub85c \uc804\uc1a1. <strong>useEffect \ub3c4 \uc5c6\uace0 loading state \ub3c4 \uc5c6\uace0 useQuery \ub3c4 \uc5c6\ub2e4<\/strong>. \ud074\ub77c\uc774\uc5b8\ud2b8 JS \ubc88\ub4e4\uc5d0 \uc774 \ucf54\ub4dc\ub294 1\ubc14\uc774\ud2b8\ub3c4 \uc548 \ud3ec\ud568\ub428.<\/p>\n\n<div class=\"databox\">\n<strong>\uc7a5\uc810<\/strong> \u2014 \u2460 \ubc88\ub4e4 \ud06c\uae30 \uac10\uc18c (\uc774 \ucef4\ud3ec\ub10c\ud2b8 + db \ub77c\uc774\ube0c\ub7ec\ub9ac \uc804\ubd80 \ube0c\ub77c\uc6b0\uc800 \ubbf8\uc804\uc1a1). \u2461 DB \ube44\ubc00\ubc88\ud638\u00b7API \ud0a4 \uac19\uc740 secret \uc548\uc804 (\uc11c\ubc84\uc5d0\ub9cc). \u2462 waterfall fetch \uc81c\uac70 (\uc11c\ubc84\uc5d0\uc11c \ubcd1\ub82c). \u2463 SEO \uce5c\ud654 (HTML \uc774 \uc11c\ubc84\uc5d0\uc11c \uc644\uc131).\n<\/div>\n\n<h2>3. \uacbd\uacc4 \u2014 &#8216;use client&#8217; \uc758 \uc815\ud655\ud55c \uc758\ubbf8<\/h2>\n\n<p>\uc778\ud130\ub799\ud2f0\ube0c (useState\u00b7\uc774\ubca4\ud2b8 \ud578\ub4e4\ub7ec\u00b7useEffect) \uac00 \ud544\uc694\ud558\uba74 \ud074\ub77c\uc774\uc5b8\ud2b8\ub85c. \ud30c\uc77c \uccab \uc904\uc5d0 <code>'use client'<\/code>.<\/p>\n\n<div class=\"code-block\">\/\/ app\/components\/LikeButton.tsx\n&#8216;use client&#8217;;\nimport { useState } from &#8216;react&#8217;;\n\nexport function LikeButton({ initialCount }: { initialCount: number }) {\n  const [count, setCount] = useState(initialCount);\n  return &lt;button onClick={() =&gt; setCount(c =&gt; c + 1)}&gt;\u2764 {count}&lt;\/button&gt;;\n}\n\n\/\/ app\/posts\/[id]\/page.tsx (\uc11c\ubc84 \ucef4\ud3ec\ub10c\ud2b8)\nimport { LikeButton } from &#8216;@\/components\/LikeButton&#8217;;\n\nexport default async function PostPage({ params }) {\n  const post = await db.posts.findUnique({ where: { id: params.id } });\n  return (\n    &lt;article&gt;\n      &lt;h1&gt;{post.title}&lt;\/h1&gt;\n      &lt;p&gt;{post.body}&lt;\/p&gt;\n      &lt;LikeButton initialCount={post.likes} \/&gt;\n    &lt;\/article&gt;\n  );\n}<\/div>\n\n<p>\uc11c\ubc84 \ucef4\ud3ec\ub10c\ud2b8\uac00 \ud074\ub77c\uc774\uc5b8\ud2b8 \ucef4\ud3ec\ub10c\ud2b8\ub97c import \ud574\uc11c \ubc15\ub294 \uac8c \ud45c\uc900 \ud328\ud134. \ub370\uc774\ud130 fetch\u00b7\uc804\uccb4 \ub808\uc774\uc544\uc6c3\uc740 \uc11c\ubc84, \uc778\ud130\ub799\ud2f0\ube0c \ubd80\ud488\ub9cc \ud074\ub77c\uc774\uc5b8\ud2b8\ub85c \uaca9\ub9ac.<\/p>\n\n<div class=\"warnbox\">\n<strong>\uc5ed\ubc29\ud5a5 import \uae08\uc9c0<\/strong> \u2014 &#8216;use client&#8217; \ucef4\ud3ec\ub10c\ud2b8\uac00 \uc11c\ubc84 \ucef4\ud3ec\ub10c\ud2b8\ub97c import \ud560 \uc218 \uc5c6\ub2e4 (children prop \uc73c\ub85c \ubc1b\uc744 \uc218\ub9cc \uc788\uc74c). \uc774\uac8c RSC \uc758 \uac00\uc7a5 \ud070 \ud559\uc2b5 \uc7a5\ubcbd. \ud55c \ucef4\ud3ec\ub10c\ud2b8 \ud2b8\ub9ac\uc5d0\uc11c server \u2192 client \ub294 \uc790\uc5f0\uc2a4\ub7fd\uc9c0\ub9cc client \u2192 server \ub294 \uc758\ub3c4\uc801\uc73c\ub85c \ucc28\ub2e8.\n<\/div>\n\n<h2>4. &#8216;use server&#8217; \u2014 Server Actions<\/h2>\n\n<p>RSC \uc758 \ubc18\ub300 \ubc29\ud5a5. \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \ud638\ucd9c\ud558\ub294\ub370 <strong>\uc11c\ubc84\uc5d0\uc11c \uc2e4\ud589\ub418\ub294 \ud568\uc218<\/strong>. \ud3fc \uc81c\ucd9c\u00b7DB \ubcc0\uacbd \uac19\uc740 mutation \uc5d0 \uc0ac\uc6a9.<\/p>\n\n<div class=\"code-block\">\/\/ app\/actions.ts\n&#8216;use server&#8217;;\n\nimport { db } from &#8216;@\/lib\/db&#8217;;\nimport { revalidatePath } from &#8216;next\/cache&#8217;;\n\nexport async function createPost(formData: FormData) {\n  await db.posts.create({\n    data: { title: formData.get(&#8216;title&#8217;), body: formData.get(&#8216;body&#8217;) }\n  });\n  revalidatePath(&#8216;\/posts&#8217;);   \/\/ \uce90\uc2dc \ubb34\ud6a8\ud654 \u2192 \ubaa9\ub85d \uc790\ub3d9 \uac31\uc2e0\n}\n\n\/\/ \ud3fc\uc5d0\uc11c \ud638\ucd9c\nimport { createPost } from &#8216;@\/app\/actions&#8217;;\nfunction NewPostForm() {\n  return (\n    &lt;form action={createPost}&gt;\n      &lt;input name=&#8221;title&#8221; \/&gt;\n      &lt;textarea name=&#8221;body&#8221; \/&gt;\n      &lt;button&gt;\ubc1c\ud589&lt;\/button&gt;\n    &lt;\/form&gt;\n  );\n}<\/div>\n\n<p>\uc774\uac8c \ub9c8\ubc95 \u2014 \ud074\ub77c\uc774\uc5b8\ud2b8 \ucf54\ub4dc\ucc98\ub7fc \ubcf4\uc774\uc9c0\ub9cc \ud568\uc218 \uc790\uccb4\ub294 \uc11c\ubc84\uc5d0\uc11c \uc2e4\ud589. fetch API \ud638\ucd9c\uc774 \uc790\ub3d9\uc73c\ub85c wired up. 17\ud3b8 useMutation \uc758 \uc77c\uc0c1 \ucf00\uc774\uc2a4\uac00 server action \ud55c \uc904\ub85c.<\/p>\n\n<h2>5. \ub3c4\uc785 \uac00\uc774\ub4dc \u2014 \uc5b8\uc81c \/ \uc5b4\ub514\uc11c<\/h2>\n\n<p>RSC \ub294 \uba54\ud0c0 \ud504\ub808\uc784\uc6cc\ud06c (Next.js 14+\u00b7Remix\u00b7Waku) \uc548\uc5d0\uc11c\ub9cc \ub3d9\uc791. Vite \uae30\ubcf8 React \uc5d0\uc120 \uc544\uc9c1 \ubabb \uc500. \ubc15\uc900\uc131\ub2d8 \ud68c\uc0ac\ucc98\ub7fc Next.js \uc4f0\ub294 \uacbd\uc6b0 \u2014 \uc0c8 \ud398\uc774\uc9c0\ubd80\ud130 RSC \ub514\ud3f4\ud2b8\ub85c, \uae30\uc874 SPA \ucf54\ub4dc\ub294 &#8216;use client&#8217; \uc720\uc9c0\ud558\uba70 \uc810\uc9c4 \ub9c8\uc774\uadf8\ub808\uc774\uc158.<\/p>\n\n<div class=\"databox\">\n<strong>\uc9c0\uae08 \ud589\ub3d9<\/strong> \u2014 Next.js \ud504\ub85c\uc81d\ud2b8\uba74 RSC \uac00 \ub514\ud3f4\ud2b8\ub77c \uc790\uc5f0\uc2a4\ub7ec\uc6c0. Vite SPA \uba74 22\ud3b8\uae4c\uc9c0 \ud328\ud134\uc774 \uc5ec\uc804\ud788 \ud45c\uc900. \ub458 \ub2e4 \uc54c\uc544\ub450\uba74 React \uc9c4\ud654 \uc5b4\ub514\ub85c \uac00\ub4e0 \ub530\ub77c\uac04\ub2e4.\n<\/div>\n\n<p>23\ud3b8\uc73c\ub85c React \uc758 \ud604\uc7ac\uc640 \ubbf8\ub798 \ubaa8\ub450 \uc815\ub9ac. 24\ud3b8\uc740 production \u2014 \ube4c\ub4dc\u00b7\ubc30\ud3ec \ud30c\uc774\ud504\ub77c\uc778 (Vite build\u00b7Vercel\u00b7Cloudflare Pages).<\/p>\n\n<div class=\"cta\">\n<h3>\ub2e4\uc74c \uae00<\/h3>\n<p>React \uad50\uc7ac 24\ud3b8 \u2014 \ube4c\ub4dc\uc640 \ubc30\ud3ec. Vite production build, code splitting, Vercel\u00b7Cloudflare \ubc30\ud3ec \ud328\ud134.<\/p>\n<\/div>\n\n<div class=\"footer-nav\">\nReact \uad50\uc7ac \uc2dc\ub9ac\uc988 \u00b7\n<a href=\"https:\/\/junai.ai\/blog\/react-testing-21\/\">21\ud3b8 \ud14c\uc2a4\ud2b8<\/a> \u00b7\n<a href=\"https:\/\/junai.ai\/blog\/react-zod-rhf-22\/\">22\ud3b8 Zod+RHF<\/a> \u00b7\n<strong>23\ud3b8 RSC<\/strong>\n<\/div>\n\n<\/article>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>React Server Components \u2014 RSC, use server\/client \uacbd\uacc4, \ub370\uc774\ud130 fetch \uc778\ub77c\uc778, Next.js App Router. \uad50\uc7ac 23\ud3b8.<\/p>\n","protected":false},"author":1,"featured_media":761,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[],"class_list":["post-787","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-react"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/787","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/comments?post=787"}],"version-history":[{"count":1,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/787\/revisions"}],"predecessor-version":[{"id":810,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/787\/revisions\/810"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/media\/761"}],"wp:attachment":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/media?parent=787"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/categories?post=787"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/tags?post=787"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}