Type something to search...

허깅페이스 무료 Inference API OTT 앱 활용 가이드

1. 허깅페이스(Hugging Face) 무료 Inference API로 OTT 앱 만들기

넷플릭스(Netflix), 왓챠(Watcha) 같은 OTT 서비스에는 AI가 곳곳에 숨어 있다. 리뷰 감성 분석, 자막 번역, 욕설 필터, 콘텐츠 추천까지 — 이 모든 기능을 허깅페이스(Hugging Face) 무료 Inference API(인퍼런스 에이피아이)와 FastAPI(패스트에이피아이)로 직접 구현한다.

정보

2026.03.28 기준 실제 테스트 완료. 베이스 URL(유알엘): https://router.huggingface.co/hf-inference/models


1.1. 전체 기능 한눈에 보기

hf-inference (텍스트·이미지·오디오)

#기능모델OTT 활용 사례
1감성 분석nlptown/bert-base-multilingual-uncased-sentiment리뷰를 1~5점으로 자동 분류
2장르 분류facebook/bart-large-mnli줄거리만으로 장르 태그 자동 생성
3스포일러 감지facebook/bart-large-mnli스포일러 댓글 자동 숨김
4욕설 필터unitary/toxic-bert댓글·리뷰 혐오 표현 자동 차단
5줄거리 요약facebook/bart-large-cnn”이전 회차 요약” 자동 생성
6인물명 추출dslim/bert-base-NER출연진·감독 이름 자동 태깅
7자막 번역Helsinki-NLP/opus-mt-ko-en자막 다국어 번역
8줄거리 QAgoogle-bert/bert-large-uncased-whole-word-masking-finetuned-squad줄거리 기반 질문응답 챗봇
9객체 탐지facebook/detr-resnet-50영상 프레임 사물 자동 태깅
10NSFW 필터Falconsai/nsfw_image_detection업로드 이미지 자동 검수
11콘텐츠 추천intfloat/multilingual-e5-large줄거리 유사도 기반 추천
12얼굴 세그멘테이션jonathandinu/face-parsing프로필 사진 부위 자동 구분
13음성→텍스트openai/whisper-large-v3자막 자동 생성

Qwen3-VL 멀티모달 (이미지 URL 기반)

#기능모델OTT 활용 사례
14이미지 설명Qwen/Qwen3-VL-8B-Instruct썸네일 자동 설명 생성
15객체 그라운딩Qwen/Qwen3-VL-8B-Instruct한국어 바운딩 박스 자동 생성
16이미지 장르 분류Qwen/Qwen3-VL-8B-Instruct썸네일 장르 자동 태깅
17CUA 장치 제어Qwen/Qwen3-VL-8B-Instruct앱 자동화 좌표 반환
18이미지 OCRQwen/Qwen3-VL-8B-Instruct자막 캡처 텍스트 추출
19AI 생성 이미지 판별Qwen/Qwen3-VL-8B-Instruct업로드 썸네일 진위 검수

1.2. 환경 설정

1.2.1. 2.1 패키지 설치

터미널
1
pip install fastapi uvicorn requests python-dotenv python-multipart
패키지설명
1fastapiAPI(에이피아이) 서버를 만드는 핵심 라이브러리(라이브러리)
1uvicornFastAPI(패스트에이피아이) 앱을 실행하는 서버
1requests외부 URL(유알엘)로 HTTP(에이치티티피) 요청을 보내는 라이브러리
1python-dotenv.env 파일에서 비밀 키를 불러오는 라이브러리
1python-multipart이미지 파일 업로드를 처리할 때 필요한 라이브러리

1.2.2. 2.2 API 토큰(토큰) 설정

허깅페이스 토큰은 코드에 직접 적지 않고 .env 파일에 따로 보관한다. 허깅페이스 Settings → Access Tokens에서 토큰을 발급받는다.

.env
1
HF_TOKEN=hf_여기에_발급받은_토큰_붙여넣기

주의

.env 파일은 절대 GitHub(깃허브)에 올리면 안 된다. .gitignore(깃이그노어) 파일에 .env를 추가해 두어라.


1.3. 공통 코드 작성

서버 전체에서 반복해서 쓰는 설정과 함수를 먼저 만든다. main.py 파일 하나에 순서대로 작성한다.

1.3.1. 3.1 패키지 불러오기

