| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- 코딩테스트
- 코테
- New Architecture
- 삼성
- 일본어공부
- React Native
- 알고리즘
- 인프런
- 자료구조
- 백준
- js
- 삼성소프트웨어아카데미
- 성인학습지
- 가벼운학습지
- 웹보안
- java
- 코딩
- springboot
- 일본어독학
- 일본어학습지
- 에러해결
- 자바스크립트
- 프로그래머스
- jpa
- javascript
- 가벼운학습지후기
- 생활정보
- SWEA
- 자바
- 마이라이트
- Today
- Total
개발에 AtoZ까지
[에러] required a bean of type '...' that could not be found — 긴 메시지에서 봐야 할 한 줄 본문
[에러] required a bean of type '...' that could not be found — 긴 메시지에서 봐야 할 한 줄
AtoZ 개발자 2026. 8. 13. 19:30새 클래스를 하나 추가하고 실행했더니 서버가 뜨지 않습니다. 콘솔에는 이런 화면이 나옵니다.
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.order.OrderService required a bean of
type 'com.example.order.OrderRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.order.OrderRepository' in your configuration.
Action에는 "빈을 정의하세요"라고만 적혀 있습니다. 그런데 코드를 보면 OrderRepository는 분명히 있습니다. 있는데 없다고 하니 막막해지죠.
이 글을 다 읽으면 세 가지가 정리됩니다. 첫째, 이 메시지에서 어디를 읽어야 하는지 알 수 있습니다. 둘째, 원인이 될 수 있는 다섯 가지 경로를 좁힐 수 있습니다. 셋째, 비슷하게 생긴 다른 메시지와 구분할 수 있습니다.
이 글은 Spring Boot·Spring Framework 공식 문서를 기준으로 정리했습니다. 버전에 따라 메시지가 조금씩 다를 수 있습니다.

메인 클래스의 패키지가 컴포넌트 스캔의 시작점이 됩니다
메시지는 세 조각으로 읽습니다
긴 스택트레이스에 겁먹기 전에 Description 한 줄만 보시면 됩니다. 여기에 필요한 정보가 다 들어 있습니다.
Parameter 0 of constructor in [주입받는 쪽] required a bean of type '[찾는 타입]' that could not be found
↑ 몇 번째 파라미터 ↑ 문제가 난 클래스 ↑ 없다고 판단한 타입
- 주입받는 쪽: 이 클래스는 빈으로 등록됐습니다. 여기까지는 성공한 것이죠.
- 찾는 타입: 이 타입의 빈이 컨테이너에 없습니다.
- Parameter 0: 생성자의 첫 번째 파라미터입니다. 파라미터가 여러 개면 번호로 어느 것인지 알 수 있습니다.
즉 "OrderService는 빈으로 만들어지려는데, 그 재료인 OrderRepository가 컨테이너에 없다"는 뜻입니다. 클래스 파일이 없다는 말이 아니라 빈으로 등록되지 않았다는 말입니다. 이 구분이 출발점입니다.
원인 1 — 컴포넌트 스캔 범위 밖에 있다
가장 자주 나는 원인입니다. Spring Boot 공식 문서는 메인 애플리케이션 클래스를 다른 클래스들 위의 루트 패키지에 두라고 안내합니다. @SpringBootApplication이 붙은 클래스의 패키지가 암묵적인 기본 검색 패키지가 되기 때문입니다.
com
└ example
└ myapp
├ MyApplication.java ← @SpringBootApplication (루트)
├ order
│ ├ OrderService.java
│ └ OrderRepository.java ← 스캔됨
└ payment
└ PaymentService.java ← 스캔됨
com
└ other
└ util
└ MailSender.java ← 스캔되지 않음
메인 클래스를 com.example.myapp.web 같은 하위 패키지에 두면 그 아래만 스캔합니다. 형제 패키지에 있는 클래스는 애노테이션이 붙어 있어도 등록되지 않습니다.
멀티 모듈 프로젝트에서 다른 모듈의 빈을 쓸 때도 같은 문제가 생깁니다. 이때는 스캔 범위를 명시합니다.
@SpringBootApplication(scanBasePackages = {"com.example.myapp", "com.example.common"})
public class MyApplication { }
공식 문서는 패키지 선언이 없는 default package 사용도 피하라고 합니다. 그 경우 모든 jar의 모든 클래스가 스캔 대상이 되어 예측하기 어려운 동작이 발생할 수 있습니다.

