반응형

1. 개요

  • Connection을 생성한 상태에서 Controller, Service, Repository, Mapper 구조를 통해 DB 데이터를 Select 하자
  • Controller, Service, Repository 클래스에 대한 자세한 설명은 하지 않겠다.

2. 준비

  • postgreSQL DB의 Select 테이블
  • mybatis 연동 경험

3. Controller 생성

  • 먼저 클라이언트의 요청을 받을 Controller 클래스를 생성한다.
  • 필자는 각각 Controller, Service, Repository 모두 각각의 패키지를 만든 후 생성했다.
  • test-select로 오는 get 요청에 대해 testService 인터페이스 구현체 응답값을 리턴하도록 하였다.
@Controller
public class TestController {

	@Autowired
	private TestService testService;
	
	@GetMapping("/test-select")
	@ResponseBody
	public List<BoardDto.Info> testSelect(){
		
		return testService.testSelect();
	}
}

4. ServiceInterface 생성

  • 서비스 인터페이스를 생성한다.
public interface TestService {

	public List<BoardDto.Info> testSelect();
}

5. Service 구현체 생성

  • TestService Interface의 구현체 클래스를 생성한다.
  • TestRepository를 Autowired 한다.
@Service
public class TestServiceImpl implements TestService{

	@Autowired
	private TestRepository testRepository;
	
	@Override
	public List<Info> testSelect() {

		return testRepository.testSelect();
	}

}

6. TestRepository 인터페이스 생성

  • 리포지토리 인터페이스를 생성한다.
  • 이 클래스는 인터페이스이기때문에 @Repository만 입력 시 구현체가 없으므로 Service 클래스에서 TestRepository를 주입에 실패했다는 에러가 발생한다. @Mapper 어노테이션은 이러한 인터페이스를 mybatis의 매퍼로 등록해주기 위해 사용된다. 즉 Mapper Bean이 되는것이다.
@Repository
@Mapper
public interface TestRepository {

	public List<Info> testSelect();
}

 

  • 하지만 이렇게 Mapper 어노테이션을 명시적으로 선언하게 되면 생성되는 모든 Repository에 다 넣어줘야한다. 이게 귀찮다면 DatabaseConfig 클래스에 @MapperScan("패키지 경로") 어노테이션을 선언해주자. 그럼 패키지 경로에 포함된 인터페이스에 대해 @Mapper 어노테이션을 선언한 효과를 얻을 수 있다.
@Configuration
@MapperScan("com.modu.repository")
public class DatabaseConfig {

	...
	
}

7. TestMapper 생성

  • Mapper.xml을 생성한다.
  • 필자의 경우 Dto 클래스를 static inner class 형식으로 사용하기 때문에 resultType에 $가 포함되어 있다. 만약, static inner class를 사용하지 않는다면 패키지 경로를 넣어주면 된다.
<?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.modu.repository.TestRepository">

	<select id="testSelect" resultType ="com.modu.dto.BoardDto$Info">
		SELECT
			board_seq
			,cat_id
			,title
			,content
			,writer
			,create_date
			,update_date
		FROM
			tbl_board
	</select>
</mapper>

 


8. 테스트

응답 값

 


 

9. 마치며

  • hikariCP와 mybatis, postgreSQL을 연동해보았는데 그저 머릿속에 있는 DB 통신에 대한 패턴을 별 생각없이 구현했다. DB 설정 클래스의 bean들은 각각 역할을 하는지, Mapper.xml 파일에 등록한 쿼리는 내부적으로 어떻게 생성되어 실제 쿼리가 처리되는지, Repository 인터페이스와 Mapper의 id가 어떻게 매핑되는지가 궁금해졌다. 다음 포스팅에서는 이런 설정 하나하나가 시스템적으로 어떻게 돌아가는지 알아봐야겠다.
반응형
반응형

1. 개요

 - SpringBoot 환경에서 hikariCP, mybatis, PostgreSQL DB를 연동해보자!


