바퀴를 개선하는 이미지

달리는 기차의 바퀴 교체하기 1. Planning

profile-img
한재엽Frontend Developer
2024. 1. 11

이미 운영 중인 제품을 전반적으로 다시 만들거나 리팩토링 하는 경험을 해볼 수 있는 기회는 흔치 않은데요. 운 좋게 팀 내 공감대가 형성되어 여러 팀원과 하나의 제품을 온전히 개선해 볼 수 있었어요. 이 글에서는 구조 개선에 앞서 어떻게 프로젝트를 플래닝하고 진행했는지 소개할게요.

브랜드페이?

토스페이먼츠의 결제 제품 중에는 브랜드페이라는 화이트 라벨(white label) 결제 제품이 있어요. 가맹점의 간편 결제를 만들 수 있도록 해주는 제품이죠. 브랜드페이를 사용하면 가맹점 어디나 본인만의 브랜드를 입힌 간편결제 서비스를 고객 대상으로 제공할 수 있어요.

가맹점은 우리가 제공하는 SDK를 통해 결제 수단 등록 및 관리 퍼널, 결제 화면 등에 브랜드명, 색상, 결제 화면 구성 등 다양한 요소를 커스터마이징할 수 있어요. 그래서 결제하는 소비자 입장에서는 브랜드페이를 통해 가맹점의 자체 간편결제 서비스를 이용하는 것처럼 느낄 수 있어요.

제품이 마주한 문제

브랜드페이를 도입하려는 회사의 상황은 다양했어요. 어떤 팀은 API로만 연동해서 클라이언트 기능은 건드리지 않았고, 어떤 팀은 버튼 텍스트 색상까지 바꾸고 싶어했죠. 이렇게 다양한 층위로 들어오는 요구사항을 받아들이려면 충분한 확장성이 필요했지만, 빠르게 성장하는 제품이다 보니 설계를 되돌아 보지 못하고 여러 요구사항에 대응하는데 보다 많은 리소스를 할애하게 되었어요. 그러다보니 새로운 기능을 추가하는 시간은 갈수록 길어졌어요. 운영에 드는 비용이 점점 불어나는 걸 느꼈죠.

사실 우리가 일상에서 접하는 많은 제품이 이런 상황을 겪어요. 제품을 처음 만들 때는 이 제품이 얼마나 지속될지, 어떤 요구사항이 생겨날 지 알 수 없어요. 미리 변수를 예상할 수 없기 때문에 제품의 성숙도가 올라오는 시점에 이런 문제가 발생하는 건 어찌 보면 자연스러워요. 소프트웨어 제품의 숙명이라고 할 수도 있겠죠. 보통 이런 경우 ‘아무것도 하지 않거나’, 무언가를 개선하고자 시도하는데요. 후자를 선택한 제품팀은 아래 세 가지 선택의 기로에 놓여요.

1번. 끊임없이 리소스를 더 투입하기

현실적으로 불가능에 가까운 선택이에요. 회사는 비용 대비 생산되는 결과물이 효율적이어야 지속 가능한데, 리소스를 끊임없이 더 투자하는 것은 지속 가능하지 않기 때문이에요. 물론 새로운 기능을 추가하지 않는다면 리소스 투입 없이 운영만 할 수도 있겠죠. (Secret: 신규 개발 없이 운영만 하는 일을 누가 해야 하는지에 대한 문제가 추가로 생길 수 있어요.)

2번. 새로 만들기

가장 재밌고 쉬워 보이네요. 그린필드 프로젝트(Greenfield project: 기존에 존재하는 코드없이 새로 시작하는 프로젝트)라니, 얼마나 설레나요! 써보고 싶은 기술도 이것 저것 사용해 볼 수 있고, 왠지 이 프로젝트는 잘 만들 수 있을 것 같은 의지가 넘쳐 흐르죠. 그러다보니 대부분의 의사결정 방향은 2번으로 흘러가요. (Secret: 자칫 잘못하면 이후에 개편할 또 하나의 프로젝트를 만드는 것과 다름 없어요.)

다만 이 선택에 있어서 한 가지 주의해야 할 점이 있다면 기존 제품을 운영하는 비용인데요. 기존에 운영하고 있던 제품을 완전히 신경쓰지 않고 종료할 수 있다면 다행이지만, 그렇지 못한 환경이라면(대표적으로 SDK 제품) 기존 제품과 새 제품을 동시에 운영해야 하는 슬픈 운명에 처하게 됩니다. 이렇게 N번이 반복되면 N개의 제품을 운영해야 하는 최악의 결말에 이르게 되죠.

3번. 적당히 잘 개선하기

