TRENDSHIFT 딥다이브 · OpenWA 분석

OpenWA 딥다이브
— 월 사용료 없이 내 서버에서 돌리는 WhatsApp 자동화 게이트웨이

상용 게이트웨이(WAHA Plus·Twilio 등)가 매달 청구하는 비용을 0원으로 만드는 셀프호스팅 오픈소스. Docker 한 줄로 띄우면 본인 서버에서 다중 세션·웹훅·미디어·그룹 API를 직접 처리한다. 진짜 가치는 NestJS 어댑터 패턴을 실전 교재로 배울 수 있다는 것. (저장소: rmyndharis/OpenWA · MIT 라이선스 · TypeScript 86.4% · ⭐7.7k · v0.2.6 · NestJS 11 / Node 22 LTS)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

OpenWA가 정확히 무엇을 하는 도구인가

OpenWA본인 서버에 직접 띄우는 무료 오픈소스 WhatsApp API 게이트웨이다. 본인 시스템과 WhatsApp 사이에 다리를 놓아, 메시지 발송·수신·세션 관리·그룹·웹훅을 REST API 한 묶음으로 다룰 수 있게 해준다. "고객이 WhatsApp으로 '주문'을 보내면 자동 응답하고 구글 시트에 기록" 같은 자동화를, 외부 SaaS에 월 사용료를 내지 않고 본인 서버에서 만든다.

한 컷 비유

"월 40달러짜리 WhatsApp 게이트웨이를 Docker 한 줄로 0원짜리 내 서버 버전으로 만든다"

WhatsApp은 일반 개발자에게 공식 API를 잘 열어주지 않는다. 그래서 본인 시스템과 WhatsApp 사이에 중간 다리 서버(게이트웨이)가 필요한데, 상용 제품들은 이걸 비싸게 판다. OpenWA는 그 다리를 오픈소스(MIT)로 본인 서버에 직접 띄울 수 있게 만든 도구다.

하지만 초보자가 진짜 가져갈 보물은 따로 있다. 엔터프라이즈급 NestJS 아키텍처 — 특히 어댑터 패턴 — 가 잘 정리된 docs와 함께 들어 있어, 백엔드 설계를 통째로 배우는 살아있는 교재가 된다.

2왜 주목받는가

상용 서비스 종속의 답답함을 정확히 친다

WhatsApp 자동화를 만들 때 보통 세 갈래 길이 있고, 앞의 두 갈래는 문제점이 명확하다.

선택지 1
상용 SaaS (WAHA Plus · Twilio · MessageBird)

세션당 또는 메시지당 과금이라 비용이 빠르게 누적된다. 본인 메시지 데이터가 외부 회사 서버에 저장돼 고객정보 유출 위험이 있고, 코드를 본인이 수정할 수도 없다.

선택지 2
WhatsApp 공식 Cloud API (Meta)

메시지당 과금에 번호 등록이 까다롭고, 데이터는 모두 Meta 서버에 보관된다. 마케팅 메시지 등 일부 용도는 사실상 막혀 있다.

OpenWA의 선택지 3
본인 서버에 직접 호스팅 (MIT 라이선스, 무료)

본인 서버나 NAS에 Docker로 띄우면 끝. 월 사용료 0원, 모든 데이터가 본인 서버에만, 코드는 마음껏 수정. 게다가 여러 개의 WhatsApp 번호를 한 인스턴스에서 동시에 관리한다.

경쟁/대안 비교

비교 대상OpenWA의 위치
WAHA Plus유료 셀프호스팅 게이트웨이. OpenWA는 동급 기능(멀티세션·웹훅·미디어)을 무료 MIT로 제공
Twilio / MessageBird완전 관리형 SaaS라 운영은 편하지만 과금·데이터 외부 보관. OpenWA는 데이터 주권 + 무과금
Meta Cloud API공식이라 안정적이지만 제약·과금 많음. OpenWA는 비공식 우회라 유연하지만 BAN 위험 존재
raw whatsapp-web.js라이브러리만 쓰면 세션·웹훅·저장소·인증을 직접 다 짜야 함. OpenWA는 그 위에 게이트웨이 인프라를 얹어줌