Spring Boot 공식 문서 — 메인 클래스를 다른 클래스 위의 루트 패키지에 두라는 권고 (출처: docs.spring.io, 확인일 2026-08-08)
원인 2 — 스테레오타입 애노테이션이 없다
클래스가 스캔 범위에 있어도 표식이 없으면 빈이 되지 않습니다.
// 빈으로 등록되지 않는다
public class OrderRepositoryImpl implements OrderRepository { }
// 등록된다
@Repository
public class OrderRepositoryImpl implements OrderRepository { }
@Component, @Service, @Repository, @Controller, @Configuration 중 하나가 필요합니다. 직접 만든 구현체를 인터페이스 타입으로 주입받는 구조에서 구현체 쪽 애노테이션을 빼먹는 실수가 흔합니다.
@Bean 메서드로 등록하는 경우에는 그 메서드가 있는 클래스에 @Configuration이 붙어 있어야 합니다.
@Configuration
public class ClientConfig {
@Bean
RestClient orderClient(RestClient.Builder builder) {
return builder.baseUrl("https://api.example.com").build();
}
}
원인 3 — JPA 리포지토리가 스캔되지 않았다
찾는 타입이 ...Repository인데 인터페이스만 있고 구현 클래스가 없다면 Spring Data JPA 리포지토리일 가능성이 큽니다. 이 인터페이스는 우리가 구현하지 않고 Spring Data가 런타임에 만들어 줍니다.
만들어지지 않는 경우는 두 가지입니다.
// 1) 리포지토리 인터페이스가 스캔 범위 밖
@EnableJpaRepositories(basePackages = "com.example.common.repository")
// 2) 엔티티가 스캔 범위 밖
@EntityScan(basePackages = "com.example.common.domain")
앞선 항목에서 본 것처럼 @SpringBootApplication의 패키지가 @Entity 검색 범위도 정합니다. 엔티티가 밖에 있으면 EntityManagerFactory 생성이 실패하고, 그 연쇄로 리포지토리 빈도 만들어지지 않습니다.
여기서 한 가지 더. DataSource 설정이 없어 DB 연결 자체가 실패하면 JPA 관련 빈이 모두 만들어지지 않습니다. 그때는 이 메시지 대신 Failed to determine a suitable driver class 계열 메시지가 먼저 나오는 게 보통입니다. 메모리 DB가 사라져 생기는 문제는 Database "mem:testdb" not found 글에 정리해 두었습니다. 에러 메시지 여러 개가 함께 뜨면 위쪽 것부터 해결하셔야 합니다.
원인 4 — 프로필이나 조건 때문에 비활성화됐다
빈 정의에 조건이 붙어 있으면 특정 환경에서만 등록됩니다.
@Service
@Profile("prod") // local 로 띄우면 등록되지 않는다
public class RealPaymentGateway implements PaymentGateway { }
@ConditionalOnProperty, @ConditionalOnMissingBean 같은 조건도 같은 결과를 만듭니다. "운영에서는 뜨는데 로컬에서만 실패한다"면 이 항목을 먼저 보세요.
// 로컬용 대체 구현을 두면 조건이 갈려도 컨텍스트가 뜬다
@Service
@Profile("!prod")
public class FakePaymentGateway implements PaymentGateway { }
원인 5 — 타입이 하나가 아니거나, 아예 다르다
메시지가 조금 다르면 원인도 다릅니다. 같이 자주 보게 되는 두 가지를 구분해 두시면 좋습니다.
# 후보가 여럿일 때
required a single bean, but 2 were found:
- mysqlOrderRepository: defined in ...
- redisOrderRepository: defined in ...
이건 없어서가 아니라 골라야 해서 나는 메시지입니다. @Primary로 기본을 정하거나 @Qualifier로 지정합니다.
@Primary
@Repository
public class MysqlOrderRepository implements OrderRepository { }
// 또는 주입 지점에서 지정
public OrderService(@Qualifier("redisOrderRepository") OrderRepository repo) { }
# 서로를 참조할 때
The dependencies of some of the beans in the application context form a cycle
이건 순환 참조입니다. Spring Boot 2.6부터 기본으로 막혀 있어 시작이 실패합니다. 설계를 바꾸는 게 정답이고, 급할 때만 임시로 허용할 수 있습니다.
# 임시 조치일 뿐, 구조를 고치는 편이 낫습니다
spring.main.allow-circular-references=true

