본문 바로가기

Programming/Spring Boot

[Spring Boot][H2 Database 연동 1/2] Sample Project

들어가며...

벡엔드 시스템을 구축하면서 빼놓을 수 없는 것 중에 하나가 Database 연동일 것입니다. 본 글에서는 Database중 관계형 database(RDBMS) 중 간단하게 테스트 또는 Feasibility 프로젝트를 수행할 때 사용하기 좋은 H2 database 연동에 대해서 알아보도록 하겠습니다.

 

  • Table of Contents
    • Project Overview
    • H2 database?
    • Model 생성
    • Repository 구현
    • Service 구현
    • Controller 구현

Project Overview

본 글에서는 사용자 정보를 H2 database에 저장(추가), 조회, 삭제하는 매우 간단한 프로젝트를 진행하면서 Spring Boot에서 database와 연동하는 방법에 대해서 알아볼 것입니다.

Database와 연동하는 방법으로는 다양한 종류가 있겠으나 크게 SQL Mapper를 이용하는 방법과 ORM(Obejct-Relational Mapping)을 이용하는 방법이 있습니다. 아래는 SQL Mapper와 ORM의 간단한 설명이며 본 글에서는 이중 ORM을 이용하여 H2 database와 연동해 보도록 하겠습니다.

 

● SQL Mapper

  • SQL <= Mapping => Object 
  • SQL문을 직접 다루어 수행한 Query의 결과를 Object와 매핑하는 방식
  • ex) Mybatis, JdbcTemplates

● ORM(Object-Relational Mapping)

  • Database table data <= Mapping => Object
  • Object와 Database table의 데이터를 자동으로 매핑해준다.
  • 별도의 SQL문 작성없이 Object에 속성을 설정하여 주면 이미 만들어져 있는 API를 이용하여 SQL문을 자동으로 생성하여 Query를 수행한다.
  • Object Mapping의 한계로 JPQL (JPA SQL문법)을 사용한 SQL문을 직접 작성하는 방식과 Native SQL문도 직접 작성할 수 있는 방법 제공한다.
  • ex) JPA, Hibernate

H2 database?

우선 H2 database 공식 사이트에서는 아래와 같이 정의하고 있습니다.

 

Welcome to H2, the Java SQL database. The main features of H2 are:

  • Very fast, open source, JDBC API
  • Embedded and server modes; in-memory databases
  • Browser based Console application
  • Small footprint: around 2 MB jar file size

위 내용 이외에도 RDBMS이며 대부분의 관계형 database에서 사용하는 SQL을 지원하며 in-memory 및 물리적인 디스크에 저장되는 형태를 모두 지원합니다.

 

좀 더 자세한 내용은 H2 database 공식 사이트를 참조하시면 좋을 듯 합니다.

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

 

H2 Database Engine

H2 Database Engine Welcome to H2, the Java SQL database. The main features of H2 are: Very fast, open source, JDBC API Embedded and server modes; in-memory databases Browser based Console application Small footprint: around 2 MB jar file size     Support S

www.h2database.com

Model 생성

이제 본격적으로 Spring Boot Project를 생성하여 H2 database 연동을 해보도록 하겠습니다.

Spring Boot Project를 생성하는 방법은

2020/01/20 - [Programming/Spring Boot] - [Spring Boot][기본 3/3] Sample Project 시작하기

를 참조하시고 Project는 아래와 같이 설정하여 생성하였습니다.

 

위의 내용들은 본인의 취향에 맞게 설정하시면 됩니다. 다음은 최초 포함할 라이브러리 목록입니다. 저는 1차적으로 "web"만 포함하였습니다.

Project가 생성되었으면 jpa, gson, h2에 대한 dependency를 추가합니다.

 

● build.gradle

plugins {
	id 'org.springframework.boot' version '2.2.5.RELEASE'
	id 'io.spring.dependency-management' version '1.0.9.RELEASE'
	id 'java'
}

group = 'com.demo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'com.google.code.gson:gson:2.8.6'
	runtimeOnly 'com.h2database:h2'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

 

  • JPA : ORM(Object-Relational Mapping)을 위해 추가
  • gson : object to string 또는 string to object를 위해 추가. 본글에서는 object to string을 위해 추가하였습니다.
  • h2 : h2 연동

