
너 토스가 만든라이브러리 봤어? 그거 요즘 인기있대 !
어느날 친구가 나에게 해줬던 말이다.
나는 그 말을 듣고 콧방귀를 뀌며 겉으로는 “그래 ? 잘 만들었나 보지” 라고 답했다.
사실 내심 “뭐 얼마나 잘 만들었겠어.. 그냥 반짝 인기 얻은거겠지” 라는 생각이 마음깊은곳에 있었다.
시간이 지난 지금 보란듯이 토스 es-toolkit은 1만 스타와 주간 다운로드수가 3000만회가 넘는 세계적인 라이브러리에 진입했다.
내 자신이 틀렸다는걸 인정해야했다.
동시에 es-toolkit이 어려운 오픈소스 시장에서 어떻게 성공했는지 궁금해졌다.
“es-toolkit은 어떻게 세계적인 라이브러리가 될 수 있었을까 ?”
JS 유틸리티 관련 라이브러리에는 Lodash라는 큰 거인이 존재한다.
이미 뿌리깊게 내린 시장에 토스는 도전장을 내밀었다.
"Lodash" 말고 "es-toolkit" 사용해 보는거 어때요 ?
사용자들은 반대로 토스에게 묻는다.
우리가 왜 전통적이고 안정화된 "Lodash" 말고 "es-toolkit" 을 사용해야 하는데요 ?
답은 간단했다.
"우리가 더 좋으니까요. 훨씬 가볍고 더 빠르거든요 !"
토스는 성장하지 않는 Lodash에 존재하는 빈틈을 파고들기 시작했다.
"배열에서 무작위 요소를 선택하는 기능이 필요하네.."
import { sample } from 'lodash';
간단한 문제를 해결하기 위해 Lodash의 sample이라는 기능 하나를 가져와 사용했다.
"Lodash를 사용하면 쉽게 해결할 수 있구나 !"
개발자는 기뻐하지만 이 기쁨은 얼마가지 않는다.
배포한뒤에 네트워크 탭을 확인해보니 번들 사이즈가 이상하게 부풀려져 있었다.

단순히 배열에서 무작위 요소를 선택하는 기능을 사용했을 뿐인데 큰 무게를 짊어져야 했다.
이 무게는 네트워크 상황에 따라 유의미하게 번질 영향이 있었다.
"무슨 문제 때문일까 ?"
확인해보니 놀랍게도 나는 sample만 사용했을 뿐인데 Lodash가 제공하는 200개가 넘는 모든 기능들을 가져왔다.
Lodash의 가장 큰 빈틈은 비효율적이고 무거운 "번들 사이즈" 였다.
"이 문제를 해결할 수 있을까 ?"
해결 방법은 생각보다 간단했다.
"우리가 sample 이라는 기능을 가져올때 sample 사용에 필요한 코드만 라이브러리로부터 가져오면 되는거 아닐까 ?"
CJS 기반인 Lodash는 트리셰이킹이 적용되지 않았다.
현대적인 ESM으로 전환하기만 하면 이 문제를 해결할 수 있었다.
단순히 "CJS를 ESM으로 전환", 즉 "현대화" 하는것만으로도 es-toolkit은 Lodash보다 훨씬 나은 번들 사이즈를 가질 수 있었다.
사실 "현대화하라" 섹션에는 몇가지 오류가 존재한다.
Lodash에도 "lodash-es"라는 ESM 기반의 Lodash를 제공하기 때문이다.
그러면 es-toolkit이 해결한 "현대화" 만으로는 차별점을 주지 못할까 ?
놀랍게도 es-toolkit은 ESM인 lodash와 비교해도 번들 사이즈 측면에서 엄청 큰 차이를 만들어냈다.

