Bibi's DevLog ๐Ÿค“๐ŸŽ

[Spring, H2 database] ์Šคํ”„๋ง ์•ฑ๊ณผ H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐํ•˜๊ธฐ ๋ณธ๋ฌธ

๐Ÿ–ฅ BE ๋ฐฑ์—”๋“œ/Spring ์Šคํ”„๋ง

[Spring, H2 database] ์Šคํ”„๋ง ์•ฑ๊ณผ H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐํ•˜๊ธฐ

๋น„๋น„ bibi 2021. 3. 15. 23:35

์‚ฌ์‹ค์ƒ ๋‚ด๊ฐ€ ๋ณด๋ ค๊ณ  ์ •๋ฆฌํ•œ ๊ฒƒ์ด์ง€๋งŒ... ๊ทธ๋™์•ˆ ์ธ๊ฐ•, ์ˆ˜์—…, ๋ฏธ์…˜ ํ•˜๋ฉฐ ๋ฐฐ์šด ๊ฑธ ์ˆœ์„œ๋Œ€๋กœ ์ฐจ๊ทผ์ฐจ๊ทผ ์ •๋ฆฌํ•ด ๋ณด์•˜๋‹ค.

(H2๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์Šคํ”„๋ง ๋ถ€ํŠธ, gradle, JPA ์‚ฌ์šฉ)

0. H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์น˜

H2๋Š” ๊ฐœ๋ฐœ ๋ฐ ํ…Œ์ŠคํŠธ, ๊ต์œก ์šฉ๋„์˜ DB์ด๋‹ค. ํ™ˆํŽ˜์ด์ง€

  • ๊ฐ€๋ณ๊ณ (์šฉ๋Ÿ‰์ด ์ž‘๋‹ค) ํŽธ๋ฆฌํ•˜๋‹ค
  • ์›น ํ™”๋ฉด์„ ์ œ๊ณตํ•ด ์ค€๋‹ค.

์œ„ ํ™ˆํŽ˜์ด์ง€์—์„œ ๋‹ค์šด๋กœ๋“œ ๋ฐ ์„ค์น˜ ํ›„ h2.shํŒŒ์ผ์„ ์‹คํ–‰ํ•œ๋‹ค.

(๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ํ™ˆ(~)์— ์„ค์น˜๋œ๋‹ค)

  • H2 - bin - h2.sh ์‹คํ–‰
    • git bash๋ฅผ ์ด์šฉํ•˜๋ฉด ํŽธํ•˜๋‹ค. cd ~/H2 - cd bin - ./h2.bat
    • ์œˆ๋„์šฐ๋Š” h2.bat (๊นƒ๋ฐฐ์‹œ์—์„œ๋Š” ./h2.bat) ์‹คํ–‰
    • ๋งฅ์€ ./h2.sh ์‹คํ–‰
    • ์ฐธ๊ณ  : ์ข…๋ฃŒ๋Š” Ctrl + c ๋กœ ์ข…๋ฃŒ.

๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ '์›น ์ฝ˜์†”' ์ฐฝ์ด ๋‚˜์˜จ๋‹ค.

์Šคํ”„๋ง H2 ์ฝ˜์†”

์ฒ˜์Œ์—๋Š” ''๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒŒ์ผ(.db)''์„ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

  • JDBC URL : ํ™ˆ์œผ๋กœ๋ถ€ํ„ฐ์˜ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๋งํ•œ๋‹ค.
    • ~/test ํ™ˆ์—์„œ testํŒŒ์ผ์„ ๋งํ•จ.
    • test๋ถ€๋ถ„์„ ์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ๋ช…์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค
  • ์—ด๋ฆฐ ์ƒํƒœ ๊ทธ๋Œ€๋กœ [์—ฐ๊ฒฐ]์„ ๋ˆ„๋ฅด๋ฉด ์ฐฝ์ด ๋ฐ”๋€๋‹ค.

ํ™ˆ์—์„œ test.mv๋ผ๋Š” ์ด๋ฆ„์˜ .dbํŒŒ์ผ(๋ฐ์ดํ„ฐ๋ฒ ์ด์ŠคํŒŒ์ผ)์ด ์ƒ๊ฒผ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.(cd, ls ๋˜๋Š” ํƒ์ƒ‰๊ธฐ๋กœ ์ง์ ‘ ํ™•์ธ)

JDBC URL์ฒ˜๋Ÿผ ํŒŒ์ผ(test)๋กœ ์ ‘๊ทผํ•˜๊ฒŒ ๋˜๋ฉด, ์›น ์ฝ˜์†”๊ณผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ„ ์ถฉ๋Œ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ

์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์—ฐ๊ฒฐ์„ ๋Š๊ณ , ๋‹ค์‹œ ์ ‘์†ํ•˜๊ธฐ ์ „์—

