Kotlin + SpringBoot DB오류 해결

조갱·2022년 2월 7일
0

이슈 해결

목록 보기
1/15

회사 신입 개발자들과 협업 과정에서 처음으로 부딪힌 버그이다.
정말 별거 아니지만, 이거때문에 하루 하고도 반나절을 삽질했다..

0. 사건의 발단

신입으로 입사 후, 회사에서 연습용 프로젝트를 진행하던 중이었다. 입사 교육 중 TDD With JUnit5의 뽕맛(?)을 본 우리는 Test케이스를 작성하며 개발하기로 다짐했다. Mysql에 CRUD하는 테스트 코드를 작성했고, Passed가 떴다. 며칠 지나지 않아, 한 팀원이 'application.properties 수정했으니 Pull 새로 받으세요'라고 얘기한다. Pull을 받으려니까 뭔가 충돌이 난 모양이다. 기존에 SVN을 쓰던 나는 뭐가 문젠지 모르고 코드를 지우고 Pull을 받았다. (이게 사건의 원흉이었다..) 그리고는 Test 코드에서 Error가 발생한다.

1. 테스트 코드

    @Test
    @Transactional
    fun insertDummies() {
        val product = ProductEntity(pID = 1, pName = "a", pPrice = 1, pCntPerDay = 1, pLastSellDate = LocalDateTime.now(), pIsShow = true)
        productRepository.save(product)
    }

별거 없다. 이게 안돌아갈리가 없다. 안돌아가면 안된다.

2. 첫 번째 증상

2-1. could not prepare statement

org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement

이런 에러가 뜬다. 조금 더 아래로 내려본다.

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "COPRODUCTS" not found; SQL statement

어이가 없는건, 저 "COPRODUCTS" 테이블은 DB에 떡하니 존재한다.

3. 첫 번째 시도

내 DB에 테이블 명은, 정확히는 COProducts 였다. 대소문자가 안맞았다. 그래서 대문자로 이루어진 COPRODUCTS를 생성했다. 여전히 안된다.

4. 두 번째 시도

테이블 명에 오류가 있을거라고 생각하여, 명명규칙을 설정하는

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

properties를 application.properties에 추가했다. 역시 안된다.

5. 세 번째 시도

곰곰히 생각하던 중,

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException

에 관심을 가져보니 뭔가 h2가 거슬린다. 분명 어디서 본거같은데...
찾았다. build.gradle.kts에 숨어있었다. 다른 팀원이 'DB연결이 잘 안돼서 h2 DB를 써야겠다'고 말한게 어렴풋이 생각난다.

implementation("com.h2database:h2")

가차없이 이 줄을 지웠다.

6. 두 번째 증상

6-1. java.lang.IllegalStateException: Failed to load ApplicationContext

반짝이는 초록색 Passed 를 받을거라 예상했다. 그러나 돌아온건 시이이이이뻘건 빨간색 느낌표였다.(어림도 없지) 오류 내용을 보아하니,

Java.lang.IllegalStateException: Failed to load ApplicationContext

이건 또 뭘까,,, 좀더 단서가 될만한 내용을 찾아 마우스 휠을 아래로 내려본다.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource

모르겠다. 조금 더 내려본다.

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception

여전히 모르겠다. 좀만 더,,

Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class

흠.. Java는 에러 설명이 (아직까진)복잡하다. C# 할때는 어떤 파일의 몇번째 줄이라고까지 알려줬는데,,
여기서 좀 오래 헤매다, 처음부터 에러메시지를 꼼꼼하게 읽어보기로 했다.

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class

에러 콘솔창 위쪽에 이런 문구가 있다.! 저 url 어디서 많이 본거같다. 다시 application.properties로 돌아가본다.

7. 네 번째 시도

application.properties의 jdbc 연결 부분을 확인했다.

spring.datasource.firstdb.jdbc-url=jdbc:mysql://{DB_HOST, 가린것}/{DB_SCHEMA, 가린것}
spring.datasource.firstdb.username={DB_ID, 가린것}
spring.datasource.firstdb.password={DB_PW, 가린것}
spring.datasource.firstdb.driver-class-name=com.mysql.cj.jdbc.Driver

저기서, jdbc-url이 뭔가 수상하다. 그래서 구글에 spring.datasource를 검색해봤다.
다른 블로그들을 보니,

  1. firstdb가 없다
  2. jdbc-url이 아니라 그냥 url이다.

