2025.02 — 현재
AIA생명
보험 영업지원 시스템 개발 및 운영
보험 설계사의 영업을 지원하는 AIMS의 백엔드 API와 Vue.js 화면을 개발·운영하고, AIMS와 새 영업지원 시스템 Phase2 및 외부 시스템 간 데이터·인증 연동을 담당합니다. 현업의 반복 보고 업무를 먼저 자동화하자고 제안해 AI 코딩 도구로 프로그램을 만들었습니다.
담당 시스템과 역할
AIMS는 보험 설계사의 영업을 지원하는 시스템으로, 원래는 영업지원 업무 전반을 혼자 맡고 있었습니다. 이후 일부 기능을 새 영업지원 시스템인 Phase2로 옮기고 Phase2에 신규 기능을 더하면서, 두 시스템이 데이터를 주고받으며 함께 동작하는 구조가 되었습니다. 은행에 비유하면 AIMS는 원천 업무 데이터를 처리하는 기간계, Phase2는 그 데이터를 받아 활용하는 정보계에 가깝습니다.
- AIMS의 Java/Spring Boot 백엔드 API와 Vue.js 화면을 개발·운영합니다.
- AIMS와 Phase2 및 외부 시스템 간 데이터·인증 연동을 담당합니다.
- 운영 오류가 발생하면 로그와 요청 데이터로 원인 계층을 좁혀 대응합니다.
- 해외 기술 파트너와 AIMS 기능을 공동 개발하고, 담당 기능의 QA와 검증 내용 공유를 맡았습니다.
역할 백엔드 API·Vue.js 화면 개발·운영, 시스템 간 데이터·인증 연동, 운영 오류 대응, 해외 파트너 공동 개발·QA
계약 만료일 배치의 Phase2 전송을 Transactional Outbox로 개선
보험 계약을 맺으면 피보험자의 개인정보 활용 동의가 함께 생기고, 계약 만료일이 바뀌면 동의 만료일도 갱신해 Phase2에 알려야 합니다. 매월 수만 건 규모로, 수수료 시스템(CMSN)이 계약별 만료일을 계산해 계약 목록을 Redis에 저장하고 RabbitMQ로 Redis 키를 전달하면, AIMS의 MQ 컨슈머가 계약을 한 건씩 처리하는 구조입니다. 기존에는 동의를 갱신할 때마다 Phase2 API를 바로 호출했고, 전체 처리가 하나의 트랜잭션으로 묶여 있었습니다. 그래서 1회 실행에 약 50분이 걸렸는데, 이는 RabbitMQ 컨슈머 타임아웃(30분)보다 길어 처리 도중 메시지가 회수되고 같은 배치가 처음부터 다시 실행됐습니다. 결국 정상 완료되지 못한 채 43시간 동안 약 50회 재실행됐고, 그동안 같은 큐의 다른 업무 메시지도 처리되지 못했으며, 실패한 건을 찾아 수동으로 복구하기도 어려웠습니다. 이를 Transactional Outbox 방식으로 바꿔, 동의 갱신과 'Phase2로 보낼 요청' 저장을 같은 트랜잭션으로 묶고 실제 전송은 별도 전송기가 백그라운드에서 처리하도록 분리했습니다.
- 대안 검토: 컨슈머 타임아웃을 늘리는 방법도 검토했지만, 컨슈머 장애 시 그 시간만큼 메시지가 묶여 장애 감지·복구가 늦어지고, ack되지 않은 메시지가 브로커 메모리에 쌓이며, 배포·재기동 시 재전달 판정도 지연되는 문제가 있어 타임아웃 상향 대신 구조 변경을 선택했습니다.
- 전송 요청 유실 방지: 동의 갱신과 Outbox 적재를 한 트랜잭션으로 묶어, 갱신만 되고 전송 요청이 남지 않는 경우를 막았습니다.
- 건별 실패 격리: 계약 단위 독립 트랜잭션(REQUIRES_NEW)으로 처리해 한 건이 실패해도 나머지는 정상 반영되고, 실패 건은 실패 이력 테이블에 기록합니다.
- 재실행 안전성: 이미 같은 만료일로 반영된 계약은 건너뛰도록 처리했고, 실제 MQ 메시지 재처리 시 완료 건이 중복 반영 없이 넘어가는 것을 확인했습니다.
- 중복 적재 방지: Outbox 테이블에 '고객번호 + 동의이력 순번' UNIQUE 제약을 걸어 같은 동의 이력이 두 번 쌓이지 않도록 DB 수준에서 막았습니다.
- 전송 상태 관리: '대기 → 처리 중 → 완료' 순서로 진행하며, 대기 건을 100건씩 '처리 중'으로 선점해 한 건씩 전송합니다.
- 재시도와 최종 실패: 첫 실패는 '1차 실패'로 기록한 뒤 한 번 더 시도하고, 다시 실패하면 '최종 실패'로 남겨 수동 확인 대상으로 분리합니다. 시도 횟수와 마지막 오류 메시지를 저장합니다.
- 전송 상태 복구: 전송기 시작 시 30분 넘게 '처리 중'으로 남은 건은 '대기'로 되돌려 재전송합니다.
- 전송기 중복 실행 방지: 이미 실행 중일 때 다시 호출되면 AtomicBoolean으로 감지해 새 실행을 건너뜁니다(같은 서버 내 중복 전송 방지).
- 조회 성능: 전송기가 대기 건을 등록 순서대로 반복 조회하므로 상태 + 일련번호 복합 인덱스를 추가했습니다.
- MQ 컨슈머 점유 해소: 컨슈머는 계약 처리와 Outbox 적재까지만 하고 바로 ack하며, 처리 결과는 MQ 로그 테이블에 기록합니다.
운영 기준, 43시간 동안 약 50회 반복되던 재실행 문제를 해결했습니다. MQ 처리 구간(만료일 갱신·동의 이력 등록·Outbox 적재)은 10분 이내로 타임아웃 안에 완료되고, Phase2 전송은 백그라운드에서 약 1시간 20분 내에 완료됩니다. 전송 상태 집계와 실패 이력으로 미완료 건과 수동 복구 대상을 바로 식별할 수 있습니다.
역할 배치·전송 구조 설계와 구현, 트랜잭션 분리, 재시도·복구 로직, 중복 처리 방지, 동작 검증
구현과 판단 과정 읽기 ↗AIMS와 외부 시스템 4곳의 데이터·인증 연동
AIMS를 기준으로 Phase2, 보험사 기간계, 웹메일, 차량 비교견적 서비스와 각각 연동했습니다. 대상마다 인증 방식과 데이터 형태가 달라 대상별로 따로 구현했습니다.
- Phase2 → AIMS 화면 진입 SSO: 사용자가 Phase2에서 Entra 토큰을 가지고 넘어오면 Entra 서버에서 토큰을 검증한 뒤 AIMS에 SSO 로그인 처리하고, 이후 세션은 AIMS에서 별도로 관리합니다.
- AIMS ↔ Phase2 API 연동: 데이터 연동 API를 개발하고, 시스템 간 호출용 토큰을 발급해 AIMS Redis에 저장해 여러 연동 기능이 공통으로 사용하도록 했습니다.
- 토큰 재발급: 토큰 자체가 만료되거나 Redis에서 만료돼 사라지면 재발급하도록 처리해, 연동 기능이 늘어나도 토큰 관리를 한 곳에서 처리합니다.
- AIMS ↔ 보험사 기간계: API Gateway를 통한 토큰 교환 방식의 인증과 SSO를 구현했습니다.
- AIMS ↔ 웹메일: SSO를 구현했습니다.
- AIMS ↔ 차량 비교견적 서비스: 데이터 연계를 구현했습니다.
역할 대상별 연동 API 개발, 토큰 발급·재발급 구조 설계와 구현, SSO 연동
레거시 연동 코드 구조 개선
AIMS는 기존 기능 위에 Phase2 연동이 계속 덧붙으면서, 연동 여부에 따른 분기가 한 메서드에 몰려 수정할 때 영향 범위를 파악하기 어려운 코드가 늘어났습니다. 영역 규모가 커서 전면 재작성은 무리가 있었기 때문에, 연동 오류가 잦고 요구사항 변경이 많은 구간부터, 오류 분석이나 수정 요청이 들어올 때 해당 구간을 함께 정리하는 방식으로 구조 개선을 이어 왔습니다. 대표적인 사례가 교육 기능입니다. Phase2 미디어센터 연동이 붙으면서 AIMS 전용 교육과 연동 교육, 교육 수정·신청자 수정·일괄 신청·일괄 취소 등 상황별 분기가 한 메서드에 몰려 300줄 이상으로 커졌고, 이 영역에서 연동 관련 오류가 반복되고 있었습니다.
- '변경되는 이유가 다른 것끼리' 기준으로 교육 정보 수정, 신청자 분류, 일괄 취소, 일괄 신청, Phase2 연동의 5개 단계로 분리했습니다.
- 연동 처리를 별도 단계로 떼어내, 핵심 업무 로직이 외부 연동 여부를 몰라도 되도록 구성했습니다.
- 연동 시 전체 신청 데이터를 일괄 전송하고, 실패 시 롤백할 수 있도록 Phase2 팀과 협업해 Phase2 측 롤백 기능을 반영했습니다.
- 각 단계의 의도와 외부 시스템 제약을 주석으로 문서화했습니다.
- 단계적으로 변경하며 단위 테스트와 테스트 환경 통합 테스트로 변경 전후 동작을 확인했습니다.
주요 메서드를 60줄 이하 단위로 분리했고, 이후 해당 영역에서 연동 오류가 재발하지 않았습니다.
역할 개선 제안·진행, 분리 기준 설계, 리팩토링, Phase2 팀과 롤백 방식 협의, 테스트, 문서화
구현과 판단 과정 읽기 ↗교육 대상자 일괄 등록 장애 원인 분석과 요청 구조 개선
교육 대상자와 첨부파일을 함께 올리는 일괄 등록 기능이 대상자가 많아지면 실패해, 한 번에 약 50명까지만 등록하도록 제한하고 있었습니다. 업무상 최대 3,000명까지 한 번에 등록해야 했습니다. 분석해 보니 파일 크기가 아니라, 대상자마다 폼 필드를 따로 보내면서 요청 하나의 multipart part 개수가 Tomcat 제한(FileCountLimitExceededException)을 넘은 것이 원인이었습니다.
- 서버 제한 설정을 올리는 대신 요청 구조를 변경하는 방향으로 결정했습니다.
- 대상자 정보를 JSON 배열 하나로 묶어 보내도록 화면과 API를 변경하고, 전용 DTO와 Jackson 파싱을 구현했습니다.
- 기존 첨부파일 처리, 신청 조건 검증, DB 저장 로직은 유지해 변경 범위를 최소화했습니다.
- 성공·실패 건수와 실패 사유별 집계 로그를 추가해 요청 형식 오류와 업무 조건에 따른 부분 실패를 구분합니다.
대상자 3,000명 기준 part 약 9,000개를 1개로 줄여, 한 번에 등록할 수 있는 대상자를 약 50명에서 3,000명으로 늘렸습니다. 서버 설정 변경 없이 3,000건의 파싱과 DTO 매핑을 검증했습니다.
역할 원인 분석, 화면과 API 요청 구조 변경, DTO·파싱 구현, 진단 로그 추가, 테스트
구현과 판단 과정 읽기 ↗현업 월간 실적 엑셀 자동화 (AI 코딩 도구 활용)
현업 담당자가 매달 경영진 보고용 설계사 실적 엑셀을 만들기 위해, 5개 테이블에 흩어진 수천 건의 데이터를 조회·조합하는 데 1~2일을 쓰고 있었습니다. Phase2 개발로 보고에 필요한 데이터 정리 작업이 더 늘어나는 것을 보고 먼저 자동화를 제안했습니다.
- 현업 양식을 기준으로 데이터 출처 테이블과 조합 규칙을 정리했습니다.
- AI 코딩 도구를 활용해 조회·조합·엑셀 생성까지 수행하는 Python 프로그램을 개발했습니다.
- 기존에 쌓인 수작업 엑셀과 결과를 대조해 정확성을 검증했습니다.
- 요청 시 프로그램으로 엑셀을 생성해 전달하고, 일부 건은 직접 재확인하는 방식으로 운영합니다.
작업 시간을 1~2일에서 5분 이내로 줄였습니다. 약 4개월째 현업 담당자 3명이 월 1~2회 사용하고 있습니다.
역할 자동화 제안, 업무·데이터 파악, 조합 규칙 정리, 프로그램 개발, 정확성 검증, 운영
Jenkins 빌드 구조 개선과 비밀정보 관리
여러 프론트엔드 프로젝트가 공통 코드를 Git submodule로 가져다 쓰고 있었는데, 브랜치를 병합할 때마다 submodule이 가리키는 커밋 정보(포인터)에서 충돌이 반복됐습니다. 또 DB 접속 정보 같은 민감 정보가 yaml 설정 파일에 그대로 적혀 있었습니다.
- 공통 저장소는 분리 유지하고, Jenkins가 빌드 시점에 환경별 공통 저장소 브랜치를 가져와 조합하는 구조로 변경했습니다.
- Git 추적 설정과 빌드 스크립트를 수정하고, 저장소 인증 정보는 Jenkins Credential Binding으로 전달했으며, 개발 환경 구성 가이드를 정리했습니다.
- 민감 정보를 AWS Secrets Manager로 옮기고 설정 파일에는 참조 값만 남겨, 코드 저장소에 민감 정보가 노출되지 않도록 개선했습니다.
테스트 프로젝트에서 검증한 뒤 전체 업무 프로젝트에 순차 적용했고, 기존 빌드·배포 동작을 유지하면서 병합 충돌을 없앴습니다.
역할 Git 설정과 Jenkins 빌드 스크립트 수정, 개발 환경 가이드 정리, 프로젝트별 적용·검증, Secrets Manager 연동
Jenkins 빌드 구조 변경 과정 읽기 ↗