본문 바로가기
Back-End/Spring Boot

[ Spring boot ] 스프링부트 데이터베이스 연동 ③

by 2CHAE._.EUN 2022. 5. 10.
프로젝트 생성

 

데이터베이스 모듈을 같이 선택해서 프로젝트를 만들 경우 Datasource에 대한 기본적인 setting이 되어 있어야

실행이 된다. 기본적인 환경 설정을 하고 시작해야한다. 

 

# Port
server.port=8024

# thymeleaf
spring.thymeleaf.prefix=classpath:templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.check-template-location=true
spring.thymeleaf.mode=HTML
spring.thymeleaf.cache=false

# DataSource
spring.datasource.url= jdbc:mysql://localhost:3306/capstone?autoReconnect=true
spring.datasource.username= spark
spring.datasource.password= spark
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver

spring.jpa.database=mysql

# New DataSource
# Datasource 정보를 읽어들일 때 datasource 하위의 hikari를 읽어들이지 않기 때문에
# 데이터소스 관련된 정보들을 hikari 내부로 옮겨서 새로 만든다.
# 데이터 베이스 URL 경우도 HikariCP는 jdbc-url이라는 지정된 이름(키)으로 받기 때문에 url에서 jdbc-url로 변경
spring.datasource.hikari.jdbc-url = jdbc:mysql://localhost:3306/capstone?autoReconnect=true&serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.hikari.username = spark
spring.datasource.hikari.password = spark
spring.datasource.hikari.driver-class-name = com.mysql.cj.jdbc.Driver

 

* 필요한 폴더 및 파일 구성

총 6개의 파일을 가지고 데이터베이스와 스프링부트 연동을 구현한다.

 

1. src/main/java

→ com.capstone

              → configuration

                          DBConfiguration.java ( DB 연동과 관련된 환경설정 파일 )

              → DTO

                          UserDTO.java

              → mapper

                          UserMapper.java ( 인터페이스( 설계도 ) ) 

 

2. src/main/resouces

→ mapper 

              UserMapper.xml ( SQL 쿼리문만 적어놓는 파일 )

application.properties 

 

3. src/test/java ( 맨 마지막에 제대로 연동이 되었는지 테스트 하는 파일 )

→ com.capstone 

              MapperTest.java

 

[ 필요한 폴더 및 파일 구성하기 ]

 

1. 각 폴더에 필요한 파일 생성하기

 

2. UserDTO 파일 작성하기

package com.example.capstone.DTO;

public class UserDTO {

    private String UserEmail;
    private String UserPassWord;
    private String UserName;

    public UserDTO(){}

    public UserDTO(String userEmail, String userPassWord, String userName) {
        UserEmail = userEmail;
        UserPassWord = userPassWord;
        UserName = userName;
    }

    public String getUserEmail() {
        return UserEmail;
    }

    public void setUserEmail(String userEmail) {
        UserEmail = userEmail;
    }

    public String getUserPassWord() {
        return UserPassWord;
    }

    public void setUserPassWord(String userPassWord) {
        UserPassWord = userPassWord;
    }

    public String getUserName() {
        return UserName;
    }

    public void setUserName(String userName) {
        UserName = userName;
    }
}

 

3. DBConfiguration 파일 작성하기

 

@Configuration : 스프링부트 환경설정 클래스임을 명시한다. 자동으로 Bean이 등록된다.

→ @Configuration이 붙게 되면 @ComponentScan이 스캔할 때, 이 클래스에 @Bean으로 지정한 모든 

Bean들도 IOC 컨테이너에 등록이 된다. 

 

컨테이너에 객체를 담아놓고 필요할 때 마다 꺼내가서 주입하는 DI( Dependency Injection ) 방식을 사용한다.  

 

* Inversion of Control : IOC를 구현하는 프레임워크로 객체를 관리하고, 객체의 생성을 책임지고 의존성을 관리하는
컨테이너이다. 의존 관계의 방향이 달라지게 되어, 프로그래머가 작성한 프로그램이 재사용 라이브러리의 흐름

제어를 받게되는 소프트웨어 디자인 패턴이다.

