본문 바로가기
프로젝트/selfmade Blog - V1 (deprecated)

4. 스프링을 사용한 의존성 주입

by zangsu_ 2023. 8. 12.

지금의 코드는 각각의 의존성을 모두 클래스가 직접 관리하고 있다. 

그러나, 결국 스프링이 제공하는 AOP등을 사용하기 위해서는 우리의 클래스를 스프링 빈으로 등록해 두어야 할 것이고, 빈으로 등록하는 김에 스프링이 직접 의존성을 관리해 주는 것이 좋을 것 같다는 생각이 들었다.

스프링에게 의존성을 주입 받도록 코드를 수정해 주자.

 

수동으로 의존성 설정

applicationContext.xml

스프링에 수동으로 빈을 등록하고, 의존성 관계를 주입하기 위해서 xml 파일에 의존성을 명시해 두도록 하자.
applicationContext.xml의 위치는 main > resource 바로 아래에 두었다.

다음은 applicationContext.xml의 전문이다.

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  
    <bean id="DAOContext" class="com.example.selfmadeBlog.DAO.DAOContext"/>     
    
    <bean id="postingDAO" class = "com.example.selfmadeBlog.DAO.PostingDAO">  
        <property name="daoContext" ref="DAOContext"/>  
    </bean>  
  
    <bean id="userDAO" class="com.example.selfmadeBlog.DAO.UserDAO">  
        <property name="daoContext" ref="DAOContext"/>  
    </bean>    
</beans>


다음과 같이 각 클래스들을 빈으로 등록해 두고, 의존성 주입을 위해 `<property>` 태그를 사용했다.

위와 같은 의존성 주입에는 setter가 사용될 것이기 때문에 `PostingDAO`와 `UserDAO`에 `setDAOContext` 메서드를 생성해 주자.

추가적으로, 스프링에서 빈으로 등록하기 위한 모든 클래스들은 기본 생성자를 가지고 있어야 한다.
이전 코드에서 싱글톤으로 객체를 관리하기 위해 `private`으로 설정해 두었던 기본생성자를 `public`으로 만들어 주거나, 모든 생성자를 전부 지워주자.

public class PostingDAO {  
  
    private DAOContext daoContext;    
  
    public void setDaoContext(DAOContext daoContext) {  
        this.daoContext = daoContext;  
    }
}


마지막으로, 테스트코드에서도 해당 스프링 빈을 가지고 와야 테스트를 진행할 수 있다.
테스트코드에서는 `ApplicationContext` 객체를 하나 생성해서, 등록되어 있는 빈을 가져 올 것이다.

@ExtendWith(SpringExtension.class)  
class PostingDAOTest {  
  
    ApplicationContext context = new 
    ClassPathXmlApplicationContext("/applicationContext.xml"); 
  
    UserDAO userDAO = context.getBean(UserDAO.class);  
    PostingDAO postingDAO = context.getBean(PostingDAO.class);
    