main.py
1
from fastapi import FastAPI, HTTPException, UploadFile, File
2
from fastapi.middleware.cors import CORSMiddleware
3
from pydantic import BaseModel, Field
4
import requests, os
5
from dotenv import load_dotenv
설명
1FastAPI — 앱 본체. HTTPException(에이치티티피익셉션) — 오류 응답 전송. UploadFile, File — 이미지 파일 업로드 처리
2CORSMiddleware(코스미들웨어) — 다른 도메인(도메인)에서 이 API를 호출할 수 있도록 허용
3BaseModel(베이스모델), Field(필드) — 요청 데이터의 형식과 예시를 정의
4requests — 허깅페이스 서버에 HTTP(에이치티티피) 요청 전송. os — 환경변수 읽기
5load_dotenv.env 파일을 읽어서 환경변수로 등록

1.3.2. 3.2 앱 초기화

main.py
1
load_dotenv()
2
3
app = FastAPI(
4
title="OTT 허깅페이스 무료 API 데모",
5
version="1.0.0",
6
)
7
8
app.add_middleware(
9
CORSMiddleware,
10
allow_origins=["*"],
11
allow_methods=["*"],
12
allow_headers=["*"],
13
)
14
15
BASE = "https://router.huggingface.co/hf-inference/models"
16
VLM_URL = "https://router.huggingface.co/v1/chat/completions"
17
VLM_MODEL = "Qwen/Qwen3-VL-8B-Instruct"
18
TIMEOUT = 60
설명
1.env 파일의 내용을 환경변수로 불러온다
3~6FastAPI 앱 객체를 만든다. titleversion은 Swagger(스웨거) 문서에 표시된다
8~13모든 도메인("*")에서 이 API를 호출할 수 있게 CORS(코스) 설정을 추가한다
15hf-inference 모델 기본 주소. 텍스트·이미지·오디오 기능(1~13번)이 사용한다
16VLM 채팅 API 주소. 멀티모달 기능(14~19번)이 사용한다
17멀티모달 기능에 사용할 VLM 모델 이름이다
18API 응답을 60초까지 기다린다. 무료 모델은 처음 실행 시 느릴 수 있다

1.3.3. 3.3 토큰 함수

main.py
1
def get_token() -> str:
2
t = os.getenv("HF_TOKEN")
3
if not t:
4
raise HTTPException(500, "HF_TOKEN 환경변수 없음")
5
return t
설명
1get_token 함수를 정의한다. -> str은 문자열을 반환한다는 표시
2환경변수에서 HF_TOKEN 값을 읽어 t에 저장한다
3~4토큰이 없으면 500 오류를 반환한다. 서버 실행 전 .env 파일을 반드시 확인해라
5토큰 문자열을 반환한다

1.3.4. 3.4 텍스트 API 호출 함수

허깅페이스에 텍스트 데이터를 보내고 결과를 받아오는 공통 함수다. 감성 분석, 번역, 요약 등 텍스트 기반 기능이 모두 이 함수를 사용한다.

요약

503 오류는 무료 모델의 **콜드 스타트(Cold Start)**다. 오랫동안 호출이 없으면 모델이 절전 상태로 들어가는데, 첫 요청 시 깨어나는 데 20~30초가 걸린다. 오류가 나도 당황하지 말고 잠시 후 다시 시도해라.

1.3.5. 3.5 이미지 API 호출 함수

이미지 파일(바이너리(바이너리) 데이터)을 허깅페이스에 전송하는 함수다. 객체 탐지, NSFW(엔에스에프더블유) 필터 기능이 이 함수를 사용한다.

main.py
1
def hf_img(model: str, data: bytes) -> dict:
2
try:
3
r = requests.post(
4
f"{BASE}/{model}",
5
headers={"Authorization": f"Bearer {get_token()}"},
6
data=data,
7
timeout=TIMEOUT,
8
)
9
except requests.exceptions.Timeout:
10
raise HTTPException(504, "모델 응답 시간 초과")
11
if r.status_code != 200:
12
raise HTTPException(r.status_code, r.text)
13
return r.json()
설명
1data: bytes(바이트) — 이미지를 바이너리(이진) 형식으로 받는다
6텍스트 함수의 json=payload 대신 data=data를 사용한다. 이미지는 JSON(제이슨)이 아닌 바이너리로 전송한다
9~10타임아웃(타임아웃) 처리
11~12오류 시 상태 코드와 오류 내용을 그대로 반환한다