누구나 이 방식을 원하고, 당연한 선택처럼 보이지만 운영 중인 프로젝트를 적당히 개선하기란 쉽지 않아요. 쉬웠다면 제품이 마주하는 문제는 없었을 겁니다. 관련해 "달리는 기차의 바퀴를 갈아 끼운다"는 유명한 격언도 있죠.

우선 제품에 대한 맥락을 모두 알고 있어야 가능한 작업입니다. 코드를 수정할 때 더 이상 사용되지 않는 코드인지 어떤 방식으로 동작하는 코드인지 알고 있어야 코드를 수정할 수 있어요. 신뢰할 수 있는 문서라도 남아있어야 하는데 오래된 제품일수록 코드 외적인 것들이 제대로 챙겨지지 않더라구요.

맥락을 알고 있어도 기존 동작을 온전히 보전하면서 코드를 수정하는 것은 쉽지 않은 작업이예요. 그렇기 때문에 테스트 코드를 작성하고 제품 코드를 수정하곤 하는데요. 보통 이런 제품들은 테스트 코드를 작성하기 어려운 구조로 작성되어 있습니다. 구조적인 어려움을 극복하고 동작하고 있는 모든 기능을 테스트 코드로 표현한 다음 코드를 수정해야 하기 때문이죠. 시간도 오래 걸리고 기술적으로 난이도가 높은 작업입니다. (Spoiler: 저희는 이 3번을 선택했어요.)

문제 해결 기반 만들기

1. 근본적인 문제 정의하기

먼저 발생하는 현상을 관찰하고 왜 발생하는지 근본적인 원인을 파악하여 문제를 제대로 정의하는게 우선이었어요.

현상 1. 장애 발생

배포 후, 간헐적으로 일부 요소에 대한 장애가 발생하기도 했고, 좀 더 많은 고민이 소요되는 케이스가 있기도 했어요. 배포 후 발생하는 장애 상황은 담당 엔지니어로 하여금 자신감을 떨어뜨리는 요소 중 하나인데요, 이렇게 떨어진 자신감은 궁극적으로 코드 개선에 소극적으로 만들고 이로 인해 제품이 근본적으로 개선되지 못하는 결과를 만듭니다. 개선되지 못하는 제품은 또 다른 장애를 만들게 되어 악순환을 만들곤 해요.

현상 2. 기능 추가의 어려움

근본적인 개선이 필요하지만, 기능이 추가되거나 버그를 수정하기 위해 추가되는 코드 등으로 인해 복잡도가 높은 구조가 만들어졌어요. 기능을 추가하는데 어려워진 제품은 속도가 느려질 수 밖에 없고 담당 엔지니어의 고민도 깊어지게 되었습니다. 제품을 담당하는 엔지니어 입장에서 본인의 문제로 생각할 수 있으나, 이는 제품의 문제이자 팀의 문제이므로 보다 근본적인 접근이 필요했습니다.

다행히 브랜드페이 제품의 문제는 명확했어요. 코드 중복과 테스트 코드였죠.

문제 1. 파편화된 코드

제품의 히스토리가 축적되고, 다양한 기능 추가와 업데이트가 진행됨에 따라 모든 코드를 이해하는 분을 찾기 어려울 정도가 되었고, 어떤 코드를 상황에 따라 사용해야 할지 여부에 대한 난이도도 점차 높아졌어요. 자연스럽게 같은 기능을 하는 코드들이 재생산되곤 했습니다. 화이트 라벨 제품의 특성상, 특정 가맹점을 위한 기능이 일부 존재하는데요. 이런 기능은 코드 외부에서 조합하는 방식으로 만들어져야 했지만, 그렇지 않은 경우도 간혹 발생했어요.

문제 2. 리팩토링 내성이 약한 테스트 코드

테스트 코드는 제품 코드가 명세대로 동작하는지 확인할 수 있는 중요한 코드입니다. 브랜드페이 제품 또한 결제가 이루어지는 제품이라 안정성이 중요하다고 판단했기 때문에 테스트 코드를 많이 작성했어요. 더 나아가 다음 세 가지를 고려하여 좋은 테스트 코드를 작성하기 위해 신경썼습니다.

  1. 제품 명세를 잘 반영해야 함.
  2. 테스트 케이스의 성공/실패가 빠르게 전달되어야 함.
  3. 제품 코드의 구현을 수정해도 그 결과가 달라지지 않아야 함.

