👨‍🍳 레거시 시스템 개편의 기술 - 권용근(킹뽀대)님

image

우아한형제들 권용근님의 레거시 시스템 개편 꿀팁 모음집. 우아한 멀티 모듈에서 느꼈던 감동을 비슷하게 느낄 수 있는 시간이었다. 도저히 테스트 할 수 없는 환경에서는 어떻게 안정감을 확보했는지까지… 실전형 꿀팁들이 넘쳐 흐른다.


👨‍🍳 레거시 개편은 왜 일어나는가?

레거시 시스템이란?

  • 낡은 시스템. 낡았다 현재는 비주류인 기술
  • 트래픽도 늘어나고 성능적인 챌린징도 하다 보니, 성능적으로 부족한 시스템.
  • 과거의 요구사항에는 적합했지만, 현재의 요구사항에는 대응하기 어려운 시스템

오래되고 늙었다 해서 레거시가 아니라, 현재 요구에 적합하지 않으면 모두 레거시로 볼 수 있군…

레거시 시스템은 항상 개편이 되어야하는가?

  • 현재는 비주류인 기술?
    • 개발자에겐 미안한 이야기지만, 개발자를 갈아내서, 시간을 더 들여서 해결할 수 있다.
  • 현재 성능이 부족한 시스템?
    • 비용을 더 지불해서 (스케일 아웃, 업)
  • 새로운 요구사항을 대응할 수 없으면?
    • 새로운 시스템을 만들어서 개편 가능

그렇다면 개편은 언제?

  • 투자자본수익률(ROI) - 가성비가 있어야 개편이 일어난다.
  • 새로운 시스템 만들기, 사람 갈아넣기, 비용 더 지불하기와 같은…
    • 시간, 비용, 인원
    • 생명주기, 학습비용, 비즈니스, 지연, 채용, 퇴사, 리스크, 한계 등…
  • 들어가는 비용 중에는 상당부분이 개발자가 측정이 가능하다.
  • 개발자들만 알아차릴 수 있는 지표들은, 직접 푸시해서 설득을 할 필요도 있다.
  • 어렵게 계산을 한 비용을, 결정권자들에게 푸시해서 결정이 있을건데, 이 비용 측정을 현재로만 하면 설득이 어렵다.
  • 여기에 미래에 대한 시간을 포함시켜서 지표를 보아야 한다.
  • 개편을 늦게하면 기존 대비에 비해 많은 비용을 쓰게 된다는 것을 알아내고 확인해야한다.

결론으로, 레거시 시스템 개편은 서비스를 지속, 성장 시키기 위해서 일어난다. (적당한 타이밍에) 개편은 서비스를 지속시키고 성장시키기 위해서 무조건 필요한데, 많은 사업부에서 이걸 놓친다.

개편이 언제 일어나는지를 기술적인 현상이 아니라, 사업적인 현상으로 풀어낸 이야기. 더 적나라하게 이야기하면 ‘개발자가 언제, 어떤 이야기로 사업부를 설득해야하는지’ 이야기. 아, 개편을 위한 시간을 확보하는 것 또한 개발자의 역량이구나.


👨‍🍳 경험한 개편은?

서비스가 로그인 > 목록 > 가게진입 > 주문 > 결제 순으로 생성 및 성장하였다.

개편 경험은 역순 결제 > 주문 > 가게진입 > 목록 > 로그인 으로 진행되었다.

  • 결제: 성능이 부족한 시스템이라 개편
  • 주문: 성능이 부족한 시스템, 현재 안어울리는 비주류 기술이라서 개편
  • 가게진입: 성능이 부족한 시스템, 현재 안어울리는 비주류 기술, 새로운 요구사항을 대응할 수 없는 시스템이라서 개편
  • 회원시스템: 성능이 부족한 시스템, 현재는 비주류인 기술이라서 개편
    • 회원이라는 도메인과 인증이라는 도메인을 분리했다.
    • 회원은 회원답게, 인증은 인증답게


👨‍🍳 개편의 기술

1. 의존성을 한 방향으로 정리하라.