● Gradle Project Refresh

build.gradle을 변경한 후에는 항상 "Refresh Gradle Project"를 수행하여야 변경된 dependency나 build rule등이 적용됩니다. 따라서 아래와 같이 Project를 refresh 해줍니다.

 

● Member class를 생성합니다.

  • package : com.demo01.store.h2.model
  • class : Member.class

 

● Member.java

package com.demo01.store.h2.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import com.google.gson.Gson;

@Entity
@Table(name = "member")
public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	private String name;
	private int age;
	
	////////////////////////////////////////////////////////////////////////////////
	//< constructors
	
	////////////////////////////////////////////////////////////////////////////////
	//< public functions
	
	@Override
	public String toString() {
		Gson gson = new Gson();
		return gson.toJson(this);
	}
	
	////////////////////////////////////////////////////////////////////////////////
	//< private functions
	
	////////////////////////////////////////////////////////////////////////////////
	//< getter and setter
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public int getAge() {
		return age;
	}
	
	public void setAge(int age) {
		this.age = age;
	}	
}

Repository 구현

JpaRepository를 상속받아 Repository interface를 작성합니다.

 

● package 생성 : com.demo01.store.h2.repository

 

● MemberRepository.java

package com.demo01.store.h2.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.demo01.store.h2.model.Member;

@Repository
public interface MemberRepository extends JpaRepository<Member, Integer> {

}

interface 파일만 생성하고 "JpaRepository" class만 상속받았지만 이미 대부분의 기능이 구현되어져 있기 때문에 별도의 추가함수는 정의하지 않았습니다. Jpa로 원하는 함수를 추가하는 방법은 기회가 있을 때 별도로 다루도록 하겠습니다.

Service 구현

기능이 간단한 Database연동이라면 굳이 Service 부분을 추가적으로 구현할 필요는 없겠지만 프로젝트를 진행하다 보면 Database연동 후 후처리 작업 또는 Policy 적용등 해야 할 작업이 많은 경우가 대부분입니다. 해당 내용을 보통 이 Service 부분에 적용하면 일관성 있는 구현을 진행할 수 있습니다.

본 프로젝트에서는 Service부분에 구현할 내용이 거의 없으나 예제삼아 추가해 보도록 하겠습니다.

 

● MemberService.java

package com.demo01.store.h2;

import java.util.List;

import com.demo01.store.h2.model.Member;

public interface MemberService {
	public Member add(Member member) throws Exception;
	public List<Member> getAll() throws Exception;
	public void deleteAll() throws Exception;
}

추가, 전체데이터 조회, 전체데이터 삭제 3가지 함수의 interface를 정의하였습니다. C언어로 비유하면 실제 사용자가 사용할 함수의 Prototype을 정의한 header 파일과 비슷하다고 할 수 있습니다. 해당 파일에 함수의 설명을 주석으로 달아두면 더 좋을 것입니다.

 

● Package 생성 : com.demo01.store.h2.service

 

● MemberServiceImpl.java

package com.demo01.store.h2.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo01.store.h2.MemberService;
import com.demo01.store.h2.model.Member;
import com.demo01.store.h2.repository.MemberRepository;

@Service(value = "memberServiceImpl")
public class MemberServiceImpl implements MemberService {
	@Autowired
	private MemberRepository memberRepository;
	
	////////////////////////////////////////////////////////////////////////////////
	//< constructors
	
	////////////////////////////////////////////////////////////////////////////////
	//< public functions
	

	@Override
	public Member add(Member member) throws Exception {
		return memberRepository.save(member);
	}

	@Override
	public List<Member> getAll() throws Exception {
		return memberRepository.findAll();
	}

	@Override
	public void deleteAll() throws Exception {
		memberRepository.deleteAll();
	}
	
	////////////////////////////////////////////////////////////////////////////////
	//< private functions
}

Controller 구현

마지막으로 추가, 조회, 삭제에 대한 Controller를 추가하도록 하겠습니다. 조회, 삭제는 GET방식으로 file path만 지정토록 하고 추가의 경우는 POST방식으로 처리토록 구현하겠습니다.

 

