Mybatis랑 친해지기

2025. 4. 18. 15:33카테고리 없음

1. JPA와 MyBatis의 차이점

저는 취업 준비 중 JPA로 프로젝트를 진행했습니다. JPA는 객체 지향적으로 데이터를 다룰 수 있게 해줘서 매우 직관적이지만, 복잡한 쿼리를 작성할 때는 한계가 있었습니다. 그래서 QueryDSL을 활용해서 JPA의 쿼리문을 좀 더 유연하게 작성했죠.

하지만 실무에서는 대부분의 회사들이 MyBatis를 사용하고 있었습니다. MyBatis는 SQL을 직접 작성하기 때문에 제어가 유연하지만, 그만큼 쉽게 실수할 수 있습니다. 회사에 입사 후, 웹사이트 개편 프로젝트에 참여하면서 MyBatis를 사용하게 되었고, 여러 번의 시행착오를 겪으면서 MyBatis의 특성을 익히기 시작했습니다.

 

2. MyBatis의 기초 - DAO와 XML

MyBatis를 처음 접했을 때, 가장 당황했던 부분은 DAOXML의 역할이었습니다.

  • **DAO (Data Access Object)**는 데이터베이스와의 연결을 관리하며, SQL 쿼리를 실행하는 메서드를 정의하는 역할을 합니다.
  • XML은 실제 SQL 쿼리를 작성하는 곳으로, MyBatis의 핵심입니다. JPA와는 다르게 SQL을 XML에 명시적으로 작성해야 하므로, 실수로 SQL 문법 오류를 범하거나 매핑을 잘못하는 일이 빈번히 발생했습니다.

3. MyBatis의 쿼리 작성과 매핑

MyBatis에서 쿼리문을 작성하는 방법은 XML 매퍼를 사용하여 SQL을 작성하는 방식입니다. 쿼리가 간단할 때는 좋지만, 복잡한 쿼리나 동적 쿼리 작성 시에는 실수를 할 수 있습니다. 예를 들어, SQL 문법이나 매핑 오류는 디버깅을 하는데 시간이 오래 걸리게 만들었죠. JPA에서는 엔티티 객체로 데이터를 관리할 수 있기 때문에, 이런 문제들이 상대적으로 적었는데, MyBatis는 SQL이 명시적으로 존재하므로 더 신경 써야 했습니다.

4. 실수와 학습

실무에서 MyBatis를 사용하며 반복했던 실수들은 대부분 SQL 매핑에서 비롯되었습니다. 예를 들어, 컬럼 이름을 잘못 작성하거나, 파라미터를 제대로 매핑하지 못하는 실수를 했습니다. @Param을 잘못 사용하여 쿼리 파라미터가 매핑되지 않거나, ResultMap을 제대로 설정하지 않아 반환값을 제대로 매핑하지 못하는 경우가 많았습니다.

그 외에도, 쿼리 성능을 고려하지 않고 작성된 쿼리로 인해 성능 저하를 경험한 적도 있었습니다. MyBatis의 경우 SQL을 직접 작성하고 실행하기 때문에, 쿼리 최적화가 중요하다는 것을 실무에서 체감했습니다.

5. MyBatis에서 겪은 실수와 해결 방법

5.1 매핑 실수

MyBatis에서 자주 겪는 실수 중 하나는 매핑 실수입니다. 예를 들어, SQL 쿼리 결과가 예상한 값과 다르게 나오거나, 원하는 필드를 반환하지 않는 경우입니다. 이때는 ResultMap을 이용하여 SQL과 객체 간의 매핑을 정확히 설정하는 것이 중요합니다.

5.2 SQL 오류

SQL 오류는 MyBatis에서 가장 흔히 발생하는 오류 중 하나입니다. SQL 문법이나 파라미터가 잘못 매핑되었을 때 오류가 발생하는데, 이때는 MyBatis 로그를 확인하고, SQL 쿼리문을 실제 DB에서 실행해보는 것이 문제 해결에 도움이 됩니다.

6. 결론

JPA와 MyBatis는 각기 다른 방식으로 데이터베이스와 상호작용하는 방법을 제공합니다. JPA는 객체 지향적이고, 자동화된 방식으로 데이터를 다루며, MyBatis는 SQL을 직접 작성할 수 있어 더 세밀한 제어가 가능합니다. 실무에서는 MyBatis를 많이 사용하고 있기 때문에, 이러한 실수를 겪으면서 점차 익숙해지고 있습니다.