1.3.6. 3.6 멀티모달(VLM) 호출 함수

이미지 URL과 텍스트 프롬프트를 Qwen3-VL 모델에 전달하는 함수다. 14번 이후 기능이 모두 이 함수를 사용한다.

main.py
1
def vlm(prompt: str, img_url: str, sys_msg: str = "") -> str:
2
msgs = []
3
if sys_msg:
4
msgs.append({"role": "system", "content": sys_msg})
5
msgs.append({
6
"role": "user",
7
"content": [
8
{"type": "image_url", "image_url": {"url": img_url}},
9
{"type": "text", "text": prompt},
10
],
11
})
12
r = requests.post(
13
VLM_URL,
14
headers={
15
"Authorization": f"Bearer {get_token()}",
16
"Content-Type": "application/json",
17
},
18
json={"model": VLM_MODEL, "messages": msgs, "max_tokens": 400},
19
timeout=TIMEOUT,
20
)
21
if r.status_code != 200:
22
raise HTTPException(r.status_code, r.text)
23
return r.json()["choices"][0]["message"]["content"]
설명
1prompt — 텍스트 지시, img_url — 분석할 이미지 주소, sys_msg — 역할 설정 프롬프트(선택)
3~5sys_msg가 있으면 system 역할 메시지를 먼저 추가한다
6~12user 메시지에 이미지 URL과 텍스트 프롬프트를 함께 담는다
13~18VLM_URL로 POST(포스트) 요청을 보낸다. hf() 함수와 달리 chat/completions 형식을 사용한다
19~20오류 시 상태 코드와 메시지를 반환한다
21AI 답변 텍스트만 꺼내서 반환한다

1.4. 요청 모델 정의

클라이언트(클라이언트)가 API에 보내는 데이터의 형식을 미리 정의한다. Pydantic(파이댄틱) BaseModel을 상속해서 만든다.

main.py
1
class Text(BaseModel):
2
text: str = Field(..., example="연출도 좋고 배우 연기가 압권이었다")
3
4
class Genre(BaseModel):
5
text: str = Field(..., example="우주비행사들이 웜홀을 통해 새 행성을 찾아 떠난다")
6
labels: list[str] = Field(
7
default=["액션", "로맨스", "공포", "코미디", "SF", "드라마", "스릴러"],
8
)
9
10
class QA(BaseModel):
11
question: str = Field(..., example="주인공의 연인은 누구인가?")
12
context: str = Field(..., example="Tony Stark is a genius inventor. Pepper Potts is his love interest.")
13
14
class Trans(BaseModel):
15
text: str = Field(..., example="오늘 밤 별이 참 예쁘다")
16
src: str = Field(default="ko", example="ko")
17
tgt: str = Field(default="en", example="en")
18
19
class Similar(BaseModel):
20
query: str = Field(..., example="우주를 배경으로 한 감동적인 SF 영화")
21
candidates: list[str] = Field(..., example=[
22
"외계인이 지구를 침공하는 액션 영화",
23
"고등학교 첫사랑 로맨스",
24
"화성 탐사 중 고립된 우주비행사의 생존기",
25
])
26
27
class ImgReq(BaseModel):
28
image_url: str = Field(..., example="http://images.cocodataset.org/val2017/000000039769.jpg")
29
prompt: str = Field(..., example="이 사진에 무엇이 보이는지 한국어로 설명해")
클래스설명
1~2Text단순 텍스트 한 줄을 받는 가장 기본 형식. 감성 분석, 요약, 번역 등에 사용
4~8Genre텍스트와 함께 장르 후보 목록(labels)을 받는다. 기본값으로 7개 장르가 설정되어 있다
10~12QAquestion — 질문, context — 답을 찾을 줄거리 또는 본문
14~17Trans번역할 텍스트와 출발 언어(src), 도착 언어(tgt)를 받는다
19~25Similar검색어(query)와 비교할 후보 문장 목록(candidates)을 받는다
27~29ImgReq이미지 URL과 텍스트 프롬프트를 함께 받는다. VLM 기능(14~19번)이 모두 사용한다

참고

Field(...)...(Ellipsis(엘립시스))는 필수 항목이라는 뜻이다. default=가 있으면 생략 가능한 선택 항목이다.