메시지 형태별로 좁혀 들어가는 경로
생성자 주입이라면 확인이 더 쉽습니다
Spring Framework 공식 문서는 필수 의존성에 생성자 주입을 권합니다. 컨테이너가 시작할 때 의존성이 없으면 바로 실패하므로 문제를 늦게 발견하지 않게 되죠. 지금 보고 있는 이 에러가 바로 그 장치가 작동한 결과입니다.
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
// 생성자가 하나면 @Autowired 를 생략할 수 있습니다
public OrderService(OrderRepository orderRepository, PaymentGateway paymentGateway) {
this.orderRepository = orderRepository;
this.paymentGateway = paymentGateway;
}
}
필드 주입(@Autowired private OrderRepository repo;)을 쓰면 이 시점에 실패하지 않고 실제 호출 때 NullPointerException으로 나타나 원인을 찾기 어려워집니다.
선택적 의존성이라면 없을 때도 뜨게 만들 수 있습니다.
// 없으면 Optional.empty
public OrderService(Optional<AuditLogger> auditLogger) { }
// 또는 ObjectProvider 로 지연 조회
public OrderService(ObjectProvider<AuditLogger> auditLoggerProvider) { }

Spring Framework 공식 문서의 @Autowired 기반 의존성 주입 섹션 (출처: docs.spring.io, 확인일 2026-08-08)
원인을 좁히는 진단 설정
추측을 줄이는 방법이 두 가지 있습니다.
# 1) 자동설정 조건 평가 보고서 — 무엇이 왜 적용되지 않았는지 나옵니다
debug=true
// 2) 실제로 등록된 빈 이름을 눈으로 확인
@Bean
ApplicationRunner beanNamePrinter(ApplicationContext ctx) {
return args -> Arrays.stream(ctx.getBeanDefinitionNames())
.filter(name -> name.startsWith("order"))
.sorted()
.forEach(System.out::println);
}
컨텍스트가 아예 뜨지 않을 때는 2번을 쓸 수 없으니, 먼저 문제 클래스의 생성자 파라미터를 하나씩 주석 처리해 어느 의존성에서 막히는지 좁히는 방식이 빠릅니다.
확인 순서
- [ ] Description에서 찾는 타입의 전체 패키지 경로를 확인한다
- [ ] 그 클래스가 메인 클래스 패키지 아래에 있는지 확인한다
- [ ] 스테레오타입 애노테이션(
@Service·@Repository등)이 붙어 있는지 확인한다 - [ ] JPA 리포지토리면
@EnableJpaRepositories·@EntityScan범위를 확인한다 - [ ]
@Profile·@ConditionalOn...조건이 붙어 있는지 확인한다 - [ ] 에러가 여러 개면 가장 위의 것부터 해결한다
- [ ]
debug=true로 자동설정 보고서를 확인한다
다음에 볼 글
애플리케이션이 뜬 다음 만나는 문제들도 이어서 정리해 두었습니다. 트랜잭션 경계에서 나는 LazyInitializationException, 그 해결 과정의 MultipleBagFetchException, 그리고 요청 파라미터 바인딩 쪽의 Spring Boot 3.2 이후 파라미터 이름 에러입니다.
잘못된 내용이나 보충이 필요한 부분이 있으면 댓글로 알려 주시면 반영하겠습니다.
참고한 자료
- Spring Boot Reference — Structuring Your Code: 메인 클래스 위치와 기본 검색 패키지, default package 주의 (확인일 2026-08-08)
- Spring Framework Reference — Annotation-based Container Configuration,
@Autowired와 생성자 주입 (확인일 2026-08-08) - Spring Framework Reference —
@Primary,@Qualifier로 후보를 좁히는 방법 (확인일 2026-08-08) - Spring Boot Reference — Auto-configuration Report(
debug=true) (확인일 2026-08-08)