    //...


위와 같이 등록된 스프링 빈을 가져온 후, 테스트코드를 실행해 보자.

테스트 결과는 성공이다!

 

자동으로 의존성 주입

그런데, 우리가 의존성 관계를 명시하지 않아도 스프링은 알아서 적절한 클래스를 찾아서 의존성을 주입해 줄 수 있다. 
`@Autowired`를 사용해서 의존성을 주입해 보자.

이제 설정 파일은 사용하지 않을 것이기 때문에 `applicationContext.xml` 파일은 삭제해 준다.

가장 먼저, 의존성 주입을 받을 클래스와 의존성 주입에 사용될 클래스 모두 스프링 빈으로 등록해 두어야 한다.

모두 DB에 접근하기 위한 클래스이니 `@Repository` 어노테이션을 사용해 등록해 준다.
그리고 `DAOContext` 클래스를 사용할 두 개의 DAO 클래스 내부의 `DAOContext` 필드에는 `@Autowired`를 붙여준다.

@Repository  
public class DAOContext{/*...*/}

@Repository  
public class PostingDAO{  
@Autowired  
private DAOContext daoContext;
/*...*/
}

@Repository  
public class UserDAO{  
@Autowired  
private DAOContext daoContext;
/*...*/
}


테스트코드에서도 `UserDAO`, `PostingDAO`와 같은 빈들을 주입 받아야 해당 클래스에 구현되어 있는 로직들을 테스트 해 볼 수 있다.
그런데, 해당 빈들이 생성되어 있는 곳은 `main` 패키지이고, 테스트 클래스들은 `test` 패키지에 생성된다. 

때문에 테스트클래스에서는 의존성 주입을 위해 다음과 같이 작성해 주어야 한다.

@SpringBootTest  
@SpringJUnitConfig(classes = SelfmadeBlogApplication.class)  
class PostingDAOTest {  
    @Autowired  
    UserDAO userDAO;  
    @Autowired  
    PostingDAO postingDAO;

//...//
}

`@SpringJUnitConfig(classes = SelfmadeBlogApplication.class)`를 사용해 메인 어플리케이션 클래스의 경로에 있는 빈들을 주입할 수 있게 되는 것이다.

 

아래 포스팅들의 도움을 받았다.

https://velog.io/@yeppi/SpringBoot2-Annotation-%EC%9E%90%EB%8F%99-%EC%84%A4%EC%A0%95-starter-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0

 

SpringBoot(2) @Annotation, 자동 설정, starter 활용하기

1. 기본적인 SpringBoot 활용 @SpringBootApplication 메인 실행 파일 묵시적으로 해당 패키지(package com.rubypaper;)를 베이스 패키지로 지정하고 있다 @SpringBootApplication 은 아래 어노테이션들이 포함

velog.io

https://cseella.tistory.com/184

 

@SpringJUnitConfig 어노테이션이란?

Spring Boot 기반으로 진행하고 있는 프로젝트에서 Test를 진행하다, @SpringJUnitConfig를 붙이느냐 안붙이느냐에 따라 생성자 자동 주입이 되느냐 안되느냐가 결정되는 것을 볼 수 있었다. 물론 이를 통

cseella.tistory.com

 

이제 테스트를 실행해 보자.

테스트 실패

테스트가 실패한다...!

스택 트레이스를 살펴보자.

java.lang.IllegalStateException: 
Failed to load ApplicationContext for ...
	at
//...
Caused by: org.springframework.beans.factory.BeanCreationException: 
	Error creating bean with name 'dataSource' defined 
	in class path resource [...] : Failed to instantiate 
	[...] : Factory method  'dataSource' threw exception with
    message: Failed to determine a suitable driver class
	at 
//...
Caused by: org.springframework.beans.BeanInstantiationException: 
	Failed to instantiate [...]: 
	Factory method 'dataSource' threw exception with message:
	Failed to determine a suitable driver class
	at 
	... 110 more
Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
$DataSourceBeanCreationException: Failed to determine a suitable driver class
at app//org.springframework.boot.autoconfigure.jdbc.DataSourceProp
... 111 more


간단히 말하자면, `dataSource` 빈을 생성하지 못한다는 것이다.

분명 나는 `dataSource` 라는 빈을 정의해 준 적도, 사용한 적도 없는데 왜 이런 오류가 생기는 것일까?

 

스프링부트의 dataSource 빈 자동 등록

우리는 JDBC를 사용하기 위해 gradle에 다음과 같은 의존성을 추가해 주었다.

//db connect  
implementation('mysql:mysql-connector-java:8.0.33')


이 때 스프링부트는 자동으로 데이터의 커넥션 풀 관리 등의 기능을 제공하는 `dataSource` 라는 이름의 빈을 등록한다. 이 때 스프링은 `application.properties`에 있는 설정을 기본으로 빈을 생성한다.

 

테스트 코드의  `@SpringBootTest`

스프링부트는 `@SpringBootTest` 어노테이션을 사용한 테스트 클래스를 실행시키면 통합테스트를 위한 환경을 만들어 준다.
모든 빈을 스캔하고, 어플리케이션 컨텍스트를 생성하는데 이 때 자동으로 `dataSource` 빈을 생성한다.

자동으로 `dataSource` 빈을 생성하지 않게 하려면 다음의 방법들을 사용할 수 있다.

1. 테스트코드에 자동 설정 제외하기

@ExtendWith(SpringExtension.class)  
@SpringBootTest  
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)  
@SpringJUnitConfig(classes = SelfmadeBlogApplication.class)  
class PostingDAOTest { /*...*/ }