1.5. 기능 구현

1.5.1. 5.1 리뷰 감성 분석

사용자 리뷰를 1~5점 별점으로 자동 분류한다. 별점 데이터를 직접 입력받지 않아도 리뷰 문장만으로 평점을 집계할 수 있다.

모델 이름에 multilingual(멀티링구얼)이 포함되어 있다. 한국어, 영어, 독일어, 프랑스어 등 여러 언어를 지원한다. “연출도 좋고 배우 연기가 압권이었다” 같은 한국어 리뷰도 바로 분석할 수 있다.


1.5.2. 5.2 장르 자동 분류

줄거리 텍스트를 입력하면 AI가 장르 후보 목록 중 가장 잘 맞는 장르를 골라준다. 장르 태그를 사람이 일일이 달지 않아도 되는 자동화 기능이다.


1.5.3. 5.3 스포일러 감지

같은 bart-large-mnli 모델을 사용하되, 레이블(레이블)만 spoiler / no spoiler로 바꾼다. 제로샷(Zero-shot) 분류의 강점 — 학습 없이 레이블만 바꾸면 전혀 다른 기능이 된다.

main.py
1
@app.post("/review/spoiler", tags=["리뷰"])
2
def spoiler(req: Text):
3
r = hf("facebook/bart-large-mnli", {
4
"inputs": req.text,
5
"parameters": {"candidate_labels": ["spoiler", "no spoiler"]},
6
})
7
if isinstance(r, list) and r:
8
top = r[0]
9
return {"is_spoiler": top["label"] == "spoiler", "confidence": round(top["score"], 3)}
10
if "labels" in r:
11
return {"is_spoiler": r["labels"][0] == "spoiler", "confidence": round(r["scores"][0], 3)}
12
return r
설명
1~2/review/spoiler 엔드포인트. Text 형식으로 댓글 문장 하나를 받는다
3~5장르 분류와 동일한 모델, 레이블만 ["spoiler", "no spoiler"]로 변경
7~8응답이 리스트면 첫 번째 항목이 최고 확률 결과다. label == "spoiler"이면 True 반환
9~10응답이 딕셔너리면 labels[0]이 최고 확률 레이블이다

요약

is_spoiler: true이면 프론트엔드(프론트엔드)에서 댓글을 흐리게 처리하거나 “스포일러 보기” 버튼을 달면 된다.


1.5.4. 5.4 욕설·혐오 표현 필터링

리뷰나 댓글에서 욕설, 모욕, 혐오 표현을 자동으로 감지한다. 점수가 0.5 이상인 항목만 걸러내어 is_toxic 여부를 반환한다.

레이블의미
toxic전반적 유독성
severe_toxic극도로 유해한 표현
obscene음란·외설 표현
threat위협적 표현
insult모욕·비하 표현
identity_hate특정 집단 혐오 표현

1.5.5. 5.5 줄거리 요약

긴 줄거리나 시즌 내용을 짧게 요약한다. “이전 회차 요약”, “시리즈 소개” 등에 활용할 수 있다.

main.py
1
@app.post("/content/summary", tags=["콘텐츠"])
2
def summary(req: Text):
3
r = hf("facebook/bart-large-cnn", {
4
"inputs": req.text,
5
"parameters": {"max_length": 80, "min_length": 20},
6
})
7
if isinstance(r, list) and r:
8
return {"summary": r[0].get("summary_text", "")}
9
return r
설명
1~2/content/summary 엔드포인트. 요약할 긴 텍스트를 받는다
3~5max_length — 요약문 최대 단어 수, min_length — 최소 단어 수
6~7응답 리스트의 첫 번째 항목에서 summary_text 키(키)로 요약문을 꺼낸다

주의

bart-large-cnn은 영어 텍스트에 최적화된 모델이다. 한국어 입력은 번역 후 요약하거나, 한국어 전용 요약 모델을 별도로 사용해라.


1.5.6. 5.6 인물명 추출 (NER)

줄거리나 기사에서 감독·배우 이름을 자동으로 찾아낸다. NER(엔이알, Named Entity Recognition — 개체명 인식)이라고 부르는 기술이다.

