고급 프론트엔드 확장 만들기

고급 프론트엔드 확장 만들기 (Create an advanced frontend extension)

React 기반의 고급 프론트엔드를 가진 Docker 확장을 설정하는 방법을 설명하는 튜토리얼이에요.

출처: 문서

본문

확장을 만들기 시작하려면 먼저 확장의 소스 코드부터 필수 확장 전용 파일까지 담긴 디렉터리가 필요해요. 이 페이지는 더 고급 프론트엔드를 가진 확장을 설정하는 방법을 알려줘요.

시작하기 전에 최신 버전의 Docker Desktop을 설치했는지 확인하세요.

확장 폴더 구조 (Extension folder structure)

새 확장을 만드는 가장 빠른 방법은 Quickstart에서처럼 docker extension init my-extension을 실행하는 거예요. 이렇게 하면 완전히 동작하는 확장을 포함한 새 디렉터리 my-extension이 생성돼요.

팁: docker extension init은 React 기반 확장을 생성해요. 하지만 이것을 시작점으로 사용하고 Vue, Angular, Svelte 같은 다른 프론트엔드 프레임워크를 사용하거나 vanilla JavaScript를 사용해도 돼요.

빈 디렉터리나 react-extension 샘플 폴더에서 시작할 수도 있지만, docker extension init 명령에서 시작해 필요에 맞게 변경하는 것을 강력히 권장해요.

.
├── Dockerfile # (1)
├── ui # (2)
│   ├── public # (3)
│   │   └── index.html
│   ├── src # (4)
│   │   ├── App.tsx
│   │   ├── index.tsx
│   ├── package.json
│   └── package-lock.lock
│   ├── tsconfig.json
├── docker.svg # (5)
└── metadata.json # (6)
  1. 확장을 빌드하고 Docker Desktop에서 실행하는 데 필요한 모든 것을 포함해요.
  2. 프론트엔드 앱 소스 코드를 포함하는 상위 폴더예요.
  3. 컴파일되거나 동적으로 생성되지 않는 자산이 여기에 저장돼요. 로고나 robots.txt 파일 같은 정적 자산이 될 수 있어요.
  4. src 또는 소스 폴더는 모든 React 컴포넌트, 외부 CSS 파일, 그리고 컴포넌트 파일로 가져오는 동적 자산을 포함해요.
  5. Docker Desktop Dashboard의 왼쪽 메뉴에 표시되는 아이콘이에요.
  6. 이름, 설명, 버전 같은 확장에 대한 정보를 제공하는 파일이에요.

Dockerfile 조정하기 (Adapting the Dockerfile)

참고: docker extension init을 사용하면 React 확장에 필요한 것을 이미 포함한 Dockerfile을 생성해요.

확장이 만들어지면 Dockerfile을 구성해 확장을 빌드하고, Marketplace에서 확장 카드를 채우는 데 사용되는 라벨을 구성해야 해요. React 확장용 Dockerfile 예시:

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM node:18.9-alpine3.15 AS client-builder
WORKDIR /ui
# cache packages in layer
COPY ui/package.json /ui/package.json
COPY ui/package-lock.json /ui/package-lock.json
RUN --mount=type=cache,target=/usr/src/app/.npm \
    npm set cache /usr/src/app/.npm && \
    npm ci
# install
COPY ui /ui
RUN npm run build

FROM alpine
LABEL org.opencontainers.image.title="My extension" \
    org.opencontainers.image.description="Your Desktop Extension Description" \
    org.opencontainers.image.vendor="Awesome Inc." \
    com.docker.desktop.extension.api.version="0.3.3" \
    com.docker.desktop.extension.icon="https://www.docker.com/wp-content/uploads/2022/03/Moby-logo.png" \
    com.docker.extension.screenshots="" \
    com.docker.extension.detailed-description="" \
    com.docker.extension.publisher-url="" \
    com.docker.extension.additional-urls="" \
    com.docker.extension.changelog=""

COPY metadata.json .
COPY docker.svg .
COPY --from=client-builder /ui/build ui

