반응형
스프링, 스프링부트에선 DB에 어떻게 접근하는지 알아보자
스프링 프레임워크와 스프링 부트는 데이터베이스 접근을 위해 매우 유연하고 효율적인 방법을 제공한다. 이번 포스트에서는 스프링에서 데이터베이스에 접근하는 다양한 방법을 탐구해 본다. 우선, 데이터 접근을 위한 기본 구조인 DAO(Data Access Object)에 대해 알아보고, ORM(Object-Relational Mapping)을 위한 Mapper의 사용과 그 특징을 살펴볼 것이다. 또한, MyBatis와 같은 SQL 매핑 도구를 사용할 때 유용한 @Mapper 어노테이션의 중요성과 사용시 주의점에 대해서도 다룰 예정이다. 이를 통해 스프링을 사용한 데이터베이스 접근 방식의 깊이와 유연성을 이해할 수 있을 것이다.
1. DAO란 무엇인가?
1-1. DAO(Data Access Object)
- DAO는 데이터베이스 접근을 단순화하고 캡슐화하는 객체로, CRUD(create, read, update, delete) 작업을 수행하는 메서드를 제공한다. 이는 데이터베이스와의 상호작용을 추상화하여 데이터 접근 로직을 나머지 애플리케이션으로부터 분리한다. 이로 인해 애플리케이션의 가독성과 유지 보수성이 향상되며, 데이터 접근 로직 변경 시 DAO 계층에만 영향을 미친다.
1-2. DAO의 주요 특징
- 추상화
- DAO는 데이터베이스에 액세스하는 방식에 대한 상세한 정보를 캡슐화함으로써 애플리케이션 나머지 부분에서 데이터 접근 로직을 분리해준다.
- DAO는 데이터베이스에 액세스하는 방식에 대한 상세한 정보를 캡슐화함으로써 애플리케이션 나머지 부분에서 데이터 접근 로직을 분리해준다.
- 일관성
- 동일한 DAO 인터페이스를 통해 여러 데이터베이스 구현에 대해 동일한 방식으로 액세스할 수 있다. 이는 데이터베이스를 변경하거나 여러 데이터베이스를 지원해야 하는 상황에서 유용하다.
- 동일한 DAO 인터페이스를 통해 여러 데이터베이스 구현에 대해 동일한 방식으로 액세스할 수 있다. 이는 데이터베이스를 변경하거나 여러 데이터베이스를 지원해야 하는 상황에서 유용하다.
1-3. DAO를 사용하는 것의 장점
- 분리의 원칙
- DAO는 데이터베이스에 대한 접근을 하나의 잘 정의된 API로 캡슐화한다. 이는 비즈니스 로직과 데이터베이스 액세스 로직을 분리하여 코드의 가독성과 유지 보수성을 향상시킨다.
- DAO는 데이터베이스에 대한 접근을 하나의 잘 정의된 API로 캡슐화한다. 이는 비즈니스 로직과 데이터베이스 액세스 로직을 분리하여 코드의 가독성과 유지 보수성을 향상시킨다.
- 유연성
- DAO 인터페이스를 사용하면 특정 데이터베이스 구현에 대한 의존성을 최소화할 수 있다. 이는 나중에 데이터베이스 기술이 변경되더라도 비즈니스 로직에 큰 영향을 미치지 않게 해준다.
- DAO 인터페이스를 사용하면 특정 데이터베이스 구현에 대한 의존성을 최소화할 수 있다. 이는 나중에 데이터베이스 기술이 변경되더라도 비즈니스 로직에 큰 영향을 미치지 않게 해준다.
1-4. DAO를 사용하는 것의 단점
- 추가적인 코드
- DAO를 사용하려면 각각의 데이터베이스 작업에 대해 DAO 인터페이스와 구현 클래스를 작성해야 한다. 이는 추가적인 작업과 코드 복잡성을 초래할 수 있다.
- DAO를 사용하려면 각각의 데이터베이스 작업에 대해 DAO 인터페이스와 구현 클래스를 작성해야 한다. 이는 추가적인 작업과 코드 복잡성을 초래할 수 있다.
- 성능 고려사항
- 각 데이터베이스 작업에 대해 별도의 DAO 메서드를 호출하면, 이는 여러 데이터베이스 연결과 트랜잭션을 필요로 할 수 있다. 이로 인해 성능이 저하될 수 있으므로, 이러한 사항을 고려하여 DAO를 설계해야 한다.
- 각 데이터베이스 작업에 대해 별도의 DAO 메서드를 호출하면, 이는 여러 데이터베이스 연결과 트랜잭션을 필요로 할 수 있다. 이로 인해 성능이 저하될 수 있으므로, 이러한 사항을 고려하여 DAO를 설계해야 한다.
1-5. DAO의 코드 작성예시
- EmployeeDAO 인터페이스 선언
// DAO Interface
public interface EmployeeDAO {
List<Employee> getAllEmployees();
Employee getEmployeeById(int employeeId);
void addEmployee(Employee employee);
void updateEmployee(Employee employee);
void deleteEmployee(int employeeId);
}
- EmployeeDAOImpl 구현체 선언
// DAO Implementation
public class EmployeeDAOImpl implements EmployeeDAO {
private JdbcTemplate jdbcTemplate;
public EmployeeDAOImpl(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public List<Employee> getAllEmployees() {
String sql = "SELECT * FROM employees";
List<Employee> employees = jdbcTemplate.query(sql, new EmployeeRowMapper());
return employees;
}
// ... remaining methods implemented
}
2. Mapper란 무엇인가?
2-1. Mapper란?
- Mapper는 ORM(Object-Relational Mapping) 도구나 SQL 매핑 도구에서 사용되는 중요한 개념이다. 데이터베이스와 객체 지향 프로그래밍 언어 간의 데이터를 "매핑"하는 역할을 수행한다. 예를 들어, MyBatis는 SQL 쿼리와 그 결과를 객체에 매핑하기 위해 Mapper를 사용한다.
2-2. Mapper의 주요 특징
- SQL 매핑
- Mapper는 SQL 쿼리와 그 결과를 객체로 매핑하는 역할을 한다.
- SQL 쿼리는 Mapper 내에 XML이나 어노테이션을 사용해 정의된다.
- 데이터베이스 특화
- 특정 SQL 쿼리를 작성할 수 있기 때문에 데이터베이스의 특성을 최대한 활용할 수 있다.
- 복잡한 쿼리나 데이터베이스 특화 기능을 사용해야 하는 경우 Mapper를 사용하면 유용하다.
2-3. Mapper의 장점
- SQL 커스터마이징
- SQL 쿼리를 직접 작성하므로, SQL 최적화 및 특화 작업이 가능하다. 복잡한 쿼리나 특정 DB의 기능을 최대한 활용해야 할 때 유용하다.
- SQL 쿼리를 직접 작성하므로, SQL 최적화 및 특화 작업이 가능하다. 복잡한 쿼리나 특정 DB의 기능을 최대한 활용해야 할 때 유용하다.
- 성능 향상
- SQL 쿼리를 직접 작성하고 최적화할 수 있으므로, 특정 상황에서 성능 향상을 가져올 수 있다.
- SQL 쿼리를 직접 작성하고 최적화할 수 있으므로, 특정 상황에서 성능 향상을 가져올 수 있다.
2-4. Mapper의 단점
- SQL 쿼리 작성 필요
- SQL 쿼리를 직접 작성해야 하므로, SQL에 대한 지식이 필요하고, 때로는 복잡한 쿼리를 작성하는데 많은 시간이 필요할 수 있다.
- SQL 쿼리를 직접 작성해야 하므로, SQL에 대한 지식이 필요하고, 때로는 복잡한 쿼리를 작성하는데 많은 시간이 필요할 수 있다.
- 유지 관리 어려움
- SQL 쿼리가 코드와 분리되어 있거나 (XML 파일에 있을 경우) 코드 내에 임베딩되어 있을 경우, 유지 관리가 어려울 수 있다. SQL과 코드가 밀접하게 연결되어 있기 때문에, SQL 변경 시 코드도 함께 변경해야 할 수 있다.
- SQL 쿼리가 코드와 분리되어 있거나 (XML 파일에 있을 경우) 코드 내에 임베딩되어 있을 경우, 유지 관리가 어려울 수 있다. SQL과 코드가 밀접하게 연결되어 있기 때문에, SQL 변경 시 코드도 함께 변경해야 할 수 있다.
- 데이터베이스 이식성 감소
- SQL 쿼리는 데이터베이스에 따라 다르게 동작할 수 있다. 따라서, 매퍼를 사용하면 데이터베이스 간에 코드를 이식하기가 더 어려울 수 있다.
- SQL 쿼리는 데이터베이스에 따라 다르게 동작할 수 있다. 따라서, 매퍼를 사용하면 데이터베이스 간에 코드를 이식하기가 더 어려울 수 있다.
2-5. Mapper 작성예시1
- MyBatis는 SQL 매핑 프레임워크로, SQL 쿼리와 그 결과를 객체로 매핑한다. SQL 쿼리는 XML 파일이나 어노테이션을 사용해 정의된다.
1. Mapper XML코드 작성예시
<mapper namespace="com.example.EmployeeMapper">
<select id="getAllEmployees" resultType="com.example.Employee">
SELECT * FROM employees
</select>
</mapper>
2. Mapper Interface 클래스 작성예시
public interface EmployeeMapper {
List<Employee> getAllEmployees();
}
3. @Mapper Annotation이용한 클래스 작성예시
- Mybatis에서는 @Mapper 어노테이션을 이용하여 Mapper 인터페이스를 사용할 수 있다. 매핑 인터페이스가 자동으로 구현되므로, 개발자는 SQL 쿼리를 직접 작성하고 결과를 객체로 매핑하는 복잡한 작업을 신경 쓸 필요가 없다.
@Mapper
public interface EmployeeMapper {
@Select("SELECT * FROM employees")
List<Employee> getAllEmployees();
@Insert("INSERT INTO employees(name, position, salary) VALUES(#{name}, #{position}, #{salary})")
void insertEmployee(Employee employee);
@Update("UPDATE employees SET name = #{name}, position = #{position}, salary = #{salary} WHERE id = #{id}")
void updateEmployee(Employee employee);
@Delete("DELETE FROM employees WHERE id = #{id}")
void deleteEmployee(int id);
}
- MyBatis에서는 XML 파일에 SQL 쿼리를 작성하고, Mapper 인터페이스에서 XML의 쿼리 ID를 참조하여 SQL을 실행할 수도 있다.
2-6. Mapper 작성예시2
- 아래는 XML로 쿼리를 설정하는 기반의 MyBatis를 사용한 예제 코드다. 여기서 적어주는 쿼리는 아래의 인터페이스 코드에서 id값을 통해 사용한다.
<mapper namespace="com.example.EmployeeMapper">
<select id="getAllEmployees" resultType="com.example.Employee">
SELECT * FROM employees
</select>
<insert id="insertEmployee">
INSERT INTO employees(name, position, salary)
VALUES (#{name}, #{position}, #{salary})
</insert>
<update id="updateEmployee">
UPDATE employees SET name = #{name}, position = #{position}, salary = #{salary}
WHERE id = #{id}
</update>
<delete id="deleteEmployee">
DELETE FROM employees WHERE id = #{id}
</delete>
</mapper>
- Mapper Interface 코드 작성
public interface EmployeeMapper {
List<Employee> getAllEmployees();
void insertEmployee(Employee employee);
void updateEmployee(Employee employee);
void deleteEmployee(int id);
}
- 위의 예시에서, EmployeeMapper 인터페이스의 메소드 이름은 XML 파일의 쿼리 ID와 일치해야 한다. 그러면 MyBatis가 알아서 해당 메소드를 XML 파일의 쿼리와 매핑시켜 준다. 이 방법은 XML 파일에서 쿼리를 쉽게 관리하면서 동시에 인터페이스 메소드를 통해 쿼리를 실행할 수 있게 해준다. 물론, 이런 방식을 사용할 때는 SQL 쿼리가 변경될 때마다 XML 파일과 인터페이스 모두를 업데이트해야 한다는 점을 유의해야 한다.
- @Mapper를 사용한 interface 코드 구현
@Mapper
public interface EmployeeMapper {
List<Employee> getAllEmployees();
void insertEmployee(Employee employee);
void updateEmployee(Employee employee);
void deleteEmployee(int id);
}
3. @Mapper 어노테이션 사용시 주의점
Spring과 MyBatis를 같이 쓸 때, @Mapper 어노테이션은 꽤 중요하다. 이걸로 Spring이 그 인터페이스를 MyBatis Mapper로 알아보게 해주기 때문이다. 만약 @Mapper 어노테이션을 안 붙이면, Spring은 그 인터페이스를 그냥 일반 Bean으로 여긴다. 그러면 SQL 쿼리 메서드를 호출해도 제대로 안 될 수 있다.
근데, mybatis-spring 라이브러리에서 MapperScannerConfigurer라는 걸 쓰면 이야기가 달라진다. 이걸로 MyBatis Mapper 인터페이스를 자동으로 찾도록 설정할 수 있는데, 이렇게 하면 @Mapper 어노테이션을 안 붙여도 된다. 이 설정을 사용하면 지정된 패키지 안의 모든 인터페이스가 자동으로 Mapper로 등록되기 때문이다.
@Mapper 어노테이션이 없어도 작동하는 경우에는 대부분 MapperScannerConfigurer를 통해서 Mapper 인터페이스가 자동으로 등록된 것이다. 그래도 @Mapper 어노테이션을 명시적으로 사용하는 게 혼란을 줄이고 코드 읽기도 쉬워져서 더 좋은 방법이라고 할 수 있다. 그래서 @Mapper 어노테이션 사용을 권장한다.
다른 스프링 기초 지식이 궁금하다면?
반응형
'Spring 기초 > Spring 기초 지식' 카테고리의 다른 글
Spring MVC의 Model, ModelAndView, ModelMap 비교 (0) | 2023.08.13 |
---|---|
Spring에서의 인스턴스 생성 비교: new 키워드 대 DI (0) | 2023.08.13 |
스프링에서 데이터 전달의 핵심: VO와 DTO의 이해 및 활용 (0) | 2023.08.09 |
Spring Boot 폼 데이터 바인딩: @ModelAttribute 활용법 (0) | 2023.08.09 |
Spring Boot에서 REST 컨트롤러 활용하기: @RestController 어노테이션 이해하기 (0) | 2023.08.08 |