main.py
1
@app.post("/content/ner", tags=["콘텐츠"])
2
def ner(req: Text):
3
r = hf("dslim/bert-base-NER", {"inputs": req.text})
4
if isinstance(r, list):
5
persons = list(set(
6
x["word"] for x in r
7
if x.get("entity_group") == "PER" and x.get("score", 0) > 0.8
8
))
9
return {"persons": persons}
10
return r
설명
1~2/content/ner 엔드포인트
3NER(개체명 인식) 모델에 텍스트를 보낸다
4~7entity_group == "PER" — 사람 이름만 필터링. score > 0.8 — 확률 80% 이상만 채택
4~5set()으로 중복 제거 후 list()로 변환한다
8추출된 인물명 목록을 반환한다
의미
PER사람 이름 (Person)
ORG조직·기관 이름 (Organization)
LOC지명 (Location)
MISC기타 고유명사

1.5.7. 5.7 자막 번역

한국어 자막을 영어로, 또는 다른 언어 쌍으로 번역한다. Helsinki-NLP/opus-mt-{src}-{tgt} 형식으로 모델 이름을 동적으로 조합한다.

main.py
1
@app.post("/subtitle/translate", tags=["자막"])
2
def translate(req: Trans):
3
r = hf(f"Helsinki-NLP/opus-mt-{req.src}-{req.tgt}", {"inputs": req.text})
4
if isinstance(r, list) and r:
5
return {"translated": r[0].get("translation_text", "")}
6
return r
설명
1~2/subtitle/translate 엔드포인트. Trans 형식으로 텍스트와 언어 코드를 받는다
3f"..." 포맷 문자열로 출발어(src)와 도착어(tgt)를 모델 이름에 직접 삽입한다
4~5번역된 텍스트를 translation_text 키로 꺼내 반환한다

srctgt에는 ISO 639-1(아이에스오) 언어 코드를 사용한다.

코드언어
ko한국어
en영어
ja일본어
zh중국어
fr프랑스어
de독일어

모든 언어 쌍이 지원되지는 않는다. 허깅페이스에서 opus-mt-ko-ja 등으로 검색해 존재 여부를 확인해라.


1.5.8. 5.8 줄거리 QA(질문응답)

줄거리나 소개 텍스트를 컨텍스트(Context(컨텍스트))로 주고, “이 영화 주인공은 누구야?”처럼 자연어로 질문하면 답을 찾아준다.

main.py
1
@app.post("/content/qa", tags=["콘텐츠"])
2
def qa(req: QA):
3
r = hf(
4
"google-bert/bert-large-uncased-whole-word-masking-finetuned-squad",
5
{"inputs": {"question": req.question, "context": req.context}},
6
)
7
if "answer" in r:
8
return {"answer": r["answer"], "score": round(r["score"], 3)}
9
return r
설명
1~2/content/qa 엔드포인트. QA 형식으로 질문과 컨텍스트를 받는다
3~5inputsquestioncontext를 딕셔너리로 함께 전달한다 — 텍스트 API와 형식이 다르다
6~7응답에 answer 키가 있으면 답과 확률 점수를 반환한다

참고

이 모델은 컨텍스트(줄거리)에 없는 내용은 답하지 못한다. “인터스텔라 감독 누구야?”라고 물어도 컨텍스트에 감독 이름이 없으면 답을 찾지 못한다.


1.5.9. 5.9 객체 탐지

이미지 파일을 업로드하면 화면 속 사물을 인식하고 위치(바운딩 박스(Bounding Box(바운딩 박스)))를 반환한다. 영상 프레임에서 등장 사물을 자동 태깅하거나 장면 검색에 활용한다.

main.py
1
@app.post("/image/detect", tags=["이미지"])
2
async def detect(file: UploadFile = File(...)):
3
data = await file.read()
4
r = hf_img("facebook/detr-resnet-50", data)
5
if isinstance(r, list):
6
return [
7
{"label": x["label"], "score": round(x["score"], 3), "box": x["box"]}
8
for x in r if x.get("score", 0) > 0.7
9
]
10
return r
설명
1/image/detect 엔드포인트
2async def — 파일 업로드는 비동기(Async(어싱크)) 처리. UploadFile — 업로드된 파일 객체
3await file.read() — 파일 내용을 바이트(byte(바이트)) 형식으로 읽는다
4텍스트가 아닌 이미지이므로 hf_img() 함수를 사용한다
5~8탐지된 객체 중 신뢰도(score) 70% 이상인 것만 반환한다

