blog.blcklamb
All Posts

2년차, 드디어 레거시를 만나다

신규 프로덕트와 0 to 1 프로젝트를 지나 처음으로 레거시를 마주하며 느낀 개발 조직, 코드베이스, 마이그레이션 비용에 대한 회고.

8 min read

TL;DR 기술적으로 깊게 파고드는 글이라기보다는, 여러 회사에서 경험한 개발 방식과 애자일 문화, 그리고 처음 마주한 레거시에 대한 사견을 담은 회고입니다.

세 번째 회사에 들어온 지 2개월이 됐다. 아직 2년 차일 뿐인데 벌써 세 번째 회사라니, 여러 환경을 비교해볼 수 있다는 장점은 있지만 이직의 맥락이 충분히 전달되지 않으면 끈기가 부족한 사람처럼 보일 수도 있다. 그래서 요즘은 앞으로 커리어를 어떻게 쌓아야 할지에 대해서도 자주 생각한다.

그런데 제목처럼, 만으로 14개월 정도의 경력에 접어든 뒤에야 처음으로 레거시를 제대로 만나게 됐다. 오늘은 그동안 어떤 환경에서 일해왔고, 왜 이제서야 레거시를 만났는지, 그리고 레거시를 마주하며 어떤 생각을 하게 됐는지 정리해보려고 한다.

네 명이서 맨땅에 젠가 쌓기

2022년 말, 첫 회사에서 커리어를 시작했다. B2C 교육 플랫폼을 운영하는 회사였고, 이미 php로 만들어진 강의 및 커뮤니티 서비스가 있었다. 다만 내가 처음 참여한 프로젝트는 기존 서비스의 유지보수가 아니라, 프리미엄 고객을 위한 신규 프로덕트 개발이었다.

처음 합류했을 때 레포지토리는 프로젝트 초기 설정과 CI/CD 정도가 준비된 상태였다. 무언가를 짓고 있기는 한데, 최종적으로 어떤 건물이 올라갈지는 아직 선명하지 않은 상태에 가까웠다. 실제로 운영 중인 서비스가 아니었기 때문에 어떤 코드가 실제 효용을 갖는지, 어떤 요구사항이 살아있는지 판단하기도 쉽지 않았다.

이것을 특정 개발자 한 명의 문제라고 생각하지는 않는다. 비록 회사에는 운영 중인 프로덕트가 있었지만, 신규 프로덕트를 만드는 팀은 이제 막 만들어지는 중이었다. 짧은 주기로 기획이 바뀌었고, 유기적으로 연결된 태스크들이 산발적으로 흩어져 있었으며, 우선순위가 명확하지 않아 많은 일이 동시에 in progress 상태로 남아 있었다.

컴포넌트 파일을 보며 요구사항을 추론하다가 다른 정책과 충돌하는 부분을 발견해 질문하면, 다시 기획자에게 확인해야 하는 경우가 많았다. 그 답변을 듣고 나면 이미 deprecated된 요구사항이 코드에 남아 있거나, 애초에 요구사항 자체에 빈틈이 있는 경우도 있었다.

이후 프론트엔드 개발자 두 명이 더 합류하면서 네 명이 함께 신규 프로젝트를 만들기 시작했다. 황야 같던 레포지토리를 몇 번씩 갈아엎고, 덜어내고, 다시 쌓아가며 조금씩 마을의 형태로 바꿔갔다.

가장 기억에 남는 스프린트는 Atomic 디자인 패턴을 도입했던 경험이다. 레거시가 없는 신규 프로덕트였기 때문에 가장 작은 UI 단위부터 조합 가능한 UI 블록까지 디자이너, 앱 개발자와 활발히 논의할 수 있었다. 결과적으로 Atomic 디자인 패턴은 잦은 변경에 대응하기 어려워 폐기됐지만, 그 과정을 통해 설계가 현장의 요구사항과 얼마나 밀접하게 맞물려야 하는지 배울 수 있었다.

혼자서 공장 돌리기

두 번째 회사는 2024년 3월에 합류한 아웃소싱 스타트업이었다. 첫 회사에서 만들던 프로젝트를 릴리즈하지 못한 채 떠났기 때문에, 실제 사용자를 마주하는 경험에 대한 갈증이 있었다. 그래서 빠르게 다양한 서비스를 만들 수 있는 SI, 특히 다채로운 기획을 가진 스타트업을 대상으로 하는 외주 개발 회사에 들어가게 됐다.

