새로운 기술 도입의 어려움 - ts-rest 실패기

새로운 기술을 도입하는 건 언제나 도박이다. 특히 팀 전체가 영향을 받는 기술이라면 더욱 그렇다. ts-rest 도입 과정에서 겪은 실패를 통해 배운 것들을 정리해본다.

문제의 시작

우리 팀은 TypeScript를 사용하면서도 API 경계에서는 타입 안전성을 포기하고 있었다.

// 프론트엔드가 기대하는 타입
interface User {
  id: number;
  name: string;
  email: string;
}

// 실제 서버 응답 (email이 userEmail로 변경됨)
{
  "id": 1,
  "name": "John",
  "userEmail": "john@example.com"  // 💥 런타임 에러
}

배포 후에야 발견되는 이런 문제들이 반복됐다. 뭔가 해결책이 필요했다.

왜 ts-rest였나

여러 대안을 검토했다. tRPC는 REST와 거리가 멀었고, GraphQL은 러닝커브가 컸다. OpenAPI Generator는 계약 우선 개발이 어려웠다.

ts-rest는 기존 REST API 구조를 유지하면서도 타입 안전성을 확보할 수 있었다. 중간 다리 역할을 할 수 있을 거라 생각했다.

// 계약을 한 번 정의하면
const contract = c.router({
  getUser: {
    method: 'GET',
    path: '/users/:id',
    responses: {
      200: UserSchema,
    },
  },
});

// 서버와 클라이언트 모두 타입 안전

제안과 동의

팀에 제안했다. 반응은 긍정적이었다.

점진적 도입 계획을 세웠다. 내가 담당하는 API부터 적용하고, 기존 API는 천천히 마이그레이션하기로 했다. 모두가 동의했다.

그러나 현실은

실제 적용 단계에서 문제가 드러났다.

당장 처리해야 할 업무들이 있었고, ts-rest 학습은 계속 미뤄졌다. 계약을 수정하면 에러가 빵빵 터졌는데, 이게 장점임을 알면서도 팀원들에게는 부담이었다.

결국 서버의 일부, 클라이언트의 극히 일부에만 적용됐다. 두 가지 방식이 공존하는 애매한 상태가 되었다.

무엇이 잘못됐나

혼자 시작했다

서버와 클라이언트를 함께 전환해야 가치가 나오는데, 처음부터 함께할 동료를 확보하지 못했다.

“제가 먼저 확인해볼 테니까 관심 있으신 분은 말씀주세요”

바쁜 와중에 누가 선뜻 나서겠는가. 시작부터 최소 팀(이 경우에는 서버 1명, 클라이언트 1명)을 구성했어야 했다.

MVP의 실패

점진적 도입도 최소 가치는 있어야 한다. 서버에서만 사용하는 건 기존 NestJS 데코레이터를 다른 방식으로 표현하는 것에 불과했다.

// 기존 NestJS
@Get('/users/:id')
async getUser(@Param('id') id: string) { ... }

// ts-rest (서버만 사용)
// 이게 뭐가 더 나은가?
contract.getUser: {
  method: 'GET',
  path: '/users/:id',
  ...
}

진짜 가치는 클라이언트에서 타입 안전하게 호출할 때 발생하는데, 그 부분이 빠졌다.

신뢰 자본의 부족

향로님의 블로그를 읽고 깨달았다. 내게 부족했던 건 ‘신뢰 자본’이었다.

기술의 장점만 설명했을 뿐, 실제로 이 도구가 팀원들의 일상적인 고통을 어떻게 해결해줄지 보여주지 못했다. “타입 안전성”보다 “어제 그 버그를 막을 수 있었다”는 구체적인 증명이 필요했다.

학습 시간의 환상

“바쁜 시간이 지나면 배우자”

바쁜 시간은 끝나지 않는다. 처음부터 학습 시간을 공식적으로 확보했어야 했다.

배운 것들

MVP 단위로 도입하자

  • 하나의 API라도 서버-클라이언트 전체를 전환해야 한다
  • 부분적 적용은 가치를 만들지 못한다

처음부터 팀을 구성하자

  • 혼자서는 실험, 둘 이상이어야 도입이다
  • 최소한 서버 1명, 클라이언트 1명은 필요하다

구체적인 문제를 해결하자

  • “이론적으로 좋다” < “이 버그를 막을 수 있다”
  • 실제 고통을 해결하는 사례가 필요하다

학습 시간을 확보하자

  • 나중은 오지 않는다
  • 공식적인 학습 시간이 필요하다

아이러니

내게는 ts-rest를 사용한 API들이 테스트하기 쉽고 안정적인 코드다.

하지만 팀원들에게는 통일성 없이 작성된, 수정하기 어려운 코드일 뿐이다. 내가 만든 ‘좋은 코드’가 팀에게는 새로운 레거시가 되어버렸다.

지금도 여전히

ts-rest는 일부에만 적용되어 있으며 기존 API들은 변경되지 않았다.

우리 팀은 여전히 클라이언트-서버 간 타입 불일치로 고통받고 있다. API 인터페이스 변경은 두려운 작업이다.

점진적 접근이 맞았는지, 더 과감하게 뛰어들었어야 했는지는 아직도 모르겠다. 신중한 접근이었지만, 결과는 어정쩡했다.

결론

이 실패에서 배운 가장 큰 교훈:

나에게 좋은 코드가 팀에게도 좋은 코드는 아니다.

기술 도입은 기술만의 문제가 아니다. 팀이 함께 이해하고, 함께 유지보수할 수 있어야 진짜 좋은 코드다.

새로운 기술을 도입하려는 누군가에게.

  • 혼자만의 ‘좋은 코드’를 만들지 마라
  • 처음부터 동료를 확보하라
  • 최소한의 가치를 증명하라
  • 팀 모두가 이해할 수 있는 방식으로 시작하라

참고


박대성
Written by@박대성

독서와 지식관리에 관심이 많은 개발자

GitHub