웹브라우징 자동화 with. Airtop
✔️ n8n과 Airtop을 활용하여, 코딩 없이 '말로만' 시키는 나만의 웹 브라우징 자동화 에이전트를 만드는 방법을 완전 공개합니다! 코딩 1도 없이, 반복적인 웹서핑과 데이터 수집을 완전 자동화! 그동안 자바스크립트(동적 페이지) 때문에 크롤링을 포기해야 했던 복잡한 웹사이트도 이제 문제없이 제어할 수 있습니다.
✔️ 10만원 대 미니PC 한 대로, 월 수십만 원의 가치를 하는 나만의 자동화 서버(n8n)를 구축하는 모든 과정을 A to Z로 떠먹여 드립니다. 서버 구축은 전문가만 하는 어려운 일이라고 생각해서 도전조차 못 하셨던 분들을 위해, 최대한 세세하게 완벽 가이드 영상으로 만들었습니다.
flowchart TD TITLE["🌐 n8n서버 네트워크 구조"] USER["사용자"] INTERNET["외부 인터넷망"] DUCKDNS["DuckDNS 도메인<br/>code.duckdns.org<br/>n8n.duckdns.org"] PUBLICIP["공인IP<br/>xxx.xxx.xxx.xxx"] ROUTER["공유기"] PRIVATENET["내부사설망<br/>192.168.x.x"] MINIPC["미니PC<br/>192.168.x.100"] CADDY["Caddy 프록시서버<br/>:80, :443"] VSCODE["VSCode 서버<br/>:8080"] N8N["n8n 서버<br/>:5678"] USER --> INTERNET INTERNET --> DUCKDNS DUCKDNS -->|DNS 해석| PUBLICIP PUBLICIP --> ROUTER ROUTER -->|포트포워딩<br/>80→80, 443→443| PRIVATENET PRIVATENET --> MINIPC MINIPC --> CADDY CADDY -->|localhost:8080| VSCODE CADDY -->|localhost:5678| N8N style TITLE fill:#1976d2,color:#ffffff style USER fill:#4caf50 style INTERNET fill:#e3f2fd style DUCKDNS fill:#e8f5e8 style PUBLICIP fill:#fff3e0 style ROUTER fill:#fce4ec style PRIVATENET fill:#f3e5f5 style MINIPC fill:#e1f5fe style CADDY fill:#fff9c4 style VSCODE fill:#c8e6c9 style N8N fill:#ffebee
flowchart LR TITLE["🌐 서버 구축 프로세스 안내"] A[1번] A-1["MiniPC 준비"] A-2["리눅스서버 부팅디스크 준비"] A-3["Windows 제품키 백업"] A-4["리눅스 서버 설치"] A-5["공유기 포트포워딩 설정"] B[2번] B-1["SSH터미널 설정"] B-2["초기 보안 설정"] B-3["노트북에서 미니PC 접속"] B-4["바이브 코딩 도구 설치"] C[3번] C-1["DuckDNS 도메인 준비"] C-2["VSCode 서버 호스팅"] C-3["VSCode 환경 준비"] D[4번] D-1["Docker 설치"] D-2["n8n 서버구축"] D-3["n8n접속 / 초기설정"] D-4["n8n 업데이트 및 유지보수"] A --> A-1 --> A-2 --> A-3 --> A-4 --> A-5 B --> B-1 --> B-2 --> B-3 --> B-4 C --> C-1 --> C-2 --> C-3 D --> D-1 --> D-2 --> D-3 --> D-4 style TITLE fill:#1976d2,color:#ffffff style A-1 fill:#e1f5fe style A-2 fill:#e1f5fe style A-3 fill:#e1f5fe style A-4 fill:#e1f5fe style A-5 fill:#e1f5fe style B-1 fill:#f3e5f5 style B-2 fill:#f3e5f5 style B-3 fill:#f3e5f5 style B-4 fill:#f3e5f5 style C-1 fill:#e8f5e8 style C-2 fill:#e8f5e8 style C-3 fill:#e8f5e8 style D-1 fill:#fff3e0 style D-2 fill:#fff3e0 style D-3 fill:#fff3e0 style D-4 fill:#fff3e0
ㅤ | 개념 | 한줄 설명 | 상세 설명 | 예시 |
ㅤ | Public IP (공인IP) | 인터넷에서 사용하는 실제 주소 | ISP에서 할당받은 전 세계 유일한 IP 주소. 외부에서 내 집 공유기를 찾을 때 사용 | 203.123.45.67 |
ㅤ | Private IP (사설IP) | 집 내부에서만 사용하는 주소 | 공유기가 집 안 기기들에게 할당하는 로컬 주소. 외부에서 직접 접근 불가 | 192.168.1.100 |
ㅤ | DuckDNS | 무료 도메인 서비스 | 숫자로 된 IP 주소를 기억하기 쉬운 도메인명으로 변환해주는 서비스 | myserver.duckdns.org → 203.123.45.67 |
ㅤ | 서비스 | 한줄 설명 | 역할 | 왜 필요한가? |
ㅤ | Ubuntu | 리눅스 운영체제 | 서버용 무료 운영체제. Windows 대신 사용 | 안정적이고 보안이 좋으며 서버에 최적화됨 |
ㅤ | Caddy | 웹서버 프로그램 | 외부 요청을 내부 서비스로 전달하는 리버스 프록시 | 하나의 서버로 여러 웹사이트를 운영할 수 있게 해줌 |
ㅤ | Docker | 가상화 프로그램 | 프로그램을 컨테이너라는 박스에 담아서 실행 | 프로그램 설치/관리가 쉽고 충돌 방지 |
ㅤ | Node.js | 자바스크립트 실행환경 | 브라우저가 아닌 서버에서 JavaScript를 실행 | n8n과 많은 웹도구들이 Node.js로 만들어짐 |
ㅤ | 도구 | 한줄 설명 | 기능 | 사용 예시 |
ㅤ | UFW | 우분투 방화벽 | 특정 포트만 열고 나머지는 차단하는 방화벽 | sudo ufw allow 22 (SSH 포트 열기) |
ㅤ | Fail2ban | 공격 차단 도구 | 여러 번 로그인 실패하는 IP를 자동으로 차단 | 해커의 무차별 공격 방어 |
ㅤ | SSH | 원격 접속 프로토콜 | 다른 컴퓨터에 안전하게 원격 접속하는 방법 | 노트북에서 미니PC에 터미널로 접속 |
ㅤ | 명령어 | 용도 | 사용법 | 실제 예시 |
ㅤ | ssh | 원격 접속 | ssh 사용자명@IP주소 | ssh dante@192.168.1.100 |
ㅤ | ssh-keygen | SSH 키 생성 | 비밀번호 대신 키파일로 로그인하기 위한 키 생성 | ssh-keygen -t rsa -b 2048 |
ㅤ | ssh-copy-id | 키 복사 | 생성한 공개키를 서버에 복사 | ssh-copy-id dante@192.168.1.100 |
ㅤ | Get-Content | (윈도우즈 PowerShell) 키 복사 | 생성한 공개키를 서버에 복사 | type $HOME\.ssh\miniPC.pub | ssh username@server_ip "cat >> ~/.ssh/authorized_keys" |
ㅤ | systemctl | 서비스 관리 | 프로그램 시작/중지/상태확인 | sudo systemctl start ssh |
ㅤ | 명령어 | 용도 | 설명 | 실제 예시 |
ㅤ | docker compose up | 스택 시작 | docker-compose.yml에 정의된 모든 서비스 실행 | docker compose up -d |
ㅤ | docker compose down | 스택 중지 | 실행 중인 모든 컨테이너 중지 및 제거 | docker compose down |
ㅤ | docker compose ps | 상태 확인 | 현재 프로젝트의 컨테이너 상태 보기 | docker compose ps |
ㅤ | docker compose logs | 로그 확인 | 모든 서비스의 로그를 실시간으로 보기 | docker compose logs -f |
ㅤ | docker compose restart | 서비스 재시작 | 특정 서비스만 재시작 | docker compose restart n8n |
ㅤ | docker compose pull | 이미지 업데이트 | 최신 이미지로 업데이트 | docker compose pull |
ㅤ | docker compose exec | 컨테이너 내부 접속 | 실행 중인 컨테이너에 명령어 실행 | docker compose exec postgres bash |
ㅤ | 명령어 | 기능 | 설명 |
ㅤ | systemctl start [서비스명] | 서비스 시작 | 프로그램을 즉시 실행 |
ㅤ | systemctl stop [서비스명] | 서비스 중지 | 실행 중인 프로그램을 종료 |
ㅤ | systemctl restart [서비스명] | 서비스 재시작 | 프로그램을 껐다가 다시 켬 |
ㅤ | systemctl enable [서비스명] | 자동 시작 설정 | 컴퓨터 부팅 시 자동으로 실행되도록 설정 |
ㅤ | systemctl status [서비스명] | 상태 확인 | 프로그램이 정상 작동하는지 확인 |
ㅤ | 포트 번호 | 용도 | 설명 |
ㅤ | 22 | SSH | 원격 터미널 접속용 |
ㅤ | 80 | HTTP | 일반 웹사이트 접속용 |
ㅤ | 443 | HTTPS | 보안 웹사이트 접속용 (SSL 인증서 적용) |
ㅤ | 5678 | n8n | n8n 웹 인터페이스 접속용 |
ㅤ | 8080 | VS Code | VS Code 웹 에디터 접속용 |
sudo
: "관리자 권한으로 실행해줘"라는 뜻# PowerShell을 관리자 권한으로 실행 후 wmic path softwarelicensingservice get OA3xOriginalProductKey
F7
또는 Del
키로 BIOS 진입sudo apt install net-tools ifconfig ip addr show
sudo apt install network-manager nmcli dev wifi list nmcli dev wifi connect "SSID" password "YOUR_PASSWORD" ifconfig
192.168.1.1
접속가능 (동영상은 192.168.219.1 로 접속함)- 서비스명: SSH - 외부포트: 22 (보안상 22 대신 2222 사용하는것이 더 좋음. 단 접속시 ssh 사용자명@IP주소 -p 2222 로 접속해야함) - 내부포트: 22 - Private IP(내부 IP): 미니PC IP (예: 192.168.1.100) - 서비스명: HTTP - 외부포트: 80 - 내부포트: 80 - Private IP(내부 IP): 동일 - 서비스명: HTTPS - 외부포트: 443 - 내부포트: 443 - Private IP(내부 IP): 동일
sudo apt update sudo apt upgrade -y
# ssh터미널 서버 활성상태 확인 sudo systemctl status ssh # ssh터미널 서버 활성화 sudo systemctl start ssh # 방화벽 활성상태 확인 sudo ufw enable # 방화벽 포트 열기 (ssh : 22, http: 80, https: 443) sudo ufw allow ssh sudo ufw allow http sudo ufw allow https # 방화벽 상태 확인 sudo ufw status
ssh [사용자명]@[프라이빗네트워크주소] ex) ssh dante@192.168.219.109 # 사용자명과 암호는 우분투OS 설치할때 정한 것입니다. # 명령어 입력후 암호 입력합니다.
# 공개키와 비밀키 쌍 생성 ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_minipc (엔터 2번) # 출력된 공개키 내용을 미니PC 서버에 저장 # (MAC사용자) ssh-copy-id -i ~/.ssh/id_minipc.pub [사용자명]@[프라이빗네트워크주소] (엔터, 압호입력) # (왼도우 사용자) # Windows Powershell 환경)에서는 ssh-copy-id 명령어가 되지 않습니다. 아래 명령어로 수행하세요. type $env:USERPROFILE\.ssh\id_minipc.pub | ssh [사용자명]@[프라이빗네트워크주소] "cat >> ~/.ssh/authorized_keys" # 다시 서버 접속 ssh -i ~/.ssh/id_minipc [사용자명]@[프라이빗네트워크주소
sudo apt update sudo apt install curl ca-certificates gnupg -y curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install nodejs node -v npm -v
# Claude Code CLI sudo npm install -g @anthropic-ai/claude-code # 또는 Gemini CLI sudo npm install -g @google/gemini-cli gemini auth login
curl ifconfig.me
# VScode 서버 도메인 : dantelabs-code.duckdns.org # n8n 서버 도메인 : dantelabs-n8n.duckdns.org
~/.config/code-server/config.yaml
설정 (localhost 바인딩, 비밀번호 인증)curl -fsSL https://code-server.dev/install.sh | sh sudo systemctl enable --now code-server@[사용자명] ex) sudo systemctl enable --now code-server@dante sudo systemctl status code-server@dante cat > /home/dante/.config/code-server/config.yaml (엔터) ################################## bind-addr: 127.0.0.1:8080 auth: password password: [YOUR_PASSWORD] cert: false ################################## (Ctrl + C) sudo systemctl restart code-server@dante sudo systemctl status code-server@dante
sudo apt update sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg lsb-release curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/caddy-stable-archive-keyring.gpg] \ https://dl.cloudsmith.io/public/caddy/stable/deb/ubuntu/ $(lsb_release -cs) main" | \ sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install -y caddy
sudo systemctl enable --now caddy sudo systemctl status caddy caddy version
sudo vi /etc/caddy/Caddyfile (엔터) dantelabs-code.duckdns.org { reverse_proxy localhost:8080 } dantelabs-n8n.duckdns.org { reverse_proxy localhost:5678 }
sudo systemctl restart caddy
curl -fsSL https://get.docker.com | sh sudo systemctl status docker sudo usermod -aG docker $USER newgrp docker
.env
파일 작성 (도메인, DB, 암호화 키, 비밀번호 등)# -------------------------------- # Postgres 설정 # -------------------------------- POSTGRES_DB=n8ndb POSTGRES_USER=n8nuser POSTGRES_PASSWORD=supersecurepassword # -------------------------------- # n8n 시간대 설정 # -------------------------------- GENERIC_TIMEZONE=Asia/Seoul TZ=Asia/Seoul # -------------------------------- # n8n 설정 # -------------------------------- # 자격증명 암호화 키(openssl rand -base64 48 명령어 이용) N8N_ENCRYPTION_KEY=verylongrandomstringkey # 프록시 뒤에서 외부 접근 도메인 DOMAIN=yourdomain-n8n.duckdns.org
docker-compose.yml
에 PostgreSQL, Redis, n8n 메인 및 워커 정의services: postgres: image: postgres:15-alpine environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - pgdata:/var/lib/postgresql/data restart: unless-stopped redis: image: redis:7-alpine command: ["redis-server", "--save", "", "--appendonly", "no"] restart: unless-stopped n8n: image: docker.n8n.io/n8nio/n8n:latest depends_on: - postgres - redis environment: # DB(Postgres) DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} TZ: ${TZ} # 큐모드 + Redis EXECUTIONS_MODE: queue QUEUE_BULL_REDIS_HOST: redis QUEUE_BULL_REDIS_PORT: 6379 # 보안/기본설정 N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_PORT: 5678 # 프록시 뒤에서 올바른 웹훅 URL을 위해 WEBHOOK_URL 지정 WEBHOOK_URL: https://${DOMAIN} # Proxy 설정 (Caddy 뒤에서 실행) N8N_PROXY_HOST: "0.0.0.0::" N8N_PROTOCOL: https # Task runners 활성화 N8N_RUNNERS_ENABLED: "true" # Manual executions을 workers로 오프로드 OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true" # 파일 권한 자동 적용 N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true" ports: - "127.0.0.1:5678:5678" # 로컬에서만 노출, Caddy가 외부 공개 volumes: - n8n_data:/home/node/.n8n restart: unless-stopped worker: image: docker.n8n.io/n8nio/n8n:latest depends_on: - postgres - redis environment: DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} TZ: ${TZ} EXECUTIONS_MODE: queue QUEUE_BULL_REDIS_HOST: redis QUEUE_BULL_REDIS_PORT: 6379 N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} # Task runners 활성화 N8N_RUNNERS_ENABLED: "true" command: ["worker"] restart: unless-stopped volumes: pgdata: n8n_data:
docker compose up -d docker compose ps docker compose logs -f
#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" COMPOSE_FILE="./docker-compose.yml" SERVICES=("n8n" "worker") # 함께 갱신할 서비스들 IMAGE_FQN="docker.n8n.io/n8nio/n8n:latest" # 필요 시 특정 버전으로 고정 # 네임드 볼륨 백업 설정 N8N_VOLUME="n8n_data" BACKUP_DIR="./backups" LOG_FILE="./n8n-autoupdate.log" # (선택) Postgres 백업 PG_BACKUP="false" PG_SERVICE="postgres" PG_DB="${POSTGRES_DB:-n8n}" PG_USER="${POSTGRES_USER:-n8n}" PG_PW="${POSTGRES_PASSWORD:-password}" timestamp() { date +"%Y-%m-%d_%H-%M-%S"; } log() { echo "[$(timestamp)] $*" | tee -a "$LOG_FILE"; } # Docker 접근 권한 확인(미리 친절한 에러 제공) if ! docker info >/dev/null 2>&1; then echo "[오류] Docker 데몬에 접근할 수 없습니다. 사용자 계정을 docker 그룹에 추가하거나 sudo로 실행하세요." id groups exit 1 fi mkdir -p "$BACKUP_DIR" touch "$LOG_FILE" log "=== n8n 자동 업데이트 시작 ===" # 현재 실행 중 n8n 컨테이너 이미지 ID(다이제스트 유사) RUNNING_ID=$(docker compose -f "$COMPOSE_FILE" ps -q "n8n" || true) RUNNING_DIGEST="" if [[ -n "${RUNNING_ID:-}" ]]; then RUNNING_DIGEST=$(docker inspect --format='{{.Image}}' "$RUNNING_ID" || true) fi # 최신 이미지 받기 log "최신 이미지 가져오는 중: $IMAGE_FQN" if ! docker pull "$IMAGE_FQN" | tee -a "$LOG_FILE"; then log "[오류] 이미지 pull 실패" exit 1 fi LATEST_DIGEST=$(docker image inspect "$IMAGE_FQN" --format='{{.Id}}' || true) # 최신 여부 판별 if [[ -n "$RUNNING_DIGEST" && -n "$LATEST_DIGEST" && "$RUNNING_DIGEST" == "$LATEST_DIGEST" ]]; then log "이미 최신 버전입니다. 업데이트가 필요하지 않습니다." log "=== n8n 자동 업데이트 종료(변경 없음) ===" exit 0 fi # 1) n8n 데이터 볼륨 백업(tar.gz) BK_NAME_VOL="n8n_data_$(timestamp).tgz" log "네임드 볼륨 백업 진행: '$N8N_VOLUME' -> $BACKUP_DIR/$BK_NAME_VOL" if ! docker run --rm -v "${N8N_VOLUME}":/src -v "$(pwd)":/dst alpine \ sh -c "cd /src && tar czf /dst/$BACKUP_DIR/$BK_NAME_VOL ."; then log "[경고] n8n 볼륨 백업 실패(업데이트는 계속 진행)" fi # 2) (선택) Postgres 덤프 if [[ "$PG_BACKUP" == "true" ]]; then BK_NAME_PG="pg_dump_$(timestamp).sql.gz" log "Postgres 백업 진행: $BACKUP_DIR/$BK_NAME_PG" if ! PGPASSWORD="$PG_PW" docker compose -f "$COMPOSE_FILE" exec -T "$PG_SERVICE" \ sh -lc "pg_dump -U '$PG_USER' -d '$PG_DB' | gzip -c" > "$BACKUP_DIR/$BK_NAME_PG"; then log "[경고] Post
chmod a+x update-n8n.sh
crontab -e
0 0 * * * /home/dante/n8n/update-n8n.sh
# 설치 sudo apt install fail2ban # 시작 및 활성화 sudo systemctl start fail2ban sudo systemctl enable fail2ban # 상태 확인 sudo systemctl status fail2ban # 보안설정 sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local # /etc/fail2ban/jail.local 파일을 VSCode에서 편집 (아래는 예시) ########################################## [DEFAULT] ignoreip = 127.0.0.1/8 bantime = 30m # 차단 시간 findtime = 10m # 검사 기간 maxretry = 5 # 실패 허용 횟수 [sshd] enabled = true port = 22 # SSH 포트(변경 시 수정) ########################################## # 재시작 sudo systemctl restart fail2ban
당신은 Vibe Coding 환경의 AI 코딩 에이전트입니다. context7을 통해 공식 문서를 반드시 확인하여 문법 오류가 없는 서버 설정코드를 작성하세요. 다음 두 가지 입력 자료를 참고하여 설정 파일과 실행 스크립트를 생성해야 합니다. 1) [n8n 서버 설치용] 바이브 코딩 컨텍스트 문서: 서버 설정 전체 절차 및 코드 구조 설명 - URL : [붙여넣기] 2) [n8n 서버 설치용] 사용자 입력 문서 : 환경변수, 도메인, 인증 토큰, 비밀번호 등 사용자가 직접 입력한 값 - File Path : [붙여넣기] 목표: - DuckDNS 도메인 및 Caddy 기반 자동 HTTPS 환경 구성 - code-server를 Caddy를 통해 외부 노출 - Docker Compose로 n8n(Postgres, Redis, 워커 포함)을 구성하고 외부노출 (~/n8n/ 폴더에 프로젝트 구성) - `.env`, `docker-compose.yml`, `/etc/caddy/Caddyfile`, DuckDNS 업데이트 스크립트 등을 자동 생성 - n8n 프로젝트 폴더에 update-n8n.sh 스크립트를 작성하고 1일 1회 업데이트 스케줄링 등록 - 모든 코드와 설정은 최신 공식 문법을 준수해야 함 요구사항: - 사용자 입력 문서에서 제공된 값들을 변수로 반영 - context7 mcp로 (n8n, caddy, duckdns, docker) 공식 문서를 참고하여 문법 오류 없이 정확한 설정을 출력 - 각 단계별 필요한 CLI 명령어, 구성 파일, 주석을 함께 제시 - caddy는 향후 n8n서버 외에도 타 서비스도 노출할수 있도록 확장을 지원하도록 파일 작성 및 관리