Spring์ In memory-DB์ธ H2๋ฅผ ํ์ฉํด์ ์ฝ๋ฉ์ ํ๋ค๋ณด๋ฉด,์๋ฒ๋ฅผ ํ๋ฒ ๋๊ณ ํค๋ ๋์ ๊ทธ์์ ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ๋ ๋ผ๊ฐ๊ฒ๋๋ค.
Test ์ฉ๋๋ก๋ ํ๋ฅญํ์ง๋ง,๋๋๋ง๋ค ์๊น ๋ฃ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์ฝ์ ํ๊ณ ๋ฃ๊ณ ...ํ์ธํ๋ ๊ณผ์ ์ ๊ฑฐ์น๋๊ฒ์ ์ฌ๊ฐ ๊ท์ฐฎ์ ๊ฒ์ด ์๋๋ค.
Spring์์๋ ์๋์ผ๋ก SQL๋ฌธ์ ๋ฃ์ด์ฃผ๋ ๊ธฐ๋ฅ์ด ์ธํ ๋์ด์๋ค.
๋ฐฉ์์ ์ด๋ฐ์์ผ๋ก ์๋๋์ด์ง๋ค.๋ด๊ฐ ๋ฐ๋ก ์ค์ ์ ํ์ง ์์๋,resources
์์ data.sql
,schema.sql
๋ ์์ ์ด ์ฌ์ฉํ๊ณ ์๋ DB์ ๋ง์ถฐ์ ์๋ฒ๊ฐ ์ผ์ง์ ๋ฐ๋ผ์ ์๋์ผ๋ก data.sql
์ DML (Insert๋ฌธ) schema.sql
์ DDL(Create)๋ฌธ์ ์๋์ผ๋ก ์ ์ฉ์์ผ์ค๋ค.
๋ ๋์๊ฐ์, Spring Data JPA๋ DB์ ๋ณ๊ฒฝ์ ๋ฐ๋ฅธ SQL๋ฌธ์ ๋ณ๊ฒฝ ์ํฉ๋ ๊ณ ๋ คํ์ฌ ํ๋ ํผ์ ๋ฐ๋ผ์ SQL๋ฌธ ์ญ์ ์๋์ํฌ ์ ์๋ค.
schema-${platform}.sql
, data-${platform}.sql
์ ๊ฐ์ ํํ๋ก DBํ๋ ํผ์ ๋ฐ๋ผ์ ๋ณ๊ฒฝํ ์ ์์ผ๋ฉฐ, ๊ทธ ์ค์ ์ application.properties์์ ํน์ yamlํ์ผ์์ DB ํ๋ ํผ ๋ช
์ ๋ฐ๋ผ์ ์๋ํ ์ ์๋ค. -> spring.datasource.platform
= hsqldb
, h2
, oracle
, mysql
, postgresql
๋ฑ๋ฑ...
์ฐธ๊ณ ๋ก Schema์ ๊ฒฝ์ฐ Hibernate์์ ์ค์ ์ ๋ฐ๋ก ํ์ง ์์ผ๋ฉด ์๋์ผ๋ก DDL์ด ์ฝ์ ๋๊ฒ ๋์์ด์, ๋ง์ฝ ์์ ๋ง์ ์คํค๋ง๋ฅผ ๋ฐ๋ก ์ก์์ค์ผํ๋ ๊ฒฝ์ฐ์๋ ๋ฐ๋์ ์ต์ ์ ์ค์ ํด์ค์ผํ๋ค.
spring.jpa.generate-ddl
๋ spring.jpa.hibernate.ddl-auto
๋ฅผ ์ค์ ์ ๋ฐ๊ฟ์ค์ผ์ง ๋์์ ์ฝ์
์ด ๋์ง ์์, ์ค๋ฅ๋ฅผ ๋ฐ์ํ ์ ์๋ ๊ตฌ์์ ๋ ์ขํ ์ ์๋ค.
{% hint style="success" %}
์ข ๋ ๊ตฌ์ฒด์ ์ธ DB ์ฝ์
๊ณผ ๊ด๋ จ๋ ์ต์
๋ค์ด ์๋๋ฐ, spring.datasource.continue-on-error
๋ ๋ฐ์ดํฐ๊ฐ ์ด์ํ๊ฒ ๋ฃ์ด์ ธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ผ๋ ๊ทธ๋ฅ ์งํ ์ํค๊ฒ ํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
spring.datasource.sql-script-encoding=UTF-8
data.sql
์ ํ์ฉํ๋ฉด์ ์ธ์ฝ๋ฉ ์ต์
์ ๋ฐ๋ก ์ค ์ ์๋๋ฐ ๊ฒ์์ ๋ง์ดํด๋ณธ ๊ฒฐ๊ณผ, ํ๊ธ ์ต์
์ ์ฌ๋๋ก ์ฃผ์ง ์๋๋ค๋ฉด, ๊นจ์ ธ์ ๋ค์ด๊ฐ์ ์ธ์ฝ๋ฉ ์ต์
์ ์ค์ ์๊นจ์ง๊ฒ ๋ฃ๋ ๋ฐฉ๋ฒ๋ ์์๋ค.
{% endhint %}
๋ ๋์๊ฐ์, Spring Data JPA๋ DB์ ๋ณ๊ฒฝ์ ๋ฐ๋ฅธ SQL๋ฌธ์ ๋ณ๊ฒฝ ์ํฉ๋ ๊ณ ๋ คํ์ฌ ํ๋ ํผ์ ๋ฐ๋ผ์ SQL๋ฌธ ์ญ์ ์๋์ํฌ ์ ์๋ค.
schema-${platform}.sql
, data-${platform}.sql
์ ๊ฐ์ ํํ๋ก DBํ๋ ํผ์ ๋ฐ๋ผ์ ๋ณ๊ฒฝํ ์ ์์ผ๋ฉฐ, ๊ทธ ์ค์ ์ application.properties์์ ํน์ yamlํ์ผ์์ DB ํ๋ ํผ ๋ช
์ ๋ฐ๋ผ์ ์๋ํ ์ ์๋ค. -> spring.datasource.platform
= hsqldb
, h2
, oracle
, mysql
, postgresql
๋ฑ๋ฑ...
์ฐธ๊ณ ๋ก Schema์ ๊ฒฝ์ฐ Hibernate์์ ์ค์ ์ ๋ฐ๋ก ํ์ง ์์ผ๋ฉด ์๋์ผ๋ก DDL์ด ์ฝ์ ๋๊ฒ ๋์์ด์, ๋ง์ฝ ์์ ๋ง์ ์คํค๋ง๋ฅผ ๋ฐ๋ก ์ก์์ค์ผํ๋ ๊ฒฝ์ฐ์๋ ๋ฐ๋์ ์ต์ ์ ์ค์ ํด์ค์ผํ๋ค.
spring.jpa.generate-ddl
๋ spring.jpa.hibernate.ddl-auto
๋ฅผ ์ค์ ์ ๋ฐ๊ฟ์ค์ผ์ง ๋์์ ์ฝ์
์ด ๋์ง ์์, ์ค๋ฅ๋ฅผ ๋ฐ์ํ ์ ์๋ ๊ตฌ์์ ๋ ์ขํ ์ ์๋ค.
ํํธ: ์ข ๋ ๊ตฌ์ฒด์ ์ธ DB ์ฝ์
๊ณผ ๊ด๋ จ๋ ์ต์
๋ค์ด ์๋๋ฐ, spring.datasource.continue-on-error
๋ ๋ฐ์ดํฐ๊ฐ ์ด์ํ๊ฒ ๋ฃ์ด์ ธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ผ๋ ๊ทธ๋ฅ ์งํ ์ํค๊ฒ ํ๋ ๋ฐฉ๋ฒ์ด ์๊ณ ,
spring.datasource.sql-script-encoding=UTF-8
data.sql
์ ํ์ฉํ๋ฉด์ ์ธ์ฝ๋ฉ ์ต์
์ ๋ฐ๋ก ์ค ์ ์๋๋ฐ ๊ฒ์์ ๋ง์ดํด๋ณธ ๊ฒฐ๊ณผ, ํ๊ธ ์ต์
์ ์ฌ๋๋ก ์ฃผ์ง ์๋๋ค๋ฉด, ๊นจ์ ธ์ ๋ค์ด๊ฐ์ ์ธ์ฝ๋ฉ ์ต์
์ ์ค์ ์๊นจ์ง๊ฒ ๋ฃ๋ ๋ฐฉ๋ฒ๋ ์์๋ค.
์ ๊ฒ๋ง์ผ๋ก๋ ํฐ ์ํ์ด์์ผ๋, TDD๋ฅผ ์ํด์ JPA TestCode๋ฅผ ์์ฑ์ค์ด์๋๋ฐ ์ค๋ฅ๊ฐ ์ง์์ ์ผ๋ก ๋ฐ์ํ๋ค.
@DataJpaTest ์ด๋ ธํ ์ด์ ์ ์ฝ์ ํ๊ณ ์คํํ์ ๊ฒฝ์ฐ ์๊พธ ์ด๋ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement ENGINE=[*]INNODB"; expected "identifier"; SQL statement:
์ ์ด๋ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ ํ์ธํด๋ณด๋๊น
Application.yaml ์ dialect์ ์กฐ๊ฑด์ mysql engine์ innodb๋ก ์คฌ๊ธฐ๋๋ฌธ์ ์๊พธ DDL๋ฌธ์ด ENGINE=INNODB ๊ณผ ๊ฐ์ ์ค๋ฅ๋ฅผ ๋ง๋ค์ด ๋๋ค.
spring:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
storage_engine: innodb # <-๋ฐ์ง์๋ฉด ์ด์น๊ตฌ๊ฐ ๋ฌธ์ ์๋ค.
datasource:
hikari:
jdbc-url: jdbc:h2:mem://localhost/~/testdb;MODE=MYSQL
continue-on-error: true
datasource๋ ๋ถ๋ช ํ ์ฌ๋๋ก MODE=MYSQL์ธ๋ฐ, ๋๋์ฒด ์ ์๋๋๊ฐ ํ๋๋๋ง, ๊ณต์๋ฌธ์์ ์ด๋ ธํ ์ด์ ์ ํ๊ณ ํ๊ณ ๊ฐ๋ณด๋ฉด ๋ค์์ฒ๋ผ ๋์ด์๋ค.
@AutoConfigureTestDatabase //์ด ์ด๋
ธํ
์ด์
์ ํจ์์ธ
// ใด ๋ฐ์
EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE; // ์ด ์๋ฒ ๋๋Connection์ ์กฐ๊ฑด์ค์ H2์ต์
์ ํ์ธํด๋ณด๋ฉด
//ใด ์์
H2(EmbeddedDatabaseType.H2, DatabaseDriver.H2.getDriverClassName(),
"jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", (url) -> url.contains(":h2:mem")),
H2 ์ต์ ์ด ์๋์ค์ ๋ ๊ฒ์ด๋ค. (๋ญ ๋ด๊ฐ ์ค์ ์ ๋ฐ๋กํ์ง์์ผ๋ฉด, ์๋์ผ๋ก AutoConfiguration์ ์ํด์ h2๋ก ์กํ ๊ฒ์ด๋ค. ) ๊ทผ๋ฐ ์ JDBC ์กฐ๊ฑด์๋ MYSQL์กฐ๊ฑด์ด ์์ผ๋ฏ๋ก dialect๊ฐ MySQL5InnoDBDialect ์ด๋ผ๋ฉด ๋น์ฐํ ์คํค๋ง ์ ์ฉ์ ์ค๋ฅ๊ฐ ๋๊ณ DML ๋ถ๋ถ์์๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
@DataJPATest๋ฅผ ํ๋ฉด ์๋์ผ๋ก @AutoConfigureTestDatabase
๊ฐ ์ ์ฉ์ด ๋๋๊น ์ฐ๋ฆฌ๋ ๋ฌด์จ ์๋ฅผ ์จ๋ H2์ธ ๊ฒฝ์ฐ์๋ H2์ ์ต์
๋ณ๊ฒฝ์ด ์๋๋ค๋ ๋ป์ด๊ธฐ๋ ํ๋ฐ...
๋ฌผ๋ก Spring๊ฐ๋ฐ์๋ค์ด ๊ทธ๋ ๊ฒ ๋นก๋นกํ๊ฒ ๊ตด๋ฆฌ๋ ์์ง์์๊ฐ? ๋ด๊ฐ ํ ์คํธ์ฉ DB๋ฅผ H2๋ก ๊ณ ์ ํ ๊ฑด ์๋ํ ๋ฐ, ๋ค๋ฅธ DB๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ ๋ํ ๋์ฒต๋ ์ญ์ ๋ง๋ จ๋์ด์๋ค.
๊ณต์๋ฌธ์: https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-database-initialization
@AutoConfigureTestDatabase
๋ฅผ ์ ๋๋ฌ๋ณธ๋ค๋ฉด, ๋ค์๊ณผ ๊ฐ์ ๊ต์ฒด๊ฐ๋ฅํ Enum์ด ์กด์ฌํ๋ค.
Replace replace() default Replace.ANY;
enum Replace {
ANY,
AUTO_CONFIGURED,
NONE
}
๊ธฐ๋ณธ์ ์ผ๋ก ANY๊ฐ ์ ํ๋์๊ณ , ANY๋ NONE, AUTO_CONFIGURED ์ค ํ๋ ์ ํํ๋๊ฒ์ธ๋ฐ, ๊ฑฐ์ ๋๋ถ๋ถ AUTO๋ก ์ธ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ํํ ๊ฒ์ด๊ธฐ๋๋ฌธ์ ์ฐ๋ฆฌ๋ ๋ฐ๋์ NONE์ ์ ํํด์ผ์ง๋ง, url ์ต์ ๋ค์ด ์ฌ๋ผ์ง์ฑ๋ก ์ฐ๋ฆฌ๊ฐ ์ง์ด๋ฃ๋ DB์ต์ ๊ฐ์ ๋ฐ์์ DB Testing์ ์งํ์์ผ์ค๋ค.
์ด๋ฌํ DB์ต์ ๋ค์ ๋น์ฐํ Application.yaml์ด๋ properties์์ DB๊ด๋ จ ์ ๋ณด๋ค์ ๋ฃ์ ์ ์๋ค.
@ActiveProfiles("....")
์ ๋ฐ๋ก ์ง์ ํด๋์ง ์์ผ๋ฉด, ๊ธฐ๋ณธ์ผ๋ก ์ค์ ๋์ด์๋ application.yaml์ผ๋ก ๋ถ์ด์ ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ๊ณต์ฉ์ผ๋ก ์ฌ์ฉ๋์ด์ง๋ ์ต์
์ผ๋ก ์ฒ๋ฆฌ๊ฐ ๋์ด์ ๋ ๊ฐ์ ๊ฒฝ์ฐ๋ ์ฐจ๋ผ๋ฆฌ Test์ฉ application.yaml์ ๋ฐ๋ก ๋ง๋ค๊ณ ๋ฐ๋ก profile์ ์ง์ ํด์ @ActiveProfiles("....")
์ ํตํด์ ํ์ฑํ์ํค๋ ๋ฐฉํฅ์ผ๋ก ๋ฐ๊พธ์๋ค.
{% hint style="info" %} @DataJpaTest๋ @Transactional ์ด๋ ธํ ์ด์ ์ด ๋ค์ด์์ด์ ํ ์คํธ ๊ธฐ๋ฅ์ค์๋ ํ ์คํธ๊ฐ ๋๋๋ฉด ์๋ RollBack๋๊ฒ ๋์๋ค.์๋์ฒ {% endhint %}
2021-04-30 16:57:37.854 INFO 85053 --- [ Test worker] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test:
{% hint style="info" %} spring.jpa.show-sql ์ญ์ true์ฒ๋ฆฌ๋์ด์์ด์ ๋ฐ๋ก ์ค์ ํด์ฃผ์ง ์์๋ ์๋์ผ๋ก ํ์ธ๊ฐ๋ฅํ๋ค. {% endhint %}
TestCode๋ฅผ ์์ฑํ๋ ค๋ค๋ณด๋ ํ์คํ ์ป๋ ์ ์ด ๋ง์์ก๋ค. ๋ด๊ฐ ์์ง ๋ชปํ๋ ๊ฒ๋ค์ ๋ํด์ ์ฌ๋๋ก ์๊ณ ๋์ด๊ฐ ์ ์์ด์ ์ข์๋๊ฒ ๊ฐ์ ์ ๋ฆฌ๋ ์ข ๋นก์ธ๊ฒ ์งํํด๋ดค๋ค.
๋ธ๋ก๊ทธ: https://kangwoojin.github.io/programing/auto-configure-test-database/
https://pravusid.kr/java/2018/10/10/spring-database-initialization.html