02C. JPA
02C. JPA ๊ด๋ จ
์ฐ๋ฆฌ๊ฐ ๋ง๋ค SBB๋ ์ง๋ฌธ ๋ต๋ณ ๊ฒ์ํ์ด๋ค. ์ง๋ฌธ์ด๋ ๋ต๋ณ์ ์์ฑํ๋ฉด ๋ฐ์ดํฐ๊ฐ ์์ฑ๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ฑฐ๋ ์กฐํํ๊ฑฐ๋ ์์ ํ๋ ๋ฑ์ ๊ธฐ๋ฅ์ ๊ตฌํํด์ผ ํ๋ค. ์น ์๋น์ค๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ ๋๋ถ๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ค.
๊ทธ๋ฐ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด SQL ์ฟผ๋ฆฌ(query)๋ผ๋ ๊ตฌ์กฐํ๋ ์ง์๋ฅผ ์์ฑํ๊ณ ์คํํ๋ ๋ฑ์ ๋ณต์กํ ๊ณผ์ ์ด ํ์ํ๋ค. ์ด๋ ORM(object relational mapping)์ ์ด์ฉํ๋ฉด ์๋ฐ ๋ฌธ๋ฒ๋ง์ผ๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ค๋ฃฐ ์ ์๋ค. ์ฆ, ORM์ ์ด์ฉํ๋ฉด ๊ฐ๋ฐ์๊ฐ ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ์ง ์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค.
์ด ์ฑ ์ ๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ๋ ์ด๋ SQL ์ฟผ๋ฆฌ์ ๊ธฐ์ด๋ ์๋ค๊ณ ๊ฐ์ ํ๋ค. ๊ทธ๋ฌ๋ ๋ ์๊ฐ ์ด๋ฐ ๊ธฐ์ด์ง์์ด ์์ด๋ ์ด ์ฑ ์ ์ค์ต์ ๋ฐ๋ผ ํ๊ณ ๋ด์ฉ์ ์ดํดํ๋๋ฐ ๋ฌด๋ฆฌ๊ฐ ์๋๋ก ๊ตฌ์ฑํ๋ค. ORM์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ํ ์ด๋ธ์ ์๋ฐ ํด๋์ค๋ก ๋ง๋ค์ด ๊ด๋ฆฌํ๋ ๊ธฐ์ ๋ก ์ดํดํด๋ ์ข๋ค.
ORM
SQL ์ฟผ๋ฆฌ์ ORM์ ๋น๊ตํด ๋ณด์. ๋ค์๊ณผ ๊ฐ์ ํํ๋ก ๊ตฌ์ฑ๋ ์ง๋ฌธ ํ ์ด๋ธ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฅํ๋ค๊ณ ๊ฐ์ ํด ๋ณด์.
question
ํ
์ด๋ธ ๊ตฌ์ฑ ์
id | subject | content |
---|---|---|
1 | ์๋ ํ์ธ์ | ๊ฐ์ ์ธ์ฌ๋๋ฆฝ๋๋ค ^^ |
2 | ์ง๋ฌธ ์์ต๋๋ค | ORM์ด ๊ถ๊ธํฉ๋๋ค |
... | ... | ... |
ํ์์
id
๋ ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ถํ๋ ๊ณ ์ณ๊ฐ์ด๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ค์ ์ ํตํด ๊ฐ์ด ์๋์ผ๋ก ์ฆ๊ฐ๋์ด ์ ์ฅ๋๋๋ก ํ ์ ์๋ค.
์ด๋ ๊ฒ ๊ตฌ์ฑ๋ question
ํ
์ด๋ธ์ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ์ฝ์
ํ๋ ์ฟผ๋ฆฌ๋ ๋ณดํต ๋ค์์ฒ๋ผ ์์ฑํ๋ค.
INSERT INTO question (subject, content) VALUES ('์๋
ํ์ธ์', '๊ฐ์
์ธ์ฌ๋๋ฆฝ๋๋ค ^^');
INSERT INTO question (subject, content) VALUES ('์ง๋ฌธ ์์ต๋๋ค', 'ORM์ด ๊ถ๊ธํฉ๋๋ค');
ํ์ง๋ง ORM์ ์ฌ์ฉํ๋ฉด ์ฟผ๋ฆฌ ๋์ ์๋ฐ ์ฝ๋๋ก ๋ค์์ฒ๋ผ ์์ฑํ ์ ์๋ค.
๋ค์์ฝ๋๋ ์์ฑํ ํ์์์ด ๋์ผ๋ก๋ง ํ์ธํ์.
Question q1 = new Question();
q1.setSubject("์๋
ํ์ธ์");
q1.setContent("๊ฐ์
์ธ์ฌ๋๋ฆฝ๋๋ค ^^");
this.questionRepository.save(q1);
Question q2 = new Question();
q2.setSubject("์ง๋ฌธ ์์ต๋๋ค");
q2.setContent("ORM์ด ๊ถ๊ธํฉ๋๋ค");
this.questionRepository.save(q2);
์์ ๊ฐ์ด ORM์ ์ด์ฉํ ๋ฐ์ดํฐ์ ์ฝ์ ์์ ๋ ์ฝ๋ ์์ฒด๋ง ๋๊ณ ๋ณด๋ฉด ์์ด ๋ง์ ๋ณด์ด์ง๋ง ๋ณ๋์ SQL ๋ฌธ๋ฒ์ ๋ฐฐ์ฐ์ง ์์๋ ๋๋ค๋ ์ฅ์ ์ด ์๋ค.
์ฝ๋์์ Question์ ์๋ฐ ํด๋์ค์ด๋ฉฐ, ์ด์ฒ๋ผ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉํ๋ ORM ํด๋์ค๋ฅผ ์ํฐํฐ(Entity)๋ผ๊ณ ํ๋ค. ORM์ ์ฌ์ฉํ๋ฉด ๋ด๋ถ์์ SQL ์ฟผ๋ฆฌ๋ฅผ ์๋์ผ๋ก ์์ฑํด ์ฃผ๋ฏ๋ก ์ง์ ์์ฑํ์ง ์์๋ ๋๋ค. ์ฆ, ์๋ฐ๋ง ์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ง์ํ ์ ์๋ค.
ORM์ ์ฅ์ ์ ๋ ์์๋ณด์
ORM์ ์ด์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ข ๋ฅ์ ์๊ด ์์ด ์ผ๊ด๋ ์ฝ๋๋ฅผ ์ ์งํ ์ ์์ด์ ํ๋ก๊ทธ๋จ์ ์ ์งยท๋ณด์ํ๊ธฐ๊ฐ ํธ๋ฆฌํ๋ค. ๋ํ ๋ด๋ถ์์ ์์ ํ SQL ์ฟผ๋ฆฌ๋ฅผ ์๋์ผ๋ก ์์ฑํด ์ฃผ๋ฏ๋ก ๊ฐ๋ฐ์๊ฐ ๋ฌ๋ผ๋ ํต์ผ๋ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๊ณ ์ค๋ฅ ๋ฐ์๋ฅ ๋ ์ค์ผ ์ ์๋ค.
JPA ๋?
์คํ๋ง๋ถํธ๋ JPA(Java Persistence API)๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฒ๋ฆฌํ๋ค. JPA๋ ์๋ฐ ์ง์์์ ORM(Object-Relational Mapping)์ ๊ธฐ์ ํ์ค์ผ๋ก ์ฌ์ฉํ๋ ์ธํฐํ์ด์ค์ ๋ชจ์์ด๋ค.
JPA๋ ์ธํฐํ์ด์ค์ด๋ค. ๋ฐ๋ผ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ์ค์ ํด๋์ค๊ฐ ํ์ํ๋ค. JPA๋ฅผ ๊ตฌํํ ๋ํ์ ์ธ ์ค์ ํด๋์ค์๋ ํ์ด๋ฒ๋ค์ดํธ(Hibernate)๊ฐ ์๋ค. SBB๋ JPA + ํ์ด๋ฒ๋ค์ดํธ ์กฐํฉ์ ์ฌ์ฉํ๋ค.
H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค
JPA๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ค์นํด ๋ณด์. ๊ฐ๋ฐ์์๋ Oracle, MSSQL ๋ฑ์ ๊ตต์งํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ณด๋ค๋ ์ค์น๋ ์ฝ๊ณ ์ฌ์ฉ๋ ํธ๋ฆฌํ H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค.
H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค
H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ฃผ๋ก ๊ฐ๋ฐ์ฉ์ด๋ ์๊ท๋ชจ ํ๋ก์ ํธ์์ ์ฌ์ฉ๋๋ ํ์ผ ๊ธฐ๋ฐ์ ๊ฒฝ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ด๋ค. ๊ฐ๋ฐ์์๋ H2๋ฅผ ์ฌ์ฉํ์ฌ ๋น ๋ฅด๊ฒ ๊ฐ๋ฐํ๊ณ ์ค์ ์ด์์์คํ ์ ์ข ๋ ๊ท๋ชจ์๋ DB๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ธ ๊ฐ๋ฐ ํจํด์ด๋ค.
๋ค์๊ณผ ๊ฐ์ด H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ค์นํ์.
ํ์ผ๋ช :
/sbb
/build.gradle
// (... ์๋ต. ...)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
}
// (... ์๋ต ...)
๊ทธ๋ฆฌ๊ณ "Refresh Gradle Project"๋ฅผ ์คํํ์ฌ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ์.
runtimeOnly
build.gradle
ํ์ผ์ runtimeOnly
๋ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ฐํ์(Runtime)์์๋ง ํ์ํ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ค. ์ปดํ์ผ(Compile)์์๋ง ํ์ํ ๊ฒฝ์ฐ์๋ runtimeOnly
๋์ compileOnly
๋ฅผ ์ฌ์ฉํ๋ค.์ค์นํ H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์ค์ ์ ํด์ผ ํ๋ค. ๋ค์๊ณผ ๊ฐ์ด application.properties
ํ์ผ์ ์์ ํ์.
ํ์ฌ application.properties
ํ์ผ์๋ ์๋ฌด๋ฐ ๋ด์ฉ์ด ์์ ๊ฒ์ด๋ค.
ํ์ผ๋ช :
/sbb/src/main/resources/
application.properties
# DATABASE
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/local
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
๊ฐ๊ฐ์ ํญ๋ชฉ์ ๋ํด์ ์์๋ณด์.
spring.h2.console.enabled
- H2 ์ฝ์์ ์ ์์ ํ์ฉํ ์ง์ ์ฌ๋ถ์ด๋ค. true๋ก ์ค์ ํ๋ค.spring.h2.console.path
- ์ฝ์ ์ ์์ ์ํ URL ๊ฒฝ๋ก์ด๋ค.spring.datasource.url
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์์ ์ํ ๊ฒฝ๋ก์ด๋ค.spring.datasource.driverClassName
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์์ ์ฌ์ฉํ๋ ๋๋ผ์ด๋ฒ์ด๋ค.spring.datasource.username
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฌ์ฉ์๋ช ์ด๋ค. (์ฌ์ฉ์๋ช ์ ๊ธฐ๋ณธ ๊ฐ์ธ sa๋ก ์ค์ ํ๋ค.)spring.datasource.password
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํจ์ค์๋์ด๋ค. ๋ก์ปฌ ๊ฐ๋ฐ ์ฉ๋๋ก๋ง ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํจ์ค์๋๋ฅผ ์ค์ ํ์ง ์์๋ค.
๊ทธ๋ฆฌ๊ณ spring.datasource.url
์ ์ค์ ํ ๊ฒฝ๋ก์ ํด๋นํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์ผ์ ๋ง๋ค์ด์ผ ํ๋ค. ์์์ spring.datasource.url
์ jdbc:h2:~/local
๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์์ ํ๋๋ ํฐ๋ฆฌ(~
์ ํด๋นํ๋ ๊ฒฝ๋ก) ๋ฐ์ local.mv.db
๋ผ๋ ํ์ผ์ ์์ฑํด์ผ ํ๋ค. ๋ง์ฝ jdbc:h2:~/test
๋ผ๊ณ ์ค์ ํ๋ค๋ฉด test.mv.db
๋ผ๋ ํ์ผ์ ์์ฑํด์ผ ํ๋ค.
์ฌ์ฉ์์ ํ๋๋ ํฐ๋ฆฌ๋ ์๋์ฐ์ ๊ฒฝ์ฐ์๋ C:\Users\(์ฌ์ฉ์๋ช
)
์ด๊ณ ๋งฅOS์ ๊ฒฝ์ฐ์๋ /Users/(์ฌ์ฉ์๋ช
)
์ด๋ค. ๋ณธ์ธ์ด ์ฌ์ฉํ๋ OS์ ๋ง๋ ํ๋๋ ํฐ๋ฆฌ์ local.mv.db
ํ์ผ์ ์์ฑํ์. ํ์ผ์ ๋ด์ฉ ์์ด ๋นํ์ผ๋ก ์์ฑํ๋ค.
๋งฅ OS์์ local.mv.db ํ์ผ ์์ฑํ๊ธฐ
touch local.mv.db
์ฌ๊ธฐ๊น์ง ๋ง๋ฌด๋ฆฌ ๋์์ผ๋ฉด ์ด์ H2 ์ฝ์์ ํตํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ํ ์ ์๋ค. ๋ธ๋ผ์ฐ์ ์์ ๋ค์์ URL ์ฃผ์๋ก H2 ์ฝ์์ ์ ์ํด ๋ณด์.
- http://localhost:8080/h2-console
ํ๊ตญ์ด๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ ์ธ์ด ์ค์ ์ "ํ๊ตญ์ด"๋ก ์ค์ ํ ์ ์๋ค.
JPA ํ๊ฒฝ์ค์
H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ ์ค๋น๊ฐ ์๋ฃ๋์๋ค. ์ด์ ์๋ฐ ํ๋ก๊ทธ๋จ์์ H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ํด์ผํ๋ค. ์๋ฐ ํ๋ก๊ทธ๋จ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ฑฐ๋ ์กฐํํ๋ ค๋ฉด JPA๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. ํ์ง๋ง JPA๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์ JPA๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ์ค๋น ์์ ์ด ํ์ํ๋ค.
JPA๋ฅผ ์ฌ์ฉํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ ์กฐ๊ธ ํ์ ์์ธํ ์์๋ณธ๋ค.
๋ค์์ฒ๋ผ build.gradle
ํ์ผ์ ์์ ํ์.
ํ์ผ๋ช :
/sbb
/build.gradle
// (... ์๋ต ...)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
// (... ์๋ต ...)
๊ทธ๋ฆฌ๊ณ "Refresh Gradle Project"๋ก ๋ณ๊ฒฝ์ฌํญ์ ์ ์ฉํ๋ฉด JPA ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋๋ค.
implementation
build.gradle
ํ์ผ์ implementation
์ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น๋ฅผ ์ํด ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ ์ค์ ์ด๋ค. implementation
์ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ณ๊ฒฝ๋๋๋ผ๋ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฐ๊ด๋ ๋ชจ๋ ๋ชจ๋๋ค์ ์ปดํ์ผํ์ง ์๊ณ ์ง์ ๊ด๋ จ์ด ์๋ ๋ชจ๋๋ค๋ง ์ปดํ์ผํ๊ธฐ ๋๋ฌธ์ rebuild ์๋๊ฐ ๋น ๋ฅด๋ค.๊ทธ๋ฆฌ๊ณ JPA ์ค์ ์ ์ํด application.properties
ํ์ผ์ ์์ ํ์.
ํ์ผ๋ช :
/sbb/src/main/resources/
application.properties
# DATABASE
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/local
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# JPA
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
์ถ๊ฐํ ํญ๋ชฉ์ ๊ฐ๋จํ๊ฒ ์ดํด๋ณด์.
spring.jpa.properties.hibernate.dialect
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ง ์ข ๋ฅ๋ฅผ ์ค์ ํ๋ค.spring.jpa.hibernate.ddl-auto
- ์ํฐํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ํ ์ด๋ธ์ ์์ฑํ๋ ๊ท์น์ ์ ์ํ๋ค.
spring.jpa.hibernate.ddl-auto
์ ์ค์ ์์ spring.jpa.hibernate.ddl-auto
๋ฅผ update๋ก ์ค์ ํ๋ค. update์ ๊ฐ์ ์ค์ ๊ฐ์ ๋ํด์ ๊ฐ๋จํ ์์๋ณด์.
none
- ์ํฐํฐ๊ฐ ๋ณ๊ฒฝ๋๋๋ผ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ณ๊ฒฝํ์ง ์๋๋ค.update
- ์ํฐํฐ์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ์ ์ฉํ๋ค.validate
- ๋ณ๊ฒฝ์ฌํญ์ด ์๋์ง ๊ฒ์ฌ๋ง ํ๋ค.create
- ์คํ๋ง๋ถํธ ์๋ฒ๊ฐ ์์๋ ๋ ๋ชจ๋ dropํ๊ณ ๋ค์ ์์ฑํ๋ค.create-drop
- create์ ๋์ผํ๋ค. ํ์ง๋ง ์ข ๋ฃ์์๋ ๋ชจ๋ drop ํ๋ค.
๊ฐ๋ฐ ํ๊ฒฝ์์๋ ๋ณดํต update ๋ชจ๋๋ฅผ ์ฌ์ฉํ๊ณ ์ด์ํ๊ฒฝ์์๋ none ๋๋ validate ๋ชจ๋๋ฅผ ์ฌ์ฉํ๋ค.