당시 회사의 프로세스는 0 to 1에 가까웠다. 창업 아이디어가 있으면 기획, 디자인, 개발, 배포까지 위탁받고, 프로덕트와 어드민, 코드까지 전달하는 방식이었다. 프로젝트 기간은 짧으면 한 달, 길면 3개월 정도였고, 8개월 동안 총 4개의 프로젝트를 진행했다. 대부분의 프로젝트는 혼자 맡았다.

짧은 기간 안에 혼자 0 to 1을 반복하다 보면 좋지 않은 습관이 생긴다. 리팩토링을 미루고, 유지보수성을 충분히 고려하지 못하고, QA 단계에서 당연한 버그가 쏟아진 뒤에야 그 방식이 틀렸다는 것을 깨닫는다. 하지만 곧바로 다음 프로젝트가 시작되면 일정의 압박에 밀려 다시 같은 습관으로 돌아가곤 했다.

코드 리뷰도 쉽지 않았다. 각자 1인 1프로젝트를 맡고 있었기 때문에 리뷰를 하려면 먼저 다른 개발자가 맡은 프로젝트의 기획과 맥락을 이해해야 했다. 시간상 코드 리뷰를 생략하는 경우도 많았고, 리뷰를 하더라도 각자 다른 방향으로 일단 굴러가는 공장을 만드는 일이 반복됐다.

돌이켜보면 만 1년의 경력 동안 나는 제대로 된 레거시를 만나본 적이 없었다. 좋은 레거시를 만들어야 한다는 의식조차 충분히 갖지 못한 채, 오히려 누군가에게는 나쁜 레거시가 될 코드를 생산하는 입장에 가까웠다.

레거시와의 조우

두 번째 회사를 뒤로하고 2025년 1월, 지금의 회사에 합류했다. 그리고 드디어 많이들 고통스럽다고 말하던 레거시를 만나게 됐다.

물론 지금의 회사도 스타트업이기 때문에 흔히 말하는 5년, 10년, 혹은 그보다 오래된 레거시는 아니다. 현재 담당하는 프로젝트도 아직 2년이 넘지 않았다. 그럼에도 이미 사용자가 있고, 기존 의사결정의 결과가 코드와 구조에 남아 있으며, 유지보수와 개선, 신규 기능 구현이 동시에 요구된다는 점에서 나에게는 처음 마주한 진짜 레거시였다.

처음 만난 레거시에 대한 인상은 의외로 흥미롭고, 흥분된다에 가까웠다.

흥미로운 레거시

흥미롭다고 느낀 이유는 크게 세 가지였다.

  1. 평소 생각하던 설계와 코드 스타일이 달랐다.
  2. 익숙하게 쓰던 라이브러리보다 낮은 버전으로 세팅되어 있었다.
  3. 재사용성 높은 컴포넌트와 라이브러리 내장 기능을 감싸는 추상화가 우아했다.

이전 회사에서는 풀스택으로 혼자 프로젝트를 진행하는 일이 많았고, 빠르게 결과물을 만들어야 했다. 그 과정에서 Cursor나 GitHub Copilot 같은 코드 생성 AI에 의존하는 시간도 늘어났다. 협업이나 논의 과정을 충분히 거치지 못한 채 자동완성에 맞춰 코드를 쌓다 보면, 내가 원하는 개발이 정말 이런 모습인지 회의감이 들 때도 있었다.

그런 상태에서 사람이 오래 고민하며 만들어온 코드들을 보니 오히려 흥미로웠다. 내가 익숙하게 떠올리던 설계와 다르게 문제를 풀어낸 흔적이 있었고, 그 차이를 읽는 과정 자체가 공부가 됐다.

라이브러리 버전도 마찬가지였다. 이전에는 Next.js 14의 App Router를 주로 사용했는데, 현재 프로젝트는 Next.js 13의 Pages Router 기반이었다. 취업 준비 시절에 보던 문법과 구조를 다시 마주하니 잠깐 과거로 돌아간 듯한 기분도 들었다. App Router는 DX와 최적화 측면에서 장점이 있지만, Pages Router로 작성된 프로젝트를 읽는 일은 Next.js가 어떤 흐름으로 변화해왔는지 다시 확인하는 계기가 됐다.

현재 중점적으로 담당하는 프로덕트는 회사의 첫 번째 프로덕트에서 검증된 UI 시스템, 라우터 구조, API 전략을 바탕으로 만들어진 두 번째 프로덕트다. 그 덕분에 재사용되는 UI 컴포넌트와 TanStack Query 기반 API 레이어, 타입 추상화가 꽤 견고하게 구성되어 있었다. 처음부터 맨땅에 헤딩하듯 만들었다면 쉽게 도달하지 못했을 구조라는 생각이 들었다.

