개요
Docker는 컨테이너에서 애플리케이션을 개발, 출시, 실행하는 데 사용하는 개방형 플랫폼입니다. Docker는 코드를 더 빠르게 빌드, 테스트, 배포하고 코드 개발과 실행 사이의 주기를 단축하는 데 도움이 됩니다. 이는 Docker가 커널 컨테이너화 기능을 애플리케이션 관리 및 배포를 지원하는 워크플로 및 도구와 결합하기 때문입니다.
Docker를 사용하면 Dockerfile이라는 스크립트를 사용하여 애플리케이션 빌드 프로세스를 표현할 수 있습니다. Dockerfile은 복잡성을 대가로 유연성을 제공하는 하위 수준 접근방식을 제공합니다. Dockerfile은 소스 코드를 컨테이너 이미지로 전환하는 방법을 자세히 설명하는 매니페스트입니다.
Docker 컨테이너는 Cloud Run 및 Kubernetes에서 직접 사용할 수 있으므로 이러한 플랫폼에서 쉽게 실행할 수 있습니다. Docker의 핵심 내용을 학습하면 컨테이너화된 애플리케이션 개발을 시작하는 데 필요한 기술을 갖출 수 있습니다.
목표
이 실습에서는 다음을 수행하는 방법에 대해 알아봅니다.
- Docker 컨테이너를 빌드, 실행, 디버그합니다.
- Docker 이미지를 Google Cloud의 컨테이너 이미지 저장소인 Artifact Registry로 푸시합니다.
- Artifact Registry에서 Docker 이미지를 가져옵니다.
설정
각 실습에서는 정해진 기간 동안 새 Google Cloud 프로젝트와 리소스 집합이 무료로 제공됩니다.
-
시크릿 창을 사용하여 Qwiklabs에 로그인합니다.
-
실습 사용 가능 시간(예: 1:15:00)을 참고하여 해당 시간 내에 완료합니다.
일시중지 기능은 없습니다. 필요한 경우 다시 시작할 수 있지만 처음부터 시작해야 합니다.
-
준비가 되면 실습 시작을 클릭합니다.
-
실습 사용자 인증 정보(사용자 이름 및 비밀번호)를 기록해 두세요. Google Cloud Console에 로그인합니다.
-
Google Console 열기를 클릭합니다.
-
다른 계정 사용을 클릭한 다음, 안내 메시지에 이 실습에 대한 사용자 인증 정보를 복사하여 붙여넣습니다.
다른 사용자 인증 정보를 사용하는 경우 오류가 발생하거나 요금이 부과됩니다.
-
약관에 동의하고 리소스 복구 페이지를 건너뜁니다.
Google Cloud Shell 활성화하기
Google Cloud Shell은 다양한 개발 도구가 탑재된 가상 머신으로, 5GB의 영구 홈 디렉터리를 제공하며 Google Cloud에서 실행됩니다.
Google Cloud Shell을 사용하면 명령줄을 통해 Google Cloud 리소스에 액세스할 수 있습니다.
-
Cloud 콘솔의 오른쪽 상단 툴바에서 'Cloud Shell 열기' 버튼을 클릭합니다.

-
계속을 클릭합니다.
환경을 프로비저닝하고 연결하는 데 몇 분 정도 소요됩니다. 연결되면 사용자가 미리 인증되어 프로젝트가 PROJECT_ID로 설정됩니다. 예:

gcloud는 Google Cloud의 명령줄 도구입니다. Cloud Shell에 사전 설치되어 있으며 명령줄 자동 완성을 지원합니다.
- 다음 명령어를 사용하여 사용 중인 계정 이름을 나열할 수 있습니다.
gcloud auth list
출력:
Credentialed accounts:
- @.com (active)
출력 예시:
Credentialed accounts:
- google1623327_student@qwiklabs.net
- 다음 명령어를 사용하여 프로젝트 ID를 나열할 수 있습니다.
gcloud config list project
출력:
[core]
project =
출력 예시:
[core]
project = qwiklabs-gcp-44776a13dea667a6
참고:
gcloud 전체 문서는 gcloud CLI 개요 가이드를 참조하세요.
작업 1. 환경 설정
이 실습에서는 실습을 위해 사전 프로비저닝된 별도의 VM에서 셸 명령어를 실행합니다.
-
VM에서 SSH 세션을 시작하려면 Cloud Shell에서 다음 명령어를 실행합니다.
gcloud compute ssh lab-vm --zone={{{project_0.default_zone| Zone}}}
-
메시지가 표시되면 Y를 입력하여 계속 진행합니다.
-
암호의 경우 Enter를 입력하여 암호를 사용하지 않습니다.
-
Enter를 다시 입력합니다.
-
Docker에서 사용하는 소켓에 대한 권한을 학생 사용자에게 부여합니다.
sudo chmod 666 /var/run/docker.sock
-
프로젝트 ID와 리전 환경 변수를 설정하려면 다음 명령어를 실행합니다.
PROJECT_ID={{{project_0.project_id| Project}}}
REGION={{{project_0.default_region| Region}}}
아래 작업의 모든 실습 명령어를 Cloud Shell의 SSH 세션에서 실행해야 합니다.
작업 2. 컨테이너 이미지 빌드
이 작업에서는 Dockerfile을 사용하여 Docker 컨테이너 이미지를 만듭니다.
Dockerfile 만들기
-
테스트 디렉터리를 만들고 해당 디렉터리로 변경합니다.
mkdir test && cd test
-
Dockerfile을 만듭니다.
cat > Dockerfile <<EOF
# Use an official Node runtime as the parent image
FROM node:lts
# Set the working directory in the container to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Make the container's port 80 available to the outside world
EXPOSE 80
# Run app.js using node when the container launches
CMD ["node", "app.js"]
EOF
참고: 이 단계에서는 먼저 상위 또는 기본 이미지를 선택하여 컨테이너 이미지를 어셈블합니다.이는 FROM 명령어와 Node 버전 LTS(장기적 지원)의 공식 Docker 이미지로 지정됩니다.
WORKDIR 명령어는 Dockerfile에서 다음에 나오는 추가 명령어에 대한 컨테이너의 작업 디렉터리를 설정합니다. 이 실습에서는 /app 디렉터리를 컨테이너의 작업 디렉터리로 사용합니다.
COPY 명령어는 소스 위치의 디렉터리 또는 파일을 컨테이너 이미지 파일 시스템의 대상 경로로 복사합니다. 여기서는 현재 디렉터리의 파일을 /app으로 복사합니다.
EXPOSE 명령어는 컨테이너의 포트를 노출하여 해당 포트(이 실습에서는 포트 80)에서 연결을 수락하도록 합니다.
마지막으로 CMD 명령어는 실행 중인 컨테이너에서 애플리케이션을 실행하기 위한 노드 명령어를 제공합니다.
애플리케이션 개발
다음으로 Node.js 애플리케이션의 코드를 작성합니다. 이 애플리케이션은 포트 80에서 요청을 수신 대기하고 정적 메시지로 응답하는 간단한 HTTP 서버입니다.
-
애플리케이션 소스 코드를 사용하여 app.js 파일을 만듭니다.
cat > app.js <<EOF
const http = require('http');
const hostname = '0.0.0.0';
const port = 80;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Welcome to your first Docker container!\n');
});
server.listen(port, hostname, () => {
console.log('Server running at http://%s:%s/', hostname, port);
});
process.on('SIGINT', function() {
console.log('Caught interrupt signal and will exit');
process.exit();
});
EOF
참고: 애플리케이션은 컨테이너가 중지될 때 메시지를 기록하고 정상적으로 종료하기 위해 SIGINT 핸들러 함수도 구현합니다.
-
애플리케이션 소스 파일의 콘텐츠를 확인합니다.
cat app.js
컨테이너 이미지 빌드
이 하위 작업에서는 docker build 명령어를 사용하여 Dockerfile에서 컨테이너 이미지를 빌드합니다.
-
docker build 명령어를 실행합니다.
docker build -t my-app:0.1 .
명령어 출력의 일부는 다음과 같습니다.
=> [internal] load dockerignore
=> => transferring context: 2B
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 394B
=> [internal] load metadata for docker.io/library/node:lts
=> [1/3] FROM docker.io/library/node:lts@sha256:586cdef48f920dea2f47a954b8717601933aa1daa0a08264abf9144789abf8ae
=> => resolve docker.io/library/node:lts@sha256:586cdef48f920dea2f47a954b8717601933aa1daa0a08264abf9144789abf8ae
=> => sha256:b7483c70b94e9fbb68e91d64456ee147d120488f876d69efeae815ba164e8b54 2.21kB / 2.21kB
...
...
=> [internal] load build context
=> => transferring context: 912B
=> [2/3] WORKDIR /app
=> [3/3] COPY . /app
=> exporting to image
=> => exporting layers
=> => writing image sha256:8cf51a1aba351cf505cd6d8eefa966b
=> => naming to docker.io/library/my-app:0.1
참고: 이 명령어는 현재 디렉터리의 Dockerfile에 있는 명령어에 따라 컨테이너 이미지를 빌드하고 결과 이미지에 지정된 이름과 버전으로 태그를 지정합니다. Google은 최신 이미지 버전과 이전 이미지 버전을 구분하기 위해 태그를 지정할 것을 권장합니다.
-
빌드된 이미지 목록을 봅니다.
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-app 0.1 8cf51a1aba35 10 minutes ago 997MB
참고: 이 명령어는 기본 node 이미지와 빌드한 my-app 이미지를 나열합니다. 이미지의 크기는 다른 가상 머신 이미지에 비해 상대적으로 작습니다.
목표를 확인하려면 내 진행 상황 확인하기를 클릭합니다.
컨테이너 이미지 빌드
작업 3. 애플리케이션 실행 및 테스트
컨테이너 이미지가 성공적으로 빌드된 후 애플리케이션을 실행하고 테스트하여 예상대로 작동하는지 확인할 수 있습니다.
컨테이너 실행
-
컨테이너를 실행하려면 다음 명령어를 실행합니다.
docker run -p 8080:80 --name my-app -d my-app:0.1
참고: docker run 명령어는 지정된 이름으로 컨테이너를 시작합니다. -p 인수는 호스트의 포트 8080을 컨테이너의 포트 80에 매핑하여 요청이 http://localhost:8080의 서버에 도달할 수 있도록 합니다. -d 인수는 백그라운드에서 컨테이너를 실행합니다.
-
실행 중인 컨테이너 목록을 보려면 다음 명령어를 실행합니다.
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
46c209a1fd87 my-app:0.1 "docker-entrypoint.s…" 19 seconds ago Up 17 seconds 0.0.0.0:8080->80/tcp my-app
애플리케이션 테스트
-
애플리케이션을 테스트하려면 curl 명령어를 사용하여 HTTP 요청을 보냅니다.
curl http://localhost:8080
-
애플리케이션 응답을 확인합니다.
Welcome to your first Docker container!
-
docker stop 명령어를 실행하여 컨테이너를 중지합니다.
docker stop [CONTAINER ID]
[CONTAINER ID]를 이전 하위 작업에서 실행된 명령어의 출력에 있는 CONTAINER ID 값으로 바꿉니다.
작업 4. 컨테이너 수정
이미지에서 공통 레이어를 공유하는 컨테이너화된 애플리케이션의 여러 버전을 가질 수 있습니다. 이 작업에서는 컨테이너화된 애플리케이션의 다른 버전을 만들고 두 버전을 모두 테스트합니다.
애플리케이션 코드 수정
-
test 디렉터리에서 app.js 파일의 콘텐츠를 업데이트합니다.
cat > app.js <<EOF
const http = require('http');
const url = require('url');
const hostname = '0.0.0.0';
const port = 80;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
console.log('Received request with URL: ?%s', req.url);
var q = url.parse(req.url, true).query;
res.end(q.user + ', Welcome to your first Docker container!\n');
});
server.listen(port, hostname, () => {
console.log('Server running at http://%s:%s/', hostname, port);
});
process.on('SIGINT', function() {
console.log('Caught interrupt signal and will exit');
process.exit();
});
EOF
참고: 수정된 애플리케이션 코드는 애플리케이션 응답에서 HTTP 요청으로 전달된 user 쿼리 파라미터의 값을 추가합니다.
-
cat 명령어를 사용하여 수정된 애플리케이션 소스 파일의 콘텐츠를 확인합니다.
cat app.js
컨테이너 이미지 다시 빌드
-
새 태그로 이미지를 다시 빌드합니다.
docker build -t my-app:0.2 .
-
docker build 명령어의 출력을 확인합니다.
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 394B
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [internal] load metadata for docker.io/library/node:lts
=> [1/3] FROM docker.io/library/node:lts@sha256:586cdef48f920dea2f47a954b8717601933aa1daa0a08264abf9144789abf8ae
=> [internal] load build context
=> => transferring context: 691B
=> CACHED [2/3] WORKDIR /app
=> [3/3] COPY . /app
=> exporting to image
=> => exporting layers
=> => writing image sha256:5fc2d7a43c4678da17daf204ef4b071f2da869ead758864622d90d880a40c24b
=> => naming to docker.io/library/my-app:0.2
출력에서 볼 수 있듯이 새 컨테이너 이미지에서는 app.js 소스 파일이 변경되었기 때문에 3단계 이하의 레이어만 수정됩니다.
새 컨테이너 실행
-
새 컨테이너를 실행하려면 다음 명령어를 실행합니다.
docker run -p 8080:80 --name my-app-2 -d my-app:0.2
-
실행 중인 컨테이너 목록을 보려면 다음 명령어를 실행합니다.
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7071749fe0f6 my-app:0.2 "docker-entrypoint.s…" 10 seconds ago Up 10 seconds 0.0.0.0:8081->80/tcp my-app-2
애플리케이션 테스트
-
애플리케이션을 테스트하려면 curl을 사용하여 HTTP 요청을 보냅니다.
curl http://localhost:8080?user=Learner
-
애플리케이션 응답을 확인합니다.
Learner, Welcome to your first Docker container!
참고: 응답에는 애플리케이션의 이미지 버전 0.2를 실행 중인 컨테이너에 대한 요청에 전달된 user 인수의 값이 포함되어 있습니다.
내 진행 상황 확인하기를 클릭하여 목표를 확인합니다.
컨테이너 수정 및 컨테이너 이미지 다시 빌드
작업 5. 문제 해결
컨테이너화된 애플리케이션의 문제를 해결하는 간단한 기술이 몇 가지 있습니다. 이 작업에서는 이러한 방법 중 일부를 검토합니다.
컨테이너 로그 보기
-
먼저 로그를 보려는 컨테이너의 ID를 가져옵니다.
docker ps
-
컨테이너 로그를 보려면 docker logs 명령어를 실행합니다.
docker logs [CONTAINER ID]
CONTAINER ID를 이전 명령어의 출력에서 컨테이너 ID를 고유하게 식별하는 컨테이너 ID의 첫 번째 문자로 바꿉니다. 컨테이너가 실행 중일 때 로그 출력을 확인하려면 logs 명령어와 함께 -f 옵션을 사용합니다.
Server running at http://0.0.0.0:80/
Received request with URL: ?/?user=Learner
컨테이너 내부에서 셸을 시작합니다.
실행 중인 컨테이너 내에서 대화형 셸을 시작하여 문제를 해결할 수 있습니다.
-
대화형 Bash 세션을 시작하려면 다음을 실행합니다.
docker exec -it [CONTAINER ID] bash
root@7071749fe0f6:/app#
-
이 셸 내에서 문제를 해결하기 위해 컨테이너 파일 시스템과 애플리케이션이 사용할 수 있는 다른 데이터 파일을 검사할 수 있습니다.
ls
-
Bash 셸을 종료합니다.
exit
컨테이너 메타데이터 검사
-
컨테이너의 메타데이터를 보려면 다음을 실행합니다.
docker inspect [CONTAINER ID]
다음은 명령어의 부분적인 출력입니다.
[
{
"Id": "7071749fe0f66b8b1953bbb6f28f159bd5dbeae079595675e2591d32d87ae5dc",
"Created": "2023-02-23T18:08:34.286519913Z",
"Path": "docker-entrypoint.sh",
"Args": [
"node",
"app.js"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 1099,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-02-23T18:08:34.785098365Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:5fc2d7a43c4678da17daf204ef4b071f2da869ead758864622d90d880a40c24b",
"ResolvConfPath": "/var/lib/docker/containers/7071749fe0f66b8b1953bbb6f28f159bd5dbeae079595675e2591d32d87ae5dc/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/
...
...
}
]
참고: 기본적으로 inspect 명령어는 JSON 배열로 상세한 메타데이터 정보를 제공합니다. --format 인수로 결과를 필터링하여 출력에서 특정 필드를 검사할 수 있습니다.
작업 6. 컨테이너 이미지 게시
Artifact Registry는 컨테이너 이미지와 소프트웨어 패키지를 포함한 소프트웨어 아티팩트를 비공개 저장소에 저장하고 관리하는 데 사용되는 Google Cloud 서비스입니다.
이 작업에서는 컨테이너 이미지를 Artifact Registry로 푸시하여 스테이징, 프로덕션 등 소프트웨어 배포 수명 주기를 구성하는 다른 환경에 배포할 수 있도록 합니다.
이미지 저장소 만들기
컨테이너 이미지를 Artifact Registry로 내보내려면 먼저 저장소를 만들어야 합니다.
-
Google Cloud 콘솔의 탐색 메뉴(
)에서 모든 제품 보기를 클릭합니다. 그런 다음 CI/CD 카테고리에서 Artifact Registry > 저장소로 이동합니다.
-
저장소 만들기 페이지에서 다음 정보를 제공하고 나머지 설정은 기본값으로 둡니다.
|
속성
|
값 (입력 또는 선택)
|
|
이름
|
my-repo
|
|
형식
|
Docker
|
|
위치 유형
|
리전
|
|
리전
|
|
-
만들기를 클릭합니다.
저장소를 사용하기 위해 Docker 인증
저장소에서 이미지를 내보내거나 가져오려면 먼저 Artifact Registry에서 저장소에 대한 요청을 인증하도록 Docker를 구성해야 합니다.
-
리전의 Docker 저장소에 인증을 설정하려면 VM 셸에서 다음 명령어를 실행합니다.
gcloud auth configure-docker ${REGION}-docker.pkg.dev
-
메시지가 표시되면 Y를 입력합니다.
생성한 저장소의 전체 이름은 -docker.pkg.dev//my-repo입니다.
Docker 이미지 저장소 이름은 Artifact Registry에서 [location]-docker.pkg.dev 형식을 사용합니다.
컨테이너를 Artifact Registry로 푸시하기
-
Artifact Registry에서 호스팅하는 비공개 레지스트리에 컨테이너 이미지를 푸시하려면 먼저 레지스트리 이름으로 이미지에 태그를 지정해야 합니다.
docker build -t ${REGION}-docker.pkg.dev/${PROJECT_ID}/my-repo/my-app:0.2 .
-
빌드한 Docker 이미지를 나열합니다.
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-app 0.2 9b1ef4854d32 4 minutes ago 997MB
us-east1-docker.pkg.dev/qwiklabs-gcp-02-7c092125ce3a/my-repo/my-app 0.2 9b1ef4854d32 4 minutes ago 997MB
my-app 0.1 8cf51a1aba35 5 minutes ago 997MB
-
이미지를 Artifact Registry에 푸시하려면 다음 명령어를 실행합니다.
docker push ${REGION}-docker.pkg.dev/$PROJECT_ID/my-repo/my-app:0.2
이 명령어의 출력은 다음과 비슷합니다.
The push refers to repository [east1-docker.pkg.dev/qwiklabs-gcp-02-7c092125ce3a/my-repo/my-app]
b29bce04ddbb: Pushed
9ba0e19073ee: Pushed
3c397285cb7e: Pushed
a8d01c684adc: Pushed
56c4ec92f013: Pushed
4c92897e605e: Pushed
0b6859e9fff1: Pushed
11829b3be9c0: Pushed
dc8e1d8b53e9: Pushed
9d49e0bc68a4: Pushed
8e396a1aad50: Pushed
0.2: digest: sha256:383ffb5213f92e33dedb49042c0f070a9f76f263621226de20499dffd863b3df size: 2628
-
명령어가 완료되면 Google Cloud 콘솔의 탐색 메뉴(
)에서 모든 제품 보기를 클릭합니다. 그런 다음 CI/CD 카테고리에서 Artifact Registry > 저장소로 이동합니다.
-
my-repo 저장소를 클릭하여 my-app Docker 컨테이너 이미지를 표시합니다.
내 진행 상황 확인하기를 클릭하여 목표를 확인합니다.
컨테이너 이미지 게시
작업 7. 컨테이너 이미지 가져오기 및 테스트
이 작업에서는 새로운 환경에서 시작한 다음 Artifact Registry에서 컨테이너 이미지를 가져와 테스트합니다. 다른 환경을 시뮬레이션하기 위해 이 실습의 이전 작업에서 만든 모든 컨테이너와 이미지를 셸 환경에서 중지하고 삭제합니다.
컨테이너 중지 및 삭제
-
실행 중인 모든 컨테이너를 중지하려면 다음 명령어를 실행합니다.
docker stop $(docker ps -q)
-
모든 컨테이너를 삭제하려면 다음 명령어를 실행합니다.
docker rm $(docker ps -aq)
모든 컨테이너 이미지 삭제
-
레지스트리 태그가 지정된 컨테이너 이미지를 삭제하려면 다음 명령어를 실행합니다.
docker rmi ${REGION}-docker.pkg.dev/$PROJECT_ID/my-repo/my-app:0.2
참고: 이 명령어는 레지스트리에서 컨테이너 이미지를 삭제하지 않습니다.
-
다른 이미지를 모두 삭제하려면 다음 명령어를 실행합니다.
docker rmi -f $(docker images -aq)
-
VM 환경에 컨테이너 이미지가 없는지 확인합니다.
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
이제 로컬 이미지가 없는 새로운 호스트 환경이 만들어졌습니다.
이미지 테스트하기
Artifact Registry에서 이미지를 가져와 테스트합니다.
-
Artifact Registry에서 이미지를 가져오려면 다음 명령어를 실행합니다.
docker pull ${REGION}-docker.pkg.dev/${PROJECT_ID}/my-repo/my-app:0.2
-
이미지를 나열하려면 다음 명령어를 실행합니다.
docker images
-
컨테이너를 실행하려면 다음 명령어를 실행합니다.
docker run -p 8080:80 -d ${REGION}-docker.pkg.dev/${PROJECT_ID}/my-repo/my-app:0.2
-
애플리케이션을 테스트하려면 다음을 실행합니다.
curl http://localhost:8080?user=Learner
참고: 이 작업은 호스트 머신에 애플리케이션 종속 항목을 설치할 필요 없이 Docker를 사용하여 다른 VM 또는 환경에서 컨테이너화된 애플리케이션을 실행할 수 있는 컨테이너 이식성을 보여줍니다. 컨테이너 이미지는 Docker에서 액세스할 수 있는 공개 또는 비공개 레지스트리에서 호스팅할 수 있습니다.
수고하셨습니다
Docker로 컨테이너를 만드는 기본사항에 관한 이 실습을 완료하셨습니다. 이 실습에서 학습한 내용은 다음과 같습니다.
- Docker로 컨테이너 이미지를 빌드하고 Docker 컨테이너를 실행했습니다.
- Docker 이미지를 Google Cloud의 컨테이너 이미지 저장소인 Artifact Registry로 푸시했습니다.
- Artifact Registry에서 Docker 이미지를 가져와 새로운 환경에서 실행하여 컨테이너 이식성을 검증했습니다.
다음 단계/자세히 알아보기
Docker 및 Artifact Registry에 대한 자세한 내용은 다음 문서를 참조하세요.
Copyright 2026 Google LLC All rights reserved. Google 및 Google 로고는 Google LLC의 상표입니다. 기타 모든 회사명 및 제품명은 해당 업체의 상표일 수 있습니다.