2. 환경

 - SpringBoot

 - Gradle

 - PostgreSQL

 - mybatis

 - JDK 1.8


3. hikariCP란?

  • hikari : '빛'의 일본어, CP : ConnectionPool, 뭐다? 빛처럼 빠른 Connection Pool !(?) 이라는 의도로 히카리라고 지었는지는 모르겠지만, JDBC ConnectionPool 중 하나이다. (실제로 tomcat ConnectionPool 보다 성능이 좋다)
  • ConnectionPool이란, 서버 시작 시 DB와의 Connection을 일정 개수 생성하여 Pool에 저장시켜 놓는 기술이다. 그 후 DB에 접근할 일이 생기면 Pool에서 놀고있는 Connection을 가져다 쓰고, 사용이 끝나면 다시 반환시켜 놓는다. ConnectionPool을 사용하지 않으면 트랜잭션 요청이 들어올때마다 Connection을 맺는 작업을 해야하는데, 접속이 몰리게 되면 서버에 부하를 가져올 수 있다. 그래서 일반적으로 ConnectionPool 방식을 많이 사용한다.
  • 결론은 hikariCP란 JDBC ConnectionPool이며, 스프링 부트 2 버전에서는 jdbc 의존성 주입 시 기본적으로 요녀석을 제공할 만큼 똑똑하고 빠른놈이란걸 알 수 있다.

 4. 의존성 주입

    •  build.gradle에 postgreSQL, jdbc, mybatis에 대한 의존성을 추가해주자.
//postgreSQL
runtimeOnly 'org.postgresql:postgresql'

//jdbc (hikari)
implementation 'org.springframework.boot:spring-boot-starter-jdbc'

//mybatis
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4'

5. hikari 설정

  • hikari 설정은 크게 DB 접속 정보Connection Pool 설정으로 구성된다. application.properties 파일에 다음과 같이 추가하도록 하자.
# DB 접속정보
spring.datasource.hikari.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.jdbc-url=jdbc:postgresql://[URL]:5432/[DB명]
spring.datasource.hikari.username=[접속 ID]
spring.datasource.hikari.password=[접속 PW]
spring.datasource.hikari.pool-name=[PoolName - 임의]

# CP Setting
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=50
spring.datasource.hikari.minimum-idle=30
spring.datasource.hikari.idle-timeout=60000
  • DB 접속정보와 ConnectionPool 셋팅 정보를 입력해주었다. CP Setting 정보는 다음과 같다.
  • connection-timeout : 클라이언트가 Pool에 Connection을 요청했을 때 기다리는 시간(ms)이다. 만약 최대 50개의 Connection을 생성해둔 상태에서 50개의 요청이 한번에 들어올 경우 51번째 클라이언트는 텅텅 비어있는 Pool에서 유휴 Connection을 기다릴 수밖에 없다. 그 참을성에 대한 시간이다. 30초동안은 기다린다는 뜻이며, 만약 30초가 지날 경우 ConnectionTimeoutException이 throw된다. 이 경우 connection-timeout 값을 늘려주거나, maximum-pool-size를 늘려줘야한다.
  • maximum-pool-size : Pool에 저장할수 있는 Connection의 최대 개수이다.
  • minimum-idle : Pool에서 저장시켜야 할 Connection의 최소 개수이다. 서버 최초 기동 시 Pool에 Connection을 생성하는데 minimum-idle을 설정할 경우 maximum-pool-size만큼 생성하지 않고 minimum-idle개수만큼 생성한다. 30개는 유휴상태로 유지시키는 것이다. 만약 DB 통신 중 에러가 발생하여 한개의 Connection이 폐기되어 유휴 커넥션이 29개가 되면 이 개수를 맞추기 위해 1개의 Connection을 생성하게 된다.
  • idle-timeout : minimum-idle 개수를 넘어가게 되면 Connection을 사용하고 Pool에 반환하는게 아닌 폐기시킨다. 앞서 말했듯이 30개의 Connection만 유휴상태로 유지시키기 때문이다. 그런데 요청이 계속 들어오면 오히려 폐기하는 것보다는 유휴상태로 유지시키는 것이 효율적인 상황이다. minimum-idle 개수를 넘어간 상황에서 Connection 추가 생성 후 해당 커넥션을 일정 시간 유휴상태로 유지시키는 설정이 idle-timeout이다. 이 설정을 넣지 않으면 바로 폐기가 될까?라고 생각할 수 있지만 그것도 아니다. default 값이 60000이기 때문에 1분동안은 유지되다가 더이상 사용되지 않을 경우 폐기된다.

