모놀리식 서버에서 팀 간 배포경합 완화하기

KIYOUNG KWON·2023년 12월 10일
0
post-custom-banner

개요 및 동기

뭔가 제목은 거창하게 써놨지만 막상 글로 적으려고 하니 어떻게 정리해야 할까 고민을 했지만 일단 적고 생각을 해보기로 했다. 이 글은 모놀리식 서버에서 팀 간 배포경합이 되는 상황을 개선하기 위해 한해동안 시도했던 내용을 의식의 흐름대로 적어보려고 한다.

올해 초 사내 백엔드 팀은 2~3명이서 팀을 구성하여 feature 개발과는 별도의 목표를 잡아 진행을 하기로 하였다. 거기서 나는 배포경합을 완화하자. 라는 목표를 선택하였다. 이러한 목표가 가지고 있던 동기는 목적 조직(사일로)과는 별도로 기능 조직(스쿼드)에서 feature를 개발하는데 스쿼드 단위로 배포하기 위한 독립성을 갖기 위한 요소 중 하나였다.

또 프론트의 배포와 의존성이 존재하는 API가 수정되는 경우 프론트엔드와의 배포일자를 맞춰야 하는 문제도 존재하였다.

우리는 trunk base 환경에서 개발하기 때문에 master에 머지가 되는 순간 배포가 필요한 경우 관련 없는 기능이 포함되어 있어도 배포가 나가야 한다. 그렇기 때문에 스쿼드 간 업무 진행상황을 공유하고 경합을 예상되는 경우 스쿼드 간 배포 일정을 미리 맞추어야 했다.

이러한 부분이 스쿼드 간 업무속도를 저해한다고 팀내에서 판단하여 해결하기 위해 시도했던 내용들을 정리해보려고 한다.

서버 어플리케이션의 분리

처음에 단순하게 생각을 했을 때 경합을 제거하는 가장 확실한 방법은 스쿼드 간 작업하는 서버를 분리하는 것이라 생각하였다. 스쿼드 별로 별도의 서버를 관리하면 결국 경합자체가 발생하지 않게될 것이라 생각했다. 현재 우리 백엔드 팀은 모노레포에 모놀리식 서버에서 (일부 분리된 부분이 있긴하지만..) 작업을 진행하고 있다.

우리가 얻고 싶었던 것...

우린 아직 도메인 간 관계에 대해 구체적인 고민을 한 경험이 없어 분리를 한다면 어디서 부터 어디까지 분리를 진행할지 먼저 정의할 필요가 있었다. 일반적으로 이러한 작업에 이벤트 스토밍을 활용해서 바운디드 컨텍스트를 정의하여 경계를 구분하고 이를 기준으로 분리를 진행한다. 결과적으로 위와 같은 매핑도를 얻게되는 것이 목적이다. 다만 현재 모놀리식 서버에 구현된 모든 비지니스 로직에 대해서 이벤트 스토밍을 진행하려면 팀의 모든 기획자와 개발자들의 도움이 필요하였다.

그래서 우선 현재 이 프로젝트를 진행하고 있는 구성원이 속한 도메인만을 추출해서 진행을 하고 잘 진행되었다면 이를 모두에게 전파하려고 하였다. 하지만 서버분리는 결과적으로 진행하지 않기로 하였다. 크게 이유는 아래와 같은데

  • 구성원이 아직 도메인 간 관계에 대한 이해가 불명확함
  • 도메인 간 관계를 명확하게 하더라도 분산환경에 대한 기술적 성숙도 부족
  • 분리하고 얻을 수 있는 이득의 부족

도메인 간 관계에 대해선 시간을 들여 정리하면 가능하다 생각을 한다. 하지만 도메인을 명확하게 구분하더라도 구현하기 위한 기술적인 성숙도가 부족했다. 우리가 시도했던 구체적인 코드레벨을 이야기하긴 애매하지만 간단히 말하면 공통의 코드를 뽑아내려한 나머지 엔티티도 공통 모듈로 뽑아내려 했고 이는 분산 모놀리식 서버로 가는 지름길 이었다.


대충 위 그림과 같은 구조가 되는데 이렇게 되면 안된다는 의미다.