그런데 몇몇 테스트 케이스에서 3번이 부족한 경우가 있었습니다. 테스트 코드를 잘 작성하려고 하다 보면 제품 코드도 자연스럽게 더 좋은 구조로 수정하곤 하는데요, 일정 상 구조 변경은 잠시 미뤄두고 기능을 검증하는데 집중하여 테스트 코드를 작성하곤 했어요. 당시에는 명세를 잘 표현하는 테스트 코드이고 제품 코드의 정상 동작을 잘 보장하여 많은 도움이 됐는데요, 나중에 제품 코드를 변경하려고 할 때 간혹 발목을 잡는 경우가 발생하곤 합니다. 이렇게 작성된 테스트 케이스는 여러 의존성을 가지게 되어 검증하는 비즈니스 로직이 변경되지 않았음에도 다른 변경에 의해 실패하곤 했는데요, 이 부분을 신경써줘야 했습니다.

제품 코드를 수정했을 때, 기존 기능들이 제대로 동작하는지 믿고 맡길 수 있는 테스트 코드가 있어야 개발자가 자신감을 갖고 제품 코드를 변경할 수 있을 것이라 판단했습니다.

2. 공감대 형성

앞서 정의한 문제들이 얼마나 시급한지 엔지니어끼리만 인지하고 있다면 현실적으로 이를 개선하기 어려워요. 결국 개선 작업에 드는 비용 대비 기대 효과가 명확하게 정의되어야 하는데 기술적인 개선이라는 추상적인 결과물로는 설득하기 어렵기 때문입니다.

브랜드페이 제품은 가맹점으로 하여금 지속적으로 이용하고 싶은 제품 중 하나였어요. 앞으로도 많은 기능들이 추가될 예정이었고 토스페이먼츠와 가맹점의 성장에 많은 기여를 할 제품이라는 의견이 모아졌습니다. 장기적인 관점에서 엔지니어링 해야 하는 이유가 있는 제품이라고 판단했어요.

이러한 의견을 바탕으로 이해관계자 분들과 리소스에 대한 이야기를 나눌 수 있었어요. 제품을 담당하는 TPO(Technical Product Owner)분도 리소스 대비 효율성을 높여야 한다는 측면에 대해 공감하였고, 제품의 방향성과 현재 상황을 고려했을 때 추가적인 리소스 투입을 통해 제품을 고도화할 필요가 있음을 Head of Technology 분과의 논의를 통해 도출할 수 있었어요.

3. 목표 설정하기

단순 리팩토링이 아닌 ‘재구조화’ 우선

브랜드페이라는 제품은 결제 고객이 마주하는 클라이언트 제품뿐만 아니라 가맹점에서 사용하는 SDK 제품도 있어요. 즉 기존에 운영하고 있던 제품을 완전히 종료할 수 없는 제품이라는 뜻인데요. 앞서 이야기한 방법 중 2번째 방법인 ‘새로 만들기’는 운영 비용이 많이 들어간다고 판단했어요. 자연스럽게 3번인 어떻게 하면 지금의 제품을 잘 개선해 볼 수 있을까 고민하게 됐어요.

'재구조화'라는 단어는 리팩토링보다 좀 더 거시적인 관점에서의 개선을 뜻해요. 리팩토링은 아시다시피 모듈 간의 협력 관계를 개선하거나, 함수의 구현을 수정하는 등 가독성을 높이기 위한 작업 등을 의미하는데요, 리팩토링을 위한 프로젝트 구조 개선 작업이라고 생각하면 좋을 것 같아요. ‘나무보다는 숲’을 중심에 두고 싶었죠. 물론 리팩토링도 제품을 개선하는 데 있어서 꼭 필요한 작업이기 때문에 재구조화 작업을 진행한 다음 다시 시스템을 만들어서 진행하기로 했어요.

이번 프로젝트에서는 테스트 코드의 리팩토링 내성을 가장 먼저 개선하려고 했어요. 테스트 코드가 변경을 방해하지 않도록 수정하고 개발자가 자신 있게 수정, 배포를 할 수 있도록 말이죠.

그러면서 동시에 테스트를 작성하기 쉽게 만드는 것 또한 중요했어요. 지금 당장의 문제만 해결한다면 시간이 지나 또 같은 문제가 발생할 것이기 때문에 앞으로 추가될 기능에 대해서도 테스트 코드를 잘 작성할 수 있는 기반을 마련해야 했어요.

프리모템(pre-mortem)으로 시작하기

즉각적인 리소스 투입을 통해 코드를 수정하기에는 제품 전체를 대상으로 하는 작업이기에 크기가 컸어요. 그래서 개선에 대한 플래닝이 필요했고, 먼저 프리모템을 해보기로 했습니다. 장애가 발생하면 재발을 방지하기 위해 사후 회고(Postmortem)를 하는데, 프리모템은 발생할 것 같은 문제 상황을 가정하고 미리 회고하는 것을 뜻해요.