JDBC URL ํ•ญ๋ชฉ์„ jdbc:h2:tcp://localhost/~/test ๋กœ ๋ฐ”๊พผ ๋’ค ๋‹ค์‹œ [์—ฐ๊ฒฐ]ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ์ ‘์†ํ•˜๋ฉด ํŒŒ์ผ์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๊ฒŒ ์•„๋‹Œ, ์†Œ์ผ“์„ ํ†ตํ•ด ์ ‘๊ทผํ•˜๊ฒŒ ๋˜์–ด ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

๋งŒ์•ฝ ์ž˜ ์•ˆ ๋˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, rm test.mv.db ๋กœ test.mv.dbํŒŒ์ผ์„ ์™„์ „ํžˆ ์‚ญ์ œ ํ›„, ์„œ๋ฒ„๋ฅผ ๊ป๋‹ค ์ผ  ๋‹ค์Œ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ๋„ํ•ด ๋ณธ๋‹ค.

1. build.gradle, application.properties

build.gradle์˜ dependencies์— ์•„๋ž˜ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€

runtimeOnly 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

(โ— ์ˆ˜์ • ํ›„ gradle์„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•œ๋‹ค)

application.properties์— ์•„๋ž˜ ์„ค์ •์„ ์ถ”๊ฐ€

spring.datasource.url=jdbc:h2:tcp://localhost/~/java-qna
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.ddl-auto=create-drop

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
  • spring.datasource.url : H2์ฝ˜์†”์—์„œ JDBC URL ์— ๋„ฃ์—ˆ๋˜ ๊ฒฝ๋กœ์™€ ๊ฐ™์•„์•ผ ํ•œ๋‹ค.
  • spring.jpa.show-sql , spring.jpa.properties.hibernate.format_sql: JPA๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ์ „๋‹ฌ๋˜๋Š” ์ฟผ๋ฆฌ๋ฌธ์„ ์ถœ๋ ฅํ•˜๊ฒŒ ํ•˜๋Š” ์˜ต์…˜
  • spring.jpa.hibernate.ddl-auto : ํ…Œ์ด๋ธ” ์ž๋™ ์ƒ์„ฑ ์„ค์ •.
    • create-drop : ์„œ๋ฒ„ ์‹œ์ž‘์‹œ DB ํ…Œ์ด๋ธ”์„ drop(์‚ญ์ œ)ํ›„ ๋‹ค์‹œ ์ƒ์„ฑํ•จ
    • validate : ์„œ๋ฒ„ ์‹œ์ž‘์‹œ DB ํ…Œ์ด๋ธ”์„ dropํ•˜์ง€ ์•Š์Œ
  • spring.h2.console ... : H2 DB ์ฝ˜์†”์— ๋Œ€ํ•œ ์ ‘๊ทผ ์„ค์ •

2. DB์— ๋„ฃ์„ ํด๋ž˜์Šค๋ฅผ DBํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘

์ž๋ฐ” ๊ฐ์ฒด์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ๋งคํ•‘ํ•˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค.

์–ด๋–ป๊ฒŒ? JPA๋ผ๋Š” ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•จ.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๊ฒฐ๋˜๋Š” ํด๋ž˜์Šค๋“ค์€ ๋ณดํ†ต repository ๋˜๋Š” domain ์ด๋ผ๋Š” ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค์–ด ๊ด€๋ฆฌํ•œ๋‹ค.

DB์— ๋„ฃ์„ ํด๋ž˜์Šค (์—ฌ๊ธฐ์„œ๋Š” User)๋ฅผ DBํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘ํ•œ๋‹ค.

  • JPA๋Š” ORM ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•œ๋‹ค.

    • ORM : Object Relational Mapping, ๊ฐ์ฒด - ๊ด€๊ณ„ํ˜•๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ”์„ ๋งตํ•‘ํ•˜๋Š” ๊ธฐ์ˆ .
  • ๋งตํ•‘์€ ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง„๋‹ค.

import javax.persistence.*;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length=20)
    private String userId;

    private String password;
    private String name;
    private String email;

    // getter and setter
}

