AI를 활용해 나만의 서비스를 구축하고 나면, 외부에서 내 AI 서비스에 접속할 수 있도록 만드는 것이 필수적입니다. 하지만 일반적인 클라우드 서비스는 무료 제공량에 한계가 있고, 유료 이용 시 매번 발생하는 비용이 부담될 수 있습니다.
이때 클라우드 서버 비용 없이 개인 컴퓨터를 활용하는 방법이 있습니다. 고성능 GPU가 탑재된 컴퓨터를 24시간 클라우드 서버에 맡기면 비용이 상당하지만, 이미 집에 좋은 PC가 있다면 상황은 달라집니다. 로컬 AI 서버를 외부에서 접속할 수 있게 해주는 도구인 FastAPI와 ngrok을 활용하면, 내 컴퓨터를 그대로 AI 백엔드 서버로 전환할 수 있습니다.
이번에는 불교 경전 DB와 Gemma4 AI 모델을 결합한 AI 불교 상담사를 FastAPI로 구축하고, ngrok 터널을 통해 외부 인터넷에 공개한 뒤, 워드프레스 블로그 페이지에 실시간 챗봇으로 연동하는 전 과정을 단계별로 설명합니다.
목차
내 PC로 AI 서버 시스템 구조 한눈에 보기
본격적인 구현에 앞서 전체 데이터 흐름을 파악하는 것이 중요하죠! AI 개인 서버 시스템은 크게 세 계층으로 구성됩니다.
[워드프레스 블로그]
│
▼ (사용자 질문 입력)
[FastAPI 서버 - 내 컴퓨터]
│
├── DB 검색 (ChromaDB + BGE-M3)
├── AI 응답 생성 (Gemma4)
└── 결과 반환
│
▼ (ngrok 터널)
[외부 인터넷 접속]블로그 방문자가 AI 챗팅 창에서 질문을 입력하면, ngrok이 그 요청을 내 컴퓨터의 FastAPI 서버로 전달합니다. FastAPI 서버는 이미 구축한 ChromaDB에서 관련 경전을 벡터 검색으로 찾아오고, Gemma4 AI 모델이 이를 바탕으로 응답을 생성한 뒤 블로그에 결과를 돌려줍니다. 이 전체 과정이 클라우드 비용 없이 내 컴퓨터 안에서 이루어집니다.
1. FastAPI와 ngrok이란? — 개념 먼저 이해하기
AI 서버 코드 작성 전에 두 핵심 도구FastAPI와 ngrok를 정확히 이해하는 것이 중요합니다.
1.1 FastAPI = “레스토랑 주방”
FastAPI는 Python으로 만드는 파이썬 AI 웹서버 프레임워크입니다. Python 3.6+ 환경에서 동작하며, 내부적으로 Starlette(ASGI 프레임워크)를 기반으로 구축되어 있고, Pydantic으로 요청/응답 데이터를 자동 검증합니다. ASGI(Asynchronous Server Gateway Interface) 방식으로 동작하기 때문에 uvicorn과 함께 사용하면 비동기 처리로 여러 요청을 효율적으로 처리할 수 있습니다.
| 비유 | 설명 |
|---|---|
| 주방 | FastAPI 서버 (Python 코드 실행) |
| 요리사 | AI 모델 (Gemma4) |
| 재료 | DB (ChromaDB) |
| 요리 | AI 응답 |
FastAPI의 핵심 기능:
- 웹에서 API 요청(
/api/chat) 수신 - AI 모델 실행 및 DB 검색
- 결과를 JSON 형태로 반환
/docs경로에서 Swagger UI 자동 생성 (개발 중 테스트에 매우 유용)
1.2 ngrok = “배달 대행 서비스”
ngrok은 FastAPI ngrok 터널을 만들어주는 도구입니다. 내 컴퓨터는 공유기(NAT) 뒤에 있어 외부 인터넷에서 직접 접속할 수 없습니다. ngrok은 이 방화벽과 NAT 문제를 우회해 로컬 서버를 인터넷에 공개하는 역할을 합니다.
| 비유 | 설명 |
|---|---|
| 내 컴퓨터 주소 | http://localhost:8000 (집 주소) |
| ngrok 주소 | https://xxxx.ngrok-free.dev (공개 주소) |
| 배달 대행 | 외부에서 내 컴퓨터로 연결 중개 |
ngrok의 핵심 기능:
localhost:8000을 인터넷에 공개- 외부에서 접속 가능한 HTTPS URL 자동 생성
- 무료 플랜: 보안 HTTPS 제공 (단, 세션당 2시간 제한, 매 실행마다 URL 변경)
- 유료(Pro) 플랜: 고정 도메인, 무제한 세션 지원
1.3 왜 FastAPI와 ngrok 둘 다 필요한가?
───────────────┐
│ [워드프레스 블로그]
│ 사용자가 질문 입력
└──────────────┘
│
▼ (ngrok 주소로 요청)
──────────────────┐
│ [ngrok] = 배달 대행 서비스
│ https://xxxx.ngrok-free.dev → http://localhost:8000
└──────────────────┘
│
▼
───────────────────┐
│ [FastAPI] = 레스토랑 주방 (내 컴퓨터)
│
│ │ /api/chat → AI 응답 생성
│ │ DB 검색 → ChromaDB
│ │ AI 모델 → Gemma4
│ └───────────────────────────────────────────| 항목 | FastAPI | ngrok |
|---|---|---|
| 역할 | 웹 서버 (API) | 터널 (외부 연결) |
| 실행 위치 | 내 컴퓨터 | 내 컴퓨터 + ngrok 서버 |
| 접속 주소 | http://localhost:8000 | https://xxxx.ngrok-free.dev |
| 접속 범위 | 내 컴퓨터만 | 인터넷 전체 |
| 보안 | HTTP | HTTPS |
| 필요 이유 | AI 코드를 웹에서 실행 | 외부에서 내 컴퓨터 접속 |
결론: FastAPI만으로는 외부 접속이 불가능하고, ngrok만으로는 실행할 서버 자체가 없습니다. 두 도구는 반드시 함께 사용해야 합니다.
1.4 실제 실행 흐름 4단계
1️⃣ FastAPI 실행
uvicorn web_app:app --host 0.0.0.0 --port 8000→ 내 컴퓨터에서 서버 실행 (http://localhost:8000)
2️⃣ ngrok 실행
ngrok http 8000→ 외부 주소 생성 (https://xxxx.ngrok-free.dev)
3️⃣ 워드프레스 연결
const API_URL = 'https://xxxx.ngrok-free.dev';→ 블로그가 내 컴퓨터 API를 호출
4️⃣ 사용자 요청 처리
블로그 방문자 → ngrok 주소 → FastAPI → AI 응답 → 블로그 표시2. 개발 환경 준비
2.1 Python 가상환경 생성
conda 가상환경 FastAPI 서버 실행을 위해 먼저 독립된 Python 환경을 만듭니다. Anaconda 또는 Miniconda(경량 버전)가 설치되어 있어야 합니다. Python 버전을 3.10으로 지정하는 이유는 이 프로젝트에서 사용하는 torch 2.0.1과의 호환성을 보장하기 위해서입니다.
# Anaconda Prompt 실행
conda create -n ai_venv python=3.12
conda activate ai_venv2.2 필요한 패키지 설치
torch는 CUDA 11.8 버전 기반으로 설치합니다. --index-url https://download.pytorch.org/whl/cu118 옵션을 붙여야 GPU 가속이 지원되는 CUDA 빌드를 정확하게 받을 수 있습니다. 이 옵션 없이 설치하면 CPU 전용 버전이 설치되어 AI 응답 속도가 크게 느려집니다.
# 필수 패키지
pip install fastapi uvicorn python-multipart
pip install chromadb sentence-transformers
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install kiwipiepy chonkie requests
pip install pydantic
# 전체 패키지 한 번에
pip install -r requirements.txt2.3 requirements.txt 파일
프로젝트를 다른 환경에서 재현하거나 팀원과 공유할 때 requirements.txt를 사용합니다. 아래 파일을 프로젝트 루트에 저장해 두세요.
fastapi==0.104.1
uvicorn==0.24.0
python-multipart==0.0.6
chromadb==0.4.22
sentence-transformers==2.2.2
torch==2.0.1
kiwipiepy==0.15.1
chonkie==0.1.0
requests==2.31.0
pydantic==2.5.03. 프로젝트 폴더 구조
아래 구조로 프로젝트를 구성합니다. 각 파일의 역할을 미리 파악해두면 전체 코드를 이해하는 데 훨씬 수월합니다.
G:\buddha_project\
├── web_app.py # FastAPI 서버 메인 코드 (API 엔드포인트 정의)
├── server.bat # FastAPI 서버 실행 배치 파일
├── ngrok.bat # ngrok 터널 실행 배치 파일
├── start_all.bat # 서버 + ngrok 한 번에 실행
├── stop_all.bat # 서버 종료
├── vision_engine.py # AI 엔진 (Gemma4 모델 로드 및 추론)
├── db_search.py # DB 검색 엔진 (ChromaDB + BGE-M3 벡터 검색)
├── db_uploader.py # 경전 데이터를 벡터 DB에 업로드
├── config.py # DB 경로, 모델명 등 설정 값 관리
├── db\ # 벡터 DB 저장 폴더
│ └── buddha_vector_db\
├── data\ # 원본 경전 데이터
│ └── scripture_contents.json
└── summary_cache\ # AI 응답 캐시 폴더 (반복 요청 속도 개선)vision_engine.py는 Gemma4 모델을 로드하고 프롬프트를 받아 텍스트를 생성하는 핵심 AI 엔진입니다. db_search.py는 BGE-M3 임베딩 모델로 사용자 질문을 벡터화하고 ChromaDB에서 유사도 검색을 수행합니다. ChromaDB는 로컬 파일 기반 오픈소스 벡터 데이터베이스로, 별도 서버 설치 없이 폴더에 데이터를 저장합니다. BGE-M3(BAAI/bge-m3)는 다국어를 지원하는 임베딩 모델로, 한국어 처리 성능이 우수해 경전 검색에 적합합니다.
4.1 전체 서버 구조
FastAPI CORS 설정 워드프레스 연동의 핵심은 CORSMiddleware 설정입니다. 워드프레스 블로그 도메인과 내 로컬 서버는 서로 다른 출처(Origin)이므로, 브라우저의 CORS 정책에 의해 기본적으로 API 요청이 차단됩니다. allow_origins=["*"]로 설정하면 어느 도메인에서든 이 API를 호출할 수 있게 됩니다.
"""
web_app.py - FastAPI 서버 (웹 API)
"""
import sys
import os
import traceback
import re
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Dict, Any
# 경로 설정
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 기존 모듈 임포트
from vision_engine import VisionEngine
from db_search import search_scriptures_db
import chromadb
from sentence_transformers import SentenceTransformer
# ============================================================
# 설정
# ============================================================
DB_PATH = "./db/buddha_vector_db"
MODEL_NAME = "BAAI/bge-m3"
# ============================================================
# FastAPI 앱
# ============================================================
app = FastAPI(
title="AI-Powered Buddhist Scripture Knowledge Archive",
description="불교 AI 상담사 웹 API",
version="1.0.0",
)
# CORS 설정 (프론트엔드 접근 허용)
# allow_origins=["*"] → 워드프레스 포함 모든 도메인에서 API 호출 허용
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ============================================================
# 요청/응답 모델 (Pydantic으로 자동 데이터 검증)
# ============================================================
class ChatRequest(BaseModel):
query: str
mode: str = "general"
n_results: int = 5
class ChatResponse(BaseModel):
response: str
scriptures: List[Dict[str, Any]]
is_buddhist: bool
# ============================================================
# 전역 객체
# ============================================================
vision_engine = None
model = None
collection = None
db_connected = False
def initialize_engine():
"""엔진 초기화"""
global vision_engine, model, collection, db_connected
# ... (초기화 코드)
# ============================================================
# API 엔드포인트
# ============================================================
@app.get("/", response_class=HTMLResponse)
async def root():
"""웹 UI - 브라우저에서 직접 접속 시 표시되는 테스트 페이지"""
return """<!DOCTYPE html>...""" # HTML 코드
@app.post("/api/chat")
async def chat(request: ChatRequest):
"""채팅 API - 워드프레스에서 호출하는 메인 엔드포인트"""
# ... (채팅 처리 코드)
@app.get("/api/health")
async def health_check():
"""상태 확인 - 서버 정상 작동 여부 및 DB 연결 상태 반환"""
return {"status": "ok", "db_connected": db_connected}
# ============================================================
# 실행
# ============================================================
if __name__ == "__main__":
import uvicorn
uvicorn.run("web_app:app", host="0.0.0.0", port=8000, reload=True)Pydantic의 BaseModel을 상속한 ChatRequest와 ChatResponse는 요청과 응답 데이터의 타입을 자동으로 검증합니다. 잘못된 데이터가 들어오면 FastAPI가 자동으로 422 에러를 반환하므로 별도의 유효성 검사 코드가 필요 없습니다. /api/health 엔드포인트는 서버 상태를 외부에서 모니터링할 때 유용하며, 서버 기동 후 브라우저에서 http://localhost:8000/docs로 접속하면 Swagger UI로 모든 API를 바로 테스트할 수 있습니다.
5. 서버 실행 배치 파일 만들기
매번 긴 명령어를 타이핑하지 않도록 배치 파일로 자동화합니다. 배치 파일 상단의 chcp 65001 명령은 Windows 콘솔의 인코딩을 UTF-8로 전환해 한글이 깨지는 현상을 방지합니다.
5.1 server.bat — FastAPI 서버 실행
CONDA_PATH, ENV_NAME, PROJECT_PATH 세 가지 변수만 본인 환경에 맞게 수정하면 됩니다.
@echo off
chcp 65001 > nul
title AI Buddha - FastAPI Server
echo ============================================================
echo AI Buddha - FastAPI Server
echo ============================================================
echo.
REM 가상환경 경로 (사용자 환경에 맞게 수정)
set CONDA_PATH=G:\miniconda3
set ENV_NAME=ai_venv
set PROJECT_PATH=G:\buddha_project
echo [1/4] 가상환경 활성화 중...
call "%CONDA_PATH%\Scripts\activate.bat" %ENV_NAME%
echo 가상환경 활성화 완료
echo.
echo [2/4] 프로젝트 폴더로 이동...
cd /d "%PROJECT_PATH%"
echo 프로젝트 폴더: %cd%
echo.
echo [3/4] FastAPI 서버 실행 중...
echo.
echo ============================================================
echo 접속 주소:
echo - 로컬: http://localhost:8000
echo - 네트워크: http://192.168.219.100:8000
echo - API 문서: http://localhost:8000/docs
echo ============================================================
echo.
echo 서버를 종료하려면 Ctrl+C 를 누르세요.
echo.
uvicorn web_app:app --host 0.0.0.0 --port 8000 --reload
pause--reload 옵션은 코드 변경 시 서버를 자동으로 재시작해줍니다. 개발 중에는 편리하지만, 운영 환경에서는 제거하는 것이 권장됩니다.
5.2 ngrok.bat — 외부 접속 터널
NGROK_PATH를 ngrok.exe가 실제로 설치된 경로로 수정합니다.
@echo off
chcp 65001 > nul
title AI Buddha - ngrok Tunnel
echo ============================================================
echo AI Buddha - ngrok Tunnel
echo ============================================================
echo.
REM ngrok.exe가 있는 경로로 수정
set NGROK_PATH=C:\Program Files\WindowsApps\ngrok.ngrok_3.39.8.0_x64__1g87z0zv29zzc
echo [1/2] ngrok 실행 중...
cd /d "%NGROK_PATH%"
echo.
echo ============================================================
echo 외부 접속 주소가 생성됩니다
echo ============================================================
echo.
ngrok http 8000
pause5.3 start_all.bat — 서버 + ngrok 한 번에 실행
start_all.bat의 timeout /t 3 명령은 FastAPI 서버가 완전히 기동될 때까지 3초를 기다린 후 ngrok을 실행하기 위한 안전 지연입니다. 순서가 바뀌거나 이 대기 없이 ngrok을 먼저 실행하면 연결 실패가 발생할 수 있습니다. start ... cmd /k 구문은 각 프로세스를 별도의 콘솔 창에서 독립적으로 실행하여 하나가 종료되어도 다른 하나는 계속 유지되도록 합니다.
@echo off
chcp 65001 > nul
title AI Buddha - Full Server
echo ============================================================
echo AI Buddha - Full Server
echo ============================================================
echo.
set PROJECT_PATH=G:\buddha_project
start "AI Buddha Server" cmd /k "%PROJECT_PATH%\server.bat"
timeout /t 3 /nobreak > nul
start "AI Buddha ngrok" cmd /k "%PROJECT_PATH%\ngrok.bat"
echo.
echo ============================================================
echo 서버 실행 완료!
echo - 로컬: http://localhost:8000
echo - ngrok 주소는 ngrok 창에서 확인하세요
echo ============================================================
echo.
pause6. ngrok 설치 및 설정
6.1 ngrok 다운로드
ngrok 무료 플랜으로 로컬 서버 공개하기는 아래 순서로 진행합니다.
- https://ngrok.com/download 접속
- Windows 버전 다운로드
- 압축 해제 후
ngrok.exe를 원하는 폴더에 배치
6.2 ngrok 인증 토큰 등록 (선택 사항)
ngrok 사이트에 회원가입 후 발급받은 인증 토큰을 등록하면 연결 안정성이 향상됩니다. 토큰 없이도 기본 동작은 가능하지만, 토큰을 등록하면 더 긴 세션 시간과 추가 기능을 사용할 수 있습니다.
ngrok authtoken YOUR_AUTH_TOKEN6.3 ngrok 실행
ngrok http 8000실행 결과 화면:
Forwarding https://unrash-hannah-preimperial.ngrok-free.dev -> http://localhost:8000이 화면에 표시되는 https:// 주소가 바로 외부에서 내 컴퓨터에 접속할 수 있는 공개 URL입니다. 이 주소를 다음 단계 워드프레스 코드의 API_URL에 입력합니다.
무료 플랜과 유료 플랜 비교:
| 항목 | 무료 플랜 | 유료(Pro) 플랜 |
|---|---|---|
| 세션 시간 | 2시간 제한 | 무제한 |
| URL | 매 실행마다 변경 | 고정 도메인 사용 가능 |
| 동시 연결 | 제한적 | 넉넉함 |
| HTTPS | 제공 | 제공 |
| 가격 | 무료 | 유료 (월정액) |
개인 프로젝트나 테스트 용도라면 무료 플랜으로도 충분히 운영할 수 있습니다.
7. 워드프레스에 AI 추가하는 방법
워드프레스 FastAPI 연동의 마지막 단계입니다. 워드프레스 Gutenberg 에디터에서 새 페이지 또는 게시글을 만든 뒤, 블록 추가 버튼을 눌러 HTML 블록을 선택합니다. 아래 전체 코드를 그 HTML 블록 안에 붙여 넣습니다.

7.1 HTML + JavaScript 전체 코드
html
<!-- AI Buddha Chat -->
<div id="ai-buddha-chat" style="max-width:800px;margin:0 auto;font-family:'맑은 고딕',sans-serif;padding:0 10px;">
<!-- 상단 헤더 -->
<div style="background:#2C3E50;color:white;padding:10px 16px;border-radius:12px 12px 0 0;">
<h2 style="margin:0;color:white;font-size:17px;"> AI Buddha</h2>
<p style="margin:1px 0 0;font-size:10px;color:#A0C4E8;">AI-Powered Buddhist Scripture Archive</p>
</div>
<!-- 채팅 영역 -->
<div id="chatMessages" style="background:#FAFAFA;padding:14px;min-height:280px;max-height:380px;overflow-y:auto;border-left:1px solid #ddd;border-right:1px solid #ddd;">
<div style="background:#FFF3CD;padding:10px 14px;border-radius:8px;text-align:center;color:#856404;font-size:13px;">
안녕하세요. 불교 AI 상담사입니다.<br>개인 서버에서 운영 중이라 응답 속도가 느릴 수 있습니다.
</div>
</div>
<!-- 입력 영역 -->
<div style="display:flex;gap:8px;padding:10px;background:white;border:1px solid #ddd;flex-wrap:wrap;">
<textarea id="chatInput" rows="2" style="flex:1;min-width:150px;padding:10px 12px;border:2px solid #ddd;border-radius:8px;font-size:14px;resize:vertical;min-height:44px;font-family:inherit;" placeholder="질문을 입력하세요... (Enter로 전송)"></textarea>
<button id="chatSend" style="padding:10px 20px;background:#2C3E50;color:white;border:none;border-radius:8px;font-size:15px;font-weight:bold;cursor:pointer;min-height:44px;flex-shrink:0;">전송</button>
</div>
<!-- 상태 표시 -->
<div id="chatStatus" style="text-align:center;margin-top:6px;font-size:12px;color:#888;">
<span style="color:#2ECC71;">● 준비</span>
<span id="waitTime" style="margin-left:10px;font-size:11px;color:#999;display:none;"></span>
</div>
<!-- 안내 메시지 -->
<div style="margin-top:14px;background:#E8F4FD;border-left:4px solid #2C3E50;border-radius:6px;padding:12px 16px;font-size:13px;color:#1A5276;line-height:1.7;border:1px solid #d4e6f1;">
<div style="display:flex;align-items:flex-start;gap:8px;">
<span style="font-size:18px;">📌</span>
<div>
<span style="font-weight:bold;">안내</span><br>
• 이 서비스는 <strong>불교 경전 DB</strong>와 <strong>AI</strong>를 활용합니다.<br>
• 응답 생성까지 <strong>5~30초</strong> 소요될 수 있습니다.<br>
• 개인 서버에서 운영 중이라 속도가 느릴 수 있습니다.
</div>
</div>
</div>
</div>
<script>
// ngrok 실행 후 생성된 주소로 변경 (매 실행마다 확인 필요)
const API_URL = 'https://unrash-hannah-preimperial.ngrok-free.dev';
const chatMessages = document.getElementById('chatMessages');
const chatInput = document.getElementById('chatInput');
const chatSend = document.getElementById('chatSend');
const chatStatus = document.getElementById('chatStatus');
const waitTime = document.getElementById('waitTime');
let startTime = null;
let timerInterval = null;
function addMessage(text, type) {
const div = document.createElement('div');
div.style.cssText = `
padding: 8px 14px;
border-radius: 8px;
margin-bottom: 6px;
max-width: 88%;
word-wrap: break-word;
font-size: 14px;
line-height: 1.5;
`;
if (type === 'user') {
div.style.cssText += `
background: #2C3E50;
color: white;
margin-left: auto;
`;
} else if (type === 'assistant') {
div.style.cssText += `
background: #F0F0F0;
color: #1A1A1A;
`;
} else {
div.style.cssText += `
background: #FFF3CD;
color: #856404;
text-align: center;
max-width: 100%;
font-size: 12px;
`;
}
div.textContent = text;
chatMessages.appendChild(div);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
function setStatus(text, type) {
const color = type === 'ready' ? '#2ECC71' : type === 'thinking' ? '#F39C12' : '#E74C3C';
chatStatus.innerHTML = `<span style="color:${color};"> ${text}</span>`;
if (type === 'thinking') {
startTime = Date.now();
waitTime.style.display = 'inline';
timerInterval = setInterval(() => {
const elapsed = Math.floor((Date.now() - startTime) / 1000);
waitTime.textContent = ` ${elapsed}초 경과`;
}, 1000);
} else {
waitTime.style.display = 'none';
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = null;
}
}
}
async function sendMessage() {
const query = chatInput.value.trim();
if (!query) return;
addMessage(query, 'user');
chatInput.value = '';
chatSend.disabled = true;
setStatus('AI 응답 생성 중...', 'thinking');
try {
const response = await fetch(`${API_URL}/api/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// ngrok 무료 플랜의 브라우저 경고 페이지를 우회하기 위한 필수 헤더
'ngrok-skip-browser-warning': 'true'
},
body: JSON.stringify({ query: query, mode: 'general', n_results: 5 })
});
const data = await response.json();
addMessage(data.response, 'assistant');
if (data.scriptures && data.scriptures.length > 0) {
const refs = data.scriptures.slice(0, 3)
.map(s => s.title)
.filter(t => t && t !== '제목 없음');
if (refs.length > 0) {
addMessage(' 참고: ' + refs.join(' | '), 'system');
}
}
const elapsed = Math.floor((Date.now() - startTime) / 1000);
addMessage(` ${elapsed}초`, 'system');
setStatus('준비', 'ready');
} catch (error) {
addMessage(' 오류: ' + error.message, 'system');
setStatus('오류', 'error');
} finally {
chatSend.disabled = false;
chatInput.focus();
}
}
chatSend.addEventListener('click', sendMessage);
chatInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
</script>7.2 코드 핵심 포인트
JavaScript fetch() 요청 헤더에 'ngrok-skip-browser-warning': 'true'를 포함하는 것이 중요합니다. ngrok 무료 플랜은 브라우저로 직접 접속할 경우 경고 페이지를 먼저 표시하는데, 이 헤더를 추가하면 그 경고 페이지를 건너뛰고 바로 API 응답을 받을 수 있습니다. 이 헤더가 없으면 챗봇이 경고 페이지 HTML을 응답으로 받아 JSON 파싱 오류가 발생합니다.
API_URL 변수는 ngrok을 실행할 때마다 새로 생성되는 주소로 업데이트해야 합니다. ngrok 유료 플랜을 사용하면 고정 도메인을 설정할 수 있어 이 번거로움을 없앨 수 있습니다.
8. 주의사항
| 항목 | 설명 |
|---|---|
| 서버 전원 | 컴퓨터를 24시간 켜둬야 서비스가 유지됩니다 |
| ngrok 제한 | 무료 플랜: 2시간 세션 제한, 재시작마다 URL 변경 |
| 속도 | 개인 PC 사양(CPU/GPU/RAM)에 따라 응답 속도가 달라집니다 |
| 방화벽 | Windows 방화벽에서 8000번 포트 허용이 필요합니다 |
| 보안 | ngrok은 HTTPS를 제공하므로 데이터 전송 구간은 암호화됩니다 |
| API_URL 갱신 | ngrok 재시작 시 워드프레스 HTML의 API_URL을 새 주소로 수정해야 합니다 |
9. 자주 묻는 질문 (FAQ)
ngrok 주소가 바뀌면 어떻게 하나요?
ngrok을 재시작할 때마다 무료 플랜은 새로운 주소가 생성됩니다. 워드프레스 HTML 블록에서 const API_URL = '...' 부분을 새 주소로 수정하고 페이지를 업데이트하면 됩니다. 번거롭다면 ngrok 유료 플랜의 고정 도메인 기능을 활용하세요.
서버가 응답하지 않아요
server.bat을 실행한 콘솔 창을 확인합니다. Python 오류 메시지가 있으면 해당 오류를 먼저 해결해야 합니다. 오류 없이 Uvicorn running on http://0.0.0.0:8000 메시지가 표시되어 있다면 ngrok 콘솔 창도 확인하고, 필요시 start_all.bat으로 전체 재시작합니다.
502 Bad Gateway 오류가 발생해요
ngrok은 정상이지만 FastAPI 서버가 응답하지 않는 경우입니다. server.bat 창에서 서버가 실행 중인지 확인하고, 종료되어 있다면 다시 실행합니다. 간혹 모델 로딩 시간이 길어 초기 요청에서 타임아웃이 발생하기도 합니다.
무료로 계속 사용할 수 있나요?
ngrok 무료 플랜은 세션당 2시간 제한이 있습니다. 2시간마다 ngrok을 재시작해야 하며 URL도 바뀝니다. 상시 운영이 필요하다면 ngrok 유료 플랜을 고려하거나, Cloudflare Tunnel(무료, 고정 도메인)을 대안으로 검토해볼 수 있습니다.
이번에는 FastAPI 워드프레스 연동의 전 과정, 즉 FastAPI와 ngrok의 개념 이해부터 conda 가상환경 구성, 서버 코드 작성, 배치 파일 자동화, ngrok 터널 설정, 워드프레스 HTML 삽입까지 모두 살펴봤습니다.
클라우드 없이 개인 컴퓨터만으로 AI 서비스를 운영한다는 것은 비용 절감 그 이상의 의미가 있습니다. 내 데이터가 외부 클라우드 서버를 거치지 않고, 내 컴퓨터 안에서 처리된다는 점에서 데이터 프라이버시 측면에서도 유리합니다. ngrok 무료 플랜의 제약이 있지만, 개인 프로젝트나 소규모 커뮤니티 서비스에는 충분히 훌륭한 솔루션입니다.