참고: 예시 Dockerfile에서 이미지 라벨 com.docker.desktop.extension.icon이 아이콘 URL로 설정된 것을 볼 수 있어요. Extensions Marketplace는 확장을 설치하지 않고도 이 아이콘을 표시해요. 또한 Dockerfile은 COPY docker.svg .로 이미지 안에 아이콘 파일을 복사해요. 이 두 번째 아이콘 파일은 확장이 설치된 후 Dashboard에서 확장 UI를 표시하는 데 사용돼요.

중요: 아직 Vue용 작업 Dockerfile은 없어요. 양식을 작성해 Vue용 Dockerfile을 원한다고 알려주세요. 중요: 아직 Angular용 작업 Dockerfile은 없어요. 양식을 작성해 Angular용 Dockerfile을 원한다고 알려주세요. 중요: 아직 Svelte용 작업 Dockerfile은 없어요. 양식을 작성해 Svelte용 Dockerfile을 원한다고 알려주세요.

메타데이터 파일 구성하기 (Configure the metadata file)

확장을 위한 Docker Desktop의 탭을 추가하려면 확장 디렉터리 루트의 metadata.json 파일에서 구성해야 해요.

{
  "icon": "docker.svg",
  "ui": {
    "dashboard-tab": {
      "title": "UI Extension",
      "root": "/ui",
      "src": "index.html"
    }
  }
}
  • title 속성은 Docker Desktop Dashboard의 왼쪽 메뉴에 표시되는 확장 이름이에요.
  • root 속성은 시스템이 호스트에 배포할 때 사용하는 확장의 컨테이너 파일 시스템에서 프론트엔드 애플리케이션의 경로예요.
  • src 속성은 root 폴더 안에서 프론트엔드 애플리케이션의 HTML 진입점 경로예요.

metadata.json의 ui 섹션에 대한 자세한 내용은 Metadata를 참조하세요.

확장 빌드하고 설치하기 (Build the extension and install it)

확장을 구성했으니 Docker Desktop이 설치에 사용할 확장 이미지를 빌드해야 해요.

docker build --tag=awesome-inc/my-extension:latest .

awesome-inc/my-extension:latest 태그의 이미지가 빌드됐어요. docker inspect awesome-inc/my-extension:latest를 실행해 자세한 내용을 볼 수 있어요.

마지막으로 확장을 설치하고 Docker Desktop Dashboard에 나타나는 것을 확인할 수 있어요.

docker extension install awesome-inc/my-extension:latest

Extension APIs 클라이언트 사용하기 (Use the Extension APIs client)

Extension APIs를 사용하고 Docker Desktop으로 작업을 수행하려면 확장이 먼저 @docker/extension-api-client 라이브러리를 가져와야 해요. 설치하려면 아래 명령을 실행하세요:

npm install @docker/extension-api-client

그런 다음 createDockerDesktopClient 함수를 호출해 확장 API를 호출할 클라이언트 객체를 만들어요.

import { createDockerDesktopClient } from '@docker/extension-api-client';

const ddClient = createDockerDesktopClient();

TypeScript를 사용할 때는 @docker/extension-api-client-types를 dev dependency로 설치할 수도 있어요. 그러면 확장 API에 대한 타입 정의와 IDE 자동 완성을 제공해요.

npm install @docker/extension-api-client-types --save-dev

예를 들어 docker.cli.exec 함수를 사용해 docker ps --all 명령으로 모든 컨테이너 목록을 얻고 결과를 표로 표시할 수 있어요.

ui/src/App.tsx 파일을 다음 코드로 교체하세요:

// ui/src/App.tsx
import React, { useEffect } from 'react';
import {
  Paper,
  Stack,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Typography
} from "@mui/material";
import { createDockerDesktopClient } from "@docker/extension-api-client";

//obtain docker desktop extension client
const ddClient = createDockerDesktopClient();

