티스토리 뷰

반응형

Spring Boot 에서는 대표적인 DBMS(DataBase Management System)에 표준적으로 접속 정보를 설정하고 JDBC, Connection Pool 등을 자동으로 구성해서 사용자가 쉽게 질의(query)를 할 수 있는 기능을 제공합니다.

1. 프로젝트 생성

아래와 같이 프로젝트 생성 시 JDBC 와 DBMS 에 맞는 Driver 만 선택해주면 의존성 추가를 해줍니다.



실제 이미지와 같이 선택하면 아래 코드와 같이 pom.xml 파일에 두 개의 의존성만이 추가될 뿐입니다. 하지만, 이 두 개의 의존성 추가만으로 DB 접속을 할 수 있는 환경을 쉽게 구축할 수 있습니다.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>


2. DB 연결 정보 설정

프로젝트를 생성한 뒤 실행해보면 아래와 같이 오류가 발생합니다.



이유는 Spring Boot JDBC 환경을 구성한 상태에서 아무런 접속 정보를 입력하지 않았기 때문에 접속할 수 있는 DBMS 가 없기 때문입니다. 만약 Embedded 하게 동작하는 DBMS(H2, HSQL, Derby) 을 프로젝트 생성 시 선택했다면 프로그램 실행과 동시에 DBMS 가 같이 시작되고 Spring Boot 의 기본값에 의해 자동 접속을 하기 때문에 위와 같은 오류가 나타나지 않지만, 위에서 MySQL 을 선택했기 때문에 오류가 발생합니다.

그래서 아래와 같이 application.yml (application.properties 을 이용해도 됩니다) 파일에 DB 접속 정보를 설정합니다. (물론 아래 접속 정보는 실제 정보와 다릅니다)

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost/dev
    username: zepinos
    password: "testpassword"

사실 일반적인 DBMS 는 위의 네 가지 정보만 입력해도 사용가능하지만, 여러가지 설정을 위해서 Spring Boot 레퍼런스 가이드의 부록을 확인하여 더 많은 JDBC 옵션을 이용할 수 있습니다.

추가적으로, 지금 이 글을 적고 있는 현재의 Spring Boot 최신 버전은 2.0.3-RELEASE 로 Connection Pool 은 기본적으로 Hikari CP 를 이용하도록 되어 있습니다. Hikari CP 는 뛰어난 성능을 보여주는 Connection Pool 이지만 설정변수가 기존의 다른 Connection Pool 와 조금 다릅니다. 하지만, Spring Boot 에서 기본 DBMS 설정 정보로 입력해줄 경우에는 Hikari CP 고유의 설정변수가 아니어도 치환되어서 자동으로 설정을 해줍니다.
Spring Boot 의 기본 DBMS 접속 설정은 1 개의 접속 설정만 입력받을 수 있기 때문에, 2 개 이상의 접속 설정을 입력 받아 Connection 을 여러 개 만들어야 한다면 Hikari CP 을 이용할 경우 설정 변수명을 바꿔야 하므로 주의해야 합니다.

위와 같이 설정 정보를 입력한 후 다시 프로그램을 실행하면 정상적으로 프로그램이 실행되는 것을 확인할 수 있습니다.



3. 웹사이트 생성 확인

DB 에 질의를 하기 전에, 먼저 확인하기 쉽도록 웹페이지를 만들어 보겠습니다.

먼저 Controller 파일입니다.

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Collections;
import java.util.Map;

@Controller
@RequestMapping("/hello")
public class HelloController {

	@GetMapping("")
	public String get(ModelMap modelMap) throws Exception {

		Map<String, String> test = Collections.singletonMap("name", "테스트");
		Map<String, Object> result = Collections.singletonMap("test", test);

		modelMap.put("result", result);

		return "/hello/get";

	}

}

Controller 파일의 return 에서 "hello/get" 을 입력했고, 프로젝트 생성 시 Thymeleaf 을 선택했기 때문에 templates/hello/get.html 파일을 아래와 같이 만들어서 내용을 출력합니다.

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>Hello</title>
</head>
<body>
Hello World : <span th:text="${result.test.name}">HTML</span>
</body>
</html>

프로그램을 실행해서 결과를 확인해봅니다.



Controller 에서 입력한 테스트라는 글자가 표시됨을 확인할 수 있습니다.