※ 수치는 2026-06 기준 GitHub: ⭐7.7k · 포크 1.6k · 최신 릴리스 v0.2.6(2026-06). 초기 버전대라 프로덕션 채택 시 안정성은 직접 검증 권장.

3기술 스택 전체 지도

언어 구성 · 런타임 · WhatsApp 엔진 · 인프라

언어 구성 (GitHub 집계)

TypeScript ████████████████████████ 86.4% ← NestJS 서버 + 어댑터 + 모듈 전체 CSS ████ 11.8% ← React 대시보드 스타일 Shell ▏ 0.8% ← Docker/배포 스크립트 등

핵심 런타임 / 프레임워크

기술역할상세
NestJS 11백엔드 프레임워크모듈·DI(의존성 주입)·데코레이터 기반. 어댑터 교체의 토대
Node.js 22 LTS런타임서버 실행 환경
TypeScript 5.x언어인터페이스로 어댑터 계약(IStorageAdapter 등) 정의
whatsapp-web.jsWhatsApp 엔진Puppeteer로 web.whatsapp.com을 자동 조작 (비공식 우회)
SwaggerAPI 문서2785/api/docs에서 모든 엔드포인트를 브라우저로 직접 호출

데이터 / 저장 계층 (어댑터로 교체 가능)

계층기본(개발)프로덕션 대안
DBSQLitePostgreSQL
저장소로컬 파일시스템S3 / MinIO
캐시 / 큐메모리Redis (+ 큐 시스템)

프론트엔드 / 인프라

기술역할상세
React 대시보드웹 UI세션 생성·QR 스캔·상태 모니터링 (포트 2886)
Docker Compose배포profiles(postgres/redis/minio/with-dashboard/with-proxy/full)로 환경 구성
Traefik리버스 프록시with-proxy 프로파일로 선택 활성화
API Key 인증보안X-API-Key 헤더 + CIDR 화이트리스트 + 레이트 리미팅 + 감사 로깅

4아키텍처 심화 분석

어댑터 패턴 · 멀티세션 · 웹훅 · 엔진 추상화

시스템 구조도

┌──────────────────────────────────────────────────────────────┐ │ 클라이언트 (당신의 앱 / n8n / curl) │ │ REST API 호출 (X-API-Key 헤더) · 웹훅 수신 │ └───────────────────────────────┬──────────────────────────────┘ │ HTTP :2785 ┌───────────────────────────────▼──────────────────────────────┐ │ OpenWA (NestJS 11) │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ modules/ session · message · webhook · group · contact │ │ │ │ auth · infra · health (기능 모듈) │ │ │ └───────────────────────────┬──────────────────────────────┘ │ │ ┌───────────────────────────▼──────────────────────────────┐ │ │ │ engine/ WhatsApp 엔진 추상화 (whatsapp-web.js 래핑) │ │ │ └───────────────────────────┬──────────────────────────────┘ │ │ ┌───────────────────────────▼──────────────────────────────┐ │ │ │ common/ cache/ · storage/ (어댑터 인터페이스) │ │ │ │ IStorageAdapter ICacheAdapter ... │ │ │ └──────┬─────────────────┬─────────────────┬───────────────┘ │ └─────────┼─────────────────┼─────────────────┼─────────────────┘ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────────┐ │ SQLite / │ │ 메모리 / │ │ 로컬파일 / │ │ PostgreSQL │ │ Redis │ │ S3 · MinIO │ └────────────┘ └────────────┘ └────────────────┘ ▲ │ ┌──────┴──────┐ Puppeteer 브라우저 ▼ 웹훅(HMAC 서명) │ SessionMgr │── 세션마다 1개 ──→ web.whatsapp.com 당신의 서버 │ Map│ (재시도 큐) └─────────────┘

핵심 설계 패턴 ①: 어댑터 패턴 (이 레포의 백미)

패턴
어댑터 패턴 (Adapter Pattern)
다른 인터페이스를 가진 부품을 우리 시스템이 기대하는 "모양"으로 변환하는 디자인 패턴. 한국 220V 콘센트에 미국 110V 기기를 꽂는 멀티 어댑터처럼, 서로 다른 부품을 같은 구멍에 꽂게 만든다.
콘센트 비유