6. DatabaseConfig 클래스 생성

  • 의존성 주입 및 application.properties에 CP 설정을 마친 상태에서 서버를 기동하면 다음과 같은 에러가 발생한다.
    Consider the following:
    	If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    	If you have database settings to be loaded from a particular profile you may need to activate it (the profiles oauth,prod are currently active).
     
  • H2, HSQL db에 대한 classpath를 추가해달란 것인데, 이 오류가 뜨는 이유는 앞서 설정했던 설정값들이 현재 어플리케이션에 적용이 되지 않아 default DB인 H2, HSQL로 셋팅이 되고, 실제 application.properties에는 이에 대한 설정값이 없기 때문에 발생하는 에러이다. DB 설정하는 Configuration 클래스를 만들면 해결이 된다.

 

  • DatabaseConfig.Class
package com.modu.config;


import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
public class DatabaseConfig {

	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.hikari")
	public HikariConfig hikariConfig() {
		return new HikariConfig();
	}
	
	@Bean
	public DataSource dataSource() {
		return new HikariDataSource(hikariConfig());
	}
	
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception{
		final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource);
		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		sessionFactory.setMapperLocations(resolver.getResources("mapper/*.xml")); 	//mapper 파일 로드
		sessionFactory.setConfigLocation(resolver.getResource("mybatis-config.xml"));//mybatis-config 로드
		return sessionFactory.getObject();
	}
	
	@Bean
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception{
		final SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
		return sqlSessionTemplate;
	}
}
  • ConfigurationProperties(prefix = "spring.datasource.hikari") 구문은 prefix로 시작하는 properties 값들을 추출하여 특정 객체에 바인딩 시키는 어노테이션이다. 즉, hikari 관련 설정들을 HikariConfig Bean 객체 생성 시 바인딩을 시키는 것이다.  바인딩 시 완화된 규칙이 적용되어 있어 jdbcUrl이라는 변수에 바인딩 시 jdbc-url, jdbc_url도 정상적으로 바인딩된다. 실제로 bean 객체를 Autowired 하여 debug 해보면 properties에 설정한 데이터들이 객체화되어 들어가고 있음을 알 수 있다. 나머지 값들을 HikariConfig Bean 생성 시 기본으로 주입된 값들이다.

hikariConfig

  • DataSource를 생성할 때 위에서 만든 객체를 HikariDataSource의 생성자로 주입하면 히카리 CP에 대한 Datasource 객체가 생성된다.
  • mybatis 설정파일 및 mapper 파일을 로드하는 설정에 맞게 resource 경로에 mybatis-config.xml 파일과 mapper 폴더를 생성해준다.
  • 서버를 기동하면 정상적으로 기동됨을 확인할 수 있다. 추가적으로 로그가 설정되어 있다면 debug 레벨에 다음과 같이 Connection이 생성되었다는 로그를 확인할 수 있다.

Connection 생성 완료

 

실제 DB에서 데이터를 조회해보는 것은 다음 게시글에 포스팅하도록 하겠다.

반응형
반응형

