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

키썬님의 프로필 이미지

작성한 질문수

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

AOP 적용

에러가 나서 해결은 하긴했는데 이유가 궁금해요(서버구동시 생성자 매개변수 bean 2개주입으로 인한 에러)

23.03.23 12:57 작성

·

673

·

수정됨

1

안녕하세요.

제가 aop를 프로젝트 다 만들고나서 서버를 구동하니 에러가 좀나더라구요.

우선 상황은

MemoryMemberRepository 클래스파일에 @Repository 어노테이션이 추가되어 있는 상태였으며, TimeTraceAop 클래스 생성 후 강의보면서 강사님의 코드를 입력해서 추가, 이후 서버를 구동하니 에러가 나더라구요.

아래는 에러메세지 입니다.

Parameter 0 of constructor in hello.hellospring.SpringConfig required a single bean, but 2 were found:

- memoryMemberRepository: defined in file [(생략)\hello\hellospring\repository\MemoryMemberRepository.class]

- springDataJpaMemberRepository: defined in hello.hellospring.repository.SpringDataJpaMemberRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration

 

해당 에러문을 보고 MemoryMemberRepository클래서파일에 추가되어있는 @Repository 어노테이션을 제거 후 서버를 재구동하니 에러없이 정상적으로 프로젝트가 잘 돌아갔습니다.

 

제 SpringConfig파일에는

 

@Configuration

public class SpringConfig {

/** 2. 스프링 데이터 JPA 사용후 추가한 부분*/

private final MemberRepository memberRepository;

 

 

public SpringConfig(MemberRepository memberRepository) {

this.memberRepository = memberRepository;

}

 

@Bean

public MemberService memberService() {

return new MemberService(memberRepository);

}

}

이렇게만 코딩되어 있는데요.

여기서 궁금한게 왜 SpringConfig생성자가 호출될때 매개변수로 2개가 들어간건지가 궁금합니다.

원래는 SpringDataJpaMemberRepository 하나만 들어가야 하는거 같은데 어떻게 SpringDataJpaMemberRepository와 MemoryMemberRepository가 동시에 생성자의 매개변수로 들어가려 했는지.. @Repository가 어떤 영향을 주는걸까요?

이전 jpa 강의를 들을때는 MemberRepository로 사용할 매개변수를 직접설정했는데 그 코드도 주석처리가 되어있습니다. 그래서 이해가 되지 않아 게시판이 질문남깁니다

답변 1

1

y2gcoder님의 프로필 이미지

2023. 03. 23. 15:07

안녕하세요, 키썬 님. 공식 서포터즈 y2gcoder 입니다.

문제만 보면 MemberRepository의 구현체가 MemoryMemberRepository, SpringDataJpaMemberRepository 가 있고 둘 다 스프링 빈으로 등록되는 상황입니다. MemoryMemberRepository 에 @Repository, SpringDataJpaMemberRepository는 JpaRepository 인터페이스를 상속하고 있습니다.

먼저 @Repository가 붙은 클래스는 애플리케이션 실행 시 컴포넌트 스캔에 의해 스프링 빈으로 등록됩니다 그리고 JpaRepository를 상속한 인터페이스는 애플리케이션 실행 시 스프링에서 자동으로 구현체를 만들어 스프링 빈으로 등록합니다. 그렇게 되면 2개 다 스프링 빈으로 등록된 상태고 이 상태에서 SpringConfig에서 MemberService에 의존성 주입해줄 빈을 MemberRepository 인터페이스 타입으로 찾게 됩니다. 그러면 2개의 빈이 나오고 말씀하셨던 오류가 발생하게 됩니다 :)

해당 부분은 MVC 1편 강의에 자세하게 소개되어있으니 수강해보시는 것을 추천드립니다!

감사합니다.

키썬님의 프로필 이미지
키썬
질문자

2023. 03. 23. 15:48

답변감사합니다.

MemoryMemberRepository는 MemberRepository을 상속받고 있고, JPARepository를 상속한 인터페이스도 마찬가지로 MemberRepository를 상속 받고 있기 때문에 둘다 bean으로 등록되면 memberservice가 생성될때 누굴 생성자에 인자로 넣어야 하는지 2개라서 문제가 발생된 문제이군요

 

그럼 여기서 또 궁금해지는게 있는데..

추가로 질문 드려요!

 

SpringConfig파일에서 다형성을 이용할때 memberRepository를 어떤걸로 생성할지 설정하는 코드에서 return new MemoryRepository();를 주석한 후

