SpringBoot

springboot로 Rest api 만들기(3) H2 Database 연동

pepega 2021. 10. 21. 15:43

전체 소스코드

https://github.com/GHGHGHKO/Springboot/tree/main/pepega_chapter_3

 

GitHub - GHGHGHKO/Springboot: 블로그에 업로드 된 소스코드

블로그에 업로드 된 소스코드. Contribute to GHGHGHKO/Springboot development by creating an account on GitHub.

github.com

 

전 포스팅에서는

boot를 활용하여 HelloWorld를 GetMapping, ResponseBody로로 출력해보았다.

이번 포스팅에는

SpringBoot에 Database를 연동하는 실습을 포스팅하겠다.

Database는 H2 database를 활용할 예정이다.

H2 Database

H2는 최소한의 리소스로 실행 가능한 경량 DB이다.

테스트 용으로 사용하기 아주 알맞다.

MySQL처럼 번잡하게 깔지 않아도 되고

파일 하나만 실행하면 DB를 활용할 수 있다.

본 포스팅은 Windows 10 환경에서 진행하였다.

https://www.h2database.com/html/download.html

 

Downloads

Downloads Version 1.4.200 (2019-10-14) Windows Installer (SHA1 checksum: 12710a463318cf23c0e0e3f7d58a0f232bd39cfe) Platform-Independent Zip (SHA1 checksum: 5898966bbca0b29ee02602fb84e0eb90ec92eec2) Version 1.4.199 (2019-03-13), Last Stable Windows Installe

www.h2database.com

위 사이트에 접속 후

Platform-Independent Zip을 다운로드하여

원하는 곳에 압축을 푼다.

실행 방법은

CMD를 켠 후

압축 푼 파일 기준으로

/bin에 접속하여

h2.bat

이라는 명령어만 치면 된다.

접속하면 하단처럼 나온다.

 

위 내용을 그대로 적용한 뒤

연결 버튼을 누르고

h2 데이터베이스를 종료한 뒤 위 페이지를 다시 켜고

하단 내용을

JDBC URL에 넣고 다시 연결한다.

 

jdbc:h2:tcp://localhost/~/test

데이터베이스 세팅은 어느정도 끝났다.

build.gradle에 라이브러리 추가

이제 Spring에서 H2로 접속하기 위한 설정을 한다.

build.gradle 파일에 H2 라이브러리 설정이 없으면

dependencies에 하단 내용을 추가하여 H2 라이브러리를 다운받는다.

runtimeOnly 'com.h2database:h2'

 

application.yml 에 접속정보 추가

application.yml에 H2 접속 정보를 설정한다.

yml은 들여쓰기가 필수다.

 

server:
  port: 8080
  
spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/test
    driver-class-name: org.h2.Driver
    username: sa
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    properties.hibernate.hbm2ddl.auto: update
    showSql: true

 

url: DB 접속 주소

driver-class-name: DB 접속을 위한 디바이스 드라이버

username: DB 접속 user 명

jpa: jpa에서 사용할 DB로 H2 세팅

properties:hibernate.hbm2ddl.auto: update

도메인 객체 구성과 DB의 스키마를 비교해

필요한 테이블이나 컬럼이 없을 때

도메인 객체에 맞춰 DB 스키마를 변경

showSql: true

jpa가 실행하는 쿼리를 console로 출력하기 위해 사용

H2에 저장된 table 데이터를 다루는 코드 작성

Table 데이터 Mapping을 위한 Model을 하나 만든다.

com.example.pepega 하위에 entity라는 package 생성

entity 패키지 안에 User 클래스를 생성한다.

entity는 데이터베이스 Table 간의 구조와 관계를

JPA가 요구하는 형태로 만든 Model 이다.

Table에 있는 Column 값의 정보,

테이블 간의 연관 관계(1:N, N:1 등) 정보를 담고 있다.

Lombok을 활용하면 소스가 간단해진다.

 

package com.example.pepega.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Builder
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "user")
public class User {

    @Id //primaryKey 임을 알린다.
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    // primaryKey 생성 전략을 DB에 위임한다는 뜻.
    // primaryKey 필드를 auto_increment로 설정해 놓은 경우와 같다.
    private long msrl;

