Contribution 발견 문제 해결
소스에는 있는데 Shell이나 권한 카탈로그, 런타임 레지스트리에 도달하지 않는 contributor를 진단합니다.
Contribution 발견 문제 해결
클래스를 작성해 저장했는데 내비게이션 항목이나 권한, descriptor, 화면이 없습니다. 패키지 등록부터 화면 연결까지 어느 단계에서 빠졌는지 확인합니다.
Package doctor로 시작합니다.
아래 명령의 예시 App 키 quality는 진단할 App으로 바꾸세요. APP_TENANT_ID에는 대상 테넌트 ID를 지정합니다. 설치·복구 명령의 --tenants가 실행 대상을 해당 테넌트로 제한합니다.
task artisan -- nexia-apps:doctor-package-app qualitydoctor가 검사별로 OK, WARN, FAIL 행을 출력합니다. 아래에서 자기 증상을 그 행으로 찾고 소유 패키지의 선언을 고치세요.
일반 요청은 생성된 contribution manifest를 읽고 package source directory를 순회하지 않습니다. 따라서 올바른 소스 변경도 manifest를 다시 만들기 전에는 보이지 않을 수 있습니다.
호스트 쪽 폴백을 추가해 발견을 고치지 마세요. 플랫폼이 특수 처리해줘서만 동작하는 contribution은 발견된 것이 아니라 하드코딩이고, 다음 App에서 깨집니다.
증상 색인
| doctor 행 | 절 |
|---|---|
runtime app registration — AppRegistry cannot see {app_key} | App이 런타임에 보이지 않음 |
runtime app metadata — registered manifest metadata differs from composer.json | Manifest와 composer.json이 어긋남 |
contribution locations — no package contribution location is registered | contribution 디렉터리가 선언되지 않음 |
permission contributors / navigation contributors — 0 classes | 클래스가 contributor로 인식되지 않음 |
permissions discovered — 0 permission definitions | 클래스가 contributor로 인식되지 않음 |
| 소스가 바뀌었는데 이전 contribution이 남음 | 생성된 manifest가 낡음 |
| 새 CLI 출력은 맞는데 브라우저에는 이전 contribution이 남음 | Octane worker가 이전 contribution을 계속 제공함 |
frontend component registration — index.ts does not call … | 프런트엔드 화면이 등록되지 않음 |
shell component pairing — missing index.ts registrations: … | 프런트엔드 화면이 등록되지 않음 |
translation catalogs — missing 또는 invalid | 라벨이 원시 키로 나옴 |
모든 행이 OK인데 메뉴 항목이 없음 | 전부 발견됐는데 아무것도 안 보임 |
App이 런타임에 보이지 않음
WARN runtime app registration AppRegistry cannot see quality. Run activation,
then restart the process if Composer autoload changed.
원인. AppRegistry는 파일시스템이 아니라 설치된 App 맵에서 만들어집니다. 패키지가 호스트 Composer 요구사항에 없거나, 실행 중 PHP 프로세스가 패키지 추가 이전의 낡은 오토로더를 쥐고 있습니다.
해결.
task artisan -- nexia-apps:activate-package-app quality
task artisan -- nexia-runtime:generate-app-map
task artisan -- nexia-runtime:refresh-runtime-caches그다음 장기 실행 프로세스를 재시작하세요. 큐 워커, Octane, Horizon, Vite 개발 서버입니다. 새 네임스페이스의 새 클래스는 그 이전 오토로드 맵을 가진 프로세스에게 보이지 않습니다.
확인. 행이 OK가 되고 Manifest 클래스를 지칭합니다.
OK runtime app registration Amuzcorp\Nexia\Quality\QualityAppManifest
예방. 패키지 추가나 네임스페이스 변경을 단순 파일 변경이 아니라 프로세스 재시작으로 취급하세요.
Manifest와 composer.json이 어긋남
FAIL runtime app metadata registered manifest metadata differs from composer.json.
원인. doctor가 등록된 Manifest의 AppDefinition::toArray()를 packages/{app_key}/composer.json의 extra.nexia.app과 비교합니다. 둘이 갈라졌고, 보통 낡거나 캐시된 정의가 남은 상태에서 composer.json을 편집했거나 한쪽에만 손으로 필드를 고친 경우입니다.
해결. extra.nexia.app을 유일한 편집 지점으로 만들고 다시 활성화하세요.
task artisan -- nexia-access:sync-manifest
task artisan -- nexia-apps:activate-package-app qualityapp_key와 app_table_prefix는 계약상 안정적입니다. 정말 둘 중 하나가 틀렸다면 테이블·권한·descriptor·번역 키에 걸친 rename 작업이고 메타데이터 편집이 아닙니다.
확인. OK runtime app metadata registered manifest matches composer.json.
예방. 정의 필드를 두 번째 파일에 절대 복제하지 마세요.
contribution 디렉터리가 선언되지 않음
WARN contribution locations no package contribution location is registered in
the current process.
원인. manifest 생성은 App Manifest가 contributionLocations()에서 돌려주는 namespace·directory 쌍만 순회합니다. 그 root 밖의 class는 어떤 interface를 구현해도 색인되지 않습니다. 일반 요청은 생성된 index를 신뢰하며 선언된 directory와 선언되지 않은 directory 어느 쪽도 스캔하지 않습니다.
해결. Manifest에 디렉터리를 선언하세요.
public function contributionLocations(): array
{
return [
['namespace' => 'Amuzcorp\Nexia\Quality\Contribution', 'directory' => __DIR__.'/Contribution'],
['namespace' => 'Amuzcorp\Nexia\Quality\Descriptors', 'directory' => __DIR__.'/Descriptors'],
];
}namespace가 directory 안 클래스의 PSR-4 네임스페이스와 정확히 일치해야 합니다. 하위 디렉터리는 덮이므로 Contribution/Resources/NoteModule.php는 Contribution 항목이 찾습니다. 그래서 불일치는 거의 항상 오타이거나 선언된 모든 루트 밖에 놓인 클래스입니다.
root를 바꾼 뒤 contribution과 route cache를 다시 만드세요.
task artisan -- nexia-runtime:refresh-runtime-caches확인. OK contribution locations 2 registered와 이어지는 0이 아닌 contributor 수.
예방. 새 contributor를 형제 디렉터리를 만들지 말고 이미 선언된 루트 아래에 추가하세요.
클래스가 contributor로 인식되지 않음
OK contribution locations 2 registered
WARN permission contributors 0 classes
WARN permissions discovered 0 permission definitions
원인. manifest가 생성될 때 class가 surface 목록을 만드는 계약을 만족하지 않았습니다. 색인은 interface로 매칭하고 interface에 선언된 메서드를 구현해야 합니다. 일부 contribution은 instance method를 사용합니다. 가장 흔한 원인은 trait을 쓰면서 그것이 구현하는 interface를 선언하지 않은 것입니다. trait이 method를 공급하지만 class가 type을 선언하지 않아 선택되지 않습니다.
| 인터페이스 | 필수 메서드 | 공급 가능한 trait |
|---|---|---|
ResourceCatalogContribution | resourceKey(), resourceModelClass() | — |
ResourceAuthorizationContribution | resourceAuthorization() | — |
PermissionContribution | catalogPermissionDefinitions() | ContributesResourcePermissions (permissionResources()에서) |
NavigationContribution | navigationItems() | ContributesNavigationDestination ($navigation에서) |
해결. 클래스가 실제로 제공하는 모든 인터페이스를 선언하세요.
App Menu 목적지 추가에 import와 발견 정보 갱신 명령을 포함한 완전한 NavigationContribution 예시가 있습니다. 해당 interface 구현 방식을 자기 클래스에 적용하고, 추가하는 contribution마다 필수 메서드를 구현하세요.
클래스가 abstract이 아닌지, PSR-4가 오토로드할 수 있게 파일 이름이 클래스 이름과 일치하는지도 확인하세요.
그다음 생성된 index를 다시 만드세요.
task artisan -- nexia-runtime:refresh-runtime-caches확인. 기여자 수가 늘고 의도한 권한 키가 발견되어야 합니다. App별 개수는 다르므로 App 테스트하기에서 해당 contribution을 확인하세요.
예방. 인터페이스와 trait을 같은 편집에서 추가하세요.
생성된 manifest가 낡음
소스에서 contribution class를 바꿨는데 이전 navigation, permission 또는
descriptor 결과가 계속 제공됩니다.
원인. schema v1 contribution manifest는 interface-to-class 전체 index입니다. 설치 위치 hash가 같으면 일반 요청은 저장된 목록을 신뢰합니다. 없는 interface key도 빈 결과로 cache된 것입니다. source mtime 검사는 의도적으로 warm request path에 포함되지 않습니다.
해결. contribution metadata와 route cache를 다시 만드세요.
task artisan -- nexia-runtime:refresh-runtime-caches확인. nexia-apps:doctor-package-app에 기대한 0이 아닌 contributor count가
나오고 새 process에서 변경된 surface가 보입니다.
예방. source-change와 deployment workflow에 nexia-runtime:refresh-runtime-caches를
유지하세요. scripts/run-vite-dev.sh는 local Vite 시작 때
nexia-runtime:cache-contributions를 실행하고 Laravel optimize는 host service
provider를 통해 전체 refresh를 호출합니다.
Octane worker가 이전 contribution을 계속 제공함
새 CLI process는 변경된 navigation이나 resource definition을 해석하지만
브라우저에는 이전 contribution이 계속 표시됩니다.
원인. 새 CLI process는 편집한 소스를 읽지만 실행 중인 Octane worker에는 class나 이전에 만든 contribution registry가 남을 수 있습니다. 이미 열린 Shell도 앞서 조회한 navigation 결과를 유지할 수 있습니다. 생성된 manifest를 갱신해도 두 process의 상태가 교체되지는 않습니다.
해결. web application 컨테이너만 재시작하고 healthy 상태까지 기다리세요.
task app:reload그런 다음 브라우저를 새로고침하세요. contributor class나 interface, route를 추가하거나 제거했다면 먼저 생성된 metadata를 다시 만드세요.
task artisan -- nexia-runtime:refresh-runtime-caches
task app:reload확인. task status에서 app이 healthy이고 새로고침한 Shell에 현재
contribution이 표시됩니다.
예방. Octane watch가 백엔드 편집을 반영하지 않을 때 task app:reload를
확정적인 로컬 폴백으로 사용하세요. 데이터나 다른 서비스는 초기화하거나
재시작하지 않습니다.
프런트엔드 화면이 등록되지 않음
FAIL frontend component registration index.ts does not call
appComponentRegistry.register or autoRegisterPackageSurfaces.
원인. 백엔드가 pageElements()로 컴포넌트 이름을 공개하고, 프런트엔드가 각 이름을 컴포넌트에 묶어야 합니다. doctor 행 둘이 서로 다른 실패를 다루므로 잘못된 쪽을 읽으면 엉뚱한 수정으로 갑니다.
| 행 | 실패 조건 |
|---|---|
frontend component registration | index.ts가 appComponentRegistry.register(...)도 autoRegisterPackageSurfaces도 호출하지 않음 — 등록이 아예 없음 |
shell component pairing | 등록은 있는데 pageElements()가 게시한 이름 하나에 바인딩이 없음 |
위 메시지는 첫 번째 행입니다. 아무것도 등록되지 않은 경우입니다. 이름 하나가 빠진 경우는 shell component pairing을 읽으세요. 맞지 않는 이름을 나열해줍니다.
doctor는 명시적 appComponentRegistry.register(...) 호출이나 autoRegisterPackageSurfaces의 overrides 항목, 또는 resources/js/resources/{resource}/surface/의 파생 가능한 화면 파일 중 하나를 받아들입니다. 셋 다 없으면 Route가 해석되고 아무것도 그리지 않습니다.
해결. 생성된 등록을 index.ts에 유지하고 생성된 화면을 파생 경로에 두세요. resources/js/resources/notes/surface/NoteListSurface.tsx 같은 형태입니다. 화면을 옮기거나 이름을 바꿀 때는 명시적 overrides 항목을 추가하세요.
빌드된 asset을 사용하는 설치에서는 등록 변경 후 패키지를 다시 빌드합니다.
task artisan -- nexia-apps:activate-package-app quality --build-assets함께 읽을 만한 행입니다.
WARN frontend entry shape entry exists, but app-specific route/component
tokens were not obvious.
index.ts는 있는데 /{app_key}도 studly app key도 언급하지 않는다는 뜻이고, 보통 채워지지 않은 스텁입니다.
확인. OK frontend component registration 과 OK shell component pairing, 그리고 화면이 App Route에서 그려짐.
예방. 프런트엔드 변경을 커밋하기 전에 둘 다 돌리세요. 검증기는 의존성 그래프를, ratchet은 import 경계를 덮습니다.
task artisan -- nexia-apps:validate-app-frontend-dependencies --app={app_key} --require-manifests
php scripts/coupling-ratchet.php check {app_key}라벨이 원시 키로 나옴
quality.defect_code.status.ACTIVE
UI에 라벨 대신 quality.defect_code.status.ACTIVE가 보입니다.
WARN translation catalogs missing ko.json. Add packages/<app>/resources/lang/{locale}.json.
WARN translation catalogs invalid ko.json (catalog must be a flat JSON object).
원인. 패키지 카탈로그는 packages/{app_key}/resources/lang/{locale}.json의 flat JSON 객체이고 지원 로케일마다 하나입니다. doctor가 JSON 리스트인 카탈로그, 또는 키에 .이 없거나 :이 있거나 값이 문자열이 아닌 항목을 담은 카탈로그를 거부합니다.
해결. 점 표기 flat 키와 문자열 값을 쓰세요.
{
"quality.defect_code.status.ACTIVE": "Active",
"quality.defect_code.status.RETIRED": "Retired"
}그다음 검증하고 캐시를 갱신하세요.
task artisan -- nexia-runtime:validate-translations
task artisan -- nexia-runtime:clear-translation-catalog-cache확인. OK translation catalogs, 그리고 모든 지원 로케일에서 라벨이 그려짐.
예방. 키와 같은 커밋에 모든 로케일을 추가하세요. 카탈로그는 교체가 아니라 병합되므로 생성된 항목과 손으로 쓴 항목이 공존합니다.
전부 발견됐는데 아무것도 안 보임
Doctor is clean, routes respond, and the App Menu entry is still absent.
원인. 셋 중 하나이고 가능성 순서입니다.
해결. 아래 세 관문을 순서대로 확인하고 현재 tenant와 actor에 맞지 않는 첫 항목을 고치세요.
항목이 의도적으로 숨겨짐. --without-navigation이 contributor에 visible => false를 씁니다. Route와 권한, 카탈로그 메타데이터는 모두 남고 메뉴 항목만 억제됩니다.
protected static array $navigation = [
'group' => 'master-data',
'visible' => false,
];항목을 공개하려면 true로 두세요.
아무도 권한을 갖지 않음. 활성화는 권한 정의를 등록하고 아무것도 부여하지 않습니다. quality.defect_code.read로 게이팅된 내비게이션 항목은 그 권한이 없는 행위자에게 올바르게 숨겨집니다. 여러분도 포함입니다. 접근 관리자가 App Role을 만들거나 골라 권한을 붙이고 배정해야 합니다.
이 테넌트에 App이 설치되지 않음. 호스트 활성화와 테넌트 설치는 별개 단계입니다. 설치가 없으면 app.installed:{app_key}가 닫힘 방향으로 실패하고 App이 이 테넌트에 아무것도 기여하지 않습니다.
task artisan -- tenants:run "nexia-apps:install-tenant-app quality --dry-run" --tenants="$APP_TENANT_ID"
task artisan -- tenants:run "nexia-apps:install-tenant-app quality" --tenants="$APP_TENANT_ID"미게시 로컬 App(APP_ENV=local)은 task artisan -- nexia-apps:install-dev-app quality --tenant="$APP_TENANT_ID" --dry-run으로 확인하고 --dry-run 대신 --with-prerequisites로 실행합니다. 게시 상태와 readiness는 그대로이며 로컬 복구에도 같은 명령을 사용합니다. 일반 install·repair 명령은 운영 배포 정책을 검사합니다.
확인. App이 설치·operational인 테넌트에서 게이팅 권한을 가진 사용자에게 항목이 나타납니다.
예방. 무언가 없을 때 발견을 의심하기 전에 권한 부여와 테넌트 설치를 확인하세요.
관련 문서
- Resource 만들기 — 생성된 contributor와 각 선언이 놓이는 곳
- Resource 계약 — 필수 인터페이스와 메서드, 생성기 오류 메시지
- 설치 오류 해결 — 활성화와 테넌트 설치 중 실패