● Package : com.demo01.controller

 

● MemberController.java

package com.demo01.controller;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.demo01.store.h2.MemberService;
import com.demo01.store.h2.model.Member;

@RestController
public class MemberController {
	//< MemberServiceImpl.java의 @Service annotation의 value값으로 정의한 이름을 설정
	@Resource(name = "memberServiceImpl")
	private MemberService memberService;
	
	/**
	 * Add
	 */
	@RequestMapping(value = "/add", method = RequestMethod.POST)
	public Member add(@RequestBody Member member) {
		Member addMember = null;
		
		try {
			addMember = memberService.add(member);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return addMember;
	}
	
	/**
	 * Get All
	 */
	@RequestMapping(value = "/list", method = RequestMethod.GET)
	public List<Member> getAll() {
		try {
			return memberService.getAll();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	/**
	 * Delete All
	 */
	@RequestMapping(value = "/deleteAll", method = RequestMethod.GET)
	public String deleteAll() {
		try {
			memberService.deleteAll();
			
			return "delete all!!";
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return "fail to delete ):";
	}
}

 

Controller까지 구현이 완료되었으므로 프로젝트 실행 후 테스트를 진행해보도록 하겠습니다. GET방식의 API만 있다면 Browser에서 테스트를 진행하셔도 무방하지만 추가하는 부분을 POST방식으로 구현하였기 때문에 HTTP Client를 사용하여 테스트를 진행하시는 것이 좋습니다. 보유하고 계신 (없다면 Postman 추천) HTTP Client 프로그램을 사용하여 추가, 조회, 삭제 테스트를 진행하면 다음과 같습니다.

 

● 최초 조회 : http://localhost:8080/list 요청 시 아래의 그림과 같이 Empty Array가 반환되면 정상입니다.

 

● 두명의 Member 추가

 

● 조회 : 추가한 두명의 Member가 조회되면 정상입니다.

 

● 삭제 : http://localhost:8080/deleteAll 을 수행하여 데이터를 삭제해봅니다.

 

이후 다시 조회를 했을 시 Empty Array가 반환되면 모두 정상적으로 동작하는 것입니다.

간단하게 ORM 및 Controller등의 테스트를 위해서는 지금까지만으로도 충분하겠지만 위 프로젝트는 새로 시작하면 기존에 저장했던 데이터가 없어지는 문제점이 있습니다.

이는 build.gradle에 H2를 포함시키기는 했지만 기타 아무런 설정을 하지 않았기 때문에 default로 in-memory형태로 동작하기 때문입니다. 이를 물리적인 저장공간에서 영속성을 갖도록 하는 방법에 대해서는 다음 포스팅에서 이어가도록 하겠습니다.

마무리...

이번 글에서는 Spring Boot을 이용하여 H2 database를 연동하고 사용하는 방법에 대해서 간단한 프로젝트를 생성하면서 살펴보았습니다. H2연동이라고는 했지만 관련 설정이 하나도 없기 때문에 의아할 수도 있지만 Spring Boot에 기본적으로 H2 database embedded형태로 내장되어 있기 때문에 아무런 설정 없이도 H2 database를 사용할 수 있었습니다.

다음 포스팅에서는 명시적으로 H2 database를 설정하고 영속성을 갖는 형태로 설정하는 방법에 대해서 알아보도록 하겠습니다.

 

● 요약

  • H2 database는 java 기반의 RDBMS로 간단한 테스트 및 feasibility 적용 시 빠르게 구현할 때 좋다.
  • ORM 방식의 Database 연동 시 기본적으로 Controller -> Service -> Repository with Model Object 의 형태로 구현하면 좋다.
  • H2 database는 Spring Boot에 기본적으로 embedded 되어 있으면 default in-memory 형태로 동작한다.

● 소스참조 : 위 내용과 다음 글에서 설명할 property값을 설정까지 완료된 프로젝트는 아래 링크를 참조 하시면 됩니다.

https://github.com/ykson/spring-boot-demo

 

ykson/spring-boot-demo

Spring Boot Basic Sample Project + H2. Contribute to ykson/spring-boot-demo development by creating an account on GitHub.

github.com

 


U2ful은 입니다. @U2ful Corp.