
바퀴 대신 로켓 만들기
바퀴 대신 로켓 만들기 관련
;:: info
FEConf2024에서 발표한 <바퀴 대신 로켓 만들기>를 정리한 글입니다. 발표 내용을 2회로 나누어 발행합니다. 1회에서는 개발자가 직면하는 ‘디자인 프로토타입’ 병목 현상과 그 해결 방법에 대해 알아봅니다. 2회에서는 ‘백엔드 API’, ‘요구 사항’ 병목의 해결 방법에 대해 알아봅니다. 본문에 삽입된 이미지의 출처는 모두 이 콘텐츠와 같은 제목의 발표 자료로, 따로 출처를 표기하지 않았습니다.
:::
안녕하세요. 저는 ‘바퀴 대신 로켓 만들기’라는 제목의 발표를 진행할 양의현입니다. 저는 주로 리액트를 사용하여 개발하고, 인터페이스나 협업, 영감을 주는 활동에 관심이 많습니다. 지금은 토스 페이먼츠라는 회사의 프론트엔드 개발자로 일하며, 어드민 프로덕트 팀에서 어드민 제품을 주로 만들고 있습니다. 제가 이 팀에서 경험하며 얻은 것들을 이번 발표를 통해 들려드리려고 합니다.
레거시 청산
토스 페이먼츠가 짊어진 유산
본격적인 설명에 앞서 배경 설명을 먼저 하겠습니다. 토스 페이먼츠라는 회사는 5년 정도 된 신생 기업이지만, 사실 데이콤이라는 회사부터 시작해 LG U+ PG라는 회사를 거쳐, PG 업계에서 30년 넘게 경험을 쌓아온 회사입니다. 스타트업이지만 꽤나 긴 기간 동안 업을 이어오면서 많은 유산을 상속받았습니다.

토스 페이먼츠가 처음 법인을 설립한 당시에는 시장을 혁신해보자라는 자신감이 넘쳤습니다. 하지만 그 유산 속에 존재하는 다양하고 방대한 레거시 서비스들이 성장에 대한 발목을 잡고 있습니다. JSP, EUC-KR 등 생소한 것들이 이곳저곳 지뢰처럼 존재하며 비즈니스의 발전을 막고 있다고 생각합니다.
회사 전사 차원에서도 이 문제를 중요하게 생각하며, 24년 상반기에는 특히 이 레거시들을 꼭 청산해야 한다는 의견이 급부상했습니다. 이로 인해 제가 속한 팀에서도 레거시 청산 업무가 굉장히 많아졌습니다.

무려 400개가 넘는 어드민 화면이 레거시로 존재하고, 6개월 내에 팀에서 관리 가능한 수준과 형태로 이관하는 프로젝트가 필요했습니다. 하지만 주어진 6개월은 짧은 시간이었고, 할당된 FE 개발자는 4명이 전부였습니다. 또한 단순히 이관하고 끝나는 것이 아니라, 우리가 만든 제품이 ‘10년 이상 지속 가능한 제품이 되도록 만들어야 한다’는 목표가 있었기 때문에 부담감도 컸습니다.
장애물
처음에는 한정적인 리소스와 높은 목표로 인해 ‘불가능하지 않을까?’라는 생각이 많이 들곤 했습니다. 당장 개발을 시작하기에도 여러 장애물들이 있다고 생각했습니다. 개발하는 도중 협업을 위한 커뮤니케이션 과정으로 개발 속도는 점점 느려질 것이라고 생각했고, 개발해야 하는 물리적인 양도 굉장히 많았습니다. 개발이 끝나더라도 유지 보수에 들어갈 리소스를 무시할 수 없었습니다.
이런 장애물들과 더불어 프론트엔드 개발자의 개발을 막는 병목 현상이 있습니다. 개발 업무를 시작하려면 먼저 아래와 같은 것들이 갖춰져야 합니다.
- 디자인 프로토타입
- 서버 API
- 요구 사항 분석

