단일 HTML 파일의 머티리얼 UI

Material UI는 React 구성 요소 라이브러리이므로 단일 HTML 파일에도 React가 필요합니다. 가져오기 맵과 모듈 스크립트는 아무것도 설치할 필요 없이 jsDelivr에서 모두 로드됩니다.

영문 원문 보기

머티리얼 UI(MUI)는 Google의 머티리얼 디자인을 구현하는 오픈 소스 React 구성요소 라이브러리입니다. 미리 만들어진 버튼, 텍스트 필드, 대화 상자, 목록 및 테마 시스템을 모두 React 구성 요소로 제공합니다.

마지막 부분은 단일 HTML 파일에 중요합니다. 머티리얼 UI는 링크한 스타일시트가 아닙니다. 페이지에는 React와 구성 요소를 렌더링하는 스크립트도 필요합니다. 작동하는 가장 작은 버전은 다음과 같습니다.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Material UI in one HTML file</title>
<!-- Roboto, the font Material UI uses by default -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap">
<!-- The import map must come before the module script.
     The last four entries send every copy of React to one version. -->
<script type="importmap">
{
  "imports": {
    "react": "https://cdn.jsdelivr.net/npm/react@19.2.8/+esm",
    "react-dom/client": "https://cdn.jsdelivr.net/npm/react-dom@19.2.8/client/+esm",
    "@mui/material": "https://cdn.jsdelivr.net/npm/@mui/material@9.4.0/+esm",

    "https://cdn.jsdelivr.net/npm/react@18.2.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@19.1.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@%5E17.0.0%20%7C%7C%20%5E18.0.0%20%7C%7C%20%5E19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/"
  }
}
</script>
<style>
  body { margin: 0; font-family: Roboto, system-ui, sans-serif; }
</style>
</head>
<body>
<div id="root"><p style="padding: 20px">Loading Material UI…</p></div>

<script type="module">
  import React from 'react';
  import { createRoot } from 'react-dom/client';
  import { Box, Button, Stack, Typography } from '@mui/material';

  const h = React.createElement;  // plain JavaScript instead of JSX: no build step

  function App() {
    const [count, setCount] = React.useState(0);
    return h(Box, { sx: { p: 3 } },
      h(Typography, { variant: 'h5', gutterBottom: true }, 'Hello, Material UI'),
      h(Typography, { color: 'text.secondary', sx: { mb: 2 } },
        'Real MUI components, loaded from jsDelivr.'),
      h(Stack, { direction: 'row', spacing: 1.5 },
        h(Button, { id: 'add', variant: 'contained', onClick: () => setCount(count + 1) },
          'Clicked ' + count + (count === 1 ? ' time' : ' times')),
        h(Button, { id: 'reset', variant: 'outlined', onClick: () => setCount(0) }, 'Reset')
      )
    );
  }

  createRoot(document.getElementById('root')).render(h(App));
</script>
</body>
</html>
jsDelivr에서 로드된 Real Material UI 구성 요소입니다. 버튼을 클릭한 다음 코드를 편집하면 예제가 다시 실행됩니다.

npm도 없고 번들러도 없고 서버도 없습니다. 파일은 브라우저에서 그대로 열립니다.

파일에 필요한 것

Material UI 문서는 npm을 사용하여 설치하고 React 및 React DOM을 피어 종속성으로 나열합니다. 단일 파일에서 해당 세 가지 패키지는 대신 CDN에서 제공됩니다.

조각 기능 여기서 나오는 내용
로보토 Material UI가 기본적으로 사용하는 글꼴 구글 폰트 링크
반응과 반응 DOM 구성 요소를 실행하고 그립니다. jsDelivr
@mui/material 구성요소와 테마 jsDelivr
귀하의 스크립트 구성 요소에서 페이지를 빌드합니다. 파일의 모듈 스크립트