box 안의 값은 이미지에서 객체가 있는 사각형 영역의 픽셀(pixel(픽셀)) 좌표다.

의미
xmin왼쪽 경계의 x 좌표
ymin위쪽 경계의 y 좌표
xmax오른쪽 경계의 x 좌표
ymax아래쪽 경계의 y 좌표

프론트엔드에서 이 좌표로 이미지 위에 사각형을 그려 시각화할 수 있다.


1.5.10. 5.10 NSFW(성인 콘텐츠) 이미지 필터링

사용자가 업로드한 프로필 사진이나 썸네일에 부적절한 이미지가 있는지 자동으로 검사한다.

main.py
1
@app.post("/image/nsfw", tags=["이미지"])
2
async def nsfw(file: UploadFile = File(...)):
3
data = await file.read()
4
r = hf_img("Falconsai/nsfw_image_detection", data)
5
if isinstance(r, list):
6
for x in r:
7
if x.get("label") == "nsfw":
8
return {"is_nsfw": x["score"] > 0.5, "score": round(x["score"], 3)}
9
return {"is_nsfw": False, "score": 0}
10
return r
설명
1~3객체 탐지와 동일한 방식으로 이미지를 업로드받아 바이트로 읽는다
4NSFW 감지 모델에 이미지를 전달한다
5~7응답 목록에서 label == "nsfw" 항목을 찾아 점수가 0.5 이상이면 is_nsfw: true를 반환한다
8nsfw 항목이 없으면 안전한 이미지로 판단해 is_nsfw: false를 반환한다

1.5.11. 5.11 유사 콘텐츠 추천

줄거리를 벡터(Vector(벡터))로 변환하고 코사인 유사도(Cosine Similarity(코사인 시밀래리티))를 계산해 “이 작품과 비슷한 콘텐츠”를 추천한다.

텍스트를 AI가 이해할 수 있는 숫자 배열로 변환한 것이다. 예를 들어 “우주 탐사 영화”와 “화성에서 혼자 살아남는 이야기”는 의미가 비슷하므로 두 문장의 벡터가 비슷한 방향을 가리키게 된다. 코사인 유사도는 두 벡터가 얼마나 같은 방향을 가리키는지를 -1 ~ 1 사이 숫자로 나타낸다. 1에 가까울수록 의미가 유사하고, 0에 가까울수록 관련이 없다.


1.5.12. 5.12 얼굴 세그멘테이션

업로드된 이미지에서 피부, 눈썹, 머리카락 등 얼굴 부위를 자동으로 구분한다. 사용자 프로필 사진 편집, 필터 적용 등에 활용한다.

main.py
1
@app.post("/image/face", tags=["이미지"])
2
async def face(file: UploadFile = File(...)):
3
data = await file.read()
4
r = hf_img("jonathandinu/face-parsing", data)
5
if isinstance(r, list):
6
return [{"label": x["label"], "score": round(x["score"], 3)} for x in r]
7
return r
설명
1~3이미지를 업로드받아 바이트로 읽는다
4얼굴 파싱(face-parsing) 모델에 이미지를 전달한다
5~7감지된 각 부위의 레이블과 확률 점수를 목록으로 반환한다

1.5.13. 5.13 음성→텍스트

오디오 파일을 업로드하면 Whisper(위스퍼) 모델이 음성을 텍스트로 변환한다. 자막 자동 생성, 대화 내용 검색 등에 활용한다.

main.py
1
@app.post("/audio/asr", tags=["오디오"])
2
async def asr(file: UploadFile = File(...)):
3
data = await file.read()
4
r = hf_img("openai/whisper-large-v3", data)
5
return {"text": r.get("text", "")}
설명
1~3오디오 파일을 업로드받아 바이트로 읽는다. 이미지와 동일한 hf_img() 함수로 전송한다
4Whisper(위스퍼) 모델에 오디오 데이터를 전달한다
5변환된 텍스트를 반환한다

요약

whisper-large-v3는 한국어 음성 인식도 지원한다. MP3, WAV, FLAC 형식을 모두 처리할 수 있다.


1.5.14. 5.14 이미지 설명 생성

이미지 URL을 전달하면 Qwen3-VL 모델이 이미지 내용을 한국어로 설명한다. 썸네일 자동 설명 생성, 시각 장애인용 접근성 텍스트 제공 등에 활용한다.

