유지상

설치형 전환의 산출물 경계

고객 인증서를 고객사 밖으로 내보낼 수 없고 설치 산출물이 고객 손에 남는 제약 아래, 개인키 연산 분리와 native 바이너리의 서로 다른 경계를 설계·검증했습니다.

Java 21, Gradle Multi-Module, GraalVM Native Image, BouncyCastle, mTLS WebSocket

설치 산출물이 고객 환경에 남는 제약

서버가 보관하던 고객 인증서와 금융기관 로그인 로직을 고객사 환경으로 옮겨야 했습니다. 고객 인증서의 개인키는 고객사 밖으로 내보낼 수 없고, 설치한 산출물은 고객 손에 남아 디컴파일될 수 있으며 배포 뒤 원하는 시점에 고치기도 어렵습니다.

절대적인 역공학 방지를 목표로 삼지 않았습니다. 소스 형태의 직접 노출을 피하고 리버싱 비용을 높이는 보조 수단으로 보호 목표를 한정한 뒤, 구현 범위가 다른 두 단계의 경계를 따로 설계했습니다.

서버가 조회하는 단계는 개인키 연산만 분리

서버가 조회를 수행하는 단계에서는 개인키 연산만 고객사 에이전트에 두고 기관별 서명 로직은 서버에 남겼습니다. 패키지만 나누면 하나의 산출물에 함께 묶이므로 공유 계약·에이전트·서버를 별도 Gradle 모듈로 분리하고 컴파일 의존 방향으로 배포 경계를 강제했습니다.

에이전트는 업무 판단을 하지 않고 검증을 통과한 요청에만 개인키 연산을 수행합니다. 통신은 고객사에서 서버로 여는 상시 채널을 사용해 고객사 방화벽에 인바운드 연결을 추가하지 않도록 구성했습니다. 모듈 분리 뒤에는 에이전트 산출물에 기관별 서명 로직이 포함되지 않는 것을 빌드에서 확인하고 전체 테스트를 실행했습니다.

전체 로직을 옮기는 구현안은 보호할 책임만 native로

전체 인증·조회 로직을 설치형으로 옮기는 구현안에서는 기관별 로직을 서버에 남길 수 없었습니다. 인증·서명처럼 노출 비용이 큰 책임은 하나의 native 바이너리 안에 모으고, 오케스트레이션·영속·스케줄링은 밖으로 분리했습니다.

전면 재작성도 구현 대안으로 검토했습니다. 기존 인증·서명 구현은 실제 금융기관 대상 검증 이력이 있어 재작성 뒤 같은 범위를 다시 검증하는 부담이 컸습니다. 부분 native와 공유 라이브러리 방식은 보호 대상이 결국 에이전트 산출물에 함께 남으므로 선택하지 않았습니다.

기동 여부가 아니라 응답 본문까지 확인

도달 가능한 코드만 이미지에 포함되기 때문에, 작은 시험용 프로그램이 성공했다고 실제 데몬도 성공한 것으로 보지 않았습니다. 프로브와 실제 데몬을 각각 native로 빌드하고, 검증 시점에 정의된 endpoint 전체를 호출해 응답 본문을 단언했습니다. 메타데이터 누락은 빌드와 기동을 통과한 뒤 요청 시점에 드러날 수 있기 때문입니다.

기반 인증·조회 구현의 실제 금융기관 동작 확인과 설치 산출물의 검증 범위는 분리했습니다. 빌드 산출물의 모듈 경계와 native 바이너리의 응답 경로는 로컬 통제 환경에서 검증했습니다.

소속 경력