오늘 만들 것
많은 API는 DB로부터 데이터를 불러오거나 사용자로부터 전달받은 데이터를 DB에 저장하는 작업을 수행합니다.
오늘은 MyBatis를 이용해 DB에 접속하고, DB로부터 간단한 값을 불러오는 기능을 구현합니다.
만약 이미 MyBatis를 접해보셨거나 사용법을 알고 계신다면 굳이 이번 글은 자세히 보지 않으셔도 됩니다.
사전 설정
practice계정에 members 테이블을 생성해서 데이터 삽입
CREATE TABLE "PRACTICE"."MEMBERS"(
"SEQ" NUMBER(*,0) NOT NULL ENABLE,
"NAME" VARCHAR2(255 BYTE) NOT NULL ENABLE,
"COUNTRY" VARCHAR2(255 BYTE) NOT NULL ENABLE,
CONSTRAINT "MEMBERS_PK" PRIMARY KEY ("SEQ"));
INSERT INTO PRACTICE.MEMBERS (SEQ,NAME,COUNTRY) VALUES (1,'김춘배','냥국');
INSERT INTO PRACTICE.MEMBERS (SEQ,NAME,COUNTRY) VALUES (2,'한보미','한국');
INSERT INTO PRACTICE.MEMBERS (SEQ,NAME,COUNTRY) VALUES (3,'나남훈','멍국');
INSERT INTO PRACTICE.MEMBERS (SEQ,NAME,COUNTRY) VALUES (4,'오지나','한국');
INSERT INTO PRACTICE.MEMBERS (SEQ,NAME,COUNTRY) VALUES (5,'반휘혈','냥국');
INSERT INTO PRACTICE.MEMBERS (SEQ,NAME,COUNTRY) VALUES (6,'최민수','냥국');
INSERT INTO PRACTICE.MEMBERS (SEQ,NAME,COUNTRY) VALUES (7,'강이지','멍국');
MyBatis는 SQL을 처리할 때 JDBC의 PreparedStatement를 이용한다.
만약 쿼리문이 복잡한 경우 ? 로 나오는 값이 제대로 되었는지 확인하기가 쉽지 않고 실행된 SQL문의 내용을 정확히 확인하기가 어렵다.
log4jdbc-log4j2 라이브러리는 PreparedStatement에 사용된 ? 가 어떤 값으로 처리되었는지 확인할 수 있다.
그러나 이것은 필수적인건 아니고 옵션이므로 선택해서 적용하면 될거 같다.
라이브러리 추가
Gradle 사용시 (build.gradle)
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
라이브러리를 추가한 후
resources/logback.xml 파일 생성
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- Appenders -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d %5p [%c] %m%n</Pattern>
</encoder>
</appender>
<appender name="console-infolog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d %5p %m%n</Pattern>
</encoder>
</appender>
<!-- Logger -->
<logger name="jdbc.sqlonly" level="INFO" appender-ref="console-infolog" />
<logger name="jdbc.resultsettable" level="INFO" appender-ref="console-infolog" />
<!-- Root Logger -->
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>
DB 연결 정보 등록
application.properties 파일은 이름 그대로 Application에서 다룰 속성을 정의해두는 파일입니다.
/src/main/resources/application.yml
spring:
datasource:
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:oracle:thin:@localhost:1521/xe
username: practice
password: practice
mybatis:
type-aliases-package: com.example.jackboard.*
mapper-locations: classpath:mappers/*.xml
DTO 생성
예제 데이터와 같은 경우는 이전에 사용했던 Lombok을 이용해 간편하게 정의할 수 있습니다.
다음 경로에 아래 코드를 작성하여 저장합니다:
/src/main/java/YOUR/DOMAIN/ARTIFACT/dto/UserDTO.java
package com.example.jackboard.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 com.example.jackboard.dao;
import com.example.jackboard.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/*.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="com.example.jackboard.dao.UserDAO">
<select id="selectUsers" parameterType="com.example.jackboard.dto.UserDTO" resultType="com.example.jackboard.dto.UserDTO">
SELECT seq, name, country
FROM members
<if test='country != null and country != ""'>
WHERE country = #{country}
</if>
</select>
</mapper>
Controller 생성
드디어 사용자 요청을 처리하고 응답을 전송하는 컨트롤러를 정의합니다.
다음 경로에 아래 코드를 작성하여 저장합니다:
/src/main/java/YOUR/DOMAIN/ARTIFACT/controller/UserController.java
package com.example.jackboard.controller;
import com.example.jackboard.dao.UserDAO;
import com.example.jackboard.dto.UserDTO;
import lombok.RequiredArgsConstructor;
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 = "com.example.jackboard.dao")
@RequiredArgsConstructor
public class UserController {
private final 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;
}
}
테스트
서버를 실행하고 다음 API를 호출하면 아래와 같은 응답을 받을 수 있습니다:
http://localhost:8080/users
마찬가지로 다음 API를 호출하면 냥국의 냥인만 조회할 수 있습니다:
http://localhost:8080/users?country=냥국