일곱 개의 장으로 무엇을 왜 그렇게 만들었는지, 판단과 결과를 실제 화면과 함께 정리했습니다.
사내 첫 독립 프론트엔드부터 오픈소스 기여까지, 5년의 작업을 일곱 개의 장으로 묶었습니다. 각 주제는 서로 이어진 하나의 흐름입니다.
스트리밍 챗 화면은 응답이 이어지는 내내 갱신을 반복합니다. 도착 중인 마크다운도 읽을 수 있게 렌더해야 하고, 생성은 언제든 멈출 수 있어야 합니다. 2023년의 수동 SSE 파싱에서 어댑터 구조를 거쳐 라이브러리 내부 확장까지, 3년의 판단을 담았습니다.
표준 EventSource는 GET 전용이라 POST로 여는 LLM 스트림에 사용할 수 없었습니다. 2023년 국회 파일럿에서 fetch 기반 SSE 파서를 직접 구현해 프레임 구분 · 이벤트 경계 · 연결 수명까지 다뤘고, 백엔드가 늘어난 지금은 형식 변환을 BFF 어댑터 한 곳에 모아 화면은 Vercel AI SDK 프로토콜 하나만 다룹니다.
마크다운 문법은 완성된 문서를 전제로 합니다. 요소마다 미완성의 위험이 달라 렌더 시점을 분리했습니다. 코드는 열리는 즉시 증분 하이라이트되고, 다이어그램은 완성을 감지한 뒤에만 렌더되며, 수식은 완성되는 순간 원문에서 수식으로 전환됩니다.


2년 넘게 자체 구현으로 운영하다 assistant-ui를 도입했고, 라이브러리에 없는 개념이 필요해지자 렌더 primitive의 그룹핑 상태머신을 패치해 참조문서 그룹을 새 개념으로 추가했습니다. 도메인 특화 개념이라 업스트림 PR 대신 최소한의 패치로 구현했습니다. 라이브러리가 0.12에서 그룹 렌더를 공개 API로 열자 패치를 걷어내고 그 API로 옮겼습니다. 인용을 클릭하면 문서 뷰어가 해당 페이지로 이동해 문장 좌표를 하이라이트합니다. 뷰어에는 포맷별 처리가 없습니다.


납품 현장에서만 느린 화면이 있었습니다. 사내 회선에서는 0.2초에 열려 재현되지 않았고, Slow 4G 시뮬레이션에서야 13초로 재현됐습니다. 측정으로 병목을 특정하고, 변경 전후를 같은 조건으로 검증해 반영했습니다.
Turbopack이 라우트별 번들 리포트를 제공하지 않게 되어 빌드 매니페스트를 직접 파싱해 계측했습니다(116개 라우트, 실측 대조 오차 10% 이내). 주 폰트 서브셋만으로 같은 Slow 4G 조건에서 LCP가 13초에서 6.2초로 줄었습니다. next/font의 preload를 끄는 설정 두 줄로 재설계해 초기 폰트 전송이 90.5% 줄었고, woff2 변환 · 서브셋으로 폰트 자산을 52.1MB에서 5.9MB로 정리했습니다.
약 2천 개 아이콘 선택 UI를 가상 그리드로 바꿔 DOM 노드 −97%(11,637 → 339), 렌더 완료 5.3배(160 → 30ms), 목록 복귀 15배(137 → 9ms) — 아이콘 1,951개, 전후 실측 9회 중앙값. 관리 화면의 대형 트리는 지연 로딩과 무한스크롤로 바꿨습니다. 키 입력마다 번지던 리렌더는 memo 경계와 원시값 props로 억제했습니다.


