νμ₯ κ°λ₯νκ³ μ μ§λ³΄μνκΈ° μ¬μ΄ λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ² κ΅¬μΆμ μν single-SPA νλ μμν¬λ₯Ό μμ보μΈμ. κΈλ‘λ² νμ μν μ΄μ , ꡬν λ°©λ² λ° λͺ¨λ² μ¬λ‘λ₯Ό λ°°μλλ€.
Single-SPA νλ μμν¬: λ§μ΄ν¬λ‘ νλ‘ νΈμλ μ€μΌμ€νΈλ μ΄μ μ μν μ’ ν© κ°μ΄λ
μ€λλ λΉ λ₯΄κ² λ°μ νλ μΉ κ°λ° νκ²½μμ λͺ¨λ리μ νλ‘ νΈμλλ μ¦κ°νλ μ ν리μΌμ΄μ κ³Ό λΆμ°λ νμ μꡬλ₯Ό λ°λΌμ‘κΈ° μν΄ μ μ λ μ΄λ €μμ κ²ͺκ³ μμ΅λλ€. λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ μ΄λ¬ν λ¬Έμ μ λν κ°λ ₯ν ν΄κ²°μ± μΌλ‘ λ±μ₯νμ¬, κ°λ°μλ€μ΄ λ 립μ μΌλ‘ λ°°ν¬ λ° μ μ§λ³΄μ κ°λ₯ν μ»΄ν¬λνΈμ μ§ν©μΌλ‘ 볡μ‘ν μ¬μ©μ μΈν°νμ΄μ€λ₯Ό ꡬμΆν μ μκ² ν©λλ€. μ΄ μ κ·Ό λ°©μμ νμ μμ¨μ±μ λμ΄κ³ , μ½λ μ¬μ¬μ©μ±μ μ¦μ§νλ©°, μ λ°μ μΈ κ°λ° νλ‘μΈμ€λ₯Ό λ¨μνν©λλ€. λ§μ΄ν¬λ‘ νλ‘ νΈμλ μ€μΌμ€νΈλ μ΄μ μ μν΄ μ¬μ© κ°λ₯ν λ€μν νλ μμν¬ μ€μμ single-SPAλ λ€μ¬λ€λ₯νκ³ κ²¬κ³ ν μ νμ§λ‘ λ보μ λλ€.
λ§μ΄ν¬λ‘ νλ‘ νΈμλλ 무μμΈκ°?
λ§μ΄ν¬λ‘ νλ‘ νΈμλλ νλ‘ νΈμλ μ±μ λ μκ³ , λ 립μ μ΄λ©°, μ체 ν¬ν¨λ λ¨μ(λ§μ΄ν¬λ‘ νλ‘ νΈμλ)λ‘ λΆν΄νλ μν€ν μ² μ€νμΌμ λλ€. κ° λ§μ΄ν¬λ‘ νλ‘ νΈμλλ λ³λμ νμ μν΄ κ°λ°, λ°°ν¬ λ° μ μ§λ³΄μλ μ μμ΅λλ€. μ΄λ μ¬λ¬ λ―Έλ μ ν리μΌμ΄μ μ΄ ν¨κ» μλνμ¬ μΌκ΄λ μ¬μ©μ κ²½νμ νμ±νλ ꡬμ±μ΄λΌκ³ μκ°ν μ μμ΅λλ€.
λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μ£Όμ νΉμ§μ λ€μκ³Ό κ°μ΅λλ€:
- κΈ°μ λ 립μ±: κ° λ§μ΄ν¬λ‘ νλ‘ νΈμλλ λ€λ₯Έ νλ μμν¬μ κΈ°μ (React, Angular, Vue.js λ±)μ μ¬μ©νμ¬ κ΅¬μΆν μ μμ΅λλ€.
- λ 립μ μΈ λ°°ν¬: λ§μ΄ν¬λ‘ νλ‘ νΈμλλ μ ν리μΌμ΄μ μ λ€λ₯Έ λΆλΆμ μν₯μ μ£Όμ§ μκ³ λ 립μ μΌλ‘ λ°°ν¬λ μ μμ΅λλ€.
- μμ¨μ μΈ ν: λ€λ₯Έ νμ΄ λ€λ₯Έ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό μμ νκ³ μ μ§λ³΄μνμ¬ μμ¨μ±κ³Ό λ λΉ λ₯Έ κ°λ° μ£ΌκΈ°λ₯Ό μ΄μ§ν μ μμ΅λλ€.
- μ½λ μ¬μ¬μ©μ±: κ³΅ν΅ μ»΄ν¬λνΈμ λΌμ΄λΈλ¬λ¦¬λ₯Ό λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ 곡μ ν μ μμ΅λλ€.
- ν₯μλ νμ₯μ± λ° μ μ§λ³΄μμ±: λ μκ³ λ 립μ μΈ λ¨μλ λκ·λͺ¨ λͺ¨λ리μ μ ν리μΌμ΄μ μ λΉν΄ νμ₯, μ μ§λ³΄μ λ° μ λ°μ΄νΈκ° λ μ½μ΅λλ€.
μ Single-SPAλ₯Ό μ νν΄μΌ νλκ°?
Single-SPAλ λ¨μΌ λΈλΌμ°μ νμ΄μ§ λ΄μμ μ¬λ¬ μλ°μ€ν¬λ¦½νΈ μ ν리μΌμ΄μ (λ§μ΄ν¬λ‘ νλ‘ νΈμλ)μ μ€μΌμ€νΈλ μ΄μ μ μ©μ΄νκ² νλ μλ°μ€ν¬λ¦½νΈ νλ μμν¬μ λλ€. μ΄λ λ§μ΄ν¬λ‘ νλ‘ νΈμλ μ체μ λν νΉμ κΈ°μ μ€νμ κ·μ νμ§ μμΌλ―λ‘ νμ΄ νμμ κ°μ₯ μ ν©ν λꡬλ₯Ό μ νν μ μκ² ν©λλ€. μ΄ νλ μμν¬λ λ©ν νλ μμν¬ μν μ νλ©°, λ€μν λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λ‘λ©, μΈλ‘λ© λ° μλͺ μ£ΌκΈ° κ΄λ¦¬λ₯Ό μν μΈνλΌλ₯Ό μ 곡ν©λλ€.
single-SPAκ° λ§μ΄ν¬λ‘ νλ‘ νΈμλ μ€μΌμ€νΈλ μ΄μ μμ μΈκΈ° μλ μ νμΈ μ΄μ λ λ€μκ³Ό κ°μ΅λλ€:
- νλ μμν¬ λ 립μ±: single-SPAλ React, Angular, Vue.js, Svelte λ± κ±°μ λͺ¨λ μλ°μ€ν¬λ¦½νΈ νλ μμν¬μ ν¨κ» μ¬μ©ν μ μμ΅λλ€. μ΄λ¬ν μ μ°μ± λλΆμ νμ κΈ°μ‘΄ μ ν리μΌμ΄μ μ λ€μ μμ±νμ§ μκ³ λ μ μ§μ μΌλ‘ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό μ±νν μ μμ΅λλ€.
- μ μ§μ λμ : μκ³ κ²©λ¦¬λ κΈ°λ₯λΆν° μμνμ¬ λͺ¨λ리μ μ ν리μΌμ΄μ μ λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ‘ μ μ§μ μΌλ‘ λ§μ΄κ·Έλ μ΄μ ν μ μμ΅λλ€.
- μ½λ 곡μ : single-SPAλ₯Ό μ¬μ©νλ©΄ λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ μ½λμ μ’ μμ±μ 곡μ νμ¬ μ€λ³΅μ μ€μ΄κ³ μΌκ΄μ±μ ν₯μμν¬ μ μμ΅λλ€.
- μ§μ° λ‘λ©: λ§μ΄ν¬λ‘ νλ‘ νΈμλλ νμν λ λ‘λλλ―λ‘ μ΄κΈ° νμ΄μ§ λ‘λ μκ°κ³Ό μ λ°μ μΈ μ±λ₯μ΄ ν₯μλ©λλ€.
- λ¨μνλ λ°°ν¬: λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λ 립μ μΈ λ°°ν¬λ λ λΉ λ₯Έ λ¦΄λ¦¬μ€ μ£ΌκΈ°μ μν κ°μλ₯Ό κ°λ₯νκ² ν©λλ€.
- κ²¬κ³ ν μλͺ μ£ΌκΈ° κ΄λ¦¬: single-SPAλ κ° λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λν΄ μ μ μλ μλͺ μ£ΌκΈ°λ₯Ό μ 곡νμ¬, κ° νλ‘ νΈμλκ° μ μ νκ² μ΄κΈ°ν, λ§μ΄νΈ, μΈλ§μ΄νΈ λ° μλ©Έλλλ‘ λ³΄μ₯ν©λλ€.
Single-SPAμ ν΅μ¬ κ°λ
single-SPAλ₯Ό ν¨κ³Όμ μΌλ‘ μ¬μ©νλ €λ©΄ ν΅μ¬ κ°λ μ μ΄ν΄νλ κ²μ΄ μ€μν©λλ€:
- Single-SPA Config: single-SPA μ ν리μΌμ΄μ μ λΆνΈμ€νΈλ©νλ λ©μΈ μλ°μ€ν¬λ¦½νΈ νμΌμ λλ€. λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ±λ‘νκ³ λΌμ°ν λ‘μ§μ μ μνλ μν μ ν©λλ€. μ¬κΈ°μλ λͺ¨λ κ²μ κ΄λ¦¬νλ λ£¨νΈ μ»΄ν¬λνΈκ° ν¬ν¨λλ κ²½μ°κ° λ§μ΅λλ€.
- λ§μ΄ν¬λ‘ νλ‘ νΈμλ: single-SPA configμ λ±λ‘λ λ 립μ μΈ μλ°μ€ν¬λ¦½νΈ μ ν리μΌμ΄μ μ λλ€. κ° λ§μ΄ν¬λ‘ νλ‘ νΈμλλ μ¬μ©μ μΈν°νμ΄μ€μ νΉμ λΆλΆμ λ λλ§νλ μν μ ν©λλ€.
- Parcels: λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ 곡μ ν μ μλ μ¬μ¬μ© κ°λ₯ν μ»΄ν¬λνΈμ λλ€. Parcelμ μ ν리μΌμ΄μ μ μ¬λ¬ λΆλΆμμ νμν κ³΅ν΅ UI μμλ λΉμ¦λμ€ λ‘μ§μ λ§λλ λ° μ μ©ν©λλ€.
- Root Config: λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ‘λνκ³ μ€μΌμ€νΈλ μ΄μ νλ λ©μΈ μ ν리μΌμ΄μ μ Έμ λλ€. λΌμ°ν , μ μ μν κ΄λ¦¬ λ° λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ ν΅μ μ μ²λ¦¬νλ μν μ ν©λλ€.
- Activity Functions: λ§μ΄ν¬λ‘ νλ‘ νΈμλκ° νμ±(λ§μ΄νΈλ¨) λλ λΉνμ±(μΈλ§μ΄νΈλ¨) μνμ¬μΌ νλ μκΈ°λ₯Ό κ²°μ νλ μλ°μ€ν¬λ¦½νΈ ν¨μμ λλ€. μΌλ°μ μΌλ‘ URL κ²½λ‘ λλ κΈ°ν μ ν리μΌμ΄μ μνλ₯Ό κΈ°λ°μΌλ‘ ν©λλ€.
Single-SPA ꡬννκΈ°: λ¨κ³λ³ κ°μ΄λ
Reactλ‘ λΉλλ λ§μ΄ν¬λ‘ νλ‘ νΈμλ νλμ Vue.jsλ‘ λΉλλ λ€λ₯Έ νλ, μ΄λ κ² λ κ°μ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ‘ single-SPA μ ν리μΌμ΄μ μ μ€μ νλ κΈ°λ³Έ μμ λ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
1λ¨κ³: Single-SPA Config μ€μ
λ¨Όμ , single-SPA μ ν리μΌμ΄μ μ μν μ λλ ν°λ¦¬λ₯Ό λ§λ€κ³ Node.js νλ‘μ νΈλ₯Ό μ΄κΈ°νν©λλ€:
mkdir single-spa-example
cd single-spa-example
npm init -y
λ€μμΌλ‘, νμν μ’ μμ±μ μ€μΉν©λλ€:
npm install single-spa import-map-overrides
λ£¨νΈ λλ ν°λ¦¬μ `index.html` νμΌμ λ§λλλ€:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Single-SPA Example</title>
<meta name="importmap-type" content="systemjs-importmap">
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/single-spa.min.js",
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js",
"vue": "https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js"
}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/import-map-overrides@2.2.0/dist/import-map-overrides.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/system.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/extras/named-exports.js"></script>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script>
System.import('single-spa-config');
</script>
<import-map-overrides-full show-when-local-storage="devtools"></import-map-overrides-full>
</body>
</html>
μ΄ `index.html` νμΌμ SystemJS λͺ¨λ λ‘λ, μν¬νΈ λ§΅, κ·Έλ¦¬κ³ single-SPA μ€μ μ ꡬμ±ν©λλ€. μν¬νΈ λ§΅μ λ§μ΄ν¬λ‘ νλ‘ νΈμλμμ μ¬μ©νλ μ’ μμ±μ URLμ μ μν©λλ€.
`single-spa-config.js` νμΌμ λ§λλλ€:
import * as singleSpa from 'single-spa';
singleSpa.registerApplication(
'react-app',
() => System.import('react-app'),
location => location.pathname.startsWith('/react')
);
singleSpa.registerApplication(
'vue-app',
() => System.import('vue-app'),
location => location.pathname.startsWith('/vue')
);
singleSpa.start();
μ΄ νμΌμ `react-app`κ³Ό `vue-app`μ΄λΌλ λ κ°μ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ±λ‘ν©λλ€. `activityFunction`μ URLμ κΈ°λ°μΌλ‘ κ° λ§μ΄ν¬λ‘ νλ‘ νΈμλκ° μΈμ νμ±νλμ΄μΌ νλμ§λ₯Ό κ²°μ ν©λλ€.
2λ¨κ³: React λ§μ΄ν¬λ‘ νλ‘ νΈμλ λ§λ€κΈ°
React λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό μν μ λλ ν°λ¦¬λ₯Ό λ§λλλ€:
mkdir react-app
cd react-app
npx create-react-app .
npm install single-spa-react
`src/index.js` νμΌμ `single-spa-react`λ₯Ό μ¬μ©νλλ‘ μμ ν©λλ€:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import singleSpaReact from 'single-spa-react';
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: App,
errorBoundary(err, info, props) {
// Customize the root error boundary for your microfrontend here.
return (<h1>Error</h1>);
},
});
export const { bootstrap, mount, unmount } = lifecycles;
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
`public/index.html` νμΌμ΄ μλ€λ©΄ λ§λ€κ³ `root` divκ° μλμ§ νμΈν©λλ€:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
`App.js`λ₯Ό μμ νμ¬ μμ μ μ½κ² νμΈν μ μλλ‘ μ¬μ©μ μ§μ ν μ€νΈλ₯Ό νμν©λλ€:
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
This is the <b>React Micro-Frontend</b>!
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
React λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λΉλν©λλ€:
npm run build
`build` λλ ν°λ¦¬μ μ΄λ¦μ `react-app`μΌλ‘ λ³κ²½νκ³ single-SPA μ ν리μΌμ΄μ μ 루νΈμ λ°°μΉν©λλ€. κ·Έλ° λ€μ, `react-app` λλ ν°λ¦¬ λ΄μ `build/static/js` νμΌμ λ΄μ©μΌλ‘ `react-app.js` νμΌμ λ§λλλ€. `static/js` λλ ν°λ¦¬μ λ λ§μ js νμΌμ΄ μλ κ²½μ°, κ·Έκ²λ€λ ν¬ν¨ν©λλ€.
`index.html`μ μν¬νΈ λ§΅μ μ λ°μ΄νΈνμ¬ React λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό κ°λ¦¬ν€λλ‘ ν©λλ€:
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/single-spa.min.js",
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js",
"vue": "https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js",
"react-app": "/react-app/react-app.js"
}
}
3λ¨κ³: Vue.js λ§μ΄ν¬λ‘ νλ‘ νΈμλ λ§λ€κΈ°
Vue.js λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό μν μ λλ ν°λ¦¬λ₯Ό λ§λλλ€:
mkdir vue-app
cd vue-app
npx @vue/cli create .
npm install single-spa-vue --save
Vue CLI μ€μ μ€μ κΈ°λ³Έ ν리μ μ μ ννκ±°λ νμμ λ°λΌ μ¬μ©μ μ μν©λλ€.
`src/main.js` νμΌμ `single-spa-vue`λ₯Ό μ¬μ©νλλ‘ μμ ν©λλ€:
import Vue from 'vue'
import App from './App.vue'
import singleSpaVue from 'single-spa-vue';
Vue.config.productionTip = false
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
el: '#vue-app',
render: h => h(App)
}
});
export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;
`App.vue`λ₯Ό μμ νμ¬ μμ μ μ½κ² νμΈν μ μλλ‘ μ¬μ©μ μ§μ ν μ€νΈλ₯Ό νμν©λλ€:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<p>This is the <b>Vue Micro-Frontend</b>!</p>
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
Vue.js λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λΉλν©λλ€:
npm run build
`dist` λλ ν°λ¦¬μ μ΄λ¦μ `vue-app`μΌλ‘ λ³κ²½νκ³ single-SPA μ ν리μΌμ΄μ μ 루νΈμ λ°°μΉν©λλ€. κ·Έλ° λ€μ, `vue-app` λλ ν°λ¦¬ λ΄μ `dist/js/app.js` νμΌμ λ΄μ©μΌλ‘ `vue-app.js` νμΌμ λ§λλλ€. `dist/js` λλ ν°λ¦¬μ λ λ§μ js νμΌμ΄ μλ κ²½μ°, κ·Έκ²λ€λ ν¬ν¨ν©λλ€.
`index.html`μ μν¬νΈ λ§΅μ μ λ°μ΄νΈνμ¬ Vue.js λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό κ°λ¦¬ν€λλ‘ ν©λλ€:
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/single-spa.min.js",
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js",
"vue": "https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js",
"react-app": "/react-app/react-app.js",
"vue-app": "/vue-app/vue-app.js"
}
}
4λ¨κ³: μ ν리μΌμ΄μ μ 곡νκΈ°
κ°λ¨ν HTTP μλ²λ₯Ό μ¬μ©νμ¬ `index.html` νμΌμ μ 곡ν©λλ€. `http-server`μ κ°μ λꡬλ₯Ό μ¬μ©ν μ μμ΅λλ€:
npm install -g http-server
http-server -c-1
`http://localhost:8080/react`λ‘ μ΄λνμ¬ React λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ³΄κ³ , `http://localhost:8080/vue`λ‘ μ΄λνμ¬ Vue.js λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ΄ λλ€.
μ€μ κ³ λ € μ¬ν:
- μ΄ μμ λ URL μ λμ¬λ₯Ό κΈ°λ°μΌλ‘ ν κ°λ¨ν λΌμ°ν μ μ¬μ©ν©λλ€. λ 볡μ‘ν λΌμ°ν μλ리μ€μ κ²½μ° `single-spa-router`μ κ°μ μ μ© λΌμ°ν λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νλ κ²μ κ³ λ €νμμμ€.
- νλ‘λμ νκ²½μμλ μΌλ°μ μΌλ‘ CDN λλ λ€λ₯Έ μ μ μμ° νΈμ€ν μλΉμ€μμ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό μ 곡ν©λλ€.
- μ΄ μμ λ μ’ μμ± κ΄λ¦¬λ₯Ό μν΄ μν¬νΈ λ§΅μ μ¬μ©ν©λλ€. νλ‘λμ μ μν΄ Webpack λλ Parcelκ³Ό κ°μ λΉλ λꡬλ₯Ό μ¬μ©νμ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ²λ€λ§νλ κ²μ κ³ λ €νμμμ€.
κ³ κΈ Single-SPA κΈ°μ
κΈ°λ³Έμ μΈ single-SPA μ ν리μΌμ΄μ μ μ€μ ν νμλ μν€ν μ²μ νμ₯μ±κ³Ό μ μ§λ³΄μμ±μ ν₯μμν€κΈ° μν κ³ κΈ κΈ°μ μ νμν μ μμ΅λλ€.
Parcelμ μ΄μ©ν μ½λ 곡μ
Parcelμ μ¬μ©νλ©΄ λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ μ¬μ¬μ© κ°λ₯ν μ»΄ν¬λνΈμ λ‘μ§μ 곡μ ν μ μμ΅λλ€. μ΄λ μ½λ μ€λ³΅μ μ€μ΄κ³ μ ν리μΌμ΄μ μ λ°μ μΌκ΄μ±μ ν₯μμν€λ λ° λμμ΄ λ μ μμ΅λλ€.
Parcelμ λ§λ€λ €λ©΄ `singleSpa.mountRootParcel` ν¨μλ₯Ό μ¬μ©ν μ μμ΅λλ€:
import * as singleSpa from 'single-spa';
import React from 'react';
import ReactDOM from 'react-dom';
function MyParcel(props) {
return (<div>Hello from Parcel! {props.name}</div>);
}
const parcel = singleSpa.mountRootParcel(() => {
return Promise.resolve({
bootstrap: () => Promise.resolve(),
mount: (props) => {
ReactDOM.render(<MyParcel name={props.name} />, document.getElementById('parcel-container'));
return Promise.resolve();
},
unmount: () => {
ReactDOM.unmountComponentAtNode(document.getElementById('parcel-container'));
return Promise.resolve();
},
});
});
// To mount the parcel:
parcel.mount({ name: 'Example' });
λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ° ν΅μ
λ§μ΄ν¬λ‘ νλ‘ νΈμλλ μ’ μ’ λ°μ΄ν°λ₯Ό 곡μ νκ±°λ μμ μ νΈλ¦¬κ±°νκΈ° μν΄ μλ‘ ν΅μ ν΄μΌ ν©λλ€. μ΄λ₯Ό λ¬μ±νλ λ°©λ²μλ μ¬λ¬ κ°μ§κ° μμ΅λλ€:
- 곡μ μ μ μν: Redux λλ Vuexμ κ°μ μ μ μν κ΄λ¦¬ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νμ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ λ°μ΄ν°λ₯Ό 곡μ ν©λλ€.
- μ¬μ©μ μ μ μ΄λ²€νΈ: μ¬μ©μ μ μ DOM μ΄λ²€νΈλ₯Ό μ¬μ©νμ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ λ©μμ§λ₯Ό λΈλ‘λμΊμ€νΈν©λλ€.
- μ§μ ν¨μ νΈμΆ: ν λ§μ΄ν¬λ‘ νλ‘ νΈμλμμ ν¨μλ₯Ό λ΄λ³΄λ΄κ³ λ€λ₯Έ λ§μ΄ν¬λ‘ νλ‘ νΈμλμμ κ°μ Έμ΅λλ€. μ΄ μ κ·Ό λ°©μμ μ’ μμ±κ³Ό μν μ°Έμ‘°λ₯Ό νΌνκΈ° μν΄ μ μ€ν μ‘°μ μ΄ νμν©λλ€.
- λ©μμ§ λΈλ‘컀: RabbitMQ λλ Kafkaμ κ°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νμ¬ λ©μμ§ λΈλ‘컀 ν¨ν΄μ ꡬννμ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λΆλ¦¬νκ³ λΉλκΈ° ν΅μ μ κ°λ₯νκ² ν©λλ€.
μΈμ¦ λ° μΈκ°
λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²μμ μΈμ¦ λ° μΈκ°λ₯Ό ꡬννλ κ²μ μ΄λ €μΈ μ μμ΅λλ€. λ€μμ λͺ κ°μ§ μΌλ°μ μΈ μ κ·Ό λ°©μμ λλ€:
- μ€μ μ§μ€μ μΈμ¦: μ€μ μΈμ¦ μλΉμ€λ₯Ό μ¬μ©νμ¬ μ¬μ©μ λ‘κ·ΈμΈ λ° μΈμ¦μ μ²λ¦¬ν©λλ€. μΈμ¦ μλΉμ€λ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λν μμ²μ μΈμ¦νλ λ° μ¬μ©λλ ν ν°μ λ°κΈν μ μμ΅λλ€.
- 곡μ μΈμ¦ λͺ¨λ: λͺ¨λ λ§μ΄ν¬λ‘ νλ‘ νΈμλμμ μ¬μ©νλ 곡μ μΈμ¦ λͺ¨λμ λ§λλλ€. μ΄ λͺ¨λμ ν ν° κ΄λ¦¬ λ° μ¬μ©μ μΈμ μ μ²λ¦¬ν μ μμ΅λλ€.
- API κ²μ΄νΈμ¨μ΄: API κ²μ΄νΈμ¨μ΄λ₯Ό μ¬μ©νμ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λν λͺ¨λ μμ²μ λν μΈμ¦ λ° μΈκ°λ₯Ό μ²λ¦¬ν©λλ€. API κ²μ΄νΈμ¨μ΄λ ν ν°μ νμΈνκ³ μ κ·Ό μ μ΄ μ μ± μ μνν μ μμ΅λλ€.
Single-SPAλ₯Ό μ¬μ©ν λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²μ μ΄μ
- ν μμ¨μ± μ¦κ°: λ 립μ μΈ νμ λ€λ₯Έ νμ μν₯μ μ£Όμ§ μκ³ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό κ°λ°νκ³ λ°°ν¬ν μ μμ΅λλ€. μ΄λ μμ¨μ±κ³Ό λ λΉ λ₯Έ κ°λ° μ£ΌκΈ°λ₯Ό μ΄μ§ν©λλ€.
- νμ₯μ± ν₯μ: λ§μ΄ν¬λ‘ νλ‘ νΈμλλ λ 립μ μΌλ‘ νμ₯ν μ μμ΄ λ¦¬μμ€ ν λΉμ μ΅μ ννκ³ μ¦κ°λ νΈλν½μ μ²λ¦¬ν μ μμ΅λλ€.
- μ μ§λ³΄μμ± ν₯μ: λ μκ³ λ 립μ μΈ λ¨μλ λκ·λͺ¨ λͺ¨λ리μ μ ν리μΌμ΄μ μ λΉν΄ μ μ§λ³΄μ λ° μ λ°μ΄νΈκ° λ μ½μ΅λλ€.
- κΈ°μ λ€μμ±: νμ μμ μ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ κ°μ₯ μ ν©ν κΈ°μ μ€νμ μ νν μ μμ΄ λ ν° μ μ°μ±κ³Ό νμ μ νμ©ν©λλ€.
- μν κ°μ: λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λ 립μ μΈ λ°°ν¬λ λ³κ²½ μ¬ν λ°°ν¬μ μνμ μ€μ΄κ³ λ‘€λ°± μ μ°¨λ₯Ό λ¨μνν©λλ€.
- μ μ§μ λ§μ΄κ·Έλ μ΄μ : μ 체 μ¬μμ± μμ΄ λͺ¨λ리μ μ ν리μΌμ΄μ μ λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ‘ μ μ§μ μΌλ‘ λ§μ΄κ·Έλ μ΄μ ν μ μμ΅λλ€.
λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²μ κ³Όμ
λ§μ΄ν¬λ‘ νλ‘ νΈμλλ λ§μ μ΄μ μ μ 곡νμ§λ§ λͺ κ°μ§ κ³Όμ λ μμ΅λλ€:
- 볡μ‘μ± μ¦κ°: μ¬λ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό κ΄λ¦¬νλ κ²μ λ¨μΌ λͺ¨λ리μ μ ν리μΌμ΄μ μ κ΄λ¦¬νλ κ²λ³΄λ€ λ 볡μ‘ν μ μμ΅λλ€.
- ν΅μ μ€λ²ν€λ: λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ ν΅μ μ μ‘°μ νλ κ²μ μ΄λ €μΈ μ μμ΅λλ€.
- λ°°ν¬ λ³΅μ‘μ±: μ¬λ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ°°ν¬νλ κ²μ λ¨μΌ μ ν리μΌμ΄μ μ λ°°ν¬νλ κ²λ³΄λ€ λ 볡μ‘ν μ μμ΅λλ€.
- μΌκ΄μ±: λ§μ΄ν¬λ‘ νλ‘ νΈμλ μ λ°μ κ±Έμ³ μΌκ΄λ μ¬μ©μ κ²½νμ μ μ§νλ κ²μ΄ μ΄λ €μΈ μ μμ΅λλ€.
- μ€λ³΅: μ μ€ν κ³ν μμ΄λ μ½λμ μ’ μμ±μ΄ λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ μ€λ³΅λ μ μμ΅λλ€.
- μ΄μ μ€λ²ν€λ: μ¬λ¬ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό μν μΈνλΌλ₯Ό μ€μ νκ³ κ΄λ¦¬νλ©΄ μ΄μ μ€λ²ν€λκ° μ¦κ°ν μ μμ΅λλ€.
Single-SPAλ‘ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό ꡬμΆνκΈ° μν λͺ¨λ² μ¬λ‘
single-SPAλ‘ λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ₯Ό μ±κ³΅μ μΌλ‘ ꡬννλ €λ©΄ λ€μ λͺ¨λ² μ¬λ‘λ₯Ό λ°λ₯΄μμμ€:
- λͺ νν κ²½κ³ μ μ: μ’ μμ±κ³Ό ν΅μ μ€λ²ν€λλ₯Ό μ΅μννκΈ° μν΄ λ§μ΄ν¬λ‘ νλ‘ νΈμλ κ°μ κ²½κ³λ₯Ό λͺ ννκ² μ μν©λλ€.
- 곡μ μ€νμΌ κ°μ΄λ μ립: λ§μ΄ν¬λ‘ νλ‘ νΈμλ μ λ°μ κ±Έμ³ μΌκ΄λ μ¬μ©μ κ²½νμ 보μ₯νκΈ° μν΄ κ³΅μ μ€νμΌ κ°μ΄λλ₯Ό λ§λλλ€.
- λ°°ν¬ μλν: λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λ°°ν¬λ₯Ό λ¨μννκΈ° μν΄ λ°°ν¬ νλ‘μΈμ€λ₯Ό μλνν©λλ€.
- μ±λ₯ λͺ¨λν°λ§: κ° λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μ±λ₯μ λͺ¨λν°λ§νμ¬ λ¬Έμ λ₯Ό μλ³νκ³ ν΄κ²°ν©λλ€.
- μ€μ μ§μ€μ λ‘κΉ μμ€ν μ¬μ©: μ€μ μ§μ€μ λ‘κΉ μμ€ν μ μ¬μ©νμ¬ λͺ¨λ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λ‘κ·Έλ₯Ό μ§κ³νκ³ λ¬Έμ ν΄κ²°μ λ¨μνν©λλ€.
- κ²¬κ³ ν μ€λ₯ μ²λ¦¬ ꡬν: ν λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μ€λ₯κ° λ€λ₯Έ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μν₯μ λ―ΈμΉμ§ μλλ‘ κ²¬κ³ ν μ€λ₯ μ²λ¦¬λ₯Ό ꡬνν©λλ€.
- μν€ν μ² λ¬Έμν: νμ λͺ¨λ μ¬λμ΄ μλ λ°©μμ μ΄ν΄ν μ μλλ‘ λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ₯Ό λ¬Έμνν©λλ€.
- μ¬λ°λ₯Έ ν΅μ μ λ΅ μ ν: μ ν리μΌμ΄μ μ μꡬμ λ°λΌ μ μ ν ν΅μ μ λ΅μ μ νν©λλ€.
- μ±λ₯ μ°μ μ: λΉ λ₯΄κ³ λ°μμ΄ λΉ λ₯Έ μ¬μ©μ κ²½νμ 보μ₯νκΈ° μν΄ κ° λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μ±λ₯μ μ΅μ νν©λλ€.
- 보μ κ³ λ €: λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ₯Ό μ·¨μ½μ μΌλ‘λΆν° 보νΈνκΈ° μν΄ λ³΄μ λͺ¨λ² μ¬λ‘λ₯Ό ꡬνν©λλ€.
- DevOps λ¬Έν μ±ν: κ°λ°νκ³Ό μ΄μν κ°μ νμ μ μ΄μ§νκΈ° μν΄ DevOps λ¬Ένλ₯Ό μ‘°μ±ν©λλ€.
Single-SPAμ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μ¬μ© μ¬λ‘
Single-SPAμ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ λ€μκ³Ό κ°μ λ€μν μ¬μ© μ¬λ‘μ μ ν©ν©λλ€:
- ν¬κ³ 볡μ‘ν μ ν리μΌμ΄μ : λ§μ΄ν¬λ‘ νλ‘ νΈμλλ ν¬κ³ 볡μ‘ν μ ν리μΌμ΄μ μ λ μκ³ κ΄λ¦¬νκΈ° μ¬μ΄ λ¨μλ‘ λΆν΄νλ λ° λμμ΄ λ μ μμ΅λλ€.
- μ¬λ¬ νμ΄ μλ μ‘°μ§: λ§μ΄ν¬λ‘ νλ‘ νΈμλλ λ€λ₯Έ νμ΄ μ ν리μΌμ΄μ μ λ€λ₯Έ λΆλΆμμ λ 립μ μΌλ‘ μμ ν μ μλλ‘ ν©λλ€. μλ₯Ό λ€μ΄, κΈλ‘λ² μ μμκ±°λ νμ¬μμ ν νμ μ ν μΉ΄νλ‘κ·Έ(μ: λ μΌμ κΈ°λ°)μ μ§μ€νκ³ , λ€λ₯Έ νμ μΌν μΉ΄νΈ(μ: μΈλμ κΈ°λ°)λ₯Ό μ²λ¦¬νλ©°, μΈ λ²μ§Έ νμ μ¬μ©μ κ³μ (μ: λ―Έκ΅μ κΈ°λ°)μ κ΄λ¦¬ν μ μμ΅λλ€.
- λ κ±°μ μ ν리μΌμ΄μ λ§μ΄κ·Έλ μ΄μ : λ§μ΄ν¬λ‘ νλ‘ νΈμλλ λ κ±°μ μ ν리μΌμ΄μ μ λ³΄λ€ νλμ μΈ μν€ν μ²λ‘ μ μ§μ μΌλ‘ λ§μ΄κ·Έλ μ΄μ νλ λ° μ¬μ©ν μ μμ΅λλ€.
- μλΉμ€ν νλ«νΌ(PaaS) μ루μ ꡬμΆ: λ§μ΄ν¬λ‘ νλ‘ νΈμλλ κ°λ°μκ° μμ μ μ ν리μΌμ΄μ μ λ§λ€κ³ λ°°ν¬ν μ μλ PaaS μ루μ μ ꡬμΆνλ λ° μ¬μ©ν μ μμ΅λλ€.
- κ°μΈνλ μ¬μ©μ κ²½ν: μ¬μ©μ μν , μ νΈλ λλ μμΉμ λ°λΌ κ°μΈνλ μ¬μ©μ κ²½νμ μ 곡νκΈ° μν΄ λ€λ₯Έ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό μ¬μ©ν μ μμ΅λλ€. μ¬μ©μμ κ΄μ¬μ¬μ μ½κΈ° κΈ°λ‘μ λ°λΌ λμ μΌλ‘ λ€λ₯Έ μ½ν μΈ λͺ¨λμ λ‘λνλ λ΄μ€ μΉμ¬μ΄νΈλ₯Ό μμν΄ λ³΄μμμ€.
λ§μ΄ν¬λ‘ νλ‘ νΈμλμ λ―Έλ
λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ λΆμ°λ νλ‘ νΈμλ μ ν리μΌμ΄μ μ κ΅¬μΆ λ° κ΄λ¦¬ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν μλ‘μ΄ λꡬμ κΈ°μ μ΄ λ±μ₯νλ©΄μ κ³μν΄μ λ°μ νκ³ μμ΅λλ€. μ£Όλͺ©ν΄μΌ ν λͺ κ°μ§ μ£Όμ νΈλ λλ λ€μκ³Ό κ°μ΅λλ€:
- μΉ μ»΄ν¬λνΈ: μΉ μ»΄ν¬λνΈλ λͺ¨λ μΉ μ ν리μΌμ΄μ μμ μ¬μ©ν μ μλ μ¬μ¬μ© κ°λ₯ν UI μμλ₯Ό λ§λ€κΈ° μν νμ€μ λλ€. μΉ μ»΄ν¬λνΈλ₯Ό μ¬μ©νμ¬ νλ μμν¬μ ꡬμ λ°μ§ μκ³ λ€λ₯Έ μ ν리μΌμ΄μ μ μ½κ² ν΅ν©ν μ μλ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό ꡬμΆν μ μμ΅λλ€.
- λͺ¨λ νλλ μ΄μ : λͺ¨λ νλλ μ΄μ μ μλ‘ λ€λ₯Έ Webpack λΉλ κ°μ μ½λμ μ’ μμ±μ 곡μ ν μ μκ² ν΄μ£Όλ Webpack κΈ°λ₯μ λλ€. λͺ¨λ νλλ μ΄μ μ μ¬μ©νμ¬ λμ¨νκ² κ²°ν©λκ³ λ 립μ μΌλ‘ λ°°ν¬ κ°λ₯ν λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό ꡬμΆν μ μμ΅λλ€.
- μλ² μΈ‘ λ λλ§(SSR): μλ² μΈ‘ λ λλ§μ λ§μ΄ν¬λ‘ νλ‘ νΈμλ μ ν리μΌμ΄μ μ μ±λ₯κ³Ό SEOλ₯Ό ν₯μμν¬ μ μμ΅λλ€. SSRμ μ¬μ©νμ¬ μλ²μμ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μ΄κΈ° HTMLμ λ λλ§νμ¬ ν΄λΌμ΄μΈνΈμμ λ€μ΄λ‘λνκ³ μ€νν΄μΌ νλ μλ°μ€ν¬λ¦½νΈμ μμ μ€μΌ μ μμ΅λλ€.
- μ£μ§ μ»΄ν¨ν : μ£μ§ μ»΄ν¨ν μ μ¬μ©νμ¬ μ¬μ©μμ λ κ°κΉμ΄ κ³³μ λ§μ΄ν¬λ‘ νλ‘ νΈμλλ₯Ό λ°°ν¬νμ¬ λκΈ° μκ°μ μ€μ΄κ³ μ±λ₯μ ν₯μμν¬ μ μμ΅λλ€. μ£μ§ μ»΄ν¨ν μ λν μ€νλΌμΈ μ‘μΈμ€ λ° μ€μκ° λ°μ΄ν° μ²λ¦¬μ κ°μ λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μλ‘μ΄ μ¬μ© μ¬λ‘λ₯Ό κ°λ₯νκ² ν μ μμ΅λλ€.
κ²°λ‘
Single-SPAλ νμ₯ κ°λ₯νκ³ μ μ§λ³΄μ κ°λ₯νλ©° μ μ°ν λ§μ΄ν¬λ‘ νλ‘ νΈμλ μν€ν μ²λ₯Ό ꡬμΆνκΈ° μν κ°λ ₯ν νλ μμν¬μ λλ€. λ§μ΄ν¬λ‘ νλ‘ νΈμλμ μμΉμ μμ©νκ³ single-SPAμ κΈ°λ₯μ νμ©ν¨μΌλ‘μ¨ μ‘°μ§μ νμ κΆνμ λΆμ¬νκ³ κ°λ° μ£ΌκΈ°λ₯Ό κ°μννλ©° λ°μ΄λ μ¬μ©μ κ²½νμ μ 곡ν μ μμ΅λλ€. λ§μ΄ν¬λ‘ νλ‘ νΈμλλ 볡μ‘μ±μ μΌκΈ°νμ§λ§, λͺ¨λ² μ¬λ‘λ₯Ό μ±ννκ³ μ μ€νκ² κ³ννλ©° μ¬λ°λ₯Έ λꡬλ₯Ό μ ννλ κ²μ΄ μ±κ³΅μ νμμ μ λλ€. λ§μ΄ν¬λ‘ νλ‘ νΈμλ νκ²½μ΄ κ³μ λ°μ ν¨μ λ°λΌ μλ‘μ΄ κΈ°μ κ³Ό κΈ°λ²μ λν μ 보λ₯Ό μ§μμ μΌλ‘ νμ νλ κ²μ΄ νλμ μ΄κ³ νλ ₯μ μΈ μΉ μ ν리μΌμ΄μ μ ꡬμΆνλ λ° μ€μν κ²μ λλ€.