`@EnableAutoConfiguration`으로 `dataSource`의 자동 설정을 제외해 `dataSource` 빈을 생성하지 않을 수 있다.

2. 메인 어플리케이션 클래스에서 자동 설정 제외하기

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)  
public class SelfmadeBlogApplication { /*...*/ }

같은 설정을 메인 클래스에서 설정해 주어도 된다.



그러나, 나중에 리팩토링을 하면서 언젠가 사용하게 될 빈이니 이왕이면 제대로 된 설정으로 생성해 두자.

`application.property`

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 
spring.datasource.url=jdbc:mysql://localhost:3306/selfmadeblog 
spring.datasource.username=root  
spring.datasource.password=111111

\

다시 테스트가 성공한다!!

 

도움 받은 문서들


https://recordsoflife.tistory.com/1431

 

테스트를 위한 별도의 Spring DataSource 구성

1. 개요 JPA와 같은 지속성 계층에 의존하는 Spring 애플리케이션을 테스트할 때 애플리케이션을 실행하는 데 사용하는 것과는 다른 더 작고 빠른 데이터베이스를 사용하도록 테스트 데이터 소스

recordsoflife.tistory.com

https://velog.io/@ohzzi/SpringBoot-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%97%90-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%9A%A9-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0

 

SpringBoot 테스트에 테스트용 데이터베이스 연결하기

(SpringBoot 2.7.1 버전을 기준으로 작성되었습니다.) 우아한테크코스 레벨 1에서 작성했던 체스 미션을 레벨 2에서 스프링 어플리케이션으로 바꾸는 과정을 진행하고 있다. JdbcTemplate를 사용하는 DAO

velog.io

https://mangkyu.tistory.com/242

 

[Spring] 스프링부트 테스트를 위한 의존성과 어노테이션, 애플리케이션 컨택스트 캐싱(@SpringBootTes

스프링부트에서 테스트를 작성하기 위한 다양한 어노테이션(@SpringBootTest, @WebMvcTest, @DataJpaTest)들을 알아보도록 하겠습니다. 실제 테스트를 작성하는 방법은 이 포스팅을 참고해주세요. 1. 스프링

mangkyu.tistory.com

https://prolog.techcourse.co.kr/studylogs/2445

 

우아한테크코스 학습로그 저장소

우아한테크코스 크루들이 배운 내용을 기록하는 학습로그 저장소입니다.

prolog.techcourse.co.kr

https://ksh-coding.tistory.com/88

 

[Spring] JdbcTemplate 스프링 빈은 어떻게 자동으로 등록될까?(feat.DataSource)

❓ 해당 주제를 찾아본 이유 JdbcTemplate을 쓸 때 여러 구글링을 통해 사용 방법을 알아보게 되었다. 대부분의 사람들이 JdbcTemplate 을 주입받아서 사용하는 것이 아니라, DataSource 를 주입받아서 JdbcT

ksh-coding.tistory.com

https://velog.io/@daehoon12/Spring-DB-%EC%9E%90%EB%8F%99-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EB%93%B1%EB%A1%9D

 

[Spring DB] 자동 리소스 등록

[Spring DB] 자동 리소스 등록

velog.io

 

댓글