- edge_config_mapper: 엣지 설정 매핑 기능 추가 - edge_node_id: 엣지 노드 ID 생성/관리 기능 추가 - node_router_registry: 노드 라우터 레지스트리 기능 추가 - node_writer_injection: 노드 writer 주입 기능 추가 - edge console 및 server 업데이트 - node bootstrap module 업데이트
179 lines
6.2 KiB
Text
179 lines
6.2 KiB
Text
<!-- 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:29–33`
|
||
```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:75–88` 근방):
|
||
```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` 필드 없음.
|