이번 블로그 포스팅을 통해, 저와 같은 실수를 반복하지 않도록 돕는 것이 목표입니다. MyBatis는 처음에는 어렵게 느껴지지만, 반복적인 학습과 실습을 통해 점차 편안하게 다룰 수 있게 됩니다.

 

여기서부터는 Mybatis 작성법을 간략하게 ChatGPT로 추려보았습니다.


1. Controller (컨트롤러) 작성

MyBatis는 SQL을 실행하는 기능을 DAO (Data Access Object)와 XML에서 정의하므로, 컨트롤러는 주로 서비스에서 데이터를 받아오는 역할을 합니다. 컨트롤러는 데이터를 처리하고, 결과를 타임리프를 통해 뷰에 전달합니다.

  • Controller에서 DAO 호출
    • 컨트롤러는 서비스 계층을 통해 DAO를 호출하여 데이터를 처리합니다.
@Controller 
@RequestMapping("/user") 
public class UserController { 

    @Autowired 
    private UserService userService; 
    
    @GetMapping("/list") 
    public String getUserList(Model model) { 
        List<User> users = userService.getAllUsers(); 
        model.addAttribute("users", users); // 타임리프에 데이터를 전달 
        return "user/list"; // 타임리프에서 뷰 이름을 반환 
    } 
}

2. Service (서비스) 작성

서비스 계층에서는 DAO를 호출하여 실제 데이터를 가져오는 작업을 합니다.

 
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;  // MyBatis의 Mapper 인터페이스

    public List<User> getAllUsers() {
        return userMapper.selectAllUsers();  // MyBatis에서 정의된 쿼리 호출
    }
}

3. DAO (Mapper 인터페이스) 작성

MyBatis에서는 SQL 쿼리를 XML 파일에 정의합니다. 하지만 실제로 쿼리 메서드를 호출할 때는 매퍼 인터페이스를 사용합니다. UserMapper와 같은 인터페이스를 정의하고, XML에서 실제 SQL 쿼리문을 작성합니다.

@Mapper
public interface UserMapper {
    List<User> selectAllUsers();  // XML에서 해당 SQL 쿼리 호출
}

4. MyBatis XML 파일 (Mapper XML) 작성

이제 실제 SQL 쿼리를 XML 파일에 작성합니다. 이 파일은 MyBatis의 핵심입니다. UserMapper.xml에서 SQL을 정의하고, 매퍼 인터페이스에서 해당 SQL을 호출합니다.

  • Mapper XML 파일 예시
<mapper namespace="com.example.demo.mapper.UserMapper">
    
    <select id="selectAllUsers" resultType="com.example.demo.model.User">
        SELECT id, name, email FROM users;
    </select>

</mapper>

여기서 중요한 점은 namespace가 인터페이스의 패키지 경로와 일치해야 하고, id 값이 인터페이스에서 정의한 메서드 이름과 같아야 한다는 것입니다. resultType은 반환할 객체의 클래스 타입을 지정합니다.

5. 타임리프 뷰 작성

MyBatis에서 데이터를 가져온 후, 컨트롤러에서 전달된 데이터를 타임리프를 사용하여 화면에 출력합니다. list.html 같은 파일에서 데이터를 출력하는 부분을 작성합니다.

  • 타임리프 뷰 예시 (list.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>사용자 목록</title>
</head>
<body>
    <h1>사용자 목록</h1>
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>이름</th>
                <th>이메일</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="user : ${users}">
                <td th:text="${user.id}"></td>
                <td th:text="${user.name}"></td>
                <td th:text="${user.email}"></td>
            </tr>
        </tbody>
    </table>
</body>
</html>

이제, MyBatis를 통해 UserService가 UserMapper에서 데이터를 가져오고, 컨트롤러에서 Model에 전달한 데이터를 타임리프를 통해 화면에 출력할 수 있습니다.

 


요약 순서

  1. Controller에서 데이터를 요청하고, Model에 데이터를 추가하여 뷰로 전달.
  2. Service에서 UserMapper를 호출하여 데이터를 가져옴.
  3. UserMapper 인터페이스에서 SQL 쿼리 메서드를 정의.
  4. Mapper XML 파일에서 SQL 쿼리를 정의.
  5. 타임리프 뷰에서 데이터를 출력.