일단 IntelliJ 를 이용해서 서버를 구축할 것이다.

Hibernate JPA 와, MyBatis 둘 중에 하나를 선택해야 하는데.

 

이번 기회에 MyBatis 와 조금 더 친밀해져 보려고 한다.

 

일단 둘의 차이점을 알고 가보자.

 

JPA 란 무엇인가?

 

JPA(Java Persistent API)

  JPA는 여러 ORM 전문가가 참여한 EJB 3.0 스펙 작업에서 기존 EJB ORM이던 Entity Bean을 JPA라고 바꾸고 JavaSE, JavaEE를 위한 영속성(persistence) 관리와 ORM을 위한 표준 기술이다. JPA는 ORM 표준 기술로 Hibernate, OpenJPA, EclipseLink, TopLink Essentials과 같은 구현체가 있고 이에 표준 인터페이스가 바로 JPA이다.

  ORM(Object Relational Mapping)이란 RDB 테이블을 객체지향적으로 사용하기 위한 기술이다. RDB 테이블은 객체지향적 특징(상속, 다형성, 레퍼런스, 오브젝트 등)이 없고 자바와 같은 언어로 접근하기 쉽지 않다. 때문에 ORM을 사용해 오브젝트와 RDB 사이에 존재하는 개념과 접근을 객체지향적으로 다루기 위한 기술이다.

 

https://blog.woniper.net/255

 

[JPA] JPA란 무엇인가?

JPA란 무엇인가? JavaSE 환경에서 JPA 설정 및 CRUD JavaEE 환경(Spring)에서 JPA 설정 및 CRUD @OneToOne, 1:1 관계 매핑 @OneToMany / @ManyToOne, 1:N / N:1 관계 매핑 @ManyToMany, N:M 관계 매핑 Entity 객체..

blog.woniper.net

한글로 풀어 쓰면, 자바 영속성 API(Application Programming Interface) 이다. API 라는 것은 말 그대로, 어플리케이션 프로그램이 다른 프로그램을 사용할 수 있게 해주는 인터페이스를 뜻한다. 

 즉 "자바로 영속성을 다루는 프로그램"의 "인터페이스"를 제공하는 것이 바로 JPA 다.

이 것은 말 그대로 "인터페이스" 이므로, 이를 구현하는 것에는 여러가지 프로그램이 있다. 가장 대표적인 것 중 하나가 바로 Hibernate다.

 

자. 간단히 정리하자면. JPA 는 결국 인터페이스이고, 이 인터페이스를 구현한 것 중 하나가 하이버네이트이다.

이렇게 따지면 MyBatis도 JPA 를 구현한 것 아닌가?? 할 수 있겠지만. JPA 는 풀 네임만 보면 오해의 여지가 있지만, 사실 ORM 을 위한 표준 기술을 의미한다. 

 ORM 은 Object Relational Mapping 의 약자다. 

 

그럼 MyBatis는 무엇이냐? Sql Mapper다. 이 두 개의 차이점은 간단히 말하자면,

 

ORM 은 Object를 기반으로 DB 가 생기고,

SQL Mapper는 DB 구축은 해 놓고, 그 DB에서 꺼내온 데이터를 Object에 꾸겨 넣는 것이다.

 

 하여간 MyBatis를 사용하기로 하고, 한번 IntelliJ를 이용해 간단히 스프링 부트 프레임워크를 구축해보자.

 

IntelliJ 에선 그 전에 살펴봤던, Spring initializer를 간단히 사용할 수 있게 해준다.

 

 

대충 원하는대로 서버 정보를 입력 해준다.

 

웹 서버이기 때문에 Spring Web Server 를 체크 해준다.

DB 설정

DB 는 간단히 H2 를 사용하자. 나중에 이 부분은 MariaDB 로 바꿀 것이다. (mySql)

H2 란 무엇인가?

 

H2DB

H2DB는 자바 기반의 오픈소스 관계형 데이터 베이스 관리 시스템(RDBMS )입니다.

H2DB는 서버(Server) 모드임베디드(Embedded) 모드인메모리 DB 기능을 지원합니다. 물론 디스크 기반 테이블을 또한 생성할 수 있습니다.

또한 브라우저 기반의 콘솔모드를 이용할 수 있으며, 별도의 설치과정이 없고 용량도 2MB(압축버전) 이하로 매우 저용량 입니다. DB자체가 매우 가볍기 때문에 매우 가볍고 빠르며, JDBC API 또한 지원하고 있습니다.

SQL 문법은 다른 DBMS들과 마찬가지로 표준 SQL의 대부분이 지원됩니다.

이러한 장점들 때문에 어플리케이션 개발 단계의 테스트 DB로서 많이 이용됩니다.



출처: https://dololak.tistory.com/285 [코끼리를 냉장고에 넣는 방법]

인 메모리로 사용할 시, 서버가 종료하면 자동으로 데이터도 사라진다. 메모리 상에만 데이터가 떠 있게 된다.

 

캐시 서버로도 쓰일 수 있겠지만, 그 방면에서는 key-value 형식의 noSql 강자인 redis 가 있으므로 포지션이 애매해진다.

현재 가장 적합한 포지션은 "테스트용 경량 RDBMS" 정도다.

 

Developer tool 에서는 간단히 Lombok plugin 만 설정해 두었다. Lombok 에 대해서는 따로 언급하지 않겠다.

 

이제 필요한 디펜던시들이 설정되고, spring initializer 가 이 설정을 기반으로 pom.xml 을 만들어주고, 필요한 소스 파일 및 폴더 구조를 모두 만들어 준다. 조금 기다리면 메이븐 기반으로 라이브러리들이 설치된다.

 

이제 mybatis 와 h2로 기본적인 설정을 진행 해보자.

 

일단 dataSource 설정을 미리 해주어야 한다. DataSource 설정이란 무엇인가?

 

https://gmlwjd9405.github.io/2018/05/15/setting-for-db-programming.html

불러오는 중입니다...

 

여기에 아주 자세히 설명 돼있다.

 

이 링크를 타면 만나는 DAO, DTO, Entity Class 의 차이도 한 번 읽으면 좋을 것 같다.

 

https://gmlwjd9405.github.io/2018/12/25/difference-dao-dto-entity.html

불러오는 중입니다...

가장 핵심은 이것이다.

 

DAO 는 Database 에 Access 할 때 사용하는 Object다. Spring 기준으로 보면 보통 @Repository 가 붙은 놈들이다.

DTO 는 말 그대로 Data를 주고 받을 때 사용하는 Object이며

Entity Class 는 DB 와 Data를 직접 주고 받을 때 사용하는 놈이다.

 

왜 DTO와 Entity Class를 나누는가?

 

-View Layer와 DB Layer의 역할을 철저하게 분리하기 위해서

-테이블과 매핑되는 Entity 클래스가 변경되면 여러 클래스에 영향을 끼치게 되는 반면 View와 통신하는 DTO 클래스(Request / Response 클래스)는 자주 변경되므로 분리해야 한다.

-Domain Model을 아무리 잘 설계했다고 해도 각 View 내에서 Domain Model의 getter만을 이용해서 원하는 정보를 표시하기가 어려운 경우가 종종 있다. 이런 경우 Domain Model 내에 Presentation을 위한 필드나 로직을 추가하게 되는데, 이러한 방식이 모델링의 순수성을 깨고 Domain Model 객체를 망가뜨리게 된다.

또한 Domain Model을 복잡하게 조합한 형태의 Presentation 요구사항들이 있기 때문에 Domain Model을 직접 사용하는 것은 어렵다.
즉 DTO는 Domain Model을 복사한 형태로, 다양한 Presentation Logic을 추가한 정도로 사용하며 Domain Model 객체는 Persistent만을 위해서 사용한다.

 

즉, DB 와 직접 통신하는 놈은 최대한 군더더기 없이 유지하는 것이 좋다.

데이터 전달 상, DB 통신과는 전혀 상관없는 필드와 메서드들이 덕지덕지 붙을 수 있기 때문에 Entity Class와 DTO 클래스를 분리한다.

DTO 클래스는 상~당히 Entity Class 와 유사하다.

 

다시 DataSource 로 돌아가보자.

 

위에 DAO 보이는가? 

DAO 는 Data Access Object가 Database를 만나기 위해 거쳐야 하는 두 계단이 있다.

JDBC Template과 JDBC Driver다.

 

아래의 설명은 Datasource 링크의 내용이다.

 

# DataSource 란?
DataSource는 JDBC 명세의 일부분이면서 일반화된 연결 팩토리이다. 즉 DB와 관계된 connection 정보를 담고 있으며, bean으로 등록하여 인자로 넘겨준다. 이 과정을 통해 Spring은 DataSource로 DB와의 연결을 획득한다.