4. JdbcTemplate 로 질의하기

먼저 DB 에 질의하기 위해서 test 테이블을 만들고 아래와 같이 데이터를 입력해두었습니다.



DB 에 질의하기 위해 Controller 을 수정하고 Service 와 DAO 파일을 추가하였습니다.

HelloController

package com.example.demo.controller;

import com.example.demo.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/hello")
public class HelloController {

	@Autowired
	private HelloService helloService;

	@GetMapping("")
	public String get(ModelMap modelMap) throws Exception {

		modelMap.put("result", helloService.getTest());

		return "/hello/get";

	}

}

HelloService

package com.example.demo.service;

import com.example.demo.dao.TestDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Map;

@Service
public class HelloService {

	@Autowired
	private TestDao testDao;

	public Map<String, Map<String, Object>> getTest() throws Exception {
		return Collections.singletonMap("test", testDao.getName(1));
	}

}

TestDao

package com.example.demo.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class TestDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public Map<String, Object> getName(int seq) throws Exception {
		return jdbcTemplate.queryForMap(" select name from test where seq = ?", seq);
	}

}

그리고 프로그램을 재실행 한 뒤 웹페이지에 접속하면 다음과 같이 출력됩니다.



5. java.sql.Connection 획득하기

아주 간혹, 혹은 본인의 익숙함 때문에 java.sql.Connection 객체를 그대로 이용하고 싶은 경우도 있습니다. 예전처럼 객체지향 답지 않게 Connection 객체를 생성해서 Statmement/PreparedStatement 로 질의를 정의하고 ResultSet 으로 결과를 획득하여 값을 처리하는 것이 효율적인 경우도 분명 존재하기 때문에 별도로 설정을 하지 않고 Spring Boot 에서 Connection 을 획득할 수 있다면 더욱 좋을 것입니다.

위의 TestDao.java 파일의 getName() 메서더를 아래와 같이 변경해보았습니다. 실행해보면 결과는 이전과 동일합니다. 다만 코드 길이가 많이 차이남을 알 수 있습니다. JdbcTemplate 에서 Map 객체에 값을 주입해주기 때문에 ResultSet 에서 값을 가져와 객체를 생성해 값을 입력하는 과정을 생략할 수 있기 때문입니다. 주의할 점은 ResultSet 은 DBMS 의 커서를 받아 한 레코드씩 결과를 전송받아 전달해주는 객체이기 때문에 게시판처럼 수십 줄 정도를 불러와 처리하는 경우에는 별다른 차이가 없으나 수천, 수만 줄 이상의 레코드를 결과로 받아와 한 번에 처리해야 하는 경우라면 객체에 주입하기 위해, 그리고 주입 후 처리를 위해 덩치 큰 객체와 이를 처리하기 위한 객체 사용 등으로 GC 에 굉장한 부담을 줄 수 있습니다. 그러므로 Connection 객체를 이용해서 직접 ResultSet 결과를 처리해야 할 경우가 있기 때문에 아래와 같은 방법을 알고 있는 것이 좋습니다.

	public Map<String, Object> getName(int seq) throws Exception {

		Connection conn = jdbcTemplate.getDataSource().getConnection();

		PreparedStatement pstmt = conn.prepareStatement(" select name from test where seq = ? ");
		pstmt.setInt(1, seq);

		ResultSet rs = pstmt.executeQuery();

		Map<String, Object> result;
		if (rs.next()) {

			String name = rs.getString("name");

			result = Collections.singletonMap("name", name);

		} else {

			result = Collections.singletonMap("name", null);

		}

		return result;

	}

위의 코드에서는 JdbcTemplate 에서 DataSource 을 획득하여 Connection 을 획득하는 방법을 보여주고 있습니다.
JdbcTemplate 에서 이렇게 쉽게 DataSource 을 획득할 수 있지만, 사실 Spring 기반이기 때문에 Bean 으로 등록된 객체는 모두 주입(Inject) 받을 수 있기 때문에 직접 DataSource 객체를 획득할 수도 있습니다.

	@Autowired
	private ApplicationContext applicationContext;

	public Map<String, Object> getName(int seq) throws Exception {

		DataSource ds = (DataSource) applicationContext.getBean("dataSource");
		Connection conn = ds.getConnection();

		// 이하 생략

	}


반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함