main.py
1
@app.post("/vlm/describe", tags=["멀티모달"])
2
def describe(req: ImgReq):
3
result = vlm(req.prompt, req.image_url)
4
return {"description": result}
설명
1~2/vlm/describe 엔드포인트. ImgReq 형식으로 이미지 URL과 프롬프트를 받는다
3vlm() 함수에 프롬프트와 이미지 URL을 전달한다
4AI 설명 텍스트를 반환한다

1.5.15. 5.15 객체 그라운딩

이미지에서 물체를 탐지하고 한국어 이름과 바운딩 박스 좌표를 함께 반환한다. 9번 객체 탐지와 달리 한국어 레이블을 제공한다.

main.py
1
@app.post("/vlm/ground", tags=["멀티모달"])
2
def ground(req: ImgReq):
3
prompt = (
4
"이 이미지에서 모든 객체를 탐지하고 바운딩 박스 좌표를 JSON 배열로 반환해. "
5
'형식: [\{"label":"한국어이름","confidence":0.9,"bbox":[x1,y1,x2,y2]\}]'
6
)
7
result = vlm(prompt, req.image_url)
8
return {"raw": result}
설명
3~5탐지 결과를 JSON 배열로 반환하도록 프롬프트를 지정한다
6vlm() 함수에 프롬프트와 이미지 URL을 전달한다
7AI가 생성한 JSON 문자열을 raw 키로 반환한다

1.5.16. 5.16 이미지 장르 분류

썸네일 이미지를 보고 OTT 장르를 자동으로 분류한다. 텍스트 없이 이미지만으로 장르를 판별할 수 있다.

main.py
1
@app.post("/vlm/genre", tags=["멀티모달"])
2
def vlm_genre(req: ImgReq):
3
prompt = (
4
"이 이미지가 어떤 OTT 장르에 어울리는지 분류해. "
5
"선택지: [액션, 로맨스, 힐링, 공포, SF, 자연다큐]. "
6
'JSON으로 \{"genre":"선택","reason":"이유"\} 반환'
7
)
8
result = vlm(prompt, req.image_url)
9
return {"raw": result}
설명
3~6장르 후보 목록과 반환 형식을 프롬프트 안에 직접 지정한다
7vlm() 함수로 이미지를 분석한다

1.5.17. 5.17 CUA 장치 제어

스크린샷을 전달하면 “다음에 탭할 좌표”를 반환한다. 모바일 앱 자동화 테스트, UI 자동 제어 등에 활용한다.

main.py
1
@app.post("/vlm/cua", tags=["멀티모달"])
2
def cua(req: ImgReq):
3
sys_msg = (
4
"너는 모바일 앱 자동화 에이전트다. "
5
"스크린샷을 보고 다음 액션을 JSON으로 반환한다: "
6
'\{"action":"tap|swipe|type","target":"요소이름","coordinates":[x,y],"description":"설명"\}'
7
)
8
result = vlm(req.prompt, req.image_url, sys_msg=sys_msg)
9
return {"raw": result}
설명
3~6sys_msg로 AI에게 “자동화 에이전트” 역할을 부여하고 반환 형식을 지정한다
7sys_msgvlm() 함수에 함께 전달한다

1.5.18. 5.18 이미지 OCR

이미지 속 텍스트를 추출하고 한국어로 번역한다. 영수증, 자막 캡처, 간판 사진 등의 텍스트 처리에 활용한다.

main.py
1
@app.post("/vlm/ocr", tags=["멀티모달"])
2
def ocr(req: ImgReq):
3
prompt = "이 이미지에 텍스트가 있으면 모두 읽어서 한국어로 번역해줘"
4
result = vlm(prompt, req.image_url)
5
return {"text": result}
설명
3텍스트 추출과 한국어 번역을 한 번에 요청한다
4~5vlm() 함수로 이미지를 분석하고 결과를 반환한다

1.5.19. 5.19 AI 생성 이미지 판별

이미지가 사진인지, 3D 렌더링인지, AI가 생성한 것인지 분석한다. 사용자가 업로드한 썸네일의 진위를 자동으로 검수한다.