DataSource는 JDBC Driver vendor(Mysql, Oracle 등) 별로 여러가지가 존재한다.

DataSource가 하는 일
 - DB Server와의 기본적인 연결
 - DB Connection Pooling 기능 (* 아래 참고)
 - 트랜젝션 처리


DataSource의 구현 예시
 - BasicDataSource (선택!)
 - PoolingDataSource
 - SingleConnectionDataSource
 - DriverManagerDataSource

 

DataSource 는 커넥션 정보를 담는다. 즉 DB Server와의 기본적인 연결을 담당한다.

 

 

따라서 우리는 이 Datasource 설정을 해줄 필요가 있는 것이다. 

mybatis 와 h2를 이용하기 위해서 아래의 링크들을 참고할 것이다.

"참고" 이기 때문에, 아래의 링크와 다른점도 많다. (다른점이 더 많을 수도 있다.)

 

http://tech.javacafe.io/2018/07/31/mybatis-with-spring/

 

Spring Boot에서 mybatis 설정하기

스프링 프로젝트를 진행할때마다 DB와의 연동은 필수적으로 들어가게 된다. 최근에는(드디어) JPA를 이용해 프로젝트를 많이 진행하지만, 여전히 SQL을 이용한 직관적인 매핑방식도 많이 사용되고 있다. 단순한 프로젝트라면 SpringJdbc를 이용해서도 충분히 SQL을 구성할 수 있다. 하지만 실무환경에서는 다양한 경우의수가 발생하고 이에 따른 SQL도 함께 변경되어야 하고, 이러한 복잡한 SQL이 필요한 지점에서 MyBatis의 동적 SQL관리방법은 프로

tech.javacafe.io

 

 

https://www.technicalkeeda.com/spring-tutorials/spring-4-jdbctemplate-annotation-example

 

Spring 4 JdbcTemplate Annotation Example

In this tutorial, we will learn how to connect to the database and execute CRUD SQL queries using Spring 4 JdbcTemplate. Java base configuration is used to load the JdbcTemplate, here we are not using XML base configuration for JdbcTemplate.

www.technicalkeeda.com

왜 굳이 이렇게 두 개를 참고하냐면,

개인적인 공부를 위해서 Datasource 설정을 두 가지 방법으로 해볼 생각이기 때문이다.

 

첫 번 째로, spring boot 의 자동설정 기능을 이용해본다.

두 번 째로, @Configuration 으로 Datasource Bean 을 JAVA 코드로 만들어 본다.

 

 

음... 따라해보자.

일단 spirng boot 기본 설정을 이용해 datasource 설정을 해준다.

application.properties 파일에 다음과 같이 입력 해주자.

 

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=wp
spring.datasource.password=password123
spring.datasource.driver-class-name=org.h2.Driver

 

비밀번호는 테스트이므로 아무거나 입력했다. 나중에는 바꿔줘야 한다.

 

예시

 

 

H2는 인메모리 DB로 애플리케이션이 동작하는 경우에만 DB가 함께 동작한다. Spring이 시작하는 시점에 scheme이 생성되도록 /src/main/resource/scheme.sql을 생성하여 주고 아래에 다음과 같이 table 생성 SQL과 예제데이터를 추가하여 주자.

 

mybatis 설정 관련 첫 번째 링크에서 Spring이 시작되는 시점에 DB의 scheme 을 생성하기 위해 /resource 아래에 scheme.sql 을 생성해 준다고 돼있다. 링크를 타고 가서 스프링 공식 문서를 봐보자.

 

Spring Boot can automatically create the schema (DDL scripts) of your DataSource and initialize it (DML scripts). It loads SQL from the standard root classpath locations: schema.sql and data.sql, respectively. In addition, Spring Boot processes the schema-${platform}.sql and data-${platform}.sql files (if present), where platform is the value of spring.datasource.platform. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on).

 

공식 문서에 의하면, Spring Boot 는 자동으로 스키마를 생성하고, 데이터를 생성할 수 있다. 

기본 루트 클래스 패스 (/src/main/resources/schema.sql 이겠지?) 위치에 있는 scheme.sql 에 기입된 DDL scripts 를 실행해 schema 를 생성하고, data.sql 에 기입된 DML scripts 를 실행해 "데이터" 를 생성한다.

 DDL 과 DML 의 차이는 여기서는 설명하지 않겠다. (다들 알겠지.)

 

 

