SONUB 홈 KO English 한국어 日本語 中文 हिन्दी বাংলা اردو Español Português Français Deutsch Русский العربية Bahasa Indonesia Tiếng Việt ไทย Türkçe

알림을 켜는 방법

이 브라우저 또는 주소에서는 알림을 사용할 수 없습니다. 지원되는 브라우저로 보안 연결된 SONUB 사이트에 접속해 주세요.

알림이 차단되어 있으면 브라우저 설정에서 허용해 주세요.

  1. 주소 표시줄 옆의 사이트 설정 또는 브라우저의 사이트 설정을 열고, 이 사이트의 알림을 허용으로 바꾸세요.
  2. 기기 설정에서 브라우저 알림을 허용하고 방해 금지 또는 집중 모드를 확인하세요.
  3. SONUB으로 돌아와 페이지를 새로고침한 뒤 벨을 눌러 알림을 켜세요.

iPhone 또는 iPad에서는 SONUB을 홈 화면에 추가하고 거기에서 연 뒤 알림을 허용하세요.

바이브코딩 전체 보기
바이브코딩

웹서버

가 말을 걸어 주는 스크립트 20줄.

웹서버는 을 기다렸다가 을 보내는 일 뿐입니다. 그것을 증명하기 위해 bash 와 , 만으로 를 만듭니다 — 한 줄도 빠짐없이 읽을 수 있습니다. 그다음 실제 사이트를 까지 붙여 돌리는 Caddy 로 넘어갑니다.

Docker: 상자에 담은 Caddy 로 진짜 사이트를 인터넷에

는 프로그램과 그 프로그램이 도는 데 필요한 모든 것 — 라이브러리, 설정, 작은 운영체제까지 — 를 하나에 담고, 그 이미지를 컨테이너로 실행합니다. 컨테이너는 내 노트북에서도 빌린 서버에서도 똑같이 움직이므로 "제 컴퓨터에서는 되는데요"라는 오래된 핑계가 사라집니다.

Caddy 는 이미 파일 하나인데 굳이 왜 도커를 쓸까요? 진짜 서버에서는 곧 여러 가지가 함께 돌기 때문입니다 — 사이트, 앱, 데이터베이스. 도커는 그 하나하나에 자기 상자를 주고, 같은 몇 가지 명령으로 켜고 끄고 새로 바꾸고 지웁니다. 서버에 다른 것을 설치하지 않고 이웃도 건드리지 않습니다.

먼저 알아 둘 낱말 네 개

  1. 이미지프로그램과 그것에 필요한 모든 것을 얼려 둔 꾸러미입니다. 내려받거나(caddy:2) Dockerfile 이라는 조리법 파일로 직접 만듭니다. 실행하는 동안 바뀌지 않습니다.
  2. 컨테이너실행 중인 이미지입니다. 이미지 하나로 컨테이너를 여러 개 띄울 수 있고, 컨테이너를 지워도 이미지는 그대로입니다 — 다음에 켜면 새것으로 시작합니다.
  3. 포트 연결컨테이너에는 바깥에서 보이지 않는 자기만의 포트가 있습니다. -p 8080:80 은 내 컴퓨터의 8080 포트를 컨테이너 안의 80 포트에 이어서 브라우저가 닿을 수 있게 합니다.
  4. 볼륨컨테이너가 자기 안에 쓴 파일은 컨테이너를 지우면 함께 사라집니다. 볼륨이나 컨테이너에 연결한 내 폴더는 남는 저장소입니다 — 페이지, 설정, Caddy 의 인증서가 거기에 삽니다.

도커 설치

Mac 이나 Windows PC 에서는 docker.com 에서 Docker Desktop 을 설치하고 한 번 실행해 둡니다. 그러면 터미널에서 docker 명령이 됩니다. 리눅스 서버에서는 도커의 공식 스크립트가 한 줄로 모두 설치합니다. 다음으로 넘어가기 전에 확인 명령 두 개가 모두 답해야 합니다.

install
# macOS · Windows: install Docker Desktop from docker.com and start it once

# Ubuntu / Debian server: Docker's official install script
curl -fsSL https://get.docker.com | sh

# check that both commands answer
docker --version
docker compose version

명령 한 줄로 Caddy 띄우기

index.html 이 있는 폴더로 가서 아래 한 줄을 실행합니다. 처음에는 도커가 Caddy 이미지를 내려받고, 그다음부터는 1초쯤이면 켜집니다. http://:8080 을 열면 내 페이지가 보입니다.

