박민후

새로운 문제를 찾고 있습니다

복잡한 문제를단순한 구조로 풀어냅니다.

소프트웨어 엔지니어 박민후입니다. 지난 2년간 병원용 CRM을 데스크톱(C#/WPF)에서 웹(React), 백엔드 API(Kotlin)까지 스택을 넓혀 가며 만들어 왔습니다. 문제를 발견하면 구조를 제안하고, 반복되는 일은 자동화합니다.

소개

2024년부터 스마트닥터에서 병원용 CRM을 만들고 있습니다. 데스크톱 앱(C#/WPF) 유지보수로 시작해 웹 전환(React)에서 담당 모듈의 설계와 구현을 맡았고, 지금은 백엔드 API(Kotlin/Spring)까지 직접 개발합니다. 한 사람이 프론트부터 백엔드까지 다루면 개발 속도와 정합성이 훨씬 좋아진다고 판단했고, 실제로 그렇게 일하고 있습니다.

증상보다 구조를 고치는 쪽을 선호합니다. 예약 1건이 바뀔 때마다 화면 전체를 다시 조회하던 구조를 단건 갱신으로 바꾸고, 64비트 전환을 막던 32비트 전용 연동 모듈들을 별도 프로세스로 분리해서 같은 문제가 반복되지 않게 만들었습니다.

반복 작업은 자동화합니다. 릴리즈 태깅·Jira 버전 관리·리뷰어 지정을 GitHub Actions로 자동화했고, 고객 스크린샷으로 오류를 파악하던 환경에 Sentry와 로딩속도 모니터링 봇을 도입했습니다. 도구를 만들어 팀의 시간을 아끼는 일을 좋아합니다.

기술

언어
TypeScriptC#KotlinSQL
프론트엔드
ReactNext.jsZustandTailwind CSSFSD 아키텍처
백엔드
SpringJPA/HibernateKafkaWebSocketMSSQLAWS S3
데스크톱
.NET · WPF · WinFormsWebView2 브릿지프로세스 간 통신(IPC)
도구 · 관측성
GitHub ActionsSentryGA4Jira 자동화

경력

  1. 2024.08 — 현재

    스마트닥터

    소프트웨어 엔지니어 — 병원용 CRM 개발

    • 데스크톱 CRM(C#/WPF) 유지보수·기능 개발로 시작해 웹 전환(React)에서 담당 모듈의 설계·구현을 수행하고, 백엔드 API(Kotlin/Spring)까지 직접 개발하며 담당 범위를 확장
    • 웹 CRM 전환에서 월 단위 예약 캘린더, 진료 기록 화면, 시술 이력 조회 도구를 담당하고, 월 전체 일괄 조회를 주 단위 병렬 요청으로 나눠 서버의 날짜 범위별 캐시 활용
    • 예약 1건 변경에도 화면 전체를 재조회하던 데스크톱 새로고침 구조를 단건 갱신으로 개선하고, 내장 브라우저 CefSharp → WebView2 교체를 제안·주도
    • 네이티브 화면 3종을 'API 신설 → 독립 웹 앱 → 웹뷰 임베드' 패턴으로 이식하는 등 웹·API·데스크톱 3개 코드베이스에 걸친 크로스 스택 개발
    • 콜센터 옵션 조회를 배치 API로 통합해 운영에서 확인한 카테고리 12개 조건의 최초 조회를 12회에서 1회로 축소. 매칭 번호 1,000개를 넣은 재현 실험에서는 검증 후 추가 고객 검색 요청 1,000회를 제거
    • 캐시닥 병원 CMS의 모바일 운영 화면 구축과 4개 저장소에 걸친 상담 알림 연결. 스키마 → API → 화면 → 적재 순으로 배포해 마지막 적재 단계만 되돌려도 기존 동작을 유지하는 구조 구성
    • 고객 스크린샷에 의존하던 오류 파악을 Sentry 도입과 GA4 기반 화면 로딩속도 모니터링 봇 구축으로 자동 수집·알림 체계로 전환
    • rc/hotfix 자동 태깅, Jira 릴리즈 자동 생성, rc 간 cherry-pick 체이닝, AI 코드리뷰 봇 운영 등 릴리즈·리뷰 자동화 체계 구축

    TypeScript / React / C# / .NET / Kotlin / Spring / Kafka / MSSQL / GitHub Actions / Sentry

프로젝트

재직 중 수행

데스크톱 → 웹 CRM 전환

2025 — 현재

스마트닥터 · 담당 모듈 설계·구현, 실행 기반까지

데스크톱 CRM의 예약·진료 화면을 웹으로 옮기는 장기 전환 프로젝트입니다. 예약 캘린더와 진료 기록 화면을 설계·구현하고, 인증·네이티브 브릿지·WebView2 런타임 배포까지 함께 구축했습니다. 이후 'API 신설 → 독립 웹 앱 → 웹뷰 임베드' 패턴을 확립해 데스크톱 전용 화면 3종을 이식했습니다.

웹·API·데스크톱 3개 코드베이스에 걸친 크로스 스택 개발

React / TypeScript / Zustand / Kotlin / C# / WebView2

데스크톱 → 웹 CRM 전환

스마트닥터 · 담당 모듈 설계·구현, 실행 기반까지 · 2025 — 현재

데스크톱 CRM의 핵심 업무 화면을 하나씩 웹으로 옮기는 전환 프로젝트로, 예약 현황판에서 시작해 화면 3종을 독립 웹 앱으로 이식하는 패턴을 확립하는 데까지 나아갔습니다.

데스크톱 → 웹 CRM 전환담당 범위임베드브릿지REST데스크톱 CRMC# · WPFWebView2 호스트웹 앱ReactAPIKotlin · SpringMSSQL
데스크톱 CRM 안의 WebView2 호스트가 웹 앱을 띄우고, 웹 앱은 신설한 API를 거쳐 데이터베이스에 닿습니다. 웹 앱과 API가 담당 범위입니다.

배경

데스크톱 CRM은 병원 접수·상담 데스크에서 쓰는 C#/WPF 프로그램입니다. 이 프로그램의 핵심 화면을 웹으로 옮기는 전환은 2025년부터 지금까지 이어지는 장기 프로젝트로, 데스크톱과 웹 두 코드베이스에서 시작해 이후 백엔드 API 코드베이스까지 넓어졌습니다.

전환은 데스크톱 CRM 안에 WebView2 호스트를 두고 화면을 웹으로 하나씩 옮기는 방식으로 진행됐습니다. 데스크톱을 통째로 다시 만드는 대신 화면 단위로 점진적으로 이식하는 방식을 택해, 이식을 마친 화면부터 순서대로 실사용에 들어갈 수 있었습니다.

한 일

월 단위 예약 캘린더 화면인 월별보기부터 맡아 레이아웃과 셀 구조, 부서 목록 무한 스크롤, 컬럼 폭 계산을 설계하고 구현했습니다. 이후에도 전환 프로젝트에서 맡은 화면마다 설계부터 구현까지 함께했습니다.

월 전체 예약을 한 번에 조회하던 방식을 주 단위 병렬 요청으로 나누고, 서버의 날짜 범위별 캐시를 활용하도록 바꿨습니다. 전후 코드 재현에서 35일 조회를 7일씩 5개 요청으로 나눠도 최종 예약 결과가 같은 것을 확인했습니다.

웹이 데스크톱 안 WebView2로 실행되는 구조라 실행 기반도 함께 다뤘습니다. refresh token 기반 인증 구조를 도입하고, 데스크톱과 웹이 설정 변경 같은 이벤트를 주고받는 브릿지를 정비했으며, 특정 병원 PC 환경에서 반복되던 오류를 계기로 WebView2 런타임을 고정 배포하는 방식으로 바꿨습니다.

대표 구현: 진료 기록과 데이터 정합성

웹 전환에서 맡은 화면 가운데 가장 큰 모듈인 진료 기록 화면을 신규 구축했습니다. 상병·처방 입력부터 진료비·진찰료 산정, 시술 이용권 사용과 저장까지 하나의 업무 흐름으로 연결했습니다. 저장 게이트에 중복 생성 방지를 넣고 폼을 단일 진실 원천으로 정리해, 연속 저장과 팝업 상태 유지 문제를 해결했습니다.

외부 연동과 자체 검증 도구

진료 화면의 처방 저장 흐름에 심평원(HIRA)의 의약품 안전 점검(DUR)을 통합했습니다. 웹 점검 팝업과 백엔드 브로커 연동을 함께 구축하고, 확인번호 발급 → 점검 호출 → 결과 확인 → 저장으로 이어지는 흐름을 만들었습니다.

외부 서버와 주고받는 결과는 화면만으로 확인하기 어려워 dur-conformance CLI를 별도로 만들었습니다. 실제 연동과 같은 호출 경로를 재현하고 YAML로 선언한 케이스의 응답을 기대값과 자동 대조합니다. CI에는 연결하지 않고 송신전문이나 점검 로직을 바꾼 뒤 직접 실행해 확인했습니다.

패턴 확립

전환 후반에는 'API 신설 → 독립 웹 앱 → 웹뷰 임베드'라는 패턴을 세우고, 데스크톱 전용 화면 3종(숙제조회, 진료현황 보드, 접수·대기상태 현황판)에 그대로 반복 적용했습니다. 화면마다 필요한 API를 먼저 만들고 독립된 웹 앱으로 구현한 뒤 데스크톱 웹뷰에 연결하는 순서를 지킨 덕분에, 세 화면을 같은 흐름으로 빠르게 이식했습니다.

장애 시 자동 조회 조정

자동 조회가 연속으로 실패할 때 간격을 늘리도록 했습니다. 기본 주기가 60초인 화면은 3회 연속 실패 후 300초 간격으로 전환합니다. 장애가 지속되는 구간의 주기상 조회 횟수는 시간당 60회에서 12회로 80% 줄어듭니다. 이는 코드의 주기를 환산한 값이며 실제 운영 트래픽 감소율은 아닙니다.

React / TypeScript / Zustand / Kotlin / C# / WebView2

콜센터 상담 관리 시스템

2026

스마트닥터 · 프론트엔드와 백엔드 API를 함께 개발

병원 콜센터의 상담 건(리드) 수집·배분·이력 관리를 담당하는 신규 웹 모듈. 전화 연동 미들웨어와 WebSocket으로 통신해 수신 전화에서 상담 건을 자동 생성하고, 엑셀 대량 업로드·개별 등록·수신 전화 자동 생성의 수집 채널 3종과 서버사이드 필터 체계를 갖췄습니다. 수신 1건에 상담 건이 중복 생성되던 경쟁 조건, 재연결 불안정 같은 현장 문제를 요구 접수부터 검증까지 짧은 주기로 해결했습니다.

운영에서 확인한 카테고리 12개 조건의 최초 옵션 조회 재현: 12회 → 1회, 91.7% 감소

React / TypeScript / Kotlin / WebSocket / MSSQL

콜센터 상담 관리 시스템

스마트닥터 · 프론트엔드와 백엔드 API를 함께 개발 · 2026

병원 콜센터의 마케팅 리드와 인콜 상담을 관리하는 신규 웹 모듈을 처음부터 구축했습니다. 전화 연동, 리드 수집 채널 3종, 상담 이력 관리까지 화면과 API를 함께 담당했습니다.

콜센터 상담 관리 시스템담당 범위수신 이벤트CTI 미들웨어전화 연동WebSocket지수 백오프 재연결상담 화면React상담 APIKotlin엑셀 대량 업로드단일 등록MSSQL
수신 전화, 엑셀 대량 업로드, 단일 등록 세 갈래로 상담 건이 들어옵니다. 전화 연동은 CTI 미들웨어와 WebSocket으로 통신하며, 화면과 API를 함께 담당했습니다.

배경

병원 콜센터에서 발생하는 마케팅 리드(상담 DB)와 인콜(수신 전화) 상담을 관리하는 신규 웹 모듈입니다. 콜센터를 운영하는 대형 성형외과 고객사의 실사용 피드백이 티켓으로 집중적으로 들어오면서, 요구를 접수하고 개발하고 현장에서 검증하는 짧은 주기로 고도화가 이어졌습니다. 도입 초기에는 해당 고객사 한 곳이 사용했고, 하루 약 480건의 리드·콜을 처리하는 규모였습니다.

전화 연동은 '메디콜' CTI 미들웨어와 로컬 WebSocket으로 통신합니다. 연결이 끊기면 지수 백오프로 다시 연결합니다. 이 프로젝트는 화면뿐 아니라 백엔드 API까지 함께 개발했습니다.

수집 채널 3종

상담 건은 엑셀 대량 업로드, 단일 등록, 인콜 수신 자동 생성 세 갈래로 들어옵니다. 엑셀 대량 업로드는 업로드, 고객 매칭, 데이터 정정으로 이어지는 위저드로 만들었습니다. 실패한 행은 안내 모달로 알려주고, 고객 매칭 결과는 서버가 내려주는 matchType을 그대로 분류 기준으로 삼습니다. 검증(validate) 응답에 고객 후보를 함께 담아 보내, 매칭 대상의 고유 전화번호마다 고객을 다시 검색하던 호출을 없앴습니다.

단일 등록 모달은 연락처를 자동으로 매칭하고, 유입경로 1·2 항목을 필수 입력으로 두면서 두 값을 동시에 입력하도록 검증했습니다. 인콜이 수신되면 로그인한 상담사를 자동으로 지정해 리드를 생성하고, 등록되지 않은 번호는 등록 모달을 자동으로 띄웠습니다.

조회 화면에는 8개 컬럼에 헤더 필터를 달고 유입경로를 2단 트리 필터로 좁힐 수 있게 했으며, 필터 상태는 localStorage에 저장해 유지합니다. 이후 컬럼 필터를 서버사이드로 전면 전환해 목록·탭·엑셀 내보내기가 같은 결과를 보여주도록 맞췄습니다. 상담 이력은 고객 단위로 통합 조회하는 API를 신설하고, 상담기록 수정·삭제 기능도 함께 만들었습니다. 권한은 데스크톱 권한 트리, API의 enum, 웹의 권한 체크 세 저장소에 걸쳐 CCMS 전용 코드 11종을 추가했습니다.

현장에서 드러난 문제

인콜 1건에 상담 리드가 2개 생기는 경쟁 조건이 있었고, 통화가 중간에 끊기면 작성 중이던 상담 드래프트가 사라지는 문제도 있었습니다. 둘 다 수정해 인콜 흐름의 안정성을 높였습니다.

이후 2026년 7월에는 유입경로 카테고리마다 옵션을 조회하던 N+1 호출을 제거하고, 전체 목록을 한 번에 가져오는 배치 조회 API로 바꿨습니다.

측정 결과

고객 매칭의 변경 전후 코드를 실행해, 서로 다른 자동 매칭 번호 1,000개에서 검증 후 추가 검색이 1,000회에서 0회로 줄어든 것을 확인했습니다. 중복 번호, 신규 고객, 동명이인 후보가 섞인 조건에서도 매칭 결과는 같았습니다. 최초 파일 검증과 최종 등록 요청은 측정 범위에 포함하지 않았습니다.

최근 운영 로그의 응답 26건에서 한 고객사의 유입경로 카테고리가 12개인 것을 확인했습니다. 이 개수를 적용한 재현 실험에서 최초 옵션 조회는 12회에서 1회로 91.7% 줄었고, 카테고리 조회까지 포함하면 13회에서 2회로 84.6% 줄었습니다. 옵션 캐시가 유지되는 즉시 재진입에서는 전후 모두 추가 옵션 요청이 없었습니다.

엑셀 내보내기에서는 셀 병합의 중복 검증을 제거했습니다. 가상 리드 300건으로 만든 600행·4,500개 병합 범위의 로컬 벤치마크에서 파일 생성 시간 중앙값이 1.77초에서 19ms로 98.9% 줄었습니다. Apache POI 5.4.1로 전후 각각 5회 측정하고 모든 셀 값과 병합 범위가 같은지 확인했습니다. 측정 범위는 파일 생성이며 DB 조회와 다운로드 시간은 제외했습니다.

React / TypeScript / Kotlin / WebSocket / MSSQL

토스 결제 단말 연동

2026

스마트닥터 · 데스크톱·웹·백엔드에 걸친 연동

CRM 수납 흐름에 토스 결제 단말을 연동했습니다. 데스크톱(.NET)에는 자동 재연결을 갖춘 WebSocket 클라이언트를, 웹에는 결제 세션 상태머신을 구현했고, 결제 플러그인과 CRM이 서로 다른 파드에 붙으면 단말을 찾지 못하던 인메모리 세션 레지스트리의 한계를 Kafka fan-out 릴레이 구조로 해결했습니다.

멀티 파드 환경의 결제 세션 라우팅 구조를 직접 제안·설계

Kafka / WebSocket / C# / React / Zustand

토스 결제 단말 연동

스마트닥터 · 데스크톱·웹·백엔드에 걸친 연동 · 2026

CRM 수납 흐름에 토스 결제 단말을 연동하는 프로젝트로, 데스크톱과 웹 양쪽 클라이언트 연동과 백엔드 안정화를 맡았습니다. 서로 다른 파드에 세션이 흩어지며 생기던 장애는 Kafka 릴레이 구조로 해결했습니다.

토스 결제 단말 연동멀티 파드WS 세션WS 세션CRM 수납 화면토스 결제 플러그인결제 단말API 파드 AAPI 파드 BKafka세션 릴레이
CRM과 결제 플러그인의 WebSocket 세션이 서로 다른 파드에 붙으면 인메모리 레지스트리로는 단말을 찾지 못합니다. 파드 사이를 Kafka로 중계해 어느 조합이든 세션이 이어지게 했습니다.

배경

CRM 수납 흐름에 토스 결제 단말(Front Plugin)을 연동하는 Epic이었습니다. 백엔드 결제 세션 인프라는 선행 구축되어 있었고, 데스크톱 CRM(.NET 6 WPF)과 웹 양쪽 클라이언트 연동, 백엔드 안정화를 맡았습니다. 토스 측이 회사가 운영하는 포인트 서비스인 메디캐시와 연동해 병원에 토스 단말기를 많이 공급하고 싶다는 제휴 의사를 타진해오면서 시작된 연동입니다.

이후에는 메디캐시와 토스 결제가 섞였을 때의 금액 정합성 문제를 반복해서 다뤘고, 단말 연동 방식이 리더 모드에서 클라이언트 모드로 바뀌면서 관련 화면과 설정도 정리했습니다. 2026년 8월 기준으로도 아직 운영 배포 전 단계입니다.

한 일

데스크톱 CRM에 자동 재연결과 앱 단위 단일 커넥션을 갖춘 WebSocket 클라이언트를 두고, 토스 세션·환불 서비스를 구현해 수납 화면과 연결했습니다.

웹에는 Zustand 기반 세션 상태 머신, 재시도 큐, WebSocket 훅으로 이루어진 결제 슬라이스를 구축했습니다.

백엔드에서는 결제 실패·중단 흐름을 안정화했습니다. 취소나 실패 시 플러그인에 session.abort를 보내 단말 화면이 멈춰 있는 문제를 없앴고, 연결이 끊긴 채로 방치되지 않도록 감시 로직을 보강했으며, WebSocket 핸들러의 보안 컨텍스트를 다시 설정하도록 했습니다.

그 뒤로는 메디캐시 정합성 문제를 연쇄적으로 수정했습니다. 포인트를 사용하면 간편수납 요약의 할인액이 갱신되지 않던 문제, 결제를 마쳐도 포인트만큼 잔액이 수납 대기에 남아 있던 문제, 처방을 다시 불러오면 오래된 금액으로 잔액이 되살아나던 문제, 예약 없이 생성된 진료에서는 사용분이 화면에 반영되지 않던 문제를 차례로 처리했습니다. 결제 방식이 리더 모드에서 클라이언트 모드로 바뀌면서 단말 기기 등록(페어링) 화면을 새로 만들고, 기존 환경설정에 있던 단말 연동 설정 UI는 없앴습니다.

멀티 파드 문제

결제 플러그인의 WebSocket과 CRM의 WebSocket이 서로 다른 파드에 붙으면 DEVICE_OFFLINE 오류가 났습니다. 세션 정보를 인메모리 레지스트리에 두었던 탓에, 두 연결이 각각 다른 파드로 갈 때는 상대 파드에 있는 단말의 정보를 알 방법이 없었습니다.

파드 사이로 세션 정보를 실어 나르는 Kafka fan-out 릴레이를 직접 제안하고 만들었습니다. 이제 두 연결이 어느 파드 조합으로 붙어도 각 파드가 상대 쪽 단말 정보를 알 수 있고, 파드 배치와 상관없이 결제 세션이 그대로 이어집니다.

Kafka / WebSocket / C# / React / Zustand

데스크톱 CRM 64비트 전환

2025

스마트닥터 · 직접 제안하고 주도

CRM 본체의 64비트 전환을 가로막던 것은 통신사별 전화 연동과 결제 단말기의 32비트 전용 DLL이었습니다. 이들을 별도 32비트 프로세스로 분리하고 본체와 IPC로 통신하는 구조를 설계해, 프로세스 생명주기 관리·자동 재시작·오류 로깅까지 갖췄습니다. 사용 환경의 OS 비트 분포를 Sentry로 수집해 전환 판단의 근거 데이터도 만들었습니다.

레거시 연동 호환성을 유지한 채 64비트 전환 기반 마련

C# / .NET / WPF / IPC / Sentry

데스크톱 CRM 64비트 전환

스마트닥터 · 직접 제안하고 주도 · 2025

CRM 본체를 64비트로 전환하면서, 32비트로만 제공되는 벤더 DLL을 별도의 32비트 프로세스로 분리하고 본체와 IPC로 연결했습니다. 직접 제안하고 주도한 과제입니다.

데스크톱 CRM 64비트 전환32비트 전용 벤더 DLLCRM 본체64비트IPC프로세스 간 통신브릿지 프로세스32비트전화 연동 DLL결제 단말 DLL
64비트 전환을 막던 것은 32비트로만 제공되는 벤더 DLL이었습니다. 이들을 별도 32비트 프로세스에 가두고 본체와 IPC로 통신하게 해서, 본체만 64비트로 올렸습니다.

배경

CRM 본체를 64비트로 포팅하는 데는 32비트로만 제공되는 외부 연동 DLL이 걸림돌이었습니다. 통신사별 스마트콜 API(KT·LG·SK)와 카드 결제 단말기(VAN) 연동 모듈이 여기 해당했습니다. 스마트콜은 CRM에 연동된 전화 시스템이고, 단말기 모듈은 수납 흐름에서 카드 결제를 처리합니다.

64비트로 올린 본체는 이 DLL들을 그대로 불러올 수 없어, 두 연동을 살린 채 전환할 방법이 필요했습니다. 작업은 2025년 7월 말에 시작해 9월까지 이어졌습니다.

한 일

이 DLL들을 별도의 32비트 전용 프로세스(Dll32Server)로 분리하고, 본체와 IPC로 통신하는 구조를 만들었습니다. KT·LG·SK 스마트콜 연동과 결제 단말기 연동 로직을 이 프로세스로 옮겼습니다.

프로세스 생명주기 관리 방식을 다시 정리하고, 본체를 멀티스레딩으로 바꿨으며, 자동 재시작과 오류 로깅을 붙였습니다. 필요한 서비스만 초기화하도록 개선했습니다.

이벤트 전달 구조도 함께 정비했습니다. 전역 eventSender를 설정하고 주입해 이벤트가 누락되던 문제를 해결했고, UI를 띄우는 KT DLL의 특성에 맞춰 STA 스레드에서 실행하도록 했습니다.

통신사 전체와 결제 단말기 전반에 걸친 회귀 테스트를 지원하고, QA에서 실패로 돌아온 건을 수정했습니다.

판단 근거

Sentry 수집 항목에 운영체제의 32비트·64비트 비율을 추가해 실사용 환경의 비트 분포를 확보했습니다. 이 항목은 같은 분기에 진행한 Sentry 관측성 강화 작업에 함께 실었습니다.

C# / .NET / WPF / IPC / Sentry

클라우드 코드 서명 전환과 Windows 앱 공용 CI 구축

2026.08–09

스마트닥터 · 사내 Windows 앱을 위한 공용 서명 CI 구축

물리 USB 인증 장치의 관리 부담과 CI/CD 자동화 제약을 해결하기 위해 Windows 코드 서명을 DigiCert KeyLocker 기반 클라우드 방식으로 전환했습니다. 사내 Windows 앱들의 기존 빌드 CI에서 재사용할 수 있도록 서명·검증 절차를 공용 GitHub Action으로 구축하고 팀에 사용 기준을 공유했습니다.

물리 USB 의존 없이 여러 앱에서 재사용하는 클라우드 코드 서명 CI

GitHub Actions / PowerShell / DigiCert KeyLocker

클라우드 코드 서명 전환과 Windows 앱 공용 CI 구축

스마트닥터 · 사내 Windows 앱을 위한 공용 서명 CI 구축 · 2026.08–09

물리 USB 인증 방식의 관리 부담과 CI/CD 자동화 제약을 해결하기 위해 클라우드 코드 서명으로 전환했습니다. 사내 Windows 앱들이 같은 서명·검증 절차를 사용할 수 있도록 DigiCert KeyLocker 기반 공용 CI 프로세스를 구축했습니다.

배경과 역할

기존 코드 서명은 물리 USB 인증 장치에 의존했습니다. 장치 자체를 관리해야 하고 서명을 실행할 환경에 장치를 연결해야 하므로 관리 부담이 있었으며, CI/CD에서 빌드와 서명을 자동으로 이어가는 데도 제약이 있었습니다. 이 제약을 해소하기 위해 DigiCert KeyLocker 기반 클라우드 코드 서명으로 전환했습니다.

사내 .NET·Electron·Tauri 등의 Windows 앱에서 같은 서명·검증 절차를 재사용할 수 있도록 공용 GitHub Action을 구축했습니다. 각 저장소의 기존 빌드 CI에 Action 호출과 서명할 파일 경로를 추가하는 방식으로 연결하도록 구성했습니다.

EXE·DLL·MSI 파일의 서명과 검증을 공통화하고, 로컬 파일·폴더도 같은 클라우드 서명 절차로 처리할 수 있도록 수동 서명 도구를 마련했습니다. 사용 방법과 불필요한 서명 호출을 줄이는 기준을 팀에 공유했습니다.

GitHub Actions / PowerShell / DigiCert KeyLocker

배포 알림 릴레이

2026

스마트닥터 · 배포 파이프라인 연동 사내 도구

상용 핫픽스가 실제로 나갔는지를 각자 확인해야 하던 부담을 줄이려고 만든 사내 도구. 배포 웹훅을 받아 직전 배포와의 커밋 범위를 비교하고, 거기 담긴 이슈 키로 Jira에 연결된 Slack 스레드를 찾아 배포 완료를 답글로 남깁니다. 정기 릴리즈까지 알리면 소음이 될 것으로 보고 핫픽스성 배포만 골라내며, 판별에 필요한 정보가 없으면 잘못 알리는 대신 침묵하고 경고 로그만 남깁니다.

요청받지 않고 만들어 팀 운영에 정착

TypeScript / Vercel Functions / Slack API / Jira API

배포 알림 릴레이

스마트닥터 · 배포 파이프라인 연동 사내 도구 · 2026

상용 배포가 끝났는지를 관련자가 각자 확인해야 하던 상황을 줄이려고, 요청받지 않고 스스로 만든 사내 도구입니다. 배포 웹훅에서 커밋 범위를 뽑아 이슈에 연결된 Slack 스레드에 완료를 알리며, 핫픽스 배포만 골라 알리도록 좁힌 뒤 지금도 운영에서 쓰이고 있습니다.

배포 알림 릴레이조회·전달 대상배포 이벤트배포 플랫폼웹훅알림 릴레이핫픽스 판별커밋 범위 조회이슈 스레드 링크Slack 스레드완료 답글
배포 웹훅을 받으면 직전 배포와의 커밋 범위에서 이슈 키를 뽑고, 그 이슈에 연결된 Slack 스레드를 찾아 배포 완료를 답글로 남깁니다. 판별에 필요한 정보가 없으면 잘못 알리는 대신 침묵합니다.

배경

상용 배포가 나가고 나면 그 배포가 끝났는지 확인하는 일은 관련자 각자의 몫이었습니다. 배포 범위에 포함된 커밋에서 이슈 키를 뽑아, 그 이슈에 연결된 Slack 스레드에 배포 완료를 답글로 남기는 것이 핵심 동작입니다.

한 일

Vercel 웹훅 진입점에 서명 검증과 비동기 처리를 두고, 직전 상용 배포를 조회해 GitHub compare API로 커밋 범위를 가져오는 클라이언트를 만들었습니다. 페이지네이션과 응답 잘림(truncation)도 함께 처리했습니다. 이슈에 연결된 Jira 스레드를 조회하고 Slack 아카이브 링크를 해석해, 이미 답글이 달렸는지 확인한 뒤 스레드에 배포 완료를 남깁니다. 웹훅 수신, 커밋 범위 조회, 스레드 탐색, 답글 작성 네 단계를 한 흐름으로 이었습니다.

같은 SHA로 다시 배포되는 경우는 건너뛰게 하고, 커밋 파일 조회는 병렬로 처리했습니다. 웹훅 요청 본문이 잘못 와도 200을 유지하고, 서명 검증 키가 설정되어 있지 않으면 401을 돌려주는 등 예외 상황을 테스트로 덮었습니다.

2026년 7월 15일에는 대상 프로젝트를 콜센터 상담 관리 시스템과 대기상태 현황판을 포함해 5개로 넓히고 웹훅 서명 검증 키를 교체하며 실제 전환했습니다. 설계 문서와 실제로 들어온 웹훅 요청을 재생하는 검증 스크립트, DRY_RUN 모드도 함께 만들었습니다.

알림을 좁힌 판단

처음에는 상용 배포 전반을 알렸지만, 정기 릴리즈까지 알리면 알림이 소음이 될 것으로 보고 핫픽스성 배포만 남기도록 좁혔습니다. 직전 상용 배포와 브랜치가 같거나 서비스 접두사와 major.minor가 같고 patch만 오른 경우를 핫픽스성 배포로 보고, 커밋 첫 줄이 핫픽스 형식인 건만 알림 대상으로 삼습니다.

판별에 필요한 정보가 없으면 알림을 보내지 않고 경고 로그만 남깁니다. 잘못 알리는 것보다 알리지 않는 쪽을 택한 판단입니다.

TypeScript / Vercel Functions / Slack API / Jira API

개인 · 연구 · 협업

LRAGE — 법률 도메인 RAG 평가 툴킷

2024 — 2025

공동 연구 오픈소스 · 1저자

법률 태스크에서 LLM을 RAG 설정으로 평가하는 오픈소스 툴킷. lm-evaluation-harness를 확장해 Retriever·Reranker 추상 계층과 LLM-as-a-Judge 평가를 더했고, Pile-of-law 사전 구축 인덱스와 GUI를 제공합니다. 한국어(KBL)·영어(LegalBench)·중국어(LawBench) 법률 벤치마크로 검증했습니다.

1저자 데모 논문 arXiv 공개

Python / lm-evaluation-harness / Pyserini / Hugging Face

Woodshed — 기타 릭 기록·공유 커뮤니티

2026

개인 프로젝트 · 기획부터 배포까지

기타리스트가 릭(짧은 프레이즈)을 TAB으로 기록하고 공유하는 커뮤니티. 프렛을 클릭해 입력하고 해머온·벤딩 같은 아티큘레이션을 실제 악보로 렌더링하는 그래픽 TAB 에디터를 직접 만들었고, 계정·공개 범위·탐색 피드·좋아요/댓글/컬렉션에 신고 모더레이션 큐까지 서비스 운영에 필요한 요소를 갖췄습니다.

Next.js / TypeScript / Drizzle / Turso / Auth.js / Vitest

한국 건축: 예언 모델

2026

건축 전공 팀원과 공동 출품 · 문제의식·논리 구성

AI의 예측에 맞춘 공간에서의 행동이 다시 예측을 강화하는 미래를 가정한 협업 작품입니다. 선택하지 않은 삶의 가능성이 데이터에서 사라질 수 있다는 문제의식을 정리하고 피드백 루프의 논리 구성과 서사·발표 검토에 기여했습니다.

2026 젊은 건축가포럼 건축상 · 입선

문제의식 정리 / 피드백 루프 논리 구성 / 서사·발표 검토

한국 건축: 예언 모델

건축 전공 팀원과 공동 출품 · 문제의식·논리 구성 · 2026

건축 전공 팀원과 AI의 예측이 인간의 선택과 공간에 미치는 영향을 함께 탐구한 공모전 출품작입니다. 저는 CS/AI 관점에서 문제의식과 피드백 루프의 논리를 정리하고 서사·발표를 검토했습니다. 팀원은 건축 담론·건축사와의 연결 및 시각화를 주로 맡았습니다.

문제의식

AI가 인간의 행동을 모두 예측할 수 있다는 미래를 극단적으로 가정한 사고 실험입니다. 예측의 정확성뿐 아니라 예측을 바탕으로 설계한 환경이 인간의 선택에 어떤 영향을 주는지 질문합니다.

예측이 스스로를 증명하는 구조

AI가 행동을 예측하면 그 예측에 맞춰 공간을 설계합니다. 사람들이 그 안에서 생활하며 만든 행동 데이터는 다시 예측 시스템으로 들어가 기존 예측을 강화합니다. 예측 → 공간 설계 → 행동 → 데이터 → 예측이라는 피드백 루프가 작품의 핵심 논리입니다.

이 순환에서 실제로 선택하지 않은 다른 삶의 가능성은 데이터에 남지 않습니다. 따라서 높은 예측 정확도가 가능한 삶을 충분히 설명하는지, 혹은 예측에 맞춘 환경이 선택의 범위를 좁힌 결과인지 구분해야 한다는 문제를 제기합니다.

역할과 협업

핵심 아이디어와 문제의식을 정리하고 예측·공간·행동·데이터가 이어지는 피드백 루프의 논리를 함께 구성했습니다. 작품의 서사와 발표에서 이 논리가 자연스럽게 이어지는지 검토하고 피드백했습니다.

팀원은 문제의식을 건축 담론·건축사와 연결하고 작품을 시각화하는 데 주로 기여했습니다. 서로의 관점을 조율하며 데이터와 시스템의 순환 구조에 대한 기술적 사고를 건축적 질문으로 확장했습니다.

출품 결과

2026 젊은 건축가포럼 건축상에서 입선했습니다.

문제의식 정리 / 피드백 루프 논리 구성 / 서사·발표 검토

작품집 보기 ↗

학력

  1. 2022.03 — 현재

    서울시립대학교

    컴퓨터과학부

    휴학 중

연락처

커피챗, 채용 제안, 협업 제안 모두 환영합니다.

alsgn2003@naver.com