프론트엔드 개발자가 온전히 업무를 시작하려면 요구 사항 분석을 바탕으로 한 디자인 프로토타입도 준비되어야 하고, 서버 API도 개발되어야 했습니다. 짧은 프로젝트 기간에도 이런 병목들로 프론트엔드 개발자들이 당장 개발을 시작할 수 없는 환경이었습니다. 위 3가지가 충족되어야 개발 출발점에 설 수 있지만, 완료되기까지 가만히 기다려야 하는 기존 방식으로는 절대 목표를 달성할 수 없다고 판단했습니다.
일하는 방식을 바꿔보자
결국 ‘이런 장애물에 구애받지 않고 목표를 달성하기 위해 일하는 방식 자체를 바꿔볼 수 있지 않을까’라는 생각을 했습니다. 일하는 방식을 바꾼다면 병목으로부터 벗어나 조금 더 효율적으로 일할 수 있을 거라 생각했습니다.
1. ‘디자인 프로토타입’에 의한 병목
먼저 3가지 병목 중 첫 번째 병목, 디자인 프로토타입을 없애야 한다고 생각했습니다. 만약 디자이너들에게 ‘6개월 동안 400개 화면에 대해 최적화된 유저 경험을 고려한 디자인 프로토타입을 만들어주세요’라는 요청을 하면 어떻게 생각할까요? 직접 표현은 하지 않더라도 속으론 이 회사를 떠나야겠다는 생각을 했을 것 같습니다. 즉, 이런 디자인 프로토타입 자체가 우리에겐 병목이고, 업무를 효율적으로 진행하려면 이 병목이 없어야 한다고 생각했습니다.
다른 많은 회사에서도 이런 병목을 줄이기 위해 다양한 방법을 도입하고 있을 거라 생각됩니다. 토스는 ‘토스 디자인 시스템’이라는 디자인 시스템을 활용해서 이미 많은 디자인 작업에 대한 병목을 줄이고 있습니다. 하지만 이 디자인 시스템은 토스 내 모든 계열사에서 사용해야 하고, 계열사들의 모든 요구사항을 반영해야 한다는 목적을 가지고 있습니다. 그렇기 때문에 이 디자인 시스템도 결국 한계가 있다고 생각했습니다.
디자인 시스템
디자인 시스템 자체는 굉장히 많은 장점을 가진 도구라고 생각합니다. 토스 디자인 시스템을 기준으로 보면, 시스템은 아토믹한 형태를 제공하고, 다양한 기능을 조합해서 활용할 수 있습니다. 즉, 유연하고 재사용이 쉬운 도구라는 장점을 가지고 있습니다. 하지만 반복되는 코드가 자주 생기고, 작성하는 사람마다 서로 다른 구현체를 만들어 내는 경우가 생깁니다. 또, 화면을 구성하는 코드가 늘어나면 늘어날수록 구현 의도를 파악하기 어려워지는 비효율이 발생하기도 합니다.

토스 페이먼츠 프로덕트 시스템
이러한 비효율을 극복하기 위해 디자이너분들과 함께 토스 페이먼츠에서 사용할 별도의 개념을 생각했습니다. 온전히 토스 페이먼츠 시스템에 포커스를 맞춰 최적화된 패턴을 정의하고, 이를 기반으로 단일 구현체로 만들기 시작했습니다.
시스템으로 동일한 패턴에 대해서는 동일한 구현체를 제공함으로써 개별 구현을 방지했습니다. 그리고 폐쇄적인 인터페이스를 구현해서 권장하지 않는 패턴과 맞지 않는 사용을 원천적으로 방지했습니다. 또, 단순히 아토믹한 단위에서 벗어나서, 아주 작은 영역부터 스크린이라는 가장 큰 영역까지 넓은 영역에 대응하도록 레이어를 만들었습니다.
물론 디자인 시스템의 장점이 프로덕트 시스템의 단점이 될 수도 있습니다. 상대적으로 유연성이 떨어져서 재사용이 힘들어지고, 기능이 한정적일 수 있습니다. 하지만 이런 단점이 제품을 개발하는데 치명적이지는 않았습니다.

디자인 시스템(Design System) vs. 프로덕트 시스템(Product System)
예를 들어 보겠습니다. 아래는 날짜를 선택하는 DateRangePicker를 각각 두 가지 방법으로 구현한 것입니다. 왼쪽 그림이 디자인 시스템의 구현 결과이고, 오른쪽은 프로덕트 시스템의 구현 결과입니다. 디자인 시스템의 구현체를 보면 굉장히 장황하고 의도가 무엇인지 이해하기 힘들어 보입니다. 반면에 프로덕트 시스템은 굉장히 단순하고, 한눈에 들어오는 인터페이스로 구현되어 쉽게 사용할 수 있습니다.