1. 개요

 - 스프링 부트 환경에서 기본으로 제공하는 LogBack을 사용하여 로그를 남겨보자

 - spring.profiles.active를 사용하여 운영, 개발 환경에 따라 로그 설정을 분기하여 적용해보자

 [참고 : https://goddaehee.tistory.com/206 깃대희의 작은공간]

 

2. LogBack이란?

 - LogBack이란 Log4j를 만든 개발자가 Log4j를 기반으로 속도와 메모리 점유율을 개선하여 만든 로깅 프레임워크이다.

 - org.slf4j.Logger 인터페이스의 구현체이다.

   >> 코드 작성 시 이 인터페이스를 임포트해주면 된다.

 

3. LogBack 특징

 - Level : 로그 레벨을 설정할 수 있다.

 - Appender : 출력 방법을 선택할 수 있다. ex) Console, RollingFile 등

 - Logger : 로그마다 다른 설정을 적용시킬 수 있다.

 - Authmatic Reloading Configuration File : 특정 시간마다 별도의 스레드를 통해 설정 파일 변경 여부 파악 및 적용이 가능하다. > logback 설정 변경 시 서버 재시작이 필요없다.

 

4. 프로젝트 세팅

 1) Controller - 로그를 찍기 위한 클래스

 2) application.properties - spring.profiles.active 설정 추가

 3) logback-spring.xml - logback 설정

 4) logback-{운용환경}.properties - logback 설정파일에서 로드할 상수 (로그파일, 레벨 등)

 

 4.1. Controller

  /api/log 요청 시 trace부터 error 까지의 로그를 쌓는 메서드를 작성한다.

@Controller
@RequestMapping("/api")
public class FileController {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@GetMapping("/log")
	@ResponseBody
	public ResponseDTO main() {
		logger.trace("trace Log");
		logger.debug("debug Log");
		logger.info("info Log");
		logger.warn("warn Log");
		logger.error("error Log");
		
		return null;
	}
}

 

 4.2. application.properties

  spring.profiles.active=dev 를 추가하여 개발 및 운영 환경을 정의해준다.

#서버포트
server.port=9090

#운용환경 : 개발
spring.profiles.active=dev

 

 4.3. logback-spring.xml

resource 경로에 logback-spring.xml 을 생성한 후 logback 설정을 정의한다.

* resource 경로에 logback-spring.xml 파일이 있으면 서버 기동 시 자동으로 로드한다.

<?xml version="1.0" encoding="UTF-8"?>

