티스토리 뷰
서론: 고장 난 나침반이 가리키는 곳
성공적인 비즈니스 시스템은 종종 잘 계획된 도시와 비견되곤 한다. 초창기에는 명확한 청사진 아래 효율적으로 성장하지만, 세월이 흐르며 증축과 개축이 반복되고 초기 설계 사상은 점차 희미해진다. 누구도 모르는 지름길과 기록에 없는 골목이 생겨나고, 도시의 중요한 조례들은 이제는 잊힌 방언으로 쓰인 고문서처럼 변해간다. 결국 도시는 거대한 미궁, 즉 누구도 전체 구조를 파악하기 힘든 레거시 시스템이 되고 만다. 이러한 시스템은 비즈니스 확장을 저해하고, 혁신을 늦추며, 고객의 불만을 먹고 자라는 심각한 제약으로 작용한다.
내가 몸담았던 회사는 소프트웨어를 팔았지만, 그 방식이 조금 독특했다. 마치 일류 셰프가 자신의 요리뿐 아니라 그 요리를 담는 특별 제작 그릇까지 함께 팔아야 직성이 풀리는 것처럼, 우리는 교육용 앱이 설치된 물리적인 태블릿 기기를 함께 판매했다. 이 ‘1계약:1패드’라는 비즈니스 모델은 초창기에는 통제된 환경을 제공하는 견고한 ‘성곽 도시’처럼 보였을지 모른다. 하지만 도시가 번영하고 시민이 늘어날수록, 이 견고함은 성장의 발목을 잡는 ‘황금으로 만든 족쇄’가 되었다. 초기 비즈니스 모델의 성공이 역설적으로 장기적인 기술 부채로 이어진다는 사실은 주목할 만하다. 단기적인 사업 요구에 집중한 초기 설계가 장기적인 확장성을 고려하지 못했고, 그 성공은 시스템의 근본적인 한계를 가리는 장막이 되어주었다. 나중에 해결하기 훨씬 더 어려운 심각한 기술 부채가 그 장막 뒤에서 조용히 쌓여가고 있었다.
그 결과는 고객의 책상 위에서 기묘한 형태로 나타났다. 한 고객이 여러 학습 서비스를 구매할 때마다, 서비스 숫자만큼 태블릿이 하나씩 늘어가는 불편함, 21세기의 디지털 경험과는 동떨어진 이 현실은 고객센터의 전화기를 쉴 새 없이 울렸다. 고객의 목소리(VOC)는 더 이상 단순한 데이터가 아니었다. 그것은 낡은 도시의 비효율에 갇힌 시민들이 보내는 구조 신호였고, 개선을 향한 거대한 합창이었다. 혁신에 쓰여야 할 IT 예산의 상당 부분이 이 부채의 이자를 갚는 데, 즉 낡은 시스템을 겨우 유지하고 지탱하는 데 소모되고 있었다.
마침내 그 합창은 더는 외면할 수 없는 거대한 노크 소리가 되어 우리 개발팀의 문을 두드렸다. 내가 팀에 합류한 뒤 처음으로 맡게 된 이 대규모 프로젝트는 그렇게 시작되었다. 우리 손에 들린 것은 목적지는 명확히 가리키고 있지만, 그곳까지 가는 길은 알려주지 않는 ‘고장 난 나침반’과도 같았다. 우리의 과제는 이 낡은 도시의 비효율적인 교통 시스템을 근본적으로 재설계하고, 시민들이 도시 안에서 자유롭게 이동할 수 있는 새로운 길을 내는 일이었다.
1. 고대의 암호문, 오라클 프로시저와의 조우
프로젝트의 심장부를 해부하자, 그곳에는 수천 줄에 달하는 거대한 오라클 저장 프로시저가 똬리를 틀고 있었다. 시스템의 핵심 인증과 계약 정보를 처리하는 이 프로시저는, 한때 안정성의 상징이었을지 모르나 이제는 모든 변화를 가로막는 거대한 문지기가 되어 있었다. 그 실체는 ‘스파게티 코드’라는, 무질서하게 엉킨 혈전 덩어리와 같았다. 수많은 개발자들이 십수년에 걸쳐 남긴 코드 조각들이 문서도, 일관된 규칙도 없이 뒤엉켜 있었으며, 시스템의 작동 원리를 알던 ‘원로’ 개발자들은 대부분 은퇴했거나 회사를 떠난 지 오래였다.
하지만 이 복잡성은 증상일 뿐, 병의 근원은 더 깊은 곳에 있었다. 바로 ‘객체-관계 임피던스 불일치(Object-Relational Impedance Mismatch)’라는, 소프트웨어 공학의 오래된 난제였다. 이 거창한 이름이 가리키는 것은, 세상을 바라보는 두 개의 근본적으로 다른 세계관 사이의 충돌이다. 하나는 데이터를 행위와 함께 캡슐화하는 객체 지향 프로그래밍의 세계관이고, 다른 하나는 데이터를 정규화된 테이블의 집합으로 보고 선언적인 언어(SQL)로 다루는 관계형 데이터베이스의 세계관이다.
기존 시스템은 이 근본적인 충돌을 어색하게 봉합한 결과물이었다. 애플리케이션의 ‘행위’에 해당하는 복잡한 비즈니스 로직이, 데이터 저장소인 오라클 프로시저 내부에 갇혀 있었던 것이었다. 이는 자동차의 엔진 제어 로직을 연료 탱크 안에 설계한 것과 같았다. 로직을 수정하거나 테스트하는 것은 고사하고, 버전 관리, 자동화된 테스트, CI/CD 파이프라인 같은 현대적 개발 생명주기에 편입시키는 것 자체가 불가능했다. 비즈니스 규칙의 변경이 필요할 때마다 개발 라이프사이클을 우회하여 데이터베이스를 직접 수정해야 하는 상황은 변경에 대한 저항성을 극도로 높였고, 급변하는 시장에 대한 비즈니스의 신속한 대응 능력을 심각하게 저하시켰다. 기술적 제약이 비즈니스 혁신의 발목을 잡는 핵심 요인이 된 것이었다.
이러한 기술적 복잡성은 개발팀에 막대한 인간적 비용을 초래했다. 디버깅이 ‘고고학적 발굴’에 비유될 정도로 시간과 노력이 소모된다는 것은, 개발자의 좌절감과 번아웃으로 이어질 수 있음을 의미했다. 우리의 첫 과제는 이 낡고 녹슨 금고를 깨부수고 그 안에 화석처럼 갇혀 있던 비즈니스 로직을 해방시켜, 마땅히 있어야 할 유연하고 투명한 애플리케이션의 품으로 돌려보내는 일이었다. 그것은 단순한 리팩토링을 넘어, 시스템의 근본적인 체질을 개선하는 대수술의 시작이었다.
2. 점진적 혁명: 교살자 무화과와 배관공의 철학
이처럼 복잡하고 위태로운 레거시 시스템을 현대화하는 과업 앞에서, 팀은 두 가지 상반된 길 중 하나를 선택해야 했다. 하나는 기존 시스템을 한 번에 전면적으로 교체하는 ‘빅뱅(Big Bang)’ 방식이다. 이는 낡은 도시를 통째로 허물고 하루아침에 새로운 스마트 시티를 세우겠다는, 유혹적이지만 무모한 계획과도 같았다. 업계의 통계는 이러한 대규모 데이터 마이그레이션 프로젝트의 80% 이상이 예산이나 기간을 초과하며, 그 위험성은 시스템의 복잡성에 비례하여 기하급수적으로 증가한다고 경고한다. 핵심 비즈니스 로직을 다루는 이번 프로젝트에서 빅뱅 방식은 사실상 실패 확률이 높은 도박과 같았고, 만에 하나 실패할 경우 비즈니스 전체를 마비시킬 수 있는 잠재적 재앙이었다.
이러한 위험을 회피하기 위해, 우리는 마틴 파울러(Martin Fowler)에 의해 널리 알려진 ‘교살자 무화과 패턴(Strangler Fig Pattern)’이라는, 훨씬 더 신중하고 외과적인 접근법을 채택했다. 이 패턴의 이름은 숙주 나무의 가지에서 싹을 틔워, 서서히 뿌리를 내리며 숙주를 감싸고 자라나 결국에는 숙주 나무를 완전히 대체하고 그 자리에 새로운 나무로 우뚝 서는 교살자 무화과(Strangler Fig)의 생장 방식에서 유래했다. 소프트웨어의 맥락에서 이는 기존 레거시 시스템이라는 늙고 병든 나무를 그대로 살려둔 채, 그 주변부부터 새로운 기능이라는 건강한 덩굴을 하나씩 뻗어 나가게 하는 점진적 교체 전략을 의미한다. 새로운 시스템이 점차 자라나 기존 시스템의 기능을 하나씩 대체함에 따라, 낡은 시스템은 서서히 그 역할을 잃고 최종적으로는 누구도 눈치채지 못하는 사이에 조용히, 그리고 안전하게 은퇴(decommission)하게 된다.
이 전략의 기술적 심장부에는 ‘퍼사드(Facade)’ 또는 ‘프록시(Proxy)’ 역할을 하는 지능적인 라우팅 메커니즘이 있었다. 우리는 도시로 들어오는 모든 요청을 받는 새로운 정문을 세웠다. 이 지능적인 정문지기는 API 요청이 들어올 때마다 그 요청의 특성(예: 특정 파라미터 값이나 헤더 정보)을 꼼꼼히 분석하여, 이 요청을 새로 닦인 고속도로(새로운 자바 로직)로 안내할지, 아니면 아직 남아있는 낡은 골목길(기존 오라클 프로시저)로 전달할지를 실시간으로 결정했다. 이 방식을 통해 새로운 기능과 낡은 기능이 한 시스템 안에서 매끄럽게 공존하며, 사용자는 아무런 변화를 인지하지 못한 채 점진적인 전환이 이루어질 수 있었다.
더 나아가 이 라우팅 계층은 낡은 세계와 새로운 세계 사이의 번역가이자 방화벽 역할을 하는 ‘안티-코럽션 레이어(Anti-Corruption Layer)’로서 기능했다. 이는 마치 서로 다른 언어와 도량형을 사용하는 두 나라의 국경에 세워진 정교한 세관과도 같았다. 레거시 시스템의 낡은 데이터 모델이나 불합리한 관습들이 새로 건설되는 깨끗한 도시로 ‘감염’되는 것을 막아주는 전략적인 방어선이었다. 이 방화벽 덕분에 새로 개발되는 코드베이스는 레거시의 문제점을 답습하지 않고, 진정으로 현대적이고 유지보수 가능한 자산으로 구축될 수 있었다.
패턴 적용의 첫 단계이자 가장 어려운 관문은 교체할 기능 단위, 즉 ‘얇은 조각(thin slices)’을 식별하는 것이었다. 거대한 모놀리식 프로시저의 어느 부분을 첫 번째 수술 부위로 정할 것인가? 놀랍게도 그 해답은 레거시 코드 자체의 복잡성 속에 숨어 있었다. 프로시저 내부에 거미줄처럼 얽혀 있던 수많은 IF/CASE 분기문들이 바로 그것이었다. 이 분기문들은 각기 다른 비즈니스 시나리오나 정책을 처리하는 논리적 경계선 역할을 하고 있었다. 즉, 레거시 코드의 복잡한 구조 자체가 역설적으로 점진적 분해를 위한 로드맵을 제공한 셈이었다. 우리의 작업은 단순한 코드 변환을 넘어선 역공학적 탐사, 즉 ‘소프트웨어 고고학’에 가까웠다. 낡은 지도의 해독 불가능해 보였던 기호들 속에서, 오히려 새로운 도시를 건설할 구획도를 발견해낸 것이었다. 이는 낡은 수도관을 교체하기 위해 집 전체를 부수는 대신, 기존 설계도를 면밀히 분석하여 가장 문제가 되는 부분부터 하나씩 신중하게 교체해 나가는 숙련된 배관공의 철학과도 같았다.
3. 신뢰 구축의 기술: 그림자 속의 리허설과 실용주의적 타협
핵심 비즈니스 로직을 교체하는 작업은 마치 살아있는 환자의 대동맥을 교체하는 대수술과 같았다. 이러한 수술에서 ‘거의 정확하다’는 말은 ‘완전히 틀렸다’는 말과 동의어이며, 작은 실수 하나가 치명적인 결과로 이어질 수 있다. 낡은 수도관을 교체하는 동안 도시 전체의 물 공급을 끊을 수 없듯, 우리는 시스템의 심장을 교체하면서도 서비스의 맥박이 단 한 순간도 멈추는 것을 허용할 수 없었다. 이를 위해 우리는 ‘점진적 전환과 병렬 검증’이라는, 수술팀을 위한 정교한 비행 시뮬레이터와도 같은 안전장치를 고안했다.
이 전략은 업계에서 ‘병렬 실행(Parallel Run)’ 또는 ‘섀도우 모드(Shadow Mode)’로 알려진 기법으로, 새로운 시스템이 실전에 투입되기 전에 실제 환경에서 구 시스템과 동일하게 작동함을 100% 확신하기 위한 강력한 방법론이다. 개념은 간단하지만 실행은 정교했다. 패드 인증과 관련된 API 요청이 들어오면, 애플리케이션은 내부적으로 두 갈래의 작업을 동시에 수행했다. 하나는 수십 년간 안정성을 검증받은 기존의 오라클 저장 프로시저를 호출하는 것이고, 다른 하나는 새로 개발된 자바(Java) 기반의 비즈니스 로직을 실행하는 것이었다. 이 두 로직이 각각의 결과를 반환하면, 시스템 내의 엄격한 ‘심판’은 두 결과값이 원자적 수준에서 완전히 일치하는지 비교하는 검증 로직을 수행했다.
만약 두 결과 사이에 사소한 차이라도 발견되면, 시스템은 즉시 상세한 컨텍스트(입력 파라미터, 실행 시각, 두 결과값 등)와 함께 불일치 로그를 기록했다. 이 로그는 개발팀에게 무엇보다 귀중한 ‘무료 실제 테스트 케이스’가 되었다. 우리가 미처 생각하지 못했던 예외 상황이나 코드 속에 화석처럼 박혀 있던 숨겨진 비즈니스 규칙을 알려주는 귀중한 교훈이었다. 이 과정을 통해 낡은 레거시 시스템은 새로운 시스템에게 자신의 모든 지혜와 비밀을 가르치는 ‘스승’의 역할을 했다. 가장 중요한 점은, 비교 결과와 상관없이 사용자에게 최종적으로 반환되는 응답은 항상 기존의 안정적인 오라클 프로시저가 생성한 결과였다는 것이다. 새로운 자바 로직은 오직 ‘그림자’처럼 존재하며 학습하고 검증받을 뿐, 실제 서비스에는 아무런 영향을 미치지 않았다.
이 전략은 ‘배포(Deployment)’와 ‘출시(Release)’라는 두 개념을 의도적으로 분리하는, 정교한 위험 관리 기법이기도 했다. 새로운 코드는 사용자에게 영향을 주지 않는 상태로 운영 환경에 ‘배포’되어, 수많은 실제 요청들을 처리하며 스스로의 완벽함을 데이터로 증명해 나갔다. 그리고 마침내 새로운 로직이 기존 로직과 100% 동일하게 작동한다는 확신이 섰을 때, ‘출시’는 단지 설정 스위치 하나를 켜는(기존 로직 대신 새로운 로직의 결과를 반환하도록 변경하는) 지극히 간단하고 안전한 작업이 되었다.
한편, 이처럼 안전한 전환의 발판을 마련한 뒤에도 현실적인 난관은 남아있었다. 팀의 숙원이었던 JPA 도입은 기존 코드와의 철학적 충돌이라는 또 다른 과제를 안겨주었다. 기존 데이터 접근 로직은 성능을 위해 여러 테이블을 복잡하게 조인하여 한 번에 모든 것을 가져오는, 소위 ‘한방 쿼리’ 스타일의 MyBatis로 가득했다. 이는 마치 도시의 모든 정보를 얻기 위해 기록 보관소의 모든 관련 서류를 단 한 번의 방문으로 몽땅 꺼내오는 방식과 같았다. 반면, 객체 간의 관계와 그래프 탐색을 중시하는 JPA의 철학은 필요한 정보만을 따라 우아하게 탐색하는 방식에 가까웠다. 이 ‘한방 쿼리’를 JPA로 어설프게 구현하려다가는, 연관된 데이터를 조회하기 위해 수많은 추가 쿼리가 발생하는 악명 높은 ‘N+1 쿼리 문제’라는 함정에 빠지기 십상이었다.
우리는 이 기술적 간극 앞에서 원칙론자가 되기보다는 실용주의적인 배관공이 되기로 했다. 성능이라는 대원칙 아래, 상황에 맞는 최적의 도구를 선택하는 하이브리드 전략을 택한 것이다. 비교적 구조가 명확하고 단순한 조회는 JPA의 JPQL을 활용하여 높은 생산성과 가독성을 확보했다. 하지만 여러 도메인의 정보가 복잡하게 얽힌 ‘한방 쿼리’의 경우에는, 각 도메인 모델(예: 고객, 계약, 상품)을 개별적으로 조회한 뒤 애플리케이션단에서 이를 조합하고 가공하는 방식을 취했다. 때로는 이것이 객체지향의 순수성을 조금 해치는 것처럼 보일지라도, 사용자를 기다리게 하는 것보다는 현명한 타협이라 판단했다. 이처럼 기술적 이상과 현실적 제약 사이의 균형점을 찾는 과정은, 레거시 현대화가 단순히 새로운 기술을 적용하는 것을 넘어, 주어진 상황 속에서 가장 합리적인 가치를 창출하는 지혜로운 의사결정의 연속임을 가르쳐주었다.
결론: 숫자를 넘어선 보람, 그리고 남겨진 것들
프로젝트는 성공적으로 끝났다. 수술은 끝났고, 환자는 건강하게 회복했다. 도시의 교통 시스템을 마비시키던 근본 원인은 해결되었고, 시민들의 불편은 눈에 띄게 줄었다. 고객센터를 울리던 전화벨 소리는 잦아들었고, 고객 불만(VOC) 수치는 전월 대비 10% 감소라는 명확한 데이터로 보답했다. 시스템 내부적으로는 코드 커버리지가 20% 이상 향상되고 장애 대응 시간이 40% 단축되는 등, 보이지 않는 곳의 건강성도 극적으로 개선되었다. 우리는 낡은 지도를 들고 시작한 탐험을, 단 한 건의 장애 없이 안전하게 완수했다.
솔직히 고백하자면, 이 프로젝트는 이전 직장에서 개발자로서의 자신감을 많이 잃었던 내게 일종의 재활 치료와도 같았다. 복잡하게 얽힌 문제 앞에서 망설이고 있을 때, 동료들은 "철현님이 알아서 잘 결정해 주세요"라며 기꺼이 신뢰라는 지지대를 내어주었다. 그 믿음은 내가 가장 합리적인 해결책을 찾아 나설 용기를 주었고, 스스로의 판단에 대한 확신을 되찾게 해주었다. 기술적 난제에 대해 밤늦도록 토론하고, 마침내 우아한 해결책을 찾았을 때 함께 기뻐하던 순간들. "이 팀과 오래도록 함께하고 싶다"는 생각이 절로 들 만큼, 그 과정은 내게 무엇과도 바꿀 수 없는 행복한 시간이었다.
물론 모든 이야기가 그렇듯, 현실에는 언제나 약간의 그림자가 드리워져 있었다. 우리의 성과는 직접적인 매출 증대와 같은 화려한 지표로 나타나지 않았다. 우리는 도시의 새로운 랜드마크를 건설한 것이 아니라, 눈에 보이지 않는 낡은 하수관을 교체한 것에 가까웠기 때문이다. 그 결과, 프로젝트는 경영진의 화려한 스포트라이트를 받지는 못했다. 그리고 가장 큰 아쉬움은, 최고의 합을 자랑하며 이 험난한 여정을 함께했던 팀이 프로젝트 종료 후 조직 개편이라는 현실의 파도 속에서 각자의 길을 가게 된 것이었다. 성공적인 수술을 마친 외과팀이 병원의 사정으로 해체된 것과도 같은, 씁쓸한 뒷맛이 남았다.
그러나 나는 이 프로젝트의 진정한 성공이 외부의 평가나 숫자에 있지 않다고 믿는다. 그 가치는 고객의 오랜 고통을 해결했다는 뚜렷한 엔지니어링의 본질, 동료들의 깊은 신뢰 속에서 잃었던 자존감을 온전히 되찾은 개인적 성장, 그리고 다음 세대의 동료들을 위해 낡고 위험한 시스템을 더 나은 상태로 물려주었다는 장인으로서의 소박한 보람에 있다. 기술 부채의 진정한 비용은 그것을 해결하는 데 드는 직접적인 비용이 아니라, 혁신의 기회를 상실하면서 복리로 쌓이는 ‘기회비용’이다. 우리는 그 기회비용의 고리를 끊어냈고, 미래의 혁신을 위한 단단한 발판을 마련했다.
이 프로젝트는 하나의 ‘도가니’였다. 수년간의 마이그레이션을 성공적으로 마친 팀은 시작할 때와는 다른 팀이 되었다. 우리는 새로운 기술, 새로운 규율(TDD, 점진주의), 그리고 기술 부채의 비용과 품질의 가치에 대한 새로운 공유된 이해를 갖게 되었다. 비록 지금은 흩어졌지만, 그 시절 낡은 수도관을 함께 교체하며 땀 흘렸던 동료들과의 기억, 그리고 그 과정에서 체득한 문화적 유산은 내게 무엇과도 바꿀 수 없는 귀한 자산으로 남아있다. 그것이 바로 이 프로젝트가 내게 남긴, 숫자를 넘어선 진정한 보상이다.
'에세이' 카테고리의 다른 글
항해에 나서지 못한 배를 위한 변명 (7) | 2025.07.30 |
---|---|
월 3만 원짜리 팀원과 일의 미래에 대한 기묘한 통찰 (7) | 2025.07.28 |
서버실의 유령 (0) | 2023.04.25 |
구명조끼와 위조된 보물 지도: 오해와 삽질의 연대기 (0) | 2021.07.11 |
- Total
- Today
- Yesterday
- 경고
- 유지보수
- Spring in Action
- java
- was
- Markov
- 코딩의 기술
- 전략패턴
- restful api
- 문장 생성기
- 클린코드
- DP
- RESTful
- 마르코프 연쇄
- 디자인패턴
- GROUP BY
- Count
- 동적계획법
- CONVENTIONS
- 자바스크립트 개론
- html
- 마르코프
- 몰라서망신
- 로그
- markov chain
- 크롬
- Warning
- 야근
- REST API
- 자바스크립트개론
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 |