본문 바로가기
GD's IT Lectures : 기초부터 시리즈/스프링 부트(Spring Boot) & 마이바티스(Mybatis) 연동

[스프링 부트(Spring Boot) & 마이바티스(Mybatis) 연동] 고급 기능

by GDNGY 2023. 5. 7.

8. 고급 기능

고급 기능 섹션에서는 스프링 부트와 마이바티스를 활용하여 더욱 복잡한 기능을 구현하는 방법에 대해 다룹니다. 이를 위해 마이바티스의 고급 기능 중 하나인 동적 SQL 구문 작성 방법, 페이징 처리 기능, 연관 관계 매핑 등에 대해 다룹니다. 또한, 스프링 부트에서 제공하는 기능 중 하나인 AOP(Aspect-Oriented Programming)를 이용한 로깅 및 예외 처리 방법에 대해서도 다룹니다. 이러한 고급 기능들을 함께 활용하면 스프링 부트와 마이바티스를 이용하여 더욱 복잡하고 유연한 웹 애플리케이션을 구현할 수 있습니다.

 

8.1. 캐싱

8.1.1. 마이바티스 캐싱 기능

마이바티스는 자체적인 캐싱 기능을 제공합니다. 이를 사용하려면 매퍼 XML 파일에서 캐시 설정을 활성화해야 합니다. 아래와 같이 <cache> 태그를 사용하여 설정할 수 있습니다.

<mapper namespace="com.example.mapper.UserMapper">
    <cache/>
    <!-- 기존 매퍼 설정 생략 -->
</mapper>

 

8.1.2. 스프링 캐시 추상화 사용

스프링 캐시 추상화를 사용하면 캐싱 기능을 쉽게 구현할 수 있습니다. 스프링 부트는 기본적으로 EhCache, Hazelcast, Infinispan 등 다양한 캐시 구현체를 지원합니다. 먼저, 의존성을 추가해야 합니다. 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

 

서비스 레이어에서 캐시를 적용할 메서드에 @Cacheable, @CachePut, @CacheEvict 등의 어노테이션을 사용하여 캐시 동작을 정의합니다.

@Service
public class UserService {

    @Cacheable(value = "users", key = "#id")
    public User getUser(Long id) {
        return userMapper.getUser(id);
    }

    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        userMapper.updateUser(user);
        return user;
    }

    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userMapper.deleteUser(id);
    }
}

 

캐싱 기능을 적용하면 데이터베이스의 부하를 줄일 수 있고, 성능 향상을 기대할 수 있습니다.

 

 

8.2. 로깅

8.2.1. 로그 레벨 설정

스프링 부트는 기본적으로 SLF4J를 사용한 로깅을 지원합니다. 로그 레벨을 설정하려면 application.properties 또는 application.yml 파일에서 로그 레벨을 지정하면 됩니다. 예를 들어, 다음과 같이 설정할 수 있습니다.

# application.properties
logging.level.root=WARN
logging.level.com.example=DEBUG
# application.yml
logging:
  level:
    root: WARN
    com.example: DEBUG

 

로그 레벨은 TRACE, DEBUG, INFO, WARN, ERROR, OFF 등이 있으며, 낮은 로그 레벨부터 우선순위가 높습니다.

 

위 설정은 "logs" 디렉터리에 "myapp.log"라는 파일로 로그를 기록하게 됩니다. 또한, 로그 파일의 최대 크기나 롤링 정책 등을 설정할 수 있습니다. 

# application.properties
logging.file.max-size=10MB
logging.file.total-size-cap=100MB
logging.file.max-history=30
# application.yml
logging:
  file:
    max-size: 10MB
    total-size-cap: 100MB
    max-history: 30

이런 설정은 로그 파일의 최대 크기가 10MB로 제한되며, 총 로그 파일 크기는 100MB를 넘지 않게 됩니다. 또한, 최대 30개의 로그 파일을 보관합니다.

 

8.3. 트랜잭션 전파

8.3.1. 트랜잭션 전파 속성 이해

