A code-to-column change-impact knowledge graph for AI coding agents.
| English | 한국어 |
cascade view 는 로컬 웹 앱 하나를 띄웁니다. 이 앱은 MCP 서버와 같은 도구
카탈로그를 씁니다. 페이지가 질의를 따로 구현하지 않습니다. 화면의 숫자는 전부
엔진이 계약대로 만든 답에서 옵니다. 그래서 이 페이지와 MCP 로 묻는 모델이 서로
다른 답을 받는 일은 없습니다.
node bin/cascade.mjs view # every registered project
node bin/cascade.mjs view --project mall # just this one
node bin/cascade.mjs view --pack .cascade/pack # one pack directly
# → http://127.0.0.1:4319/
페이지는 머리글과 여섯 개의 자리(place)로 되어 있습니다. 규칙은 하나입니다. 한눈에 보는 것은 숫자나 그림입니다. 글 한 줄은 한 문장입니다. 긴 설명은 한 번 눌러야 열리고, 저절로 열리지 않습니다.
자리마다 읽는 사람이 들고 오는 질문이 하나씩 있습니다.
| 자리 | 답하는 질문 |
|---|---|
| Start | 어디서 시작하나? 대상을 찾아서, 바꾸면 무엇이 깨지는지, 어디까지 닿는지, 분석이 무엇을 못 봤는지 묻습니다. 팩 전체의 지도도 여기 있습니다. |
| Trace | 대상 하나를 양쪽으로 읽습니다. 무엇을 쓰는지, 어디서 쓰이는지, 그리고 그것이 무엇인지입니다. |
| Code(코드) | 프로젝트 브리핑을 읽고 복사하며, 모듈 의존 연결·선언·파일 개요·검증된 원문을 탐색합니다. |
| Structure | 팩 전체를 그림으로 봅니다. Graph, Table links(SQL 조인으로 본 테이블 관계), Coupling, Transactions 입니다. |
| Compare | 이 프로젝트의 이전 빌드와 비교해 무엇이 바뀌었는지 봅니다. 이전 빌드가 있을 때만 나옵니다. |
| Analysis status | 분석이 무엇을 봤고 무엇을 못 봤는지, 빈 곳마다 무엇에 영향을 주고 어떻게 하면 되는지, 그리고 분석에 쓴 Rules 입니다. |
뷰(view)가 여럿인 자리(Structure, Analysis status)는 자리가 놓인 줄에 가는 선을 하나 긋고 그 뒤에 뷰를 늘어놓습니다. 뷰마다 한 칸입니다. 자리를 열면 그 자리에서 마지막으로 보던 뷰가 열립니다. 이미 보고 있는 자리를 다시 누르면, 좁힐 수 있는 뷰는 처음 상태로 돌아갑니다. Show all 을 누른 것과 같습니다.
예전 탭 줄에는 성격이 다른 열 가지가 섞여 있었습니다. 질문(Explore, Flow, Impact), 그림(Graph, ERD, Coupling), 분석 자체의 상태(Rules)입니다. 질문 하나의 절반이 어느 탭에 있는지 먼저 알아야 물을 수 있었습니다. 지금은 읽는 사람이 알고 싶은 것 기준으로 나눴습니다. 예전에 만든 링크도 원래 가던 곳으로 갑니다 (URL).
머리글은 한 줄입니다. 브랜드, 데이트라인, 칩이 있고, 그 아래에 자리가 있습니다.
데이트라인은 어느 프로젝트가 답했는지(서버가 여러 프로젝트를 서빙하면 프로젝트
선택기)와 어떤 레인(lane, 분석 경로)이 팩을 만들었는지 보여 줍니다. 옆의
counts and build(개수와 빌드)를 누르면 이 엔진이 따라가는 체인이 단계마다 개수를
달고 열리고(화면, 경로 기준 API 그룹, 엔드포인트, 서비스, SQL, 테이블, 컬럼), 팩의
digest 와 빌드한 커밋도 함께 열립니다. 두 빌드가 다르게 답할 때는 꼭 필요하지만,
평소에는 눈에 걸리기만 하는 값입니다. 그래서 한 번 누르면 보이는 자리에 뒀습니다.
개수는 전부 overview 답에 있는 값이고, 페이지가 직접 세지 않습니다. 머리글은
불투명해서, 페이지를 스크롤해도 뒤의 내용이 비치지 않습니다.
reach.groups 입니다. 라우트를 경로의
첫 구간으로 셉니다. 레인이 애플리케이션이 배포되는 접두사를 알면(NestJS 전역
접두사와 버전, 선언한 pathPrefixes 항목) 그 뒤의 첫 구간으로 셉니다. Structure,
Coupling, Trace 가 묶는 단위이고, overview 가 groupRule 로 규칙을 말합니다.
프로필이 moduleAttribution.packageDepth 를 선언하면 이 단계는 modules, as
declared(모듈(프로필 선언))로 바뀌고, 핸들러 패키지를 그 단계까지 잘라서 셉니다.
Start 의 지도는 라우트를 다른 방식으로 묶을 수 있고, 그 상자에는 자기 이름을 붙입니다.code.services 입니다. 라우트에서 출발한 걷기가 컨트롤러와
SQL 사이에서 지나가는 메서드를 한 번씩 셉니다. 컨트롤러 메서드, 프런트엔드
함수, 라이브러리 메서드, statement 를 선언만 하는 매퍼 메서드는 세지 않습니다.~ 는 그 축(axis, 분석이 읽는 정보의 한 종류)이 온전하지 않다는
표시입니다. 마우스를 올리면 엔진이 쓴 이유가 나옵니다. 스키마가 없는 팩에도
SQL 이 이름을 댄 테이블은 있기 때문입니다. 아예 만들지 않은 축은
not collected 로 나오고, 누르면 Analysis status 에서 이유를 적은 줄로
갑니다.그 옆에 칩이 최대 네 개 붙습니다.
--no-watch 스냅샷은 unknown으로 남습니다. 갱신 실패는 오류로
표시하며, 이전 팩을 최신 상태로 표시하지 않습니다.not certified 는 누가 열어 본 거의 모든 프로젝트에
해당하는 말이었습니다. 그런데 머리글에서 읽는 사람이 할 수 있는 일은
없었습니다. 수준 자체는 Analysis status 와 모든 답 옆의 근거 레일(evidence
rail)에 그대로 있습니다. 거기 툴팁이 값을 바꾸는 두 가지 방법을 알려
줍니다. golden set 을 승인하거나, 프로그램이 실제로 돈 기록으로 검사에
라벨을 붙이는 것(cascade golden propose --from-otel)입니다. 실행 기록에 기댄
수준은 호박색 checked against what ran 으로 나옵니다. 툴팁은 실행 기록으로
확인할 수 없는 것을 말해 줍니다.axis:jpa, flow, summary:groups) 와
함께 그대로 열립니다. Analysis status 의 근거 레일과 같은 접힘입니다. 그래서 같은
문장이 두 번, 두 가지 말투로 나오지 않습니다(Analysis status 의 한계에 제목이 붙는
방식 참고). 답 옆의 레일은 그 답 자신의 한계를 세고, 프로젝트 전체 개수를 옆에
따로 적습니다. 그래서 두 숫자가 하나로 읽히지 않습니다.그다음이 언어 토글과 테마 토글입니다. 아래에서 설명합니다.
모든 뷰의 툴바 아래에 한 줄짜리 리드가 있습니다. 화살표를 누르면 문단이 열립니다. 이 뷰가 무엇을 하는지, 그림을 어떻게 읽는지, 엔진이 무엇을 증명하지 못했는지가 적혀 있습니다. 접는다고 지워지는 것은 없습니다. 개수는 늘 접힘 바깥에 둡니다. 한눈에 보라고 있는 것이 개수이기 때문입니다.
접힘마다 열어 둔 상태를 localStorage 에 기억합니다. 언어를 바꾸면 블록을
카탈로그에서 다시 만들지만, 열고 닫은 상태는 그대로 돌려 놓습니다.
하나의 토큰 위에 테마가 둘 있고, 토글은 머리글에 있습니다.
선택은 기억됩니다(cascade.viewer.theme). prefers-color-scheme 는 보지
않습니다. 제품에는 기본값이 있고 사용자가 그것을 바꾸는 것이지, 운영체제가 이
페이지의 모양을 정하는 것이 아니기 때문입니다.
Start 는 먼저 묻습니다. 예전의 Overview 는 무엇을 보러 왔는지 말하기도 전에 비율과 팩 전체 그림부터 보여 줬습니다.
원인이 먼저. 집계를 낸 모드에서 SQL 문에 닿는 API 가 하나도 없으면, Start 는
검색창과 비율보다 먼저 그 이유를 한 줄로 말합니다. ghostfolio 에서는 “API 118개 중
117개의 주소가 HEURISTIC 등급이라 conservative 모드는 입구에서 멈춥니다. 주소가
확실해지기 전에는 이 모드에서 SQL까지 닿는 것이 없습니다.” 라고 나오고, 그 아래에
엔진의 해결책(tsBackend.globalPrefixExclude 선언)과 heuristic 모드로 보기
버튼이 있습니다. 그 아래 빈 곳 목록은 같은 말을 되풀이하지 않습니다.
하나를 골라 묻기. 입력창에서 API 라우트, 화면, 테이블, 컬럼, SQL statement,
메서드를 모두 찾을 수 있습니다. Trace 와 같은 자동 완성을 쓰고, 행마다 종류를
적어 줍니다. Start 어디서든 / 를 누르면 커서가 입력창으로 갑니다. 대상을
고른 뒤 세 질문 중 하나를 누릅니다.
대상이 답할 수 없는 질문은 흐리게 막지 않습니다. 자리에 남아서 이유를 한 문장으로 말합니다. Trace 가 쓰는 문장과 같습니다. 테이블은 체인이 끝나는 곳이라 쓰는 것이 없습니다. 화면은 체인의 맨 위라 쓰이는 곳이 없습니다. 대상을 고르지 않고 앞의 두 질문을 누르면 Trace 의 목록이 열립니다. 그 질문을 보통 시작하는 종류(바꾸면 깨지는 것은 테이블, 어디까지 닿는지는 API)가 이미 그 방향으로 열려 있고, 목록의 리드가 어떤 질문이 기다리는지 알려 줍니다.
이 분석이 볼 수 있는 것. 프로젝트를 읽은 레인, 집계를 낸 모드와 깊이(그래서 어떤 등급을 따라갔는지), 그리고 축마다 칩 하나가 나옵니다. 칩은 다 읽었는지(collected), 일부만 읽었는지, 못 모았는지를 말합니다. 스키마는 어디서 읽었는지도 적습니다(DDL 파일, 또는 테이블을 선언한 매핑). Java 브리지 두 개(JPA, MyBatis-Plus)는 실제로 돈 경우에만 나옵니다. 다른 스택에서 “not collected” 는 소음이기 때문입니다. 칩을 누르면 Analysis status 의 그 축 줄로 갑니다.
답을 바꾸는 빈 곳. 실행에 없던 입력, 읽지 못한 것, 일부만 읽은 축, 레인이 보고한 진단 종류가 여기 나옵니다. 모두 답의 내용을 바꾸기 때문입니다. 입력이 먼저 나오고, 여섯 개까지 보여 준 뒤 나머지는 개수로 줄입니다. 각 줄은 Analysis status 의 해당 줄로 가는 버튼입니다. 줄의 이름은 페이지 말로 쓰고, 엔진의 코드는 툴팁에 둡니다. 걷기 자체의 한계나 어떤 라우트도 닿지 않는 테이블은 답을 바꾸지 않습니다. 그런 것은 Analysis status 에만 둡니다. 라우트 주소 중 일부가 추정이면 그것이 첫 줄이고, 집계 모드가 거기서 멈추는지도 함께 말합니다. 그것 때문에 SQL 에 닿는 API 가 없다면 Start 의 첫 줄이 대신 말하고, 이 목록은 되풀이하지 않습니다.
줄마다 아래에 할 일이 붙습니다. 엔진이 그 빈 곳을 닫는다고 아는 한 가지이고,
답의 remedy 에서 가져옵니다. 페이지가 지어내지 않습니다. 짧은 예가 붙은 프로필
키(“프로필에 tsBackend.globalPrefixExclude 키를 적습니다. 예: [“health”,
“docs{/*rest}”]”), 다시 분석할 때 붙일 옵션, 실행할 명령, 다시 물을 모드 중
하나입니다. 포트 추측 하나로만 내려간 web 축은 선언할 애플리케이션과 함께 servers 를
대고, 아무도 선언하지 않은 OpenAPI 문서로 놓인 라우트는 openapi.generatedFromCode 나
openapi.generatesCode 를 댑니다. 엔진이 아는 해결책이 하나로 정해지지 않으면
그렇다고 말합니다. “엔진이 아는 해결 방법은 없습니다. 무엇을 못 읽었는지만 말할 수
있습니다.” 해결책을 주기 전의 서버는 아무것도 보내지 않고, 그때는 줄도 없습니다.
ghostfolio 의 해결책은 tsBackend.globalPrefixExclude 선언입니다. 자기 exclude 목록을
선언하고 다시 분석하면 라우트 118 개가 모두 EXACT 가 되고, conservative 집계에서 SQL 에
닿는 엔드포인트는 118 개 중 0 에서 104 로, 닿는 테이블은 21 개 중 0 에서 21 로 늘어납니다.
비율. 체인 단계마다 카드가 하나입니다. 값은 overview 답의 reach 에서
옵니다. SQL 에 닿는 엔드포인트, 닿은 statement, 테이블, 컬럼, 그리고 테이블에
닿는 화면의 수입니다. 숫자 아래에는 빠진 것이 그 단계의 말로 적힙니다. 비율은
나머지와 같이 봐야 뜻이 있기 때문입니다. 그 아래에는 이 비율을 읽을 때 꼭 같이
봐야 할 한계 하나가 같은 카드에 붙습니다. 그 단계의 축을 만들지 않았거나
일부만 읽었다면 그 사실이 나옵니다(“no database schema was read: these are the
tables the SQL named”). 엔드포인트 카드에는 라우트 주소 중 몇 개가 추정인지가
나옵니다. 한계는 그 이유를 적은 줄로 가는 버튼입니다. 스키마를 한 번도 읽지
않은 프로젝트에서 “tables reached 100%” 만 보면 “데이터베이스 전체” 로 읽히기
때문입니다. 한계 아래에는 그 한계가 나온 빈 곳과 같은 할 일 줄이 붙습니다. 축의
해결책(axisRemedies)이나, 주소가 추정인 라우트의 해결책(routeRemedy)입니다.
요청을 다른 프로젝트가 받아 주는 프로젝트라면 나머지만 적는 것은 틀린
문장입니다. 게이트웨이의 화면 아홉 개는 자기 테이블에 닿지 않습니다. 가진
테이블이 없으니까요. 그중 여덟 개는 HTTP 한 번 건너 실제 컬럼에서 끝납니다.
답에 reach.viaFederation 이 있으면 엔드포인트와 화면 카드가 두 숫자를 같이
적습니다. 0 here, 8 in connected projects 입니다. 비율 자체는 이 프로젝트의
값으로 둡니다. 이 프로젝트의 비율이라는 말이 그런 뜻이기 때문입니다.
질문 아래에는 이 분석이 볼 수 있는 것과 답을 바꾸는 빈 곳이 있고, 그다음이 지도, 그다음이 비율과 가장 많이 쓰이는 테이블(몇 개 API 가 닿는지 기준), API(몇 개 테이블을 건드리는지 기준), 화면(몇 개 테이블에 닿는지 기준)입니다. 지도를 비율보다 먼저 두는 것은, 아직 대상이 없는 사람에게는 지도가 들어가는 입구이기 때문입니다. 테이블은 Trace 를 위로 걷기로 열고, API 와 화면은 아래로 걷기로 엽니다. 순위의 이름은 가운데를 줄입니다. 그래서 두 라우트나 테이블을 가르는 끝부분이 남습니다.
Start가 세는 모드. 지도의 모드 선택은 지도, 비율, 테이블·API·화면 순위를
함께 바꿉니다. 페이지는 같은 모드로 overview와 browse kind=screen을 묻고,
모드별로 답을 보관합니다. 비율 위에는 모드와 깊이가, 각 순위에는 집계 모드가
표시됩니다. 누락 목록과 분석 상태는 conservative 집계를 유지합니다. 다른 모드를
선택하면 Start가 이 차이를 밝히고 원래 모드로 돌아가는 버튼을 보여줍니다.
지도는 팩 전체를 한쪽에 상자 열 개 안팎으로 그립니다. 왼쪽은 라우트를 담은 상자,
오른쪽은 테이블 계열입니다. 상자와 계열 사이에 선이 하나씩 있습니다. 선의
굵기는 그 뒤의 테이블 수이고, 점선 모양은 가는 길에서 가장 약한 등급입니다.
summary 도구의 답이고, Start 가 화면에 나올 때 한 번 묻습니다. 그래서 Trace
로 바로 열린 페이지는 지도를 기다리지 않습니다. 왼쪽 상자는 담은 라우트 수로, 계열은
닿는 라우트 수와 그다음 크기로 남깁니다. 그래서 많은 라우트가 건드리는 테이블 하나짜리
계열이, 라우트가 적게 닿는 큰 계열보다 먼저 지도에 남습니다. 나머지는 한쪽마다
(others) 상자 하나로 접고, 무엇을 기준으로 접었는지 적습니다(“API가 적은 {nouns}
{n}개, API {routes}개”). 그 선도 그리로 가므로 걷기가 닿은 것 중 빠지는 것은
없습니다. 계열 상자마다 닿는 라우트 수를 적습니다.
선은 자기가 무엇을 싣는지 말합니다. 선이 읽을 만큼 적으면(열두 개 안팎, 또는 열린 상자 하나나 고른 노드의 선) 선마다 개수가 붙습니다. 전체 지도에서는 “API 41 → 테이블 12” 처럼 적습니다. 개수는 선 위에 깐 자기 바탕 위에 놓입니다. 선을 모두 그린 뒤에 개수를 그리므로, 어떤 선도 개수 위를 지나지 않습니다. 개수는 자기 곡선 위에서, 가운데부터 바깥으로 찾아 바탕이 두 열 사이 띠 안에 들고 다른 개수와 겹치지 않는 첫 자리에 놓입니다. 그래서 상자나 다른 개수를 가리지 않습니다. 가장 굵은 선부터 자리를 잡습니다. 빈 자리가 없는 선과 큰 지도의 선은 마우스를 올렸을 때 개수를 보여 줍니다.
summary 에 그 노드를 지나는 경로(answer.through)를 묻습니다.
라우트는 자기 행에서 닿는 계열마다 선이 나갑니다. 테이블은 그 테이블에 닿는
라우트를 가진 묶음마다 선이 들어옵니다. 선마다 자기 경로의 가장 약한 등급으로
점선이 그려집니다. 그림 옆에는 그 노드에서 Trace 로 가는 버튼(무엇을 쓰는지,
어디서 쓰이는지, 상세)과 경로 목록이 나옵니다. 목록의 항목도 모두 Trace 로
갑니다.상자는 누가 선언한 것이 아니라 읽어 낸 것입니다. 규칙마다 상자에 자기 이름을 붙입니다. 그래서 머리글이 다른 방식으로 세는 것을 지도가 “묶음” 이라고 부르는 일이 없습니다. 그림 위 문장이 어떤 규칙으로 묶었는지 알려 줍니다.
moduleAttribution.packageDepth 가 있으면, 핸들러 패키지를 그
단계까지 자른 것이 모듈입니다. 머리글도 같은 모듈을 셉니다.egovframework.com, jeecg-boot 의 org.jeecg.modules). 옆으로 떨어진 가지는
따로 상자가 됩니다. “코드의 어느 부분이 어느 테이블에 닿는가” 에 답하고, 그것은
코드를 고치는 사람이 맡은 단위입니다. 머리글의 API 그룹과는 다릅니다. ghostfolio 는
경로 기준 API 그룹 32 개와 코드 영역 21 개, jeecg-boot 는 26 개와 14 개, mall 은 32 개와
4 개입니다./api 아래의 /api/mdm/...) 그 접두사 아래 구간으로 묶은 경로 영역
입니다. 머리글의 API 그룹은 첫 구간에서 멈춥니다. 레인이 모든 라우트에 배포 접두사를
뺀 묶음을 적어 뒀다면(NestJS 의 전역 접두사와 버전) 그것도 API 그룹이고, 이때는
추정하지 않습니다.t_ds 아래 t_ds_task). 걷기가 닿은 테이블만이 아니라 팩의 모든
테이블로 읽으므로, 한 테이블은 어느 모드의 지도에서든 Table links 에서든 같은 계열에
듭니다. 단어는 밑줄, 하이픈, 대소문자가 바뀌는
곳에서 끊습니다. 그래서 SymbolProfile 은 symbol, profile 이고 MARKET_QUOTE
는 market, quote 입니다. 이름이 대부분 한 단어이고 대부분 같은 글자로
시작할 때만 글자로 묶습니다(전자정부의 COMTN..., COMTH...)._OrderToTag, table.join-table
룰 prisma.relation-tables). 그런 테이블은 잇는 테이블들이 한 계열에 있으면
그 계열로 가고, 아니면 (join tables) 로 갑니다.이것은 코드와 스키마에 드러난 패턴이지, 누가 선언한 모듈이 아닙니다. 지도 옆
summary 도구의 limits 가 그렇게 말합니다. 상자 하나에 라우트가 대부분 몰려
있으면 그것도 limits 에 나옵니다.
Trace(추적)는 한 가지를 묻는 자리입니다. API 라우트, 화면, 테이블, 컬럼, SQL statement, 메서드 중 하나를 한 번 고릅니다. 그다음 세 방향 중 하나로 읽습니다.
대상 종류마다 가능한 방향만 나옵니다.
| 대상 | What it uses | Where it is used | Details |
|---|---|---|---|
| API 라우트 | 있음 | 있음 | 있음 |
| 화면 | 있음 | 없음: 화면은 체인의 맨 위라 부르는 것이 없음 | 있음 |
| 메서드 | 있음 | 있음 | 있음 |
| SQL statement | 없음: 자기 테이블만 쓰고, 그건 Details 에 있음 | 있음 | 있음 |
| 테이블 | 없음: 체인이 끝나는 곳 | 있음 | 있음 |
| 컬럼 | 없음: 체인이 끝나는 곳 | 있음 | 있음 |
대상에 없는 방향은 흐린 버튼으로 두지 않습니다. 아예 없고, 답 위의 줄이 이유를
한 문장으로 말합니다. 이 표는 flow 도구의 표(FLOW_DIRECTIONS,
mcp.md)와 같고, 테스트가 둘을 맞춰 둡니다.
예전에는 Explore, Flow, Impact 세 탭이 이 질문 하나를 나눠 들고 있었습니다. 탭마다 고른 대상이 따로 있어서, 탭을 옮기면 대상을 잃었습니다. 그 탭들로 만든 링크도 이제 여기로 열립니다(URL).
Trace 는 빈 검색창이 아니라 목록으로 열립니다. 왼쪽 열에 고를 수 있는
종류와 개수, 정렬, 개수 줄, 그리고 고를 때 기준이 될 숫자가 붙은 행이
있습니다. 행마다 첫 줄은 이름이고 폭을 다 씁니다. 그래서 두 행을 가르는 이름 끝이
남습니다. 그 아래에 숫자가 읽는 사람의 말로 풀려 나오고(읽기 7 쓰기 8 API 22 화면
12), 그다음에 행 자신의 설명이 붙습니다. 숫자에 마우스를 올리면 무엇을 세는지
나옵니다. 개수 줄 옆에는 목록의 숫자를 어떤 모드로 걸었는지 적습니다(“conservative
모드 기준”). 목록과 옆의 답은 Trace에서 고른 모드를 함께 따릅니다.
browse.census가 목록의 모드와 깊이를 밝힙니다. 모드를 바꾸면 선택한 대상을
유지하면서 목록과 펼친 테이블의 컬럼을 다시 읽습니다. 저장해 둔 목록도 같은
모드일 때만 재사용합니다. 라우트와 화면은 자기 API 그룹 아래에 모입니다. 테이블에는 캐럿이
있어서 누르면 그 테이블의 컬럼이 펼쳐집니다. 테이블에서 바꾸려는 컬럼까지 바로
내려갈 수 있습니다. 모든 행과 숫자는 browse 답 하나에서 오고, 페이지는 세지
않습니다. 목록이 처음 여는 종류는 프로젝트마다 기억합니다. 체인의 어느
끝에서 시작하는지는 프로젝트에 따라 정해지는 일이기 때문입니다.
그림 위 입력창은 두 가지 일을 합니다. 입력하면 목록이 이미 가진 행을 걸러
내고, 요청은 보내지 않습니다. 동시에 모든 종류(라우트, 화면, 테이블, 컬럼,
statement, 메서드)를 찾는 검색창입니다. 드롭다운의 행마다 종류가 적혀 있습니다.
/ 는 필터로 커서를 옮기고, 방향키가 강조를 옮기고, Enter 가 고릅니다.
1100px 아래에서는 목록이 Browse 버튼 뒤의 서랍이 됩니다. 하나를 고르거나
Escape 를 누르면 닫힙니다.
목록의 오른쪽 가장자리를 끌어 넓힐 수 있고, 너비는 기억됩니다. 이름이 길면
가운데를 줄이고 끝은 남깁니다. 라우트는 HTTP 메서드와 마지막 구간을 남기고,
CamelCase 이름은 마지막 단어를 남깁니다. statement 와 메서드는 짧은 이름으로
나옵니다(PmsProductDao.getUpdateInfo, OmsPortalOrderController#generateOrder).
레인과 그래프 카드도 같은 이름을 씁니다. 전체 id 는 행의 툴팁에 있습니다.
필터도 전체 id 로 맞추므로, 패키지 이름을 입력해도 그 행들이 찾아집니다.
대상이 질문의 시작점입니다. 그림에서 행을 누르면 그 행을 살펴볼 뿐입니다. 카드에 그 행의 경로, 등급, 소스가 나오고, 화면의 질문은 그대로입니다. 카드의 Trace from here 를 눌러야 그 행에서 질문을 다시 시작합니다. 그 행의 종류가 같은 방향을 가지면 같은 방향으로 읽습니다. 방향을 바꿔도 시작점은 그대로입니다.
flow 가 offset 으로
레인을 나눠 줍니다).깊이, 그리는 방식, 움직임은 Options 안에 있습니다. 대상에 대한 질문이 아닌 두 가지, What my uncommitted edits touch(내 변경이 닿는 곳)와 Start over: nothing picked(처음 상태로 (선택 해제))도 거기 있습니다. 그래서 툴바가 한 줄입니다. 모드는 툴바에 남아 있습니다.
그림 위, 무엇을 물었는지 적은 줄 아래에서 Trace 가 한 문장으로 답합니다. 어떤 모드와 깊이로 셌는지도 함께 적습니다.
개수는 가져온 행이 아니라 답의 truncated 에 있는 레인 전체 개수입니다. 직접 고른
깊이에서 걷기가 잘렸으면 옆에 “깊이 {depth}에서 멈췄으니 더 있을 수 있습니다.” 라고
적고 Follow it all the way(끝까지 따라가기)를 붙입니다. 그런 걷기의 레인 칩에는 +
가 붙습니다(0+, 12+).
문장 아래에는 끝이 있습니다. 끝 레인마다 상자 하나에 개수와 처음 다섯 이름을 적습니다. 이름을 누르면 그림에서 그 행을 고르고 화면에 보여 줍니다. 레인 제목을 누르면 그 레인으로 갑니다. 그래서 읽는 사람이 찾던 API 와 화면이 그림을 스크롤하기 전에 보이고, 그림은 근거로 아래에 남습니다.
모드가 출발점에서 걷기를 멈추면(주소가 모드 기준선 아래 등급인 라우트를 아래로 걸을 때) 문장이 그것을 한 번 말하고, 더 넓은 모드를 버튼으로 줍니다(“{mode} 모드에서는 아무 데도 닿지 않습니다. 이 API의 주소부터 {grade} 등급이라 {mode} 모드는 입구에서 멈춥니다.”). 빈 레인은 그 모드에서 비었다고만 말합니다. 위로 걸을 때는 라우트 주소가 걷기를 멈추지 않습니다. 주소가 추정이면 답 옆에 한 줄로, 그 주소로 찾은 호출도 모두 같은 등급이라고 적습니다.
확실하지 않은 주소를 거쳐 닿은 행에는 주소 불확실 태그가 붙고, 툴팁이 이유를
말합니다. 라우트 자신의 주소가 추정이거나, 프런트엔드 호출 URL 의 앞부분이 추정에
기대거나(url.guess: 포트가 Spring Boot 기본값에 기댐, 포트를 모름, fallback 리터럴,
배포 호스트, 가정한 import 별칭), 더 구체적인 라우트가 가릴 수 있는 catch-all 라우트로
갔거나, 접두사를 떼고서야 맞았을 때입니다. 등급이 아니라 태그입니다. 서버가 그리는
페이지는 화면 사이에 page 태그를 달고 템플릿 이름으로 나옵니다.
체인은 단계마다 레인 하나로, 대상에서 바깥쪽으로 왼쪽에서 오른쪽으로 그립니다. 레인은 안의 노드 이름을 모두 개발자가 쓰는 말로 적습니다.
Class.method, MyBatis statement 는 Mapper.id, ORM 호출 지점은 그
자리가 하는 호출(UserService.deleteUser → Access.deleteMany), 웹 함수는
module.function 입니다(index 파일은 폴더 이름). 컬럼은 table.column 입니다. ORM
호출은 statement 행의 call({model, operation})로 이름을 짓고, 한 메서드의 두
자리가 같은 호출을 하면 먼저 순번을 붙입니다(UserService.deleteUser #0 →
Access.deleteMany).레인 너비는 이름에 맞춰 200px 에서 360px 사이로 정합니다. 이름은 두 줄까지 쓰고,
넘치면 가운데를 줄이고 끝은 남깁니다. 홉과 등급은 이름 아래 줄에 있습니다. 행은
같은 홉, 같은 등급 안에서만 선이 덜 엇갈리게 순서를 바꿉니다. 후보 묶음을 넘어
섞지는 않습니다. mall 에서 pms_product 의 Impact 는 레인 사이 교차가 200 개에서
22 개로 줄었습니다. 14 행이 넘는 레인은 같은 홉, 같은 등급에서 한 소유자의
행을 한 행으로 접고, 나머지는 “N more” 로 접습니다. 둘 다 그 자리에서
펼쳐집니다. 골라 둔 체인과 찾기 상자가 찾은 행은 접지 않습니다.
선은 그 링크 하나의 등급으로 점선 모양을 정합니다. 행의 배지는 경로 전체의 등급, 즉 시작점에서 그 행까지 가장 약한 링크의 등급입니다. EXACT 는 실선, SOUND_SET 은 점선, HEURISTIC 은 일점쇄선입니다. 파일로 저장하는 SVG 도 같은 표시를 씁니다. 후보 묶음은 무엇인지 스스로 말합니다. “candidate set (n): the real target is among these”, “guessed (n): check these” 입니다. 걷기가 아무것도 찾지 못한 레인은 행이 있는 레인 뒤에 그립니다. 그래서 빈 레인 뒤로 선이 지나가지 않습니다.
그림 위에는 스크롤해도 사라지지 않는 띠가 있습니다. 시작점과 레인마다 개수를 적고, 레인으로 바로 이동하고, 이름이나 id 로 행을 찾습니다. 골라 둔 체인은 마우스가 떠나도 불이 켜져 있습니다. 방향키로 행과 레인 사이를 옮겨 다닙니다.
작은 점들이 선을 타고 호출 방향으로 흐릅니다. 아래로 걷기에서는 왼쪽에서 오른쪽, 위로 걷기에서는 오른쪽에서 왼쪽입니다. 위로 걷기는 같은 호출을 거꾸로 그리기 때문입니다. 프레임 루프가 아니라 경로를 따라가는 SVG 애니메이션이라, 뷰가 숨어 있으면 비용이 없습니다. Options 의 Flow on 으로 멈출 수 있습니다. 시스템에 동작 줄이기를 설정한 사람에게는 멈춘 화살촉만 보이고, 움직임은 전혀 없습니다.
툴바에 보기가 두 가지 있습니다. chain 은 경로 전체를 그리고, by hop 은 같은 행을 한 단계씩 묶어 홉마다 집계를 붙입니다.
범례와 근거 레일 앞에 What limited this answer(이 답을 제한한 것) 패널이
있습니다. 개수는 문장 안에 넣습니다(“{n}개 행이 깊이 {depth}에서 멈췄습니다. 그
너머는 따라가지 않았습니다.”). 그래서 어느 언어로도 줄이 숫자 세는 말로 시작하지
않습니다. 직접 고른 깊이에서 멈춘 행 수(옆에 Follow it all the way 버튼. 위로 걸을
때는 영향받는 화면 전체를 ‘상세’에서 깊이 제한 없이 본다는 말을 덧붙입니다), 노드 상한, 프로필 설정으로 건너뛴 생성 코드 안의
단계, 깊이 상한 바로 너머에 이름만 나온 행, 일부만 보여 준 레인(shown of
total)이 나옵니다. 프런트엔드가 없는 팩이면, 어느 화면이 이 라우트를 부르는지는
“없음”이 아니라 “모름”이라고 적습니다. 답을 자른 것이 없으면 그렇다고 말합니다.
걷기가 더 깊이 갈 수 없다고는 말하지 않습니다. 걷기의 규칙은 원래 상한이 없기
때문입니다.
How to read the lines(선 읽는 법)는 등급마다 두 가지를 따로 세고 그렇다고
적습니다(선 {links}개, 행 배지 {rows}개). 선은 그 연결 하나의 등급이고, 행의 배지는
출발점에서 그 행까지 가는 길의 가장 약한 연결입니다. RUNTIME_ONLY 와 UNRESOLVED 도 뜻과
함께 나옵니다. 그래서 페이지의 어떤 배지도 설명 없는 코드로 남지 않습니다.
What this walk left out(이 걷기가 뺀 것)은 따로 된 패널입니다. 모드의 기준선이 링크를 뺐을 때 나옵니다. 뺀 등급마다 개수와 그 등급의 뜻을 적습니다. 그 링크를 걸을 더 넓은 모드를 버튼으로 주거나, 어떤 모드도 걷지 않는 링크라고 말합니다(실행 시점에 정해지는 링크, 또는 분석이 무엇을 가리키는지 알 수 없던 링크). 라우트와 핸들러를 잇는 링크 자체가 기준선 아래이면, 걷기가 라우트에서 멈췄다는 말을 가장 먼저 합니다. 그 아래에 분석의 진단이 종류마다 한 줄씩 개수와 함께 나옵니다. Analysis status 와 같은 방식입니다. 진단마다 페이지 말로 된 제목과 그 옆의 코드가 있고, 엔진이 쓴 문장은 접혀 있습니다(“엔진이 쓴 원문”).
Details 는 대상이 무엇인지 목록으로 보여 줍니다. 목록 하나가 도구 답 하나입니다.
table_usage).read 나 write 가 붙은 statement 목록(column_impact),
그리고 지금 모드에서 그 위에 있는 라우트(endpoint_impact).@Transactional 메서드도 나오고, 각각 Transactions 뷰로 갑니다.컬럼, 테이블, statement, 메서드의 카드 아래에는 여기를 바꾸면 영향이 가는
화면(screen_impact)이 나옵니다. 지금 모드로 끝까지 올라가 찾은 것이고, 그림을
어떤 깊이로 물었는지와 상관없습니다. 각 화면은 Trace 를 아래로 걷기로 엽니다.
Options 안의 What my uncommitted edits touch(내 변경이 닿는 곳)는 커밋하지 않은 git 변경에 같은 질문을 던집니다. 아래로 건드리는 것과, 위에서 영향을 받는 엔드포인트와 화면이 나옵니다.
Trace 툴바의 Save as 안에 Export, SVG, PNG 가 있습니다.
Export 는 지금 화면의 그림을 HTML 파일 하나로 저장합니다. 보고서, 설계 리뷰,
Cascade 가 없는 사람에게 보내는 메시지에 씁니다. 아래로 걷기는 Flow 파일,
위로 걷기는 Impact 파일입니다. cascade export --tab 이 받는 두 이름입니다.
셸에서는 cascade export 가 같은 파일을 만듭니다(cli.md).
Details 와 My edits 는 그림이 아니라 목록이라 저장할 것이 없습니다.
파일은 이 페이지에 답을 넣은 것입니다. 마크업, 스타일, 스크립트, 글꼴, 고른 언어, 그림을 그린 도구 응답이 서버가 준 그대로 들어 있습니다. 그래서 서버도 네트워크도 없이 아무 브라우저에서나 열리고, 라이브 페이지를 그리는 코드가 그대로 그립니다. 내보내려고 따로 그리지 않기 때문에 HEURISTIC 선은 그 표시 그대로이고, 잘린 목록은 잘렸다고 나옵니다.
머리글 아래 띠가 파일이 무엇인지부터 알려 줍니다. 이 파일이 답하는 질문 하나(그림, 시작점, 모드, 깊이, 행 제한), 언제 어느 팩에서 내보냈는지, 이 답의 신뢰 수준과 한계와 잘린 목록의 개수입니다. 각각이 무엇인지는 라이브 화면처럼 그림 옆 근거 레일에 있습니다. 파일은 밝은 테마로 열리고, 테마 전환은 그대로 됩니다.
파일은 내보낼 때의 질문 하나에만 답합니다. 다른 질문이 되는 조작(시작점, 모드, 깊이)은 꺼져 있고, 목록과 다른 자리는 없습니다. 소스 창을 포함해 서버에 물어야 하는 것은 추측하지 않고 파일에 없다고 말합니다. chain 과 by hop 은 같은 답을 두 가지로 그린 것이라 그대로 됩니다. 라이브 화면에서 줄여 보이는 이름은 파일에서 줄을 바꿔 다 보여 줍니다. 인쇄한 종이에는 마우스를 올려 볼 수 없기 때문입니다.
SVG 와 PNG 는 같은 답을 그림 한 장으로 저장합니다. 슬라이드나 문서에
넣을 때 씁니다. SVG 는 서버가 답을 보고 그립니다. 이름, 너비, 순서, 접기는 라이브
레인과 같습니다. 노드마다 등급 배지를 단 행, 링크마다 등급에 맞는 선, 잘린
레인에서 빠진 행 수, 위쪽의 질문과 신뢰 문장, 아래쪽의 한계 전문이 들어갑니다.
출발 행이 잘린 목록에 있는 링크는 그리지 않고, 몇 개를 안 그렸는지 그림에
적습니다. PNG 는 그 SVG 를 브라우저가 두 배 크기로 그린 것이라 설치할 것이
없습니다. 셸에서는 cascade export --format svg 가 SVG 를 씁니다.
그림이 주장이면 소스는 증거입니다. 소스 창 하나가 머리글 아래 오른쪽에 붙어서
그것을 보여 줍니다. 예전에 저마다 작은 코드 상자를 그리던 곳은 이제 모두 이
창을 엽니다. Trace 에서 누른 행의 카드, Details 카드의 Source 버튼, 그래프
노드 카드의 같은 버튼, Coupling 의 statement, Transactions 의 경계입니다.
GET /api/source 로 디스크의 파일을 읽습니다. 그래서 팩에 넣어 둔 사본이 아니라
지금 그 자리에 있는 내용이 보입니다. 파일은 pack 이 분석했다고 기록한 루트 아래에서만
엽니다. 프로젝트 루트와 이번 실행이 읽은 모든 루트(Java 루트, 매퍼 디렉터리,
프런트엔드 루트, TypeScript 루트, 템플릿 루트, DDL 파일)입니다. 그래서 프로젝트 옆에서
분석한 프런트엔드(--web-src ../mall-admin-web)도 열립니다. 그 밖의 경로, .. 로
쓰거나 링크로 닿는 경로는 거부하고, 창이 이유를 말합니다. 프런트엔드만 부르고 분석한
어떤 코드도 받지 않는 라우트는, 여기서 받는 핸들러가 없다고 말하고 아무것도 열지
않습니다.
파일 자신의 줄 번호를 달고, 답이 가리키는 줄에 막대와 색을 칠하고, 위로 세 줄의
맥락을 두고 그 위치로 스크롤합니다. 헤더에는 노드, 경로와 줄(누르면
path:line 을 복사), 언어, 범위가 나옵니다. snippet 은 메서드나 statement
만 보여 주고, whole file 은 파일 전체를 가져오면서 같은 줄 표시를
유지합니다. 키보드로는 f 가 같은 일을 하고, j/k 와 방향키가 한 줄씩
스크롤하고, Escape 가 닫습니다. Open in editor 는 옆의 선택 상자가 고른
편집기에서 그 줄로 파일을 엽니다. vscode://file/<path>:<line>:1 이나
idea://open?file=<path>&line=<line> 을 씁니다. none 이면 버튼이 절대 경로를
복사합니다. 창의 왼쪽 가장자리를 끌어 420px 부터 창 너비의 90% 까지 조절할 수
있고, 너비와 편집기는 기억됩니다.
뒤를 가리는 막은 일부러 두지 않았습니다. IDE 의 미리보기처럼 동작합니다. 요청할 때만 열리고, 그 뒤로는 아래에서 계속 행을 고르는 동안 그 자리에서 따라옵니다. 한 번 닫으면 다시 열 때까지 닫혀 있습니다.
Trace, Graph, Table links 는 좁힐 수 있는 뷰입니다. Graph 와 Table links 는 툴바 오른쪽 끝에 Show all 이 있고, Trace 는 Options 안에 Start over: nothing picked(처음 상태로 (선택 해제))가 있습니다. Show all 은 뷰가 이미 처음 상태면 흐리게 보입니다. 그래서 버튼만 봐도 무언가 좁혀져 있는지 알 수 있습니다. 누르면 필터와 대상을 지우고, Trace 목록을 테이블로 되돌리고 그림을 지웁니다. Graph 지도는 접고 화면에 맞추고, 테이블 강조를 없애고, 소스 창을 닫습니다.
Escape 는 뷰 안 어디서든 같은 일을 합니다. 커서가 입력창에 있으면 두 번 눌러야 합니다. 첫 번째는 입력창을 비우고, 두 번째가 뷰를 되돌립니다. 이미 보고 있는 자리나, 둘째 줄의 지금 뷰를 다시 눌러도 초기화됩니다.
팩을 그림으로 보는 자리이고, 뷰마다 그림이 하나입니다. 둘째 줄로 뷰를 바꿉니다.
tab=graph, tab=erd, tab=coupling, tab=tx 링크는 해당 뷰를 엽니다.
Graph 뷰는 프로젝트 전체 지도로 열립니다. 가만히 있을 때는 API 그룹과 테이블만 그립니다. 그룹과 테이블을 잇는 선 하나가, 그 그룹에서 그 테이블을 건드리는 모든 엔드포인트를 대신합니다. 그룹을 누르면 엔드포인트가 위성처럼 펼쳐지고, 개수 줄이 아직 몇 개가 접혀 있는지 알려 줍니다. Find 는 찾은 것이 속한 그룹을 펼칩니다.
읽기 쉽게 하는 규칙이 세 가지 있습니다. 노드 반지름은 차수의 제곱근을 따릅니다. 선이 하나뿐인 테이블도 보이고, 허브는 바로 눈에 띕니다. 라벨은 가장 바쁜 노드, 모든 그룹, 불이 켜진 것에 붙고, 1.6배 넘게 확대하면 모두에 붙습니다. 체인에 불이 들어오기 전까지 선은 조용합니다.
선 색은 엔드포인트가 테이블에 무엇을 하는지입니다. 읽기는 파랑, 쓰기와 삭제는 빨강입니다. 굵기는 그 선을 만드는 statement 수이고, 2D 의 점선은 확인하지 못한 체인입니다. 칩은 한 종류를 통째로 숨기지만 개수는 남깁니다. statement 칩은 엔진에 SQL 계층을 다시 묻습니다. 그 계층을 빼고 물었을 때는 엔진이 statement 를 하나도 보내지 않았기 때문입니다. 노드 카드에는 그 종류가 가진 방향마다 Trace 로 가는 버튼이 있습니다.
요청이 이 프로젝트를 떠나는 곳에서도 지도는 이어집니다. 등록된 다른 프로젝트가 서빙하는 라우트를 부르는 엔드포인트에는, 그 프로젝트의 라우트 노드로 가는 선이 붙습니다. 그 아래에 그 라우트가 저쪽에서 닿는 것이 달립니다. 그 노드들은 자기 종류의 채움색을 그대로 씁니다. 그래서 테이블은 여전히 테이블로 보입니다. 대신 자기 프로젝트 색의 테두리를 두르고 라벨에 프로젝트 이름을 답니다. 연결된 프로젝트마다 노드 하나가 뼈대가 되고, 라우트들이 거기 걸립니다. 무리마다 오른쪽의 자기 띠에서 시작합니다. 그래서 그림이 “우리 지도와, 저쪽에서 우리가 부르는 것” 으로 읽히고, 두 시스템이 뒤섞여 보이지 않습니다. 합치는 것은 없습니다. 이 프로젝트의 요청이 닿는 것만 그리고, 두 프로젝트의 테이블을 잇는 선은 없습니다. 칩 하나로 이 층 전체를 끌 수 있고, 그래도 개수 줄은 바깥에서 온 것이 몇 개인지 말합니다. 그런 노드를 누르면 할 수 있는 일은 하나, 그 노드를 가진 프로젝트에서 여는 것입니다. 다른 버튼은 모두 화면의 팩에 없는 것을 묻게 되기 때문입니다.
고급 보기(advanced view). 3D 와 선을 따라 움직이는 점은 Advanced view
스위치 하나 뒤에 있습니다. 기본은 꺼짐이고, 선택은 기억됩니다
(cascade.viewer.graph.advanced). 꺼져 있으면 2D/3D 를 마지막에 뭐로 뒀든 지도는
평평한 2D 캔버스이고, 불이 켜진 체인을 포함해 아무것도 움직이지 않습니다. 답은
어느 쪽이든 같고, 그리는 방식만 다릅니다. 켜면 2D/3D 스위치와 Flow on 이
툴바에 돌아옵니다. 가만히 있는 지도는 Flow on 을 누르기 전까지 움직이지 않고,
불을 켠 노드는 혼자 흐르고, 방향 있는 링크가 2500 개를 넘으면 불이 켜진
체인만 움직입니다. 툴바가 그렇게 알려 줍니다. 시스템에 동작 줄이기를 설정한
사람에게는 어느 쪽이든 움직임이 없습니다. Trace 레인의 점은 이 스위치와
상관없고, Trace 의 Options 에 따로 있습니다.
노드를 더블클릭하면 Around <node> 입니다. 가운데에 그 노드, 링 1 에 그 노드에 닿는 것, 링 2 에 다시 그것들에 닿는 것이 놓입니다. 가운데가 테이블이나 statement 이면 컬럼을 처음에 숨깁니다. 그러지 않으면 그림 대부분이 컬럼이 되기 때문입니다.
스키마 전체를, 매퍼 SQL 이 테이블 사이에 만드는 조인으로 배치합니다. 예전
이름은 ERD 였습니다. 외래 키는 읽지 않기 때문에, 그리는 것 그대로 이름을
바꿨습니다. 여기의 관계는 어떤 statement 가 실제로 하는 조인입니다. 테이블
크기는 지도와 같은 제곱근 규칙으로 관계 수를 따르고, 라벨도 그에 맞춰
커집니다. 선이 굵을수록 그 조인을 하는 statement 가 많습니다. 테이블의 계열은 지도가
읽는 계열입니다(erd 가 테이블마다 familyRule 과 함께 싣습니다). 이름의 첫 밑줄 앞
접두사가 아닙니다. 그래서 밑줄이 없는 ghostfolio 의 Prisma 모델도 계열로 묶이고, 한
테이블이 여기와 지도에서 다른 계열에 드는 일이 없습니다. 어두운 테마에서는 가장 큰 계열
다섯 개가 옅은 색(종류 색을 바탕 쪽으로 끌어당긴 색)을 하나씩 받고, 나머지는 잉크
그대로입니다. 범례는 모든 계열을 개수와 함께 적고, 색이 있는 계열에는 견본을 붙입니다.
밝은 테마는 잉크 그대로입니다. 어떤 SQL 도 조인하지 않는 테이블은
버리지 않고, 지도 아래 띠에 그렇다고 적어서 놓습니다. 테이블을 누르면 그림 옆에
상세 패널이 열립니다. 패널의 버튼으로 Trace 상세를 열거나 이 테이블을 쓰는 곳을
따라갈 수 있습니다.
상세 패널의 왼쪽 경계를 드래그하면 폭이 바뀝니다. 경계에 키보드 초점을 두고 화살표 키로 조절할 수도 있습니다. 넓히기로 공간을 늘리고 너비 초기화로 되돌립니다. 조절한 폭은 브라우저에 기억됩니다. 컬럼은 이름·타입·설명을 한 행에 나란히 표시합니다. 이름과 타입을 한 글자씩 끊지 않으며, 긴 행은 가로로 스크롤해 읽을 수 있습니다. 검색은 세 항목을 모두 찾고 서버에 다시 요청하지 않습니다. 조인 근거는 컬럼 위에서 펼쳐 봅니다. 폭을 바꿔도 선택한 테이블과 그래프 배치는 유지됩니다. 화면이 좁으면 상세 패널이 그림 아래에서 전체 폭을 씁니다.
연결된 프로젝트는 오른쪽에 테두리를 두른 무리로 놓입니다. 여기서 간 요청이 닿는 테이블만, 그리고 그 프로젝트 자신의 조인만 담습니다. 작은 라우트 표식에서 그 라우트가 닿는 테이블마다 점선이 나갑니다. 범례가 점선의 뜻을 알려 줍니다. 외래 키가 아니라 HTTP 호출로 닿았다는 뜻입니다. 이 도면의 어떤 관계도 두 프로젝트를 잇지 않습니다. 두 서비스는 외래 키를 나누지 않기 때문입니다. 범례의 스위치는 이 프로젝트에 자기 테이블이 없으면 켜진 채로 시작합니다. 안 그러면 도면이 비는데, 요청은 실제로 어딘가에서 끝나기 때문입니다. 자기 스키마가 있으면 꺼진 채로 시작합니다. 어느 쪽이든 선택은 기억됩니다. 저쪽 테이블을 누르면 그 프로젝트의 관계가 보이고, 컬럼도 그 프로젝트에 묻습니다. 화면의 팩에는 묻지 않습니다.
두 API 그룹은 서로 호출하지 않고도 서로에게 기댈 수 있습니다. 한쪽이 컬럼을 쓰고, 다른 쪽이 그 컬럼을 읽는 경우입니다. 행렬이 그런 쌍을 보여 줍니다. 세로가 쓰는 쪽, 가로가 읽는 쪽입니다. 칸을 누르면 두 그룹이 나눠 쓰는 컬럼이나 테이블과, 그것을 나르는 statement 가 나옵니다. statement 는 Trace 에서 소스와 함께 열리고, 나눠 쓰는 컬럼이나 테이블은 위로 걷기로 열립니다.
모든 @Transactional 메서드와, 그 메서드로 커밋 하나가 건드릴 수 있는
범위입니다. Trace 에서 라우트나 메서드의 Details 를 보면 걷기가 지나는
트랜잭션 목록이 나오고, 각각 이 뷰로 옵니다. 여기서 경계를 보면 Trace 의 양쪽
방향으로 돌아가는 버튼이 있습니다.
이 자리는 화면의 팩을 같은 프로젝트의 이전 빌드와 비교합니다. 이전 빌드가
있을 때만 나옵니다. 인증된 analyze 는 교체되는 팩을 .cascade/history/ 에 최근
다섯 개까지 남깁니다. 커밋과 빌드 시각으로 빌드를 고르면, 그것을 기준으로
pack_diff 를 묻습니다(무엇을 비교하는지는 cli.md 에
있습니다). 다른 프로젝트는 후보로 내놓지 않습니다. 서로 다른 코드베이스는 모든
것이 다르기 때문입니다. 기록에 없는 커밋과 비교하려면
cascade diff --base-commit <rev> 가 저장소에서 기준 팩을 만듭니다. 비교할 것이 없는
프로젝트에 예전 Compare 링크로 들어오면, 화면에 있는 동안 자리를 표시하고 “이
프로젝트에는 아직 남겨 둔 이전 빌드가 없습니다” 라고 말하며, Draw 는 꺼 둡니다.
떠나면 자리는 다시 숨습니다.
이 자리가 그리는 것은 위에서부터 읽는 보고서 한 편입니다. 제목에는 프로젝트, 기준과 변경(커밋, 빌드 시각, 팩 digest, 커밋 안 된 변경이 있었는지)이 나옵니다. 다음은 분석 조건입니다. 두 팩을 같은 방식으로 분석했는지, 다르거나 기록되지 않은 조건이 무엇인지 알려 줍니다. 개수보다 이것을 먼저 읽으십시오. 분석이 바뀌지 않았을 때만 차이가 곧 코드 변경입니다. 먼저 읽을 것이 있으면 “Read before the counts” 패널이 따라옵니다. 엔진이 밝힌 한계, 페이지가 요청한 한도(200 행)에서 잘린 목록(제목의 합계는 항상 전체입니다), 그리고 이 페이지보다 오래된 서버라면 노드 속성과 엣지 근거를 아예 비교하지 않았다는 사실입니다. 그다음 요약이 네 묶음으로 나옵니다. 내용(추가, 삭제, 속성이 바뀐 노드), 관계(추가, 삭제된 엣지), 근거(등급이 바뀌었거나 근거가 다르게 읽히는 엣지), 소스 위치만(소스에서 자리만 옮겼고 뜻은 같은 노드와 엣지)입니다. 그 아래 종류별, 엣지 유형별 줄은 함께 적힌 범례로 읽습니다.
이어서 변경 위의 엔드포인트와 화면이 나옵니다. 두 팩에서 읽습니다. 추가는 변경 쪽에서, 삭제는 기준 쪽에서, 내용이나 등급 변경은 양쪽 모두에서 그 위를 봅니다. 소스 위치만 옮긴 것은 여기 더하지 않습니다. 변경 쪽에 아직 있는 끝에는 Trace 를 아래로 걷기로 여는 버튼이 붙습니다. 이전 빌드에만 있던 끝에는 “earlier build only” 가 붙고, 아무것도 열지 않습니다. 화면의 팩에는 따라갈 체인이 없기 때문입니다. “What to check” 는 개수와 표시만으로 만든 번호 목록입니다. 어느 패널을 먼저 읽을지, 끝을 몇 개 열어 볼지, 속성과 근거 기록을 몇 건 값 단위로 읽을지를 말합니다. 점수나 판정은 없습니다.
그다음이 기록입니다. 바뀐 속성과 바뀐 근거는 필드마다 전과 후의 값을
보여 줍니다. 값은 항상 글자로 보여 주고 마크업으로 해석하지 않습니다.
문자열에는 따옴표를 붙여서 "null" 과 null 이 구별됩니다. 다른 팩에 기록이
없는 값은 “(not recorded)” 로 적고, 긴 값이나 줄바꿈이 있는 값은 접어 둡니다.
소스 위치(파일, 줄, 선언 위치)를 뺀 모든 속성을 비교합니다. 그 뒤로 등급이 바뀐
엣지, 추가되거나 삭제된 노드와 엣지(두 팩 사이에 축이 바뀐 삭제 노드는 그
사실을 적습니다)가 나오고, 마지막에 자리만 옮긴 것이 나옵니다. 파일이나 줄만
다르고 나머지는 같은 노드와 엣지를 따로 모아 설명을 붙입니다. 파일 diff 를
그래프 변경으로 잘못 읽지 않게 하려는 것입니다. 보고서 위 거르기 상자에 글자를
넣으면 그 글자가 없는 행은 숨고, 합계는 그대로입니다. 나열할 것이 없으면 어느
경우인지 보고서가 말합니다. 같은 팩, 같은 그래프, 소스 위치만 옮김, 또는 id
와 등급만 비교한 오래된 서버입니다.
제목 아래 버튼이 둘 있습니다. 인쇄는 접힌 곳을 모두 펼치고, 머리글과 자리 줄과
조작 부분을 뺀 보고서를 화면 테마와 상관없이 종이 위의 잉크로 찍습니다. 신뢰
수준, 한계, 잘린 목록이 있는 근거 레일은 보고서 뒤에 인쇄됩니다. Save as
Markdown 은 같은 보고서를 화면에 이미 있는 답만으로 만들어
cascade-compare-<project>-<base commit>-<head commit>.md 파일로 씁니다. 기준과
변경의 커밋 전체, 빌드 시각, digest, 신뢰 수준, 분석 조건, 한계, 요약, 끝,
확인할 것, 모든 목록, 그리고 파일에 일부만 담은 목록의 이름을 적는 “Rows not
shown” 절이 들어갑니다. 파일의 모든 값은 코드 스팬이나 코드 블록 안에 있고,
그 울타리는 값 안의 어떤 백틱 연속보다 하나 더 깁니다. 그래서 주석 속의
파이프, 백틱, 태그가 글자로 남습니다. 화면에서는 보고서 옆 근거 레일에 신뢰
수준, 한계, 잘린 목록이 모두 있습니다.
분석이 무엇을 봤고 무엇을 못 봤는지를 한 자리에 모아 위에서부터 읽습니다. 예전에는
Overview 옆 열, 머리글 칩, 따로 떨어진 Rules 탭에 흩어져 있었습니다. 모든 줄은
Start 를 그리는 overview 답 하나에서 옵니다. 그래서 두 자리가 다른 말을 할 수
없습니다. 첫 뷰는 Scope and gaps(범위와 빈 곳), 둘째 뷰는 Rules 입니다.
unknown 이면 “작업 트리와 비교하지 않음”), 언제, 어느 커밋에서, 어떤
digest 로 만들었는지를 붙입니다.jpa-axis-not-shipped)은 적지 않습니다. 축 줄이 그
브리지를 빼는 것과 같은 이유입니다.TS_PREFIX_EXCLUDE_UNREAD
는 “읽지 못한 설정: API 접두사 제외 목록”). 그 안에서는 행이 하는 말마다 한
줄입니다(이름, 경로, 숫자를 빼고 묶으므로, 서로 다른 실패는 두 줄로 남습니다). 앞의
몇 개는 전문을 보여 주고, 모두 몇 개인지와 몇 가지로 다른지, 그리고 접힘 밖에 할
일을 적습니다. 여럿을 대표하는 진단(스키마 리더의 진단은 종류마다 하나)은 대표하는
개수만큼 세고, 첫 문장들을 보여 줍니다. 엔진이 낼 수 있는 진단 종류와 빈 곳 종류는
모두 영어와 한국어 제목이 있습니다. test/i18n.test.mjs 가 엔진 소스에서 종류를 읽어
내므로, 제목 없이 종류를 더하면 테스트가 실패합니다. 축의 메모도 같은 방식으로
접습니다. 캐시 디렉터리가 옮겨졌을 때 같은 종류의 진단 866 줄이 이 목록을 채운 적이
있습니다. 지금은 개수가 붙은 한 줄입니다.basis.builtAt)를 적습니다. 두 도구를 묻는 Trace 의 Details 는 두 번째 답의
한계를 같은 상자에 합칩니다.한계는 엔진이 쓴 두 가지로 되어 있습니다. scope(어느 답, 또는 pack 의 어느 부분에 관한 것인지)와 문장입니다. 페이지는 문장 위에 제목을 붙일 뿐, 문장을 고쳐 쓰지 않습니다. 제목은 다음 순서로 정합니다.
diagnostic:<KIND> 는 그 진단의 제목입니다.axis:<name> 은 축이 자기에 대해 말하는 것을 Start 와 Analysis status 가 쓰는 말로
적습니다. “JPA 매핑: 수집 안 함”, “프론트엔드 호출: 일부만 읽음”, 모은 축의 메모라면
“코드: 못 본 부분이 있음” 입니다.규칙은 src/viewer/limit_titles.mjs 에 있고, 카탈로그처럼
GET /viewer/lib/limit_titles.js 로 페이지에 갑니다. test/i18n.test.mjs 는 엔진이 낼
수 있는 한계 scope 를 src/ 에서 모두 읽어 내고, 영어나 한국어 제목이 없는 것이 있으면
실패합니다. 주제의 시작 문구가 src/ 의 어떤 문장도 더는 시작하지 않을 때도 실패합니다.
그래서 문장을 고쳐 쓰면 제목이 조용히 사라지지 않고 테스트가 빨갛게 됩니다. 페이지가
모르는 scope(더 새 엔진이 쓴 것)는 전처럼 scope 와 문장의 첫머리로 보여 줍니다. 한
진단이 쏟아지면 그 종류의 제목을 붙이고, 코드는 접힘 안의 문장 옆으로 갑니다.
답 하나를 바꾸는 한계는 Trace 에서 그 답 옆에도 나옵니다(What limited this answer, What this walk left out). 이 자리는 전체 목록을 모은 곳이지, 유일한 사본이 아닙니다.
다른 곳의 링크(Start 의 축 칩이나 빈 곳, 비율 카드의 한계 줄, 머리글에서 not collected 로 나온 개수)는 이 자리의 해당 줄을 펼치고 표시한 채로 엽니다.
연결된 프로젝트. 이 프로젝트가 자기가 서빙하지 않는 라우트를 부르면, 누가 그것을 받는지 패널이 보여 줍니다. 등록된 프로젝트마다 한 줄이고, 라우트와 여기서 그 호출을 하는 메서드 수가 나옵니다. 라우트는 다섯 개까지 보여 준 뒤 개수로 줄입니다. 줄을 누르면 그 프로젝트로 갑니다. 마지막 줄에는 등록된 어느 프로젝트도 서빙하지 않는 호출이 모입니다. 할 수 있는 일은 하나, 그것을 서빙하는 프로젝트를 등록하는 것이고, 그 안내가 같이 적혀 있습니다. 아무도 부르지 않는 프로젝트에는 이 패널이 없습니다. 이 팩이 하지도 않는 일에 대해 0 이라고 말하는 패널은 소음이기 때문입니다.
엔진이 프레임워크에 대해 아는 것은 룰 팩(rule pack)에 있습니다(rules.md).
이 뷰는 rules 도구로 룰을 읽습니다. MCP 로 모델이 받는 답과 같습니다
(mcp.md). 여기에 적용되는 룰, 즉 이 프로젝트에서 무언가를 만든
룰부터, 많이 만든 순서로 엽니다. 나머지는 한 번 누르면 나옵니다(applies here 또는
all). 적용되는 것이 하나도 없어도 그렇게 엽니다. 어떤 룰 팩도 연결이나 노드를
만들지 않은 프로젝트는 한 줄로 그렇다고 말하고(“이 프로젝트에서는 규칙 팩의 어떤
규칙도 연결이나 노드를 만들지 않았습니다. 규칙 팩이 다루는 프레임워크를 쓰지 않는
프로젝트라면 정상입니다. …”), 다른 스택의 룰을 열어 보이지 않습니다.
예전에는 NestJS 프로젝트를 열어도 거기서 아무것도 하지 않은 MyBatis-Plus-Join
룰이 먼저 나왔습니다.
검색 상자와 종류, 레인 필터는 답이 이미 가진 행을 거를 뿐, 서버에 다시 묻지
않습니다. 룰을 고르면 설명, 왜 있는지, params, 라이브러리 룰이 기대는 것과 그것이
적힌 곳, 그 룰의 링크가 가질 수 있는 가장 강한 등급, 그리고 여기서 만든 것이
나옵니다. 만든 것은 등급별 링크 수와 종류별 노드 수입니다. 그래서 ghostfolio 의
nestjs.routes 는 EXACT 1, HEURISTIC 117 로 나옵니다. 링크는 열 개씩 나오고,
양 끝 노드는 Trace 로 엽니다. 분류만 하는 종류의 룰이고 팩 어디에도 그 이름이
없으면, 셀 것이 없다고 말합니다. 쓰이지 않았다는 뜻과는 다릅니다. 예시마다 맞는지
틀리는지가 붙습니다. GET /api/rules/examples 가 cascade rules test 와 같은
방식으로 예시를 돌린 결과입니다.
서버는 좁히지 않으면 ~/.cascade/registry.json 의 모든 프로젝트를 서빙합니다.
페이지는 한 번에 한 프로젝트만 보여 줍니다. 어느 팩에서 온 답인지 말하지
않는 답은 답이 없는 것보다 나쁘기 때문입니다.
<select>, 딱 하나면
고정된 이름입니다. 고를 것이 없으니까요. 하나도 없으면 그렇다고 적고
cascade init 과 cascade analyze 를 알려 줍니다.Around <node>, Table links
렌더러를 해체하고, Graph 는 프로젝트 전체 지도로 돌아가고, Start 와 Analysis
status 가 쓰는 첫 답을 다시 묻습니다. 프로젝트를 건너 재사용하는 것은
없습니다. Trace 의 모드와 깊이는 남습니다. 팩이 아니라 사용자의 설정이기
때문입니다.GET /api/projects)은 지연 로딩입니다. 레지스트리만
읽고 다른 것은 읽지 않습니다. 프로젝트의 meta(digest, 빌드 시각, 레인, 축,
최신성)는 그 프로젝트가 실제로 무언가에 답하기 전까지 null 입니다. 여기서
null 은 “아직 안 읽음” 이지 “이 팩은 할 말이 없음” 이 아닙니다.페이지는 지금 보여 주는 것을 해시에 적습니다.
http://127.0.0.1:4319/#p=<project>&tab=<start|trace|graph|erd|coupling|tx|compare|status|rules>
[&pick=<kind>:<id>][&dir=<down|up|detail>&mode=<mode>&depth=<n>][&src=<kind>:<id>]
tab 은 뷰 이름입니다. Trace 에서는 링크가 질문 전체를 담습니다. 대상, 방향,
모드, 깊이(all 이면 빠짐)입니다. 그래서 링크를 나누면 같은 질문을 하게
됩니다. 새로 고침하면 같은 프로젝트, 같은 뷰, 같은 질문, 같은 소스 창으로
돌아옵니다. 그 링크를 다른 사람에게 보낼 수도 있습니다.
Trace 에서 새 대상이나 새 방향을 고르면 히스토리 항목이 하나 쌓입니다. Graph 나 Table links 에서 고른 것, 소스 창을 연 것도 그렇습니다. 그래서 브라우저의 뒤로 가기가 물었던 질문을 하나씩 되짚고, 메모리에 남은 답은 서버에 다시 묻지 않고 그립니다. 답은 그 답을 만든 조건 전체(프로젝트, 대상, 방향, 모드, 깊이, 레인당 행 수) 아래에 기억합니다. 그래서 다른 모드로 물은 답을 이 답으로 돌려주는 일이 없습니다. 같은 질문의 모드나 깊이 변경, 뷰 변경, 프로젝트 변경은 항목을 쌓지 않고 바꿉니다. 하나씩 되돌리고 싶은 단계가 아니기 때문입니다.
자리를 나누기 전에 만든 링크도 원래 가던 곳으로 갑니다. tab=overview 는
Start 를 엽니다. tab=explore, tab=flow, tab=impact 는 같은 대상으로 Trace
를 열고, 각각 Details, 아래로 걷기, 위로 걷기로 읽습니다. tab=graph,
tab=erd, tab=coupling, tab=tx 는 Structure 의 해당 뷰를, tab=structure
는 그 Graph 를 엽니다. tab=rules 는 Analysis status 의 Rules 뷰를 엽니다.
프로젝트, 고른 대상, 소스 창도 같이 따라오고, 그 뒤 페이지가 새 형식으로 다시
적습니다.
처음 열 때 프로젝트는 이 순서로 정합니다. 해시, CLI 가 시작할 때 출력하는
?project= 쿼리, 그리고 이 브라우저가 지난번에 고른 값(localStorage)입니다.
서버가 서빙하지 않는 프로젝트는 보내지 않고 무시합니다. 그래서 오류 대신 서빙
목록의 첫 프로젝트가 열립니다.
모르는 프로젝트는 404, 여러 프로젝트를 서빙하는 서버에서 프로젝트 이름을 대지
않으면 409 이고, 둘 다 JSON 입니다. 페이지는 그것을 물어본 창 안의 배너로
그리고, 빈 패널로 두지 않습니다. 메시지는 엔진이 쓴 문장 그대로입니다.
머리글의 토글이 인터페이스 언어를 바꿉니다. 기본은 영어이고, 선택은
localStorage 에 기억됩니다. 바꾸면 크롬(chrome, 페이지가 스스로 쓴 말)만 다시
그리고 서버에는 아무것도 묻지 않습니다. 화면에 이미 있는 답은 그대로입니다.
엔진의 말은 인터페이스 언어에 따라 바뀌지 않기 때문입니다. Start, 지도,
Analysis status, Trace 의 레인은 메모리에 있는 답으로 다시 그립니다.
번역하는 것은 크롬, 즉 페이지가 스스로 쓴 모든 말입니다.
번역하지 않는 것은 엔진이 한 모든 말입니다.
EXACT, SOUND_SET, HEURISTIC, RUNTIME_ONLY, UNRESOLVED)limits, 잘림 안내, empty 이유(not-shipped, not-in-this-axis). 한계에는 페이지
말로 된 제목이 붙지만, 문장은 엔진의 것 그대로 한 번 누르면 그 아래에 있습니다이 경계가 정직성 계약입니다. 번역한 등급은 이 프로젝트가 지어낸 등급입니다. 어떤 독자도 그것을 엔진의 답과 대조할 수 없고, 두 뷰어가 그 뜻에 합의할 수도 없습니다. 엔진의 말은 증거이고, 증거는 바꿔 쓰지 않고 인용합니다. 페이지가 그런 말을 꺼내야 할 때는 뜻을 쉬운 말로 한 번 설명하고 그대로 씁니다. 등급마다 설명은 하나이고, 레인 범례, 모든 배지의 툴팁, SVG 범례, What this walk left out 이 같은 설명을 씁니다.
src/viewer/i18n.mjs 에 조회 함수(makeT, interpolate, richText)와
영어 카탈로그 VIEWER_STRINGS.en 이 있습니다. 페이지는 import 를 할 수
없습니다. 그래서 로컬 서버가 이 모듈을 export 키워드만 떼고
GET /viewer/lib/i18n.js 로 내려 줍니다. 파일이 하나라 어긋날 사본이 없고,
test/i18n.test.mjs 가 내려간 것이 그 모듈인지 확인합니다.src/viewer/limit_titles.mjs 는 한계의 제목을 카탈로그의 어느 키에서 가져올지
정합니다. 페이지는 카탈로그와 같은 방식으로, export 키워드를 뗀 것을
GET /viewer/lib/limit_titles.js 로 받습니다.viewer/i18n/<lang>.json 이 다른 언어마다 하나씩 있습니다. /vendor 처럼 허용
목록 디렉터리에서 GET /i18n/<lang>.json 으로 내려갑니다. .json 만, 그
디렉터리에서만 나가고, 빠져나가려는 요청은 모두 404 입니다.번역을 페이지 안이 아니라 파일로 둔 데는 이유가 있습니다. 영어 전용 게이트
(test/gates.test.mjs)가 src, bin, adapters, scripts, viewer/index.html,
viewer/js 에서 영어가 아닌 글을 찾습니다. viewer/i18n 은 유일하게 빠지는
경로입니다. 영어가 아닌 글이 있어야 하는 단 하나의 자리이기 때문입니다.
t(key, params) 는 예외를 던지지 않습니다. 고른 언어에 없는 키는 영어로
대신합니다. 어느 카탈로그에도 없는 키는 키 이름 그대로 그리고 t.missing 에
기록합니다. 빈 곳이 조용히 비지 않고 화면에 보이고, 테스트에서 셀 수 있게 하려는
것입니다.
이 페이지의 모든 문장은 한 사람의 말투입니다. 경험 있는 개발자가, Spring 과
SQL 은 알지만 이 도구는 처음 보는 동료에게 화면을 설명하는 말투입니다. 실제로는
이렇습니다. 무엇인지보다 무엇에 쓰는지를 먼저 말합니다. 읽는 사람의 명사(API,
컨트롤러, 서비스, 매퍼, SQL, 테이블, 컬럼)를 씁니다. 한 문장에 한 생각만 담고,
숫자는 사실로 말합니다. test/i18n_voice.test.mjs 가 기계로 확인할 수 있는
부분을 지킵니다. 카탈로그 문자열에 em dash 와 가운뎃점을 쓰지 않습니다. 리드는
90 자 이하, 문단은 네 문장 이하입니다. 한국어 문장은 모두 메모체가 아니라
합니다체로 끝납니다. 라틴 문자 단어, 숫자, 닫는 괄호와 그 뒤의 조사 사이에는
띄어쓰기를 하지 않습니다(SQL까지, API에). 이 검사는 자리표시자를 채운
문자열에 합니다.
viewer/i18n/ko.json 을 viewer/i18n/<lang>.json 으로 복사하고 값을
번역합니다. {placeholder} 는 모두 남겨 둡니다. test/i18n.test.mjs 가
번역에서 빠진 것이 없는지 확인합니다. {message} 는 엔진의 문장이 들어가는
자리이기 때문입니다.lang.label 에 그 언어 자신의 말로 이름을 넣습니다. 토글은 각 언어를 그
언어로 보여 주기 때문입니다.viewer/js/00_state.js 의 LANGS 에 코드를 추가합니다.키 집합은 강제입니다. 번역은 영어 키를 정확히 그대로 가져야 합니다. 적으면 무언가가 조용히 영어로 떨어지고, 많으면 낡은 키가 남기 때문입니다.
concepts.md 가 등급, 부분 팩, 빈 답의 뜻을 설명합니다. mcp.md 는 같은 질문 창구를 사람이 아니라 모델을 위해 설명합니다.