스파게티 코드, 스파게티 의존성. 객체나 컴포넌트의 의존 참조 방향이 복잡하게 꼬여있어서 어려운 상황. 코드를 수정했을 때 사이드 이펙트 측정이 어렵다. 어디서 어떤 문제가 발생할지 여기저기 꼬여있기 때문에 사이드 이펙트를 측정하기 어려운건 개편을 하기 무서운 상태.

스파게티 코드가 왜 발생을 했을까? 코드의 재사용성을 생각하는건 개발자의 덕목이기 때문에. 기존 객체의 코드를 그대로 재사용. 새로운 요구사항이 생기면 또 재사용. 그렇게 재사용을 극한으로 활용하다보면 어느새 스파게티 의존성이 만들어진다. 그럼 스파게티 의존성을 어떻게 풀어내는가?

해답은 의존성을 한 방향으로. 계층이나 아키텍처가 정해지면 좋겠지만, 그게 어렵다면 의존의 깊이로 층을 형성해라. (패키지 분리를 하던지) 한 방향으로 결정을 할 떈, 같은 계층끼리 의존하는 것도 허용하면 안된다. 그렇게 되면 분명히 문제가 생긴다.

image

재사용하려는 코드가 있다고 하더라도 적절하게 분리를 해서 두 서비스가 모두 의존할 수 있는 계층에다가 옮겨서 작성을 해라. 의존성을 한 방향으로만 잘 관리를 해도 스파게티 코드를 피할 수 있게 된다. 그 상태에서 코드를 수정하게 되면 사이드이펙트가 어디서 발생할지 예측이 가능하게 된다.

image

같은 계층간에도 의존을 허용하면 안된다!! 아! 머리에 망치를 맞은 느낌. 흔히 트랜잭션 관리나 비즈니스 로직 특성에 따라 서비스 계층의 클래스를 여러개로 나누는데, (ex. SaveService, ReadService) 이 때 SaveService 에서 ReadService를 의존하게 만들곤 했다. 그걸 허용했을 때 발생하는 문제점은 무엇인가? ReadService 의 내부동작을 변경했을 때 SaveService 는 이 변화를 예측하기 어렵다. 차라리 SaveServiceReadService가 모두 의존할 수 있는 별개의 계층을 만들자. 같은 계층끼리의 의존조차 허용하지 말자. 그래야 사이드이펙트를 추적하기 쉽다.

2. 변경 대상에 대한 경계를 나눈다.

변경의 대상이 되는 객체의 계층이 제각각이면 안된다. 가령 스프링을 기준으로 우리가 변경해야할 코드가 Repository 계층에만 있다면 ‘DB, 영속계층에 관련된 개편이 진행된다.’고 예측이 가능하지만, Controller, Service, Repository 계층이 모두 뒤섞여있다면 어떤 개편 작업을 진행하는 것인지 한 눈에 파악하기 어렵다. 이 때 변경 대상이 되는 클래스들을 모아 하나의 계층으로 인식하는 방법 (변경 대상에 대한 경계를 다르게 인지)이 하나의 솔루션이다.

패키지로 나눌 수도 있고, 모듈로 나눌 수도 있다. 나누어진 변경 대상들을 하나의 게층으로 여긴다. 하나의 계층이 되면서 그 계층 내부에서 책임과 역할을 정의한다. 개편에서 다루어야할 클래스들만 따로 확보해서, 명백하게 계층으로 정의를 해보는 것.

다르게 생각해보면 이렇게 만들어진 계층이 ‘원래 소프트웨어에서 다루어야할 계층’이었는데, Controller, Service, Repository 라는 이름에 너무 익숙해진 나머지 잘못 나누고 있었던 걸수도 있다. 계층을 확보한 후 책임과 역할이 알맞지 않은 코드는 다른 패키지로 빼버리고, 다른 패키지에 넘어가있는 책임과 역할을 확보해둔 계층으로 끌어오는 식으로 책임과 역할을 명백히 그어내는 것. 이렇게 되면 변경 범위에 대한 가시성을 확보할 수 있게 된다.

익숙한 의존성 층으로만 계층을 나누지말고, 이 패키지들을 역할과 책임으로 나눠보면 어떨까?

객체지향과 계층 등을 공부하고 나누는 궁극적인 목표가 무엇인가? 결국 소프트웨어의 유지보수를 위함이다. Controller-Service-Repository 로 이어지는 의존성 층에 너무 익숙해진 나머지 본질을 잊은 건 아닐까? 많은 고민을 해보게 되는 이야기.

