2025. 11. 6. 17:53ใJava/Mybatis
๐งฉ Spring MVC + MyBatis ์ค์ต ์ ๋ฆฌ
์ด ๊ธ์ Spring MVC + MyBatis ์ค์ต์ ํ์ผ๋ณ๋ก ๋ฏ์ด๋ณด๋ฉฐ, XML ๋ฌธ๋ฒ ํฌ์ธํธ์ ํ๋ก์ ํธ ์คํ ์ ์ด๊ธฐ ์ธํ , ๊ทธ๋ฆฌ๊ณ MVC ๊ณ์ธต์์ ๋ฐ์ดํฐ๊ฐ ์ค๊ฐ๋ ํ๋ฆ์ ๋จ๊ณ๋ณ๋ก ์ ๋ฆฌํฉ๋๋ค.
โถ ํ๋ก์ ํธ ๊ตฌ์กฐ

โท ์คํ ์ ์ด๊ธฐ ์ธํ (application.properties / mybatis-config / Mapper ์์น)
๐ง application.properties
spring.application.name=SpringBootMVCDBMybatis2
spring.mvc.view.prefix = /WEB-INF/jsp/
spring.mvc.view.suffix = .jsp
server.servlet.session.persistent=false
# DataSource(Connection Pool)
# Spring Boot use Hicari Connection Pool
spring.datasource.url=jdbc:mysql://localhost:3306/madang?useUnicode=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Mybatis
mybatis.config-location=classpath:/config/mybatis-config.xml
logging.level.com.mycom.myapp =debug
๐งฉ mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name ="mapUnderscoreToCamelCase" value="true"/>
</settings>
<mappers>
<mapper resource="mapper/Emp-mapper.xml"/>
<mapper resource="mapper/Emp-mapper-2.xml"/>
</mappers>
</configuration>
mapUnderscoreToCamelCase=true ์ค์ ์ first_name → firstName ์๋ ๋งคํ์ ์ ์ฉํฉ๋๋ค.
mybatis ๊ด๋ จ ์ค์ ๋ค์ ๊ณต์ ํํ์ด์ง์์ ํ์ธํด๋ณด๋ฉด์ ์์ ์ ์งํํ์์ต๋๋ค.
โธ DTO (EmpDto.java)
package com.mycom.myapp.dto;
public class EmpDto {
private int employeeId;
private String firstName; // first_name ๋งคํ
private String lastName; // last_name ๋งคํ
private String email;
private String hireDate; // ํ์
์ LocalDate/LocalDateTime์ผ๋ก ๋ฐ๊ฟ๋ ๋จ
public EmpDto() {}
public EmpDto(int employeeId, String firstName, String lastName, String email, String hireDate) {
this.employeeId = employeeId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.hireDate = hireDate;
}
public int getEmployeeId() { return employeeId; }
public void setEmployeeId(int employeeId) { this.employeeId = employeeId; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getHireDate() { return hireDate; }
public void setHireDate(String hireDate) { this.hireDate = hireDate; }
}
โ
JSON ํค๋ getter ์ด๋ฆ ๊ธฐ์ค์ผ๋ก ์ง๋ ฌํ๋ฉ๋๋ค. getEmployeeId()๊ฐ employeeId ํค๋ฅผ ๋ง๋ญ๋๋ค.
โน DAO (EmpDao.java)
package com.mycom.myapp.dao;
import java.util.List;
import java.util.Map;
import com.mycom.myapp.dto.EmpDto;
public interface EmpDao {
List<EmpDto> listEmp();
EmpDto detailEmp(int employeeId);
int insertEmp(EmpDto empDto);
int updateEmp(EmpDto empDto);
int deleteEmp(int employeeId);
// ๊ฒ์/์ค์ต
List<EmpDto> listEmpLike(String searchWord);
List<EmpDto> listEmpMap();
List<EmpDto> listEmpWhereIf(Map<String,String> map);
}
โบ Mapper XML โ (Emp-mapper.xml) — resultType + alias
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mycom.myapp.dao.EmpDao">
<select id="listEmp" resultType="com.mycom.myapp.dto.EmpDto">
SELECT
employeeId,
first_name AS firstName,
last_name AS lastName,
email,
hire_date AS hireDate
FROM emp
</select>
<select id="detailEmp" resultType="com.mycom.myapp.dto.EmpDto">
SELECT
employeeId,
first_name AS firstName,
last_name AS lastName,
email,
hire_date AS hireDate
FROM emp
WHERE employeeId = #{employeeId}
</select>
<insert id="insertEmp" parameterType="com.mycom.myapp.dto.EmpDto">
INSERT INTO emp (employeeId, first_name, last_name, email, hire_date)
VALUES (#{employeeId}, #{firstName}, #{lastName}, #{email}, #{hireDate})
</insert>
<update id="updateEmp" parameterType="com.mycom.myapp.dto.EmpDto">
UPDATE emp
SET first_name = #{firstName},
last_name = #{lastName},
email = #{email},
hire_date = #{hireDate}
WHERE employeeId = #{employeeId}
</update>
<delete id="deleteEmp" parameterType="int">
DELETE FROM emp
WHERE employeeId = #{employeeId}
</delete>
</mapper>
โ
๋ฌธ๋ฒ ํฌ์ธํธ
- resultType : SELECT ๊ฒฐ๊ณผ๋ฅผ DTO๋ก ๋ณํ(ํ๋กํผํฐ๋ช
๊ณผ ์ปฌ๋ผ/alias ๋งค์นญ)
- parameterType : ์ ๋ฌ ํ๋ผ๋ฏธํฐ ํ์
์ง์ (์๋ต ๊ฐ๋ฅํ๋ ๋ช
์๊ฐ ๊ฐ๋
์ฑ↑)
- ์ธ๋ฏธ์ฝ๋ก (;)์ ๋ณดํต XML ๋ด๋ถ์ ์ฐ์ง ์์ต๋๋ค.
โป Mapper XML โก (Emp-mapper-2.xml) — <where> / <if> / resultMap
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mycom.myapp.dao.EmpDao">
<!-- resultMap : ์ปฌ๋ผ๋ช
์ด ์ ๋งคํ๊ฑฐ๋ ์กฐ์ธ/๋ณตํฉ ๋งคํ์ ์ ์ฉ -->
<resultMap id="empMap" type="com.mycom.myapp.dto.EmpDto">
<id property="employeeId" column="employeeId"/>
<result property="firstName" column="first_name"/>
<result property="lastName" column="last_name"/>
<result property="email" column="email"/>
<result property="hireDate" column="hire_date"/>
</resultMap>
<select id="listEmpMap" resultMap="empMap">
SELECT employeeId, first_name, last_name, email, hire_date
FROM emp
</select>
<!-- where/if : ์กฐ๊ฑดํ๋ผ๋ฏธํฐ๊ฐ ์์ ๋๋ง WHERE ์ ์ ๊ตฌ์ฑ -->
<select id="listEmpWhereIf" parameterType="map" resultType="com.mycom.myapp.dto.EmpDto">
SELECT employeeId,
first_name AS firstName,
last_name AS lastName,
email,
hire_date AS hireDate
FROM emp
<where>
<if test="firstName != null">
first_name = #{firstName}
</if>
<if test="lastName != null">
AND last_name = #{lastName}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</select>
</mapper>
โ
๋ฌธ๋ฒ ํฌ์ธํธ
- <where>๋ ๋ด๋ถ ์กฐ๊ฑด์ด ํ๋๋ผ๋ ์ฐธ์ด๋ฉด ์๋์ผ๋ก WHERE๋ฅผ ๋ถ์ด๊ณ , ์ฒซ ์กฐ๊ฑด ์์ AND๋ ์๋ต๊ฐ๋ฅํ๋ค.
- resultMap vs resultType : ๊ฐ๋จ ๋งคํ์ resultType + alias, ๋ณต์ก ๋งคํ/์กฐ์ธ/์ปฌ๋ผ๋ช
์ด ์ ๋งคํ๋ฉด resultMap.
โผ Service ๊ณ์ธต
EmpService.java
package com.mycom.myapp.service;
import java.util.List;
import java.util.Map;
import com.mycom.myapp.dto.EmpDto;
public interface EmpService {
List<EmpDto> listEmp();
EmpDto detailEmp(int employeeId);
int insertEmp(EmpDto empDto);
int updateEmp(EmpDto empDto);
int deleteEmp(int employeeId);
List<EmpDto> listEmpLike(String searchWord);
List<EmpDto> listEmpMap();
List<EmpDto> listEmpWhereIf(Map<String,String> map);
}
EmpServiceImpl.java
@Service
public class EmpServiceImpl implements EmpService {
private final EmpDao empDao;
public EmpServiceImpl(EmpDao empDao) {
this.empDao = empDao;
}
@Override public List<EmpDto> listEmp() { return empDao.listEmp(); }
@Override public EmpDto detailEmp(int employeeId) { return empDao.detailEmp(employeeId); }
@Override public int insertEmp(EmpDto empDto) { return empDao.insertEmp(empDto); }
@Override public int updateEmp(EmpDto empDto) { return empDao.updateEmp(empDto); }
@Override public int deleteEmp(int employeeId) { return empDao.deleteEmp(employeeId); }
@Override public List<EmpDto> listEmpLike(String searchWord) { return empDao.listEmpLike(searchWord); }
@Override public List<EmpDto> listEmpMap() { return empDao.listEmpMap(); }
@Override public List<EmpDto> listEmpWhereIf(Map<String,String> map) { return empDao.listEmpWhereIf(map); }
}
โ ์๋น์ค๋ ๋น์ฆ๋์ค ๋ก์ง์ ๋ด๋นํฉ๋๋ค. ์ง๊ธ์ ๋จ์ ์์์ด์ง๋ง, ์ถํ ํธ๋์ญ์ /๊ฒ์ฆ/์กฐํฉ ๋ก์ง์ ์ฌ๊ธฐ์ ๋ฃ์ต๋๋ค.
โฝ Controller ๊ณ์ธต
EmpController.java (๋ฐ์ดํฐ JSON ์๋ต)
@RestController
@RequestMapping("/emps")
public class EmpController {
private final EmpService empService;
public EmpController(EmpService empService) {
this.empService = empService;
}
@GetMapping("/list")
public List<EmpDto> listEmp(){ return empService.listEmp(); }
@GetMapping("/detail/{employeeId}")
public EmpDto detailEmp(@PathVariable Integer employeeId){
return empService.detailEmp(employeeId);
}
@GetMapping("/listEmpWhereIf")
public List<EmpDto> listEmpWhereIf(@RequestParam Map<String,String> map){
return empService.listEmpWhereIf(map);
}
}
PageController.java (๋ทฐ ๋ผ์ฐํ )
@Controller
public class PageController {
@GetMapping("/emps") public String emps(){ return "emps"; }
@GetMapping("/salaries") public String salaries(){ return "salaries"; }
@GetMapping("/stores") public String stores(){ return "stores"; }
}

โ
๋ฐ์ดํฐ๋ @RestController(ํน์ @Controller + @ResponseBody)์์ JSON์ผ๋ก ๋ณด๋ด๊ณ , JSP ํ๋ฉด ์ด๋์ ๋ณ๋ PageController๊ฐ ๋ด๋นํ๋ฉด ์ญํ ์ด ๋ถ๋ฆฌ๋์ด ๊น๋ํฉ๋๋ค.
โพ View (emps.jsp) + fetch Ajax
<h1>์ฌ์ ๊ด๋ฆฌ</h1>
<table id="empTable" border="1">
<thead><tr>
<th>employeeId</th><th>firstName</th><th>lastName</th><th>email</th><th>hireDate</th>
</tr></thead>
<tbody id="empTbody"></tbody>
</table>
<script>
window.onload = () => listEmp();
async function listEmp(){
const res = await fetch('/emps/list');
const data = await res.json();
render(data);
}
function render(list){
const rows = list.map(emp => `
<tr data-employeeid="${emp.employeeId}" style="cursor:pointer">
<td>${emp.employeeId}</td>
<td>${emp.firstName}</td>
<td>${emp.lastName}</td>
<td>${emp.email}</td>
<td>${emp.hireDate}</td>
</tr>`).join('');
document.querySelector('#empTbody').innerHTML = rows;
document.querySelectorAll('#empTbody tr').forEach(tr => {
tr.onclick = () => detailEmp(tr.getAttribute('data-employeeid'));
});
}
async function detailEmp(employeeId){
const res = await fetch('/emps/detail/' + employeeId);
console.log(await res.json());
}
</script>
โ
JSP ์ ๋ฐฑํฑ ํ
ํ๋ฆฟ์์ ${...} EL ์ถฉ๋์ ํผํ๋ ค๋ฉด ์คํฌ๋ฆฝํ๋ฆฟ/EL์ ์ฌ์ฉํ์ง ์๋ ์์ JS๋ก ๊ตฌ์ฑํ๋ ๊ฒ ์์ ํฉ๋๋ค.

โฟ ๋ฐ์ดํฐ ํ๋ฆ ์ดํด (์์ฒญ → ์๋ต)
[๋ธ๋ผ์ฐ์ emps.jsp] --fetch--> [EmpController /emps/list]
→ EmpService.listEmp()
→ EmpDao.listEmp()
→ Emp-mapper.xml (select id="listEmp")
→ DB(emp ํ
์ด๋ธ) ์กฐํ
← List<EmpDto>
← List<EmpDto>
← JSON (List<EmpDto>)
[๋ธ๋ผ์ฐ์ ] ํ
์ด๋ธ ๋ ๋๋ง
ํต์ฌ์ Controller URL๊ณผ Mapper id, ๊ทธ๋ฆฌ๊ณ Service/Dao ์ธํฐํ์ด์ค ์๊ทธ๋์ฒ๊ฐ ์ ํํ ๋ง๋ฌผ๋ฆฌ๋๊ฐ์ ๋๋ค.
๋ง๋ฌด๋ฆฌ
์ด๋ฒ ์ค์ต์ ํต์ฌ์ MVC ๊ณ์ธต์ ์์์ ์ฑ ์์ ์ ํํ ์ดํดํ๊ณ , DTO ์ด๋ฆ/Getter, Mapper XML์ id/alias, Controller URL์ด ์ ๊ธฐ์ ์ผ๋ก ๋ง๋ฌผ๋ฆฌ๊ฒ ํ๋ ๊ฒ์ ๋๋ค. ์ด ๊ธ์ ์์(DB → DTO → Mapper(XML) → DAO → Service → Controller → JSP)๋ฅผ ์ต๊ดํํด๋๋ฉด, ์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋๋ ํ๋ค๋ฆผ ์์ด ์งํํ ์ ์์ต๋๋ค.