이 페이지는 Laravel 패키지 개발, Orchestra Testbench로 Laravel 패키지를 테스트하기, 패키지의 버전 호환성 관리의 보조 가이드입니다. 구현·테스트·버전 전략에 정적 해석을 추가하면, 장기 유지보수하기 쉬운 패키지가 됩니다.
정적 해석이란
정적 해석은, 코드를 실행하지 않고 타입의 부정합이나 잠재적인 버그를 검출하는 수법입니다. Laravel 패키지에서는, 서비스 컨테이너, Facade, Eloquent 등 동적인 구조가 많기 때문에, 동작 확인만으로는 놓치기 쉬운 문제를 이른 단계에서 잡을 수 있습니다. 정적 해석을 도입하면, 특히 다음의 효과가 있습니다.- 타입 안전성의 향상 — 잘못된 인수나 반환값을 리뷰 전에 검출할 수 있음
- 버그의 조기 발견 — 존재하지 않는 메서드 호출이나 nullable의 누락을 테스트 전에 파악할 수 있음
- IDE 자동 완성의 개선 — PHPDoc과 Generics를 정비함으로써 자동 완성 정밀도가 올라감
- 장기 유지보수의 안정화 — Laravel이나 PHP의 업그레이드 시에 부서진 곳을 씻어내기 쉬움
PHPStan의 셋업
우선은 PHPStan 단체로 해석 기반을 준비합니다. PHPStan 2.x는mixed나 nullable의 취급이 이전보다 엄밀하므로, 패키지의 공개 API를 정비하는 계기도 됩니다.
Larastan의 셋업
Laravel 패키지에서는 PHPStan만으로는, 컨테이너 해결, Facade, Eloquent 릴레이션 등 Laravel 고유의 구조를 충분히 이해할 수 없습니다. 그래서 PHPStan의 Laravel 확장인 Larastan을 병용합니다.패키지 개발이라면 Testbench도 넣어 둔다
Larastan은 Laravel 애플리케이션 컨테이너를 기동하여 타입을 해결합니다. Laravel 패키지를 단체 해석하는 경우에는,
orchestra/testbench가 필요할 때가 있습니다.phpstan.neon의 설정
phpstan.neon은 해석 레벨, 대상 경로, 제외 경로, 예외 룰을 정리하는 중심 설정입니다. Laravel 패키지라면 처음부터 100점을 목표로 하기보다, 지속적으로 올릴 수 있는 설정으로 해 두는 편이 현실적입니다.
이하는 phpstan.neon의 예입니다.
레벨 0~9의 고르는 법
PHPStan은 단계적으로 도입할 수 있습니다. 우선은 현재의 코드베이스를 다 돌릴 수 있는 레벨부터 시작하고, 수정이 진행되면 올려 가는 운용이 안전합니다.| 레벨 | 적합한 단계 |
|---|---|
| 0~3 | 우선 미지의 클래스·함수·메서드 호출을 줄이고 싶은 단계 |
| 4~6 | 공개 API의 인수·반환값·프로퍼티 타입을 정비하고 싶은 단계 |
| 7~9 | nullable, union, mixed를 엄밀하게 다루고 싶은 장기 유지보수 페이즈 |
paths
paths에는 해석하고 싶은 디렉토리를 명시합니다. 패키지에서는 src뿐만 아니라, 타입이 무너지기 쉬운 tests도 포함하면 효과적입니다.
excludePaths
생성 파일, 캐시, 검증용 Workbench 등, 정적 해석의 가치가 낮은 장소만을 제외합니다. 넓게 제외하면, 본래 검출하고 싶은 에러까지 보이지 않게 됩니다.
ignoreErrors
ignoreErrors는 최후의 수단입니다. 메시지를 정규 표현으로 좁히고, path도 병용하여 영향 범위를 한정해 주세요. 장래 Laravel이나 Larastan이 개선되었을 때 되돌리기 쉬워집니다.
자주 사용하는 타입 어노테이션
PHPStan과 Larastan의 정밀도는, PHPDoc의 작성법으로 크게 바뀝니다. Laravel 패키지에서는 특히@param, @return, @var, Generics(@template)를 갖출 가치가 높습니다.
@param class-string<TModel>— 문자열이 임의의 문자열이 아니라 Model 클래스명이라는 것을 나타냄@return TModel|null—find()가 구체적인 모델 타입을 반환하는 것을 전달@var Collection<int, TModel>— 컬렉션의 key / value 타입을 명시@template TModel of Model— 재이용 가능한 generic repository를 표현
Laravel에 특화된 주의점
Laravel의 “편리한 magic”은, 그대로는 정적 해석에 전달되지 않습니다. 패키지 측에서 타입 정보를 더해, 해석기가 이해할 수 있는 형태에 가까이 할 필요가 있습니다.Facade
커스텀 Facade는, 사용자가 호출하는 메서드를 PHPDoc으로 보충하면 IDE와 정적 해석 양쪽에 효과가 있습니다.매직 메서드
__call()이나 Macroable에 의존한 API는 편리하지만, 타입이 무너지기 쉬운 장소입니다. 공개 API로 하는 것이라면, 반환값이나 인수를 알 수 있는 래퍼 메서드를 추가하는 편이 안전합니다.
Eloquent 모델의 타입 지정
Eloquent 릴레이션이나 동적 프로퍼티에는,@property와 relation의 Generics를 조합하면 효과적입니다.
CI에서의 자동 실행
정적 해석은 로컬뿐만 아니라 CI에서도 반드시 실행해 주세요. 특히 여러 Laravel 버전을 서포트하는 패키지에서는, 패키지의 버전 호환성 관리의 test matrix와 같은 사고방식으로 돌리면, 호환성과 타입 안전성을 동시에 감시할 수 있습니다. 이하는.github/workflows/static-analysis.yml의 예입니다.
자주 있는 오검지와 대처법
정적 해석의 경고는, 우선 “타입 정보가 부족하지 않은가”를 의심해 주세요. 오검지로 보여도, 실제로는 PHPDoc이 부족한 것뿐인 경우가 자주 있습니다.@phpstan-ignore-next-line
일시적인 회피로는 사용할 수 있지만, 이유를 스스로 설명할 수 있는 줄만으로 한정해 주세요.
ignoreErrors
같은 에러가 여러 곳에서 나올 때만 검토합니다. 반드시 path를 좁히고, 조잡한 정규 표현으로 전체를 잡아 뭉개지 마세요.
우선 우선해야 할 대처
- PHPDoc을 더한다
- Facade나 relation의 반환값 타입을 명시한다
mixed를 구체적인 타입으로 치환한다- 그래도 설명할 수 있는 오검지만 ignore한다
관련 페이지
Laravel 패키지 개발
서비스 프로바이더나 공개 리소스를 포함한 패키지 구현의 기초를 확인합니다.
Orchestra Testbench로 Laravel 패키지를 테스트하기
정적 해석과 함께 운용하고 싶은 패키지용 테스트 기반을 해설합니다.
패키지의 버전 호환성 관리
Laravel / PHP의 대응표와 GitHub Actions의 matrix 전략을 확인합니다.