오늘 만들 것
많은 API는 DB로부터 데이터를 불러오거나 사용자로부터 전달받은 데이터를 DB에 저장하는 작업을 수행합니다.
오늘은 MyBatis를 이용해 DB에 접속하고, DB로부터 간단한 값을 불러오는 기능을 구현합니다.
만약 이미 MyBatis를 접해보셨거나 사용법을 알고 계신다면 굳이 이번 글은 자세히 보지 않으셔도 됩니다.
시작하기 전에: Mapper? ORM?
이미 다양한 웹 기술을 접해보았다면 ORM(Object-Relational Mapper)에 대해 이미 알고계실 수 있습니다.
그렇다면 오늘 다룰 MyBatis는 ORM 프레임워크일까요?
종종 논란이 되는 주제이지만 MyBatis는 ORM이 아니라는 의견이 많습니다.
DBMS에 종속적이지 않고 SQL에 대해 잘 몰라도 각 객체의 메소드를 호출하며 DB를 다룰 수 있는 ORM과 달리,
MyBatis는 SQL을 직접 작성하여 객체에 데이터를 Mapping하기 때문입니다.
그렇다고 MyBatis가 ORM 프레임워크보다 더 낮은 수준인 것은 아닙니다.
SQL문을 직접 작성하기 때문에 ORM에서 지원하지 않는 복잡한 질의를 요청할 수 있고,
각 DBMS가 독점적으로 제공하는 기능을 사용할 수도 있다는 장점이 있습니다.
시작하기 전에: MySQL
이번 예제에서는 MySQL을 활용합니다.
만약 MySQL 설치가 필요하다면 다음 글을 참고해주세요: Ubuntu에 MySQL 설치하기 (a.k.a. MySQL 초기 설정)
오늘 연습을 위해 활용할 샘플 데이터를 다운받아 주세요.
샘플 데이터는 사용자 고유 ID와 이름, 국적으로 이루어진 아래 데이터를 포함하고 있습니다.
시작
프로젝트를 생성할 때 아래와 같이 SQL 의존성을 포함합니다.
MySQL Driver는 Java Application이 MySQL에 접속할 수 있도록 돕는 드라이버입니다.
MyBatis Framework는 오늘 다루어볼 Framework입니다.
DB 연결 정보 등록
application.properties 파일은 이름 그대로 Application에서 다룰 속성을 정의해두는 파일입니다.
/src/main/resources/application.properties
파일에 아래와 같이 접속 정보를 등록합니다.
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sp_re_ing?serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.username=jeta
spring.datasource.password=jeta
MyBatis 초기화
MyBatis는 App 전체에서 활용할 수 있도록 Application Class에서 초기화 합니다.
아래 코드를 참고해 JavaBean을 생성합니다.
package net.jetalab.spreinglab03;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@SpringBootApplication
public class Spreinglab03Application {
public static void main(String[] args) {
SpringApplication.run(Spreinglab03Application.class, args);
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
Resource[] res = new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*Mapper.xml");
sessionFactory.setMapperLocations(res);
return sessionFactory.getObject();
}
}
Spring Boot 초기화 후 자동으로 생성된 Application Class에 20번째 줄부터 29번째 줄까지 추가되었습니다.
25번째 줄 ~ 26번째 줄: /src/main/resources/mapper/*Mapper.xml
파일들을 불러들이기로 설정합니다.
DTO 생성
예제 데이터와 같은 경우는 이전에 사용했던 Lombok을 이용해 간편하게 정의할 수 있습니다.
다음 경로에 아래 코드를 작성하여 저장합니다: /src/main/java/YOUR/DOMAIN/ARTIFACT/dto/UserDTO.java
package net.jetalab.spreinglab03.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@AllArgsConstructor
@Getter
@Setter
public class UserDTO {
private int seq;
private String name;
private String country;
}
DAO 생성
아래 코드를 자세히 보시면 Class가 아니라 Interface로 선언되어 있습니다.
이 Interface에는 데이터와 관련된 작업을 정의합니다.
굳이 Interface를 사용함으로서 얻을 수 있는 장점은 나중에 따로 글을 작성할 예정입니다.
다음 경로에 아래 코드를 작성하여 저장합니다: /src/main/java/YOUR/DOMAIN/ARTIFACT/dao/UserDAO.java
package net.jetalab.spreinglab03.dao;
import net.jetalab.spreinglab03.dto.UserDTO;
import java.util.List;
public interface UserDAO {
List<UserDTO> selectUsers(UserDTO param) throws Exception;
}
SQL Mapper 생성
SQL Mapper는 실행할 SQL문을 미리 정의해두는 파일입니다.
앞서 MyBatis를 초기화 할 때 정의했던 것처럼 /src/main/resources/mappers/*Mapper.xml
와 같이 저장합니다.
다음 경로에 아래 코드를 작성하여 저장합니다: /src/main/resources/mappers/UserMapper.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="net.jetalab.spreinglab03.dao.UserDAO">
<select id="selectUsers" parameterType="net.jetalab.spreinglab03.dto.UserDTO" resultType="net.jetalab.spreinglab03.dto.UserDTO">
SELECT `seq`, `name`, `country`
FROM lab03
<if test='country != null and country != ""'>
WHERE `country` = #{country}
</if>
</select>
</mapper>
4번째 줄: UserDAO
와 연결합니다.
6번째 줄: UserDAO
인터페이스의 selectUsers()
메소드를 호출하면 실행할 SELECT
문이며, UserDTO
형 객체를 파라미터로 받아, UserDTO
형 객체를 반환합니다.
9번째 줄 ~ 11번째 줄: 만약 파라미터에 country
가 있다면 SQL문에 10번째 줄의 WHERE
절을 추가합니다. 이와 같이 상황에 맞추어 질의문이 변경되는 것을 동적 SQL(Dynamic SQL)이라고 합니다.
Controller 생성
드디어 사용자 요청을 처리하고 응답을 전송하는 컨트롤러를 정의합니다.
다음 경로에 아래 코드를 작성하여 저장합니다: /src/main/java/YOUR/DOMAIN/ARTIFACT/controller/UserController.java
package net.jetalab.spreinglab03.controller;
import net.jetalab.spreinglab03.dao.UserDAO;
import net.jetalab.spreinglab03.dto.UserDTO;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@MapperScan(basePackages = "net.jetalab.spreinglab03.dao")
public class UserController {
@Autowired
private UserDAO userDAO;
@RequestMapping("/users")
public List<UserDTO> users(@RequestParam(value = "country", defaultValue = "") String country) throws Exception {
final UserDTO param = new UserDTO(0, null, country);
final List<UserDTO> userList = userDAO.selectUsers(param);
return userList;
}
}
14번째 줄: Mapper를 탐색할 패키지를 지정합니다.
17번째 줄 ~ 18번째 줄: UserDAO
bean을 자동으로 주입받습니다.
21번째 줄: Query String으로 country
를 받도록 설정합니다.
22번째 줄: 전달받은 country
를 가진 UserDTO
형 객체를 생성합니다. 이 객체는 MyBatis에 파라미터로 전달합니다.
23번째 줄: 22번째 줄에서 생성한 객체를 파라미터로 전달하여 DB로부터 사용자 목록을 불러옵니다.
디렉토리 구조
모든 파일을 생성하면 아래와 같은 구조를 가지게 됩니다.
테스트
서버를 실행하고 다음 API를 호출하면 아래와 같은 응답을 받을 수 있습니다: http://localhost:8080/users
마찬가지로 다음 API를 호출하면 냥국의 냥인만 조회할 수 있습니다: http://localhost:8080/users?country=냥국
예제 코드
본 포스트의 예제 코드는 GitHub에 공개되어 있습니다.
https://github.com/jETA-Kor/sp-re-ing/tree/master/lab03