enode — 기술 스택
go.mod·go.sum· CI 워크플로 · 소스 주석에서 직접 확인한 것.
의도적으로 없는 것이 이 저장소에서는 있는 것만큼 중요하다.
언어와 런타임
| 항목 | 값 | 근거 |
|---|---|---|
| 언어 | Go | 전 소스 |
| 언어 버전 하한 | go 1.26 |
go.mod |
| 툴체인 | toolchain go1.26.6 |
go.mod |
| 모듈 | github.com/taeels/enode |
go.mod |
go.mod 주석이 둘을 같은 버전으로 묶은 이유를 적어 두었다.
둘을 같은 버전으로 묶어 슬랙을 없앴다 — 넷이 서로 다른 컴파일러로 짜면
"내 기계에서는 되는데" 가 나온다. 명시적 실패가 조용한 드리프트보다 낫다
CI 도 같은 원칙을 따른다. actions/setup-go 가 go-version-file: go.mod 로
버전을 읽으므로 버전이 두 곳에 적히지 않는다.
직접 의존 라이브러리 (3)
| 라이브러리 | 버전 | 쓰임 | 쓰는 곳 |
|---|---|---|---|
github.com/jackc/pgx/v5 |
v5.10.0 |
PostgreSQL 드라이버 + pgxpool 커넥션 풀 |
internal/store 전용 |
golang.org/x/sys |
v0.47.0 |
파일 잠금(flock / LockFileEx)과 디스크 여유 측정의 OS 분기 |
internal/enode (lock_*.go · disk_*.go) |
gopkg.in/yaml.v3 |
v3.0.1 |
Mediator 설정과 노드 local.yaml 파싱 |
internal/config · internal/enode/config.go |
셋 전부 실사용된다. 미사용 의존 0건.
표준 라이브러리로 대신한 것 — 의도적 부재
이 저장소의 성격을 가장 잘 드러내는 절이다. 없는 것들이 대부분 코드 주석에
설계 결정으로 기록돼 있다.
웹 프레임워크 없음
표준 net/http 과 Go 1.22+ ServeMux 의 메서드+경로 패턴만 쓴다. 라우트 15개가
internal/api/api.go:53 Handler() 한 함수에 등록되고, 그 결정이 api.go 헤더
주석에 적혀 있다.
결과: 라우트 전수 확인이 grep 하나로 끝난다. 미들웨어 체인 대신
s.auth(handler) 명시적 래핑이라 인증 누락 라우트를 눈으로 셀 수 있다(현재 0).
테스트 프레임워크 없음
표준 testing 만 쓴다. assertion 라이브러리도 mock 라이브러리도 없다.
결과: internal/api/api_test.go 는 mock 대신 실제 PostgreSQL 을 띄우고
httptest 로 서버를 세우는 통합 스위트가 됐다(78개 테스트, 3,342줄). CI 는
Postgres 17 서비스 컨테이너를 붙이고, internal/api 에 SKIP 이 한 줄이라도
나오면 실패시키는 전용 단계를 따로 둔다.
ci.yml 주석이 근거를 적는다.
진짜 Postgres 로 돈다 —
I5(전부 아니면 전무)는 트랜잭션으로 얻는 것이라
가짜 저장소로는 검증되지 않는다.
ORM · 마이그레이션 도구 없음
internal/store/schema.sql(144줄)을 //go:embed 로 바이너리에 넣고
Store.Migrate 가 통째로 Exec 한다. 모든 DDL 이 IF NOT EXISTS /
ADD COLUMN IF NOT EXISTS 로 멱등하게 쓰여 있다.
store.go:71 이 의도를 적는다 — "MVP 는 마이그레이션 도구를 안 쓴다 — 스키마가
굳으면 그때 넣는다". 이것의 운영 함의는 code-quality-assessment.md 참조.
로거 없음
표준 log/slog 만. 외부 로깅 라이브러리 없음.
코드 생성 · 벤더링 · Makefile 없음
go:generate 없음, vendor/ 없음, Makefile 없음. 빌드는 go build ./... 다.
OpenAPI · Swagger 없음
정본 문서가 "OpenAPI 로 굳히지 않는다" 를 명시했다. godoc 외의 생성 문서 없음.
린터 설정 없음
golangci-lint 설정 파일이 없다. CI 가 강제하는 것은 gofmt -l . 와
go vet ./... 둘, 그리고 저장소 고유의 장식 문자 검사 하나다.
간접 의존 (7)
| 라이브러리 | 버전 |
|---|---|
github.com/jackc/pgpassfile |
v1.0.0 |
github.com/jackc/pgservicefile |
v0.0.0-20240606120523-5a60cdf6a761 |
github.com/jackc/puddle/v2 |
v2.2.2 |
github.com/kr/text |
v0.2.0 |
github.com/rogpeppe/go-internal |
v1.16.0 |
golang.org/x/sync |
v0.17.0 |
golang.org/x/text |
v0.29.0 |
앞의 셋은 pgx 가 끌고 오고, kr/text · rogpeppe/go-internal 은 테스트
경로에서 온다.
외부 런타임 의존
| 무엇 | 버전 | 필수인가 |
|---|---|---|
| PostgreSQL | 17 | Mediator 에 필수. 테스트는 scripts/testdb.sh 도커 또는 CI 서비스 컨테이너 |
claude CLI |
— | 노드에서 선택적. 없으면 광고에서 하네스 속성이 빠질 뿐 노드는 뜬다 |
git |
— | 워크스페이스 diff 에 쓰인다. 없으면 해당 테스트가 t.Skip |
sh |
— | 명령 단계 실행에 쓰인다. 없으면 해당 테스트가 t.Skip |
하네스가 선택적이라는 점이 중요하다 — internal/enode/claude.go 의 Probe 가
없으면 그냥 광고 목록에서 빠진다. 노드가 죽지 않는다.
빌드와 패키징
크로스 컴파일
CI 의 cross 잡이 매 커밋 검증한다.
GOOS=windows GOARCH=amd64 go build ./...
GOOS=linux GOARCH=arm GOARM=7 go build ./... (Raspberry Pi 2)
GOOS=darwin GOARCH=arm64 go build ./...
ci.yml 주석이 왜 이것을 CI 에 두었는지 적는다.
ADR-015가 Go 를 고른 핵심 이유를 검증한다. "리눅스 CI 에서 GOOS=windows 로
exe 가 나온다" 가 깨지면 윈도우 enode 배포 계획이 통째로 무너진다.
설치 패키지
| 대상 | 도구 | 파일 |
|---|---|---|
| Linux deb · rpm | nfpm | packaging/linux/nfpm.yaml.in · nfpm-mediator.yaml.in |
| Windows msi | wixl (리눅스에서 굽는다) |
packaging/windows/enode.wxs · enode-mediator.wxs |
| macOS | 셸 스크립트 | packaging/macos/build.sh |
| 공통 | packaging/build-installers.sh |
설정 파일 목록
go.mod go.sum
.github/workflows/ci.yml package.yml release.yml
packaging/linux/nfpm.yaml.in nfpm-mediator.yaml.in
packaging/windows/enode.wxs enode-mediator.wxs
packaging/macos/build.sh packaging/build-installers.sh
internal/store/schema.sql (//go:embed 로 바이너리에 들어간다)
scripts/testdb.sh (테스트용 Postgres 도커)
버전 고정 방식
- Go 버전:
go.mod한 곳. CI 가go-version-file로 읽는다 - 의존 버전:
go.sum이 해시로 고정 - 제품 버전:
internal/build가 링커 플래그로 받거나 Go VCS 스탬프에서
읽는다. 다섯 실행파일이 같은 패키지를 공유하므로 버전 문자열이 한 곳에만 있다 - PostgreSQL 버전:
ci.yml의image: postgres:17과
scripts/testdb.sh두 곳