문서에서는 CDN 옵션도 설명하고 이를 "빠른 프로토타입 제작을 위한 훌륭한 옵션"이라고 부릅니다. 공식 CDN 예제는 esm.sh에서 패키지를 로드하고 브라우저에서 Babel을 사용하여 JSX를 컴파일합니다. 이 가이드에서는 대신 cdn.jsdelivr.net 및 일반 JavaScript를 사용합니다.

가장 작은 작업 페이지

이 페이지의 모든 예시는 다음 순서대로 동일한 5개 부분으로 구성됩니다.

  1. Roboto 링크가 머리에 있습니다.
  2. 가져오기 맵(역시 헤드에 있음) 고정된 jsDelivr URL에 react 및 @mui/material와 같은 짧은 이름을 제공합니다.
  3. 빈 <div id="root">. React는 그 안에 페이지를 그립니다.
  4. React, createRoot 및 구성요소를 가져오는 모듈 스크립트입니다.
  5. 1개의 렌더링 호출: createRoot(root).render(h(App)).
가져오기 맵은 파일 이름을 지정하고, jsDelivr은 해당 파일을 모듈로 제공하며, React는 루트 요소를 채웁니다.
가져오기 맵은 파일 이름을 지정하고, jsDelivr은 해당 파일을 모듈로 제공하며, React는 루트 요소를 채웁니다.

가져오기 맵은 모듈 스크립트 앞에 와야 합니다. MDN에서는 선언된 이름을 사용하여 모듈을 가져오는 스크립트보다 먼저 가져오기 맵을 처리해야 한다고 말합니다.

<script type="importmap">
{
  "imports": {
    "react": "https://cdn.jsdelivr.net/npm/react@19.2.8/+esm",
    "react-dom/client": "https://cdn.jsdelivr.net/npm/react-dom@19.2.8/client/+esm",
    "@mui/material": "https://cdn.jsdelivr.net/npm/@mui/material@9.4.0/+esm"
  }
}
</script>

<div id="root"></div>

<script type="module">
  import React from 'react';
  import { createRoot } from 'react-dom/client';
  import { Button } from '@mui/material';

  const h = React.createElement;
  createRoot(document.getElementById('root'))
    .render(h(Button, { variant: 'contained' }, 'Hello'));
</script>

jsDelivr npm URL에 /+esm를 추가하면 패키지가 ES 모듈로 반환됩니다. 페이지가 변경되지 않도록 모든 URL에 정확한 버전을 고정하세요.

가져오기 맵에 추가 선이 4개 있는 이유

위의 짧은 지도만으로는 충분하지 않습니다. 실행하면 페이지가 공백으로 유지되고 콘솔에 다음 오류가 표시됩니다.

Cannot read properties of null (reading 'useContext')

원인은 React의 두 복사본입니다. Material UI는 기본 스타일 엔진인 Emotion과 같은 도우미 패키지를 가져옵니다. 2026-10-01을 확인했을 때 jsDelivr의 일부 헬퍼 빌드는 19.2.8 대신 19.0.0과 같은 다른 React 버전을 가져왔습니다.

왼쪽: 감정이 자체적인 React를 얻고 후크가 실패합니다. 오른쪽: 가져오기 맵은 이를 React-dom과 동일한 React로 보냅니다.
왼쪽: 감정이 자체적인 React를 얻고 후크가 실패합니다. 오른쪽: 가져오기 맵은 이를 React-dom과 동일한 React로 보냅니다.

React 문서에서는 이에 대해 설명합니다. 후크가 작동하려면 코드에서 가져오는 react가 react-dom가 사용하는 모듈과 동일해야 합니다.

가져오기 맵 키는 전체 URL일 수 있으며 /로 끝나는 키는 이 키로 시작하는 모든 주소를 다시 매핑합니다. 따라서 다음 줄은 이탈된 버전을 하나의 복사본으로 보냅니다.