물론 요 방식을 팀에 접목 시키기 까진 많은 설득과 이해의 시간이 필요할 것 같다…

3. 어떻게 해서든 테스트를 확보한다.

개편의 대상이 되는 계층 테스트 뿐만 아니라, 한 단계 더 상위 계층의 테스트 코드, 테스트 커버리지도 챙기면 좋다. 시도해보면 뛰어난 안정감을 챙길 수 있을 것.

그러나 현실을 녹록치 않다. 기존에 있던 테스트 케이스 재사용이 어려운 상황. 테스트 케이스 복사 시간도 부족하다. 그래서 용근님은 어떻게까지 테스트를 확보하셨는가? 로컬에서 테스트 할 때 E2E 결과가 똑같이 나오는지 확인했다. (레거시 API와 신규 API간 응답결과를 JSON 형태로 덤프떠서 비교). 테스트라는 것은 원래 인터넷 연결이 끊겨도 동작해야하지만, 현실적인 문제로 이렇게 해서라도 테스트를 확보했다. 이 마저도 nGrinder(성능 테스트 툴)를 이용해서 API를 계속 찌르면서 확인했다. 왜? 테스트를 짤 시간이 없으니까…

테스트 케이스가 아예 없는 경우엔 어떻게할까? 이런 경우 블랙박스 E2E 테스트를 선택했음. 테스트 사이에서 어떤 일이 일어나는지는 모르겠고, 요청이랑 DB에 어떤 데이터가 있는지만 믿고 테스트를 전부 수행하는 것. 기획서를 전부 뒤지고, 애플리케이션의 로그를 전부 긁어서 테스트 목록을 만들어냄.

테스트 툴이 제공하는 테스트만 진짜 테스트로 여기고, 안정감을 느끼곤 했던거 같은데… 기능에 대한 ‘심리적인 안정감을 얻어낸다.’ 라는게, 테스트의 본질에 가장 부합하는 것 같다. 어떻게든 일이 먼저 돌아가게 만들어야 하니까… 문뜩 작년에 스크립트를 실행할 툴을 찾기 귀찮아서 K2(성능 테스트 툴)로 스크립트를 동작시켰던 기억이 난다.

이렇게까지 테스트를 확보해서 무얼 얻었는가? 변경해도 괜찮다는 심리적 안정감

정리

  1. 의존성을 한 방향으로: 정돈된 의존성
  2. 변경에 대한 경계 나누기: 책임과 역할이 명확한 계층과 객체
  3. 테스트: 안정감

이것들을 개편에만 적용해야하는가? 결국에는 유지보수하기 좋은 소프트웨어, 유지보수하기 좋은 설계를 만들기 위해서는 우리가 저런 것들을 항상 신경쓰면서 개발을 하면 좋을거 같다.


👨‍🍳 프로젝트 가시성 확보

라면을 끓이는데 시간이 얼마나 걸릴거 같은가? 흔히 “5분이면 끓일거 같은데?” 상상할 수 있다. 그럼 실제로 라면을 끓여보면 얼마나 걸릴까?

  • 물을 끓인다 3분
  • 면, 스프를 투입하고 5초
  • 면을 익힌다. 3분 30초

“6분 35초”가 걸렸다.

큰 문제를 작은 문제로 만들어 풀어나간다. 해야할 일들을 점점 작게 가시화 시킨다. 프로세스를 작게 작게 쪼갤수록 더 정확하 일정 예측이 가능해진다.

일정에 대해서 예측을 한다는 것은, 리스크를 더 잘 관리할 수 있게 된다는 큰 장점이 된다. (지라, 컨플루언스도 좋지만 엑셀 스프레드 시트를 통해서…) 일정을 관리해서 좋은 점! 같이 일하는 팀원들이 서로 무얼 하고 있는지, 다 알 수 있고 개인 일정 계획도 가능하고, 장기적인 프로젝트를 통해서 지쳐나갈 때, 우리가 어디까지 왔는지, 얼마만큼 더 하면 끝나는지 보여주는 이정표가 된다.

