Human-in-the-Loop

강력한 AI를 위한,더 강력한하네스.

모델이 바뀌어도, 인프라가 바뀌어도.
흔들리지 않는 중심, 헥사고날 아키텍처.

기술 스택

프로젝트에서 직접 쓴 기술입니다. 누르면 그 기술을 쓴 프로젝트를 볼 수 있습니다.

백엔드
AI
인프라
프론트
설계

Works

만든 프로젝트.

01개인 · 기획부터 배포까지

샤일록의 법정Shylock

플레이어의 선택에 LLM 캐릭터들이 실시간으로 반응하는 법정 추리 웹 게임. 인증 서버, RAG, 로컬 LLM 서빙, 프로덕션 배포까지 혼자 구축했습니다.

  • 사실은 DB가, 문장은 LLM이. 인물 관계를 원문 근거가 달린 그래프(노드 7 · 엣지 17)로 저장했습니다. LLM은 관계를 지어내지 않고, DB에서 꺼낸 사실을 문장으로 풀어내기만 합니다. 저장소는 환경변수 스위치로 Postgres(재귀 CTE)와 Neo4j를 바꿔 끼울 수 있습니다.
  • 그래프가 정말 필요한지 실험으로 확인. 그래프를 넣었을 때와 뺐을 때를 비교하는 ablation(조건별 n=42)을 돌렸습니다. 여러 관계를 건너야 답할 수 있는 멀티홉 질문의 정답률이 21%에서 100%로 올랐습니다(Fisher p=3.6×10⁻¹⁵). 단일 사실 질문에서는 차이가 없다는 것도 함께 확인했습니다.
  • LLM이 전부 실패해도 판결은 나온다. 판사 ‘공작’의 판정은 LLM judge가 맡습니다. 로컬 모델이 실패하면 외부 API가, 그것도 실패하면 결정론적 규칙이 판정하는 3단 폴백입니다.
  • 월 $200 API 비용을 로컬 모델로. 평가 기준을 통과한 26B 오픈소스 모델을 직접 서빙합니다. 기능별 환경변수 스위치가 있어서, 문제가 생긴 기능만 바로 Claude · Cohere로 되돌릴 수 있습니다. 도구 호출(Function Calling) 에이전트는 호출하면 안 되는 12개 상황에서 12번 모두 호출해 기준 미달로 판정했고, 이 기능은 API에 남겼습니다.
  • 서버는 작게, 추론은 맥북에서. 1GB EC2에는 백엔드만 두고, 추론은 맥북의 Ollama가 맡습니다. 둘은 Cloudflare Tunnel + Zero Trust로 연결했습니다. Hexagonal · DDD · Vertical Slice 구조라 도메인 코드는 그대로 두고 어댑터만 추가했습니다.
  • 추론 위치만 바꿔 약 580배 빠르게. 같은 임베딩 모델을 EC2(t3.micro)에서 직접 돌리면 CPU 크레딧이 바닥나 평균 7.5초(p95 29초)가 걸렸습니다. 맥북(Apple Silicon)에서 돌리고 결과만 네트워크로 받으니 약 13ms가 됐습니다.
  • N+1은 조용히 넘어가지 않게. ORM relationship()은 진짜 애그리게이트(Trial–ChoiceHistory) 하나에만 쓰고, 나머지 FK는 ID 참조와 수동 join으로 처리합니다. AsyncSession에서 로드 안 된 관계를 건드리면 즉시 예외가 나서, N+1 쿼리가 몰래 생기지 않고 DDD 애그리게이트 경계도 지켜집니다.
  • 인증부터 배포까지 직접. Google OAuth + RS256 JWT 인증 서버, nginx, Docker Compose 배포를 직접 구축했습니다.
Next.js 15React 19FastAPIHexagonal · DDDPostgreSQL · pgvectorNeo4jDockerCloudflare TunnelAWSOllamaLangChainPyTorchpytestk6
ERD테이블 10개 · PostgreSQL + pgvector
Shylock ERD

‖ 정확히 하나 ○< 0개 이상 · 실선 = DB 외래 키, 점선 = 원문 줄 번호(ftln) 범위를 가리키는 soft ref · alembic 033 기준

불러오는 중…

샤일록의 법정 플레이 화면 — 샤일록과 포샤가 법정에서 맞서고, 존엄·기력·논리 게이지와 아이템·스킬 패널, 포샤의 대사 창이 보인다
Neo4j · 인물 관계 그래프인물 7 · 관계 17
friend_of×2father_ofdaughter_ofmarried_to×2discriminated_againstcreditor_ofdebtor_ofenemy_of ×2friend_of ×2guarantor_forfinanced_bymarried_to ×2샤일록Shylock투발Tubal제시카Jessica로렌조Lorenzo안토니오Antonio바사니오Bassanio포샤Portia