출장 가서 본 적 있을 것이다. 한국·미국·유럽 콘센트 모양은 다 달라도 멀티 어댑터만 있으면 어디서든 노트북을 충전한다. 노트북은 콘센트가 무슨 모양인지 모른다 — 그냥 익숙한 모양에 꽂힐 뿐.

OpenWA의 메시지 서비스도 "저장소가 무엇인지" 모른다. IStorageAdapter라는 모양에 대고 "이 파일 저장해줘"라고만 부탁한다. 뒤에는 로컬 파일이든 S3이든 MinIO든 꽂혀 있다.

교체 가능한 부품은 3계층 — DB(SQLite↔PostgreSQL), 저장소(로컬↔S3/MinIO), 캐시(메모리↔Redis). 코드는 한 줄도 안 건드리고 설정/프로파일만 바꿔 갈아끼운다.

// ※ 실제 엔진 인터페이스명은 IWhatsAppEngine (src/engine/interfaces/). 아래는 개념 설명용 의사 코드 // 인터페이스(계약)를 한 번 정의해두면 ↓ interface IStorageAdapter { save(key: string, data: Buffer): Promise<void>; load(key: string): Promise<Buffer>; } // 구현체를 여러 개 만들고 → DI Factory가 설정값 보고 골라 주입 class LocalStorageAdapter implements IStorageAdapter { /* 로컬 파일 */ } class S3StorageAdapter implements IStorageAdapter { /* S3/MinIO */ } // 서비스는 인터페이스만 알면 됨 → if/else 환경 분기 지옥이 사라진다
미확인
어댑터 선택 환경변수의 정확한 키 이름

README는 "구성은 첫 실행 시 자동 생성"이라고만 명시하고 STORAGE_TYPE 같은 변수명을 직접 열거하지 않는다. 실제 키 이름은 생성된 설정 파일 또는 docs/05-database-design·config/에서 확인해야 한다. (본문의 변수명 예시는 설명용 가정.)

핵심 설계 패턴 ②: 멀티세션 매니저

SessionManagerMap<세션ID, Session> 형태로 모든 세션을 들고, 각 세션은 자기만의 Puppeteer 브라우저를 가진다. 한 사람이 여러 노트북에 각각 다른 WhatsApp을 로그인해둔 셈.

함정
세션 수에 비례한 메모리

각 세션이 브라우저를 하나씩 띄우므로 세션 1개당 대략 300~500MB(환경에 따라 변동). 10개를 동시에 돌리면 수 GB가 필요하고, 작은 VPS/NAS에서는 OOM으로 죽을 수 있다. 미리 세션 수 × 약 500MB를 계산하라.

핵심 설계 패턴 ③: 웹훅 + HMAC 서명

메시지 도착 등 이벤트를 본인 서버로 자동 HTTP POST한다. 위조 방지를 위해 HMAC-SHA256 서명을 본문에 박아, 비밀 키를 모르는 외부인이 가짜 이벤트를 쏘면 수신측이 거부한다. 발송 실패 시 큐가 자동 재시도해 메시지를 잃지 않는다.

핵심 설계 패턴 ④: 엔진 추상화 + WhatsApp Web 우회

engine/ 디렉토리가 whatsapp-web.js를 한 겹 감싸, 메시지 모듈이 특정 라이브러리에 직접 묶이지 않게 한다. 동작 원리는 Puppeteer로 실제 WhatsApp Web 페이지를 사람처럼 자동 조작하는 것 — Meta 입장에선 사람이 웹을 쓰는 것처럼 보인다.

함정
비공식 클라이언트 차단(BAN) 위험

Meta는 비공식 클라이언트로 의심되는 번호를 차단할 수 있다. 본인 메인 번호로 돌리다 막히면 개인 메시지까지 끊긴다. 자동화용 별도 번호를 쓰는 게 안전하다.

5디렉토리 구조 해부