그리고 우리 구성원 뿐만 아니라 이해관계자들도 존재한다. 그 사람들이 프로젝트를 후원하기 좋아진다. 무얼하고 있는지, 어떤 일정이 있는지 명확하게 볼 수 있으므로. 해야할 일들을 가시화하여 문서화하자.

일정예측 & 일정 리스크 관리. 진행상화 공유. 거기에 잘 정리된 문서는 이해관계자를 설득하기도 쉽고, 이해 관계자가 후원하기도 쉬운 도구가 된다.

단순히 나를 위해서가 아니라, 모두를 위해서 정확한 일정 예측을 해야한다는 점. 특히나 대규모 인원이 함께 일하는 환경에서는 더 없이 중요한 지표이자 신뢰 데이터가 될 것 같다. 곧 실력으로도 인식이 되겠지. 상호간의 당연히 지켜야할 매너라 생각하고 더 정성들여 나눠봐야겠다…!


👨‍🍳 도메인 이해 공유

구성원들 간의 도메인 이해 수준의 차이가 생길 수 밖에 없다. 도메인에 대한 이해관계가 다른 사람들이 모이면, 같은 요구사항을 보더라도 다른 해석을 할 수 밖에 없다. 이럴 때 같이 일을 하는 과정에서 커뮤니케이션 비용을 줄이려면 어떻게 해야할까? 이벤트 스토밍. DDD, 전술적 설계

꼭 이벤트 스토밍이 아니더라도, 어쨌거나 도메인을 공유하는 시간을 별도로 자주 가져라.

이벤트 스토밍과 관련해서는 최근 제이슨의 DDD 세레나데 수업을 들으면서도 배우고 있다! 안그래도 최근 회사에서 운영팀분과 개발팀간의 용어차이, 도메인 이해 차이가 자주 보이는 것 같아 지현님께 이벤트스토밍을 한번 진행해보자 건의했는데… 다 같이 시간 될 때 꼭 진행 해봤으면 좋겠다.


👨‍🍳 변화를 측정한다.

측정이라는 것은 무엇에 대한 불확실 성에 대한 정도를 낮추는 행위

무엇이든 측정하고 저장하고 공유해라. 비즈니스적인 결과를 측정할 수도 있고, 기술 자원적인 변화를 측정할 수도 있다. 가아 중요한 것은 트레이드 오프, 도전. 두 가지 키워드를 기억하자.

어떠한 데이터를 보고, 나쁘다 좋다만 생각하지말고 그 나쁜것의 트레이드 오프로 어떤게 좋아졌는가? 좋은 것의 트레이드 오프로 어떤게 나빠졌는가? 무얼 도전했는가? 등을 확인해보면 좋을 것이다. 그리고 꼭 공유하자.


👨‍🍳 후기

DDD 를 공부해야겠다고 느낀 두 번째 세션. (첫번째 세션은 김대성님의 실전! 멀티 모듈 프로젝트 구조와 설계) 발표 중간중간 DDD가 조금씩 등장했다. 자꾸 주변 환경에서 DDD가 흘러나오니, DDD에 관심을 가질 수 밖에 없다.

발표를 듣는 내내 ‘아, 정말 건강하게 개발하시는구나.’ 감탄이 나왔다. 주어진 리소스에 맞춰 다소 괴상한 방법으로 테스트를 챙기면서도, 설계에 대해서는 진심으로 다가서는… 조영호님, 권용근님 발표는 당장 회사 프로젝트에 적용시킬 수 있는 팁을 많이 주시는 것 같다. 같은 계층끼리도 의존을 갖지 말라는 팁은 정말 두고두고 써먹을 것 같다. (특히나 그렇게 했다가 꼬인 코드를 경험하고 있어서인지)

‘변경에 대한 경계 나누기’ 파트에서는 개발자들 사이에 만연하게 퍼져있는, 막연하게 느끼고 있는 이론과 개념들을 다시 한번 점검하게 되었다. 당연하다는 듯 외우고 있던건 없었는지, 본질에 대해 파악은 하고 있었는지 되뇌이고 되짚었다. 어느새 스프링 MVC에 너무 익숙해진 건 아닐까? 스프링 없이 개발 할 수 있을까? 근데 스프링이 없어지는 것보다 내가 없어지는게 더 빠르지 않을까… 🤣 여러가지 고민을 하게 된다.

댓글남기기