트랜잭션 전파는 트랜잭션의 시작과 종료가 다른 여러 메서드에 걸쳐 이루어지는 경우 사용됩니다. 스프링에서는 다양한 트랜잭션 전파 속성을 제공합니다. 이들 속성은 메서드 간의 트랜잭션 처리 방식을 결정합니다. 주요 트랜잭션 전파 속성은 다음과 같습니다.

  • REQUIRED (기본값): 이미 시작된 트랜잭션이 있으면 참여하고, 없으면 새로운 트랜잭션을 시작합니다.
  • REQUIRES_NEW: 항상 새로운 트랜잭션을 시작하며, 이미 진행 중인 트랜잭션이 있으면 일시 중단합니다.
  • SUPPORTS: 시작된 트랜잭션이 있으면 참여하고, 없으면 트랜잭션 없이 진행합니다.
  • NOT_SUPPORTED: 트랜잭션 없이 진행하며, 진행 중인 트랜잭션이 있으면 일시 중단합니다.
  • MANDATORY: 이미 시작된 트랜잭션에 참여하며, 없으면 예외를 발생시킵니다.
  • NEVER: 트랜잭션 없이 진행하며, 시작된 트랜잭션이 있으면 예외를 발생시킵니다.
  • NESTED: 시작된 트랜잭션이 있으면 중첩 트랜잭션을 시작하고, 없으면 새로운 트랜잭션을 시작합니다.
@Service
public class MyService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        // ...
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // ...
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public void methodC() {
        // ...
    }
}

 

각 메서드는 서로 다른 트랜잭션 전파 속성을 사용하여 트랜잭션을 처리합니다.

 

 

8.4. 다중 데이터 소스 사용

8.4.1. 여러 데이터 소스 설정

스프링 부트와 마이바티스를 사용하여 여러 데이터 소스를 설정하려면 각 데이터 소스에 대한 DataSource, SqlSessionFactory, SqlSessionTemplate 및 TransactionManager 빈을 생성해야 합니다.

@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public SqlSessionFactory primarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(primaryDataSource());
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionFactory secondarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(secondaryDataSource());
        return factoryBean.getObject();
    }

    // ...
}

 

8.4.2. 동적 데이터 소스 라우팅

동적 데이터 소스 라우팅을 사용하면 실행 시간에 데이터 소스를 동적으로 선택할 수 있습니다. 이를 위해 추상 클래스인 AbstractRoutingDataSource를 상속하는 동적 라우팅 데이터 소스 클래스를 작성해야 합니다.

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // 로직을 구현하여 실행 시간에 데이터 소스를 결정하고 반환합니다.
    }
}

 

그런 다음, 라우팅 데이터 소스를 생성하고 데이터 소스 목록을 설정합니다.

@Configuration
public class DataSourceConfig {

    @Bean
    public DynamicRoutingDataSource dynamicRoutingDataSource() {
        DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("primary", primaryDataSource());
        targetDataSources.put("secondary", secondaryDataSource());
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(primaryDataSource());
        return routingDataSource;
    }

    // ...
}

 

라우팅 데이터 소스를 사용하여 SqlSessionFactory 및 TransactionManager 빈을 생성합니다.

 

8.5. 마이바티스 플러그인

8.5.1. 플러그인 개요

마이바티스 플러그인은 마이바티스의 기능을 확장하거나 수정할 수 있는 유연한 방법을 제공합니다. 플러그인은 인터셉터를 구현하여 실행 흐름에 개입할 수 있습니다. 대표적인 사용 예시로는 페이징 처리, 캐싱, 로깅, 트랜잭션 관리 등이 있습니다.

 

8.5.2. 플러그인 사용 예제

예를 들어, 쿼리 실행 시간을 로깅하는 플러그인을 작성해 보겠습니다. 먼저, Interceptor 인터페이스를 구현하는 클래스를 작성합니다.

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class LoggingPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = invocation.proceed();
        long endTime = System.currentTimeMillis();
        long elapsedTime = endTime - startTime;

        Method method = invocation.getMethod();
        String methodName = method.getName();
        String className = method.getDeclaringClass().getSimpleName();

        System.out.printf("Method: %s.%s - Execution time: %d ms%n", className, methodName, elapsedTime);
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

 

그런 다음, 이 플러그인을 사용하도록 마이바티스 설정 파일에 추가합니다.

<configuration>
    <!-- ... -->
    <plugins>
        <plugin interceptor="com.example.LoggingPlugin"/>
    </plugins>
    <!-- ... -->
</configuration>

쿼리를 실행할 때마다 로깅 플러그인이 실행 시간을 기록하여 출력합니다.

 

 

 

 

2023.05.07 - [프로그래밍/스프링 부트(Spring Boot) & 마이바티스(Mybatis) 연동] - [스프링 부트(Spring Boot) & 마이바티스(Mybatis) 연동] CRUD 애플리케이션 예제

 

[스프링 부트(Spring Boot) & 마이바티스(Mybatis) 연동] CRUD 애플리케이션 예제

7. CRUD 애플리케이션 예제 스프링 부트와 마이바티스를 이용하여 실제로 CRUD 애플리케이션을 구현하는 방법에 대해 다룹니다. 이를 위해 사용자 정보를 관리하는 간단한 웹 애플리케이션을 구현

gdngy.tistory.com

 

반응형

댓글