다른 예시도 보겠습니다. 셀렉트 박스를 구현할 때는 구현하는 사람마다 다르게 구현하는 경우가 쉽게 생깁니다. 어떤 사람은 옵션을 props로 처리하고, 어떤 사람은 합성으로 옵션을 처리하기도 합니다. 이러한 처리 방식은 개인의 취향마다 다를 수 있기 때문에 정답은 없지만, 이렇게 구현 방법이 다르다는 자체는 문제라고 생각됩니다.

이 문제를 해결하기 위해 프로덕트 시스템에서는 단일 구현체를 제공함으로써 여러 명의 사람이 구현을 하더라도 서로 다르게 구현되지 않도록 했습니다. 아래와 같이 일관성 있고 간결한 단일 구현체를 제공하기 때문에 서로 다른 구현체들을 만들어낼 필요가 없습니다.

컴포넌트 조합
다음으로는 더 자세히 들어가, 다중 컴포넌트 조합에 대해 알아보겠습니다. 디자인 시스템에서는 커버하지 않는 다중 컴포넌트 조합을 프로덕트 시스템에서는 자유롭게 구현할 수 있습니다. 아래와 같이 칩과 셀렉트를 조합한 칩필터라는 개념을 만들고 이것에 대한 패턴을 정의하면, 별도의 컴포넌트 구현체로써 기능을 만들 수 있습니다. 이렇게 정의된 구현체를 모든 개발자들이 공통으로 사용하면 생산성이 올라갈 것이라고 생각했습니다.

개발 생산성을 높이기 위한 또 다른 예시도 있습니다. 폼 데이터를 다룰 때는 react-hook-form이라는 라이브러리를 많이 사용하고 있습니다. 새 시스템에서는 아래와 같이 react-hook-form과 토스의 UI 요소들을 결합해 코드의 군더더기들이 생기지 않도록 방지하고 있습니다.

유지보수의 어려움
여러 명의 개발자가 이러한 프로덕트 시스템 없이 각각 화면을 구현하면 천차만별 다른 모습이 나오기 쉽습니다. 이런 화면이 400개 이상이나 있다면, 유지 보수는 당연히 어려워집니다.
아래 코드는 화면이 어떤 역할을 하는지 직관적으로 알기 어렵고, 영역 분리가 적절하게 되어 있지 않습니다. 따라서 오류가 발생했을 때 어느 부분을 보고 원인을 파악해야 하는지 알기 어렵고 시간도 오래 걸립니다. 게다가 화면의 공통 변경 사항이 발생하여 수정하고 반영할 때 여러 번의 수정이 반복되는 불편함도 있었습니다.

이런 어려움을 해결하기 위해, 새로 만든 프로덕트 시스템에 단순히 아토믹한 컴포넌트라는 틀에서 벗어나 다양한 레이어로 구성된 구현체를 만들었습니다. 이때는 아래처럼 섹션이나 스크린과 같은 넓은 단위의 커버리지를 가진 패턴을 만들어서 제공했습니다.

또한 자주 반복되는 패턴이 있다면 이 패턴에 최적화된 별도의 컴포넌트를 제공했습니다. 컴포넌트들은 가볍게 훑어보기만 해도 어떤 역할을 하는 컴포넌트인지 바로 알 수 있습니다. 테이블 목록을 그릴 때 아래 오른쪽 그림과 같이 프로덕트 시스템을 사용하면 컴포넌트의 역할이 무엇인지 명확하게 알 수 있습니다.

첫 번째 진화 : 토스페이먼츠 프로덕트 시스템
내용을 요약하면, 기존 디자인 시스템에서 벗어나 구체적인 맥락이 들어가더라도 아토믹한 구조를 벗어나서 반복 사용하기 좋은 형태의 컴포넌트를 만들었습니다. 서로 다른 인터페이스로 구현하는 상황이 없도록 방지했고, 명확한 역할의 컴포넌트를 정의해서 가독성과 유지 보수가 용이하게 했습니다. 이렇게 개발자가 패턴을 이해하고 있다면, 디자이너의 도움 없이 화면을 구현할 수 있습니다.

