React, 어디에 쓰이고 있는지 바로 말할 수 있는가
지금 당장 React처럼 우리 회사에서 주로 사용하는 핵심 오픈소스에서 CVSS 10점 만점의 취약점이 공개된다면, 패치해야 할 시스템 목록을 단 2시간 안에 특정할 수 있을까요?
12월 9일, 우리도 같은 질문 앞에 섰습니다. 그리고 깨달았습니다. 대응의 성패를 가른 것은 패치의 존재가 아니라, ‘대상을 식별할 수 있는가’였습니다. 이 글은 수백 개의 시스템과 단 3명의 담당자, 그리고 촉박한 시간 속에서 진행된 고객사 현장의 React2Shell 72시간 대응 기록입니다. 그 과정은 오픈소스 보안취약점 대응의 본질이 무엇인지 확인할 수 있었습니다.
React2Shell 왜 이렇게 시끄러운가
React2Shell(CVE-2025-55182)은 React Server Components의 Flight 프로토콜에 존재하는 역직렬화 결함으로 인해 발생한 취약점으로, CVSS 10점 만점을 받았습니다. 공격자는 인증 절차 없이 조작된 HTTP 요청만으로 서버 측 원격 코드를 실행할 수 있습니다. 특히 기본 설정 상태의 Next.js 환경에서는 공격 조건이 매우 단순해서, 보안 연구원들은 공격 성공률이 거의 100%에 달한다고 평가했습니다. 실제로 취약점 공개 후 수 시간 만에 중국 연계 위협 그룹이 공격을 시작했다는 보고가 나오면서, 고객의 불안감은 더 커졌을 것입니다.
문제는 영향 범위가 너무 넓다는 점입니다. React는 전 세계 개발자의 약 40% 이상이 사용하고, Next.js는 18~20%가 사용하는 핵심 오픈소스 프레임워크입니다. 국내에서만 18만 개 이상의 웹사이트가 이 생태계를 기반으로 운영되는데. 이번 취약점은 Next.js를 비롯해 React Router, Waku 등 React 생태계 전반이 영향권에 들어갔다는 점에서, 단일 취약점으로는 이례적인 규모입니다.
"React 취약점", "CVSS 10.0", "원격 코드 실행"
12월 초의 평범한 아침, 모니터링 키워드로 설정해 둔 구글 알림에 “React 취약점”, “CVSS 10.0”, “원격 코드 실행”이라는 키워드가 떴습니다. 기사 제목만 훑어봐도 예사롭지 않았습니다. 개인적인 정보 수집 루틴이 만들어낸 첫 접점이었습니다.
12월 5일, 회사 보안부서의 공지 메일로 동일 이슈가 공식적으로 공유되었습니다. 이 공식 텍스트가 등장하면서, 개인의 관찰이 아니라 조직 차원의 실행 계획으로 전환되었습니다. CVSS 10점, 이론상 최대치입니다. 하지만 숫자만으로 모든 것이 설명되지는 않습니다. 과거에도 10점짜리 취약점은 있었지만, 그것들은 대부분 비주류 소프트웨어거나 특정 환경에 국한되었습니다.
이번에는 달랐습니다. React는 프론트엔드 생태계에서 가장 널리 쓰이는 사실상 표준이기 때문입니다. 직접 쓰지 않는다고 생각해도, 프레임워크·번들러·플러그인·빌드 체인 어딘가에 간접 포함될 수 있습니다. 즉, React 앱이 없다는 문장이 곧바로 우리는 안전하다는 결론으로 이어지지 않습니다.
더 결정적인 건 공격 조건의 단순함입니다. 사전 인증과 복잡한 설정 없이 단일 HTTP 요청만으로 원격 코드 실행이 가능합니다. 보안 연구원들이 “공격 성공률 거의 100%”라고 평가한 이유도 여기에 있습니다. 이것은 이론상의 취약점이 아니라, 당장 악용 가능한 실전 무기였습니다.
공지가 올라온 지 며칠이 지난 12월 9일, 상황은 새로운 국면으로 접어들었습니다. 윗선 보고가 이루어졌고, 보안부서와 개발부서가 모두 실제 영향을 받을 가능성이 높은 시스템이 매우 많다는 판단을 공유했습니다. 대응 강도는 “일단 모니터링하자”에서 “지금 당장 전수 점검하자”로 전환되었습니다. 이제 행동이 필요한 시점입니다. 정확히 무엇을, 어디서부터 점검해야 하는지가 중요해졌습니다.
"우리 시스템에서 React 쓰는 곳 리스트업해주세요."
이 한 요청 앞에서, 우리의 현실적인 과제가 또 한번 명확해졌습니다.
오픈소스 지속 관리 체계로 쌓아둔 데이터들..
React 사용 시스템을 찾아내야 한다는 과제는 명확했습니다. 문제는 우리가 그 답을 즉시 꺼내놓을 수 있는 구조가 아니었다는 점입니다. 애플리케이션부터 인프라까지 한 번에 조회하고 관리하는 통합 SBOM 인벤토리가 없었습니다. 통합 SBOM 인벤토리가 갖춰져 있었다면 React라는 키워드 하나로 몇 분 만에 끝났을 일이, 지금은 불가능했습니다. 그리고 하필 그 시점에 CVSS 10점짜리 대형 이슈가 터진 것입니다.
하지만 출발선이 완전히 바닥은 아니었습니다. SCA(Software Composition Analysis) 도구로 꾸준히 오픈소스 라이선스 점검을 해왔고, 그 과정에서 시스템별 오픈소스 컴포넌트 목록이 리포트 형태로 남아 있었습니다.
"적어도 데이터는 존재한다."
이 사실이 그날의 대응을 가능하게 했습니다. 이번 대응에서 가장 중요했던 것은 패치 자체가 아니라, 평소 축적된 오픈소스 관리 이력이었습니다.
문제의 재정의: Where에서 How로
문제는 곧바로 재정의되었습니다. “React가 어디에 있는가”가 아니라 “흩어진 리포트를 어떻게 빠르게 모으고 검색할 것인가”였습니다. 불확실한 추정 게임이 아니라, 해결 가능한 수집·정규화·검색의 기술 과제로 전환된 것입니다.
하지만 현실은 녹록지 않았습니다. 점검 리포트는 Jira 이슈마다 개별 엑셀 파일로 흩어져 있었고, 이를 한 번에 조회할 통합 검색 기능도 없었습니다. 우리는 Jira 이슈를 하나씩 열어 엑셀을 일일이 다운로드해야 했습니다. 2시간여 만에 수집을 마쳤고, 엑셀의 파워쿼리 기능으로 컴포넌트 문자열 기반 통합 검색을 수행했습니다.
기준은 명확했습니다. React를 직접 사용했는지 여부뿐만 아니라, 의존성으로 포함된 흔적까지 함께 잡아내는 것이었습니다. 만약 파워쿼리가 없었다면 각 엑셀을 열어 수동 복사·붙여넣기로 통합 파일을 만들어야 했을 겁니다. 그랬다면 반나절은 더 걸렸을 것이고, 어쩌면 그 이상이었을지도 모릅니다. 이 과정을 통해 React 사용이 의심되는 시스템 목록을 추출했고, 각 시스템에 점검을 요청할 수 있는 근거를 확보했습니다. 이로 인해 수백 개의 시스템이 수십 개의 우선 점검 대상으로 압축되었습니다. 시간은 절약되었고, 대응 초점은 선명해졌습니다.
운영자들이 "우리는 React 안 쓴다"는 말을 하는 이유
수백 개 시스템을 추려 우선 점검 대상 80여 개를 리스트업했습니다. 2시간의 데이터 수집 끝에 얻은 결과였습니다. 이제 점검 요청만 보내면 된다고 생각했습니다. 하지만 그 다음부터가 진짜 전장이었습니다. 점검 요청이 나간 지 몇 시간 만에 회신이 들어오기 시작했지만, 기대했던 점검 완료 회신이 아니었습니다.
“우리 서비스는 React가 아닙니다.”
“프론트는 외주가 개발했고, 운영만 합니다.”
“해당 컴포넌트가 왜 잡히는지 모르겠습니다.”
이 반응 자체는 틀린 말이 아니었습니다. 많은 팀이 React 앱을 직접 개발했는가를 기준으로 판단했습니다. 프레임워크나 빌드 도구에 간접 포함된 React는 개발팀에게 우리가 쓰는 기술로 인식되지 않았습니다. 하지만 우리가 SCA 도구로 탐지한 것은 직접 사용과 간접 의존성을 모두 포함한 결과였습니다. 현업의 체감과 우리의 탐지 기준 사이에 간극이 생긴 것입니다.
점검 요청은 전달되었지만, 맥락은 충분히 공유되지 못했습니다. 어떤 컴포넌트가 탐지되었는지, 직접 사용인지 간접 의존성인지, 어떤 조건에서 영향이 발생하는지에 대한 설명 없이 긴급 점검 필요하다는 메시지만 전달되었습니다. 일부 현업에서는 이번 요청을 불필요한 전수 조사로 받아들였습니다. 자신들이 쓰지 않는다고 생각하는 기술에 대해 다시 확인해야 하는 상황은 부담으로 다가올 수밖에 없었습니다.
점검 요청은 기술적인 문제를 넘어 설득의 문제가 되었습니다. 더 이상 단순히 취약점을 찾는 문제가 아니었습니다. 탐지 결과를 어떻게 설명하고, 어떤 근거로 행동을 요청할 것인가의 문제였습니다. 데이터는 있었지만, 그 데이터가 곧바로 행동으로 이어지지는 않았습니다.
우리는 깨달았습니다. 취약점 대응에서 가장 어려운 구간은 기술 분석이 아니라, 공통의 기준을 만드는 과정이라는 것을 말입니다. “React를 쓴다”는 말이 무엇을 의미하는지, 보안 관점에서의 사용과 개발 관점에서의 사용이 어떻게 다른지, 그리고 왜 이번에는 직접 쓰지 않는다는 답변만으로는 충분하지 않은지를 설명해야 했습니다.
각 시스템에 대해 탐지 결과와 확인 사항을 하나씩 풀어서 설명했습니다. 이 과정에서 대응 속도는 다시 느려졌지만, 이는 의미 있는 전환점이 되었습니다. 탐지 결과에 대한 설명과 함께 점검 범위와 완료 기준을 명확히 전달하자, 하나둘씩 점검이 진행되기 시작했습니다. 왜 우리가 대상인지를 이해한 이후에는 반응도 달라졌습니다. 논쟁은 줄었고, 확인과 조치가 뒤따랐습니다.
결과적으로 모든 과정이 매끄럽지는 않았지만, 긴급 대응은 안정화 궤도에 올랐습니다. 필요한 시스템을 식별하고 점검을 완료했으며, 우선순위에 따라 조치를 진행했습니다. 효율적이지는 않았지만, 이해와 협력으로 3일 만에 이번 긴급 대응은 이렇게 마무리되었습니다.
React2Shell 시리즈는 계속된다
근본적인 문제는 이런 상황이 반복될 수밖에 없다는 점입니다. Log4Shell(2021), Spring4Shell(2022), 그리고 이번 React2Shell(2025)이 그랬듯, 오픈소스 생태계가 성장할수록 대형 보안 취약점은 예외가 아니라 이제 패턴이 되고 있습니다. 현대 소프트웨어의 70~90%가 오픈소스로 구성되는 환경에서, 의존성 체인은 갈수록 복잡해지고 영향 범위는 더욱 넓어집니다. 취약점의 이름만 바뀔 뿐, “우리 시스템 중 어디가 영향을 받는가”라는 질문 앞에서 매번 다시 서는 구조적 본질은 바뀌지 않습니다.
이번 React2Shell 대응 과정에서 몇 가지 분명한 교훈이 도출됐습니다.
•
대응의 핵심은 패치가 아니라 식별 능력입니다. 패치는 이미 존재했습니다. 문제는 그것을 어디에 적용할지 아는 것이었고, 빠른 패치보다 더 중요한 것은 정확한 대상 목록이었습니다.
•
평소의 관리가 비상시의 속도를 결정합니다. 우리가 0에서 시작하지 않을 수 있었던 이유는 라이선스 점검이라는 ‘평상시 습관’이 점검 이력(데이터)을 남겨주었기 때문입니다. 만약 그 이력이 없었다면 며칠이 걸렸을지 모릅니다. 비상 상황은 평상시 습관의 연장선입니다.
•
현업이 행동할 수 있는 ‘근거 패키지(점검이 필요한 이유와 구체적인 수행 방법)’가 필요합니다. 어디서 무엇이 탐지됐는지, 영향 조건은 무엇인지, 무엇을 어떻게 확인해야 하는지, 어디까지 하면 완료인지, 이러한 근거 패키지가 뒷받침되어야 현업이 더 빠르게 움직입니다.
위 교훈은 결국 하나로 수렴합니다. 취약점의 파괴력보다 더 중요한 것은 ‘반복 가능한 대응 구조’가 조직에 내재화되어 있는가 하는 점입니다.
이번 사건은 불완전하게나마 준비하던 우리에게는 관리 가능한 하나의 인시던트였습니다. 하지만 준비되지 않은 조직에게는 재앙이 될 수도 있습니다. 패치가 없어서가 아니라, 어디에 적용해야 하는지 합의하지 못한 채 골든타임을 허비하기 때문입니다.
평상시가 결정하는 비상시
72시간의 긴급 대응을 마치며 우리가 마주한 것은 안도가 아니라 아이러니였습니다. 사실 우리는 통합 SBOM 인벤토리의 필요성을 이미 알고 있었고, 이를 자동화 및 효율화하기 위한 별도 프로젝트를 진행 중이었습니다. 자동화된 관리 체계, 실시간 검색 기능, 통합 대시보드를 갖춘 시스템으로, 바로 이런 상황을 대비한 것이었습니다. 문제는 타이밍이었습니다. 프로젝트는 아직 완성 전이었고, React2Shell이 먼저 찾아왔습니다. 이번 사건은 우리가 추진 중인 SBOM 관리 체계 구축이 얼마나 중요한지를 실전으로 증명한 사례였습니다.
오픈소스 생태계가 커질수록 이런 위협은 멈추지 않고 계속 등장할 것입니다. 결국 보안의 성패는 취약점이 터진 그날 결정되는 것이 아닙니다. 아무 일 없던 평상시에 우리가 무엇을 기록하고 무엇을 관리하고 있었는지가 비상시의 대응을 결정합니다. 지속가능한 관리 체계는 선택이 아니라, 반복되는 위협 시대에 조직이 생존하기 위한 전제조건입니다.
전재웅 프로
오픈소스 거버넌스 전문 컨설턴트로서, 삼성 그룹 계열사를 비롯한 국내 주요 기업들의 오픈소스 관리 체계 구축 프로젝트를 다수 수행하고 있습니다.





