Cloudflare Tunnel은 외부에서 내 서버(로컬 PC나 내부 네트워크)로 안전하게 접속할 수 있도록 해주는 터널링 서비스입니다. 쉽게 말해, 공인 IP가 없거나 공유기 뒤에 숨겨진 로컬 서버를 외부 인터넷에서 누구나 접근할 수 있는 경로로 연결해 주는 다리라고 생각하시면 됩니다.
상세한 특징과 작동 원리는 다음과 같습니다.
기본적으로 외부에서 서버에 접속하려면 서버의 IP 주소를 알아야 하고, 공유기에서 포트 포워딩을 설정해야 합니다. 하지만 Cloudflare Tunnel은 이 방식을 완전히 뒤집습니다.
- 아웃바운드 연결 (Outbound Connection): 서버가 외부에서 들어오는 요청을 기다리는 것이 아니라, 서버에 설치된 작은 프로그램(
cloudflared)이 Cloudflare의 서버로 먼저 아웃바운드 연결을 생성합니다.- 포트 포워딩 불필요: 외부에서 요청이 들어오면 Cloudflare가 이미 뚫려 있는 이 터널을 통해 로컬 서버로 데이터를 전달합니다. 따라서 방화벽 설정을 건드릴 필요가 없고, 포트 포워딩이나 공인 IP가 없어도 서비스 운영이 가능합니다.
- 보안성 강화: 서버의 실제 IP 주소가 외부로 절대 노출되지 않습니다. 공격자가 서버를 직접 공격하려 해도 Cloudflare의 거대한 방화벽과 DDoS 방어 시스템을 먼저 거쳐야 하므로 보안 수준이 매우 높습니다.
- 쉬운 관리: 복잡한 네트워크 설정 없이 도메인만 연결하면 즉시 외부에서 접속 가능한 상태가 됩니다. SSL 인증서(HTTPS)도 Cloudflare가 자동으로 관리해 주기 때문에 서버에서 별도로 설정할 번거로움이 없습니다.
- 무료 및 안정성: 개인 개발자나 소규모 프로젝트 수준에서는 무료로도 매우 강력한 기능을 제공하며, 전 세계에 분산된 Cloudflare의 네트워크를 통해 연결되어 접속 속도와 안정성이 매우 뛰어납니다.
개인 개발자가 집에서 만든 웹 서비스나 로컬 PC의 API 서버(사용자님의 경우
ai_server.py같은 환경)를 밖에서 테스트하거나 다른 사람에게 보여주고 싶을 때 매우 유용합니다. 공유기를 사용하는 일반 가정 환경에서도 도메인(예: openipc.kr)만 있으면 누구나 접속할 수 있는 공개 서버처럼 운영할 수 있습니다.
목차
1. Cloudflare Tunnel이란? ngrok 비교
Cloudflare Tunnel로 로컬 서버를 외부에 공개한다는 것은, 공인 IP나 포트 포워딩 없이 로컬 PC에서 실행 중인 서버를 인터넷에 안전하게 노출시키는 기술을 의미합니다. cloudflared 클라이언트가 Cloudflare의 글로벌 엣지 네트워크와 암호화된 아웃바운드 연결을 유지하고, 외부에서 들어오는 요청을 해당 터널을 통해 로컬 서버로 전달합니다.
Quick Tunnel vs Named Tunnel
Cloudflare Tunnel에는 두 가지 방식이 있습니다.
| 구분 | Quick Tunnel | Named Tunnel |
|---|---|---|
| 실행 명령 | cloudflared tunnel --url localhost:8000 | cloudflared tunnel run [이름] |
| 도메인 | 임시 랜덤 URL (재시작 시 변경) | 고정 도메인 (예:ai.qcai.kr 등) |
| DNS 설정 | 자동 (임시) | 수동 CNAME 등록 (영구) |
| 설정 파일 | 불필요 | config.yml 필요 |
| 운영 환경 적합성 | 테스트용 | 프로덕션 운영 권장 |
이번에는 Named Tunnel 방식을 사용합니다. 고정 도메인이 유지되어야 워드프레스에서 API URL을 하드코딩할 수 있고, 서버 재시작 후에도 동일한 주소로 접근할 수 있기 때문입니다.
ngrok 대비 Cloudflare Tunnel의 핵심 장점 3가지
ngrok은 무료 플랜에서도 고정 도메인을 1개 제공합니다. 다만 Cloudflare Tunnel과 비교하면 무료 플랜 특유의 제약이 있습니다. 무료 사용자는 일일 데이터 전송량과 동시 연결 수에 제한을 받으며, 접속 시 ngrok이 띄우는 브라우저 경고 페이지를 우회하기 위해 별도의 헤더 설정이 필요합니다. 또한 트래픽 분석이나 복잡한 보안 인증 같은 고급 관리 기능은 유료 플랜에서만 지원된다는 점을 참고해야 합니다.
- 무료 영구 커스텀 도메인: ngrok 무료 플랜은 커스텀 도메인은 유료입니다. Cloudflare Tunnel은 본인 소유 도메인을 무료로 연결할 수 있습니다.
- Cloudflare CDN + DDoS 보호 내장: 터널 트래픽이 Cloudflare 엣지를 통과하므로 자동으로 DDoS 방어, SSL 종단, WAF(웹 애플리케이션 방화벽) 혜택을 받습니다.
- Zero Trust 보안 통합 가능: Cloudflare Access와 연동하면 특정 사용자 또는 IP만 터널에 접근하도록 인증 레이어를 추가할 수 있습니다. ngrok의 IP 화이트리스트와 달리 이메일 OTP, SSO, GitHub 인증 등 다양한 방식이 무료로 지원됩니다.
이 방법을 완료하면 다음 환경이 구성됩니다.
- 로컬 Windows PC에서 실행 중인 llama.cpp AI 모델 + FastAPI 서버 2개
cloudflaredNamed Tunnel로https://ai.qcai.kr도메인에 외부 공개- 워드프레스 채팅 UI에서
https://ai.qcai.kr/api/chat엔드포인트 직접 호출 - 배치 파일 하나로 전체 서버 스택 원클릭 시작/종료 자동화
2. 전체 시스템 아키텍처: 로컬 AI 서버부터 워드프레스까지
Cloudflare Tunnel로 로컬 AI 서버를 외부에 공개할 때의 전체 데이터 흐름을 먼저 파악하면 각 설정 단계의 목적이 명확해집니다.
외부 사용자 (모바일/PC)
│
▼
워드프레스 (프론트엔드)
https://qcai.kr/ai-챗봇
JavaScript: fetch('https://ai.qcai.kr/api/chat')
│
▼
Cloudflare Tunnel (cloudflared)
https://ai.qcai.kr → localhost:8000
│
▼
로컬 PC (Windows)
web_app.py (FastAPI:8000)
DB 검색
AI 서버 호출
│
▼
ai_server.py (FastAPI:8001)
AI 응답 생성
│
▼
llama-server (AI 모델:11434)
실제 AI 추론
각 레이어의 역할
Layer 1 — 외부 사용자: 모바일 또는 PC 브라우저에서 워드프레스 챗봇 페이지에 접속합니다.
Layer 2 — 워드프레스 (프론트엔드): https://qcai.kr/ai-챗봇 페이지에 삽입된 JavaScript가 사용자 입력을 받아 https://ai.qcai.kr/api/chat으로 POST 요청을 보냅니다. 이 URL은 Cloudflare Tunnel을 통해 로컬 서버로 라우팅됩니다.
Layer 3 — Cloudflare Tunnel: cloudflared 데몬이 로컬 PC에서 Cloudflare 엣지 서버와 암호화된 아웃바운드 연결을 유지합니다. 외부에서 ai.qcai.kr로 들어오는 요청을 localhost:8000으로 전달합니다. 인바운드 포트를 열 필요 없이 아웃바운드 연결만으로 동작하는 것이 핵심입니다.
Layer 4 — web_app.py (FastAPI:8000): 외부의 진입점 역할을 합니다. 사용자 쿼리를 받아 벡터 DB에서 관련 문서를 검색하고, ai_server.py에 AI 응답 생성을 요청한 뒤 결과를 합쳐 반환합니다.
Layer 5 — ai_server.py (FastAPI:8001): web_app.py로부터 내부 호출을 받아 llama-server에 실제 추론을 요청하고 생성된 텍스트를 반환합니다. 외부에서는 직접 접근할 수 없으며, web_app.py를 통해서만 호출됩니다.
Layer 6 — llama-server (포트 11434): llama.cpp 기반의 AI 추론 엔진입니다. GPU(CUDA)를 활용해 GGUF 포맷의 AI 모델을 로드하고 텍스트를 생성합니다.
3. 사전 요구사항: Windows 환경 구성 요소 정리
3.1. 로컬 PC (Windows) 디렉토리 구조
# 디렉토리 구조
G:\AI_server\
├── ai_server.py # AI API 서버 (8001 포트)
├── tts_engine.py # TTS 엔진
└── stt_module.py # STT 엔진
G:\main_project\
├── web_app.py # FastAPI 웹 서버 (8000 포트)
├── db_search.py # DB 검색 엔진
└── db/ # 벡터 DB
└── main_vector_db/
G:\llama-b9354-bin-win-cuda-13.1-x64\
└── llama-server.exe # AI 모델 실행기
G:\ngrok\ # ngrok
└── ngrok.exe3.2. 필요한 파일 및 역할
| 파일 | 설명 |
|---|---|
ai_server.py | FastAPI 기반 AI 응답 생성 API 서버. llama-server에 추론 요청을 전달하고 결과를 반환 |
web_app.py | FastAPI 기반 웹 서버. 벡터 DB 검색 + ai_server.py 호출 + 웹 UI 제공 |
llama-server.exe | llama.cpp의 HTTP API 서버. GGUF 모델을 로드하여 텍스트 생성 수행 |
ssh-key-2026-06-20.key | SSH 키 (오라클 서버 접속용, 선택 사항) |
3.3. 사용 기술 스택 요약
| 구성 요소 | 기술 | 역할 |
|---|---|---|
| AI 서버 | FastAPI + llama.cpp | AI 응답 생성 |
| 웹 서버 | FastAPI (web_app.py) | DB 검색 + AI 호출 |
| 외부 노출 | Cloudflare Tunnel | 로컬 서버 → 외부 연결 |
| 프론트엔드 | WordPress + JavaScript | 프로그램 UI |
4. Step 1: cloudflared 다운로드 및 설치 (Windows 64-bit)
cloudflared는 Cloudflare Tunnel의 핵심 클라이언트 프로그램입니다. 로컬 PC에 설치하면 Cloudflare 네트워크와의 아웃바운드 연결을 유지하여 외부 트래픽을 로컬 서버로 전달합니다.
4.1. 다운로드
두 가지 방법 중 하나를 선택합니다.
방법 A: Cloudflare 공식 다운로드 페이지 (권장)
https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/→ “Windows 64-bit MSI” 파일 다운로드 후 설치
방법 B: GitHub Releases (최신 버전 또는 특정 버전)
https://github.com/cloudflare/cloudflared/releases→ cloudflared-windows-amd64.msi 또는 cloudflared-windows-amd64.exe 다운로드
MSI vs EXE 차이: MSI 설치 파일은 Windows PATH에 자동으로
cloudflared명령을 등록하므로 어느 디렉토리에서든 명령을 실행할 수 있습니다. EXE 단독 파일은 해당 경로에서만 직접 실행 가능합니다. 운영 환경에서는 MSI 설치를 권장합니다.
4.2. 설치 확인
cloudflared --version
# 출력 예시
cloudflared version 2026.6.1 (built 2026-06-18T06:39 UTC)버전 정보가 출력되면 설치가 정상적으로 완료된 것입니다.
4.3. Cloudflare 계정 인증 (Named Tunnel용)
Named Tunnel을 생성하려면 먼저 Cloudflare 계정에 인증해야 합니다.
cloudflared tunnel login이 명령을 실행하면 다음 순서로 진행됩니다.
- 브라우저 자동 오픈: 기본 브라우저에서 Cloudflare 로그인 페이지가 열립니다.
- 계정 로그인: Cloudflare 계정으로 로그인합니다.
- 도메인 선택: 터널과 연결할 도메인(예:
qcai.kr)을 선택합니다. - 인증서 자동 저장: 인증이 완료되면
C:\Users\[사용자명]\.cloudflared\cert.pem파일이 자동 생성됩니다.
주의: 인증에 사용한 도메인이 Cloudflare에서 관리되는 도메인이어야 합니다. 외부 레지스트라에서 구매한 도메인이라면 네임서버를 Cloudflare로 변경해야 합니다.
5. Step 2: Named Tunnel 생성과 DNS 라우팅 설정
Cloudflare Tunnel로 로컬 AI 서버에 고정 도메인을 연결하는 핵심 단계입니다.
5.1. Named Tunnel 생성
cloudflared tunnel create ai-tunnel실행 결과:
Tunnel credentials written to C:\Users\ar\.cloudflared\0f5-331d-4a69-a4e-067.json
Created tunnel ai-tunnel with id 079-3d-4d-ae-0d67생성 시 자동으로 UUID 형식의 고유 터널 ID가 부여되고, 해당 ID로 된 JSON 인증 파일이 C:\Users\[사용자명]\.cloudflared\ 디렉토리에 저장됩니다. 이 JSON 파일에는 터널 접속에 필요한 자격 증명이 담겨 있으므로 외부에 노출되지 않도록 주의합니다.
5.2. DNS 라우팅 설정
생성한 터널에 실제 도메인 주소를 연결합니다.
cloudflared tunnel route dns ai-tunnel ai.qcai.kr실행 결과:
Added CNAME ai.qcai.kr which will route to this tunnel이 명령은 Cloudflare DNS에 ai.qcai.kr → [터널ID].cfargotunnel.com 형태의 CNAME 레코드를 자동으로 생성합니다. 외부 사용자가 ai.qcai.kr에 접근하면 Cloudflare DNS가 해당 터널로 라우팅하고, 터널은 로컬 서버로 전달합니다.
5.3. 설정 파일 생성 (config.yml)
터널 동작을 제어하는 설정 파일을 작성합니다. C:\Users\arhat\.cloudflared\config.yml 경로에 생성합니다.
yaml
tunnel: ai-tunnel
credentials-file: C:\Users\arhat\.cloudflared\0fa5-3d-49-a48e-0067.json
ingress:
- hostname: ai.qcai.kr
service: http://localhost:8000
- service: http_status:404각 필드 설명:
| 필드 | 설명 |
|---|---|
tunnel | 앞서 생성한 Named Tunnel의 이름 |
credentials-file | Step 5.1에서 생성된 JSON 자격 증명 파일 경로 |
ingress[0].hostname | 외부에서 접근할 도메인. Cloudflare에 등록된 도메인이어야 함 |
ingress[0].service | 요청을 전달할 로컬 서버 주소와 포트 |
ingress[-1].service | 위 hostname 규칙에 매칭되지 않는 요청에 대한 기본 처리 (404 반환) |
config.yml 고급 옵션 (필요 시 추가):
yaml
tunnel: ai-tunnel
credentials-file: C:\Users\arhat\.cloudflared\0a5-3d-4-a48e-027.json
ingress:
- hostname: ai.qcai.kr
service: http://localhost:8000
originRequest:
connectTimeout: 30s # 로컬 서버 연결 타임아웃 (기본: 30s)
noTLSVerify: false # 로컬 서버가 자체 서명 SSL인 경우 true로 설정
httpHostHeader: ai.qcai.kr # 로컬 서버에 전달하는 Host 헤더
- service: http_status:4045.4. Cloudflare 대시보드 DNS 설정 확인
DNS 라우팅 명령 실행 후 Cloudflare 대시보드 → DNS 탭에서 다음과 같이 레코드가 생성되었는지 확인합니다.
| Type | Name | Target | Proxy Status |
|---|---|---|---|
| CNAME | ai | 0f7973a5-331d-4a69-a48e-023b0f6f0767.cfargotunnel.com | Proxied |
Proxy Status가 “Proxied”(주황 구름 아이콘)로 설정되어 있어야 Cloudflare의 SSL 종단과 DDoS 보호가 적용됩니다. “DNS only”로 되어 있으면 수동으로 변경합니다.
6. Step 3: 로컬 FastAPI AI 서버 준비 및 실행
Cloudflare Tunnel이 트래픽을 전달할 로컬 서버들을 준비합니다. 터미널 3개를 순서대로 실행합니다.
6.1. llama-server 실행 (AI 모델 추론 엔진)
# 터미널 1
G:\llama-b9354-bin-win-cuda-13.1-x64\llama-server.exe -m "G:\AI_Study\Ai_model\google_gemma-4-E4B-it-Q8_0\google_gemma-4-E4B-it-Q8_0.gguf" --port 11434 --host 127.0.0.1 -ngl 50주요 파라미터 설명:
| 파라미터 | 설명 |
|---|---|
-m [경로] | 로드할 GGUF 모델 파일 경로. 여기서는 Google Gemma 4E4B Q8_0 양자화 모델 사용 |
--port 11434 | llama-server가 수신할 포트 번호 |
--host 127.0.0.1 | 로컬호스트에서만 수신하도록 제한 (보안상 외부에서 직접 접근 불가) |
-ngl 50 | GPU 레이어 오프로드 수. CUDA GPU에 오프로드할 레이어 수를 지정. 높을수록 GPU 메모리를 더 사용하지만 추론 속도가 빠름. VRAM 용량에 맞게 조정 |
-ngl설정 팁: GPU VRAM이 8GB라면-ngl 32~40, 12GB 이상이면-ngl 50또는 그 이상 설정 가능합니다.-ngl 0으로 설정하면 CPU 전용 추론이 됩니다.
http://localhost:4040/inspect/http는 ngrok이 제공하는 로컬 대시보드(Web Interface) 주소입니다.ngrok을 실행하면 로컬 PC에서 ngrok이 운영하는 서비스의 상태를 실시간으로 확인하고 관리할 수 있도록 별도의 관리 페이지를 띄우는데, 기본 포트가 4040번입니다.

