인프런 커뮤니티 질문&답변

Dongseon님의 프로필 이미지
Dongseon

작성한 질문수

스프링 핵심 원리 - 기본편

생성자 주입을 선택해라!

interface가 왜 빈에 등록이 되나요?

작성

·

112

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)

[질문 내용]
여기에 질문 내용을 남겨주세요.

 

프로젝트 압축파일 :

https://drive.google.com/file/d/1JuIfXWrbMbE0SuppC6Pnd36vSqKkp2aX/view?usp=drive_link

강의를 보면서 allTest를 하니 강의에 없는 오류가 나와서 질문드립니다. CoreApplicationTest -> contextLoads 부분 오류 내용은 다음과 같습니다

expected single matching bean but found 2: memoryMemberRepository,memberRepository

즉, 하나의 빈을 찾아야하는데 2개의 빈을 찾았다는 뜻입니다.

그래서 "MemberServiceImpl"의 생성자에 @Qualifer 어노테이션을 사용하니 오류가 사라졌습니다.

@Autowired
	public MemberServiceImpl(@Qualifier("memoryMemberRepository") MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}



하지만 궁금증이 남았습니다. @Component 어노테이션을 사용하지도 않은 interface가 빈으로 등록이 되는걸까? 궁금해서 contextLoads 함수에서 MemberRepository.class의 빈을 찍어봤습니다.

스크린샷 2024-09-12 오전 2.45.27.png


다음과 같이 interface도 빈으로 등록이 되어 있음을 확인했습니다. 추상 클래스라 생성도 하지 못 하는데 왜 빈에 올라가 있는 것인지 의아하여 gpt에게 질문을 해보니

"인터페이스를 구현한 클래스가 빈으로 등록될 때, 해당 인터페이스의 이름도 빈의 별칭(alias)으로 자동 등록됩니다. 이 빈 객체는 두 개의 이름("memoryMemberRepository"와 "memberRepository")으로 참조될 수 있습니다."

라는 답변을 받았습니다. 만약 해당 답변이 옳다면
1. 굳이 왜 alias가 필요했던 것인가?
2. alias이면 같은 것을 참조하는데 컴파일러는 왜 고민을하고 에러를 띄운것인가?
3. @Qualifier의 사용이 필연적인가? 강의에서는 왜 그렇지 않았는데 해당 오류가 없었나?


이렇게 3가지 질문을 드리고 싶습니다.

답변 3

1

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. Dongseon님

다음과 같은 오류 메시지를 확인할 수 있는데요.

Parameter 0 of constructor in hello.core.member.MemberServiceImpl required a single bean, but 2 were found:

- memoryMemberRepository: defined in file [/Users/yh/Downloads/hello-spring 5/basic/core/out/production/classes/hello/core/member/MemoryMemberRepository.class]

- memberRepository: defined by method 'memberRepository' in class path resource [hello/core/AppConfig.class]

 

MemoryMemberRepository 클래스에 붙은 컴포넌트 스캔에 의해서 빈이 등록된 것이고,

추가로 AppConfig에 있는 다음 코드에 의해서 MemberRepository의 타입으로 MemoryMemberRepository가 하나더 등록되었습니다.

	@Bean
	public MemberRepository memberRepository() {
		return new MemoryMemberRepository();
	}

결과적으로 2개의 빈이 등록되었는데, MemberServiceImpl에서는 다음과 같이 MemberRepositoy라는 인터페이스 기반으로 빈을 주입하기 때문입니다. 이때 두 빈이 모두 대상이 되기 때문에 문제가 발생합니다.

	@Autowired
	public MemberServiceImpl(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}

여기서 그럼 궁금하신 점이 memberRepository라는 매개변수의 이름으로 대상을 찾는 부분이 정상 작동하지 않게 되는데요.

이 부분은 스프링 부트 3.2에서 등장한 이슈 때문입니다.

다음 내용을 참고해서 gradle로 빌드하도록 변경해주세요.

그러면 매개변수의 이름을 사용할 수 있게 되어서 문제가 발생하지 않습니다.

https://www.inflearn.com/questions/1089023

추가로 강의 메뉴얼에서 프로젝트를 처음 설정하는 부분을 보시면 다음 내용이 있으니 참고해주세요.

주의! 스프링 부트 3.2 부터 Gradle 옵션을 선택하자.

스프링 부트 3.2 부터 앞서 Build and run using에 앞서 설명한 IntelliJ IDEA를 선택하면 몇가지 오류가 발생한다. 따라서 스프링 부트 3.2를 사용한다면 다음과 같이 IntelliJ IDEA가 아니라 Gradle을 선택해야 한다.

TULO35mEfXb9rFnPp4MSKkQVe_ZjVfRUcmEmVpJ3rX3xtlp2w8X1oasQCfX9VWxUcCrCvLazVUriDUBZiSfgxLB52BJJc6fHAMmJvhpdazrUaoER4IaFryPrV_EkDGeqm9FWPfgHfL5ApgpvoDAlUIo.png

 

감사합니다.

0

안녕하세요. Dongseon님, 공식 서포터즈 David입니다.

올려주신 구글 파일 공유는 권한 문제로 열어볼 수 없습니다.

이 부분 다시 확인 부탁드립니다:)

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다. Dongseon님.

질문 주신 내용을 바탕으로 유사한 기존 답변들을 찾아보았습니다. 비슷한 문제를 다루었던 몇 가지 질문과 답변을 참고하시면 도움이 되실 것 같습니다.

  1. 중복 오류

    • 이 질문에서는 여러 개의 빈이 등록되어 @Autowired로 주입할 때 충돌이 발생하는 문제에 대한 답변이 다루어졌습니다. @Bean과 @Component를 통해 동일한 타입의 빈이 여러 개 등록되는 상황에서의 해결 방법이 설명되어 있습니다.
  2. CoreApplicationTests 오류 질문드립니다

    • 여기에서는 AppConfig 클래스에서 메소드의 이름이 빈 이름과 충돌하여 발생하는 문제에 대해 다루고 있습니다. 특정 메소드의 빈 이름을 변경하여 문제를 해결하는 방법이 설명되어 있습니다.
  3. a single bean, but 2 were found

    • 이 질문에서는 memoryMemberRepository와 SpringDataJpaMemberRepository 빈이 중복되어 발생하는 문제를 다룹니다. @Repository 어노테이션이 제거되지 않아 빈으로 등록되어 있는 상황을 설명하고 있습니다.

위의 링크들을 참고하여, 자동으로 여러 개의 빈이 생성될 때 발생하는 충돌 문제를 해결하는 방법을 배울 수 있습니다.

풍부한 설명 추가:
Spring에서 @Component, @Repository, @Service, @Bean 등의 애노테이션을 사용하여 빈을 등록하게 되면 동일한 타입의 빈이 여러 개 생성될 수 있습니다. @Autowired로 주입 시, 해당 타입의 빈이 여러 개 존재할 경우 Spring은 어떤 빈을 주입해야 하는지 알 수 없어 충돌이 발생하게 됩니다.

이를 해결하기 위해 @Qualifier를 사용하여 특정 빈을 지정하거나, 빈 이름을 변경하는 방법을 사용할 수 있습니다. 강의에서는 빈의 이름을 변경하거나, 적절한 애노테이션을 제거하여 충돌을 피했을 가능성이 큽니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

Dongseon님의 프로필 이미지
Dongseon

작성한 질문수

질문하기