"https://cdn.jsdelivr.net/npm/react@18.2.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
"https://cdn.jsdelivr.net/npm/react@19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
"https://cdn.jsdelivr.net/npm/react@19.1.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
"https://cdn.jsdelivr.net/npm/react@%5E17.0.0%20%7C%7C%20%5E18.0.0%20%7C%7C%20%5E19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/"

이 줄은 여기에 고정된 버전과 일치합니다. 버전을 변경하는 경우 DevTools에서 네트워크 패널을 열고 react@로 필터링한 다음 자신의 버전이 아닌 다른 React 버전에 대한 줄을 추가하세요.

JSX 또는 일반 createElement

머티리얼 UI 예제는 일반적으로 <Button variant="contained">와 같은 JSX로 작성됩니다. React 문서에서는 JSX를 JavaScript용 구문 확장이라고 부르며 일반 모듈 스크립트에서는 이를 실행할 수 없습니다. 브라우저는 Unexpected token '<'가 있는 첫 번째 <에서 중지됩니다.

여기의 예에서는 h로 이름이 변경된 React.createElement를 사용합니다. React 문서에는 JSX의 대안으로 나열되어 있습니다. 각 JSX 태그는 하나의 호출이 됩니다.

// JSX (needs a compile step)
<Button variant="contained" onClick={save}>Save</Button>

// The same thing, runs as is
h(Button, { variant: 'contained', onClick: save }, 'Save')

JSX를 선호하는 경우 Babel 독립 실행형은 브라우저에서 <script type="text/babel"> 태그를 컴파일합니다. Babel 문서에서는 프로덕션에서는 이를 권장하지 않으며 미리 컴파일할 것을 권장합니다.

관련된 실수는 구성 요소를 HTML에 직접 작성하는 것입니다. 브라우저는 이를 일반 태그로 읽습니다.

HTML 태그 이름은 대소문자를 구분하지 않으므로 마크업의 Button 태그는 일반 버튼입니다. React만이 Material UI를 렌더링할 수 있습니다.
HTML 태그 이름은 대소문자를 구분하지 않으므로 마크업의 Button 태그는 일반 버튼입니다. React만이 Material UI를 렌더링할 수 있습니다.

테마: 컬러 및 다크 모드

구성 요소는 테마에서 색상, 모서리 및 글꼴을 가져옵니다. createTheme로 하나를 만들고 ThemeProvider로 페이지를 래핑하면 내부의 모든 구성 요소가 이를 따릅니다. CssBaseline는 페이지 배경과 텍스트 색상도 따라오게 만듭니다.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Material UI theme: colour and dark mode</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap">
<script type="importmap">
{
  "imports": {
    "react": "https://cdn.jsdelivr.net/npm/react@19.2.8/+esm",
    "react-dom/client": "https://cdn.jsdelivr.net/npm/react-dom@19.2.8/client/+esm",
    "@mui/material": "https://cdn.jsdelivr.net/npm/@mui/material@9.4.0/+esm",

    "https://cdn.jsdelivr.net/npm/react@18.2.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@19.1.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@%5E17.0.0%20%7C%7C%20%5E18.0.0%20%7C%7C%20%5E19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/"
  }
}
</script>
</head>
<body>
<div id="root"><p style="padding: 20px; font-family: sans-serif">Loading Material UI…</p></div>