src/는 NestJS 모범 구조 그대로 — 학습 동선이 그려진다
rmyndharis/OpenWA/ ├── src/ │ ├── main.ts # 애플리케이션 진입점 │ ├── app.module.ts # 루트 모듈 │ ├── config/ # 설정 (첫 실행 시 자동 생성) │ ├── common/ # 공용 유틸 │ │ ├── cache/ # ICacheAdapter (메모리/Redis) │ │ └── storage/ # IStorageAdapter (로컬/S3/MinIO) │ ├── core/ # 코어 시스템 │ │ ├── hooks/ # 라이프사이클 훅 │ │ └── plugins/ # 플러그인 확장점 │ ├── engine/ # WhatsApp 엔진 추상화 (whatsapp-web.js 래핑) │ └── modules/ # ⭐ 기능 모듈 (NestJS 모듈 단위) │ ├── session/ # 세션 생성·QR·연결 관리 │ ├── message/ # 텍스트·미디어·대량 발송 │ ├── webhook/ # 웹훅 등록·HMAC 서명·재시도 │ ├── group/ # 그룹·채널 관리 │ ├── contact/ # 연락처 조회 │ ├── auth/ # API Key 인증 │ ├── infra/ # 인프라(레이트리밋·감사로그 등) │ └── health/ # /health · /ready 헬스체크 ├── dashboard/ # React 웹 대시보드 (포트 2886) └── docs/ # 24개 문서(01~23) 설계 문서

학습 동선 추천: main.tsapp.module.ts로 부팅 흐름을 잡고 → common/storage/에서 어댑터 인터페이스 1개를 정독 → modules/message/가 그 어댑터를 어떻게 "모양만 보고" 쓰는지 확인하면, 어댑터 패턴의 전체 그림이 한 번에 잡힌다.

docs/ 24개 문서(01~23) (설계 교재)

#문서읽으면 얻는 것
01Project Overview전체 목적·범위
02Requirements Specification요구사항 정의 방식
03System Architecture구조도·모듈 관계 (핵심)
04Security Design인증·HMAC·화이트리스트
05Database Design스키마·어댑터 매핑
06API Specification엔드포인트 계약
07API Collection엔드포인트 예제 모음
08Development Guidelines코딩 컨벤션
14Migration Guide버전 이전

6학습 포인트 (기술별)

WhatsApp이 아니어도 어디든 옮길 수 있는 설계들

A. Pluggable Architecture (어댑터 교체 구조)

배울 것: 인터페이스로 계약을 정의하고 DI Factory가 설정값 보고 구현체를 골라 주입하는 패턴. 본인 프로젝트의 로그를 "콘솔↔파일↔Loki", 메일을 "SMTP↔SendGrid↔SES"로 추상화해보면 환경 분기(if-else) 지옥이 통째로 사라진다.

B. NestJS 모듈 설계 + DI

배울 것: 기능을 session/message/webhook처럼 모듈로 쪼개고 의존성 주입으로 결합도를 낮추는 방법. modules/ 8개가 그대로 모범 사례다.

C. HMAC 웹훅 서명

배울 것: 외부로 웹훅을 보낼 때 crypto.createHmac 한 줄로 서명을 박는 습관. 수신자가 위조 이벤트를 거부하게 해주는 1차 보안 방어선.

D. 큐 기반 재시도(안전망)

배울 것: 외부 호출(웹훅·발송)이 실패할 수 있다는 전제로 큐에 넣고 자동 재시도하는 설계. 일시적 장애에도 메시지를 잃지 않는다.

E. Docker Compose Profiles

배울 것:docker-compose 파일에 profiles: [postgres, redis, minio, with-dashboard, with-proxy, full]로 태그를 달아 같은 파일로 개발·스테이징·프로덕션을 다른 구성으로 띄우는 법.

F. Health/Ready 분리

배울 것: /health("프로세스 살아있나?")와 /ready("트래픽 받을 준비 됐나?")를 분리해 Kubernetes liveness/readiness probe에 그대로 매핑하는 운영 패턴.

7하드웨어 / 시스템 요구사항

세션 수가 메모리를 결정한다
항목최소권장
Node.js22 LTS (README 명시)22 LTS
실행 방식Docker / Docker ComposeDocker Compose + 프로파일
RAM세션 1개 기준 약 1GB 이상*세션 수 × 약 500MB + 여유
DBSQLite (내장, 개발용)PostgreSQL (프로덕션)
네트워크아웃바운드 인터넷 (WhatsApp Web 접속)웹훅 수신용 고정 IP/도메인
WhatsApp 번호QR 스캔 가능한 폰 1대자동화 전용 별도 번호