    @Column(nullable = false, unique = true, length = 30)
    // uid column을 명시한다. 필수 입력, 중복 안됨, 길이는 30제한
    private String uid;

    @Column(nullable = false, length = 100)
    // name column을 명시, 필수 입력, 길이 100 제한
    private String name;
}

만약 Lombok을 안쓴다면

클래스에 대한 생성자, 변수들에 대한 getter and setter를 설정해줘야 한다.

상단은 Lombok을 사용한 예시이다.

Table에 질의를 요청하기 위한 Repository 생성

만들어진 entity를 이용해서

Table에 질의를 요청하기 위한 Repository를 생성한다.

com.example.pepega 하위에 repo package를 생성한다.

repo package 안에 UserJpaRepo interface를 생성한다.

* interface는 동일한 목적 하에 동일한 기능을 보장하기 위한 것이다.

* 자바의 다형성을 이용하여 개발 코드 수정을 줄이고 유지보수성을 높인다.

* 인터페이스는 interface의 키워드로만 선언 가능하다.

* implements 키워드를 통해 일반 클래스에서 인터페이스를 구현할 수 있다.

 

package com.example.pepega.repo;

import com.example.pepega.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserJpaRepo extends JpaRepository<User, Long> {
}

 

Controller에 Repository 설정

위에서 만든 인터페이스인 Jpa Repo를 사용하는 Controller를 생성할 것이다.

com.example.pepega.controller 안에

v1이라는 package를 만들고

UserController를 생성한다.

 

package com.example.pepega.controller.v1;


import com.example.pepega.entity.User;
import com.example.pepega.repo.UserJpaRepo;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequiredArgsConstructor // class 내부의 final 객체는 Constructor Injection 수행, @Autowired도 가능
@RestController // 결과를 JSON으로 도출
@RequestMapping(value = "/v1") // api resource를 버전별로 관리, /v1 을 모든 리소스 주소에 적용
public class UserController {

    private final UserJpaRepo userJpaRepo;

    @GetMapping(value = "/user") // user 테이블의 모든 정보를 읽어옴
    public List<User> findAllUser() { // 데이터가 1개 이상일 수 있기에 List<User>로 선언
        return userJpaRepo.findAll(); // JPA를 사용하면 CRUD에 대해 설정 없이 쿼리 사용 가능 (select * from user 와 같음)
    }

    @PostMapping(value = "/user") // user 테이블에 데이터를 입력하는 부분 insert into user (msrl, name, uid) values (null, ?, ?) 와 같음
    public User save() {
        User user = User.builder()
                .uid("pepe@sadfrog.com") // User 클래스에서 만들어진 변수 uid, name
                .name("페페")
                .build();

        return userJpaRepo.save(user);
    }
}

 

@RequireArgsContructor

class 내부의 final 객체는 Constructor Injection(생성자 주입)를 수행한다. @Autowired로도 가능하다.

@RestController

결과 데이터를 JSON으로 내보낸다.

@RequestMapping(value = "/v1")

Api resource를 버전별로 관리하기 위해

/v1 을 모든 리소스 주소에 적용되도록 처리한다.

@GetMapping(value = "/user")

user 테이블에 있는 모든 데이터를 읽어온다.

결과가 1개 이상일 수 있기 때문에 List<User>로 선언했다.

userJpaRepo.findAll()

JPA를 사용하면 CRUD에 대해 설정 없이 쿼리를 사용할 수 있다.

select * from user; 와 같다.

@PostMapping(value = "/user")

user 테이블에 데이터 1건을 입력한다.

insert into user (msrl, name, uid) values (null, ?, ?); 와 같다.

builder를 활용하여

User 클래스에서 선언한 uid, name에 대한 정보를 입력한다.

이후 return으로 쿼리 결과를 save한다. return userJpaRepo.save(user)

어느정도 됐으니

main으로 가서 서버를 실행한다.

*만약 오류가 나면 H2 데이터베이스가 켜졌는지 확인한다.

이제 웹페이지에

http://localhost:8080/v1/user 를 실행한다.

아직 아무런 데이터를 넣지 않았기 때문에 [] 만 리턴 될 것이다.