<script type="module">
  import React from 'react';
  import { createRoot } from 'react-dom/client';
  import {
    createTheme, ThemeProvider, CssBaseline,
    Box, Stack, Button, TextField, Alert, Switch, FormControlLabel, Typography
  } from '@mui/material';

  const h = React.createElement;
  const colors = { Indigo: '#3f51b5', Teal: '#00897b', Orange: '#e65100' };

  function App() {
    const [dark, setDark] = React.useState(false);
    const [main, setMain] = React.useState(colors.Indigo);

    // One theme object styles every component inside ThemeProvider
    const theme = React.useMemo(() => createTheme({
      palette: { mode: dark ? 'dark' : 'light', primary: { main } },
      shape: { borderRadius: 10 },
    }), [dark, main]);

    return h(ThemeProvider, { theme },
      h(CssBaseline),  // page background and text colour follow the theme
      h(Box, { sx: { p: 2.5, maxWidth: 520 } },
        h(Stack, { direction: 'row', spacing: 1, sx: { mb: 2, flexWrap: 'wrap' } },
          Object.entries(colors).map(([name, hex]) =>
            h(Button, {
              key: name, size: 'small', onClick: () => setMain(hex),
              variant: hex === main ? 'contained' : 'outlined',
            }, name)),
          h(FormControlLabel, {
            sx: { ml: 1 },
            label: 'Dark',
            control: h(Switch, { id: 'dark', checked: dark, onChange: (e) => setDark(e.target.checked) }),
          })
        ),
        h(Typography, { variant: 'subtitle2', sx: { mb: 1 } }, 'The three Button variants'),
        h(Stack, { direction: 'row', spacing: 1, sx: { mb: 2.5 } },
          h(Button, { variant: 'text' }, 'Text'),
          h(Button, { variant: 'outlined' }, 'Outlined'),
          h(Button, { id: 'contained', variant: 'contained' }, 'Contained')
        ),
        h(TextField, { label: 'Your name', size: 'small', fullWidth: true, sx: { mb: 2 } }),
        h(Alert, { severity: 'info' }, 'Change the colour or switch to dark: every component follows.')
      )
    );
  }

  createRoot(document.getElementById('root')).render(h(App));
</script>
</body>
</html>
색상을 선택하거나 스위치를 켜세요. 버튼, 필드 및 경고는 모두 하나의 테마 개체를 따릅니다.

다크 모드는 하나의 팔레트 설정입니다. 문서에 따르면 Material UI에는 밝은(기본값)과 어두운 두 가지 팔레트 모드가 있습니다.

const theme = createTheme({
  palette: { mode: 'dark', primary: { main: '#00897b' } },
});

Button는 text(기본값), outlined 및 contained의 세 가지 변형으로 제공됩니다. 패딩 및 여백을 위한 sx: { p: 2, mb: 1 }와 같은 sx 소품에서 더 작은 조정이 이루어집니다.

완성된 예: 작업 목록

이것은 양식, 확인란이 있는 목록, 두 개의 아이콘 및 스낵바 메시지를 추가합니다. 양식은 페이지 내부에서 처리되므로 아무데도 전송되지 않습니다.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Material UI task list</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap">
<script type="importmap">
{
  "imports": {
    "react": "https://cdn.jsdelivr.net/npm/react@19.2.8/+esm",
    "react-dom/client": "https://cdn.jsdelivr.net/npm/react-dom@19.2.8/client/+esm",
    "@mui/material": "https://cdn.jsdelivr.net/npm/@mui/material@9.4.0/+esm",
    "@mui/icons-material/Delete": "https://cdn.jsdelivr.net/npm/@mui/icons-material@9.4.0/Delete/+esm",
    "@mui/icons-material/Add": "https://cdn.jsdelivr.net/npm/@mui/icons-material@9.4.0/Add/+esm",

    "https://cdn.jsdelivr.net/npm/react@18.2.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@19.1.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/",
    "https://cdn.jsdelivr.net/npm/react@%5E17.0.0%20%7C%7C%20%5E18.0.0%20%7C%7C%20%5E19.0.0/": "https://cdn.jsdelivr.net/npm/react@19.2.8/"
  }
}
</script>
</head>
<body>
<div id="root"><p style="padding: 20px; font-family: sans-serif">Loading Material UI…</p></div>