( 주객이 전도되어서 프레임워크를 개발할 때 컨테이너의 객체가 필요하다면 가져와서 주입해야한다. )

* preloading : 스프링부트가 시작되기 전에 미리 다 load해서 등록을 해놓는다. ( <--> lazyloading )

 

Configuration 파일에는 크게 4가지가 존재한다.

1. Hikari 객체를 생성하는 부분

ⓐ Hikari Config를 생성하는 부분

ⓑ Hikari 데이터 소스를 생성하는 부분

 데이터베이스 정보를 가져와서 Hikari 환경 설정 객체를 생성해서 그 객체를 가지고 Hikari 데이터 소스 객체를
     생성한다. ( 데이터베이스 연동 과정이 성공 )

2. MyBatis 객체를 생성하는 부분

SqlSessionFactory 생성

SqlSessionTemplate 생성

SqlSessionFactoryBean을 가지고 SqlSessionFactory 객체를 생성하고, 그 공장 객체를 가지고 Template 같은 틀을
공장 안에 넣어주는 형식으로 SqlSessionTemplate은 SqlSessionFactory를 통해 만든다.

 

Data Connection 객체들을 가지고 MyBatis 관련 객체를 생성해야한다.

 

package com.example.capstone.configuration;

// DB 연동을 위한 환경설정

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

@Configuration
@PropertySource("classpath:/application.properties") // /는 src/main/resources를 의미한다.
public class DBConfiguration {

    // Hikari 설정 1
    public HikariConfig hikariConfig(){ // 환경 설정 객체
        return new HikariConfig();
    }

    // Hikari 설정 2
    public DataSource dataSource(){ // 객체를 생성

        DataSource dataSource = new HikariDataSource( hikariConfig() );
        System.out.println(dataSource.toString());

        return dataSource;
    }

    // MyBatis 설정 1 : SqlSessionFactory <-- SqlSessionFactoryBean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
         SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
         return factoryBean.getObject();
    }

    // MyBatis 설정 2 : SqlSessionTemplate <-- SqlSessionFactory
    public SqlSessionTemplate sqlSessionTemplate() throws Exception{
        return new SqlSessionTemplate(sqlSessionFactory());
    }

}

 

* Hikari CP : 데이터베이스 커넥션을 관리해주는 도구. Connection Pool은 설정된 Pool의 크기만큼 Conenction을

허용하여 순차적으로 DB에 요청된 Connection을 처리해준다. ( 스프링부트 Default DBCP )

* MyBatis : 관계형 데이터베이스와 스프링을 연동해주는 프레임워크

 

여러 사용자를 동시에 처리해야하는 데이터베이스 연결을 이용할 때 Conenction Pool을 사용한다. 

DataSource 인터페이스를 통해서 사용하고 CP로는 Hikari를 사용한다. 

 

 

HikariCP

 

1. @Bean : 리턴되는 객체를 IOC 컨테이너에 등록한다.

→ 등록하는 이유는 프로젝트가 시작해서 끝날 때까지 필요한 객체들을 미리 preloading 해놓아야 하기 때문이다.

    필요할때마다 주입하는 방식으로 프로젝트가 구동되면서 애너테이션이 붙은 객체들을 컨테이너의 객체로 등록한다.

 

스프링부트와 데이터베이스를 연동하기 위해서는 Hikari 객체와 MyBatis 객체가 모두 필요하기 때문에 컨테이너에

이 객체들을 넣어놓고 실행한다.

 

특별히 지정하는 이름이 없다면 IOC 컨테이너에는 해당 메소드명으로 등록이 된다. ( 이름 지정도 가능하지만 보통은

메소드명으로 등록을 하고 중복은 허용하지 않는다. )

 

2. ConfigurationProperties( prefix="spring.datasource.hikari")

application.properites 파일로부터 데이터베이스 관련된 정보를 읽어와서 Hikari 설정 객체를 리턴한다.