일단 DDL 을 이용해 schema.sql 을 작성 해야 하는데 ,먼저 schema.sql 파일을 생성하자.

 

schema.sql

 

 

여기서 DDL 을 사용해 scheme.sql 안을 채워야 하는데.

 

https://www.fun-coding.org/mysql_basic3.html

 

데이터베이스 기본 (MySQL): SQL DDL(Data Definition Language) 이해 및 실습 - 잔재미코딩

3.2.2 테이블 삭제¶ 기본 문법 (DROP TABLE 테이블명)mysql> DROP TABLE [IF EXISTS] 테이블명; IF EXISTS 옵션은 해당 데이타베이스 이름이 없더라도 오류를 발생시키지 말라는 의미

www.fun-coding.org

너무 좋은 링크가 있어서, 여길 참고해서 만들어 보도록 하겠다.

 

링크의 내용과 여러 Goolgle 검색을 토대로 DDL 을 작성 해준다. 덤으로 DML 도 살짝 얹었다.

 

CREATE TABLE TEXT_DIARY(
    ID INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20) NOT NULL,
    CONTENT TEXT NOT NULL,
    DATE DATE DEFAULT CURRENT_DATE,
    CREATE_DATE DATETIME DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO TEXT_DIARY (NAME, CONTENT) VALUES ('오늘의 일기', '오늘은 공부를 했다.')

 

일단 간단한 테스트 TABLE 을 만들어 보도록 했다.

TEXT만 입력 가능한 간단한 도메인을 설정 해보자. 

 

TEXT_DIARY 의 PRIMARY_KEY 는 ID 이며  INT 타입으로, 자동으로 증가하는 값을 가진다,

이름을 갖고 있으며 20자 이내여야 한다, 내용은 길게 들어갈 수 있으므로 적당히 TEXT 로 해놓는다,

CREATE_DATE 와 DATE 를 갖는다. 이 둘을 구분한 이유는 상식적으로 일기는 항상 당일에 작성하지는 않기 때문이다.

 

아직은 테스트 단계이기 때문에 전체적인 틀을 잡는다기보다는 간단한 테스트용 테이블을 위와 같이 만들어 보았다.

 

예시

링크에서 시키는대로 엔트리 포인트가 되는 클래스에다가 @MapperScan 을 붙여준다.

 

mapper를 스캔할 수 있도록 설정하자. SpringBoot Configuration 파일상단에 아래와 같이 프로젝트 패키지의 경로를 넣자. 이렇게 하여 프로젝트 하위에 존재하는 Mapper들을 빈으로 등록할수 있게 된다.

 

@MapperScan을 붙인 모습

링크를 보면 모델의 type-alias 를 사용하기 위한 application.properties 파일 수정이 있지만, 그 부분은 제외하도록 하자.

 

DML 을 이용해 나름 데이터를 입력까지 했으니, 일단 꺼내서 확인해보자.

Entity class를 만들자. 이를 위해 package를 새로 따서, 그 안에 TextDiary 라는 Entity Class를 만들 것이다.

 

 

 

그리고 데이터를 입력 해준다.

 

package com.wpapp.voice_diary_server.model;

import lombok.Data;

import java.util.Date;

@Data
public class TextDiary {
    private Integer id;
    private String name;
    private String content;
    private Date date;
    private Date createDate;
}

 

대충 요런 형태로 만들었다.

 

그리고 DAO 역할을 해줄 mapper 인터페이스와(repository), 이 DAO 를 다루는 서비스, 그리고 그 서비스를 다루는 Rest Controller 를 만들 것이다.

 

구조는 이렇게 된다.

 

Rest Controller - 서비스 - DAO(Repository and mapper)

 

결과적으로 다음과 같은 구조가 된다.

 

 

 

TextDiaryMapper 는 다음과 같다.

 

package com.wpapp.voice_diary_server.mapper;

import com.wpapp.voice_diary_server.model.TextDiary;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface TextDiaryMapper {
    List<TextDiary> findAll();
}

 

왜 findAll() 만 있냐면, 당장 schema.sql 에서 넣은 데이터를 확인하고 싶기 때문이다. 

이름이 왜 findAll() 인가? 그냥 말 되게 하면 되는데 getAll() 이던 findAll() 이건...

