- 들어가며 — 목록이 아니라 패턴을 보려는 글
- 교체를 만드는 다섯 가지 힘
- 우리 스택이 그 궤적 위에 있는지 확인하는 법
- 옮길 것인가, 기다릴 것인가
- 마지막으로 — 목록을 다루는 태도
- 함께 읽기
- 시리즈
들어가며 — 목록이 아니라 패턴을 보려는 글
앞선 세 편에서 서른 개 남짓한 프로젝트를 봤습니다. 목록 자체는 시간이 지나면 낡습니다. 남는 것은 패턴입니다. 무엇이 교체를 만들었고, 우리 스택에서 같은 징후를 어떻게 알아볼 것인가.
한 가지 전제를 다시 적습니다. 자리를 내준 프로젝트 대부분은 자기 시대에는 옳은 선택이었습니다. 아래의 힘은 대체로 프로젝트의 품질과 무관하게 작동합니다.
교체를 만드는 다섯 가지 힘
1. 플랫폼이 기능을 흡수한다
가장 흔합니다. PhantomJS는 브라우저가 헤드리스 모드를 내장하면서, jQuery Mobile은 CSS와 터치 이벤트가 표준이 되면서 자리를 내주었습니다.
이건 실패가 아닙니다. 도구가 수요를 증명하면 플랫폼이 그 수요를 가져갑니다. 어떤 도구가 채우고 있는 구멍이 언젠가 표준으로 메워질 성질의 것인지 물어보면, 그 도구의 남은 수명을 대략 가늠할 수 있습니다.
2. 운영 부담이 이득을 넘어선다
Kafka가 ZooKeeper 의존을 걷어낸 것이 대표적입니다. 검증된 외부 시스템을 빌려 쓰는 초기 판단은 합리적이었지만, 별도 앙상블을 운영하는 비용이 계속 발생했습니다.
여기서 볼 신호는 명확합니다. 본체가 아니라 부속을 운영하는 데 드는 시간의 비율입니다. 이 비율이 커지면 어느 시점에 본체가 그 기능을 흡수하거나 걷어냅니다.
3. 유지보수 인력이 병목이 된다
이 시리즈에서 가장 반복된 이유입니다. LibSass 폐기 공지는 언어 진화를 따라갈 엔지니어링 여력이 없다고 솔직히 적었고, ingress-nginx 은퇴 공지는 수년간 한두 명이 여가 시간에 유지해 왔다고 밝혔습니다. Create React App 폐기 공지도 활성 메인테이너가 없다는 점을 들었습니다.
의존성의 위험은 코드 품질이 아니라 그 코드를 유지할 수 있는 사람의 수에 있습니다. 그리고 이 수는 스타 수나 다운로드 수로는 전혀 보이지 않습니다.
4. 라이선스가 바뀐다
기술적 이유가 전혀 없는데도 이동이 발생하는 유일한 축입니다. 사실만 정리하면 이렇습니다.
| 프로젝트 | 변경 전 | 변경 후 | 발표일 |
|---|---|---|---|
| MongoDB | AGPL v3 | SSPL | 2018-10-16 |
| Elasticsearch, Kibana | Apache 2.0 | SSPL과 Elastic License 이중 | 2021-01-14 |
| HashiCorp 제품군 | MPL 2.0 | BUSL 1.1 | 2023-08-10 |
| Redis | BSD 3-Clause | RSALv2와 SSPLv1 이중 | 2024-03-20 |
이후의 되돌림도 함께 봐야 균형이 맞습니다. Elastic은 2024년 8월 29일 AGPL을 선택지로 추가하면서 기존 라이선스를 없애는 것이 아니라 하나를 더하는 것이라고 밝혔고, Redis는 2025년 5월 1일 Redis 8부터 AGPLv3를 추가했습니다.
그리고 각 변경에서 생긴 포크입니다.
| 포크 | 원본 | 라이선스 | 관리 |
|---|---|---|---|
| OpenSearch | Elasticsearch, Kibana | Apache 2.0 | 리눅스 재단 |
| OpenTofu | Terraform | MPL 2.0 | 리눅스 재단 |
| Valkey | Redis | BSD | 리눅스 재단 |
세 포크 모두 재단으로 갔다는 점이 이 흐름의 핵심입니다. 라이선스 변경에 대한 시장의 대응은 다른 회사로 옮기는 것이 아니라 중립적 관리 주체를 만드는 것이었습니다.
여기서 동기를 추측하지는 않겠습니다. 각 회사는 자기 발표문에서 이유를 밝혔고, 그것을 그대로 읽는 것으로 충분합니다. 실무자에게 중요한 것은 하나입니다. 라이선스는 변할 수 있는 값이며, 대개 소급 적용되지 않습니다.
5. 문제 정의가 이동한다
TSLint는 잘 만든 린터였지만, 파서를 교체 가능하게 설계한 쪽이 더 오래 갔습니다. rkt는 대안 런타임이었지만, 결과적으로 남은 것은 특정 런타임이 아니라 표준 인터페이스였습니다.
개별 구현으로 푼 문제는 인터페이스로 푼 쪽에 밀리는 경향이 있습니다. 어떤 도구를 고를 때 그 도구가 인터페이스를 정의하는 쪽인지 특정 구현인지 구분해 두면, 5년 뒤 어느 쪽이 남을지 짐작하기 쉬워집니다.
우리 스택이 그 궤적 위에 있는지 확인하는 법
인상이 아니라 확인 가능한 신호로 봅니다.
문서로 확인할 것
- 공식 문서에 지원 종료 일정이 있는가. 언어와 프레임워크는 대개 있습니다.
- README나 사이트에 폐기 문구가 있는가. Bower와 Moment.js는 저장소가 살아 있는 채로 새 사용을 권하지 않는다고 적어 두었습니다.
- 후속 프로젝트가 지정되어 있는가. Rome은 Biome을, Heapster는 metrics-server를 명시했습니다.
- 에스컬레이션 경로가 문서화되어 있고 최근에 실제로 작동한 적이 있는가.
명령으로 확인할 것
저장소의 보관 여부와 마지막 푸시 시각은 API 한 번으로 확인됩니다.
# 보관 여부와 마지막 푸시 시각
OWNER_REPO="kubernetes/ingress-nginx"
curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${OWNER_REPO}" \
| python3 -c "
import sys, json
d = json.load(sys.stdin)
print('archived :', d['archived'])
print('pushed_at :', d['pushed_at'])
print('open_issues:', d['open_issues_count'])
"
더 중요한 것은 병합 권한이 몇 명에게 있는지입니다. 기여자가 많아도 병합자가 소수라면 그 소수가 곧 버스 팩터입니다.
# 최근 100개 병합 PR의 병합자 분포
curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${OWNER_REPO}/pulls?state=closed&per_page=100" \
| python3 -c "
import sys, json, collections
prs = json.load(sys.stdin)
c = collections.Counter(p['merged_by']['login'] for p in prs if p.get('merged_by'))
for who, n in c.most_common(10):
print(f'{n:4d} {who}')
print('distinct mergers:', len(c))
"
목록으로 관리할 것
핵심 의존성마다 네 값을 적어 두는 것만으로 대부분의 사고를 막을 수 있습니다. 공식 지원 종료일, 저장소 보관 여부, 지정된 후속 프로젝트, 그리고 지금 옮긴다면 드는 비용의 대략적 추정치입니다.
마지막 값이 핵심입니다. 도입 시점에 이탈 비용을 한 번 추정해 두면, 나중에 옮길지 말지를 감정이 아니라 숫자로 결정할 수 있습니다.
옮길 것인가, 기다릴 것인가
모든 폐기 공지가 즉시 행동을 요구하지는 않습니다. 두 축으로 나눠 보면 판단이 쉬워집니다.
첫째 축은 보안 노출입니다. 보안 수정이 더 이상 나오지 않고 그 구성 요소가 네트워크에 노출되어 있다면 최우선입니다. ingress-nginx 관련 성명이 은퇴 이후 계속 사용하는 선택은 사용자를 공격에 노출시킨다고 명확히 경고한 것이 이 경우입니다. 반대로 CI 안에서만 도는 빌드 도구라면 같은 폐기 공지도 긴급도가 다릅니다.
둘째 축은 대체재의 성숙도입니다. 후속 프로젝트가 공식으로 지정되어 있고 마이그레이션 문서가 있으면 비용이 예측 가능합니다. 반대로 대체재가 여럿이고 어느 쪽도 지배적이지 않다면, 한 번 더 기다리는 선택이 합리적일 수 있습니다.
두 축을 곱하면 이렇게 됩니다.
- 노출 높음 + 대체재 명확 → 지금 옮깁니다.
- 노출 높음 + 대체재 불명확 → 노출부터 줄입니다. 격리, 접근 제한, 상용 연장 지원 검토.
- 노출 낮음 + 대체재 명확 → 다음 큰 작업에 묶어 처리합니다.
- 노출 낮음 + 대체재 불명확 → 목록에 올려 두고 분기마다 상태만 확인합니다.
그리고 잊기 쉬운 선택지 하나. 유지보수되는 도구를 굳이 교체하는 것도 비용입니다. Grunt처럼 저장소가 살아 있고 보안 수정이 제공되는 도구라면, 유행이 지났다는 이유만으로 옮길 이유는 없습니다.
마지막으로 — 목록을 다루는 태도
이 시리즈를 쓰면서 지킨 규칙이 몇 가지 있습니다. 다른 곳에서 같은 주제를 다룰 때도 유용할 것 같아 남깁니다.
- 사용자와 메인테이너가 남아 있는 프로젝트를 죽었다고 쓰지 않습니다. 사실은 대개 자리를 내주었다거나 쓰임이 좁아졌다입니다.
- 왜 그만두었는지 추측하지 않습니다. 공개적으로 밝힌 것만 옮기고 출처를 답니다.
- 확인할 수 없으면 뺍니다. 인상은 근거가 아니고, 출처를 댈 수 없는 사용량 추세는 인용하지 않습니다.
- 무엇을 남겼는지 반드시 적습니다. 대부분의 프로젝트는 이름보다 오래 가는 것을 남겼습니다.
마지막 항목이 이 시리즈를 쓴 이유입니다. Atom이 남긴 Electron, rkt가 만든 표준화 압력, ZooKeeper가 정의한 조정 서비스라는 범주. 프로젝트 이름이 목록에서 사라져도 그 프로젝트가 증명한 것은 지금 우리가 쓰는 도구 안에 들어 있습니다.
지금 여러분이 쓰는 스택도 언젠가 이 목록에 오를 것입니다. 그건 나쁜 일이 아니라 그 도구가 무언가를 증명했다는 뜻일 가능성이 큽니다.
상태 정보는 2026-08-12에 직접 확인했습니다. 프로젝트는 다시 활발해지기도 하니 최신 상태는 직접 확인하세요.
함께 읽기
- 버스 팩터는 결정할 수 있는 사람 수다
- 오픈소스 거버넌스가 깨질 때
- 오픈소스 라이선스 변화 2026
- Airflow 2 EOL 마이그레이션의 현실
- 레거시 코드와 일하기
- 도구: SW 정기점검 체크리스트 · 텍스트 비교기 · Git 플레이그라운드
시리즈
- 빌드와 프론트엔드 도구
- 인프라와 컨테이너
- 데이터 저장소와 큐
- 언어와 프레임워크, 런타임
- 무엇이 기술을 교체시키는가 (이 글)
현재 단락 (1/81)
앞선 세 편에서 서른 개 남짓한 프로젝트를 봤습니다. 목록 자체는 시간이 지나면 낡습니다. 남는 것은 패턴입니다. 무엇이 교체를 만들었고, 우리 스택에서 같은 징후를 어떻게 알아볼...