첫 프로젝트는 출입 통제 시스템입니다. PART 09의 라이브니스 게이트와 PART 07의 대규모 인식을, 이번에는 웹 API로 감싸 다른 프로그램이나 기기에서 호출할 수 있게 만듭니다. 문 앞의 카메라가 사진을 보내면, 서버가 진짜 등록자인지 판정해 출입 여부를 돌려주는 구조입니다.
왜 API로 만드나
지금까지의 코드는 한 대의 컴퓨터에서 실행하는 스크립트였습니다. 실제 출입 시스템은 여러 출입구(카메라)가 한 서버에 요청을 보내는 형태가 많습니다. 그래서 인식 로직을 웹 API로 감싸면, 카메라는 사진만 보내고 무거운 인식은 서버가 처리하는 깔끔한 분리가 됩니다. 이 API를 만드는 데 가벼운 웹 프레임워크 FastAPI를 씁니다.
pip install fastapi uvicorn python-multipart
출입 판정 API
PART 09 Ch05에서 만든 access_check 로직을, 이미지 업로드를 받는 API 엔드포인트로 감쌉니다.
# 파일: access_api.py"""사진을 받아 라이브니스+인식으로 출입을 판정하는 API."""import pickleimport numpy as npimport cv2import faissfrom fastapi import FastAPI, UploadFilefrom deepface import DeepFacefrom insightface.app import FaceAnalysisapp = FastAPI()# 서버 시작 시 한 번만 모델·인덱스 로드 (요청마다 로드하면 느림)recognizer = FaceAnalysis(name="buffalo_l", providers=["CPUExecutionProvider"])recognizer.prepare(ctx_id=-1, det_size=(640, 640))index = faiss.read_index("faces.index")with open("names.pkl", "rb") as f: names = pickle.load(f)LIVENESS_MIN, RECOG_MIN = 0.9, 0.4@app.post("/access")async def access(file: UploadFile): # 업로드된 바이트를 OpenCV 이미지로 디코딩 data = await file.read() img = cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR) cv2.imwrite("/tmp/_q.jpg", img) # DeepFace는 경로 입력이 편함 # 1) 라이브니스 게이트 (PART 09) try: faces = DeepFace.extract_faces("/tmp/_q.jpg", detector_backend="yunet", anti_spoofing=True) except ValueError: return {"granted": False, "reason": "no_face"} if not faces or not faces[0]["is_real"] or faces[0]["antispoof_score"] < LIVENESS_MIN: return {"granted": False, "reason": "spoof"} # 2) 인식 (PART 07) got = recognizer.get(img) if not got: return {"granted": False, "reason": "recog_fail"} q = np.array([got[0].normed_embedding], dtype="float32") faiss.normalize_L2(q) D, I = index.search(q, k=1) if float(D[0][0]) < RECOG_MIN: return {"granted": False, "reason": "unknown"} return {"granted": True, "name": names[int(I[0][0])], "score": round(float(D[0][0]), 3)}
서버를 띄우는 명령은 다음과 같습니다.
uvicorn access_api:app --host 0.0.0.0 --port 8000
이제 출입구 카메라는 사진 파일을 POST /access로 보내기만 하면, 서버가 {"granted": true, "name": "홍길동"} 같은 답을 돌려줍니다.
두 가지 핵심 설계
이 코드에는 실무에서 중요한 두 가지가 들어 있습니다.
- 모델은 시작 시 한 번만 로드: 모델 로딩은 비쌉니다. 요청마다 로드하면 응답이 느려지므로, 서버가 켜질 때 전역으로 한 번만 준비합니다(PART 06의 워밍업과 같은 원리).
- 게이트 순서: 라이브니스 → 인식. 가짜는 인식 연산까지 가지 않습니다(PART 09).
운영을 위한 보강
| 항목 | 보강 |
|---|---|
| 동시 요청 | uvicorn 워커 수 조정, 무거운 추론은 큐로 |
| 보안 | API 키·HTTPS, 출입 로그 기록 |
| 거짓 거부 | 사번·카드 우회 경로(PART 09) |
| 개인정보 | 임베딩만 저장, 원본 사진 즉시 폐기(PART 11) |
실무 팁. 업로드 이미지를 임시 파일로 저장하는 부분(
/tmp/_q.jpg)은 동시 요청에서 서로 덮어쓸 수 있습니다. 실제 서비스에서는 요청마다 고유한 임시 파일 이름을 쓰거나(예: 요청 ID 붙이기), DeepFace에 경로 대신 배열을 직접 넘기는 방식으로 바꿔야 합니다. 데모에서는 단순화했지만, 동시성은 서버 코드에서 늘 점검할 지점입니다.
이 장에서 기억할 것
출입 통제 시스템은 FastAPI로 인식 로직을 웹 API로 감싸, 카메라가 사진만 보내고 서버가 판정하는 구조로 만듭니다. 모델은 서버 시작 시 한 번만 로드하고, 라이브니스→인식 게이트 순서를 지킵니다. 동시성·보안·거짓거부 우회·개인정보를 운영 단계에서 보강합니다. 다음 장에서는 개인을 판정하지 않고 전체 경향만 보는 집중도·감정 모니터링을 만듭니다.