์ต๊ทผ ๊ธฐ์ ๋ฉด์ ์์ ํญ์ ๋ฑ์ฅํ๋ ์ง๋ฌธ, SSR๊ณผ CSR์ ๋ํด ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ๊ทธ๋งํผ ํซํ SPA์ ์๋ฆฌ์ธ SSR(Server Side Rendering)๊ณผ CSR(Client Side Rendering)์ ๋ํด ์ดํด๋ด ์๋ค.
SSR(Server Side Rendering)์ ๋ง์์ ๋ฐ๋ก ์๋ฏธ๋ฅผ ํ์ ํ ์ ์์ต๋๋ค. ์๋ฒ์ชฝ์์ ๋ ๋๋ง์ ํ๋ค๋ผ๋ ๋ป์ ๋๋ค. ๊ทธ๋ ๋ค๋ฉด ์ฌ๊ธฐ์ '๋ ๋๋ง'์ ํ๋ค๋ ๊ฒ์ ๋ฌด์จ ์๋ฏธ์ผ๊น์?
์ถ์ฒ: The Benefits of Server Side Rendering Over Client Side Rendering
์ฒซ ๋ฒ์งธ์์ ๋ณด์๋ ๊ฒ์ฒ๋ผ ๊ฐ์ฅ ์ค์ํ ๊ฒ์ ์๋ฒ์์ ๋ ๋ ๋ ์ค๋น๋ฅผ ๋๋ง์น ์ํ๋ก HTML ์๋ต์ ๋ธ๋ผ์ฐ์ (ํด๋ผ์ด์ธํธ)์ ๋ณด๋ด๋ ๊ฒ์
๋๋ค. ๊ทธ ์๋ฏธ๊ฐ ๋ฐ๋ก ๋ ๋๋ง
์ด๋ผ๋ ์๋ฏธ์
๋๋ค.
์ฌ๊ธฐ์ ์ง๊ณ ๋์ด๊ฐ์ผํ ๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง์
๋๋ค. ๋ธ๋ผ์ฐ์ ์์ ๋งํ๋ ๋ ๋๋ง์ ์๋ฏธ๋ HTML, CSS, JavaScript ํ์ผ์ ๋ฐ์์ ์ด๋ฅผ ์ฝ๊ณ ํ์ฑํด์ ์คํํ ๊ฒฐ๊ณผ๋ฌผ๋ก ํ๋ฉด์ ๊ทธ๋ ค๋ด๋ ๊ณผ์ ์
๋๋ค. ๊ทธ๋ฌ๋ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง์ ์๋ ๋ ๋๋ง
์ ๋ป์ ์ด์ ์ข ๋ค๋ฆ
๋๋ค.
์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง์์ ์๋ฏธ๋ฅผ ๋ฐ์ง์๋ฉด, HTML ํ์ผ ๋ด์ ๋ด์ฉ์ด ์๋๋ ์๋๋์ ๋๋ค. ๋ด์ฉ์ด ์๋ค๋ฉด, ๋ ๋๋ง์ด ๋ ๊ฒ์ ๋๋ค.
์ด๋ ๊ฒ HTML ํ์ผ ๋ด์ ๋ด์ฉ์ด ๋ชจ๋ ์์ผ๋ฏ๋ก ๋ธ๋ผ์ฐ์ ๋ ๋ฐ๋ก ํ์ด์ง๋ฅผ ๋ ๋๋งํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์์๊ฒ ๋ฐ๋ก ๋ณด์ฌ์ง๋ ๊ฒ์ ๋๋ค. ์ดํ ๋ธ๋ผ์ฐ์ ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ ๋ค์ด ๋ฐ์ต๋๋ค. ์ด๋ ๊ฒ ๋ธ๋ผ์ฐ์ ๊ฐ ํด๋น ํ์ผ์ ์คํ์ํค๋ฉด, ํ์ด์ง์ ์ํธ์์ฉ์ด ๊ฐ๋ฅํด์ง๋๋ค.
์ฐ๋ฆฌ๊ฐ ๊ธฐ์ตํด์ผํ ๋ถ๋ถ์ ํ์ด์ง๊ฐ ๋ณด์ฌ์ง๋ ๊ฒ์ 2๋จ๊ณ์์ ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ๋๋ค. ์ด ์ ์ด ๋ฐ๋ก ์ค๋ช ํ๊ฒ ๋ CSR๊ณผ์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ ๋๋ค.
๋ฌผ๋ก , JS ํ์ผ์ ์ฝํ๊ธฐ ์ ์ด๋ฏ๋ก ์ด๋ฅผ ๋ค์ด๋ก๋ ๋ฐ์์ค๋ฉด์ ์ ์ ๋ ์ปจํ ์ธ ๋ฅผ ๋ณผ ์ ์์ง๋ง, ์ฌ์ดํธ๋ฅผ ์กฐ์ํ ์๋ ์์ต๋๋ค. ์ด ๋์ ์ฌ์ฉ์ ์กฐ์์ ๊ธฐ์ตํ๊ณ ์๊ฒ ๋ฉ๋๋ค. JS๊น์ง ์ฑ๊ณต์ ์ผ๋ก ์ปดํ์ผ ๋์๋ค๋ฉด, ๊ธฐ์ตํ๊ณ ์๋ ์ฌ์ฉ์ ์กฐ์์ด ์คํ๋๊ณ , ์ด์ ์นํ์ด์ง๋ ์ํธ์์ฉ์ด ๊ฐ๋ฅํด์ง๋๋ค.
SSR์ ๊ฐ๋ ์ ์ด์ ๋๋ก ํ๊ณ , Client Side Rendering, CSR์ ๋ํด ์์๋ด ์๋ค.
SSR ๊ธฐ์ตํด์ผํ ์
Step2: Viewable
Step4: Interactable
CSR์ ๋ง ๊ทธ๋๋ก SSR๊ณผ ๋ฌ๋ฆฌ ๋ ๋๋ง์ด ํด๋ผ์ด์ธํธ ์ชฝ์์ ์ผ์ด๋ฉ๋๋ค. ์ฆ, ์๋ฒ๋ ์์ฒญ์ ๋ฐ์ผ๋ฉด ํด๋ผ์ด์ธํธ์ HTML๊ณผ JS๋ฅผ ๋ณด๋ด์ฃผ๊ณ , ํด๋ผ์ด์ธํธ๋ ๊ทธ๊ฒ์ ๋ฐ์ ๋ ๋๋ง์ ์์ํฉ๋๋ค. ๊ทธ๋ฆผ์ผ๋ก ํ ๋ฒ ๋ ๋ณด๊ฒ ์ต๋๋ค.
์ถ์ฒ: The Benefits of Server Side Rendering Over Client Side Rendering
CSR(Client Side Rendering)์ ์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด 2๋จ๊ณ์์ ๋ฐ๋ก ๋ณผ ์ ์์ต๋๋ค. ๋ง์ง๋ง 4๋จ๊ณ์์ ๋ณผ ์ ์์ผ๋ฉฐ, ์ํธ์์ฉ ํ ์ ์๋ ๊ฒ์ ๋๋ค.
๊ทธ ์ด์ ๋ ์๋ฒ๊ฐ HTML ํ์ผ์ ์ค ๋, ๋ ๋๊ฐ ์ค๋น๊ฐ ๋ ํ์ผ์ด ์๋๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ฆ, HTML ํ์ผ ์์๋ ์๋ฌด๋ฐ ๋ด์ฉ์ด ์๋ค๋ ๊ฒ์ ๋๋ค. ๊ทธ ๋ด์ฉ์ JS ํ์ผ์ ๋ฐ์ ์คํ์ ์์ผ์ผ ๊ทธ์ ์์ผ ๋ง๋ค์ด์ง๋ ๊ฒ์ ๋๋ค.
React๋ฅผ ์จ๋ณด์ ๋ถ์ ์๊ฒ ์ง๋ง, React.createElement
๋ผ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด๋ณด์
จ์ ๊ฑฐ๋ผ ์๊ฐํฉ๋๋ค. ์ด๋document.createElement
์ ๋ง์ฐฌ๊ฐ์ง๋ก JS์์ HTML ํ๊ทธ๋ฅผ ์์ฑํ๋ ๊ฒ์
๋๋ค. ์ด ๋์ ํ๊ทธ๊ฐ ๋ ๋๋ง ๋ ์ ์๊ธฐ ๋๋ฌธ์ SSR๊ณผ์ ๋๋ ทํ ์ฐจ์ด๋ฅผ ๋ณด์ด๋ ๊ฒ์
๋๋ค.
๊ทธ๋ฆฌ๊ณ ํนํ, index.html ํ์ผ์ ๋ฐ๋ ํ๊ทธ ์์ <div id="root"></div>
์ ๊ฐ์ด div ํ๊ทธ ํ๋๋ง ๋ฉ๊ทธ๋ฌ๋ ์๊ณ ์๋ฌด๋ฐ ๋ด์ฉ์ด ์๋ ๊ฒ์ ๋ง์ด ๋ณด์
จ์ ๊ฑฐ๋ผ ์๊ฐํฉ๋๋ค. ์ด๋ ๊ฒ ์๋ฌด๊ฒ๋ ์๋ ์ํ๋ก ์ ๋ฌ๋๋ฏ๋ก ์ ์ ๋ ์๋ฌด๊ฒ๋ ๋ณผ ์ ์์ต๋๋ค.
ReactDOM.render(<App />, document.getElementById('root'));
๊ทธ๋ฌ๋ ์ดํ, ๋ธ๋ผ์ฐ์ ๊ฐ ์ถ๊ฐ์ ์ผ๋ก JS ํ์ผ์ ๋ค์ด๋ฐ๊ณ ์คํํ๋ฉด ๊ทธ๋ ๋์ด index.js์์ ์์ ๊ฐ์ด root ํ๊ทธ๋ฅผ ํ๋ฉด์ ๋ ๋๋ง, ๊ทธ๋ ค์ฃผ๊ฒ ๋๋ ๊ฒ์ ๋๋ค.
๋ฐ๋ฉด์ SSR์ index.html ํ์ผ ๋ด์ ํ๋ฉด์ ๊ทธ๋ ค๋ด๋ ์ฝ๋๋ค์ด ์ด๋ฏธ ์์ฑ๋์ด ์๊ธฐ ๋๋ฌธ์ CSR๊ณผ ๋ฌ๋ฆฌ ํ์ด์ง๊ฐ 2๋จ๊ณ์์ Viewable
ํ ์ ์๋ ๊ฒ์
๋๋ค.
์น ํ์ด์ง์ ๋ก๋ฉ์ ์ข ๋ฅ๋ ๋ ๊ฐ์ง๋ก ๋๋์ด ๋ณผ ์ ์์ต๋๋ค. ์น ์ฌ์ดํธ์ ์ฒซ ํ์ด์ง๋ฅผ ๋ก๋ฉํ๋ ๊ฒ๊ณผ ๋ค๋ฅธ ๋๋จธ์ง๋ฅผ ๋ก๋ฉํ๋ ๊ฒ์ ๋๋ค.
์ฒซ ํ์ด์ง ๋ก๋ฉ ์๊ฐ
CSR์ ๊ฒฝ์ฐ HTML, CSS์ ๋ชจ๋ ์คํฌ๋ฆฝํธ๋ค์ ํ ๋ฒ์ ๋ถ๋ฌ์ต๋๋ค. ๋ฐ๋ฉด SSR์ ํ์ํ ๋ถ๋ถ์ HTML๊ณผ ์คํฌ๋ฆฝํธ๋ง ๋ถ๋ฌ์ค๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์ SSR์ด ๋ ๋น ๋ฆ
๋๋ค.
๋๋จธ์ง ํ์ด์ง ๋ก๋ฉ ์๊ฐ
์ฒซ ํ์ด์ง๋ฅผ ๋ก๋ฉํ ํ, ์ฌ์ดํธ์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ์ด๋ํ๋ ์์ ๋์์ ๊ฐ์ ํด๋ด
์๋ค. CSR์ ์ด๋ฏธ ์ฒซ ํ์ด์ง ๋ก๋ฉํ ๋ ๋๋จธ์ง ๋ถ๋ถ์ ๊ตฌ์ฑํ๋ ์ฝ๋๋ฅผ ๋ฐ์์์ผ๋ฏ๋ก ๋น ๋ฆ
๋๋ค. ๋ฐ๋ฉด, SSR์ ์ฒซ ํ์ด์ง๋ฅผ ๋ก๋ฉํ ๊ณผ์ ์ ์ ํํ๊ฒ ๋ค์ ์คํํฉ๋๋ค. ๊ทธ๋์ CSR์ด SSR๋ณด๋ค ๋น ๋ฅธ ๊ฒ์
๋๋ค.
๊ฒ์ ์์ง์ ์๋ํ๋ ๋ก๋ด์ธ 'ํฌ๋กค๋ฌ'๋ก ์น ์ฌ์ดํธ๋ฅผ ์ฝ์ด๋ค์ ๋๋ค. CSR์ JS๋ฅผ ์คํ์์ผ ๋์ ์ผ๋ก ์ปจํ ์ธ ๊ฐ ์์ฑ๋๊ธฐ ๋๋ฌธ์ JS๊ฐ ์คํ๋์ด์ผ metadata๊ฐ ๋ฐ๋์์ต๋๋ค. ์ด๋ ๊ฒ ์ด์ ํฌ๋กค๋ฌ๋ JS๋ฅผ ์คํ์ํค์ง ์์๊ธฐ ๋๋ฌธ์ SEO ์ต์ ํ๊ฐ ํ์์ ์ด์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ตฌ๊ธ์ด ์ด๋ฌํ ์ด์ ๋ก JS ํ์ผ์ ํด์ํด์ ๋ด์ฉ์ ์ฐพ์์ฃผ๊ธฐ ์์ํ๊ธฐ ๋๋ฌธ์ ๊ทธ ํธ๋ ๋๋ฅผ ๋ฐ๊พธ๊ณ ์๋ค๊ณ ํฉ๋๋ค.
๋ฌผ๋ก , SSR์ ์ ์ด์ ์๋ฒ ์ฌ์ด๋์์ ์ปดํ์ผ๋์ด ํด๋ผ์ด์ธํธ๋ก ๋์ด์ค๊ธฐ ๋๋ฌธ์ ํฌ๋กค๋ฌ์ ๋์ํ๊ธฐ ์ฉ์ดํฉ๋๋ค.
SSR์ด ์๋ฒ ์์์ ๋ ๋ง์ด ์ฌ์ฉํฉ๋๋ค. ๋งค๋ฒ ์๋ฒ์ ์์ฒญ์ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ด๋ ๊ฒ CSR, SSR์ ๊ฐ๊ฐ์ ์ฅ๋จ์ ์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ ์ ์ ํ ์ํฉ์์ ์ฌ์ฉ์ ํ๋ฉด ๋ฉ๋๋ค. ๊ฐ๋จํ ์์๋ก๋ CSR์ ๋คํธ์ํฌ๊ฐ ๋น ๋ฅด๊ณ , ์๋ฒ์ ์ฑ๋ฅ์ด ์ข์ง ์์ ๋ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์น ์ดํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ์์ ์ํธ์์ฉํ ๊ฒ๋ค์ด ๋ง์ ๋๋ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ค์ผ ํ๋ ๋ฐ์ดํฐ์ ์์ด ๋ง์ ๋ ๋ก๋ฉ์ฐฝ์ ๋์ธ ์ ์์ผ๋ฏ๋ก ์ด๋ฌํ ์ฅ์ ์ด ์์ต๋๋ค.
๋ฐ๋๋ก SSR์ CSR์ ํ๋ฒ์ ๋ชจ๋ ๊ฒ์ ๋ถ๋ฌ์ค์ง๋ง SSR์ ๊ฐ ํ์ด์ง๋ง๋ค ๋๋ ๋ถ๋ฌ์ค๋ฏ๋ก ๋คํธ์ํฌ๊ฐ ๋๋ฆฌ๊ฑฐ๋, SEO๊ฐ ํ์ํ ๋ ์ฌ์ฉํ ์ ์์ ๊ฒ์ ๋๋ค. ๋ํ, ์น ์ฌ์ดํธ๊ฐ ์ํธ์์ฉ์ด ๋ณ๋ก ์๋ค๋ฉด SSR์ ์ ํฉํฉ๋๋ค.
๋ํ, ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ CSR + SSR ํ๊ฒฝ์ ํตํด ๊ฐ๋ฐํ๋ ํ์ฌ๋ค๋ ๋ง์์ก์ต๋๋ค.
์ถ์ฒ: CSR ์ฑ์์ SSR + CSR ํ๊ฒฝ์ผ๋ก ์ด์ฃผํ๊ธฐ
์ ์ฒด์ ์ธ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ๋๋ฉ์ธ ๋ค์์ ํตํด NginX ์น์๋ฒ์ ์ ๊ทผํ๋ฉด NginX๋ EC2 ๋ด๋ถ์ ๋ค๋ฅธ ํฌํธ์์ ๋์ํ๊ณ ์๋ express ์๋ฒ์ ์ ๊ทผํฉ๋๋ค. express ์๋ฒ๋ ์ผ๋ถ๊ฐ ์์ฑ๋ html ๋ฌธ์๋ฅผ NginX๋ฅผ ํ๋ฒ ๊ฑฐ์ณ์ express์ ๊ฐ์ WAS์ ์ฐ๊ฒฐํ๋ ๋ฐฉ์์ reverse proxy๋ผ๊ณ ํฉ๋๋ค. reverse proxy๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ง๋ง, ์ธ๋ถ ์ฌ์ฉ์๋ค์๊ฒ WAS๊ฐ ์ด๋ค ํฌํธ์์ ๋์ํ๋์ง ์จ๊ฒจ ๋ณด์์ ์ผ๋ก ๊ฐํํ๊ธฐ ์ํ ๋ชฉ์ ์ด ์์ต๋๋ค.
์ด๋ฌํ ํ๋ฆ์ผ๋ก ์ฌ์ฉ์๋ ์ด๋ ์ ๋ ํ๋ฉด์ด ๋ง๋ค์ด์ง html ๋ฌธ์๋ฅผ ์ฒ์ ์๋ต์ผ๋ก ๋ฐ์๋ณผ ์๊ฐ ์๋๋ฐ, ๋น์ด์๋ body๋ฅผ ์ฒซ html ์๋ต์ผ๋ก ๊ฐ์ ธ์ค๋ CSR๋ณด๋ค๋ ํจ์ฌ ๋น ๋ฅธ ํ๋ฉด ๋ก๋ฉ ์๋๋ฅผ ๋ณด์ฌ์ค๋๋ค.
app.get('/', (req, res) => {
const indexFile = path.resolve(path.join(__dirname, '../client/index.html'));
const app = ReactDOMServer.renderToString(<App />);
fs.readFile(indexFile, 'utf8', (err, data) => {
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
const result = data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
return res.send(result);
});
}
๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๊ฐ๋ฐ์๋ express.js์์ ๋๋ต ์์ ๊ฐ์ ์ฝ๋๋ฅผ ์์ฑํ๋๋ฐ ReactDOMServer.renderToString
์ ์ฌ์ฉํ๋ค๋ ์ ์ ๋นผ๋ฉด CSR์์ ๋ ๋๋งํ๋ ๋ฐฉ์๊ณผ ํฌ๊ฒ ๋ค๋ฅธ ๊ฒ์ ์์ต๋๋ค.
renderToString
์ ๋ฆฌ์กํธ๊ฐ ์ปดํฌ๋ํธ ๊ตฌ์กฐ๋ฅผ ํ์
ํ๊ณ HTML string์ผ๋ก ๋ฐ๊ฟ์ฃผ๋ ๋์์ ํฉ๋๋ค.
์ด๋ ๊ฒ ์์ ์ ๋ง์น๋ฉด ๊ฐ๋ฐ์ ๋๊ตฌ์์ html์ ๋ฐ์์จ ๊ฒฐ๊ณผ๊ฐ ์ผ๋ถ ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ์ฒซ html์ ๋ฐ์์จ ์ด ์์ ์ดํ๋ถํฐ๋ ๋๋ SSR์ด ์๋ CSR๋ก ๋ฐ๋๋๋ค. ํ์ด์ง ์ด๋๋ SPA์ฒ๋ผ ๋์ํ๋๊ฒ ์ด๊ฒ ์ด๋ป๊ฒ ๊ฐ๋ฅํ ๊ฒ์ผ๊น์?
์ผ๋จ CSR + SSR ํ๊ฒฝ์ด๋ฏ๋ก ์ฒซ html์ ๋ฐ์์จ ํ์๋ CSR ํ๊ฒฝ์ ๋์์ํค๊ธฐ ์ํ JavaScript๋ฅผ ๋ถ๋ฌ์ค๋ script ํ๊ทธ๊ฐ ์ฌ์ ํ ๋ค์ด๊ฐ์๋ ์ํ์ ๋๋ค. ๊ทธ๋์ html์ ๋ฐ์์จ ํ JS ํ์ผ์ ๊ฐ์ ธ์ CSR๋ก ๋์์ํค๊ธฐ ๋๋ฌธ์ SSR์์ CSR๋ก์ ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ ๊ฒ์ ๋๋ค.
์ด๋ ๊ฒ ์ค๋์ SPA์ ์๋ฆฌ์ธ SSR, CSR์ ๋ํด ์์๋ดค์ต๋๋ค. ํนํ, ๊ธฐ์ ๋ฉด์ ์์ ์์ฃผ ๋ฑ์ฅํ๋ ์ฃผ์ ์ด๋ฏ๋ก ์กฐ๊ธ ์์ธํ ์ ๋ฆฌํด๋ณด์์ต๋๋ค. ์๋ฃ ์ถ์ฒ๋ ์๋์ ๋จ๊ฒจ๋๋ฆฌ๋ฏ๋ก ๋์ฑ ์์ธํ ๋ด์ฉ์ ์ํ์๋ ๋ถ์ ๊ผญ ์ฐธ๊ณ ํด์ฃผ์๊ธฐ ๋ฐ๋ผ๊ฒ ์ต๋๋ค.๐โโ๏ธ๐โโ๏ธ๐โโ๏ธ
SSR๊ณผ CSR์ ์ฐจ์ด
[BOAZ] SSR๊ณผ CSR ์ด ์์ ํ๋๋ก ๋๋ด๊ธฐ! (SEO ํด๊ฒฐ ํฌํจ)
์ด์ ์, SSR์ ์ฒ์์ด์ง? ๋์
ํธ
CSR ์ฑ์์ SSR + CSR ํ๊ฒฝ์ผ๋ก ์ด์ฃผํ๊ธฐ