[LG U+ 유레카 3기]Spring Boot + JDBC -> MyBatis 전환 실습
2025. 11. 6. 12:10ㆍJava/Spring
❶ 상황 설명
이번 실습에서는 기존 JDBC 기반 DAO 구현(Impl) 프로젝트를 MyBatis로 전환했다. 핵심은
DAO 구현체(BookDaoImpl) 제거 후 @Mapper 인터페이스 + XML 매퍼로 대체하고,
View는 webapp 하위의 index.html과 webapp/WEB-INF/jsp 하위의 jsp/books.jsp를 사용했다.
프로젝트 생성할땐 Spring Strarter Project 에서 MyBatis Framework을 추가해야한다.
기존의 저는 JDBC API 추가된 실습이였는데 그걸 MyBatis로 바꾸는 실습이기에
JDBC API 해제 해주고 MyBatis Framework을 추가했습니다.

❷ 디렉터리 구조

여기서 중요한점은 config와 mapper 폴더가 생기고 xml 파일들을 하나씩 추가해줘야한다!
✅ book-mapper.xml (MyBatis 매퍼)
<?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.BookDao">
<select id="listBook" resultType="com.mycom.myapp.dto.BookDto">
select bookid bookId,bookname bookName ,publisher,price from book;
</select>
<select id="detailBook" resultType="com.mycom.myapp.dto.BookDto">
select bookid bookId,bookname bookName ,publisher,price
from book
where bookid = #{bookId};
</select>
<insert id = "insertBook" parameterType = "com.mycom.myapp.dto.BookDto">
insert into book ( bookid, bookname, publisher, price)
values ( #{bookId} , #{bookName},#{publisher},#{price});
</insert>
<update id ="updateBook" parameterType = "com.mycom.myapp.dto.BookDto">
update book
set bookname = #{bookName}
,publisher = #{publisher}
,price = #{price}
where bookid = #{bookId};
</update>
<delete id ="delteBooke" parameterType = "int">
delete form book
where bookid = #{bookId};
</delete>
</mapper>
✅ 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>
<mappers>
<mapper resource="mapper/book-mapper.xml"/>
</mappers>
</configuration>
❸ 실행 흐름 (요청 → 응답)
- 사용자가
http://localhost:8080/접속 →webapp/WEB-INF/index.html노출 - 메뉴에서
/books클릭 → Controller 매핑으로WEB-INF/jsp/books.jsp렌더 books.jsp의 JavaScript가 Ajax로 API 호출GET /books/list→ 목록GET /books/detail/{bookId}→ 상세POST /books/insert→ 등록POST /books/update→ 수정GET /books/delete/{bookId}→ 삭제
- Controller → Service → BookDao(@Mapper) →
book-mapper.xmlSQL 실행 → DB
❹ 핵심 코드
✅ index.html (메뉴)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>안녕하세요</h2>
<a href="/books">도서 관리</a>
<a href="#">회원 관리</a>
<a href="#">상품 관리</a>
</body>
</html>
✅ books.jsp (Ajax 기반 CRUD 화면)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>도서 관리</title>
</head>
<body>
<h1>도서 관리</h1>
<table>
<thead>
<tr><th>bookId</th><th>bookName</th><th>publisher</th><th>price</th></tr>
</thead>
<tbody id="bookTbody"></tbody>
</table>
<hr>
<form>
<input type="text" name="bookId" id="bookId"></input><br>
<input type="text" name="bookName" id="bookName"></input><br>
<input type="text" name="publisher" id="publisher"></input><br>
<input type="text" name="price" id="price"></input><br>
</form>
<hr>
<button id="btnInsert">등록</button> <button id="btnUpdate">수정</button> <button id="btnDelete">삭제</button> <button id="btnClear">초기화</button>
<script>
window.onload = function(){
listBook();
document.querySelector("#btnClear").onclick = clearForm;
document.querySelector("#btnInsert").onclick = insertBook;
document.querySelector("#btnUpdate").onclick = updateBook;
document.querySelector("#btnDelete").onclick = deleteBook;
}
async function listBook(){
let url = '/books/list';
let response = await fetch(url);
let data = await response.json();
makeListHtml(data);
}
function makeListHtml(list){
let listHtml = ``;
list.forEach(book => {
listHtml +=
`<tr style="cursor:pointer" data-bookId=\${book.bookId}>
<td>\${book.bookId}</td>
<td>\${book.bookName}</td>
<td>\${book.publisher}</td>
<td>\${book.price}</td>
</tr>`;
});
document.querySelector("#bookTbody").innerHTML = listHtml;
document.querySelectorAll("#bookTbody tr").forEach(tr => {
tr.onclick = function(){
let bookId = this.getAttribute("data-bookId");
detailBook(bookId);
}
});
}
async function detailBook(bookId){
let url = '/books/detail/' + bookId;
let response = await fetch(url);
let data = await response.json();
document.querySelector("#bookId").value = data.bookId;
document.querySelector("#bookName").value = data.bookName;
document.querySelector("#publisher").value = data.publisher;
document.querySelector("#price").value = data.price;
}
function clearForm(){
document.querySelector("#bookId").value = "";
document.querySelector("#bookName").value = "";
document.querySelector("#publisher").value = "";
document.querySelector("#price").value = "";
}
async function insertBook(){
let book = {
bookId: document.querySelector("#bookId").value,
bookName: document.querySelector("#bookName").value,
publisher: document.querySelector("#publisher").value,
price: document.querySelector("#price").value
};
let urlParams = new URLSearchParams(book);
let response = await fetch('/books/insert', { method: "post", body: urlParams });
let data = await response.json();
if (data.result == "success"){ alert("도서 등록 성공!"); listBook(); clearForm(); }
else { alert("도서 등록 실패!"); }
}
async function updateBook(){
let urlParams = new URLSearchParams({
bookId: document.querySelector("#bookId").value,
bookName: document.querySelector("#bookName").value,
publisher: document.querySelector("#publisher").value,
price: document.querySelector("#price").value
});
let response = await fetch('/books/update', { method: "post", body: urlParams });
let data = await response.json();
if (data.result == "success"){ alert("도서 수정 성공!"); listBook(); clearForm(); }
else { alert("도서 수정 실패!"); }
}
async function deleteBook(){
let bookId = document.querySelector("#bookId").value;
let response = await fetch('/books/delete/' + bookId);
let data = await response.json();
if (data.result == "success"){ alert("도서 삭제 성공!"); listBook(); clearForm(); }
else { alert("도서 삭제 실패!"); }
}
</script>
</body>
</html>
✅ pom.xml (의존성)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mycom</groupId>
<artifactId>SpringBootMVCDBMybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBootMVCDBMybatis</name>
<description>SpringBootMVCDBMybatis</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.5</version>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-jdbc</artifactId>-->
<!-- </dependency>-->
<!-- jsp -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
✅ application.properties

# Mybatis
mybatis.config-location=classpath:/config/mybatis-config.xml
이 코드를 무조건 추가하고 알맞는 경로에 mybatis-cofing.xml 파일을 추가해야한다.
❺ 왜 이 실습을 하는가 (학습 관점의 깨달음)
- “구현 세부를 빼고 계약에 집중” — JDBC의 보일러플레이트(연결/해제/예외)를 걷어내고, 인터페이스 시그니처 = 시스템 계약에만 집중한다. 유지보수·테스트가 쉬워진다.
- SQL을 코드에서 분리 — SQL 변경은 XML만 바꾸면 된다. 릴리즈 리스크가 줄고 협업이 쉬워진다(백엔드/DBA 역할 분리).
- 레이어 책임 분리 감각 — Controller/Service/DAO가 왜 나뉘는지 “경계의 의미”를 손으로 체득한다. 면접에서 설계 질문에 강해진다.
- 웹 리소스 동작 원리 —
webapp,WEB-INF,resources/static의 탐색 우선순위를 경험으로 익혀 배포 구조 문제를 스스로 해결할 수 있다. - JPA로의 다리 — MyBatis의 바인딩·트랜잭션·레이어링을 이해하면 JPA/Hibernate의 추상화도 자연스럽게 흡수된다.
❻ 반드시 알아야 하는 핵심
- namespace = 인터페이스 FQN, id = 메서드명 — 이 계약이 깨지면 바로 매핑 실패다.
#{}사용이 기본 —${}는 문자열 치환이라 인젝션 위험. 정렬 키/컬럼명 동적 치환 등 꼭 필요할 때만 엄격히 사용.- 파일 경로 감각 — 정적은
webapp또는resources/static, JSP는WEB-INF/jsp. 404/Whitelabel이 나면 우선 경로·우선순위를 의심.
'Java > Spring' 카테고리의 다른 글
| [LG U+ 유레카 3기] Spring MVC + CORS 실습 (0) | 2025.11.25 |
|---|---|
| [LG U+ 유레카 3기] Spring MVC + JSP + MyBatis 프로젝트 정리 (0) | 2025.11.17 |
| [LG U+ 유레카 3기] Spring Boot MVC 도서 관리 시스템 실습 (0) | 2025.11.05 |
| [LG U+ 유레카3기]Spring MVC | HttpSession 로그인 → 유지 → 로그아웃 실습 정리 (0) | 2025.11.05 |
| [LG U+ 유레카 3기]Spring MVC | 요청 바인딩 + View/Model/Redirect 실습 정리 (0) | 2025.11.05 |