Spring Boot에서 API 처리 시간을 로깅하고 DB에 저장하는 방법

 

Spring Boot에서 API 처리 시간을 로깅하고 DB에 저장하는 방법

Spring Boot에서 API 처리 시간을 로깅하고 DB에 저장하는 방법

애플리케이션 개발 중, 특정 API의 처리 시간을 측정하고 이를 로그로 기록하거나 데이터베이스에 저장해야 하는 요구사항이 발생하는 경우가 있습니다. 이번 포스팅에서는 Spring Boot를 활용하여 API 처리 시간을 계산하고 이를 로그로 출력하는 방법과 Oracle 데이터베이스에 저장하는 방법까지 자세히 알아보겠습니다.


1. API 처리 시간 로깅 구현

Spring Boot에서 특정 메소드의 처리 시간을 로깅하려면 System.nanoTime()을 사용하는 것이 가장 간단한 방법입니다. 예를 들어, 아래와 같은 Service 클래스 메소드에서 처리 시간을 측정할 수 있습니다.

Service 클래스 예제

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class SampleService {

    private static final Logger logger = LoggerFactory.getLogger(SampleService.class);

    public void executeTask() {
        long startTime = System.nanoTime(); // 시작 시간 측정

        try {
            // 처리 로직
            Thread.sleep(3000); // 예시: 3초 대기
        } catch (InterruptedException e) {
            logger.error("Error during task execution", e);
        }

        long endTime = System.nanoTime(); // 종료 시간 측정
        double duration = (endTime - startTime) / 1_000_000_000.0; // 초 단위로 변환
        logger.info("### 총 처리 시간 : {} 초 ###", String.format("%.3f", duration));
    }
}

코드 설명

  • System.nanoTime(): 작업의 시작과 끝 시간을 밀리초보다 더 정밀하게 측정할 수 있는 API입니다.
  • 초 단위 변환: nanoTime()은 나노초 단위의 값을 반환하므로 계산 후 1,000,000,000으로 나누어 초 단위로 변환합니다.
  • 로그 출력: logger.info()를 통해 소수점 3자리까지의 처리 시간을 출력합니다.


2. 처리 시간을 Oracle 데이터베이스에 저장

처리 시간을 데이터베이스에 저장하려면 MyBatis 또는 JPA를 통해 데이터베이스와 연동할 수 있습니다. 여기서는 MyBatis를 사용한 예제를 소개합니다.

2.1 테이블 생성

Oracle 데이터베이스에서 number(5,3) 타입 컬럼은 총 5자리 중 소수점 이하 3자리를 저장할 수 있습니다.

CREATE TABLE process_log (
    id           NUMBER(10) PRIMARY KEY,
    process_time NUMBER(5,3)
);

2.2 DTO 클래스

import java.math.BigDecimal;

public class ProcessLogDTO {
    private Long id;
    private BigDecimal processTime; // BigDecimal로 선언

    // Getter/Setter
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public BigDecimal getProcessTime() {
        return processTime;
    }

    public void setProcessTime(BigDecimal processTime) {
        this.processTime = processTime;
    }
}

2.3 MyBatis Mapper XML

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.mapper.ProcessLogMapper">

    <!-- Insert -->
    <insert id="insertProcessLog" parameterType="com.example.dto.ProcessLogDTO">
        INSERT INTO process_log (id, process_time)
        VALUES (#{id}, #{processTime})
    </insert>

    <!-- Select -->
    <select id="getProcessLogById" parameterType="Long" resultType="com.example.dto.ProcessLogDTO">
        SELECT id, process_time
        FROM process_log
        WHERE id = #{id}
    </select>

</mapper>

2.4 Mapper Interface

package com.example.mapper;

import com.example.dto.ProcessLogDTO;
import org.apache.ibatis.annotations.*;

@Mapper
public interface ProcessLogMapper {

    @Insert("INSERT INTO process_log (id, process_time) VALUES (#{id}, #{processTime})")
    void insertProcessLog(ProcessLogDTO processLog);

    @Select("SELECT id, process_time FROM process_log WHERE id = #{id}")
    ProcessLogDTO getProcessLogById(Long id);
}

2.5 Service에서 데이터 저장 및 조회

import com.example.dto.ProcessLogDTO;
import com.example.mapper.ProcessLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ProcessLogService {

    @Autowired
    private ProcessLogMapper processLogMapper;

    public void saveProcessLog(Long id, double duration) {
        ProcessLogDTO log = new ProcessLogDTO();
        log.setId(id);
        log.setProcessTime(BigDecimal.valueOf(duration)); // double 값을 BigDecimal로 변환
        processLogMapper.insertProcessLog(log);
    }

    public ProcessLogDTO getLog(Long id) {
        return processLogMapper.getProcessLogById(id);
    }
}


3. AOP를 활용한 전역 처리 시간 로깅

API마다 처리 시간을 측정하는 코드를 반복적으로 작성하기 번거롭다면 AOP(Aspect-Oriented Programming)를 활용해 전역적으로 처리 시간을 측정할 수 있습니다.

AOP 구현 예제

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ExecutionTimeAspect {

    private static final Logger logger = LoggerFactory.getLogger(ExecutionTimeAspect.class);

    @Around("execution(* com.example.service..*(..))") // 특정 패키지 적용
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.nanoTime();
        Object proceed = joinPoint.proceed(); // 메소드 실행
        long endTime = System.nanoTime();

        double duration = (endTime - startTime) / 1_000_000_000.0;
        logger.info("Method [{}] executed in {} seconds", joinPoint.getSignature(), String.format("%.3f", duration));

        return proceed;
    }
}

설명

  • @Around: 특정 메소드 실행 전후로 시간을 측정합니다.
  • 패키지 지정: execution(* com.example.service..*(..)) 표현식으로 특정 패키지 내 메소드에만 적용됩니다.


4. 마치며

Spring Boot에서 API 처리 시간을 측정하고 로그로 남기는 방법부터 Oracle 데이터베이스에 저장하는 방법까지 살펴보았습니다. 이러한 구현은 성능 모니터링 및 분석에 유용하며, 특히 트래픽이 많은 환경에서 병목 구간을 파악하는 데 큰 도움이 됩니다.

Spring Boot와 MyBatis, AOP를 활용하여 효율적으로 API 처리 시간을 관리해 보세요!

Comments