Postgres와 같은 인물 관계 데이터를 그래프로 미러링한 대안 게이트웨이 ·
×2 = 대칭 관계를 A→B, B→A 두 방향 엣지로 저장

라이프 튜토리얼 플레이 화면 — 픽셀아트 편의점에서 팔짱 낀 나사장과 천사 강아지가 등장하고, 신용점수·멘탈·법률지식 게이지와 나사장의 대사, 반박 입력창이 보인다
02팀 BeyondFacade · 풀스택
고용노동부 장관상 수상 (K-디지털 트레이닝 해커톤)

라이프 튜토리얼Life Tutorial

청소년이 첫 아르바이트에서 마주할 부당한 상황을 게임으로 미리 겪어 보는 노동권 교육 서비스.

  • 방어전 — 사장에게 직접 반박해 보는 대화 연습. NPC ‘나사장’에게 자유 텍스트로 반박하며 대응을 연습하는 모듈입니다. 판정 모델은 벤치마크 점수가 아니라 도메인 실측으로 골랐습니다. Gemini Flash-Lite는 틀린 답을 정답으로 처리하는 치명적인 오판정을 냈지만, EXAONE 7.8B는 0건이었습니다(자연어 75발화에서도 위험 오판정 0).
  • 가드 — 판정은 규칙이, 마지막 판단은 사람이. 근로계약서를 OCR로 읽고 규칙 기반으로 위법 조항을 판정합니다. 최종 판단은 상담 기관의 사람에게 연결합니다.
  • 리포트 — 날조 0건. 인터뷰 답변에서 상담에 필요한 사실을 추출해 기관 전달용 리포트를 만듭니다. 156케이스 평가셋으로 3개 모델을 교차 평가해 Gemma3 12B를 채택했습니다(분류 100% · 값 일치 90.2% · 날조 0건).
  • 법령 문장은 LLM이 쓰지 않는다. 법령 · 고시 · 행정해석을 1,593개 청크로 나눠 RAG를 구성했습니다. 화면의 법령 문장은 100% DB 원문을 그대로 보여 줘서, 틀린 조문이 구조적으로 나올 수 없습니다.
  • 개인정보는 밖으로 나가지 않게. 실제 근로계약서를 다루기 때문에 폐쇄망 로컬 추론을 전제로 설계했습니다. 16GB GPU 한 장에 EXAONE과 Gemma를 동시에 올려 두어 시연 중 모델 교체를 없앴고, OCR은 CPU로 분리했습니다. 임베딩 모델이 다르면 벡터가 호환되지 않아서, 청크마다 온라인 · 오프라인 벡터를 미리 채워 두었습니다. 전환할 때 재임베딩은 0건입니다.
  • 트러블슈팅 — 한 턴 28초를 0.4초로. 임베딩 429 지연의 원인이 우리 호출량이 아니라 모델 공용 쿼터라는 것을 실측으로 확인했습니다. 고정 25초 대기를 지수 백오프로 바꿔, 방어전 한 턴이 28초에서 중앙값 0.4초로 줄었습니다.
Next.jsTypeScriptPhaser 4FastAPIPostgreSQL · pgvectorPaddleOCROllamaEXAONEGemmaGeminiDocker
ERD테이블 23개 · Bounded Context 5개
Life Tutorial ERD

‖ 정확히 하나 ○< 0개 이상 · 각 FK 컬럼에서 부모 테이블의 PK로 연결

불러오는 중…

SoundBridge 메인 화면 — '당신의 Playlist 언어로 국악을 소개합니다' 문구와 곡 검색창, 추천 검색어, 국악 둘러보기 카드 목록이 보인다
03팀 BeyondFacade · 팀장 / 백엔드

SoundBridge사운드브릿지

“비 오는 날 아이유 듣는 기분”처럼 자연어로 취향을 말하면 국립국악원 공공 음원 500곡 중 가장 가까운 3곡을 추천 이유와 함께 찾아주는 서비스.

  • 500곡을 검색 가능한 데이터로. 공공 음원 적재 → EXAONE으로 곡 설명 미리 생성 → Cohere 임베딩 → R2 업로드로 이어지는 데이터 파이프라인을 직접 만들었습니다.
  • 뜻으로 찾는 곡 검색. Cohere embed-v4 임베딩과 pgvector HNSW 부분 인덱스로 의미 기반 검색을 합니다. 부분 인덱스라서 임베딩이 있는 곡만 색인합니다.
  • LLM이 실패해도 추천은 나온다. 추천 설명은 3단계 대체 흐름으로 만듭니다. EXAONE 묶음 요청 → 곡별 동시 요청 → 템플릿 문장 순서입니다. 답이 부실하거나 90초를 넘기면 다음 단계로 넘어가서, 추천 결과는 항상 반환됩니다.
  • 같은 질문은 다시 계산하지 않는다. 같은 입력의 검색 결과를 Redis에 1시간 캐시해, 반복 요청의 LLM 호출과 응답 시간을 줄였습니다.
  • 도메인은 프레임워크를 모른다. Hexagonal · DDD로 설계해 도메인 계층이 FastAPI · SQLAlchemy를 모릅니다. 영속성은 Repository 계층에 격리했습니다.
  • 역할별로 나눠 배포. FastAPI는 Railway, Next.js는 Vercel, 음원은 Cloudflare R2에 올렸습니다.