흥분되는 레거시

흥분된다고 느낀 이유도 있다.

  1. 나에게 새로운 형태의 프로젝트라 앞으로 어떤 일이 생길지 기대된다.
  2. 고칠 수 있는 부분이 곳곳에 보인다.
  3. 신규 입사자의 시선으로 기존 관점에 다른 질문을 던질 수 있다.

지금까지는 주로 0 to 1을 해왔다. 하지만 지금은 이미 돌아가는 제품을 더 낫게 만드는 1 to 1+의 일을 하고 있다. 새로운 문제를 마주할 때 비용과 시간을 들여서라도 직접 경험해보고 싶은 편이라, 이 변화 자체가 꽤 설렌다.

물론 프로젝트가 완벽하다는 뜻은 아니다. 처음 할당받은 태스크는 프로젝트에서 개선할 수 있는 부분을 직접 찾아 고치는 일이었는데, 원하는 코드를 찾는 것부터 쉽지 않았다. 페이지 route와 컴포넌트 이름이 맞지 않거나, 이름은 비슷하지만 실제로 쓰이지 않는 코드가 공존하기도 했다. 예를 들어 프로그램 리스트를 나타내는 컴포넌트가 Programs.tsx, ProgramList.tsx처럼 함께 존재하면 어떤 파일이 현재 화면과 연결되는지부터 확인해야 했다.

스타일링 방식도 의문을 남겼다. 같은 목적의 스타일인데 어떤 곳은 inline style을 사용하고, 어떤 곳은 emotion의 css prop을 사용하고 있었다. 이 차이가 의도된 분리인지, 당시 작성자의 습관인지, 혹은 점진적 이관의 흔적인지 파악하는 과정이 필요했다.

tsx
// 1. style을 사용하는 경우
<CertainComponent
style={{
fontSize: 16,
backgroundColor: "red",
}}
>
{/* ... */}
</CertainComponent>
// 2. css prop을 사용하는 경우
<CertainComponent
css={css`
font-size: 16px;
background-color: red;
`}
>
{/* ... */}
</CertainComponent>

이런 지점들은 기존 개발자들이 매일 보던 프로젝트에서는 너무 당연해져서 잘 보이지 않을 수 있다. 반대로 새로 들어온 사람의 눈에는 당연하지 않은 부분이 더 잘 보인다. 그 차이를 단순한 불평으로 소비하지 않고, 프로젝트를 더 이해하기 위한 질문과 개선 제안으로 바꾸는 것이 지금 내가 할 수 있는 일이라고 생각한다.

마무리

아직 레거시를 만난 지 2개월밖에 되지 않았다. 전부 파악하기에는 턱없이 짧은 시간이고, 앞으로 더 많은 것을 찾아내고 느끼게 될 것이다. 그러기 위해서는 지금 막 합류한 사람만이 가질 수 있는 신선한 시야를 잃지 않아야 한다. 동시에 예전 버전의 라이브러리를 다시 공부하고, 당시의 공식 문서와 의사결정 배경을 읽는 일도 소홀히 하면 안 된다.

입사 초반에 사수분께 이런 질문을 한 적이 있다.

왜 Next.js App Router로 마이그레이션하지 않나요?

나는 유지보수성과 최적화 측면에서 App Router가 더 낫고, 나에게도 더 익숙하기 때문에 최신 버전을 쓰는 것이 당연히 좋은 선택이라고 생각했다. 하지만 사수분은 현재 프로덕트의 배포 구조상 App Router로 업그레이드해도 얻을 수 있는 이점이 크지 않고, 마이그레이션에 쓸 비용을 기능 구현과 개선에 쓰는 편이 더 임팩트가 크다고 설명해주셨다.

그 답변을 들으며 기존 프로덕트를 가장 잘 이해하고 있는 사람의 관점을 존중해야 한다는 생각이 들었다. 새로운 기술이 항상 더 나은 선택은 아니고, 마이그레이션은 그 자체로 비용이다. 중요한 것은 지금 이 제품에 필요한 변화가 무엇인지 판단하는 일이다.

레거시는 단순히 극복해야 할 대상이 아니라, 개선해 나가며 배우는 과정이다. 앞으로 이 프로젝트 안에서 내가 제안할 수 있는 변화가 무엇일지 기대된다.

댓글