main.py
1
@app.post("/vlm/ai-detect", tags=["멀티모달"])
2
def ai_detect(req: ImgReq):
3
prompt = (
4
"이 이미지의 모든 시각적 요소를 분석하고, "
5
"어떤 기술로 만들어졌는지 추정해줘 "
6
"(사진/3D렌더/일러스트/AI생성)"
7
)
8
result = vlm(prompt, req.image_url)
9
return {"analysis": result}
설명
3~6판별 기준(사진/3D렌더/일러스트/AI생성)을 프롬프트에 명시한다
7~8vlm() 함수로 이미지를 분석하고 결과를 반환한다

1.6. 서버 실행

터미널
1
uvicorn main:app --reload
옵션설명
mainmain.py 파일
app파일 안의 app = FastAPI(...) 객체
--reload코드를 수정하면 서버가 자동으로 재시작된다. 개발할 때만 사용한다

서버가 실행되면 브라우저에서 아래 주소로 Swagger(스웨거) UI(유아이)에 접속한다.

http://localhost:8000/docs

Swagger UI에서 각 엔드포인트를 직접 클릭해서 테스트할 수 있다.

요약

Render(렌더) 같은 클라우드(Cloud(클라우드)) 서비스에 배포할 때는 --reload 없이 아래 명령어를 시작 커맨드로 지정해라.

Terminal window
1
uvicorn main:app --host 0.0.0.0 --port 10000

1.7. 동작 확인 목록

2026년 3월 28일, 실제 토큰으로 호출해서 200 응답을 확인한 결과다.

1.7.1. hf-inference (CPU 추론)

#기능모델한국어
1감성 분석nlptown/bert-base-multilingual-uncased-sentiment
2제로샷 분류facebook/bart-large-mnli
3욕설 감지unitary/toxic-bert
4NER 인물 추출dslim/bert-base-NER
5요약facebook/bart-large-cnn
6번역Helsinki-NLP/opus-mt-ko-en
7QAgoogle-bert/bert-large-uncased-whole-word-masking-finetuned-squad
8객체 탐지facebook/detr-resnet-50-
9NSFW 필터링Falconsai/nsfw_image_detection-
10임베딩intfloat/multilingual-e5-large
11얼굴 세그멘테이션jonathandinu/face-parsing-
12음성→텍스트openai/whisper-large-v3

1.7.2. Inference Providers (멀티모달)

#기능모델한국어
13카메라 이미지 인식Qwen/Qwen3-VL-8B-Instruct
14객체 그라운딩 (바운딩 박스)Qwen/Qwen3-VL-8B-Instruct
15이미지 장르 분류Qwen/Qwen3-VL-8B-Instruct
16CUA 장치 제어 (탭 좌표)Qwen/Qwen3-VL-8B-Instruct
17이미지 OCRQwen/Qwen3-VL-8B-Instruct
18AI 생성 이미지 판별Qwen/Qwen3-VL-8B-Instruct

정보

14~19번은 하나의 모델(Qwen3-VL-8B-Instruct)로 프롬프트(prompt)만 바꿔서 처리한다.


1.8. 무료 불가 확인 목록

실제 호출해서 실패를 확인한 모델이다. 유료 프로바이더(provider) 크레딧이 필요하다.

모델에러
Qwen/Qwen2-VL-7B-Instructmodel_not_supported
meta-llama/Llama-3.2-11B-Vision-Instructmodel_not_supported
google/gemma-3-4b-itmodel_not_supported
Qwen/Qwen2.5-VL-7B-InstructInternal Error (500)
Intel/dpt-large (깊이 추정)404
MIT/ast-finetuned-audioset (오디오 분류)404
Salesforce/blip-image-captioning-large404

1.9. 참고 사항

  • 무료 계정에 월 100K Inference Provider 크레딧이 제공된다.
  • hf-inference 태스크(task)는 콜드 스타트 시 10~30초 로딩이 발생한다. 503 응답이 오면 20초 후 재시도한다.
  • SLA 보장이 없으므로 프로덕션(production)에는 적합하지 않다.
  • PRO 플랜($9/월)으로 업그레이드하면 크레딧과 속도가 증가한다.
용도URL
hf-inference (CPU 태스크)https://router.huggingface.co/hf-inference/models/\{model\}
Inference Providers (VLM 채팅)https://router.huggingface.co/v1/chat/completions
구 API (폐기됨)https://api-inference.huggingface.co