[Spring] MyBatis 설정은 잘됐는데 왜 자꾸 에러가 뜨지?

이명범·2022년 4월 24일
0

트러블슈팅

목록 보기
1/8

1. NoSuchBeanDefinitionException

싸피 스프링 과제 중 Junit 테스트를 하는데, 자꾸 NoSuchBeanDefinitionException라는 에러가 발생했다. 설정은 분명 제대로 했는데 도대체 왜 안되는지 몰라서 한참을 해멨는데, 이 에러에 대한 해답을 알고나니 너무 바보같았다 ㅠ

그림 1) root-context.xml 파일

프로젝트의 root-context.xml 파일이다.

dataSource라는 빈이 있는데, 이 빈은 JndiObjectFactoryBean이라는 클래스를 스프링 컨테이너에 등록한다. 그런데 나는 아무 생각 없이 DataSource라는 이름으로 등록되었다는 이유만으로 DataSource 클래스에 이 빈을 주입시키려는 바보같은 짓을 했다,,,,

그림 2) 테스트 코드

위의 코드가 테스트 코드이다. DataSource는 빈으로 등록되어있지 않았는데, @Autowired로 등록하려고 해서 벌어진 예외 상황이었다. 원래 쓰려고 했던 클래스는 SqlSession이었으므로 DataSource 클래스 대신 SqlSession으로 변경하니 이 예외는 사라졌다.

하지만 저거를 고치고 나니 새로운 에러가 터져버렸다...

2.javax.naming.NoInitialContextException

이 예외는 JNDI가 초기화에 실패해서 발생하는 예외이다. 오류 로그는 다음과 같이 발생했다.

Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial
	at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:702)
	at java.naming/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
	at java.naming/javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:342)
	at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)
	at org.springframework.jndi.JndiTemplate.lambda$lookup$0(JndiTemplate.java:157)
	at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:92)
	at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:157)
	at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:179)
	at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:96)
	at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:114)
	at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:239)
	at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:225)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)

이거에 대해서 구글링을 해보니 JNDI는 WAS에 설정 정보가 있고, WAS를 켰을 때 그에 대한 정보를 읽는다고 하는데 JUnit은 WAS 없이 테스트하기 때문에 JNDI가 초기화되지 않아서 발생하는 것 같다. 실제로 WAS를 켜서 실제 서비스에서 DB 연동이 되는지 확인해보니 제대로 작동하는 걸 확인할 수 있었다.

그림 3) 실제 서비스 환경

JUnit 테스트 환경은 WAS와 별개이고 여기에서 JNDI를 사용하기 위해서는 따로 설정을 해줘야 한다는 말이다.

그 설정은 다음과 같다.

package com.ssafy.ws;

import org.junit.runners.model.InitializationError;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jndi.JndiTemplate;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

public class TestSpringRunner extends SpringJUnit4ClassRunner {

	public TestSpringRunner(Class<?> clazz) throws InitializationError {
		super(clazz);
		try {
        bindJndi();
    } catch (Exception e) {
        
    }
	}

	@SuppressWarnings("deprecation")
	private void bindJndi() throws Exception {
        SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
        builder.activate();
        
        JndiTemplate jndiTemplate = new JndiTemplate();
        
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/ssafydb?useSSL=false&amp;serverTimezone=UTC");
        dataSource.setUsername("ssafy");
        dataSource.setPassword("ssafy");
        
        jndiTemplate.bind("java:comp/env/jdbc/ssafy", dataSource);
    }
}

SpringJUnit4ClassRunner를 상속받아 TestSpringRunner라는 클래스를 만들어준다. 그리고 bindJndi()라는 메서드를 생성해서 jndiTemplate이라는 객체를 만들어서 데이터 소스를 바인딩한다. 그러면 나중에 테스트 환경에서 jndi를 로드하려고 할 때, jndiTemplate에 바인딩되어 있는 java:comp/env/jdbc/ssafy라는 이름의 데이터 소스가 있기 때문에 WAS가 없어도 스프링 컨테이너에 빈을 등록해서 사용할 수 있게 된다. (참고로 테스트 환경을 아래 사진처럼 위에 만든 TestSpringRunner 클래스로 돌려줘야한다.

그림 4) TestSpringRunner 환경에서 테스트해야한다.

이렇게 하고나니 드디어 정상적으로 테스트를 통과하는 걸 확인할 수 있었다!!!!!!

그림 5) 테스트를 통과하는 모습

3. 고찰

JUnit은 항상 스프링 부트 환경에서 간단한 테스트만 해봐서 WAS 없이 스프링 환경에서 단위 테스트를 돌리려고 하니까 제대로 되지 않는 부분이 이렇게 많을 줄은 몰랐다. 제대로 된 단위 테스트를 만들기 위해서 테스트에 대한 공부가 많이 필요하다는 것을 이번 싸피 과제를 통해 알게 되었다.

4. 참고자료

https://developyo.tistory.com/82

profile
백엔드 개발자가 될거야

0개의 댓글