iop/agent-task/node_router_registry/plan_local_G03_0.log
toki 6ec73e47d5 feat: edge config mapper, node id, router registry, writer injection 구현
- edge_config_mapper: 엣지 설정 매핑 기능 추가
- edge_node_id: 엣지 노드 ID 생성/관리 기능 추가
- node_router_registry: 노드 라우터 레지스트리 기능 추가
- node_writer_injection: 노드	writer 주입 기능 추가
- edge console 및 server 업데이트
- node bootstrap module 업데이트
2026-05-05 09:19:16 +09:00

179 lines
6.2 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- task=node_router_registry plan=0 tag=REFACTOR -->
# node.Node 라우터·레지스트리 이중 참조 제거
## 이 파일을 읽는 구현 에이전트에게
각 체크리스트 항목을 완료하고 중간 검증 명령을 실행한 뒤 출력을 `CODE_REVIEW-local-G03.md`의 검증 결과 섹션에 붙여 넣으세요.
`CODE_REVIEW-local-G03.md`의 "이 파일을 읽는 리뷰 에이전트에게" 섹션에 있는 아카이브·complete.log 생성 지시는 **리뷰 에이전트 전용**이므로 구현 에이전트는 실행하지 마세요.
## 배경
`node.Node`는 현재 `runtime.Router`와 `*adapters.Registry` 둘 다를 필드로 가진다.
`OnRunRequest`에서 `router.Resolve()`로 adapter 이름을 결정한 뒤 다시 `registry.Get()`으로 어댑터 인스턴스를 꺼낸다.
라우터가 이름만 반환하기 때문에 불가피하게 이중 참조가 발생하며, `node.Node`가 `adapters.Registry` 구현 세부에 의존하게 된다.
`runtime.Router`가 이름 대신 `runtime.Adapter`를 직접 반환하도록 인터페이스를 확장하면 `Node`에서 registry 의존성을 제거할 수 있다.
---
### [REFACTOR-1] `runtime.Router` 인터페이스 확장 — `ResolveAdapter` 추가
**문제**
`apps/node/internal/runtime/types.go:128`
```go
// Router resolves a RunRequest into a concrete ExecutionSpec.
type Router interface {
Resolve(ctx context.Context, req RunRequest) (ExecutionSpec, error)
}
```
`Resolve`는 어댑터 이름만 담긴 `ExecutionSpec`을 돌려준다. `Node`가 이름을 가지고 다시 `registry.Get()`을 호출해야 한다.
**해결 방법**
`Router` 인터페이스에 `ResolveAdapter` 메서드를 추가한다.
```go
// Router resolves a RunRequest into a concrete ExecutionSpec and the Adapter to execute it.
type Router interface {
Resolve(ctx context.Context, req RunRequest) (ExecutionSpec, error)
ResolveAdapter(ctx context.Context, req RunRequest) (ExecutionSpec, Adapter, error)
}
```
기존 `Resolve`는 하위 호환성 유지를 위해 남겨 두고, `Node`는 `ResolveAdapter`만 사용한다.
**수정 파일 및 체크리스트**
- [x] `apps/node/internal/runtime/types.go` — `Router` 인터페이스에 `ResolveAdapter` 추가
**테스트 작성**: 스킵 — 인터페이스 추가만이므로 구현 측 테스트로 검증 가능.
**중간 검증**
```
go build ./apps/node/internal/runtime/...
```
기대 결과: 빌드 성공.
---
### [REFACTOR-2] `router.defaultRouter` 에 `ResolveAdapter` 구현
**문제**
`apps/node/internal/router/router.go`의 `defaultRouter`가 `runtime.Router`를 구현하는데, 새 메서드가 없으면 인터페이스 불일치 컴파일 오류가 발생한다.
**해결 방법**
`Resolve` 로직을 내부 헬퍼로 추출하고, `ResolveAdapter`는 같은 헬퍼를 거쳐 registry에서 어댑터를 꺼내 반환한다.
```go
func (r *defaultRouter) ResolveAdapter(ctx context.Context, req runtime.RunRequest) (runtime.ExecutionSpec, runtime.Adapter, error) {
spec, err := r.Resolve(ctx, req)
if err != nil {
return runtime.ExecutionSpec{}, nil, err
}
adapter, ok := r.registry.Get(spec.Adapter)
if !ok {
return runtime.ExecutionSpec{}, nil, fmt.Errorf("router: adapter %q not found", spec.Adapter)
}
return spec, adapter, nil
}
```
**수정 파일 및 체크리스트**
- [x] `apps/node/internal/router/router.go` — `ResolveAdapter` 메서드 추가
**테스트 작성**: `apps/node/internal/router/router_test.go`에 `TestResolveAdapter_Found` / `TestResolveAdapter_NotFound` 추가.
- `TestResolveAdapter_Found`: 등록된 어댑터 이름으로 요청 시 올바른 spec과 adapter 인스턴스가 반환되는지 확인.
- `TestResolveAdapter_NotFound`: 등록되지 않은 어댑터 이름으로 요청 시 에러 반환 확인.
**중간 검증**
```
go test ./apps/node/internal/router/...
```
기대 결과: 신규 테스트 포함 모두 PASS.
---
### [REFACTOR-3] `node.Node`에서 `registry` 필드 제거, `ResolveAdapter` 사용
**문제**
`apps/node/internal/node/node.go:2933`
```go
type Node struct {
nodeID string
router runtime.Router
registry *adapters.Registry // ← 제거 대상
store *store.Store
...
}
```
`OnRunRequest:84`에서 `registry.Get(spec.Adapter)`를 직접 호출한다.
**해결 방법**
`registry` 필드를 제거하고, `OnRunRequest` 내 어댑터 조회를 `router.ResolveAdapter` 한 번의 호출로 대체한다.
Before (`node.go:7588` 근방):
```go
spec, err := n.router.Resolve(ctx, rr)
if err != nil {
return fmt.Errorf("node: resolve: %w", err)
}
adapter, ok := n.registry.Get(spec.Adapter)
if !ok {
return fmt.Errorf("node: adapter %q not found after routing", spec.Adapter)
}
```
After:
```go
spec, adapter, err := n.router.ResolveAdapter(ctx, rr)
if err != nil {
return fmt.Errorf("node: resolve: %w", err)
}
```
`New()` 시그니처에서 `registry *adapters.Registry` 파라미터를 제거한다.
**수정 파일 및 체크리스트**
- [x] `apps/node/internal/node/node.go` — `registry` 필드 제거, `New()` 파라미터 제거, `OnRunRequest` 수정
- [x] `apps/node/internal/bootstrap/module.go` — `node.New(...)` 호출에서 `reg` 인수 제거
- [x] `apps/node/internal/node/node_test.go` — `makeNode` 시그니처 및 호출 수정
**테스트 작성**: 기존 테스트에서 `makeNode`가 `fixedRouter`를 사용하므로 `fixedRouter`에도 `ResolveAdapter` 구현 추가가 필요하다.
**중간 검증**
```
go test ./apps/node/...
```
기대 결과: 모두 PASS, registry 참조 없음.
---
## 수정 파일 요약
| 파일 | 항목 |
|------|------|
| `apps/node/internal/runtime/types.go` | REFACTOR-1 |
| `apps/node/internal/router/router.go` | REFACTOR-2 |
| `apps/node/internal/router/router_test.go` | REFACTOR-2 |
| `apps/node/internal/node/node.go` | REFACTOR-3 |
| `apps/node/internal/bootstrap/module.go` | REFACTOR-3 |
| `apps/node/internal/node/node_test.go` | REFACTOR-3 |
## 의존 관계 및 구현 순서
REFACTOR-1 → REFACTOR-2 → REFACTOR-3 순서로 구현해야 한다. 컴파일 의존성이 있다.
## 최종 검증
```
go build ./apps/node/...
go test ./apps/node/...
```
기대 결과: 빌드 성공, 모든 테스트 PASS, `node.Node` 구조체에 `registry` 필드 없음.