여기까지, 디자인 프로토타입에 대한 의존성에서 벗어나기 위해 활용한 방법에 대해 알아봤습니다. 다음은 백엔드 개발에 의한 병목을 해결하는 방법입니다.
2. 백엔드 개발에 의한 병목
프론트엔드 개발자는 백엔드 API가 준비되어야 개발을 시작할 수 있습니다. 그렇다면 반드시 백엔드 API가 완성되어야만 프론트엔드 개발을 시작할 수 있을까요? 그렇지는 않다고 생각합니다.
개발을 시작하는 단계에서는 실제로 서버 API가 구체적인 구현을 했는지 사실 보다는, 프론트엔드에서 요청할 HTTP 요청의 엔드 포인트와 리퀘스트 파라미터, 응답 데이터와 같은 인터페이스가 필요합니다.

팀은 이미 이런 협업을 위해 인터페이스에 대한 정의 문서인 API 스펙 문서를 전달받아 이 문서를 기반으로 개발하고 있습니다. 하지만 이 스펙 문서만으로는 조금 부족하다고 생각했습니다. 스펙 문서를 보며 기능을 구현하는 과정에 비효율이 많이 발생했기 때문입니다.

초기 단계에는 스펙 문서를 확인하고, 스펙을 기반으로 타입 스크립트 코드를 작성하고, Data Fetcher를 통한 메소드를 호출했습니다. 다만 이러면 호출한 데이터에 문제가 있을 때마다 다시 스펙 수정을 기다려야 했고, 스펙 수정이 끝난다 해도 과정을 다시 반복하게 됩니다. 저는 이런 비효율적인 과정을 줄이고자 했습니다.
OpenAPI Code Generator
앞서 설명한 비효율을 줄이기 위해 생각한 방법이 바로 OpenAPI Code Generator입니다. 실제로 활용할 만한 도구들은 많이 있습니다. 단 아래의 요구사항이 충족되어야 했습니다.
- API 클라이언트 생성 없이 스펙 정보만 자동 생성할 수 있는가?
- zod로 요청 파라미터와 응답 파라미터를 파싱 할 수 있는가?
- 상황에 맞게 커스텀 transform 조건을 쉽게 주입할 수 있는가?
- 별도의 학습 비용 없이 사용하거나, zero config로도 원하는 결과를 생성할 수 있는가?
아쉽게도 이 요구사항을 100% 만족하는 오픈소스는 찾지 못했습니다. 그래서 이런 도구를 깊이 분석하기 보다, 요구사항을 완전히 구현할 수 있도록 직접 만들자는 결론을 내렸습니다.
지금까지 프론트엔드 개발자가 만나는 병목에 대한 내용과 ‘디자인 프로토타입에 대한 병목’과 이를 해결한 내용에 대해 알아봤습니다. ‘서버 API에 대한 병목’ 역시 문제점까지 알아봤습니다. 다음 글에서는 Codegen을 직접 구현해 서버 API에 대한 병목을 해결한 방법과 마지막 병목에 대해 알아보겠습니다.
이전 글에서는 토스페이먼츠 프론트엔드 개발자가 레거시 문제를 해결하다 만난 병목, 그 가운데 ‘디자인 프로토타입에 대한 병목’과 이를 해결한 내용, 나아가 ‘서버 API 병목’ 문제에 대해서도 알아봤습니다. 이번 글에서는 이를 해결하고자 Codegen을 구현한 방법과 마지막 병목에 대해 다뤄보겠습니다.
토스 페이먼츠 codegen
서버 API 병목을 해결하기 위해 제가 ‘직접 구현하겠다’고 말했던 OpenAPI Code Generator는 아래와 같은 역할을 해야 합니다.
- JSON 스키마를 원하는 형식 대로 다듬기
- 타입 스크립트, zod 기반 코드로 구현하기
- 파일로 쓰기
이렇게 구현하려는 Codegen에는 리소스(Resource)라는 개념이 있습니다. 여기에서 리소스란 하나의 파일에서 관리 가능한 API 명세의 객체라고 생각하면 됩니다. 이 리소스로 스펙을 관리하도록 계획했습니다. 이를 통해 API에 대한 정보가 소스 코드 곳곳에 퍼지는 현상을 방지하고자 했습니다.
그리고 필요에 따라서 리소스에 정의한 파라미터가 스키마 파서로 처리되어 정합성을 확인하도록 했으며, 리소스 이름을 기준으로 필요한 파라미터와 응답 데이터를 추론하는 기능을 제공했습니다.