그리고 가장 큰 문제는 결과적으로 서버를 스쿼드 단위로 잘 분리하더라도 이를 통해 가져올 수 있는 노력대비 이익이 크지 않다는 것이었다. 우리 백엔드 팀은 2개의 스쿼드로 나누어 지는데 서버를 분리하더라도 2개의 스쿼드간 경합이 없어진다가 분리를 통해 가져올 수 있는 이익의 전부였다.

모듈러 모놀리스

그렇다면 우리의 도메인에 대한 이해도를 높이고 경계를 구분하고자 했던 방향성 자체가 문제였을까? 물론 아니다! 좋은 구조로 가는데 분명 필요한 것 이지만 그 결과물이 서버를 분리할 필요는 없다. 우리의 규모에서 적절한 구조를 찾아가면 된다. 서버를 분리할 필요는 없지만 도메인의 경계를 명확하게 하여 구현하고 싶다면 같은 서버(어플리케이션) 내부에서 이를 구분해주면 된다.

우리의 서버 어플리케이션은 레이어드 아키텍처로 모놀리식 서버가 구현되어 있다. 이 형태는 빠르게 구현하긴 좋으나 비지니스 로직이 복잡해질수록 서비스 레이어가 매우 비대해지고 도메인 간 경계가 불명확한 특징을 갖고있다. 그렇기에 도메인 모듈 간 분리를 코드레벨(혹은 모듈단위로)에서 진행하는 모듈러 모놀리스가 우리 규모에선 적절한 선택이 될 것이라 생각했다.

다만 이는 우리의 소스코드 구조를 개선하고 앞으로 진행해야 할 방향은 맞지만 처음에 해결하고자 했던 배포경합을 완화하자.는 목표를 달성할 수 있는 수단이 아니었다. 결국 배포해야할 어플리케이션은 하나이기 때문이다.

우리가 근본적으로 하고싶었던 것이 무었일까?

우리가 문제로 생각하고 있던 부분을 다시 생각해 보기로 하였다. 처음에 정의했던 문제가 뭐였을까? 무엇을 해결하고 싶었던 것일까? trunk base 구조에서 개발 중인 기능이 배포에 포함되는 것을 방지하기 위해 롤백을 수행하거나 머지를 나중에 한번에 하는 상황을 막고 싶은 것이다.

이를 최대한 심플하게 해결할 수 있는 수단이 뭘까를 생각하면 답은 어쩌면 정해져있던 것 일지도 모르겠다.

feature-flag

최종적으로 우리가 선택한 방법은 feature-flag를 도입하는 것이었다. feature-flag는 코드를 수정하지 않고 시스템의 동작을 변경할 수 있는 기술이다.

return if (featureRouter.get(FeatureFlagKeys.SOMETHING_FEATURE)) {
     // new feature
} else {
     // old feature
}

간략하게 위처럼 flag값이 true이면 새로운 기능, false이면 기존의 기능으로 분기를 시킨다고 보면된다. feature-flag를 도입하면 과장을 조금 보태서 하루에 몇번이든 배포해도 상관이 없어진다. 아직 검증이 덜 된 기능은 feature-flag가 분기만 되지 않으면 되기 때문이다.

어쩌면 왜 아직도 없었어? 라고 할지도 모르겠지만.. 현재까진 크게 필요성을 느끼지 못했던 것이 아닐까 싶다. 하지만 팀 구성원이 늘어나고 배포도 잦아지면서 점점 경합하게 되는 상황이 늘어나면서 필요성을 갖게되었다.

사실 구현이나 사용은 어렵지 않지만 이를 도입하기 위한 새로운 배포 프로세스를 정의하는 것이 가장 큰 문제였다. 이에 대한 이야기는 별도로 feature-flag 도입에 대해서 정리하게 된다면 자세히 이야기하면 좋을 것 같다.

결론

사실 이 이야기에서 하고 싶었던 이야기는 우리가 feature-flag를 도입했다! 라기 보단 문제를 잘 정의하고 지금 상황에서 목표를 달성하기 위해 효율적인 선택이 중요하다는 것을 말하고 싶었다. 프로젝트를 이끌었다 라고 까지 말하긴 애매하지만 메인으로 진행을 하면서 갈팡질팡하며 내가 갈길이 멀구나 하는 것을 많이 느끼게 되었던 것 같다.

ps) 위에선 TMI라 뭔가 애매하여 적지 못했지만 삽질하고 있는 우리에게 많은 피드백을 주신 우리 CTO님께 감사의 인사를 드리고 싶습니다. 🥲

post-custom-banner

0개의 댓글