terminal
# serve this folder at http://localhost:8080 (Control-C stops it)
docker run --rm -p 8080:80 -v "$PWD":/usr/share/caddy caddy:2
  1. docker run이미지로 컨테이너를 만들고 실행합니다.
  2. --rm멈추자마자 컨테이너를 지웁니다. 멈춘 컨테이너가 쌓이지 않게 하려는 것입니다. 이미지는 남으므로 다음 실행도 똑같이 빠릅니다.
  3. -p 8080:80내 컴퓨터의 8080 포트를 Caddy 가 기다리는 컨테이너 안의 80 포트에 잇습니다. 왼쪽 숫자는 마음대로 고르고, 오른쪽 숫자는 이미지가 정해 둔 값입니다.
  4. -v "$PWD":/usr/share/caddy지금 폴더($PWD)를 컨테이너 안의 /usr/share/caddy 로 보이게 합니다 — 공식 이미지가 기본으로 내놓는 폴더입니다. 여기서 파일을 고치면 다음 새로 고침에 바로 보입니다.
  5. caddy:2실행할 이미지 — 공식 Caddy 이미지의 2.x 버전입니다. :2 꼬리표를 빼면 도커는 latest 를 가져오는데, 어느 날 큰 버전이 바뀌어 발밑이 흔들릴 수 있습니다.

Control-C 로 멈춥니다. 잠깐 보기에는 그만이지만 명령이 길고, 터미널을 닫는 순간 잊힙니다. 오래 둘 것은 파일에 적어 둡니다 — 그것이 다음 프로젝트입니다.

프로젝트 1: HTML 홈페이지를 폴더에서 인터넷까지

프로젝트 전체가 작은 파일 세 개입니다. index.html 은 페이지, Caddyfile 은 Caddy 가 무엇을 내놓을지, 은 도커가 Caddy 를 어떻게 실행할지를 적습니다. Docker Compose 가 그 파일을 읽으므로 긴 docker run 명령을 다시 칠 일이 없습니다.

my-site
my-site/
├── compose.yaml
├── conf/
│   └── Caddyfile
└── site/
    └── index.html
compose.yaml
services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./conf:/etc/caddy
      - ./site:/srv
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:

compose.yaml 한 줄씩

  1. image: caddy:2실행할 이미지 — 앞에서와 같은 공식 Caddy 이미지입니다.
  2. restart: unless-stopped컨테이너가 죽거나 서버가 다시 켜지면, 일부러 멈추기 전까지 도커가 알아서 다시 띄웁니다.
  3. "80:80" · "443:443"기계의 80(http) · 443(https) 포트를 Caddy 에 엽니다. 인증서를 받고 http 손님을 https 로 넘기려면 둘 다 필요합니다.
  4. "443:443/udp"같은 443 포트를 UDP 로 한 번 더 엽니다. 브라우저가 가장 새로운 HTTP/3 를 쓸 수 있게 합니다. 빼도 해롭지 않고, 그러면 사이트는 HTTP/2 로 돕니다.
  5. ./conf:/etc/caddy내 conf 폴더가 Caddy 가 Caddyfile 을 찾는 /etc/caddy 가 됩니다. Caddy 문서가 파일 하나 대신 폴더를 연결하는 까닭은, 많은 편집기가 저장할 때 파일을 새것으로 갈아 끼워서 파일 하나만 연결해 두면 옛 내용이 계속 보이기 때문입니다.
  6. ./site:/srv내 site 폴더가 Caddyfile 이 내놓는 /srv 가 됩니다. 디스크에서 페이지를 고치면 다시 켜지 않아도 Caddy 가 바로 새 판을 내놓습니다.
  7. caddy_data:/data도커가 맡아 두는 이름 있는 볼륨입니다. Caddy 가 HTTPS 인증서와 키를 여기에 두므로 컨테이너를 지우고 다시 만들어도 살아남습니다. 옆의 caddy_config 도 Caddy 의 자동 설정을 같은 방식으로 지킵니다.

먼저 내 컴퓨터에서

Caddyfile 의 주소 자리에 :80 을 씁니다. "어떤 이름으로 물어 오든 80 포트에서 답하라"는 뜻으로, 인증서 없는 맨 http 입니다. 도메인이 없는 내 컴퓨터에는 이것이 딱 맞습니다. 그다음 켜고 http://localhost 를 엽니다.