<!-- 10초마다 파일 변화를 체크하여 갱신시킨다. -->
<configuration scan="true" scanPeriod="10 seconds">

	<!-- spring.profile에 따른 설정파일 분기 -->
	<springProfile name = "prod">
		<property resource = "logback-prod.properties"/>
	</springProfile>
	
	<springProfile name = "dev">
		<property resource = "logback-dev.properties"/>
	</springProfile>
	
	
	<!-- 루트 로그 레벨 -->
	<property name ="LOG_LEVEL" value = "${log.config.level}"/>
	
	<!-- 로그 파일 경로 -->
	<property name ="LOG_PATH" value = "${log.config.path}"/>
	
	<!-- 로그 파일 명 -->
	<property name ="LOG_FILE_NAME" value = "${log.config.filename}"/>
	<property name ="ERR_LOG_FILE_NAME" value = "${log.config.filename}_error"/>
	
	<!-- 로그 파일 패턴 -->
	<property name ="LOG_PATTERN" value = "%-5level %d{yyyy-MM-dd HH:mm:ss}[%thread] [%logger{0}:%line] - %msg%n"/>
	
	
	
	<!-- 콘솔 Appender 설정 -->
	<appender name ="CONSOLE" class ="ch.qos.logback.core.ConsoleAppender">
		<encoder class ="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<pattern>${LOG_PATTERN}</pattern>
		</encoder>
	</appender>
	
	<!-- 파일 Appender 설정 -->
	<appender name="FILE" class ="ch.qos.logback.core.rolling.RollingFileAppender">
		<!-- 파일 경로 설정 -->
		<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
		
		<!-- 로그 패턴 설정 -->
		<encoder class = "ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<pattern>${LOG_PATTERN}</pattern>
		</encoder>
		
		<!-- 롤링 정책 -->
		<rollingPolicy class = "ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!-- gz, zip 등을 넣을 경우 자동 로그파일 압축 -->
			<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/${LOG_FILE_NAME}_%i.log</fileNamePattern>
			
			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<!-- 파일당 최고 용량 -->
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			
			<!-- 로그파일 최대 보관주기 -->
			<maxHistory>30</maxHistory>
		</rollingPolicy>
	</appender>
	
	
	<appender name = "ERROR" class ="ch.qos.logback.core.rolling.RollingFileAppender">
		<filter class ="ch.qos.logback.classic.filter.LevelFilter">
			<level>error</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
		<file>${LOG_PATH}/${ERR_LOG_FILE_NAME}.log</file>
		
		<!-- 로그 패턴 설정 -->
		<encoder class = "ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<pattern>${LOG_PATTERN}</pattern>
		</encoder>
		
		<!-- 롤링 정책 -->
		<rollingPolicy class = "ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!-- gz, zip 등을 넣을 경우 자동 로그파일 압축 -->
			<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/${ERR_LOG_FILE_NAME}_%i.log</fileNamePattern>
			
			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<!-- 파일당 최고 용량 -->
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			
			<!-- 로그파일 최대 보관주기 -->
			<maxHistory>30</maxHistory>
		</rollingPolicy>
	</appender>
	
	<root level = "${LOG_LEVEL}">
		<appender-ref ref="CONSOLE"/>
		<appender-ref ref="FILE"/>
		<appender-ref ref="ERROR"/>
	</root>
	
	<logger name="org.apache.ibatis" level = "DEBUG" additivity = "false">
		<appender-ref ref="CONSOLE"/>
		<appender-ref ref="FILE"/>
		<appender-ref ref="ERROR"/>
	</logger>
</configuration>

 - springProfile 태그를 사용하여 시스템 변수의 spring.profile.active 값을 조회할 수 있다. 이 값에 따라 logback-prod, logback-dev 파일 중 하나를 로드하여 설정파일의 프로퍼티로 사용한다.

 

 4.4. logback-dev.properties

 - 개발환경에서는 로그레벨을 debug로 설정한다.

#로그 레벨
log.config.level=debug

#로그파일 경로
log.config.path=/logs

#로그파일 명
log.config.filename=reckey

 

 4.5. logback-prod.properties

 - 운영 환경에서는 로그레벨을 info로 설정한다.

#로그 레벨
log.config.level=info

#로그파일 경로
log.config.path=/logs

#로그파일 명
log.config.filename=reckey

 

5. 테스트

 spring.profile.active를 prod 설정하면 로그레벨이 INFO로 잡혀있어 불필요한 로그는 조회되지 않지만 dev로 설정할 경우 서버 기동 시 발생되는 로그, DB 로그 등이 중구난방으로 나오게 된다.

 원인은 dev로 설정할 경우 logback 설정의 루트 로그 레벨이(root level) 값이 debug로 설정되어 어플리케이션 내 모든 클래스의 debug 로그가 찍히기 때문이다.

 이런 로그들은 특수한 목적이 없는 한 debug보단 info로 올리는 것이 좋다. 이를 위해서는 logger 태그를 사용하여 클래스별 로그 레벨 분기처리를 해야한다.

DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [AnonymousAuthenticationFilter:96] - Set SecurityContextHolder to anonymous SecurityContext
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [FilterSecurityInterceptor:210] - Authorized filter invocation [GET /api/log] with attributes [permitAll]
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [FilterChainProxy:323] - Secured GET /api/log
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [DispatcherServlet:91] - GET "/api/log", parameters={}
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [PropertySourcedRequestMappingHandlerMapping:108] - looking up handler for path: /api/log
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [RequestMappingHandlerMapping:522] - Mapped to com.reckey.controller.FileController#main()
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [FileController:24] - debug Log
INFO  2021-10-07 02:11:23[http-nio-9090-exec-1] [FileController:25] - info Log
WARN  2021-10-07 02:11:23[http-nio-9090-exec-1] [FileController:26] - warn Log
ERROR 2021-10-07 02:11:23[http-nio-9090-exec-1] [FileController:27] - error Log
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [RequestResponseBodyMethodProcessor:268] - Using 'application/json;q=0.8', given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [application/json, application/*+json, application/json, application/*+json]
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [RequestResponseBodyMethodProcessor:298] - Nothing to write: null body
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [DispatcherServlet:1131] - Completed 200 OK
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [HttpSessionSecurityContextRepository:346] - Did not store anonymous SecurityContext
DEBUG 2021-10-07 02:11:23[http-nio-9090-exec-1] [SecurityContextPersistenceFilter:118] - Cleared SecurityContextHolder to complete request
DEBUG 2021-10-07 02:11:25[reckeyPool housekeeper] [HikariPool:421] - reckeyPool - Pool stats (total=10, active=0, idle=10, waiting=0)
DEBUG 2021-10-07 02:11:25[reckeyPool housekeeper] [HikariPool:517] - reckeyPool - Fill pool skipped, pool is at sufficient level.

 

 5.1. 특정 클래스 분기처리 (로그레벨 INFO로 격상)

  일단 분기처리할 클래스의 경로를 확인해보자. 현재 로그 설정으로는 클래스 명만 나오고 있기 때문에 클래스 경로를 파악하기 힘들다. 로그 패턴에 클래스 경로 패턴인 %C를 다음과 같이 추가해보자.  자동 리로드 설정이 있으므로 서버 재시작은 하지 않아도 된다.

<property name ="LOG_PATTERN" value = "%-5level %d{yyyy-MM-dd HH:mm:ss}[%thread] [%C] [%logger{0}:%line] - %msg%n"/>

 그리고 다시 요청을 보내면 다음과 같이 클래스 경로가 함께 조회된다.

INFO  2021-10-07 02:14:37[restartedMain] [org.springframework.boot.StartupInfoLogger] [ReckeyApplication:61] - Started ReckeyApplication in 0.703 seconds (JVM running for 3806.024)
DEBUG 2021-10-07 02:14:37[restartedMain] [org.springframework.boot.availability.ApplicationAvailabilityBean] [ApplicationAvailabilityBean:77] - Application availability state LivenessState changed to CORRECT
INFO  2021-10-07 02:14:37[restartedMain] [org.springframework.boot.devtools.autoconfigure.ConditionEvaluationDeltaLoggingListener] [ConditionEvaluationDeltaLoggingListener:63] - Condition evaluation unchanged
DEBUG 2021-10-07 02:14:37[restartedMain] [org.springframework.boot.availability.ApplicationAvailabilityBean] [ApplicationAvailabilityBean:77] - Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator] [HikariPool:728] - reckeyPool - Added connection org.postgresql.jdbc.PgConnection@36e9f17b
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator] [HikariPool:728] - reckeyPool - Added connection org.postgresql.jdbc.PgConnection@73eb69c2
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator] [HikariPool:728] - reckeyPool - Added connection org.postgresql.jdbc.PgConnection@439a7476
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator] [HikariPool:728] - reckeyPool - Added connection org.postgresql.jdbc.PgConnection@42521e57
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator] [HikariPool:728] - reckeyPool - Added connection org.postgresql.jdbc.PgConnection@3bc95d4f
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator] [HikariPool:728] - reckeyPool - Added connection org.postgresql.jdbc.PgConnection@13c4968d
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator] [HikariPool:728] - reckeyPool - Added connection org.postgresql.jdbc.PgConnection@36d6947
DEBUG 2021-10-07 02:14:37[reckeyPool connection adder] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:421] - reckeyPool - After adding stats (total=10, active=0, idle=10, waiting=0)
DEBUG 2021-10-07 02:15:07[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:421] - reckeyPool - Pool stats (total=10, active=0, idle=10, waiting=0)
DEBUG 2021-10-07 02:15:07[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:517] - reckeyPool - Fill pool skipped, pool is at sufficient level.
DEBUG 2021-10-07 02:15:37[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:421] - reckeyPool - Pool stats (total=10, active=0, idle=10, waiting=0)
DEBUG 2021-10-07 02:15:37[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:517] - reckeyPool - Fill pool skipped, pool is at sufficient level.
DEBUG 2021-10-07 02:16:07[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:421] - reckeyPool - Pool stats (total=10, active=0, idle=10, waiting=0)
DEBUG 2021-10-07 02:16:07[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:517] - reckeyPool - Fill pool skipped, pool is at sufficient level.
DEBUG 2021-10-07 02:16:37[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:421] - reckeyPool - Pool stats (total=10, active=0, idle=10, waiting=0)
DEBUG 2021-10-07 02:16:37[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:517] - reckeyPool - Fill pool skipped, pool is at sufficient level.
DEBUG 2021-10-07 02:17:07[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:421] - reckeyPool - Pool stats (total=10, active=0, idle=10, waiting=0)
DEBUG 2021-10-07 02:17:07[reckeyPool housekeeper] [com.zaxxer.hikari.pool.HikariPool] [HikariPool:517] - reckeyPool - Fill pool skipped, pool is at sufficient level.

 org.springframework경로의 여러 클래스와 com.zaxxer.hikari로 시작하는 여러 클래스에서 DEBUG레벨로 로그들이 찍히고 있다. (히카리 로그는 DB연결로 인함입니다. 신경쓰지않으셔도됩니다.) 이 클래스들만 DEBUG레벨에서 INFO 레벨로 올린다면 이 문제가 해결될 것이다.

logback-spring.xml 파일의 logger 태그에 분기처리할 클래스를 다음과 같이 추가한다.

name에 들어가는 경로의 하위 모든 클래스에서 발생하는 로그를 INFO 레벨로 올리게 된다.

	<logger name="org.springframework" level = "INFO" additivity = "false">
		<appender-ref ref="CONSOLE"/>
		<appender-ref ref="FILE"/>
		<appender-ref ref="ERROR"/>
	</logger>
	
	<logger name="com.zaxxer.hikari" level = "INFO" additivity = "false">
		<appender-ref ref="CONSOLE"/>
		<appender-ref ref="FILE"/>
		<appender-ref ref="ERROR"/>
	</logger>

 

이제 실제 요청을 보내면 다음과 같이 불필요한 debug 레벨의 로그들을 쌓지 않게 된다.

DEBUG 2021-10-07 02:26:21[http-nio-9090-exec-1] [com.reckey.controller.FileController] [FileController:24] - debug Log
INFO  2021-10-07 02:26:21[http-nio-9090-exec-1] [com.reckey.controller.FileController] [FileController:25] - info Log
WARN  2021-10-07 02:26:21[http-nio-9090-exec-1] [com.reckey.controller.FileController] [FileController:26] - warn Log
ERROR 2021-10-07 02:26:21[http-nio-9090-exec-1] [com.reckey.controller.FileController] [FileController:27] - error Log

 

6. 마치며

 스프링 부트에서 로그백을 사용하여 로그를 남겨본 것은 처음이다. 생각보다 되게 간단하고, log4j 같은 경우 로그파일 리로드 설정을 외부 설정파일에서 해줘야하는데 logback은 리로드되는 기능이 설정 파일 안에 있어서 좋은것 같다.

반응형

+ Recent posts