이 개선 프로젝트가 제대로 진행되지 않았다면, 그리고 원하는 목표를 달성할 수 없었다면 왜 그랬을지 먼저 회고해봤어요.

우선순위

이미 운영 중인 제품이기 때문에 새로운 기능 개발을 멈출 수는 없었어요. 새 기능 개발과 기존 기능 개선을 동시에 해야 했죠. 자칫 리소스 분배를 잘못하면 새로운 기능을 만드는 것에 집중하게 되고 개선 작업을 못 하게 될 것 같았어요. 일정이 정해져 있는 상황에서 눈에 보이는 이익을 가져다주는 작업과 그렇지 않은 개선 작업을 비교했을 때 후자는 대부분 우선순위가 밀리는 경우가 많은 편이기 때문이에요.

따라서, 랜드페이 제품에 투입되는 리소스를 구성할 때, 신규 기능을 개발하기 위한 리소스와 운영하면서 개선 작업을 진행할 리소스를 철저히 분리했어요. 신규 개발과 개선 작업의 우선순위를 동일 선상에서 파악하지 않고 다른 트랙에서 고민하기 위해서였어요. 최종적으로 운영/개선 작업엔 필자 포함 2명, 신규 개발엔 1명으로 총 3명이 작업을 진행했습니다.

기술적인 역량

개선 작업을 위한 리소스를 확보해도 문제를 정확하게 개선할 수 있는 기술적인 역량이 뒷받침되어야 해요. 똑같이 문제있는 테스트 코드를 작성하거나 변경에 유연하지 못한 코드로 수정한다면 원하는 목표를 달성하지 못해 개선 작업을 하는 의미가 없어지겠죠.

다행히 팀 내에 신뢰할 수 있는 분이 계셔서 별도의 신규 채용 없이 팀을 셋업할 수 있었어요.

프로젝트 매니징

개선 프로젝트는 제품의 로드맵을 설계하고 기능을 구현하기 위해 요구사항을 분석하는 프로젝트와는 다른 면이 있었어요. 앞서 이야기한 ‘기대 효과’라는 부분을 측정하기 어려운데요, ‘이 기능을 추가 했을 때 몇 명의 사용자가 늘어난다.’처럼 ‘제품을 개선한다면 생산성이 몇 % 오를 수 있다.’와 같이 수치화하기 어렵기 때문입니다.

일정 산정도 쉽지 않아요. 기능을 구현하기 위한 일정은 기능을 배포하면 마무리되는 것이지만 개선 작업은 끝도 없이 진행할 수 있기 때문이죠. 앞서 이야기한 것처럼 목표를 구체적으로 정할 수 없으니 개선 작업을 어느 시점에 마무리해야 하는지도 고민이 필요했어요.

별다른 성과 없이 작업 기간만 길어진다면 작업에 참여하는 엔지니어들의 동기부여도 떨어질 것이고 해결하고자 했던 문제도 제대로 해결 못할 수 있다고 판단했어요.

문서화

이 프로젝트가 제대로 돌아가려면 목표가 명확해야 하고 업무가 제대로 분배되어 진행하고 있는 것들이 투명하게 공유되는 것이 중요하다고 판단했어요. 그래서 프로젝트가 늘어지지 않도록 기대하는 설계를 먼저 그렸고, 이 설계에 이르기까지 진행 상황을 가시화 했습니다.

브랜드페이 개선 프로젝트 노션 데이터베이스

마무리

PM 직군은 아니지만 팀의 목표를 달성하는 데 있어서 중요한 문제라고 생각했고 직접 팀을 셋업해서 진행해 봤어요. 문제를 명확하게 정의하는데 집중했고 프로젝트가 잘 진행될 있도록 여러 장치들을 고민했는데 어려운 점도 많았고 배운 점도 많았습니다.

이번 글에서는 제품 개선 작업을 어떻게 시작했고 어디에 집중했는지 다뤘다면 다음 편에서는 실제로 어떻게 개선했는지 기술적인 내용을 중심으로 다룰 예정입니다. 어떤 방식으로 테스트 코드의 신뢰를 확보하고 제품을 리팩토링해 나갔는지를 소개할 예정이니 많은 관심 부탁드려요.

Write 한재엽 Review 박성현 Edit 한주연

재미있게 읽으셨나요?

좋았는지, 아쉬웠는지, 아래 이모지를 눌러 의견을 들려주세요.

😍
🤔
website-code-blue

토스팀이 만드는 수많은 혁신의 순간들

당신과 함께 만들고 싶습니다.
지금, 토스팀에 합류하세요.
채용 중인 공고 보기