<script type="module">
  import React from 'react';
  import { createRoot } from 'react-dom/client';
  import {
    createTheme, ThemeProvider, CssBaseline, Paper, Typography, Stack, TextField, Button,
    List, ListItem, ListItemIcon, ListItemText, Checkbox, IconButton, Chip, Snackbar
  } from '@mui/material';
  import DeleteIcon from '@mui/icons-material/Delete';
  import AddIcon from '@mui/icons-material/Add';

  const h = React.createElement;
  const theme = createTheme({ palette: { primary: { main: '#00796b' }, background: { default: '#eef2f1' } } });
  let nextId = 3;

  function App() {
    const [tasks, setTasks] = React.useState([
      { id: 1, text: 'Pick a theme colour', done: true },
      { id: 2, text: 'Send the share link', done: false },
    ]);
    const [text, setText] = React.useState('');
    const [toast, setToast] = React.useState('');

    // The form is handled in the page: nothing is sent to a server
    function add(e) {
      e.preventDefault();
      const value = text.trim();
      if (!value) return;
      setTasks([...tasks, { id: nextId++, text: value, done: false }]);
      setText('');
      setToast('Added "' + value + '"');
    }
    const toggle = (id) => setTasks(tasks.map((t) => t.id === id ? { ...t, done: !t.done } : t));
    const remove = (id) => setTasks(tasks.filter((t) => t.id !== id));
    const left = tasks.filter((t) => !t.done).length;

    return h(ThemeProvider, { theme },
      h(CssBaseline),
      h(Paper, { elevation: 2, sx: { m: 2, p: 2, maxWidth: 480 } },
        h(Stack, { direction: 'row', sx: { alignItems: 'center', justifyContent: 'space-between', mb: 1.5 } },
          h(Typography, { variant: 'h6' }, 'Tasks'),
          h(Chip, { id: 'left', size: 'small', color: 'primary', label: left + ' open' })
        ),
        h('form', { id: 'add-form', onSubmit: add },
          h(Stack, { direction: 'row', spacing: 1 },
            h(TextField, {
              id: 'task', label: 'New task', size: 'small', fullWidth: true,
              value: text, onChange: (e) => setText(e.target.value),
            }),
            h(Button, { type: 'submit', variant: 'contained', startIcon: h(AddIcon) }, 'Add')
          )
        ),
        h(List, { id: 'list', dense: true },
          tasks.map((t) => h(ListItem, {
            key: t.id,
            secondaryAction: h(IconButton, { edge: 'end', 'aria-label': 'Delete', onClick: () => remove(t.id) }, h(DeleteIcon)),
          },
            h(ListItemIcon, null, h(Checkbox, { edge: 'start', checked: t.done, onChange: () => toggle(t.id) })),
            h(ListItemText, {
              primary: t.text,
              sx: { textDecoration: t.done ? 'line-through' : 'none', color: t.done ? 'text.disabled' : 'text.primary' },
            })
          ))
        )
      ),
      h(Snackbar, {
        open: !!toast, message: toast, autoHideDuration: 2500, onClose: () => setToast(''),
        anchorOrigin: { vertical: 'bottom', horizontal: 'center' },
      })
    );
  }

  createRoot(document.getElementById('root')).render(h(App));
</script>
</body>
</html>
작업을 입력하고 Enter를 누르세요. 체크하거나 삭제하세요. 스낵바는 각각의 새로운 작업을 확인합니다.
  • 형식: onSubmit는 e.preventDefault()를 호출하고 상태에 작업을 추가합니다. 대신 서버로 보내려면 HTML 양식 제출을 참조하세요.
  • 아이콘: 문서는 @mui/icons-material에서 각 아이콘을 자체 구성요소로 내보냅니다. @mui/icons-material/Delete와 같이 사용하는 항목만 매핑하세요.
  • 상태: React.useState는 목록을 보유하고 있으며 변경 시 Material UI가 다시 그려집니다.

npm으로 전환해야 하는 시기

Material UI 문서는 제작에 CDN 접근 방식을 권장하지 않습니다. 브라우저는 사용하는 구성 요소에 관계없이 전체 라이브러리를 다운로드합니다. 제품의 경우 패키지를 설치하고 다음을 빌드합니다.

