전체 소스코드
https://github.com/GHGHGHKO/Springboot/tree/main/pepega_chapter_3
전 포스팅에서는
boot를 활용하여 HelloWorld를 GetMapping, ResponseBody로로 출력해보았다.
이번 포스팅에는
SpringBoot에 Database를 연동하는 실습을 포스팅하겠다.
Database는 H2 database를 활용할 예정이다.
H2 Database
H2는 최소한의 리소스로 실행 가능한 경량 DB이다.
테스트 용으로 사용하기 아주 알맞다.
MySQL처럼 번잡하게 깔지 않아도 되고
파일 하나만 실행하면 DB를 활용할 수 있다.
본 포스팅은 Windows 10 환경에서 진행하였다.
https://www.h2database.com/html/download.html
위 사이트에 접속 후
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/
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
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/
https://github.com/codej99/SpringRestApi
https://limkydev.tistory.com/197
'SpringBoot' 카테고리의 다른 글
springboot로 Rest api 만들기(6) ControllerAdvice를 이용한 Exception 처리 (0) | 2021.10.22 |
---|---|
springboot로 Rest api 만들기(5) API 인터페이스 및 결과 데이터 구조 설계 (0) | 2021.10.22 |
springboot로 Rest api 만들기(4) Swagger API 문서 자동화 (0) | 2021.10.21 |
springboot로 Rest api 만들기(2) HelloWorld (0) | 2021.10.21 |
SpringBoot로 Rest api 만들기(1) Intellij community, start.spring.io (0) | 2021.10.21 |