FastAPINext.jsEXAONEpgvector HNSWRedisCloudflare R2Docker
ERD테이블 4개 · PostgreSQL + pgvector
SoundBridge ERD

‖ 정확히 하나 ○< 0개 이상 · FK 컬럼에서 부모 테이블의 PK로 연결 · ORM 기준

불러오는 중…

REMAKE DAY 플레이 화면 — 1일째 기상 장면, 지하 대피소에서 네 사람이 배급을 기다리는 그림과 관리자의 방송 대사, 인물 선택 패널과 질문 입력창이 보인다
04팀 BeyondBob · 백엔드 / AI

REMAKE DAY리메이크데이

“사람은 언제 AI의 제안을 받아들일까?” 실패 → AI 제안 → 사람의 선택 → 규칙 적용 → 재실행으로 이어지는 에이전트 운영 루프를 게임으로 재현하고, 사람의 판단을 데이터로 쌓는 실험형 게임.

  • 줄거리가 곧 에이전트 운영 루프. 매일 멸망하는 대피소의 진짜 정체는 돼지농장이고, 유저는 끝까지 자기가 사람이라고 믿습니다. 멸망할 때마다 규칙을 고쳐 하루를 다시 돌리는데, 이 구조가 ‘실패 → 규칙 수정 → 재실행’ 루프입니다.
  • 하네스를 고치는 사람의 선택을 기록. 유저는 규칙을 직접 쓰거나 AI 제안을 골라 세계에 겁니다. 여기서 하네스는 AI가 따르는 규칙과 틀을 말합니다.
  • 사람은 AI 제안을 얼마나 받아들였나. 사람 플레이 14판을 관찰했습니다. 적용된 규칙 60건 중 48건(80%)이 AI 제안이었고, ‘원숭이손’ 제안은 21건 중 17건(81%)이 수락됐습니다. 5회차까지 완주한 판은 14판 중 10판입니다.
  • 수치는 코드가, 판단은 LLM이. 도메인 계층은 LLM 없이 테스트할 수 있는 순수 함수입니다. 코어는 시나리오를 모르는 Hexagonal 구조입니다.
  • 모델은 미리 정한 게이트로 선정. 로컬 · 외부 4개 조건으로 비교 실험을 372회 실행했고, 폴백 실패는 0건이었습니다. NPC 모델은 라이선스 문제로 교체했습니다. 제출용은 Claude, 로컬 롤백용은 Gemma 4 · Kanana로 준비했습니다.
  • 채점이 불안정하다는 것도 공개. 같은 답이 67.9점과 53.3점으로 갈렸고, md5까지 같은 제출이 temperature 0.0에서도 판정이 뒤집혔습니다. 단조 잠금을 도입한 뒤 6판에서 4→5회차 점수 하락은 0건이었습니다. 다만 이는 잠금이 작동한다는 근거일 뿐, 채점기가 안정적이라는 근거는 아닙니다.
HexagonalClaude Sonnet · HaikuGemma · GeminiLLM 채점Human-in-the-LoopDocker
ERD테이블 15개 · 외래 키 5개
REMAKE DAY ERD

‖ 정확히 하나 ○< 0개 이상 · 실선 = DB 외래 키, 점선 = 코드로만 지키는 관계 · ref = 외래 키 없는 참조

불러오는 중…

Metabole 지도 탐색 화면 — 서울 행정동별 창업 경고 등급을 색으로 칠한 지도와 상점 클러스터, 압구정동 카페 업종의 District Brief(비추천 판정과 근거 지표) 패널이 보인다
05팀 BeyondFacade · 풀스택 · 진행 중

Metabole메타볼레

