04. 아키텍처
1. 최상위 파이프라인
jam-lite:
┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ ┌──────────┐
│ Discover │ → │ Lang Frontend│ → │ Analyze │ → │ Score │ → │ Report │
│ 파일 수집 │ │ Go AST/lexer │ │ 메트릭 실행 │ │ 점수 합성 │ │ md/csv │
└──────────┘ └──────────────┘ └──────────────┘ └───────────┘ └──────────┘
│ │ │
.gitignore internal/lang analyzer 인터페이스
jam.yaml 경량 렉서 파일/프로젝트 단위
생성파일 감지 Go parser
jam-full:
jam-lite ─┬─ test-cli run → test/report.json
└─ security-cli scan → security/security-report.json
↓
jam-full-report.md + jam-full.json
assurance (점수와 독립):
jam-run + detail CSV + security report + policy/review → security verdict
security verdict + SBOM/scan/ticket evidence + control plan → control evidence index
단계별 책임
- Discover: 루트부터 파일 순회. 루트와 하위 디렉터리의
.gitignore,jam.yaml제외 규칙, 기본 패키지/빌드/가상환경 디렉터리(node_modules/,.next/,.venv/,venv/,env/,target/,dist/,build/,coverage/등), 자동 생성 파일 휴리스틱(Code generated by,*.min.js, lockfile)을 적용한다. 언어 감지(확장자 + 셔뱅). 결정적 순서로 정렬. - Lang Frontend: Go는 표준 라이브러리
go/parser/go/ast를 사용한다. TS/JS, Python, Rust, Java, C#, C/C++은internal/lang경량 렉서 프론트엔드가 함수/복잡도/import 정보를 추출한다. - Analyze:
model.Analyzer구현체를 카테고리 순서로 실행한다. 파일 단위 지표(SIZE/CPLX/SEC/RES/HYG)와 프로젝트 단위 지표(ARCH/DUP)를 같은 인터페이스로 다룬다. - Score: 03-scoring-model.md의 jam-lite SQALE 모델 적용.
- Report:
jam-report.md,jam-detail.csv,jam-run.json, 옵션 JSON/SARIF 생성(06-output-spec.md). - Full compose:
--set full이면internal/full이test-cli와security-cli를 호출하고,jam-full-report.md/jam-full.json을 생성한다. - Assurance:
internal/assurance가 scanner finding을 review candidate로 유지한 채 Git 대상·실행 시각·coverage·정책·귀속 가능한 리뷰를 검증한다. 별도 control plan은 원본 증거의 SHA-256과 commit/source hash 상관관계를 기록하며 품질 점수에는 영향을 주지 않는다.
2. 파서 전략 — 핵심 결정
결정: Go AST + 내부 경량 렉서 + 필요 지표의 tree-sitter WASM
| 옵션 | 장점 | 단점 | 판정 |
|---|---|---|---|
smacker/go-tree-sitter (CGo) | 성숙, 빠름 | CGo → 3-OS 크로스 컴파일 시 각 OS용 C 툴체인 필요, 정적 단일 바이너리 어려움 | ✗ |
| tree-sitter 문법을 wasm32-wasi로 빌드 + wazero 런타임 | 순수 Go 실행, CGo 없이 타입 내부 구조/함수 내 taint 추출 | wasm 바이너리 크기, 문법 업데이트 관리 | ✓ 현재 부분 경로 |
| Go AST + 내부 경량 렉서 프론트엔드 | 빌드 불가 프로젝트도 스캔, 단일 바이너리 유지 | 비-Go 경로의 타입 해석 한계, confidence medium 중심 | ✓ 현재 기본 경로 |
| 외부 Language Server 구동 | 정밀 | 외부 프로세스/설치 의존 → "단일 실행파일" 제약 위반 | ✗ (선택적 확장으로만) |
- Go 소스는 표준 라이브러리 AST 경로가 high confidence 기준이다.
- 그 외 언어는 함수/분기/import 추출을 경량 렉서로 수행하고 finding confidence를 medium 중심으로 기록한다.
- 타입 내부 구조가 필요한 ARCH-07, ARCH-08, SIZE-04와 TS/JS SEC-07/08 함수 내 taint 추적은
internal/treesitter의 임베드 WASM 문법을 사용한다. 나머지 함수/import/복잡도 추출은 경량 프론트엔드가 기본이다.
공통 프론트엔드 추상화
언어마다 구문이 다르므로 메트릭 코드가 언어별 lexer를 직접 알지 않도록 internal/lang.Frontend를 둔다.
type Frontend interface {
Languages() []string
Functions(content []byte) []Function
Imports(content []byte) []Import
}
프론트엔드는 init()에서 등록되며, 엔진은 internal/engine에서 언어 패키지를 blank import한다.
3. 패키지 구조
jam/
├── cmd/jam/ # main. CLI 파싱(flag 또는 cobra), 종료 코드
├── internal/
│ ├── discover/ # 파일 수집, gitignore, 생성 파일 감지, 언어 감지
│ ├── lang/ # 경량 언어 프론트엔드(jsts/python/rust/java/csharp/cpp)
│ ├── model/ # Finding, Metric, Location, Severity 등 공용 타입
│ ├── analyze/
│ │ ├── size/ # SIZE-01..04
│ │ ├── cplx/ # CPLX-01..05
│ │ ├── arch/ # import 그래프, Tarjan SCC, fan-in/out
│ │ ├── dup/ # 토큰 정규화, Rabin-Karp 클론 인덱스
│ │ ├── sec/ # 시크릿 엔트로피, 패턴 룰 엔진
│ │ ├── res/ # 자원 쌍 추적
│ │ └── hyg/ # 에러 무시, 데드 코드, SATD
│ ├── score/ # SQALE debt 계산, 가중 합성, 등급
│ ├── report/ # markdown/csv/json/sarif
│ ├── runmeta/ # jam-run.json v2 provenance/integrity 계약
│ ├── assurance/ # security verdict와 control-evidence 계약/게이트
│ ├── full/ # jam-full external CLI orchestration
│ ├── e2ecorpus/ # 16개 fixed-SHA regression manifest 계약
│ ├── labeledbench/ # 독립 정답지 parser와 confusion matrix 평가
│ └── config/ # jam.yaml 로드, 기본값, 임계값 오버라이드
├── tools/
│ ├── e2e-corpus/ # clone/strict scan/exact score·산출물 회귀 gate
│ └── labeled-benchmark/# OWASP label 기반 precision/recall/FPR/F1 gate
└── testdata/
├── fixtures/ # 안티패턴 주입 픽스처 (언어별)
├── e2e-corpus.yaml # project-score regression partition
├── labeled-benchmarks.yaml # independent validation partition
└── golden/ # golden file 테스트 (report/csv 스냅샷)
핵심 인터페이스
type Analyzer interface {
Category() string
Analyze(p *model.Project) ([]model.Finding, error)
}
type Finding struct {
MetricID string
Severity model.Severity // info / warning / violation / critical
Confidence model.Confidence // high / medium / low
File string // 루트 기준 상대 경로, '/' 구분자 통일
Line, Col int // 1-based
EndLine int
Message string // 사람용 설명
Value float64 // 측정값 (CCN=23 등)
Threshold float64
Related []Location // 클론 상대편, 사이클 경로 등
}
4. 동시성 모델
- 현재 구현은 analyzer를 고정 순서로 순차 실행해 결정성을 우선한다.
- 결과는 항상 metric_id·경로·라인 순으로 정렬하여 동일 입력 → 동일 출력 보장.
- 후속 성능 확장에서는 파일 단위 analyzer를 worker pool로 병렬화하되, 최종 정렬과 해시 시드를 고정해 출력 결정성을 유지한다.
- 메모리: 프로젝트 모델은 파일 내용, 라인 통계, import/토큰 분석용 중간 산물을 보유한다. 거대 파일은 discovery 단계에서 스킵/기록한다.
5. 크로스 플랫폼 고려사항
| 항목 | 처리 |
|---|---|
| 경로 | 내부 표현은 항상 / 구분 상대 경로. OS 경계에서만 filepath 변환 |
| 줄바꿈 | CRLF/LF 모두 라인 카운트 일관 처리. 라인 인덱스는 바이트 오프셋 테이블로 산출 |
| 인코딩 | UTF-8 가정 + BOM 스킵. 비UTF-8 파일은 스킵하고 jam-run.json에 기록; production 파일이면 --strict에서 exit 3 |
| 빌드 | CGO_ENABLED=0 go build 로 3 OS × (amd64, arm64) 매트릭스. goreleaser 사용 예정 |
6. 성능 목표와 전략
- 목표: 100k LOC / 30초, 1M LOC / 5분 (일반 노트북).
- DUP의 해시 인덱스가 메모리 상한 결정 요인 → 윈도 해시는 8바이트 fingerprint만 저장, 충돌 시 재검증.
- 대상 파일 1MB 초과 시 파싱/점수에서 스킵한다. production 파일이면 런 매니페스트를
incomplete로 표시하고, test/docs/config면 감사 목록에만 남긴다. 스킵된 바이트를 CLOC로 추정해 SIZE 점수에 혼합하지 않는다.
7. 확장 포인트 (v1 이후)
--lsp모드: 설치된 Language Server를 발견하면 심볼 해석 정밀도 향상(기본 off, 단일 실행파일 제약 유지).- tree-sitter WASM 커버리지 확장: 현재 임베드 경로를 필요한 메트릭/언어에만 보수적으로 확장.
- 룰 플러그인: 메타데이터(YAML) 또는 tree-sitter 쿼리 기반 사용자 정의 SEC/HYG 룰 추가.