conf/Caddyfile
:80 {
	root * /srv
	file_server
}
terminal
cd my-site
docker compose up -d      # start Caddy in the background
open http://localhost     # or type the address into a browser
docker compose logs -f    # watch requests arrive (Control-C stops watching)

인터넷에 올리기

  1. 서버 빌리기공인 가 있는 면 무엇이든 됩니다 — 작고 싼 것으로 충분합니다. 위의 스크립트로 도커를 설치하고, 방화벽이 80 · 443 포트를 들여보내는지 확인합니다.
  2. 도메인이 서버를 가리키게도메인을 산 곳에서 DNS 에 A 레코드를 더합니다 — 이름은 example.com, 값은 서버의 IP 주소입니다. 인터넷 전체가 바뀐 값을 보기까지 몇 분에서 몇 시간이 걸릴 수 있습니다.
  3. 진짜 주소 적기Caddyfile 의 :80 을 내 도메인으로 바꿉니다. 이 한 곳을 바꾸는 것이 자동 HTTPS 를 켭니다 — Caddy 가 인증서를 요청하고, 설치하고, 갱신하고, http 손님을 https 로 넘깁니다.
  4. 복사하고 켜기rsync 로 폴더를 서버에 복사하고, ssh 로 들어가 docker compose up -d 로 켭니다. 기록을 지켜보다가 인증서를 받았다는 줄이 나오면 사이트가 열린 것입니다.
conf/Caddyfile
example.com {
	root * /srv
	file_server
	encode gzip zstd
}

아래의 203.0.113.10 은 예시용으로 비워 둔 주소이니 내 서버의 IP 로 바꿉니다. 나중에 페이지를 고칠 때는 index.html 을 고치고 같은 rsync 를 다시 실행합니다. site 폴더가 컨테이너에 연결되어 있어서 아무것도 다시 켜지 않아도 다음 새로 고침에 새 페이지가 나옵니다.

terminal
# on your computer: copy the project folder to the server
rsync -av my-site/ root@203.0.113.10:~/my-site/

# on the server: start it — Caddy fetches the HTTPS certificate by itself
ssh root@203.0.113.10
cd ~/my-site
docker compose up -d
docker compose logs -f caddy

프로젝트 2: React 홈페이지

는 작게 나눠 다시 쓰는 부품(컴포넌트)으로 페이지를 만드는 라이브러리입니다. 에서 중요한 것은 하나입니다 — React 프로젝트는 그대로 내놓지 않습니다. 단계가 그것을 dist 폴더 안의 평범한 HTML · CSS · JavaScript 파일로 바꾸고, 거기서부터는 프로젝트 1 과 같은 일입니다.

아래 명령에는 내 컴퓨터에 가 있어야 합니다. Vite 가 바로 돌아가는 React 프로젝트를 만들고, run dev 가 작업하는 동안 화면을 보여 주고, npm run build 가 완성된 사이트를 씁니다.

terminal
# needs Node.js (nodejs.org) — create a React project with Vite
npm create vite@latest my-react -- --template react
cd my-react
npm install
npm run dev      # development server at http://localhost:5173

npm run build    # the finished site is written to dist/

방법 A: 내 컴퓨터에서 빌드하고 결과만 복사

가장 쉬운 길은 프로젝트 1 을 그대로 다시 쓰는 것입니다. 빌드한 뒤 dist 폴더만 서버의 site 폴더로 복사합니다. --delete 는 이번 빌드에 없는 지난 빌드의 파일을 지워서 찌꺼기가 쌓이지 않게 합니다.

terminal
# way A: build here, copy only the result into Project 1's site folder
npm run build
rsync -av --delete dist/ root@203.0.113.10:~/my-site/site/
React 가 Caddyfile 에 꼭 필요로 하는 한 줄

React 앱은 index.html 하나에서 모든 페이지를 스스로 그립니다. /about 같은 주소는 디스크의 파일이 아니라서, 도움 없이는 누군가 그 페이지를 새로 고치거나 공유된 링크로 들어오는 순간 Caddy 가 404 로 답합니다. try_files 는 Caddy 에게 "파일이 있으면 그것을, 없으면 index.html 을 주고 React 가 알맞은 페이지를 그리게 하라"고 말합니다.

Caddyfile
example.com {
	root * /srv
	encode gzip zstd
	try_files {path} /index.html
	file_server
}

방법 B: 두 단계 Dockerfile 로 도커가 빌드하게

