Skip to content
hello tis
Go back

[26.10.05] 사내 버셀을 Spring으로 통합하기

사내 버셀의 Python 기능들은 독립된 프로세스로 실행돼 관리가 어려웠습니다. 관리할 프로세스를 줄이기 위해 Spring 단일 애플리케이션으로 통합하기로 했습니다.

팀에서 사용하는 LGTM 연동과 로그 설정도 Spring Boot를 기준으로 구성돼 있었습니다. Python을 유지하면 모니터링과 로그 연동을 별도로 구현하고 관리해야 했습니다.

구성이점추가 작업
Python 유지기존 구현 유지모니터링과 로그 연동의 별도 구현과 유지보수
Spring 통합팀 공통 설정 재사용, 프로세스 통합기능 이식과 기존 동작의 호환성 검증

기능을 이식하는 비용은 있지만, 팀 공통 설정을 재사용하고 관리할 프로세스를 줄이기 위해 Spring을 선택했습니다.

팀 공통 설정 재사용하기

우리 팀은 공통 가이드를 주제별 문서로 관리하고, Codex와 Claude Code용 플러그인으로 제공합니다. 에이전트 지침과 문서 검색 인덱스를 함께 두어 필요한 설정과 절차를 찾아 적용할 수 있도록 구성했습니다.

skills/altools-guideline/
├── SKILL.md             # 에이전트 지침
├── TAGS.md              # 문서 검색 인덱스
└── docs/
    ├── api/             # API 컨벤션
    ├── development/     # 개발 규칙과 코드 리뷰
    ├── deployment/      # 배포 절차
    ├── infrastructure/  # 서버와 LGTM 설정
    └── onboarding/      # 온보딩 안내

프로젝트별 환경과 공통 규칙의 예외는 각 저장소의 ALTOOLS.md에 기록합니다. Codex와 Claude Code가 같은 프로젝트 문서를 참고하도록 AGENTS.md와 CLAUDE.md에서 연결합니다.

프로젝트 저장소/
├── ALTOOLS.md  # 프로젝트별 환경과 규칙
├── AGENTS.md   # Codex: @ALTOOLS.md 참조
└── CLAUDE.md   # Claude Code: @ALTOOLS.md 참조

이렇게 공통 설정은 가이드에, 프로젝트별 차이는 ALTOOLS.md에 두고 에이전트가 두 문서를 함께 참고하도록 했습니다.

모니터링 가이드에는 Spring Boot 의존성, 메트릭과 트레이스의 OTLP 전송 설정, Logback을 통한 로그 수집과 연동 확인 절차를 정리했습니다. 사내 버셀에도 이 가이드를 적용하면 기존 설정과 점검 절차를 재사용할 수 있습니다.

Control API부터 단계적으로 전환하기

Control API 전환 과정

기존 프리뷰 서비스를 유지한 채 Control API를 Spring Boot로 교체했습니다.

Control API, Portal, Webhook 수신기, Poller와 Worker는 각각 독립된 프로세스로 실행됩니다. Control API만 Python에서 Spring Boot로 교체하고 나머지 Python 서비스와 기존 PostgreSQL, 작업 큐는 유지합니다.

전체 전환 완료 후 구조

전체 전환이 완료되면 서비스별로 실행하던 Python 프로세스를 하나의 Spring 프로세스로 통합합니다.

전환 전에는 Control API, Portal, Worker, Webhook 수신기와 Poller가 각각 독립된 Python 프로세스로 실행됩니다. 전체 전환 완료 시에는 모든 기능을 하나의 Spring 프로세스로 통합하고 기존 PostgreSQL과 작업 큐는 유지합니다.

재배포 시 작업 보존

재배포 중에도 작업을 보존하도록 배포 요청 수신과 실행을 분리하고, 복구에 필요한 정보는 PostgreSQL에 저장합니다. 이 구조는 단계별 교체와 최종 통합 구조에서도 유지합니다.

Control API가 배포 요청을 PostgreSQL의 영속 작업 큐에 저장합니다. Worker는 작업을 획득해 실행하고 상태와 결과를 PostgreSQL에 기록합니다. 재시작 후에는 저장된 작업과 상태를 조회합니다.

Worker는 실행 중 heartbeat로 처리 권한을 갱신합니다. 이 동작은 기존 작업 보존 구조에서도 사용했습니다. 복구에 필요한 정보를 애플리케이션 메모리에만 두지 않는 것이 stateless를 유지하기 위한 조건입니다.

마이그레이션 시 로그인 세션 끊기는 문제 해결

Spring Security OAuth2 Client로 로그인 처리를 간소화하면서 기존 사용자의 로그인 상태도 유지해야 했습니다. 처음에는 Spring Session으로 이관하려 했지만, 새 테이블에 맞춘 호환 처리와 롤백을 위한 이중 쓰기까지 고려해야 했습니다. 그래서 기존 쿠키와 세션 테이블을 그대로 공유하는 방식으로 설계를 변경했습니다.

첫 번째 대안은 기존 세션을 조회해 새 Spring 세션에 저장합니다. 선택한 방식은 전환 전 Python과 전환 후 Spring이 같은 PostgreSQL의 기존 세션과 GitHub 토큰을 같은 형식으로 읽고 쓰도록 합니다.

참고한 전환 사례

올리브영의 무중단 OAuth2 전환기에서 세 가지를 참고했습니다.

GitHub 장애로 로그인이 끊기는 문제

GitHub 장애로 토큰 갱신에 실패하면 access token 만료 후 900초까지 기존 토큰을 보존합니다. Spring에서도 이 정책을 유지하기로 했습니다. 유예 중에는 stale 배너로 갱신 지연을 알리고 등록 해제를 막습니다. 일시적 장애와 갱신 거부는 구분하며, 갱신이 거부되면 세션을 폐기합니다.

900초는 실측 전 정한 임시값입니다. GitHub 상태 API의 “API Requests” 장애 지속 시간을 집계해 중앙값을 제시한 공식 자료는 확인하지 못했습니다.

토큰 갱신 실패부터 복구까지 걸린 시간과 유예 적용 시간을 메트릭으로 관찰할 계획입니다. 관측 결과를 바탕으로 필요한 유예만 남겨, access token 만료 후 로그인 유지 시간을 줄이고 보안을 강화하려고 합니다.

회고

처음에는 빠르게 개발하기 위해 Python을 선택했습니다. 사내 서비스로 적용하려니 팀 공통 인프라 설정에 맞추는 작업이 추가로 필요했습니다. 공통 설정과 다른 운영 사례를 남기고 싶지 않아 Python 구성으로 서비스를 더 추가하지 않기로 했습니다.

Spring으로 전환하면서 기존 기능을 이식하고 호환성을 확인하는 만큼 작업량도 늘었습니다. 다음 PoC에서는 개발 속도와 함께 사내 적용 가능성, 팀 공통 인프라 설정에 맞추는 비용도 기술 선택 단계에서 고려하려고 합니다.


Share this post:

Previous Post
[26.10.06] 사내 버셀의 HTML 배포 지원과 GitHub 조직 초대 자동화하기
Next Post
[26.10.02] ClickHouse Merge 증가 원인 분석하기