본문으로 건너뛰기
개발 머꼬
개발 노트Spring
hohyeon.dev49

빈이 분명히 있는데 can't resolve dependencies가 난 이유 — 패키지 위치

  • #Common Pitfall
  • #Engineering Note
  • #Spring

문제 발생

서비스와 리포지토리를 분명히 만들었는데 앱이 뜨지 않았습니다.

Field service in com.example.api.PostController required a bean of type
'com.example.core.PostService' that could not be found.

@Service도 붙어 있고 오타도 없었습니다. 패키지 구조는 이랬습니다.

com.example.api     ← MyApplication.java (메인 클래스)
com.example.core    ← PostService.java

원인 분석

스캔 시작점이 메인 클래스의 패키지입니다. Spring Boot 문서가 설명합니다 — @SpringBootApplication 애노테이션은 보통 메인 클래스에 놓이며, 특정 항목들에 대한 기본 "검색 패키지"를 암묵적으로 정의합니다. 예로 JPA 애플리케이션이라면 @SpringBootApplication이 붙은 클래스의 패키지가 @Entity 검색의 기준이 됩니다.

우리 구조에서는 기준이 com.example.api이므로 형제 패키지인 com.example.core는 스캔 범위 밖입니다. 빈은 만들어진 적이 없고, 주입할 것을 찾지 못한 것입니다.

문서의 권고가 곧 해법입니다 — 메인 애플리케이션 클래스를 다른 클래스들보다 위의 루트 패키지에 두는 것을 일반적으로 권장하며, 루트 패키지를 쓰면 컴포넌트 스캔이 우리 프로젝트에만 적용됩니다.

기본 패키지에 대한 경고도 있습니다 — 패키지 선언이 없는 클래스는 "기본 패키지"에 속하는데, 이는 일반적으로 권장되지 않으며 피해야 합니다. @ComponentScan, @EntityScan, @SpringBootApplication을 쓰는 앱에서는 모든 jar의 모든 클래스를 읽게 되어 특히 문제가 됩니다.

해결 방안

  1. 메인 클래스를 루트 패키지로 올립니다. 문서가 제시하는 구조 그대로입니다.
com.example.myapplication
 ├─ MyApplication.java
 ├─ customer/  (Customer, CustomerController, CustomerService, CustomerRepository)
 └─ order/     (Order, OrderController, OrderService, OrderRepository)
  1. 기능별로 묶습니다. 계층별(controllers, services)이 아니라 도메인별로 나누면 스캔 범위와 응집도가 함께 정리됩니다.

  2. @ComponentScan으로 억지로 넓히지 않습니다. 스캔 경로를 손으로 나열하기 시작하면 새 패키지를 추가할 때마다 같은 오류가 반복됩니다 — 구조를 고치는 편이 낫습니다.

  3. 라이브러리 모듈의 빈은 명시적으로 가져옵니다. 다른 아티팩트의 빈이 필요하다면 자동 설정(@AutoConfiguration)이나 @Import처럼 의도가 드러나는 방법을 씁니다.

  4. 기본 패키지를 쓰지 않습니다. 예제 코드를 옮겨오다 package 선언이 빠지는 경우가 실제로 생깁니다.

  5. 오류 메시지를 끝까지 읽습니다. Spring Boot의 실패 분석기는 "빈이 없다"와 함께 후보 클래스가 스캔되지 않았을 가능성까지 알려줍니다 — 그 문장이 패키지 구조를 보라는 신호입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.