ReScript의 데드 코드 검출 정적 분석기가 반응형(reactive) 기반으로 재구축되면서, 프로젝트 전체 수준의 분석 결과가 편집 중에도 실시간에 가깝게 갱신되는 방향을 소개합니다.
URL: https://rescript-lang.org/blog/reactive-analysis/
ReScript 파일을 편집하면서 프로젝트 전체(project-wide) 데드 코드 경고가 작업하는 즉시 거의 바로 업데이트되는 모습을 상상해 보세요. 전체 재분석 명령을 기다릴 필요가 없습니다. 프로젝트에서 실제로 사용되는 부분이 무엇인지에 대한 지속적인 피드백만 있으면 됩니다.
이것이 우리가 ReScript에 가져오고 있는 변화입니다.
데드 코드 탐지를 구동하는 정적 분석기가 반응형(reactive) 기반 위에서 다시 만들어지고 있습니다. 한 파일에서 함수에 대한 참조를 추가하면, 다른 파일에 있는 해당 선언의 “unused(사용되지 않음)” 경고가 빠르게 사라집니다. 프로젝트 어딘가에서 모듈의 마지막 사용을 제거하면, 데드 코드 경고가 즉시 나타납니다. 분석은 코드와 동기화된 상태를 유지하며, 실시간으로 업데이트됩니다.
ESLint나 유사한 린터처럼 데드 코드를 표시하는 대부분의 도구는 현재 파일 만을 봅니다. 사용되지 않는 로컬 변수를 알려줄 수는 있지만, 한 모듈에서 export된 함수가 프로젝트 어디에서도 import되지 않는다는 사실까지 알려주지는 못합니다. 진정한 프로젝트 전체 데드 코드 탐지는 전체 코드베이스를 분석해야 하며, 전통적인 분석기는 배치(batch) 모드로 이를 수행합니다: 모든 파일을 모으고, 전부 분석하고, 결과를 보고합니다. 이는 CI 체크나 수동 명령으로는 잘 동작합니다. 하지만 코드를 적극적으로 편집하는 동안 원하는 방식은 아닙니다.
배치 분석에는 어색한 트레이드오프가 있습니다:
개발자가 실제로 원하는 것은 타이핑 속도를 따라잡는 지속적인 피드백입니다. 반응형 분석(reactive analysis)은 정확히 이를 제공합니다.
매 변경마다 프로젝트 전체를 재분석하는 대신, 반응형 분석기는 분석을 계산 그래프(computation graph) 로 표현합니다. 각 데이터 조각—선언, 참조, 생존(liveness) 정보—이 이 그래프를 통해 흐릅니다. 파일이 바뀌면 그래프 중 영향을 받는 부분만 다시 계산됩니다.
결과적으로, 큰 코드베이스에서도 일반적인 편집에 대해 분석이 거의 즉시 완료됩니다.
main이 helper를 호출하고, helper가 format과 validate를 호출하는 콜 그래프가 있다고 가정해 봅시다. 편집 과정에서 validate가 old를 호출하기 시작합니다.
배치 분석에서는 분석기가 모든 것을 다시 스캔합니다. 반응형 분석에서는 다음과 같습니다:
편집된 .cmt가 업데이트되고 — FlatMap이 새 참조를 추출합니다. 컴파일러는 이미 이를 해석(resolved)했습니다: 해당 참조는 타깃(old)과 소스 위치(validate 내부)를 알고 있습니다.
Join이 그 소스 위치를 그것이 속한 선언에 매핑하여, 새로운 그래프 간선(edge)인 validate → old 를 생성합니다.
Fixpoint가 루트(main)로부터 간선을 따라 도달 가능성(reachability)을 전파합니다 — 이제 old가 도달 가능해지고 live set에 들어갑니다.
마지막 Classify 단계(선언과 live set을 조인한 결과)가 old를 live로 표시합니다 — 따라서 데드 코드 경고가 사라집니다.
프로젝트 그래프의 나머지 부분은 건드리지 않습니다.
다음은 반응형 데드 코드 파이프라인을 단순화한 모습입니다:
분석 파이프라인은 조합 가능한 연산자들로 구성됩니다:
Sources는 .cmt 파일에서 나온 원시 데이터를 보관합니다
FlatMap은 각 파일의 데이터에서 선언, 참조, 어노테이션을 추출합니다
Union은 컬렉션을 병합합니다 — 예를 들어 값 참조, 타입 참조, 예외 참조를 하나의 통합된 집합으로 결합합니다
Join은 컬렉션 간에 데이터를 매핑합니다 — 예를 들어 각 참조를 그 참조를 포함하는 선언에 매핑하거나, fixpoint 결과를 사용해 선언을 dead와 live로 분할합니다
Fixpoint는 전이적 도달 가능성(transitive reachability)을 계산합니다 — 루트 선언(엔트리 포인트, @live로 어노테이션된 항목, 혹은 외부에서 참조되는 항목)에서 시작해 간선을 따라가며 live인 모든 것을 찾습니다
마지막 fixpoint는 특히 흥미롭습니다. 데드 코드 탐지는 엔트리 포인트에서 도달 가능한 선언이 무엇인지 알아야 합니다. 이는 전형적인 그래프 순회이지만, 이를 증분(incremental)으로 수행하는 것은 어렵습니다. 그래프에 간선 하나를 추가할 때마다, 처음부터 다시 계산하지 않고 도달 가능한 노드 집합을 어떻게 효율적으로 업데이트할까요?
위의 예에서는 새 간선(validate → old) 하나만 추가되며, fixpoint가 그 델타(delta)로부터 생존성을 전파합니다.
반응형 fixpoint 연산자는 정확히 이 문제를 해결합니다. 이는 루트 집합과 간선 집합을 받아, 전체 도달 가능 집합을 유지합니다. 이 예에서 old가 validate를 통해 도달 가능해지면, fixpoint는 새로운 간선의 영향권에 있는 이웃(neighbors)만 전파하며, 그래프의 무관한 부분은 다시 방문하지 않습니다. 이것이 분석을 실시간 사용에 충분히 빠르게 만드는 핵심입니다.
반응형 시스템에는 미묘한 정확성 이슈가 있습니다: 글리치(glitches) 입니다. 노드 A가 B와 C 모두에 의존하고 있는데 B와 C가 모두 업데이트될 때, 노드 A가 잠깐 불일치 상태를 볼 수 있습니다—C는 이전 값인데 B만 새 값인 상태를 보거나, 그 반대가 될 수 있습니다.
분석에서 글리치는 잘못된 결과를 의미합니다. 실제로는 사용되는 함수가, 해당 참조가 아직 전파되지 않았다는 이유로 잠깐 데드로 보일 수 있습니다.
반응형 스케줄러는 간단한 원칙으로 이를 방지합니다: 누적한 뒤 전파(accumulate, then propagate). 그래프의 각 레벨에서 모든 업데이트가 모인 뒤에야 어떤 다운스트림 노드도 실행됩니다. 노드는 위상 정렬(topological order) 순서로, 웨이브(wave) 단위로 처리됩니다. 분석은 결코 부분 업데이트를 보지 않습니다.
반응형 분석 인프라가 지금 도입되고 있습니다. 이를 통해 가능해지는 것들입니다:
에디터 통합: 최신 확장(extension)에서 지금 사용 가능하며, 활발히 개발하는 동안 반응형 데드 코드 업데이트가 제공됩니다
모노레포 지원: 분석기가 이제 모노레포 셋업에서 올바르게 동작하며, 워크스페이스 루트에서 실행되고 적절한 바이너리 탐색이 수행됩니다
더 빠른 CI 분석: 증분 실행이 전체 실행보다 극적으로 빠릅니다
즉각적인 피드백 루프: 변경의 영향을 즉시 확인할 수 있습니다
반응형 프리미티브는 범용입니다. 데드 코드 탐지를 구동하는 동일한 인프라로 다른 분석도 표현할 수 있습니다:
의존성 그래프 시각화
사용되지 않는 export 탐지
참조 카운팅 및 핫스팟 식별
프로젝트별 커스텀 체크
반응형 분석은 에이전트 기반(agentic) 코딩 워크플로에서도 특히 강력합니다. AI 코딩 에이전트는 빠른 피드백과 함께 가장 잘 동작합니다: 변경하고, 결과를 관찰하고, 반복합니다. 코드베이스를 정리하는 임무를 받은 에이전트는 함수를 제거한 뒤, (다른 무언가가 그것에 의존했기 때문에) 새로운 데드 코드 경고가 생기는지 즉시 확인하고, 기다림 없이 조정할 수 있습니다.
에이전트 기반 코딩이 개발 워크플로의 더 큰 부분이 될수록, 지속적이며 프로젝트 전체 범위의 피드백을 제공하는 도구는 필수 인프라가 됩니다.
이는 더 큰 전환의 시작입니다. 동일한 반응형 기반이 에디터 경험의 다른 부분으로 확장될 것입니다:
타입 체크: 빌드를 기다리지 않는 증분 타입 피드백
내비게이션: 파일이 바뀌어도 정확성을 유지하는 정의로 이동(jump-to-definition)
리팩터링: 이름 변경 및 이동 작업의 실시간 프리뷰
목표는 도구가 당신을 따라잡는 에디터 경험입니다—기다림도, 오래된(stale) 결과도 없이, 지속적인 도움만 제공하는 것.
반응형 분석은 Editor Code Analysis의 최신 기능이며 더 새로운 확장 버전이 필요합니다.
ReScript 컴파일러 >= 12.1.0
VSCode 또는 Cursor + rescript-vscode 확장 >= 1.73.9 (프리릴리스)
빌드 워처(build watcher)를 시작합니다(ReScript 파일을 열 때 뜨는 프롬프트를 수락하거나 "ReScript: Start Build" 를 실행)
"ReScript: Start Code Analyzer" 를 실행합니다
데드 코드 경고가 에디터에 나타나며 개발하는 동안 반응형으로 업데이트됩니다(현재는 저장/빌드 업데이트에 의해 구동됨). 설정 옵션과 사용 상세는 Dead Code Analysis 가이드를 참고하세요.
반응형 프리미티브는 SkipLabs의 작업을 바탕으로 합니다. 그들의 반응형 컬렉션 라이브러리는 글리치 없는 증분 계산의 기반을 제공하며, 이것이 실시간 분석을 가능하게 합니다.
ReScript 커뮤니티에 이 기능을 제공하게 되어 기쁩니다. 당신이 따로 신경 쓰지 않아도, 백그라운드에서 지속적으로 돌아가는 정적 분석—우리가 지향하는 경험은 바로 그것입니다.
더 폭넓은 반응형 툴링 업데이트도 기대해 주세요. 그리고 언제나처럼 GitHub와 포럼에서 피드백을 환영합니다.