npm install @mui/material @emotion/react @emotion/styled

그러면 코드는 CDN URL 대신 설치된 패키지를 가져옵니다. 빌드된 프런트 엔드 호스팅에서는 구축된 앱을 온라인에 올리는 방법을 다룹니다.

작동하지 않을 때

당신이 보는 것 원인 수정
빈 페이지, 콘솔의 useContext 오류 React 사본 2개 가져오기 맵에 React 리맵 라인 추가
Unexpected token '<' 일반 모듈 스크립트의 JSX h(...) 또는 Babel 독립형 사용
Failed to resolve module specifier 가져오기 맵에서 이름이 누락되었거나 맵이 스크립트 뒤에 옵니다. 이름을 추가하고 스크립트 위에 지도를 유지하세요.
회색 브라우저 버튼, 스타일 없음 HTML 태그로 작성된 구성 요소 모듈 스크립트에서 렌더링
잘못된 글꼴 Roboto 링크 없음 헤드에 Google Fonts 링크 추가
새 릴리스 이후 페이지 변경 버전이 URL에 고정되지 않았습니다. 모든 URL에 정확한 버전을 입력하세요.

기타 스크립트 문제의 경우 HTML JavaScript가 작동하지 않는 이유에서 일반적인 검사를 거칩니다.

링크로 공유하세요

머티리얼 UI 페이지는 설명하는 것보다 보여주는 것이 더 쉽습니다. 스크린샷을 클릭할 수 없으며 .html 첨부 파일이 일반 코드로 열리거나 전화기에서 전혀 열리지 않을 수 있습니다.

작업 버전을 보내려면 페이지를 NOS 문서에 붙여넣고 공유 링크 만들기를 선택하세요. 링크할 HTML이 이를 안내합니다.

페이지는 작성된 대로 렌더링되고, 해당 스크립트가 실행되고, cdn.jsdelivr.net의 모듈이 로드되므로 페이지를 받는 사람은 실제 구성 요소를 직접 클릭할 수 있습니다. 나중에 코드를 변경하면 동일한 링크에 새 버전이 표시됩니다.

자주 묻는 질문

React 없이 Material UI를 사용할 수 있나요?

아니요. Material UI는 React 구성 요소 라이브러리이므로 모든 Button, TextField 및 Dialog는 React 구성 요소입니다. 이 페이지의 예제처럼 단일 HTML 파일은 React를 로드하고 이를 사용하여 구성 요소를 렌더링할 때 작동합니다.

머티리얼 UI는 무료인가요?

@mui/material 패키지는 package.json 및 Material UI 저장소의 LICENSE 파일에 명시된 대로 MIT 라이선스에 따라 릴리스됩니다.

예제에서는 어떤 버전을 사용합니까?

React 19.2.8이 포함된 Material UI 9.4.0, 둘 다 가져오기 맵에 고정되어 있습니다. 2026-10-01에는 9.4.0이 Material UI 문서에 표시된 버전이었고 npm에는 최신 버전이었습니다. 고정하면 새 릴리스가 나올 때 페이지가 변경되지 않습니다.

Roboto 글꼴이 필요합니까?

머티리얼 UI는 기본적으로 Roboto를 사용합니다. 기본 테마에는 Roboto, Helvetica, Arial 및 sans-serif가 나열되므로 글꼴 링크가 없어도 페이지는 계속 작동하며 장치에 설치된 다음 글꼴로 대체됩니다.

실제 제품에 CDN 접근 방식이 적합합니까?

머티리얼 UI 문서에서는 사용하는 구성 요소에 관계없이 브라우저가 전체 라이브러리를 다운로드하기 때문에 제작이 아닌 신속한 프로토타이핑을 위해 권장합니다. 제품의 경우 npm을 사용하여 패키지를 설치하고 앱을 빌드합니다.