이제 데이터를 넣기 위해 POST로 http://localhost:8080/v1/user 을 실행할 것이다.

웹 페이지 입력으로는 GET URL만 호출할 수 있으므로

POST를 테스트하기 위해선 POSTMAN을 활용하여야 한다.

https://www.postman.com/downloads/

 

Download Postman | Get Started for Free

Try Postman for free! Join 17 million developers who rely on Postman, the collaboration platform for API development. Create better APIs—faster.

www.postman.com

 

POSTMAN 설치 후

하단처럼 세팅한다.

Basic Auth를 활용하여

Username, Password를 입력하는 이유는

Springboot 프로젝트 생성할 때 Spring Security Api 를 포함하였기 때문이다.

웹페이지를 킬 때처럼 이름과 패스워드를 입력하면 된다.

GET 방식은 잘 불러와진다.

POST 방식은?

안된다.

아마 Spring Security API 때문에 403 오류가 나는 것 같다.

해결 방법을 찾았다.

https://stackoverflow.com/questions/50486314/how-to-solve-403-error-in-spring-boot-post-request

 

How to Solve 403 Error in Spring Boot Post Request

I am newbie in spring boot rest services. I have developed some rest api in spring boot using maven project. I have successfully developed Get and Post Api. My GET Method working properly in postm...

stackoverflow.com

 

com.example.pepega 안에 config 라는 package를 생성한다.

config package 안에 WebSecurityConfig 라는 클래스를 생성한다.

 

package com.example.pepega.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
}

위 내용은 나중에 수정하도록 하겠다.

POSTMAN으로 다시 POST 전송을 시도한다.

잘 들어간다

DB Table 자동 생성

이번 포스팅에서는 Table을 생성하지 않았는데도 불구하고

POST로 User 생성 시 오류가 발생하지 않고 정상 입력되었다.

설정을 따로 해주지 않으면 entity(package)의 정보를 토대로 서버 실행 시

테이블이 자동 생성되기 때문이다.

개발 시에는 편리한 점이 있지만

서비스에 사용할 경우엔

테이블의 자동 생성은 위험할 수 있다.

하단과 같이 설정하여 자동 생성되지 않도록 해야한다.

application.yml

properties.hibernate.hbm2ddl.auto: none

 

server:
  port: 8080

spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/test
    driver-class-name: org.h2.Driver
    username: sa
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    properties.hibernate.hbm2ddl.auto: none
    showSql: true

hbm2ddl.auto

* create - 서버 시작할 때 모든 테이블 생성

* create-drop - 서버 시작할 때 테이블 생성, 종료시 삭제

* update - 서버 시작 시 entity, table 비교 후 변경된 내용 반영, table이 없으면 새로 생성

* validate - 서버 시작 시 entitiy, table 비교하여 다르면 시작하지 않고 종료

* none - 아무런 처리를 하지 않음

출처

https://daddyprogrammer.org/post/152/spring-boot2-h2-database-intergrate/

 

SpringBoot2로 Rest api 만들기(3) – H2 Database 연동

이번 시간에는 SpringBoot에 Database를 연동하는 방법을 실습합니다. 실습은 H2 database로 진행하겠습니다. H2 Database H2는 최소한의 리소스로 실행 가능한 경량 DB로서 테스트 용으로 사용하기 알맞은 DB

daddyprogrammer.org

https://github.com/codej99/SpringRestApi

 

GitHub - codej99/SpringRestApi: SpringBoot2, SpringSecurity, JWT, Stateless Restful API

SpringBoot2, SpringSecurity, JWT, Stateless Restful API - GitHub - codej99/SpringRestApi: SpringBoot2, SpringSecurity, JWT, Stateless Restful API

github.com

https://limkydev.tistory.com/197

 

[JAVA] 자바 인터페이스란?(Interface)_이 글 하나로 박살내자

1. 인터페이스 개념과 역할 인터페이스....이 글하나로 박살내자. (회사에서 존댓말을 많이 쓰기때문에 여기서라도 반말로 글을 써보고 싶음 ㅎ) 인터페이스는 뭘까?? 결론부터 말하면, 극단적으

limkydev.tistory.com