“역삼동 카페” 한 줄이면 상권 분석부터 손익분기, 받을 수 있는 지원금까지. ‘이 동네에서 이 업종으로 창업해도 되는가’를 공공데이터 근거로 답하는 서울 상권 분석 에이전트.

  • 서울 상권을 데이터로 정리. 서울 427개 행정동 × 18개 업종을 인허가 · 개폐업 · 추정매출 · 유동인구 · 임대료 · 금리로 구조화했습니다. 47개 테이블, 약 1,166만 행입니다.
  • 판정이 맞는지 과거 데이터로 검증. 위험 신호로 동 × 업종을 비추천 · 조건부 · 경고 없음으로 판정하고, 과거 개업 데이터로 백테스트했습니다. 비추천 판정의 3년 내 폐업률은 경고 없음의 1.70배였습니다(카페 2.01배). 기준(1.10배)에 못 미친 편의점 · 부동산은 판정 대상에서 뺐습니다.
  • 맡은 일. Hexagonal 모듈러 모놀리스(21개 Bounded Context)에서 백엔드 BC와 프론트 feature를 구현했습니다. 데이터 수집과 RAG 평가셋 검수도 맡았습니다.
  • 매일 새 공고를 읽는 Hybrid RAG. 공고 · 뉴스를 매일 수집합니다. Gemini와 로컬 Gemma를 섞어 온프레미스에서도 동작합니다. 평가 180건에서 Hit@5 1.00 · MRR 0.95입니다.
  • 테스트와 자동화. TDD로 백엔드 테스트 1,072건, 프론트 약 580건을 작성했습니다. cron은 9종입니다(수집 5 · RAG 색인 1 · 운영 2 · 개발일지 1).
Hexagonal · DDDFastAPINext.js 16pgvector · Neo4jHybrid RAGTDDDocker
ERD테이블 47개 · 외래 키 49개
Metabole ERD

‖ 정확히 하나 ○< 0개 이상 · 실선 = DB 외래 키, 점선 = 애플리케이션 레벨 관계 · 키 컬럼만 표시, 이름 옆 숫자는 실제 적재 행 수(2026-10-01)

불러오는 중…


How I work

구조는 단단하게,
판단은 사람이.

01 / Architecture

바뀌어도 흔들리지 않게

핵심 로직은 가운데에 두고, DB나 AI 모델은 바깥에 끼워 쓰는 부품처럼 다룹니다. 그래서 비싼 API를 무료 로컬 모델로 바꿀 때도 핵심 코드는 그대로 두었습니다.

02 / Fractal

모든 기능을 같은 모양으로

기능마다 똑같은 구성의 파일 묶음으로 만듭니다. 모양이 같으니 AI가 헷갈리지 않고, 실수해도 그 묶음 안에서만 영향이 납니다.

03 / Patterns

if문 대신 GoF 디자인 패턴

조건문을 줄줄이 늘어놓는 대신 GoF의 디자인 패턴(Strategy · State · Factory 등)으로 정리합니다. 새 기능을 붙일 때 기존 코드를 덜 건드리고, AI에게도 한 단어로 의도를 전할 수 있습니다.

04 / Harness

AI가 일할 틀을 짓는다

사실 확인은 DB가, 말로 풀어내는 작업은 AI가 맡도록 나눕니다. AI가 실패하면 다른 모델과 규칙이 이어받아 서비스가 멈추지 않습니다. 개발할 때도 CLAUDE.md에 규칙과 검증 기준을 적어 두고, AI가 그 안에서 코드를 쓰게 합니다.

05 / Verify

완성의 기준부터 정한다

무엇이 되면 ‘완성’인지 테스트로 먼저 적고, 그 테스트를 통과시키는 식으로 일합니다. AI 모델도 미리 정한 평가셋을 통과한 모델만 씁니다.

06 / Human-in-the-Loop

책임은 사람이 진다

AI에게 코딩을 맡겨도, 중요한 작업일수록 더 꼼꼼히 검토합니다. 마지막 판단과 책임은 언제나 사람의 몫입니다.

사람이 정한다
  • Bounded Context
  • Port · 인터페이스
  • 도메인 규칙
  • TDD 시나리오
  • 검토 등급
AI가 채운다
  • UseCase
  • Adapter
  • Repository
  • DTO · Mapper
A
무거움 비용 · 보안 · 인증 · DB 마이그레이션 · 채점 로직task마다 구현 검토 + 최종 전체 리뷰
B
보통 게임 로직 · UI 동작 변경최종 전체 리뷰 1회, 단순 task는 개별 검토 생략
C
가벼움 문서 · 시나리오 문장 · 자산 등록 · 테스트 정리diff와 테스트 결과만 확인

리뷰어가 Critical을 내면 자동으로 한 등급 올리고, 내리는 건 사람만 할 수 있습니다.


Profile

이력.

학력

한영외국어고등학교 일본어과 (졸업)

University College London BSc Psychology (중퇴)

이화여자대학교 영어영문학부 (졸업)

수상

제8회 K-디지털 트레이닝 해커톤 고용노동부 장관상 (우수상)

어학

OPIc 영어 AL · OPIc 일본어 AL · JLPT N1 · OPIc 중국어 IM2