경력·사례·글은 한국어 원문으로 제공됩니다.

← 모든 에피소드

통합 인증 이후 · 회사 업무 · 인계와 공통화

공통 코드를 한 저장소로 모은 일

버전과 API 규약을 함께 맞추려고 서비스와 공통 코드를 한 저장소에 모았다. 함께 바꾸기 편해진 만큼, 따로 움직일 때의 비용도 생겼다.

그때
프로젝트마다 버전과 API 규약을 따로 맞춰야 했다.
지금
함께 관리할 코드와 서비스별로 바꿀 부분의 경계를 돌아본다.

맞춰야 할 것이 프로젝트마다 달랐다

회사에서 여러 내부 프로젝트를 동시에 개발했다. 비슷한 기능은 늘어나는데 사람은 줄었고, 나중에는 둘이서 여러 서비스를 다루게 됐다. 각 프로젝트의 React와 React Native 버전이 달랐다. 서버와 클라이언트가 함께 쓸 로직, 주고받을 값의 규약, API 문서도 따로 맞춰야 했다.

공통 모듈이 없었던 것은 아니다. 처음에는 서비스와 떨어진 곳에서 관리했다. 하지만 공통 모듈을 고치는 일과 그 모듈을 쓰는 서비스를 확인하는 일이 나뉘어 있었다. 적은 인원으로 여러 프로젝트를 계속 바꾸려면 함께 볼 수 있는 구조가 필요했다.

여러 번 시도하면서 서비스와 공통 코드를 한 저장소 안에 두는 모노레포를 쓰게 됐다. 라이브러리의 버전을 같이 올리고, 사용하는 서비스의 코드와 테스트도 한곳에서 확인하려는 선택이었다.

한 저장소 안에서도 앱과 패키지는 나뉜다

모노레포는 여러 프로젝트의 코드를 한 저장소에서 관리하는 방식이다. 앱과 공통 패키지를 한 파일로 섞는다는 뜻은 아니다. 앱은 앱대로 두고, 여러 앱에서 쓰는 코드는 별도 패키지로 나눌 수 있다.

workspace는 이 안에 어떤 프로젝트가 있고 서로 무엇을 사용하는지 패키지 관리 도구에 알려주는 구성이다. 예를 들어 앱 A가 같은 저장소의 공통 패키지를 사용한다고 적어두면, 도구가 두 프로젝트를 연결해 다룬다. pnpm의 workspace: 표기는 외부에 배포된 동명 패키지 대신 저장소 안의 패키지를 사용하도록 지정하는 방법이다. pnpm workspace 문서

API 규약을 예로 들면 조금 더 분명하다. 서버가 어떤 입력을 받고 어떤 값을 돌려주는지 바꿀 때, 그 값을 쓰는 화면도 함께 살펴봐야 한다. 목록 API의 items라는 이름을 results로 바꾼다고 가정해보자. 서버만 바꾸면 이전 이름을 읽는 화면은 기대한 값을 받지 못한다. 설명을 위해 만든 예지만, 함께 바꿀 코드가 있다는 점은 같다.

한 저장소에 두면 규약을 정의한 곳과 사용하는 곳을 같은 변경 안에서 살펴보기 수월하다. 실제로 공통 코드를 고친 뒤 쓰는 쪽을 함께 확인하고, 여러 서비스의 버전을 같이 올릴 수 있었다. 다만 같은 저장소에 들어 있다는 사실만으로 모든 사용처가 자동으로 고쳐지거나 검증되는 것은 아니다. 어떤 코드가 공통 패키지에 기대고 있는지, 무엇을 확인해야 하는지는 여전히 알아야 한다.

같이 바꾸기 쉬워진 만큼 따로 움직이기 어려웠다

급하게 서비스 하나만 배포하려 할 때는 불편했다. 공통 코드와 버전을 함께 관리하는 편리함이, 특정 서비스만 다른 방향으로 바꿀 때는 부담이 됐다. 처음에는 같아 보였던 기능이 시간이 지나면서 더 이상 공통이라고 부르기 애매해지는 경우도 있었다.

이 경험을 모노레포에서는 반드시 모든 서비스를 함께 배포해야 한다는 뜻으로 받아들이지는 않는다. 저장소를 함께 쓰는 것, 패키지의 버전을 정하는 것, 서비스를 배포하는 것은 각각 결정할 일이다. 당시에는 이 일들을 함께 관리하는 과정에서 비용을 치렀다.

지금 다시 배포 흐름을 살펴본다면, 바뀐 패키지와 그 패키지를 사용하는 프로젝트를 먼저 골라 확인하는 방법을 검토할 수 있겠다. pnpm의 필터는 이름뿐 아니라 패키지의 의존 관계를 기준으로 실행 대상을 고를 수 있다. 이를테면 공통 패키지가 바뀌었을 때 그 패키지에 의존하는 프로젝트까지 검사하는 식이다. pnpm 필터 문서

그렇다고 검사 대상을 골랐다는 이유만으로 독립 배포가 안전해지는 것은 아니다. 서버와 앱이 서로 다른 시점에 바뀌어도 통신할 수 있는지 따로 봐야 한다. 의존 관계를 빠뜨리면 필요한 검사도 빠질 수 있다. 이 기능을 현재의 배포 절차에 적용해 효과를 확인한 것은 아니며, 그때 함께 묶였던 일을 어디서 나눌 수 있을지 보는 방법 중 하나다.

같은 자바스크립트라도 불러오는 방식이 달랐다

ESM과 CJS의 차이로 미묘하게 동작하지 않는 일도 있었다. 둘 다 자바스크립트 코드를 모듈로 나눠 불러오는 방식이다. ESM에서는 주로 import와 export를, CJS에서는 require와 module.exports를 쓴다. 문법만 다른 것이 아니라 코드를 해석하고 불러오는 규칙에도 차이가 있다.

Node.js에서는 파일 확장자와 package.json의 type 같은 설정이 해석 방식에 관여한다. 패키지가 어떤 파일을 외부에 제공하는지도 확인해야 한다. 그래서 같은 함수를 불러오려 해도 라이브러리가 제공하는 형식과 사용하는 쪽의 설정이 맞는지 살펴야 한다. Node.js 패키지 문서

공통 코드로 바꾸기 위한 수동 작업도 있었고, 서로 다른 프레임워크를 맞추는 비용도 있었다. 지금 생각하면 몇몇 라이브러리를 억지로 호환되게 만드는 데 시간을 쓰기보다, 필요한 환경을 지원하는 다른 라이브러리를 골랐으면 어땠을까 싶다.

다시 선택해도 모노레포를 썼을 것 같지만, 이번에는 어디까지 같이 바꿔도 되는지부터 더 자세히 보고 싶다. 한곳에 모으는 일은 한 번으로 끝날 수 있어도, 서비스마다 달라지는 요구는 계속 생기기 때문이다.

이 글의 연결

환경변수 한 줄을 빼고 배포한 일환경변수 한 줄을 빼고 …프로젝트마다 만들던 기능을 함께 쓰기까지프로젝트마다 만들던 기능…

댓글

댓글은 GitHub 토론을 사용하며, 열면 GitHub 로그인이 필요합니다. 고객·사람이 드러나는 정보는 남기지 말아 주세요.

작성 2026-10-05 · 수정 2026-10-06

기억에 의존한 회고이며 당시 문서로 확인하지 못했다.