6.2. ai_server.py 실행 (AI API 서버)
# 터미널 2
cd G:\AI_server
conda activate ai_venv
python ai_server.py
# 실행 결과
INFO: Uvicorn running on http://0.0.0.0:8001ai_server.py는 llama-server에 대한 프록시 역할을 합니다. web_app.py로부터 쿼리를 받아 llama-server의 /completion 엔드포인트를 호출하고, 생성된 텍스트를 구조화된 JSON으로 반환합니다.
6.3. web_app.py 실행 (웹 서버 + DB 검색)
# 터미널 3
cd G:\buddha_project
conda activate ai_venv
python web_app.py
# 실행 결과
INFO: Uvicorn running on http://0.0.0.0:8000web_app.py는 외부에서 접근하는 실제 API 진입점입니다. 포트 8000에서 요청을 수신하고, 벡터 DB에서 관련 문서를 검색한 뒤 ai_server.py를 내부 호출하여 최종 응답을 조합합니다. Cloudflare Tunnel은 이 포트로 트래픽을 전달합니다.
6.4. 로컬 서버 동작 확인
세 서버가 모두 실행된 후 로컬에서 정상 동작을 확인합니다.
# web_app.py 상태 확인
curl http://localhost:8000/api/health
# ai_server.py 상태 확인
curl http://localhost:8001/health정상 응답:
json
{"status":"ok","db_connected":true,"ai_server_connected":true}두 엔드포인트에서 JSON 응답이 오면 로컬 서버 스택이 정상 동작 중인 것입니다. 이 상태에서 Cloudflare Tunnel을 실행하면 외부 접근이 가능해집니다.
7. Step 4: Cloudflare Tunnel 실행 및 외부 접속 확인
로컬 서버가 모두 정상 동작하는 것을 확인한 후, Cloudflare Tunnel을 실행하여 외부 연결을 활성화합니다.
7.1. Tunnel 실행
cloudflared tunnel run ai-tunnel정상 실행 시 출력:
INF Connection established for hostname ai.qcai.kr
INF Tunnel connected to https://ai.qcai.krcloudflared는 실행과 동시에 Cloudflare 엣지 서버와 4개의 병렬 연결을 수립합니다. 하나의 연결이 끊어지더라도 나머지 연결로 서비스가 유지되므로 안정성이 높습니다.
7.2. 외부에서 Cloudflare Tunnel 경유 API 호출 테스트
로컬 서버와 터널이 모두 실행 중인 상태에서 외부 접근을 테스트합니다.
# Cloudflare Tunnel 경유 외부 접속 테스트
curl https://ai.qcai.kr/api/healthcurl에서 정상 JSON 응답이 반환되면 로컬 AI 서버가 Cloudflare Tunnel을 통해 외부에 공개된 것입니다.
실행 순서 중요:
cloudflared tunnel run은 반드시web_app.py가 실행된 후에 시작합니다. 로컬 서버가 없는 상태에서 터널만 실행하면 521 오류가 발생합니다.
8. Step 5: 워드프레스에서 Cloudflare Tunnel API 호출 연동
워드프레스 채팅 페이지에서 Cloudflare Tunnel 도메인을 통해 로컬 AI 서버를 호출하도록 구성합니다.
8.1. API_URL 설정
javascript
// 🔥 Cloudflare Tunnel 도메인으로 고정
const API_URL = 'https://ai.qcai.kr';8.2. 전체 워드프레스 코드
html
<!-- AI Buddha Chat - Cloudflare Tunnel Ver -->
<div id="ai-buddha-chat" style="max-width:800px;margin:0 auto;font-family:'맑은 고딕',sans-serif;padding:0 10px;">
<!-- ... 헤더, 채팅창, 입력창 ... -->
</div>
<script>
const API_URL = 'https://ai.qcai.kr';
async function sendMessage() {
const query = chatInput.value.trim();
if (!query) return;
try {
const response = await fetch(`${API_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: query,
mode: 'general',
n_results: 5
})
});
const data = await response.json();
// 응답 처리...
} catch (error) {
console.error('Error:', error);
}
}
</script>8.3. 엔드포인트 경로 확인
| 서버 | 엔드포인트 | 메서드 | 설명 |
|---|---|---|---|
| web_app.py | /api/chat | POST | DB 검색 + AI 호출. 외부에서 실제로 호출하는 엔드포인트 |
| web_app.py | /api/health | GET | 서버 전체 상태 확인 (DB 연결 + AI 서버 연결 포함) |
| ai_server.py | /api/chat | POST | AI 응답 생성. web_app.py에서 내부적으로만 호출 |
| ai_server.py | /health | GET | AI 서버 단독 상태 확인 |
8.4. CORS 설정 (FastAPI)
워드프레스(https://qcai.kr)에서 다른 도메인(https://ai.qcai.kr)으로 API를 호출하려면 web_app.py에 CORS 미들웨어가 반드시 설정되어 있어야 합니다.
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)보안 강화 권장 설정: 운영 환경에서는 allow_origins=["*"] 대신 실제 워드프레스 도메인만 허용하도록 제한하면 다른 출처에서의 API 무단 호출을 차단할 수 있습니다.
app.add_middleware(
CORSMiddleware,
allow_origins=["https://qcai.kr"], # 워드프레스 도메인만 허용
allow_credentials=True,
allow_methods=["POST", "GET"], # 필요한 메서드만 허용
allow_headers=["Content-Type"], # 필요한 헤더만 허용
)9. Step 6: 배치 파일로 전체 서버 자동 실행 자동화
매번 터미널 4개를 열어 서버를 수동 실행하는 번거로움을 없애기 위해, 배치 파일 하나로 전체 스택을 시작하고 종료할 수 있도록 구성합니다.
9.1. start_ai_server.bat (통합 실행)
batch
@echo off
chcp 65001 > nul
title AI Server + Cloudflare Tunnel (통합)
echo ============================================================
echo AI Server + Cloudflare Tunnel 실행
echo ============================================================
echo.
set AI_SERVER_PATH=G:\AI_server
set BUDDHA_PROJECT_PATH=G:\buddha_project
set CONDA_ENV=ai_venv
set AI_STUDY_PATH=G:\AI_Study
:: llama-server 경로
set LLAMA_SERVER_PATH=G:\llama-b9354-bin-win-cuda-13.1-x64
:: ngrok 경로
set NGROK_PATH=G:\ngrok
:: 로그 폴더 생성
if not exist "%AI_SERVER_PATH%\logs" mkdir "%AI_SERVER_PATH%\logs"
:: ============================================================
:: 1. 기존 프로세스 종료
:: ============================================================
echo [1/7] 기존 프로세스 정리 중...
taskkill /f /im python.exe /t >nul 2>&1
taskkill /f /im llama-server.exe /t >nul 2>&1
taskkill /f /im ngrok.exe /t >nul 2>&1
taskkill /f /im cloudflared.exe /t >nul 2>&1
timeout /t 2 /nobreak >nul
:: ============================================================
:: 2. Conda 환경 활성화
:: ============================================================
echo [2/7] Conda 환경 활성화 중...
call conda activate %CONDA_ENV%
:: ============================================================
:: 3. llama-server 실행
:: ============================================================
echo [3/7] llama-server 실행 중... (포트 11434)
start "llama-server" /min "%LLAMA_SERVER_PATH%\llama-server.exe" -m "G:\AI_Study\Ai_model\google_gemma-4-E4B-it-Q8_0\google_gemma-4-E4B-it-Q8_0.gguf" --port 11434 --host 127.0.0.1 -ngl 50
timeout /t 5 /nobreak >nul
:: ============================================================
:: 4. AI Server 실행 (ai_server.py)
:: ============================================================
echo [4/7] AI Server 실행 중... (포트 8001)
set PYTHONIOENCODING=utf-8
start "AI Server" /min cmd /c "cd /d %AI_SERVER_PATH% && conda activate %CONDA_ENV% && python -X utf8 ai_server.py"
timeout /t 5 /nobreak >nul
:: ============================================================
:: 5. web_app.py 실행 (FastAPI)
:: ============================================================
echo [5/7] web_app.py 실행 중... (포트 8000)
start "web_app" /min cmd /c "cd /d %BUDDHA_PROJECT_PATH% && conda activate %CONDA_ENV% && python -X utf8 web_app.py"
timeout /t 5 /nobreak >nul
:: ============================================================
:: 6. ngrok 실행 (ai_server.py 노출용)
:: ============================================================
echo [6/7] ngrok 실행 중... (포트 8001)
start "ngrok" /min "%NGROK_PATH%\ngrok.exe" http 8001
timeout /t 3 /nobreak >nul
:: ============================================================
:: 7. Cloudflare Tunnel 실행
:: ============================================================
echo [7/7] Cloudflare Tunnel 실행 중... (ai.qcai.kr)
start "Cloudflare Tunnel" /min cloudflared tunnel run ai-tunnel
:: ============================================================
:: 8. 실행 정보 출력
:: ============================================================
timeout /t 3 /nobreak >nul
cls
echo ============================================================
echo AI Server + Cloudflare Tunnel 실행 완료!
echo ============================================================
echo.
echo 📡 AI Server: http://localhost:8001
echo 📡 web_app: http://localhost:8000
echo 📡 상태 확인: http://localhost:8000/api/health
echo 🌐 외부 접속: https://ai.qcai.kr
echo 🔗 ngrok 대시보드: http://localhost:4040
echo.
echo ⏹ 종료: stop_ai_server.bat 실행
echo ============================================================
echo.
pause배치 파일 실행 흐름 설명:
- [1/6] 기존 프로세스 정리: 이전에 실행 중이던 Python, llama-server, cloudflared 프로세스를 강제 종료하여 포트 충돌을 방지합니다.
- [2/6] Conda 환경 활성화:
ai_venv가상 환경을 활성화하여 FastAPI, uvicorn 등 필요한 패키지를 사용합니다. - [3/6] llama-server: GPU 모델 로드에 시간이 걸리므로 가장 먼저 실행하고 5초 대기합니다.
- [4/6] ai_server.py: llama-server가 준비된 후 AI API 서버를 실행하고 5초 대기합니다.
- [5/6] web_app.py: ai_server.py가 준비된 후 웹 서버를 실행하고 5초 대기합니다.
- [6/6] cloudflared: 로컬 서버가 모두 준비된 후 마지막으로 터널을 연결합니다.
9.2. stop_ai_server.bat (종료)
batch
@echo off
chcp 65001 > nul
title AI Server 종료
taskkill /f /im python.exe /t >nul 2>&1
taskkill /f /im llama-server.exe /t >nul 2>&1
taskkill /f /im cloudflared.exe /t >nul 2>&1
echo 모든 서비스 종료 완료!
pause9.3. Windows 서비스로 등록 (선택 — PC 시작 시 자동 실행)
PC 재시작 후에도 cloudflared가 자동으로 실행되도록 Windows 서비스로 등록할 수 있습니다.
# 관리자 권한 PowerShell에서 실행
cloudflared service install
# 서비스 시작
cloudflared service start
# 서비스 상태 확인
sc query cloudflared서비스로 등록하면 Windows 시작 시 자동으로 cloudflared가 실행되어 터널이 유지됩니다. 단, Python FastAPI 서버(web_app.py, ai_server.py)와 llama-server는 별도로 자동 실행을 설정해야 합니다. 이를 위해 start_ai_server.bat을 Windows 작업 스케줄러에 등록하거나, 시작 프로그램 폴더(shell:startup)에 추가하는 방법을 사용할 수 있습니다.
cloudflared service 사용 시 주의:
service install명령은 현재cloudflared의 기본 설정 파일(C:\Users\[사용자명]\.cloudflared\config.yml)을 사용합니다. 설정 파일 경로가 달라질 경우 서비스 등록 명령에--config플래그로 명시해야 합니다.
10. 트러블슈팅: 521, 502, 404, CORS, 422 오류 완벽 해결
Cloudflare Tunnel + FastAPI 운영 시 자주 발생하는 오류 유형과 정확한 원인, 해결 방법을 정리합니다.
10.1. 521 오류 (Cloudflare → 웹 서버 연결 실패)
오류 의미: Cloudflare 엣지 서버가 localhost:8000에 연결을 시도했으나 거부됨. web_app.py가 실행 중이지 않을 때 발생합니다.
진단 명령어:
# web_app.py 실행 여부 확인
netstat -ano | findstr :8000해결:
cd G:\buddha_project
conda activate ai_venv
python web_app.py포트 8000에서 수신 대기 중(LISTENING)인 프로세스가 없으면 web_app.py를 재실행합니다.
10.2. 502 오류 (Cloudflare Tunnel 연결 실패)
오류 의미: cloudflared 자체가 실행 중이지 않거나 Cloudflare 엣지와의 연결이 끊어진 상태입니다. 터널이 없으니 요청이 로컬까지 도달하지 못합니다.
진단 명령어:
# cloudflared 실행 여부 확인
tasklist | findstr cloudflared해결:
cloudflared tunnel run ai-tunnel터널을 재실행한 후 로그에서 INF Connection established 메시지를 확인합니다.
10.3. 404 오류 (API 경로 없음)
오류 의미: web_app.py는 실행 중이지만 /api/chat 엔드포인트가 정의되어 있지 않거나 경로가 다를 때 발생합니다.
진단 방법: FastAPI의 자동 생성 문서에서 엔드포인트 목록을 확인합니다.
http://localhost:8000/docs해결: web_app.py에서 엔드포인트 데코레이터를 확인합니다.
@app.post("/api/chat")
async def chat(request: ChatRequest):
# 엔드포인트 존재 확인경로가 /chat 또는 /api/v1/chat 등 다른 형태로 정의되어 있으면 워드프레스 JavaScript의 fetch URL과 일치시킵니다.
10.4. CORS 오류 (브라우저 콘솔)
오류 메시지 예시:
Access to fetch at 'https://ai.qcai.kr/api/chat' from origin 'https://qcai.kr' has been blocked by CORS policy오류 의미: 브라우저가 다른 출처(도메인)로의 요청을 차단합니다. web_app.py에 CORS 허용 설정이 없거나 허용 출처에 워드프레스 도메인이 누락된 경우입니다.
해결: web_app.py에 CORS 미들웨어를 추가합니다.
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)추가 확인:
app.add_middleware()는 반드시 라우터 등록(@app.post) 이전에 위치해야 합니다. 미들웨어 등록 순서가 잘못되면 CORS 헤더가 응답에 포함되지 않을 수 있습니다.
10.5. 422 오류 (데이터 형식 불일치)
오류 의미: HTTP 요청 자체는 서버에 도달했지만, 요청 본문의 필드명 또는 타입이 FastAPI의 Pydantic 모델 정의와 맞지 않을 때 발생합니다. web_app.py에서 ai_server.py를 내부 호출할 때 필드명이 다르면 발생합니다.
원인 파악: ai_server.py의 Pydantic 모델 정의 확인
# ai_server.py의 요청 모델
class ChatRequest(BaseModel):
query: str # 'query' 필드를 기대함
mode: str = "general"
n_results: int = 5해결: web_app.py의 call_ai_server() 함수에서 필드명을 맞춥니다.
# 수정 전 (잘못된 필드명)
json={"message": message}
# 수정 후 (ai_server.py가 기대하는 필드명)
json={"query": message} # ai_server.py는 query 필드 기대422 오류 발생 시 FastAPI가 반환하는 오류 응답 본문에 어떤 필드가 잘못됐는지 상세히 나타나므로, curl -v 또는 브라우저 개발자 도구의 네트워크 탭에서 응답 본문을 확인하면 빠르게 원인을 파악할 수 있습니다.
11. 최종 확인 체크리스트: 로컬부터 외부 접속까지
모든 설정이 완료된 후 단계별로 연결 상태를 확인합니다.
11.1. 로컬 서버 개별 테스트
# ai_server.py 단독 상태 확인
curl http://localhost:8001/health
# web_app.py + DB + AI 연결 통합 상태 확인
curl http://localhost:8000/api/health11.2. Cloudflare Tunnel 경유 외부 접속 테스트
# Cloudflare Tunnel 통해 외부 접근 확인
curl https://ai.qcai.kr/api/health11.3. 워드프레스 채팅 UI 접속 테스트
https://qcai.kr/ai-챗봇브라우저에서 위 URL에 접속하여 채팅 입력창이 표시되고, 메시지를 전송했을 때 AI 응답이 정상 반환되는지 확인합니다.
11.4. 전체 연결 구조 확인
| 단계 | 명령어 | 정상 응답 |
|---|---|---|
| 1 | curl http://localhost:8001/health | JSON 응답 |
| 2 | curl http://localhost:8000/api/health | JSON 응답 |
| 3 | curl https://ai.qcai.kr/api/health | JSON 응답 |
| 4 | 워드프레스 페이지 접속 | 채팅 UI 표시 |
최종 시스템 상태
| 구성 요소 | 주소 | 상태 |
|---|---|---|
| llama-server | localhost:11434 | 실행 중 |
| ai_server.py | localhost:8001 | 실행 중 |
| web_app.py | localhost:8000 | 실행 중 |
| Cloudflare Tunnel | https://ai.qcai.kr | 연결됨 |
| 워드프레스 | https://qcai.kr/ai-챗봇 | 접속 가능 |
핵심 포인트 정리
- 공인 IP·포트 포워딩 불필요:
cloudflared가 아웃바운드 연결만으로 외부 접근을 활성화합니다. - FastAPI 서버 2개 운영:
web_app.py(포트 8000)는 외부 진입점,ai_server.py(포트 8001)는 내부 AI 처리 전용으로 역할을 분리합니다. - Named Tunnel = 고정 도메인: Quick Tunnel 임시 URL과 달리 재시작 후에도
ai.qcai.kr주소가 유지됩니다. - 실행 순서 준수:
llama-server→ai_server.py→web_app.py→cloudflared순서로 실행해야 의존성 오류 없이 동작합니다. - 배치 파일로 운영 자동화:
start_ai_server.bat하나로 전체 스택을 원클릭 시작,stop_ai_server.bat으로 즉시 종료합니다. - CORS 보안 강화: 운영 환경에서는
allow_origins=["https://qcai.kr"]으로 제한하여 허가되지 않은 도메인의 API 호출을 차단합니다.
이제 로컬 PC에서 실행 중인 AI 서버를 Cloudflare Tunnel로 안전하고 빠르게 외부에 공개할 수 있습니다