* RAM 수치는 README에 명시되지 않음 — 멀티세션이 Puppeteer 브라우저를 세션마다 띄우는 구조에서 도출한 추정치다. 실제 부하는 동시 세션 수·미디어 트래픽에 좌우된다.

주요 포트

포트용도
2785REST API
2785/api/docsSwagger 문서
2886React 대시보드

8직접 해볼 수 있는 실습 과제

난이도별 5가지 과제
과제 1

Docker로 띄우고 QR 스캔 ★ 초급 1~2시간

레포를 클론해 띄우고, 대시보드에서 세션을 만들어 QR을 폰으로 스캔한 뒤 본인 다른 폰으로 메시지를 자동 전송해본다.

# 1. 클론 후 개발용 컴포즈로 기동
git clone https://github.com/rmyndharis/OpenWA
docker compose -f docker-compose.dev.yml up -d
# 2. 대시보드(:2886) 접속 → 세션 생성 → QR 스캔
# 3. 폰 WhatsApp → 연결된 기기 → 그 QR 스캔
과제 2

Swagger로 API 한 바퀴 ★ 초급 1시간

localhost:2785/api/docs에서 코드 없이 클릭만으로 엔드포인트를 호출한다. 텍스트 메시지 발송 형식을 직접 확인.

# 텍스트 발송 (X-API-Key 헤더 필요)
POST /api/sessions/{sessionId}/messages/send-text
{ "chatId": "628123456789@c.us", "text": "Hello from OpenWA!" }
과제 3

웹훅 받아 자동 응답 ★★ 중급 2~3시간

웹훅 URL을 등록하고, 들어오는 메시지를 받아 자동 회신하는 작은 서버를 만든다. HMAC 서명 검증까지 구현해 위조 요청을 거부해본다.

구현: 웹훅 수신 엔드포인트 작성 → 헤더의 HMAC-SHA256 서명을 공유 비밀키로 재계산해 비교 → 일치할 때만 처리 → send-text API로 회신
과제 4

저장소 어댑터 교체 (로컬 → MinIO) ★★★ 고급 3~4시간

minio 프로파일로 S3 호환 스토리지를 띄우고, 미디어 저장 위치를 로컬 파일에서 객체 스토리지로 옮긴다. 코드 변경 없이 설정만으로 바뀌는 어댑터 패턴의 위력을 체감.

# MinIO 포함 프로파일로 기동
docker compose --profile minio up -d
# config/ 생성 파일에서 저장소 타입을 s3 계열로 전환 후 재시작
과제 5

엔진 인터페이스 분석 후 어댑터 확장 ★★★ 고급 4시간+

src/engine/interfaces/whatsapp-engine.interface.tsIWhatsAppEngine 시그니처를 확인하고, src/engine/adapters/ 구현체와 비교해 어댑터 패턴이 엔진 레이어에서 어떻게 동작하는지 파악한다. 한 번 해보면 어댑터 패턴이 본인 도구함의 영구 무기가 된다.

구조: src/engine/interfaces/whatsapp-engine.interface.ts의 IWhatsAppEngine 시그니처 확인 → src/engine/adapters/ 구현체 비교 → DI Factory에서 주입 흐름 추적 → 나만의 mock 어댑터 스케치

9관련 기술 심화 학습 로드맵

4주 학습 플랜 — NestJS 백엔드 아키텍처를 손에 넣기

1주차: 띄우고 흐름 잡기

요일학습 내용실습
Docker Compose 기동dev 컴포즈로 띄우고 세션 QR 스캔
Swagger로 API 탐험send-text·세션·연락처 직접 호출
부팅 흐름 읽기main.ts → app.module.ts 따라가기
docs 01~03 정독System Architecture 구조도 손으로 다시 그리기
모듈 경계 파악modules/ 8개 책임 한 줄씩 정리

2주차: 어댑터 패턴 해부