시작은 앱 하나였습니다. 세 번의 구조 전환을 거쳐, 지금은 하나의 모노레포에 21개 앱이 공통 패키지를 공유하며 각자 배포됩니다. 개발 환경과 CI까지 함께 만들었습니다.
인증 · API 클라이언트 · 설정 · 디자인 시스템을 공통 패키지 4종으로 분리했고, 새 앱은 스캐폴딩 제너레이터로 같은 규약 위에서 출발합니다. 전 제품의 인증은 별도의 IdP가 맡습니다. 현행 공공 납품은 이 메인 코드베이스를 그대로 사용하고, K-water GPT · On-AI는 이전 세대에서 파생됐습니다. 동적 메뉴는 셸에 등록하는 API로만 렌더되고, 셸을 우회하는 구현은 앱 소스를 파싱해 규약 위반을 잡는 거버넌스 테스트가 차단합니다.




설정을 코드 밖으로 꺼내 중앙 설정 서버(platform-core)에 두고, 바뀐 값은 각 앱에 SSE로 실시간 반영됩니다. 고객사별 UI 차이는 코드 분기 없이 런타임 레이아웃 설정으로 수용합니다. 감사 로그와 세션 리플레이는 PostHog를 하나의 이미지로 묶은 분석 서비스(analytics-service)가 맡고, 브라우저 수집은 개인정보 마스킹이 기본입니다.





공유 인프라(인증 서버 · 설정 서버 · 프록시)는 하나만 두고, 개발자 PC에는 모노레포만 남겼습니다. git 사용자명 기반으로 서브도메인을 동적으로 발급해, 자기 이름의 주소로 접속하면 운영과 같은 인증 흐름을 거쳐 각자의 로컬 dev 서버로 라우팅됩니다. AI 에이전트가 작업한다는 전제로 디렉터리별 작업 규칙 문서(AGENTS.md)를 두고, 산출물은 lint · 계약 검사 · E2E 게이트를 통과한 뒤 리뷰합니다.
사내 GitLab에 CI를 최초로 도입했습니다. 지금은 20개 레포지토리가 쓰는 표준이 됐고, 누적 파이프라인은 5,000개를 넘었습니다(2026.08 기준). 브랜치가 배포 환경에 대응하고, 이미지는 CI 전용으로 새로 구축한 레지스트리에 저장합니다.


기관마다 제약이 달랐습니다. 망이 분리되어 있거나, 보안 심사를 받거나, 문서가 DRM으로 암호화되어 있었습니다. 제약을 하나씩 풀다가 2026년에는 인증 서버를 직접 만들게 됐습니다.
보안 심사에서 반복해 나온 지적을 구조로 풀었습니다. 리버스 프록시의 forward auth가 JWT를 프록시 뒤에서 붙이기 때문에 토큰이 브라우저로 내려가는 일 자체가 없습니다. 권한 판정 결과는 TTL 60초로 캐시하되 권한이 바뀌면 권한 버전을 올려 즉시 무효화하고, 정책이 등록되지 않은 경로는 fail-closed로 차단합니다. 기관 PC에서 올라오는 DRM 문서는 전담 서비스가 C++ 네이티브 모듈을 FFI로 호출하고 워커로 병렬화해 복호화합니다.
국가정보원 국가 망 보안체계(N2SF) 가이드라인을 준용해 oidc-provider를 기반으로 어댑터 · 정책 · 화면 · 감사 계층을 직접 구현해 OAuth2/OIDC 인증 서버를 만들었고, 레거시 인터페이스와 호환성을 유지해 소비 앱 수정 없이 교체했습니다. 로그인 · 동의 · 회원가입 화면은 인증 서버 레포지토리 안에서 React SSR로 만들었습니다.


큰 파일을 올리는 동안에도 사용자는 화면을 옮겨 다닙니다. 업로드 상태를 Service Worker가 소유하고 IndexedDB에 저장하게 해 라우팅과 새 탭에도 이어지는 대기열을 만들었고, 청크 단위 재시도와 취소 전파까지 담았습니다.