하지만 프로그래머는 convention 을 따르는 것이 중요하기 때문에, 다음에 이 DAO method 의 관례에 대해 알아보자.

 

 

그리고 TextDiaryMapper.xml 을 만들어 준다.

 

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.wpapp.voice_diary_server.mapper.TextDiaryMapper">
    <select id="findAll" resultType="com.wpapp.voice_diary_server.model.TextDiary">
        SELECT
        ID, NAME, CONTENT, DATE, CREATE_DATE as createDate
        FROM TEXT_DIARY
    </select>
</mapper>

이 파일은 다음과 같은 위치에 있다.

 

이 mapper xml 파일을 제대로 읽어들이려면, application.properties 파일에 이 mapper xml 파일들의 위치를 설정해주어야 한다.

 

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=wp
spring.datasource.password=password123
spring.datasource.driver-class-name=org.h2.Driver


mybatis.mapper-locations=classpath:/mapper/*.xml

그러면 application.properties 의 모양새는 위처럼 된다.

(classpath: 는 resources 다.)

 

 

TextDiaryService 는 다음과 같다.

 

package com.wpapp.voice_diary_server.service;

import com.wpapp.voice_diary_server.mapper.TextDiaryMapper;
import com.wpapp.voice_diary_server.model.TextDiary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class TextDiaryService {
    @Autowired
    private TextDiaryMapper textDiaryMapper;

    public List<TextDiary> findAll() {
        return textDiaryMapper.findAll();
    }
}

 

솔직히 왜 있는줄 모르겠다.

이것 또한 관례를 따른 것인데, 이 관례도 워낙 옛날에 듣고 따라하는 것인지라 나중에 자세히 알아보도록 하자.

(서비스 - 리포지토리 분리)

 

-----

나중에 할 것

1. repository method 명명 관례(혹은 dao method)

2. service, repository 등의 구분 이유 및 관례

------

 

마지막으로 RestController

 

package com.wpapp.voice_diary_server.controller;

import com.wpapp.voice_diary_server.model.TextDiary;
import com.wpapp.voice_diary_server.service.TextDiaryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/text-diaries")
public class TextDiaryRestController {

    @Autowired
    private TextDiaryService textDiaryService;

    @GetMapping
    public List<TextDiary> findAllTextDiary() {
        List<TextDiary> diaries = textDiaryService.findAll();
        return diaries;
    }
}

 

이렇게 일사천리로 코드를 작성하고 localhost:8080/text-diaries 에 들어가보면 어찌 되는가?

 

시간이 이상하게 나온다.

 

...??

 

타임존이 다르다.

 

Date 가 JSON 으로 바뀌는 과정에서 뭔가 이상한 타임존의 변화가 있는 것일까?

일단 Date 를 LocalDate 와 LocalDateTime 으로 바꾸면 당장은 제대로 작동하게 된다.

 

TextDiary를 다음과 같이 바꾸었다.

 

package com.wpapp.voice_diary_server.model;

import lombok.Data;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Data
public class TextDiary {
    private Integer id;
    private String name;
    private String content;
    private LocalDate date;
    private LocalDateTime createDate;
}

 

이러니 제대로 작동한다. 왜 이러는 걸까?

당장은 다음의 과제로 남겨야 할 것 같다. 졸리기 떼문이다.

 

https://www.baeldung.com/java-set-date-time-zone

 

Set the Time Zone of a Date in Java | Baeldung

Learn how to set the time zone of a date in Java 7, Java 8 and Joda-Time

www.baeldung.com

일단 위의 링크와

 

https://www.baeldung.com/jackson-serialize-dates

불러오는 중입니다...

위의 링크를 남기고, 다음에 한번 왜 이러한 상황이 발생 했는지 정리하도록 하자.

 

 

이번에는 "초기 설정" 이기 때문에 세세히 정리를 해두었다. 

초기 설정은, 처음에 한 뒤로 "프로젝트를 다시 시작하지 않는 이상" 다시 안 할 가능성이 높기 때문에,

그만큼 덜 해보게 되고, 그만큼 한 번 하고나면 잊고 끝나기 마련이다.

 

따라서 초기 설정을 세세히 정리하고, 다시 복습하고자 요래 쓸데없이 장황하게 정리를 해놓았다.

 

미래의 나에게 도움이 되길 빈다.

블로그 이미지

맛간망고소바

,