" "안에 들어가는 접두어는 해당 접두어로 시작하는 정보들을 읽어온다는 의미가 된다.

 

 @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariConfig hikariConfig(){ // 환경 설정 객체
        return new HikariConfig();
    }

    // Hikari 설정 2 : Hikari 설정 객체( hikariconfig() )를 받아서 데이터 소스 객체 생성
    // Hikari DataSource : DBCP을 활용해 실제 데이터베이스에 연결하는 역할
    @Bean
    public DataSource dataSource(){ // 객체를 생성

        DataSource dataSource = new HikariDataSource( hikariConfig() );
        System.out.println(dataSource.toString());

        return dataSource;
    }

 

만약 아이디나 패스워드가 틀렸다면 이 단계에서 오류가 발생하고, application.properties 파일을 체크해야한다.

DB 연결이 잘 되었는지 확인하기 위해서는 콘솔에 datasource 객체를 toString() 메소드로 출력해야한다.

→ Hikari 뒤에 숫자가 붙어서 나온다. ( HikariDataSource ( HikariPool-1 ) )

이 단계를 통해서 Hikari Connection Pool 연결이 완성이 되고, DB 연결이 필요한 부분에서 이 DataSource를

가지고 연결해주면 된다. DataSource를 MyBatis에 줘서 필요한 데이터 베이스 연동 과정을 진행한다.

 

 

MyBatis

 

1. SqlSessionFactoryBean을 가지고 SqlSessionFactory 객체를 생성한다.

이때, 데이터 소스 객체를 넘겨 받아서 처리해도되고, 아니면 setDataSource(datasource())로 해줘도 됨.

→ SqlSessionFactory 생성을 위해서 내부의 SqlSessionFactoryBean을 사용한다.

 

* 기본적인 설정 3가지 → SqlSessionFactoryBean 객체에 해당 정보들을 넘겨준다.

 

setDataSource :  빌드된 DataSource 셋팅

 

setMapperLocations : SQL 구문이 작성된 Mapper.xml의 경로를 정확히 등록

→ Mapper에 대한 리소스는 ApplicationContext 객체에서 가져올 수 있다.

    ApplicationContext는 프레임워크 컨테이너라고 생각하면 된다.

    ApplicationContext는 애플리케이션이 시작해서 끝나는 순간까지 이 애플리케이션에서 필요한 모든 자원들을
    모아놓고 관리한다.

→ Mapper 파일이 생성될 수 있는 모든 패턴을 고려해서 경로를 설정해야한다.

 

setTypeAliasesPackage : 인자로 Alias 대상 클래스가 위치한 패키지 경로

→ 여러 정보를 하나의 DTO 객체로 만들고 전달해서 데이터베이스에 전달할 수 있도록 DTO 객체 위치를 전달해야 한다.

 

* 주의사항 

SqlSessionFactory에 저장할 config 설정 시 Mapper에 서 사용하고자 하는 DTO, VO, Entitiy에 대해서 

setTypeAliasesPackage 지정이 필요하다. 만약 지정해주지 않는다면 aliases를 찾지 못한다는 오류가 발생할 수 있다.

 

* applicationContext 객체가 에러나는 이유 : new를 사용해서 객체를 매번 생성하기보다 CP 방식을 사용해서 컨테이너에서 필요한 객체를 꺼는 DI를 수행해기 위해서는 applicationContext 또한 CP에 주입을 해야한다. 

즉 getResource() 메소드를 사용하기 위해서는 applicationContext 객체를 주입시켜야하고 주입시키는 방법으로는

@Autowired 애너테이션을 사용해야한다. @Autowired는 해당 애너테이션이 붙은 객체에 CP를 연결해준다.

 

@Autowired
private ApplicationContext applicationContext;
    
@Bean
public SqlSessionFactory sqlSessionFactory( DataSource dataSource ) throws Exception {
	SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); // factoryBean이 SqlSessionFactory 객체를 생성한다.

    factoryBean.setDataSource( dataSource );
    factoryBean.setMapperLocations( applicationContext.getResources("classpath:/mapper/*Mapper.xml"));
    // classpath는 src/main/resources이다.
    // 여러 개의 Mapper를 사용할 수 있기 때문에 *를 사용해서 모든 xml 파일로 설정할 수 있다. ( + 폴더 추가 가능 )
    factoryBean.setTypeAliasesPackage("com/example/capstone/DTO");
    
    return factoryBean.getObject();
    
}

 

 