1세대 코드베이스에서는 기관별로 분기해 브랜딩과 화면 구성을 달리 납품했고, 지금은 분기 없이 단일 코드베이스로 납품합니다. K-water GPT 납품은 국내 공공기관 중 RAG 기반 업무 시스템을 정식 오픈한 최초 사례로 보도됐습니다(인공지능신문, 2025.02).




요구사항 명세(SRS)와 유스케이스(UC) 문서를 받아 사이트맵을 설계하고, 화면마다 컴포넌트 구성을 정한 뒤 Next.js 코드를 생성합니다. LLM의 생성을 어디까지 허용하고 어떻게 판정할지를 가장 오래 고민했습니다. 기준은 프론티어 모델과 인터랙티브하게 작업해 직접 다듬은 골든 페이지입니다.
LLM이 편집할 수 있는 위치를 marker 구간으로 한정하고 import는 규칙으로 주입합니다. 존재하지 않는 모듈을 불러오는 일 자체가 생기지 않습니다. 컴포넌트 레지스트리는 pgvector 벡터 검색으로 조회하고, 호출마다 흔들리는 LLM 한글 출력은 자모 분해 유사도 매칭으로 흡수합니다.
완성되지 않은 JSON을 증분 파싱해 노드가 도착하는 대로 트리에 붙입니다. 구조 데이터는 일부만 도착한 상태에서도 항상 올바른 트리여야 합니다. 수정 요청이 반영되면 변경된 노드만 하이라이트하고 첫 변경 노드로 스크롤합니다.


판정은 LLM에 맡기지 않고, 기준을 코드로 정의했습니다. 실패 유형 45종 게이트와 Playwright DOM 실측을 거치고, 상호작용 컴포넌트는 9단계 시나리오로 검사합니다. ts-morph 상주 인스턴스 풀로 생성 코드 재검증 시간이 초 단위에서 밀리초 단위로 줄었고(매 검증마다 tsc를 새로 실행하던 기존 구조 대비 실측), 재시도는 전역 중단 대신 페이지 단위 상한으로 관리합니다.


회사 홈페이지의 디자인과 초기 코드베이스를 만들었고, 제품 화면의 반응형 규칙을 요소 단위로 설계합니다. 지금 보고 계신 이 사이트도 같은 원칙으로 직접 만들었습니다.
2023년 말 리뉴얼부터 지금까지 관여해 온 홈페이지입니다. 코드베이스 초기 구성과 디자인을 맡았습니다. 데스크톱부터 모바일까지 반응형으로 동작하며, 인포그래픽은 직접 그려 사용했습니다.






제품 상단바의 접힘 규칙을 요소별로 정했습니다. 로고는 폭 고정, 브레드크럼은 마지막 항목만, 툴바 버튼은 라벨을 숨기고 아이콘만 남깁니다. 시작은 농협은행 · 교보생명 RPA 포털 디자인과 UI 개발이었고, 교보 포털에서는 Parcel · Pug · Sass 빌드 파이프라인을 직접 구성했습니다(2021–2022).


업무에서 shadcn/ui로 개발하다 공공 고객사의 KRDS(정부 디자인 시스템) 적용 요구를 받을 때마다 불편했습니다. 동료와 함께 KRDS 가이드라인을 shadcn/ui 방식의 컴포넌트로 옮기고 있습니다. npx shadcn add로 설치하면 소스가 그대로 프로젝트 코드가 됩니다.



vllm-project/vllm-metalUI 생성 에이전트의 LLM 서빙을 검증하기에는 다른 트래픽이 섞이지 않는 로컬이 나았고, 개발용 Mac은 하드웨어로는 충분했습니다. 사용하려던 모델이 vLLM의 Apple Silicon 플러그인에서 지원되지 않아 지원을 직접 추가했습니다. Metal 커널 작성과 GPU 행(hang) 수정, 리뷰 반영을 거쳐 PR 5건이 머지됐고, 변경마다 벤치마크를 측정 조건과 함께 커밋에 남겼습니다.