이런 도구, 그리고 도구를 만드는 예시는 오픈 소스 생태계에 굉장히 많이 있습니다. 그런 만큼 누구나 도구들을 자유롭게 선택하여 쉽게 만들 수 있다고 생각합니다.
예를 들어 스웨거 처리를 위한 스웨거 파서, JSON 객체를 zod 객체로 변환하는 도구, 템플릿 사용을 위한 핸들바, CLI 처리를 위한 clipanion 등의 도구 등을 잘 조합하면 다양한 방법으로 코드 제네레이터를 만들 수 있습니다.

Codegen 구현하기
Codegen을 구현하는 과정을 자세하게 알아보겠습니다.
스웨거 독스 URL로부터 오픈 API 스펙 객체를 생성하는 것이 시작입니다. 이 스펙의 일부 요소는 spec.components 요소로부터 리퀘스트 바디와 리스폰스에 대한 정보를 가져와서 zod 스키마로 생성합니다. 그리고 spec.paths로부터 API 엔드 포인트를 비롯한 각종 스펙을 원하는 형태의 객체로 가공합니다. 마지막으로 이 결과물을 원하는 위치에 타입 스크립트 파일로 생성하는 작업을 진행합니다.

이때 spec.components란 무엇일까요? spec.components에서는 아래와 같이 스웨거의 스키마들 목록을 볼 수 있습니다. 스웨거에서 openapi.json이라는 json 스키마 객체를 뽑아 오고, 이 객체를 다듬어 자동으로 스키마 파일을 생성해 줍니다.

spec.paths도 마찬가지로 스웨거에서 패스 목록을 확인할 수 있고, 이 데이터를 json으로 파싱하고, 파싱한 결과를 앞서 정의한 리소스(Resource) 형태로 가공해서 생성합니다.

실제로 코드를 실행하면, 커맨드를 입력하자 몇 초 만에 결과물이 생성되는 것을 볼 수 있습니다.

Codegen의 효과
Codegen은 저 혼자 일주일 정도 시간을 사용해 구현했습니다. 생각보다 짧은 시간 안에 구현했지만, 이로 얻은 여러 이점이 있습니다.
우선 CLI로 생성한 api 엔드 포인트는 2,000개가 넘으며, 이 엔드 포인트로부터 생성된 코드 라인 수는 3만 줄 넘는 부분도 있었습니다. 이런 방대한 양의 코드를 단 10여 초 만에 생성할 수 있도록 자동화 시킨 것입니다. 이 결과물들은 당연히 재사용이 가능한 형태로, 무엇보다 스펙을 제대로 옮겨왔는지에 대한 검증을 할 필요 없이 CLI 한 번만 실행하면 바로 동기화되는 자동화 루틴을 얻을 수 있었습니다.
두 번째 진화 : 토스 페이먼츠 Codegen
Codegen을 도입함으로써 스펙 문서를 확인하고, 스펙을 코드로 생성하고, 메소드를 호출해서 문제점이 파악되면 스펙을 수정하는 과정의 반복을 효율적으로 바꿀 수 있었습니다. 즉, 개발자가 API 문서를 보고 한 땀 한 땀 직접 코드를 옮기는 과정이 필요 없어지고, CLI 명령어 한줄만 실행하도록 축소한 것입니다.
또한 API 오류가 발생했을 때, 옮기는 과정의 실수인지, 잘못된 서버 API 구현인지 추적할 필요가 없어졌습니다. 유즈케이스에 맞게 가공할 수 있는 100% 제어 가능한 도구를 얻었으며, API가 반환하는 인터페이스가 바뀌어도 변경 사항을 즉시 싱크할 수 있습니다.
이렇게 지금까지 백엔드 개발에 의한 의존성에서 벗어나는 방법에 대해 알아봤습니다.

3. 프론트엔드 개발의 비효율에 의한 병목
사실 앞서 제시한 세 가지 병목에 따르면, 이번 단락은 ‘제품 요구 사항 분석’에 의한 병목을 알아봐야 하는 것이 맞습니다. 그러나 사실 제품 요구사항에 대한 의존성을 끊어내는 일을 기술적으로 해결하는 것이 쉽지는 않다고 판단했습니다.
그래서 세 번째 주제를 살짝 바꿔봤습니다. 앞선 2가지 병목과 같은 외적인 요소 말고, ‘프론트엔드 개발자의 프론트엔드 개발 자체에 비효율이 있지 않을까?’ 라는 생각으로 시작했습니다.