나머지 코드들을 한번씩 실행했을때에도 서버올려서 프로젝트가 잘 진행되었었는데요 이때도 마찬가지로 MemoryMemberRepository클래스에는 @repository 어노테이션이 존재하고 있었는데 그렇다면 이상황도 문제가 되어야 하는게 맞는거 같은데 .. 프로젝트가 잘 진행되었었거든요. 이 경우 둘다 빈으로 등록이 될거같은데 아닌걸까요?

 

아래는 제가 말씀드린 상황의 코드입니다.

@Configuration

public class SpringConfig {

@Bean

public MemberRepository memberRepository() {

//return new MemoryMemberRepository(); 1. 자바코드

//return new JdbcMemberRepository(dataSource); 2. 순수JDBC연결 사용

//return new JdbcTemplateMemberRepository(dataSource); 3. 스프링 jdbcTEmplate 사용

return new JpaMemberRepository(em); //4. jpa 사용

}

*/

 }

  


 

@Repository

public class MemoryMemberRepository implements MemberRepository {

}

 

 

y2gcoder님의 프로필 이미지

2023. 03. 23. 17:15

저도 확실히 답변 드리고 싶어서 간단하게 나마 코드를 짜봤습니다.

폴더 구조는 다음과 같습니다.

image

TestService -> MemberService

image생성자 주입 때 어떤 객체가 주입되는지 보고 싶어서 로깅을 해뒀습니다.


TestRepository -> MemberRepository

image
TestRepositoryA -> MemoryMemberRepository 등에 대응

image
TestRepositoryB -> SpringDataJpaMemberRepository 에 대응

image

빈으로 사용할 각 클래스들의 코드들의 기본 상태는 위와 같습니다.

결론부터 말씀드리면 빈이 각각 등록된 것이 맞고, @Autowired를 사용하는 의존관계 자동 주입을 사용할 때 타입으로 주입할 빈을 찾기 때문입니다. 기존의 케이스에서는 의존관계를 수동으로 직접 주입해주고 있기 때문에 이상없이 작동합니다.

1) TestService: 수동 빈 등록, TestRepositoryA: 수동 빈 등록, TestRepositoryB: 자동 빈 등록

image

프로젝트 실행 로그:

image

TestRepositoryA가 testRepository 라는 빈 이름으로 등록되고, TestRepositoryB는 testRepositoryB라는 빈 이름으로 등록된 것을 볼 수 있습니다. 이는 빈 등록 방법의 차이 때문입니다.
- @Bean으로 수동 등록할 때: 메서드 이름이 빈으로 등록됩니다.
- @Component로 자동 등록할 때: 클래스의 첫 글자만 소문자로 바꿔 빈 이름으로 등록합니다.
의존성을 수동으로 직접 주입하고 있기 때문에 문제가 생기지 않습니다.

 

2) TestService: 자동 빈 등록, TestRepositoryA: 자동 빈 등록, TestRepositoryB: 자동 빈 등록

이 경우는 키썬님께서 처음 질문에서 작성하셨던 것과 똑같은 케이스라고 보시면 될 것 같습니다. 왜냐하면 SpringConfig에서 memberRepository 빈을 의존성 자동 주입해오는 것처럼TestService를 자동 빈 등록하면 관련 의존성을 자동으로 주입해오기 때문입니다.

TestService

image

위에다가 컴포넌트 스캔의 대상이 될 수 있도록 @Service를 달아주었습니다.

TestRepositoryA

image마찬가지로 컴포넌트 스캔의 대상이 될 수 있도록 @Repository를 달아주었습니다.

SpringConfig

image모두 주석 처리했습니다.

프로젝트 실행 로그:

image컴포넌트 스캔을 통해 스프링 빈을 등록하던 도중 MemberService의 의존성 자동 주입 단계에서 키썬님의 첫번째 코드와 동일한 오류(NoUniqueBeanDefinitionException)를 뱉습니다. 말씀드렸던 것처럼 @Autowired를 통해 자동 주입할 때는 타입으로 빈을 찾기 때문입니다.

해당 내용에 대한 부연설명과 해결방안은 스프링 핵심 원리 기본편을 수강하시면 더 자세하고 확실하게 아실 수 있습니다 :)
도움이 되셨으면 좋겠습니다!

키썬님의 프로필 이미지

작성한 질문수

질문하기