와 같은 차이점을 볼 수 있었다. 그래서 설정을 수정했다.

spring.datasource.url=jdbc:mysql://{DB_HOST, 가린것}/{DB_SCHEMA, 가린것}
spring.datasource.username={DB_ID, 가린것}
spring.datasource.password={DB_PW, 가린것}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

이번엔 진짜 느낌이 좋다. 결과는? 성공이다.

8. 왜 이런 문제가 발생했을까?

8-1. Merge시 충돌 코드 확인

위에 0. 사건의 발단을 보면, Git이 익숙하지 않아 충돌난 파일을 그냥 지우고 새로 받았다고 써있다. 맞다. 원래 내 코드는 정상이었으나... 다른 양반(?)들이 수정하고 충돌난 코드를 확인도 안하고 다시 받아버렸기 때문에, 뭐가 문젠지도 모르게 오류가 덮어씌워져 버렸다.

8-2. 인터넷에서 무지성으로 가져다 쓴 코드

h2 DB를 추가한답시고, 또 mysql을 연동한답시고 인터넷에 있는 소스를 긁어다 붙이기만 했다. 저 사람들은 아직 DB에 CRUD테스트를 안했었으니, DB쪽에 오류가 난줄도 모르고 있었던거다. (실제로, DB에 접근하는 Test코드를 주석처리 했더니 프로젝트는 정상적으로 실행됐다.) 그 상황에서 내가 최초로 DB 연동 CRUD 테스트를 했으니, 나에게서 오류가 발견된 것이고.

8-3. 번외로...

spring.datasource.firstdb.jdbc-url를 구글에 검색해봤더니, https://basketdeveloper.tistory.com/74 블로그가 나왔다. (그것도 딱 하나.) 마침 제목도 Spring에 MySQL 연동하기이다. 우리 사람들이 좋은 먹잇감이라 생각하고 복붙했나보다. 포스팅 주인분께서는 너무 좋은 포스팅을 써주셨지만, 쓰는 사람이 확인도 안하고 사용했던게 문제다.

9. 분석

그렇다면, 왜 jdbc-urlurl 로 수정하니까 오류가 해결됐을까?

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.data-access.configure-custom-datasource 를 통해 알 수 있었다.


결론은, connection pool마다 속성이 다르기 때문이다. 그리고, Spring boot에서 기본적으로 제공하는 외부 Datasource 설정은 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#data.sql.datasource.production 에서 확인할 수 있다.

그렇다면, 왜 firstdb를 붙이니까 오류가 났던걸까?

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.data-access.configure-two-datasources 를 통해 알 수 있었다.


결론은, 두 개 이상의 DataSource를 사용할 때 필요하기 때문이다. 그리고, 아래 예시처럼 @ConfigurationProperties 어노테이션에 설정도 따로 해주어야 한다.

실제로, 위 블로그 포스팅(https://basketdeveloper.tistory.com/74)을 보면, 아래와 같이 datasource에 대한 설정을 따로 해주셨다.
(블로그 본문 중 일부)

10. 느낀점

사실 이 오류를 해결하고 나서도, 왜 해결이 된 것인지?에 대한 이유는 정확히 알지 못했다. 일단 해결은 됐으니까 프로젝트를 계속 진행했다. 하지만 이슈해결에 대한 블로그 포스팅을 하며, 보다 정확한 정보전달을 위해 다시 자료를 찾아보고, 내가 먼저 이해하면서 복습을 통해 확실히 알게 되었다. 앞으로 이 문제는 다시 발생하지 않을 것이다. 또한, Git 관리에 대한 중요성, 코드리뷰에 대한 중요성과 인터넷에서 가져온 코드에 대해 무지성 Ctrl+C, V는 옳지 않다는걸 다시금 생각하게 되는 계기가 되었다. 그리고, Spring boot는 Document가 정말 잘돼있다,, 감탄.

끝!

==================================================================================

Reference :
https://stackoverflow.com/questions/59173037/spring-properties-jdbc-url-vs-url
https://stackoverflow.com/questions/27175460/springboot-datasource-configuration

검색어 : could not prepare statement, Java.lang.IllegalStateException, Failed to load ApplicationContext, Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource, Spring mysql 오류

profile
A fast learner.

0개의 댓글