export function App() {
  const [containers, setContainers] = React.useState<any[]>([]);

  useEffect(() => {
    // List all containers
    ddClient.docker.cli.exec('ps', ['--all', '--format', '"{{json .}}"']).then((result) => {
      // result.parseJsonLines() parses the output of the command into an array of objects
      setContainers(result.parseJsonLines());
    });
  }, []);

  return (
    <Stack>
      <Typography data-testid="heading" variant="h3" role="title">
        Container list
      </Typography>
      <Typography
      data-testid="subheading"
      variant="body1"
      color="text.secondary"
      sx={{ mt: 2 }}
    >
      Simple list of containers using Docker Extensions SDK.
      </Typography>
      <TableContainer sx={{mt:2}}>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell>Container id</TableCell>
              <TableCell>Image</TableCell>
              <TableCell>Command</TableCell>
              <TableCell>Created</TableCell>
              <TableCell>Status</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {containers.map((container) => (
              <TableRow
                key={container.ID}
                sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
              >
                <TableCell>{container.ID}</TableCell>
                <TableCell>{container.Image}</TableCell>
                <TableCell>{container.Command}</TableCell>
                <TableCell>{container.CreatedAt}</TableCell>
                <TableCell>{container.Status}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>
    </Stack>
  );
}

중요: 아직 Vue용 예시는 없어요. 양식을 작성해 Vue 샘플을 원한다고 알려주세요. 중요: 아직 Angular용 예시는 없어요. 양식을 작성해 Angular 샘플을 원한다고 알려주세요. 중요: 아직 Svelte용 예시는 없어요. 양식을 작성해 Svelte 샘플을 원한다고 알려주세요.

프론트엔드 코드에 적용되는 정책 (Policies enforced for the front-end code)

확장 UI 코드는 별도의 electron 세션에서 렌더링되며, node.js 환경이 초기화되어 있지 않고 electron API에 직접 접근할 수 없어요. 이는 전체 Docker Dashboard에 예기치 않은 부작용이 발생할 가능성을 제한하기 위한 것이에요.

확장 UI 코드는 확장 프레임워크와 함께 제공되는 SDK API를 사용하는 것 외에는 시스템 변경 같은 권한 있는 작업이나 하위 프로세스 생성 같은 작업을 수행할 수 없어요. 확장 UI 코드는 Docker Desktop과의 상호작용, 예를 들어 Dashboard의 여러 곳으로 이동하는 것도 확장 SDK API를 통해서만 할 수 있어요.

확장 UI 부분은 서로 격리되어 있고, 확장 UI 코드는 각 확장에 대해 자체 세션으로 실행돼요. 확장은 다른 확장의 세션 데이터에 접근할 수 없어요.

localStorage는 브라우저 웹 저장소의 메커니즘 중 하나예요. 사용자가 브라우저에서 나중에 사용하기 위해 데이터를 키-값 쌍으로 저장할 수 있게 해줘요. localStorage는 브라우저(확장 창)가 닫혀도 데이터를 지우지 않아요. 이 때문에 확장에서 Docker Desktop의 다른 부분으로 이동할 때 데이터를 유지하는 데 이상적이에요.

확장이 데이터를 저장하기 위해 localStorage를 사용한다면, Docker Desktop에서 실행되는 다른 확장은 여러분의 확장 로컬 저장소에 접근할 수 없어요. 확장의 로컬 저장소는 Docker Desktop이 중지되거나 재시작된 후에도 유지돼요. 확장이 업그레이드되면 로컬 저장소가 유지되지만, 제거되면 완전히 삭제돼요.

확장 다시 빌드하고 업데이트하기 (Re-build the extension and update it)

확장 코드를 수정했으므로 확장을 다시 빌드해야 해요.

$ docker build --tag=awesome-inc/my-extension:latest .

빌드된 후 업데이트해야 해요.

$ docker extension update awesome-inc/my-extension:latest

이제 Docker Desktop Dashboard의 containers 탭에서 백엔드 서비스가 실행되는 것을 볼 수 있고, 디버그가 필요할 때 로그를 확인할 수 있어요.

팁: 변경할 때마다 확장을 다시 빌드하지 않도록 핫 리로딩을 켤 수 있어요.

다음은 무엇인가요?

더 알아보기 (Learn more)