본문으로 바로가기

목차

    0. 환경

    • windows10
    • openjdk version "1.8.0_242"
    • STS4 TooL (이클립스) 
    • Spring Framework 4.3.8 released
    • Spring MVC Project (Legacy Project) 

    1. maven 추가 (pom.xml)

    • mysql-connector-java
    • mybatis
    • mybatis-spring
    • spring-jdbc
    • HikariCP
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
    </dependency>
    
    <!-- MyBatis 3.4.1 -->
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
    	<groupId>org.mybatis</groupId>
    	<artifactId>mybatis</artifactId>
    	<version>3.4.1</version>
    </dependency>
    
    <!-- MyBatis-Spring -->
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
    	<groupId>org.mybatis</groupId>
    	<artifactId>mybatis-spring</artifactId>
    	<version>1.3.0</version>
    </dependency>
    
    <!-- Spring-jdbc -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-jdbc</artifactId>
    	<version>${org.springframework-version}</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP --> 
    <dependency> 
    	<groupId>com.zaxxer</groupId> 
    	<artifactId>HikariCP</artifactId> 
    	<!-- <version>3.4.0</version> --> 
    	<version>4.0.3</version>
    </dependency>

     

    [HikariCP Artifacts version]

    • 자바(jdk) 버전에 맞게 HikariCP버전을 조절해줍니다.
    • 필자는 openjdk 1.8.0을 사용하므로 4.0.3 버전을 사용합니다.
    Java 11+ maven artifact:
    <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP</artifactId>
       <version>5.0.0</version>
    </dependency>
    
    Java 8 maven artifact (maintenance mode):
    <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP</artifactId>
       <version>4.0.3</version>
    </dependency>
    
    Java 7 maven artifact (maintenance mode):
    <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP-java7</artifactId>
       <version>2.4.13</version>
    </dependency>
    
    Java 6 maven artifact (maintenance mode):
    <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP-java6</artifactId>
       <version>2.3.13</version>
    </dependency>

     

    [HikariCP]

    • HikariCP란 Springboot 2.0부터 default로 설정되어 있는 DB Connection Pool로써 Zero-Overhead가 특징으로 높은 성능을 자랑하는 DB Connection Pool입니다.
    • HikariCP가 해주는 역할은 Database와의 Connection Pool을 관리합니다.
    • HikariCP는 미리 정해놓은 만큼에 커넥션은 Pool에 담아 놓습니다. 요청이 들어오면 Thread가 커넥션을 요청하고, Hikari는 Pool내에 있는 커넥션을 연결해줍니다.
    • Connection pool을 관리하는 것이 중요한 이유는 성능에 큰 영향을 미칩니다.

     

    [Connection Pool]

    • Connection Pool이란 Pool 속에 Database와의 연결(connection)을 미리 만들어 두고 Database에 접근 시 Pool에 남아 있는 Connection 중 하나를 받아와서 사용한 뒤 반환하는 기법을 말합니다.
    • 데이터베이스로의 추가 요청이 필요할 때 연결을 재사용할 수 있도록 관리되는 데이터베이스 연결의 캐시입니다.
    • Pool 속에 미리 Connection이 생성되어 있기 때문에 Connection을 생성하는 데 드는 연결 시간이 소비되지 않습니다.
    • Connection을 생성하고 닫는 시간이 소모되지 않아 애플리케이션의 실행 속도가 빨라지며, 또한 한 번에 생성될 수 있는 Connection 수를 제어하기 때문에 동시에 많은 요청이 들어와도 웹 애플리케이션이 쉽게 다운되지 않습니다.

    2. Connection Pool bean 등록 (root-context.xml)

    src/main/webapp/WEB-INF/spring/root-context.xml

    2.1. Namespaces

    • Mybatis 설정을 위해 root-context.xml의 Namespace를 수정해 줍니다.

    Namespaces

    2.2. HikariDataSource, HikariConfig, SqlSessionFactoryBean, SqlSessionTemplate

    • DB 연동(mysql, mybatis, hikaricp)을 위해 root-context.xml에 다음과 같이 작성합니다.
    • DB 연결 정보는 자기 자신 DB 정보에 맞게 수정해줍니다.
    <!-- DB POOL Setting Start -->
    	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    		<constructor-arg ref="hikariConfig" />
    	</bean>	
    
    	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig"> 
    		<property name="idleTimeout" value="30000" />
    		<property name="connectionTimeout" value="3000" /> 
    		<property name="maxLifetime" value="400000" />
    		<property name="maximumPoolSize" value="300" /> 
    		<property name="minimumIdle" value="20" />
    		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
    		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306?useSSL=false&amp;serverTimezone=UTC" />
    		<property name="username" value="id" /> 
    		<property name="password" value="password" /> 		
    	</bean>	
    
    	<!-- mybatis SqlSessionFactoryBean -->
    	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="configLocation" value="classpath:/mybatis-config.xml" />
    		<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml" />
    	</bean>
    	
    	<!-- 
    		mybatis Template
    		destroy-method속성 : Spring Bean소멸전 호출되는 메소드를 속성값으로 설정 
    		clearCache : sqlSession관련 JDBC 객체를 소멸시키는 메소드(= sqlSession.close() )			 
    	-->
    	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
    		<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
    	</bean>
    <!-- DB POOL Setting END -->

    2.3. 설정 설명

    [HikariDataSource]

    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    	<constructor-arg ref="hikariConfig" />
    </bean>
    • 데이터베이스와의 연결을 담당하는 객체(dataSource)를 설정합니다.
    • hikariCP를 사용할 것이므로 hikariConfig에 연결 정보를 세팅하면 됩니다.

    [HikariConfig]

    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig"> 
    	<property name="idleTimeout" value="30000" />
    	<property name="connectionTimeout" value="3000" /> 
    	<property name="maxLifetime" value="400000" />
    	<property name="maximumPoolSize" value="300" /> 
    	<property name="minimumIdle" value="20" />
    	<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
    	<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306?useSSL=false&amp;serverTimezone=UTC" />
    	<property name="username" value="id" /> 
    	<property name="password" value="password" /> 		
    </bean>
    • hikariCP 설정을 할 수 있습니다.
    • 연결을 담당하는 dataSource 객체의 db연결 정보를 다음과 같이 이곳에 작성하면 됩니다.
    • 설정 관련 정보는 해당 사이트를 참고해주세요

    [SqlSessionFactoryBean]

    <!-- mybatis SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    	<property name="dataSource" ref="dataSource" />
    	<property name="configLocation" value="classpath:/mybatis-config.xml" />
    	<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml" />
    </bean>
    • dataSource(db연결 정보 객체), mybatis-config.xml(설정 파일), mappers/**/*Mapper.xml(SQL 매퍼 파일)을 참조해 connection을 생성하고 처리하는 객체입니다.
    • 데이터베이스와의 연결과 SQL의 실행에 대한 모든 것을 가진 중요한 객체입니다.
    • 객체가 DataSource를 참조하여 MyBatis와 Mysql 서버를 연동시켜줍니다.

    [SqlSessionTemplate]

    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
    	<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
    • mybatis spring 연동 모듈의 핵심
    • DAO나 Mapper에서 공유하기 위해 설정합니다.
    • SqlSession을 구현하고 코드에서 SqlSession를 대체하는 역할
    • 쿼리문을 수행해주는 역할
    • DAO클래스에 직접 SqlSession객체를 선언하고 @Autowired로 의존 주입하여 쿼리문을 수행하는 방식을 위해 설정합니다.

    3. mybatis-config.xml (mybatis 설정 파일)

    경로) classpath:/mybatis-config.xml (root-context.xml)

    <property name="configLocation" value="classpath:/mybatis-config.xml" />

     

    [mybatis-config.xml]

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration 
    	PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    	"http://mybatis.org/dtd/mybatis-3-config.dtd">
    	<configuration>
    	
    		<!-- VO&DTO를 등록하세요 (별칭 설정)--> 
    		<typeAliases>
    			<typeAlias type="com.test.dto.TestDTO" alias="TestDTO"/>
    		</typeAliases>
    
    	</configuration>
    • src/main/resources에 파일을 생성합니다.
    • mybatis의 설정 관련 파일입니다.
    • DTO로 사용할 클래스의 별명을 미리 작성해둡니다.

    4. mapperLocations (mapper 경로 설정)

    경로) classpath:mappers/**/*Mapper.xml (root-context.xml)

    <property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml" />

     

    [TestMapper.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="TestMapper">
    	
    	<select id="test" parameterType="map" resultType="TestDTO">
    		SELECT id, macaddress, ip
    		FROM node
    		LIMIT #{from}, #{to}
    	</select>    
    	
    </mapper>​
    • src/main/resources에 mapper 패키지 생성 후 TestMapper.xml 파일을 생성합니다. 
    • mapper 패키지에 테스트용 TestMapper.xml 파일 생성하고 테스트를 위한 쿼리도 작성합니다.
    • xml을 이용하여 다음과 같이 SQL 쿼리를 작성하여 사용합니다.
    • test 쿼리는 map을 이용해 요청을 받아 from, to값을 대입하고, TestDTO를 이용해 반환합니다.

    [test table 생성]

    • 다음과 같은 node 테이블과 값이 있다고 가정하에 테스트를 합니다. 간단하게 생성해주세요

    5. 구조(예시)

    구조

    6. DTO 

    [DTO 란]

    • 데이터 전송 객체(data transfer object, DTO)는 프로세스 간에 데이터를 전달하는 객체
    • 계층 간 데이터 교환을 위한 객체(Java Beans)
    • DB에서 데이터를 얻어 ServiceController 등으로 보낼 때 사용하는 객체
    • 로직을 갖고 있지 않는 순수한 데이터 객체(getter/setter 메서드)
    • View LayerDB Layer의 역할을 철저하게 분리하기 위해
      • 계층 간 데이터 교환은 DTO로 하고,
      • 실제 DB의 테이블과 매칭 될 클래스를 Entity Class(Domain)로 따로 두는 경우도 있지만
      • 이 예시는 DTO 하나로 처리했습니다.

    [Controller] --[DTO]-- [Service] --[DTO]-- [DAO(Repository)] -- [Entity Class Domain)] -- [DB]

     

    [TestDTO.java]

    package com.test.dto

    package com.test.dto;
    
    public class TestDTO {
    
    	private String id;
    	private String macaddress;
    	private String ip;
    			
    	public String getId() {
    		return id;
    	}
    	
    	public void setId(String id) {
    		this.id = id;
    	}
    	
    	public String getMacaddress() {
    		return macaddress;
    	}
    	
    	public void setMacaddress(String macaddress) {
    		this.macaddress = macaddress;
    	}
    
    	public String getIp() {
    		return ip;
    	}
    
    	public void setIp(String ip) {
    		this.ip = ip;
    	}
    	
    }

    7. DAO

    [DAO 란]

    • DAO(Data Access Object) = Repository package
    • 실제로 DB에 접근하는 객체
    • Service와 DB를 연결하는 고리의 역할
    • DB의 data에 Access 하는 객체
    • persistence 계층 : Database(영구 저장소)에 data를 CRUD 하는 계층

    [TestDAO.java (interface)] 인터페이스

    package com.test.dao

    package com.test.dao;
    
    import java.util.HashMap;
    import java.util.List;
    
    import com.test.dto.TestDTO;
    
    public interface TestDAO {
    
    	public List<TestDTO> test(HashMap<String, Object> map) throws Exception;
    }

    [TestDAOImpl.java] 구현체 클래스

    package com.test.dao;

    • SqlSessionTemplate를 주입해서 쿼리를 실행합니다.
    • 위에서 생성한 mapper의 namespace를 상수로 선언해둡니다.
      • private static final String Namespace = "TestMapper";
    package com.test.dao;
    
    import java.util.HashMap;
    import java.util.List;
    
    import org.mybatis.spring.SqlSessionTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    
    import com.test.dto.TestDTO;
    
    @Repository
    public class TestDAOImpl implements TestDAO {
        
        @Autowired
        private SqlSessionTemplate sqlSession;
        
        private static final String Namespace = "TestMapper"; //<mapper namespace="TestMapper">
    
    	@Override
    	public List<TestDTO> test(HashMap<String, Object> map) throws Exception {
    
    		return sqlSession.selectList(Namespace + ".test", map);
    	}
    
    }

    8. Service

    [TestService.java]

    package com.test.service

    package com.test.service;
    
    import java.util.HashMap;
    import java.util.List;
    
    import javax.annotation.Resource;
    import javax.inject.Inject;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.test.dao.TestDAO;
    import com.test.dto.TestDTO;
    
    @Service
    public class TestService {
    
    	@Autowired
    	private TestDAO dao;
    	
    	public List<TestDTO> test(HashMap<String, Object> map) throws Exception {
    		
    		return dao.test(map);
    	}
    }

    9. Controller

    [TestApiController.java]

    package com.test.controller

    package com.test.controller;
    
    import java.util.HashMap;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.test.dto.TestDTO;
    import com.test.service.TestService;
    
    @RestController 
    public class TestApiController {
    
    	@Autowired
    	TestService service;
    	
    	@GetMapping("/testApi")
    	public List<TestDTO> testApi(@RequestParam(value="from", defaultValue="0") int from, @RequestParam(value="to", defaultValue="10") int to ) throws Exception {
    		
    		HashMap<String, Object> map = new HashMap<String, Object>();
    		map.put("from", from);
    		map.put("to", to);
    		
    		return service.test(map);
    	}
    }

    10. 컴포넌트 스캔 설정

    • servlet-context.xml, root-contex.xml에 어노테이션으로 선언한 컴포넌트 스캔을 할 수 있도록 설정합니다.
    • xml 파일은 모두 객체(Bean)를 정의합니다.

    [servlet-context.xml]

    • src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
    • url과 관련된 controller나, @(어노테이션), ViewResolver, Interceptor, MultipartResolver 등의 설정을 해줍니다.
    <context:component-scan base-package="com.test.controller" />

    [root-contex.xml]

    • src/main/webapp/WEB-INF/spring/root-context.xml
    • servlet-context.xml 과는 반대로 view와 관련되지 않은 객체(Service, Repository(DAO), DB 등 비즈니스 로직과 관련된 설정)를 정의합니다.
    <context:component-scan base-package="com.test.dao" />
    <context:component-scan base-package="com.test.dto" />
    <context:component-scan base-package="com.test.service" />

    11. 결과

    • http://localhost:8080/testApi?from=0&to=100