프로그래머스에서 제공하는 SQL 고득점 KIT 내 문제들을 풀면서 문득, 내가 이런 쿼리도 몰랐었나? 라는 생각이 들었습니다.
ORM 위주로 사용해왔던 터라, 자동 생성된 SQL 쿼리문으로 처리해왔던 저 스스로 반성을 하게된 계기였습니다. 이로 인해서 다음 프로젝트에서는 쿼리 작성 및 쿼리 튜닝에 중점을 두어 프로젝트를 진행하고 싶다는 생각이 들었습니다.
이번 GGB 프로젝트에서는 MyBatis를 사용하기로 하였습니다. 기존에 JPA 관련 환경 설정만 해봤었기 때문에, MyBatis 세팅 및 동작이 어떻게 되는지에 대한 이해가 많이 부족했습니다.
이번 프로젝트를 진행하면서 MyBatis에 대해서 조금 더 자세하게 학습해볼까 합니다.
우선, 이번 포스팅에서는 MyBatis가 무엇인지와 MyBatis를 직접 프로젝트에 적용해보는 내용을 작성하겠습니다.
한마디로 Java를 위한 오픈 소스 SQL Mapper 프레임워크입니다.
SQL Mapper는 SQL을 실행한 결과를 Java 객체와 매핑할 수 있도록 해주는 도구입니다. MyBatis는 JDBC 코드의 상당 부분과 파라미터 설정 및 결과 매핑을 내부적으로 처리해줍니다.
위에서 언급한대로 MyBatis는 SQL을 직접 작성하면서 Java 객체 코드와 자동으로 매핑해주는 프레임워크. 즉, SQLMapper입니다. ORM은 데이터베이스와 객체지향 프로그래밍 사이의 패러다임 불일치 문제를 해결하기 위해 SQL을 추상화하고 자동화 해줍니다.
위처럼 ORM을 사용하면 패러다임 불일치를 해결하여, 객체 지향적인 프로그래밍이 가능함에도 불구하고 MyBatis를 사용하는 이유는 무엇일까요?
전통적인 ORM 프레임워크는 쿼리를 자동으로 생성해주지만 복잡한 쿼리 작성 시 제한적입니다. 반면에, MyBatis는 SQL 쿼리문을 직접 작성하기 때문에, 복잡한 쿼리 또는 동적 쿼리를 필요로 하는 경우에 유연하게 작성할 수 있습니다.
또한, 이처럼 직접 쿼리문을 작성하는 것이 오히려 성능 측면에서는 좋을 수 있습니다. ORM은 개발자의 의도와 다르게 쿼리가 자동 생성되어 잘못된 설계 또는 사용으로 인해 불필요한 쿼리가 발생하여 성능 저하의 원인이 될 수 있기 때문입니다.
// MyBatis
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
MyBatis의 핵심 모듈인 org.mybatis:mybatis:3.5.14 를 포함하며, Spring Boot에서 MyBatis를 쉽게 사용할 수 있도록 제공하는 라이브러리입니다.
spring:
datasource:
url: jdbc:h2:mem:test
username: {username}
password: {password}
driver-class-name: org.h2.Driver
mybatis:
config-location: classpath:mybatis/sqlmap-config.xml
mapper-locations: mybatis/map/*.xml
type-aliases-package: com.ggb.graduationgoodbye.domain
이외에 추가적인 설정은 Spring Boot MyBatis 공식 문서를 참조 바랍니다.
<?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="cacheEnabled" value="false"/>
<setting name="useGeneratedKeys" value="true"/>
<setting name="defaultExecutorType" value="REUSE"/>
<setting name="jdbcTypeForNull" value="NULL"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
<?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="MemberMapper">
</mapper>
매퍼 파일은 SQL 쿼리와 자바 객체 간의 매핑 정보를 담고 있습니다. 일반적으로 .xml 확장자를 가지며, 클래스패스 상에 위치합니다.
매퍼 파일은 SQL문을 작성하고, 어떻게 파라미터를 넘기고 결과를 받아올지 정의합니다.
namespace는 XML 문서에서 해당 문서가 속하는 범주 또는 그룹을 정의하는 역할을 합니다. mybatis에서는 Mapper 파일과 Mpper 인터페이스 또는 DAO 클래스를 연결해줍니다.
namespace에 지정된 값은 Mapper 인터페이스의 패키지 경로와 이름이 되며, 이를 통해 SQL 쿼리문을 호출하는 코드에서 어떤 mapper 파일의 어떤 SQL 쿼리문을 호출할 것인지 확인할 수 있습니다.
예를 들어, com.myapp.mapper.MyMapper 라는 Mapper 인터페이스가 존재하고, 이 인터페이스에서 findByEmail라는 SQL 쿼리문을 호출하려면 다음과 같이 코드를 작성해주면 됩니다.
@Mapper
public interface MyMapper {
User findByEmail(String email);
}
}
<?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.myapp.mapper.MyMapper">
<select id="findByEmail" parameterType="String" resultType="user">
SELECT * FROM users WHERE email = #{email};
</select>
</mapper>
parameterType 은 MyBatis에서 SQL 쿼리 실행 시 필요한 파라미터의 타입을 명시하는 역할을 합니다. resultType은 SQL 쿼리 실행 후 매핑될 Java 객체의 타입을 의미합니다.
parameterType과 resultType은 alias로 등록된 별칭으로 대체가 됩니다.
예를 들어, User라는 Java 객체에 @Alias("users") 라고 별칭이 지정되어 있으면, 다음과 같이 xml 작성이 가능합니다.
<mapper namespace="com.myapp.mapper.MyMapper" parameterType="String" resultType="users">
<!-- SQL 문-->
</mapper>
위와 같이 MyBatis란 무엇인지, 그리고 MyBatis를 사용하는 방법에 대해서 간단하게 정리해봤습니다. 하지만, 해당 내용만으로는 MyBatis라는 SQLMapper가 어떻게 동작하는지 잘 모르겠습니다. 이를 앞으로 야금야금 깊게 학습해보도록 하겠습니다.
그리고, 현재에는 Mapper 인터페이스를 활용하여 간단하게 구현을 해놨지만, 실제 저희 프로젝트에서는 Redis, MongoDB와 같이 다양한 DB를 연동하여 사용할 때, DIP를 위배하게 될 수도 있게됩니다. 따라서, 현재의 Mapper 인터페이스를 클래스로서 구현한 뒤 SqlSession을 의존 주입하여 사용할 수 있도록 Refactorying할 예정입니다.