๊ฐ ์–ด๋…ธํ…Œ์ด์…˜์„ Ctrl+ํด๋ฆญ ํ•˜๋ฉด ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์˜ ๊ธฐ๋ณธ๊ฐ’(default)๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•˜๋ฉฐ ์ž‘์—…ํ•˜๋ฉด ์ข‹๋‹ค.

  • @Entity : ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๊ฒฐ๋˜๋Š” ํด๋ž˜์Šค์ž„์„ ๋ช…์‹œํ•œ๋‹ค. JPA๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ์ด์ž DB์˜ ํ…Œ์ด๋ธ”๋ช…์ด ๋จ.

  • @Id : ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ณ€์ˆ˜๋ฅผ PK(Primary Key)๋กœ ์„ค์ •ํ•œ๋‹ค.

  • Primary Key(PK) : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ ๋ฐ์ดํ„ฐ๋“ค์„ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ๊ณ ์œ  ์‹๋ณ„์ž๋ฅผ ์˜๋ฏธํ•จ.

    • ๋ณดํ†ต์€ PK๋ฅผ ์ž๋™์ฆ๊ฐ€ํ•˜๋Š” ์ˆซ์ž๋กœ ์‚ฌ์šฉํ•˜๋ฉฐprivate long id;๋ฅผ @Id, @GeneratedValue๋กœ ์„ค์ •ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

    • @GeneratedValue : ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ++ํ•˜์ง€ ์•Š์•„๋„ DB๊ฐ€ ์ž๋™์œผ๋กœ ๊ฐ’์„ ์ƒ์„ฑํ•ด ๋”ํ•ด์ค€๋‹ค.

  • @Column : DB ํ…Œ์ด๋ธ”์˜ ์นผ๋Ÿผ๊ณผ ๋งตํ•‘ํ•  ์š”์†Œ๋ฅผ ์„ ์–ธ

    • nullable : ์ƒ๋žต ๊ฐ€๋Šฅํ•œ์ง€์— ๋Œ€ํ•œ ์†์„ฑ. ๊ธฐ๋ณธ์ ์œผ๋กœ true์ด๋‹ค.
    • length : ์ตœ๋Œ€ ๊ธธ์ด์— ๋Œ€ํ•œ ์†์„ฑ.
  • โ— ๋ฐ˜๋“œ์‹œ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค๋ณ€์ˆ˜์— ๋Œ€ํ•œ getter, setter๋ฅผ ๊ตฌํ˜„ํ•ด ๋‘์–ด์•ผ ํ•œ๋‹ค.

    (๊ทธ๋ž˜์•ผ ์Šคํ”„๋ง์ด ์ž๋™์œผ๋กœ ๊ฐ’์„ ๋„ฃ๊ณ  ๋บ„ ์ˆ˜ ์žˆ๋‹ค)

3. CRUD ๊ตฌํ˜„ - ์ธํ„ฐํŽ˜์ด์Šค ์„ ์–ธ

import com.codessquad.qna.User;
import org.springframework.data.repository.CrudRepository;

public interface UserReopsitory extends CrudRepository<User, Long> {
}
  • CrudRepository<?, ?> : ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†ํ•˜๋ฉด CRUD ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์„ ์ง€์›๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    • <?, ?> : ์™ผ์ชฝ ?์—๋Š” DB์— ๋„ฃ๊ณ  ๋นผ๋Š” ๊ฐ์ฒด (์—ฌ๊ธฐ์„œ๋Š” User)๋ฅผ, ์˜ค๋ฅธ์ชฝ ?์—๋Š” Primary Key(์—ฌ๊ธฐ์„œ๋Š” id)์˜ ํƒ€์ž…(Long)์„ ๋„ฃ์–ด ์ค€๋‹ค.
  • ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ ์–ธํ•˜๋ฉด ์Šคํ”„๋ง์—์„œ ์ž๋™์œผ๋กœ ๊ทธ ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , ์Šคํ”„๋ง ๋นˆ์— ๋“ฑ๋กํ•ด ์ค€๋‹ค.

4. Controller์—์„œ DB ์‚ฌ์šฉ

  • ์ปจํŠธ๋กค๋Ÿฌ์—์„œ, ์„ ์–ธํ•œ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•œ๋‹ค.

  • CrudRepository์—์„œ ์ง€์›ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋“ค - save(), findAll(), findBy...() .. ๋ฅผ ํ™œ์šฉํ•ด ์ปจํŠธ๋กค๋Ÿฌ์— CRUD๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

import com.codessquad.qna.User;
import com.codessquad.qna.repository.UserReopsitory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.ArrayList;
import java.util.List;

@Controller
public class UserController {

    @Autowired
    private UserReopsitory userReopsitory;

    @PostMapping("/user/create")
    public String createUser(User user) {
        userReopsitory.save(user); // ์ €์žฅ
        return "redirect:/users";
    }

    @GetMapping("/users")
    public String showUser(Model model) {
        Iterable<User> allUsers = userReopsitory.findAll(); // ๋ชจ๋‘ ์กฐํšŒ
        model.addAttribute("users", allUsers);
        return "list";
    }

    @GetMapping("/{id}")
    public ModelAndView showProfile(@PathVariable Long id) {
        ModelAndView modelAndView = new ModelAndView("userProfile");
        modelAndView.addObject("user", userRepository.findById(id).get());
        return modelAndView;
    }

    // codes..
}
  • @Autowired private UserReopsitory userReopsitory;

    : ์Šคํ”„๋ง์ด ์ž๋™ ์ƒ์„ฑํ•ด ์ค€ UserRepository์˜ ๊ตฌํ˜„์ฒด๋ฅผ, ์ธ์Šคํ„ด์Šค๋ณ€์ˆ˜UserRepository์— ์ฃผ์ž…ํ•ด ์ค€๋‹ค

  • ModelAndView

์ด์ œ ์Šคํ”„๋ง ์•ฑ์„ ์‹คํ–‰ํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ…Œ์ŠคํŠธํ•œ ๋’ค H2๋ฅผ ํ™•์ธํ•˜๋ฉด ๊ฐ’์ด ์ž˜ ๋“ค์–ด๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค.