Skip to content

Commit dda5ff1

Browse files
authored
Merge pull request #14 from dev-bookclub/soo/vue
SSR 동작 확인해 보기
2 parents 5e97a7c + 049ef77 commit dda5ff1

8 files changed

Lines changed: 156 additions & 0 deletions

File tree

민수/SSR/READMD.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# SSR 동작 확인해 보기
2+
3+
## 시작하기
4+
5+
### Client & Server 코드 작성
6+
7+
Client와 Server에서 앱을 로드하기 위한 공통으로 사용하는 코드를 작성합니다.
8+
9+
``` typescript
10+
// main.js
11+
const app = createSSRApp(App);
12+
const pinia = creatpPinia();
13+
const router = createRouter();
14+
const head = createHead();
15+
16+
app.use(pinia);
17+
...
18+
19+
return {
20+
app,
21+
pinia,
22+
router
23+
};
24+
```
25+
26+
### Client 코드 작성
27+
28+
``` typescript
29+
const { app, pinia, router } = await run();
30+
31+
router.isReady().then(() => {
32+
app.mount('#app');
33+
});
34+
```
35+
36+
### Server 코드 작성
37+
38+
express에서 진입 시 render(entry-server.js)를 호출합니다.
39+
40+
``` typescript
41+
// server.js
42+
async function createServer(isProd = process.env.NODE_ENV === 'production') {
43+
// render = entry-server.js
44+
// template = index.html
45+
46+
app.use(/(.*)/, async (req, res) => {
47+
try {
48+
const url = req.originalUrl;
49+
const userAgent = req.get('User-Agent');
50+
const userAgentParser = UAParser(userAgent);
51+
const device = 'mobile' === userAgentParser.device.type ? 'mobile' : 'desktop';
52+
const isCrawler = false; // userAgent.test(//);
53+
let render;
54+
let html;
55+
56+
if (isProd) {
57+
render = entryRender;
58+
} else {
59+
// always read fresh template in dev
60+
template = await vite.transformIndexHtml(
61+
url,
62+
template
63+
);
64+
render = (await vite.ssrLoadModule('./src/entry-server.ts')).render;
65+
}
66+
67+
const {
68+
html: appHTML,
69+
headTags,
70+
state,
71+
preloadLinks,
72+
isRedirected,
73+
currentPath
74+
} = await render({ req }, url, manifest);
75+
76+
// if(isCrawler) {
77+
html = template[device]
78+
.replace('<!--preload-links-->', preloadLinks)
79+
.replace('<!--ssr-outlet-->', appHTML)
80+
// .replace('<!--head-tags-->', headTags)
81+
.replace('<!--store-state-->', state);
82+
// }
83+
// else {
84+
// html = template.replace(`<!--preload-links-->`, preloadLinks).replace(/\<\$\= title \$\>/g, Math.random());
85+
// }
86+
87+
res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
88+
89+
html = null;
90+
91+
} catch (ex) {
92+
vite?.ssrFixStacktrace(ex);
93+
res.status(500).end(ex.stack);
94+
}
95+
});
96+
}
97+
```
98+
99+
render함수 에서 [vue 인스턴스](Client_&_Server_코드_작성)를 가져오고 [renderToString](https://vuejs.org/api/ssr#rendertostring)을 통해 html 내용물을 가져옵니다.
100+
101+
``` typescript
102+
// entry-server.js
103+
export async function render(context: Context, url: string, manifest: any) {
104+
const { app, pinia, router } = await run({
105+
req: context.req,
106+
device: context.device,
107+
renderType: 'ssr'
108+
});
109+
110+
// set the router to the desired URL before rendering
111+
await router.push(url);
112+
await router.isReady();
113+
114+
// passing SSR context object which will be available via useSSRContext()
115+
// @vitejs/plugin-vue injects code into a component's setup() that registers
116+
// itself on ctx.modules. After the render, ctx.modules would contain all the
117+
// components that have been instantiated during this render call.
118+
const ctx = {
119+
vueServerContext: context
120+
} as any;
121+
122+
const html = await renderToString(app, ctx);
123+
const state = xss(JSON.stringify(pinia.state));
124+
// const { headTags } = await renderHeadToString(head);
125+
126+
// the SSR manifest generated by Vite contains module -> chunk/asset mapping
127+
// which we can then use to determine what files need to be preloaded for this
128+
// request.
129+
const preloadLinks = renderPreloadLinks(ctx.modules, manifest);
130+
131+
return {
132+
html,
133+
// headTags,
134+
state,
135+
preloadLinks
136+
};
137+
}
138+
```
139+
140+
다시 [server.js](Server_코드_작성)로 돌아가서 Client측으로 HTML을 응답합니다.
141+
142+
## Server 측에서 데이터 주입하기하고 Client에서 사용하기
143+
144+
1. onServerPrefetch를 사용하여 Server에서 데이터를 조회하고 주입합니다.
145+
146+
![onServerPrefetch](./assets/1.jpeg)
147+
148+
2. Server에서 조회 한 데이터를 Client 환경에 주입합니다.
149+
150+
![prefetch데이터 사용하기](./assets/2.jpeg)
151+
152+
3. 그리고 소스보기를 통해 SSR이 정상적으로 동작했는지 확인 합니다.
153+
154+
![SSR Test](./assets/4.png)
155+
156+
이와같이 최초 페이지 진입 시에는 SSR환경으로, 그 이후에는 CSR 환경으로 동작하게 됩니다.

민수/SSR/assets/1.jpeg

27.3 KB
Loading

민수/SSR/assets/2.jpeg

33.9 KB
Loading

민수/SSR/assets/3.png

72.8 KB
Loading

민수/SSR/assets/4.png

104 KB
Loading
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)