"es-toolkit은 어떻게 번들 사이즈를 줄일 수 있었을까 ?"
Lodash는 정말정말정말 다양한 방어로직이 존재한다.
// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
function arrayLikeKeys(value, inherited) {
var isArr = isArray(value),
isArg = !isArr && isArguments(value),
isBuff = !isArr && !isArg && isBuffer(value),
isType = !isArr && !isArg && !isBuff && isTypedArray(value),
...
for (var key in value) {
if ((inherited || hasOwnProperty.call(value, key)) &&
!(skipIndexes && (
key == 'length' || // Safari 9 버그 우회
(isBuff && (key == 'offset' || key == 'parent')) || // Node 0.10 버그 우회
(isType && (key == 'buffer' || ...)) // PhantomJS 2 버그 우회
---
for (var key in value) {
// hasOwnProperty로 "이 프로퍼티가 진짜 이 객체의 프로퍼티인가?" 매번 확인한다.
if ((inherited || hasOwnProperty.call(value, key)) && ...) {
}
}
---
// Array.prototype.map 을 사용하지 않고 직접 구현해서 쓴다.
function arrayMap(array, iteratee) {
var index = -1,
length = array == null ? 0 : array.length,
result = Array(length);
while (++index < length) {
result[index] = iteratee(array[index], index, array);
}
return result;
}
배열, 유사배열, 버퍼, Arguments 등등 다양한 타입대응.
브라우저별 런타임별 대응로직.
Prototype을 건드리는 상황 대응로직.
내장 객체가 제공하는 Prototype 메서드를 사용하지 않고 직접 구현 등등
비효율적인 코드가 너무나도 많다.
즉, Lodash는 앞에서도 설명했듯이 굉장한 엣지케이스들에 많은 비용을 내고 있다.
es-toolkit은 과감하게 "얻는것 대비 비용이 큰" 비효율적인 부분들을 전부 제거했다.
비효율적인 부분을 제거하고도 es-toolkit은 더 과감한 판단을 한다.
sample([1, 2, 3, 4, 5]);
sample({ a: 1, b: 2, c: 3 });
sample('hello');
위와 같이 Lodash는 한 기능에 여러 종류의 자료형들을 사용할 수 있다.
단순히 보면 편리해 보이지만 sample의 내부로직을 생각하면 이 편리함은 공짜가 아니다.
"전달된 게 배열인가, 객체인가, 문자열인가?"
함수는 실행될 때마다 타입을 확인하고, 객체라면 값 배열로 바꾸고, 문자열이라면 이터러블로 펼치는 분기를 품어야 한다.
정작 배열 하나만 넘기는 대부분의 개발자도 이 모든 분기 코드를 번들에 짊어지게 된다.
"이 변환을 라이브러리가 제공해야 할까, 아니면 호출하는 쪽이 명시하면 될까?"
sample([1, 2, 3, 4, 5]);
sample(Object.values({ a: 1, b: 2, c: 3 }));
sample([...'hello']);
객체를 기반으로 사용하고 싶으면 Object.values를 사용하면되고 문자열에서 사용하고 싶으면 스프레드 연산을 사용하면 된다.
이미 언어에서 제공되는 기능을 라이브러리가 품을 필요 없다.
물론 이 부분은 트레이드오프일 수 있다.
문자열, 객체와 같은 대상을 다루는 경우가 많으면 많을수록 호출부가 지저분해질 것이다.
하지만 es-toolkit은 이 대가를 치루기로 결심했다.
제거하고 덜어냄으로써 가벼움과 속도를 택했다.
es-toolkit은 Lodash의 비효율적인 "방어로직" 을 제거했다.
여기서 "방어로직을 제거했으니까 es-toolkit은 안전하지 않은거 아닌가요?" 라는 생각이 들 수 있다.
타당한 걱정이다.
Lodash가 그 수많은 검사 코드를 괜히 넣은 건 아닐것이기 때문이다.
잘못된 값이 들어왔을 때 예상하지 못한 동작을 하지 않도록 지켜주는 안전장치였다.
es-toolkit또한 이 부분들을 고려했고 이 안전장치를 "타입" 이라는 방식으로 두었다.
export function sample<T>(arr: readonly T[]): T {
const randomIndex = Math.floor(Math.random() * arr.length);
return arr[randomIndex];
}
es-toolkit의 sample 코드는 이게 전부다.
타입이 입구를 지켜주기 때문에 함수 안에서 방어로직을 둘 필요가 없다.
Lodash에서는 "배열이 아니면 어떡하죠?" 라는 질문을 10줄이 넘는 런타임 코드로 해결했다.
그리고 그 코드들은 번들에 실려 사용자의 브라우저까지 전달되었다.
타입은 다르다.
타입은 컴파일이 끝나는 순간 사라진다.
즉, es-toolkit은 Lodash에서 안전을 지키는 코드를 번들에서 완전히 걷어냄과 동시에 다른 방식으로 지켜냈다.
토스는 Lodash보다 훨씬 가볍고 빠른 유틸리티 라이브러리를 만들었다.
하지만 아무리 좋은 제품을 만들었다고 할지라도 사용자를 끌어들일 수 있는건 아니다.
"저희거 정말 좋은데 써보는거 어때요 ?"
"좋네요! 근데 지금 당장 저희 프로젝트에 도입하기는 어려울거 같아요.. Lodash에서 이주하기 어렵거든요"
결국 Lodash라는 거인을 쓰러트리기 위해서는 이미 뿌리깊게 내린 Lodash 사용자들을 es-toolkit으로 끌어들여야 했다.

그렇게 es-toolkit의 compat이 탄생하게 되었다.
compat은 compatibility의 줄임말로 Lodash와 완전히 호환되면서 es-toolkit의 장점인 번들 사이즈, 속도적인 이점을 가져갈 수 있었다.
기존 es-toolkit과 Lodash 사이에 중간 레이어가 하나 추가된것과 같다.
사용자를 끌어들일 수 있는 핵심인 이주를 도움으로써 자연스럽게 es-toolkit으로 물들였다.
사실 토스출신도 아닌 내가 이런 글을 작성하고 있는게 묘하다.
하지만 확실히 토스가 es-toolkit을 오픈소스라는 시장에 내세우고 성공에 이르기까지의 전략은 배울점이 많았다.
결국 라이브러리도 하나의 프로덕트이다.
단지 DX를 목표로 하고 타겟이 개발자일 뿐이다.
이미 시장에서 뿌리깊게 자리잡은 Lodash를 상대했다.
마치 삼성전자에게 핸드폰으로 도전장을 내민것과 같다.
이 도전은 누군가가 보기에 "바보같은 도전" 이라고 생각할만하다.
아마 과거에 나도 "도대체 왜 ? 이미 성공한 Lodash가 있는데 거기에 도전을해?" 라고 답했을 것이다.
하지만 돌아보니 es-toolkit은 성공할 이유가 충분했다.
더 이상 성장하지 않는 Lodash로 인해 불만을 느끼고 귀기울여 들었다.
"사람들은 큰 번들사이즈와 비효율적인 Lodash가 싫어도 본인이 새롭게 만들기는 어렵기 때문에 어쩔 수 없이 사용할 것이다"
이 가정은 옳았다.
그렇게 es-toolkit은 "현대, 효율적" 이라는 키워드로 "속도, 가벼움" 을 내세워 차별화 했다.
하지만 좋은 제품은 도전의 자격을 줄 뿐, 승리를 주지는 않았다.
es-toolkit은 이주라는 다리를 놓아, 거인 위에 서 있던 사용자들이 건너올 길을 열었다.
거인을 무너뜨리는 방법은 정면으로 미는 것이 아니라, 그 위의 사람들을 하나씩 초대하는 것이었다.
es-toolkit은 여전히 성장중이다.
더 많은 유틸리티를 만들고 함수형, 타입 유틸리티같은 새로운 영역에 발을 내밀고 있다.
토스는 지금도, 성장을 멈춘 거인을 천천히 쓰러트리고 있다.
저도 최근에 es-toolkit을 사용해봤는데, 번들 크기가 작고 성능도 좋아서 기존 Lodash를 대체할 만하다고 느꼈습니다. 특히 최신 JavaScript 환경에서는 꽤 매력적인 선택인 것 같아요. 게임 관련 웹 서비스를 운영하면서도 성능 최적화와 빠른 로딩 속도가 정말 중요하다는 걸 느끼는데, 참고로 저는 https://bloxxfruits.com/ 같은 프로젝트에서도 이런 경량 라이브러리를 선호하고 있습니다. 유지보수성과 성능을 함께 잡을 수 있다는 점이 큰 장점인 것 같습니다.
정말 흥미로운 분석이네요. 개인적으로도 es-toolkit이 성공한 가장 큰 이유는 단순히 "새로운 라이브러리"라서가 아니라, 기존 Lodash의 무게와 번들 크기 같은 문제를 명확하게 해결했다는 점이라고 생각합니다. 성능, 트리 셰이킹, TypeScript 지원까지 현대적인 개발 환경에 맞춰 최적화한 전략이 제대로 통했던 것 같습니다.
개발도 결국 문제를 더 효율적으로 해결하는 과정이라는 점에서, 퍼즐을 최적의 방법으로 푸는 것도 비슷한 접근이라고 생각합니다. 저는 어려운 퍼즐을 풀 때 https://smartblockblastsolver.com/ Block Blast Optimizer 같은 도구를 활용해 다양한 수를 분석해보곤 하는데, "더 나은 해결책"을 찾는 과정이라는 점에서 비슷한 재미가 있더라고요.
알잘딱한 문제 의식과 해결책이네요... 덕분에 몰랐던 토스의 오픈소스에 대해 알게 되었습니다. 이런 이야기들을 들을 때마다 개발자 입장에서 '토스답다'가 점점 긍정적인 의미로 느껴지는 것 같습니다!