프론트엔드 개발 생태계에는 너무나 많은 라이브러리나 툴이 있고, 많은 개발자가 이를 활용해 개발합니다. 예를 들어 리액트, Next.js, Vite, Tanstack Query 등이 대표적입니다. 하지만 이 도구들을 활용하는 결과물은 개발자마다 모두 다를 것입니다.
저는 이렇게 서로 다른 결과물을 만들어내는 일 자체가 또 다른 ‘바퀴를 만드는 일’이라고 생각합니다. 즉, 이런 비효율을 반복하지 말고, 모두가 일관성 있고 지속 가능한 코드를 만들어낼 수 있는 기반이 필요하다고 생각했습니다.
토스 페이먼츠 프레임워크: Pramework
이 기반을 만들기 위해, 하나의 방식으로 통일한 프레임워크를 만들어야 한다고 생각했습니다.
이 프레임워크를 만들기 위해서 리파인(Refine)이라는 도구를 활용했으며, 많은 부분에서 아이디어를 얻어 설계했습니다.
리파인은 오픈소스 리액트 어드민 툴입니다. 이 툴에서 제공하는 인터페이스들은 헤드리스한 형식으로 제공되기 때문에 특정 UI와 결합되어 있지 않습니다. 따라서 자유로운 조합을 사용할 수 있습니다. 또한, 리소스라는 개념을 사용해 API 명세를 한 곳에서 객체 형태로 관리할 수 있으며, 필요한 기능들은 프로바이더 패턴을 통해 자유롭게 주입할 수 있습니다.

저는 이러한 리파인의 아이디어를 바탕으로 여러 가지 비효율을 해결해 보고자 했습니다. 프론트엔드에서 어드민 제품을 만들 때 가장 많이 반복되는 비효율은 무엇일까요? 제가 생각한 비효율은 아래와 같습니다.
- 데이터 페칭
- 폼 컨트롤
- 테이블
- 로깅
데이터 페칭
첫 번째로 데이터 페칭부터 살펴보겠습니다. 먼저 API 스펙을 리소스 객체로 분리하고 API에 대한 맥락을 한곳에서 제어할 수 있게 했습니다. 그리고 리소스와 API 클라이언트는 리액트 컨텍스트를 통해서 프로바이더에 주입하고, 빈번한 CRUD 반복과 같은 ‘행위에 대한 훅’을 제공하여 데이터를 불러올 때 목적을 명확하게 파악할 수 있고, 선언적인 방식으로 사용할 수 있게 만들었습니다. 마지막으로 리소스 이름을 기준으로 필요한 요청 파라미터와 반환된 응답을 자동으로 추론할 수 있도록 했습니다.

코드로 좀 더 알아보겠습니다. 아래와 같이 useQuery와 직접 만든 useItem을 비교해 보면, useQuery는 코드가 굉장히 장황하고 API가 어떤 역할을 하는지 알기 어렵습니다. 프론트엔드 개발자가 어떤 파라미터에 어떤 데이터를 넘겨야 하는지 일일이 다 제어해야 합니다.

반면, useItem는 단일 객체를 가져오겠다는 의도가 명확하게 보입니다. 리소스를 주입하면 API 명세를 신경 쓰지 않습니다. 필요한 파라미터 객체만 넣어 보내도 헤더에 필요하다면 헤더에 배치를 하고, 쿼리에 필요하면 쿼리에 배치를 하는 등 내부적으로 필요한 부분에 적절하게 배치해서 처리합니다.
또한 단순히 코드만 간결해지는 것뿐만 아니라, 선언한 리소스 이름에 따라 필요한 파라미터를 자동으로 추론해 줍니다. 반환 값 역시 바로 추론하도록 만들어져있습니다.

폼 컨트롤
다음으로 폼 컨트롤에 대한 개선입니다. 폼 컨트롤 역시 불필요한 비효율을 줄이기 위해 여러 가지 개선을 했습니다.
데이터 페칭과 마찬가지로 리소스에 정의한 API를 호출하고 호출한 결과는 디폴트 밸류에 자동으로 주입되도록 했습니다. 또, 쿼리 파라미터를 자동으로 파싱하여 디폴트 밸류로 주입하는 등 다양한 외부 소스로부터 폼 데이터 주입이 가능합니다. 마지막으로 폼을 제출하거나 초기화할 때 쿼리 파라미터를 자동으로 업데이트하는 기능도 만들었습니다.