2. SqlSessionFactory 객체를 넘겨받아 SqlSessionTemplate 객체를 생성 및 리턴한다.

 

SqlSessionTemplate : SQL 구문의 실행과 트랜잭션을 관리하는 가장 핵심적인 역할이다. 

MyBatis의 SqlSession 객체가 Spring + MyBatis 연동 모듈에서는 SqlSessionTemplate이 대체한다.

 

@Bean
public SqlSessionTemplate sqlSessionTemplate( SqlSessionFactory sqlSessionFactory ) throws Exception{
	return new SqlSessionTemplate(sqlSessionFactory);
}

 

 

전체 DB 연동 환경 설정 파일

 

package com.example.capstone.configuration;

// DB 연동을 위한 환경설정

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

@Configuration
@PropertySource("classpath:/application.properties") // /는 src/main/resources를 의미한다.
public class DBConfiguration {

    @Autowired
    private ApplicationContext applicationContext;

    // Hikari 설정 1
    // @Bean : 리턴되는 객체를 IoC 컨테이너에 등록
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariConfig hikariConfig(){ // 환경 설정 객체
        return new HikariConfig();
    }

    // Hikari 설정 2 : Hikari 객체를 받아서 데이터 소스 객체 생성
    // Hikari DataSource : DBCP을 활용해 실제 데이터베이스에 연결하는 역할
    @Bean
    public DataSource dataSource(){ // 객체를 생성

        DataSource dataSource = new HikariDataSource( hikariConfig() );
        System.out.println(dataSource.toString());

        return dataSource;
    }

    // MyBatis 설정 1 : SqlSessionFactory 객체 생성 <-- SqlSessionFactoryBean
    //
    @Bean
    public SqlSessionFactory sqlSessionFactory( DataSource dataSource ) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); // factoryBean이 SqlSessionFactory 객체를 생성한다.

        factoryBean.setDataSource( dataSource );
        factoryBean.setMapperLocations( applicationContext.getResources("classpath:/mapper/*Mapper.xml"));
        // classpath는 src/main/resources이다.
        // 여러 개의 Mapper를 사용할 수 있기 때문에 *를 사용해서 모든 xml 파일로 설정할 수 있다. ( + 폴더 추가 가능 )
        factoryBean.setTypeAliasesPackage("com/example/capstone/DTO");

        return factoryBean.getObject();
    }

    // MyBatis 설정 2 : SqlSessionTemplate <-- SqlSessionFactory
    @Bean
    public SqlSessionTemplate sqlSessionTemplate( SqlSessionFactory sqlSessionFactory ) throws Exception{
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

 

모든 구현 부분은 서로 의존 관계를 가지고 있다. 

 

 

4. Mapper inferface 작성하기 

 

package com.example.capstone.mapper;

import com.example.capstone.DTO.UserDTO;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {

    // 설계도 -> 실질적인 구현은 XML 파일에서 SQL 쿼리로 처리한다.

    // 유저 추가
    public void insertUser(UserDTO userDTO);

    // 유저 정보 확인
    public void checkUser();

    // 유저 정보 수정
    public void updateUser();

    // 유저 삭제
    public void deleteUser();

}

 

 

5. Mapper.xml 파일에 SQL문 쿼리 작성하기

 

 

id에는 인터페이스에서 작성한 함수이름을 넣어줘야하고 파라미터에는 넘어오는 객체명을 적어줘야한다.

 

<?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/example/capstone/mapper/UserMapper">

    <insert id="insertUser" parameterType="UserDTO">
        INSERT INTO coinuser ( userEmail, userPassword, userName )
        Values ( #{userEmail}, #{userPassword}, #{userName} );
    </insert>


</mapper>

 

 

6. test 파일을 통해 테스트하기

 

package com.example.capstone;

import com.example.capstone.DTO.UserDTO;
import com.example.capstone.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testInsert(){

        UserDTO u1 = new UserDTO();
        u1.setUserEmail("2chaechae@gmail.com");
        u1.setUserName("이채채");
        u1.setUserPassWord("1234");

        System.out.println(u1);
        userMapper.insertUser(u1);
    }
}