요일학습 내용실습
인터페이스 = 계약 개념common/storage의 IStorageAdapter 정독
구현체 비교로컬 vs S3 어댑터 코드 차이 분석
DI Factory 주입설정값 → 어느 구현체가 주입되는지 추적
저장소 교체 실습minio 프로파일로 전환(과제 4)
나만의 어댑터IWhatsAppEngine 시그니처 분석 + 어댑터 스케치(과제 5)

3주차: 웹훅·보안·운영

요일학습 내용실습
웹훅 등록·수신수신 서버 만들어 이벤트 받기
HMAC 서명 검증crypto.createHmac로 위조 요청 거부
큐 재시도수신 서버를 잠깐 끄고 재시도 관찰
인증·레이트리밋API Key·CIDR 화이트리스트 설정
Health/Ready/health·/ready 응답 차이 확인

4주차: 멀티세션·프로덕션 전환

요일학습 내용실습
SessionManager 구조Map<id,Session> + Puppeteer 추적
메모리 산정세션 수 × 약 500MB로 용량 계획
DB 어댑터 전환SQLite → PostgreSQL 프로파일
full 프로파일 배포postgres+redis+minio 한 번에 기동
종합 자동화웹훅→처리→회신 파이프라인 완성

10핵심 키워드 사전

이 레포를 이해하는 데 꼭 필요한 용어들
키워드설명
셀프호스팅남의 서비스를 빌리지 않고 본인 서버/NAS에 직접 설치해 돌리는 방식. 월 사용료 0원, 데이터 외부 유출 없음, 운영 책임은 본인
API 게이트웨이외부 시스템(WhatsApp)과 내 앱 사이의 중간 서버. 인증·로깅·재시도 등 공통 처리를 모은다
어댑터 패턴다른 인터페이스의 부품을 같은 "모양"으로 변환해 끼울 수 있게 하는 디자인 패턴. OpenWA의 DB/저장소/캐시 교체의 핵심
의존성 주입 (DI)객체가 필요한 부품을 직접 만들지 않고 외부(NestJS 컨테이너)에서 받아쓰는 방식. 어댑터 교체를 가능케 함
NestJS모듈·DI·데코레이터 기반 Node.js 백엔드 프레임워크. OpenWA의 골격
whatsapp-web.jsPuppeteer로 web.whatsapp.com을 자동 조작하는 라이브러리. 공식 API 없이 메시지를 주고받는 우회 엔진
PuppeteerChrome 브라우저를 코드로 원격 조작하는 라이브러리. 사람이 클릭·타이핑하는 흉내를 낸다
세션 (Session)하나의 WhatsApp 번호 연결 단위. 각 세션은 자기만의 브라우저 인스턴스를 가짐
멀티세션한 인스턴스에서 여러 WhatsApp 번호를 동시에 관리하는 능력. SessionManager가 담당
웹훅 (Webhook)"이벤트 생기면 이 주소로 알려줘"라고 등록해두는 방식. 메시지 도착 시 OpenWA가 본인 서버로 HTTP POST
HMAC 서명공유 비밀키로 메시지에 디지털 도장을 찍어 위조를 막는 방법(HMAC-SHA256). 수신자가 가짜 요청을 거부
QR 페어링대시보드의 QR을 폰 WhatsApp "연결된 기기"로 스캔해 번호를 연결하는 과정
S3 / MinIO객체 스토리지. S3는 AWS 표준, MinIO는 그걸 본인 서버에 띄우는 오픈소스. 미디어 저장 어댑터의 대안
Docker Compose Profile한 컴포즈 파일에 태그(postgres/redis/minio/full 등)를 달아 환경별로 다른 서비스 조합을 띄우는 기능
Health / Ready프로세스 생존(/health)과 트래픽 수용 준비(/ready)를 분리한 운영 엔드포인트. K8s probe에 매핑
레이트 리미팅과도한 요청을 제한하는 보안 기능. CIDR 화이트리스트·감사 로깅과 함께 제공
BAN (차단)Meta가 비공식 클라이언트로 의심되는 번호를 차단하는 것. 자동화 전용 번호 사용으로 위험 분리

11참고 링크

더 깊이 파고들 때 유용한 자료들