이것 역시 코드로 더 알아보겠습니다. useForm이라는 기능은 리소스를 정의하면 폼을 렌더링 할 때 리소스 기반으로 GET 요청을 실행하여 기본 값을 주입합니다. 폼 데이터를 제출할 때도 저장 API를 호출할 수 있도록 submitter라는 메소드를 제공합니다. 이를 통해 원하는 시점에 자유롭게 호출하고 저장할 수 있게 했습니다.

그리고 쿼리 파라미터를 불러와서 파서를 필드 형태로 가공하거나, 제출할 때 필드를 다시 쿼리 파라미터로 변환하는 과정을 단일 구현체로 제공합니다. 따라서 사람마다 서로 다른 쿼리 파라미터에 대한 처리 방식을 고민할 필요가 없습니다.

테이블
세 번째, 테이블에 대한 개선입니다. 테이블도 마찬가지로 리소스에 정의한 API를 호출해서 테이블 형태로 렌더링합니다.
테이블 특성상 트리구조 형태의 코드가 반복되기 쉬운 구조이기 때문에 깊은 depth의 JSX를 반복 작성하는 대신에, 모델이라는 개념을 주입해서 테이블이 렌더링 되도록 했습니다. 그 덕에 테이블 렌더링 코드의 가독성이 훨씬 좋아졌습니다. 마지막으로 테이블에 공통으로 필요한 요구사항인 페이지네이션, 열 번호, 체크 박스와 같은 요소를 공통으로 처리할 수 있게 했습니다.

아래 코드를 보면 반복되는 코드는 공통 옵션을 제공하고, 복잡하고 읽기 어려운 트리 구조에서 벗어나서 테이블이 어떤 역할을 하는지 명확하게 알 수 있습니다.

로깅
마지막으로 로깅에 대한 개선입니다. 로깅 개선은 생각보다 간단합니다.
로그 클라이언트를 리액트 컨텍스트에 주입하고, 어떤 기능을 개발할 때 해당 컴포넌트를 Logger로 래핑 하면 자동으로 로깅을 실행하도록 했습니다. 개발자가 굳이 컴포넌트마다 로깅 처리를 했는지 신경 쓸 필요 없이 래핑만 해주면 알아서 로깅 처리를 할 수 있습니다.

세 번째 진화 : 토스페이먼츠 프레임워크
이렇게 프레임워크로 4가지 비효율을 해결해 봤습니다. 이 프레임워크를 통해 여러 개발자가 서비스를 구현하더라도 일관성 있는 형태로 개발할 수 있게 됐습니다. 그와 함께 유지 보수가 가능한 형태의 서비스를 만들 수 있었습니다.

지금까지 디자인 프로토타입, 백엔드 개발, 프론트엔드 자체라는 3가지 병목을 해결하는 과정에 대해 소개했습니다. 이 노력을 통해 많은 것을 얻게 됐습니다.

시작할 때는 6개월 안에 이 기능을 개발하기 힘들 것이라고 생각했지만, 실제로는 2달 밖에 걸리지 않았습니다. 4개의 어드민과 400페이지 이상의 메뉴를 여러 개발자가 연차 상관없이 동일한 수준의 품질을 가진 코드로 개발할 수 있었습니다. 화면을 구현하는 데 하루 정도 걸리던 작업을 한 시간으로 줄인 경험도 있습니다. 코드들 모두 유지 보수가 쉽고 확장 가능한 구조로 만들어졌습니다.
마치며
지금까지 다양한 비효율을 개선하며 프론트엔드 개발을 효율적으로 바꾼 과정에 대해 알아봤습니다. 제가 소개한 비효율 제거 과정이 유의미한 변화를 가져왔다고 생각하시나요?
여러분들도 업무를 진행하면서 비효율적인 순간을 목격한다면 무시하지 말고, 작은 부분부터 차근차근 변화를 시도해 보면 좋겠습니다. 이런 작은 변화들이 모여 제가 경험한 유의미한 변화와 같은 값진 순간으로 돌아올 것이라고 생각합니다. 감사합니다.