Huginn은 2013년 Andrew Cantino가 개발한 오픈소스 에이전트 기반 자동화 플랫폼입니다. 웹을 모니터링하고, 이벤트를 감지하며, 사용자를 대신해 자동으로 작업을 수행하는 “에이전트”를 구축할 수 있습니다. IFTTT나 Zapier의 셀프호스팅 대안으로, 데이터 주권을 유지하면서 무제한 자동화를 구현할 수 있습니다. GitHub Stars 46,000+로 검증된 프로젝트입니다.
Huginn이란?
Huginn(후긴)은 북유럽 신화에서 오딘의 까마귀 이름으로, “생각”을 의미합니다. 이 플랫폼은 사용자를 대신해 온라인에서 자동화된 작업을 수행하는 에이전트 시스템입니다. 에이전트들이 이벤트를 생성하고 소비하며, 방향성 그래프(Directed Graph)를 따라 전파합니다.
핵심 아키텍처
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 에이전트 A │ ──▶ │ 이벤트 │ ──▶ │ 에이전트 B │
│ (데이터 수집) │ │ (JSON) │ │ (필터링) │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ 에이전트 C │
│ (알림 발송) │
└─────────────┘
IFTTT/Zapier vs Huginn
| 특징 | IFTTT/Zapier | Huginn |
|---|---|---|
| 호스팅 | 클라우드 | 셀프호스팅 |
| 데이터 소유권 | 제3자 | 사용자 |
| 가격 | 월 구독 (제한적 무료) | 완전 무료 |
| 워크플로우 복잡도 | 단일 트리거 | 다중 트리거, 조건부 로직 |
| 웹 스크래핑 | ❌ | ✅ 네이티브 |
| 커스텀 로직 | ❌ | ✅ Ruby/JS |
| 오프라인 동작 | ❌ | ✅ |
| 작업 수 제한 | 있음 | 무제한 |
주요 기능
🕷️ 에이전트 유형 (60+ 내장)
데이터 수집
WebsiteAgent: 웹페이지 스크래핑, CSS/XPath 선택자RssAgent: RSS/Atom 피드 모니터링TwitterStreamAgent: 트위터 스트림 모니터링IMAPFolderAgent: 이메일 모니터링
데이터 처리
TriggerAgent: 조건부 필터링 (정규식, 비교 연산)DataOutputAgent: 데이터 형식 변환JavaScriptAgent: 커스텀 JS 로직 실행LiquidOutputAgent: 템플릿 기반 변환
액션 실행
EmailAgent: 이메일 발송SlackAgent: Slack 메시지TwilioAgent: SMS 발송PushbulletAgent: 푸시 알림WebhookAgent: 웹훅 호출ShellCommandAgent: 셸 명령 실행
🔗 풍부한 통합
- Slack, Twitter, Telegram, Discord
- IMAP, SMTP, FTP
- MQTT, Jabber/XMPP
- Twilio, Pushbullet, Pushover
- Amazon Mechanical Turk
- RSS, Webhook, HTTP API
🎯 고급 기능
- Liquid 템플릿: 동적 메시지 생성
- 시나리오: 에이전트 그룹 내보내기/가져오기
- 스케줄링: 분/시간/일 단위 실행
- 이벤트 보존: 과거 이벤트 조회 및 재실행
- 커스텀 에이전트: Ruby Gem으로 확장
사전 요구사항
- Docker 및 Docker Compose
- 최소 1GB RAM (권장 2GB+)
- MySQL 또는 PostgreSQL
- 프로덕션: 도메인 및 SMTP 설정
Docker Compose 설치 방법
방법 1: 빠른 테스트 (단일 컨테이너)
데이터가 영구 저장되지 않는 테스트용입니다:
docker run -it -p 3000:3000 huginn/huginn
http://localhost:3000으로 접속합니다. 기본 계정: admin / password
방법 2: 기본 설치 (MySQL 포함)
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: huginn-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: huginn
MYSQL_USER: huginn
MYSQL_PASSWORD: huginn_password
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
huginn:
image: huginn/huginn
container_name: huginn
restart: unless-stopped
ports:
- "3000:3000"
environment:
- HUGINN_DATABASE_ADAPTER=mysql2
- HUGINN_DATABASE_HOST=mysql
- HUGINN_DATABASE_PORT=3306
- HUGINN_DATABASE_NAME=huginn
- HUGINN_DATABASE_USERNAME=huginn
- HUGINN_DATABASE_PASSWORD=huginn_password
- APP_SECRET_TOKEN=your_random_secret_token_here_at_least_64_chars
- TIMEZONE=Asia/Seoul
- DOMAIN=localhost:3000
depends_on:
mysql:
condition: service_healthy
volumes:
mysql_data:
방법 3: PostgreSQL 사용
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: huginn-postgres
restart: unless-stopped
environment:
POSTGRES_USER: huginn
POSTGRES_PASSWORD: huginn_password
POSTGRES_DB: huginn
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U huginn"]
interval: 10s
timeout: 5s
retries: 5
huginn:
image: huginn/huginn
container_name: huginn
restart: unless-stopped
ports:
- "3000:3000"
environment:
- HUGINN_DATABASE_ADAPTER=postgresql
- HUGINN_DATABASE_HOST=postgres
- HUGINN_DATABASE_PORT=5432
- HUGINN_DATABASE_NAME=huginn
- HUGINN_DATABASE_USERNAME=huginn
- HUGINN_DATABASE_PASSWORD=huginn_password
- APP_SECRET_TOKEN=your_random_secret_token_here_at_least_64_chars
- TIMEZONE=Asia/Seoul
- DOMAIN=localhost:3000
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
방법 4: 프로덕션 설정 (SMTP + 환경 변수)
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: huginn-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: huginn
MYSQL_USER: huginn
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
huginn:
image: huginn/huginn
container_name: huginn
restart: unless-stopped
ports:
- "3000:3000"
environment:
# 데이터베이스
- HUGINN_DATABASE_ADAPTER=mysql2
- HUGINN_DATABASE_HOST=mysql
- HUGINN_DATABASE_PORT=3306
- HUGINN_DATABASE_NAME=huginn
- HUGINN_DATABASE_USERNAME=huginn
- HUGINN_DATABASE_PASSWORD=${MYSQL_PASSWORD}
# 애플리케이션
- APP_SECRET_TOKEN=${APP_SECRET_TOKEN}
- TIMEZONE=Asia/Seoul
- DOMAIN=${DOMAIN}
- FORCE_SSL=true
# SMTP 설정
- SMTP_DOMAIN=${SMTP_DOMAIN}
- SMTP_SERVER=${SMTP_SERVER}
- SMTP_PORT=587
- SMTP_USER_NAME=${SMTP_USER}
- SMTP_PASSWORD=${SMTP_PASSWORD}
- SMTP_AUTHENTICATION=plain
- SMTP_ENABLE_STARTTLS_AUTO=true
- EMAIL_FROM_ADDRESS=${EMAIL_FROM}
# 초대 코드 (신규 가입 제한)
- INVITATION_CODE=${INVITATION_CODE}
# 트위터 (선택)
- TWITTER_OAUTH_KEY=${TWITTER_KEY:-}
- TWITTER_OAUTH_SECRET=${TWITTER_SECRET:-}
depends_on:
mysql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
volumes:
mysql_data:
.env 파일:
# 데이터베이스
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_PASSWORD=your_huginn_password
# 애플리케이션
APP_SECRET_TOKEN=generate_a_random_64_character_string_here_for_security_purposes
DOMAIN=huginn.yourdomain.com
INVITATION_CODE=your_secret_invitation_code
# SMTP
SMTP_DOMAIN=yourdomain.com
SMTP_SERVER=smtp.gmail.com
SMTP_USER=your_email@gmail.com
SMTP_PASSWORD=your_app_password
EMAIL_FROM=huginn@yourdomain.com
# 소셜 (선택)
TWITTER_KEY=your_twitter_api_key
TWITTER_SECRET=your_twitter_api_secret
방법 5: Traefik 리버스 프록시 연동
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: huginn-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: huginn
MYSQL_USER: huginn
MYSQL_PASSWORD: huginn_password
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- huginn
huginn:
image: huginn/huginn
container_name: huginn
restart: unless-stopped
environment:
- HUGINN_DATABASE_ADAPTER=mysql2
- HUGINN_DATABASE_HOST=mysql
- HUGINN_DATABASE_PORT=3306
- HUGINN_DATABASE_NAME=huginn
- HUGINN_DATABASE_USERNAME=huginn
- HUGINN_DATABASE_PASSWORD=huginn_password
- APP_SECRET_TOKEN=your_random_secret_token_here
- TIMEZONE=Asia/Seoul
- DOMAIN=huginn.yourdomain.com
- FORCE_SSL=true
labels:
- "traefik.enable=true"
- "traefik.http.routers.huginn.rule=Host(`huginn.yourdomain.com`)"
- "traefik.http.routers.huginn.entrypoints=websecure"
- "traefik.http.routers.huginn.tls=true"
- "traefik.http.routers.huginn.tls.certresolver=letsencrypt"
- "traefik.http.services.huginn.loadbalancer.server.port=3000"
depends_on:
mysql:
condition: service_healthy
networks:
- huginn
- traefik
volumes:
mysql_data:
networks:
huginn:
driver: bridge
traefik:
external: true
방법 6: 단일 컨테이너 (내장 SQLite – 개발용)
version: '3.8'
services:
huginn:
image: huginn/huginn-single-process
container_name: huginn
restart: unless-stopped
ports:
- "3000:3000"
environment:
- APP_SECRET_TOKEN=your_random_secret_token_here
- TIMEZONE=Asia/Seoul
- DOMAIN=localhost:3000
volumes:
- huginn_data:/app/data
volumes:
huginn_data:
샘플 에이전트 구성
1. 웹사이트 변경 모니터링
{
"name": "XKCD 새 만화 감지",
"type": "WebsiteAgent",
"schedule": "every_1h",
"options": {
"url": "https://xkcd.com",
"type": "html",
"mode": "on_change",
"extract": {
"title": {
"css": "#ctitle",
"value": "string(.)"
},
"image_url": {
"css": "#comic img",
"value": "@src"
},
"alt_text": {
"css": "#comic img",
"value": "@title"
}
}
}
}
2. RSS 피드 → 이메일 다이제스트
{
"name": "기술 뉴스 RSS",
"type": "RssAgent",
"schedule": "every_1h",
"options": {
"url": "https://news.ycombinator.com/rss",
"expected_update_period_in_days": 1
}
}
{
"name": "뉴스 다이제스트 이메일",
"type": "DigestEmailAgent",
"schedule": "8am",
"options": {
"subject": "오늘의 기술 뉴스 다이제스트",
"headline": "Hacker News 오늘의 주요 기사",
"expected_receive_period_in_days": 1
},
"sources": ["기술 뉴스 RSS"]
}
3. 날씨 알림
{
"name": "날씨 정보 수집",
"type": "WeatherAgent",
"schedule": "every_1h",
"options": {
"location": "Seoul, KR",
"api_key": "your_openweathermap_api_key"
}
}
{
"name": "비 오면 알림",
"type": "TriggerAgent",
"options": {
"rules": [
{
"type": "regex",
"path": "conditions",
"value": "rain|Rain|thunderstorm"
}
],
"message": "내일 비가 올 예정입니다. 우산을 챙기세요! 🌧️"
},
"sources": ["날씨 정보 수집"]
}
4. 가격 모니터링 + Slack 알림
{
"name": "아마존 제품 가격 모니터링",
"type": "WebsiteAgent",
"schedule": "every_6h",
"options": {
"url": "https://www.amazon.com/dp/B09XXXXXXX",
"type": "html",
"extract": {
"price": {
"css": ".a-price-whole",
"value": "string(.)"
},
"title": {
"css": "#productTitle",
"value": "normalize-space(.)"
}
}
}
}
{
"name": "가격 하락 감지",
"type": "TriggerAgent",
"options": {
"rules": [
{
"type": "field<value",
"path": "price",
"value": "100"
}
]
},
"sources": ["아마존 제품 가격 모니터링"]
}
{
"name": "Slack 알림 발송",
"type": "SlackAgent",
"options": {
"webhook_url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
"channel": "#deals",
"username": "Huginn Bot",
"message": "🎉 가격 하락! {{title}} - ${{price}}"
},
"sources": ["가격 하락 감지"]
}
환경 변수 설정
주요 환경 변수
| 변수명 | 설명 | 기본값 |
|---|---|---|
APP_SECRET_TOKEN | 세션 암호화 키 (64자+) | 필수 |
DOMAIN | 외부 접속 도메인 | localhost:3000 |
TIMEZONE | 타임존 | UTC |
FORCE_SSL | HTTPS 강제 | false |
INVITATION_CODE | 회원가입 초대 코드 | – |
데이터베이스 설정
# MySQL
HUGINN_DATABASE_ADAPTER=mysql2
HUGINN_DATABASE_HOST=mysql
HUGINN_DATABASE_PORT=3306
HUGINN_DATABASE_NAME=huginn
HUGINN_DATABASE_USERNAME=huginn
HUGINN_DATABASE_PASSWORD=password
# PostgreSQL
HUGINN_DATABASE_ADAPTER=postgresql
HUGINN_DATABASE_HOST=postgres
HUGINN_DATABASE_PORT=5432
SMTP 설정 (이메일 발송)
SMTP_DOMAIN=yourdomain.com
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USER_NAME=your_email@gmail.com
SMTP_PASSWORD=your_app_password
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS_AUTO=true
EMAIL_FROM_ADDRESS=huginn@yourdomain.com
업그레이드 및 백업
버전 업그레이드
# 최신 이미지 풀
docker compose pull
# 재시작 (마이그레이션 자동 실행)
docker compose down
docker compose up -d
데이터베이스 백업
# MySQL 백업
docker exec huginn-mysql mysqldump -u huginn -p huginn > huginn_backup_$(date +%Y%m%d).sql
# PostgreSQL 백업
docker exec huginn-postgres pg_dump -U huginn huginn > huginn_backup_$(date +%Y%m%d).sql
시나리오 내보내기
웹 UI에서 Scenarios → 원하는 시나리오 선택 → Export → JSON 파일 다운로드
문제 해결
컨테이너 시작 실패
# 로그 확인
docker logs huginn
# DB 연결 확인
docker exec huginn rake db:migrate:status
에이전트 실행 안 됨
- 스케줄 설정 확인 (never로 설정 시 실행 안 됨)
- 소스 에이전트 연결 확인
- 이벤트 로그에서 오류 확인
이메일 발송 실패
# SMTP 설정 확인
docker exec huginn env | grep SMTP
# 이메일 테스트
docker exec huginn rake emails:send_test
메모리 부족
services:
huginn:
deploy:
resources:
limits:
memory: 2G
사용 사례
1. 정보 수집 및 모니터링
- 뉴스/블로그 RSS 모니터링
- 경쟁사 웹사이트 변경 감지
- 소셜 미디어 키워드 추적
- 가격 변동 알림
2. 자동 알림
- 날씨 예보 기반 알림
- 주가/암호화폐 가격 알림
- 시스템 모니터링 알림
- 이벤트 티켓 오픈 알림
3. 데이터 파이프라인
- 웹 스크래핑 → 데이터베이스 저장
- API 데이터 수집 → 스프레드시트 업데이트
- 이메일 → Slack 전달
4. 개인 비서
- 일일 다이제스트 이메일
- 약속 알림
- 할일 관리 자동화
도구 비교
| 기능 | Huginn | n8n | IFTTT | Zapier |
|---|---|---|---|---|
| 오픈소스 | ✅ MIT | ✅ Fair Code | ❌ | ❌ |
| 셀프호스팅 | ✅ | ✅ | ❌ | ❌ |
| 웹 스크래핑 | ✅ 강력 | ⚠️ 제한 | ❌ | ❌ |
| 조건부 로직 | ✅ | ✅ | ⚠️ | ⚠️ |
| 가격 | 무료 | 무료/유료 | 유료 | 유료 |
| UI 친화성 | 중간 | 높음 | 높음 | 높음 |
| 학습 곡선 | 높음 | 중간 | 낮음 | 낮음 |
결론
Huginn은 다음과 같은 경우에 적합합니다:
- 데이터 주권: 자동화 데이터를 제3자에게 맡기고 싶지 않은 경우
- 비용 절감: Zapier/IFTTT 유료 플랜 비용이 부담스러운 경우
- 복잡한 로직: 다중 조건, 분기, 데이터 변환이 필요한 경우
- 웹 스크래핑: 네이티브 스크래핑 기능이 필요한 경우
- 오프라인 환경: 인터넷 없이도 로컬에서 자동화가 필요한 경우
12년간 검증된 46,000+ Stars 프로젝트로, 개인부터 기업까지 폭넓게 사용됩니다. 학습 곡선이 있지만, 한번 익히면 무한한 자동화 가능성을 열어줍니다!