더 튼튼한 길은 도커 안에서 빌드하는 것입니다. 그러면 서버든 동료의 컴퓨터든 늘 똑같이 빌드되고, 서버에 Node 를 설치할 사람도 없습니다. Dockerfile 은 두 단계입니다 — 첫 단계는 Node 로 빌드하고, 둘째 단계는 결과만 챙깁니다.

Dockerfile
# stage 1: build the React app with Node
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# stage 2: only the finished files go into the Caddy image
FROM caddy:2
COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /srv

Dockerfile 한 줄씩

  1. FROM node:24-alpine AS build첫 단계는 Node.js 가 든 이미지에서 시작하고, 다음 단계가 여기서 파일을 가져갈 수 있게 이 단계에 build 라는 이름을 붙입니다.
  2. COPY package*.json ./ · RUN npm ci패키지 목록만 먼저 복사하고 잠금 파일에 적힌 버전 그대로 설치합니다. 목록이 그대로인 동안 도커가 이 단계를 다시 쓰므로, 다음 빌드부터는 느린 설치를 건너뜁니다.
  3. COPY . . · RUN npm run build나머지 소스를 복사하고 빌드합니다. 완성된 사이트가 build 단계 안의 /app/dist 에 생깁니다.
  4. FROM caddy:2둘째 단계는 깨끗한 Caddy 이미지에서 다시 시작합니다. 복사하지 않은 것은 첫 단계에서 하나도 따라오지 않습니다.
  5. COPY --from=build /app/dist /srvbuild 단계에서 빌드된 파일만 가져와 Caddyfile 이 내놓는 자리에 둡니다. Node 와 node_modules 는 남겨 두므로 최종 이미지가 작습니다.

try_files 를 넣은 Caddyfile 을 Dockerfile 옆에 두고 아래 compose.yaml 을 씁니다. build: . 은 이미지를 내려받는 대신 "이 폴더의 Dockerfile 로 만들라"는 뜻입니다. 서버에서 코드를 clone 하면 명령 하나로 빌드하고 켜고, 고칠 때마다 pull 한 뒤 같은 명령을 다시 실행합니다.

compose.yaml
services:
  web:
    build: .
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:
terminal
# keep node_modules and old builds out of the image
printf 'node_modules\ndist\n' > .dockerignore

# on the server: get the code, then build and start in one step
git clone https://github.com/you/my-react.git
cd my-react
docker compose up -d --build

# after you push a change: pull it and rebuild
git pull && docker compose up -d --build

자주 쓰는 명령

명령하는 일
docker compose up -dcompose.yaml 에 적힌 것을 모두 뒤에서 켭니다. compose.yaml 을 고친 뒤 다시 실행하면 바뀐 것만 새로 만듭니다.
docker compose ps이 프로젝트의 컨테이너와 도는지 여부를 보여 줍니다. 사이트가 답하지 않을 때 가장 먼저 봅니다.
docker compose logs -f기록을 실시간으로 따라갑니다. 인증서 문제, 틀린 경로, 죽는 일이 모두 여기에 먼저 나옵니다. Control-C 는 서버가 아니라 지켜보기만 멈춥니다.
docker compose exec -w /etc/caddy caddy caddy reload도는 컨테이너 안에서 Caddy 가 연결 하나 끊지 않고 Caddyfile 을 다시 읽게 합니다. Caddyfile 을 고친 뒤에 씁니다.
docker compose down컨테이너를 멈추고 지웁니다. 이름 있는 은 남으므로 인증서도 살아남습니다. -v 를 붙이면 그것까지 지우니 피합니다.
docker compose pull더 새 Caddy 이미지를 내려받습니다. 이어서 docker compose up -d 를 실행해야 새것으로 바뀝니다.
caddy_data 는 절대 버리지 않는다

인증서는 caddy_data 볼륨에 삽니다. docker compose down -v 로, 또는 compose.yaml 에서 볼륨을 빼먹어서 그것이 지워지면 Caddy 는 켤 때마다 인증서를 새로 요청해야 합니다. 인증 기관은 한 도메인이 한 주에 받을 수 있는 인증서 수를 제한하므로, 몇 번의 부주의한 재시작으로 사이트가 며칠 동안 HTTPS 없이 지낼 수 있습니다.

웹 클라이언트

스스로 표시하는 칸이에요. 여기서 채점하지는 않아요.

이 단계를 이해한 것으로 표시할까요?

아직 취소할 수 없으니, 정말 이해했을 때만 표시해 주세요.

이해한 것으로 표시했어요

스스로 표시하는 칸이에요. 여기서 채점하지는 않아요.