GDSC Sookmyung 활동/10 min Seminar

스프링부트 로그 찍기

주다애 2023. 3. 5. 22:33

안녕하세요, 이번 10분 세미나에서 발표를 맡은 컴퓨터과학전공 주다애입니다. 여러분 다들 개강 무사히 하셨나요? 학기가 지날수록 개강은 설렘이나 기대감보다는 귀찮음과 짜증이 더 많이 나는 일인 것 같습니다. 방학을 치열하게 보내시고 개강을 맞이한 후 처음하는 세미나이기 때문에 빠르고 쉽고 간단하게 준비해 보았습니다. 편하게 들어주시면 좋을 것 같아요!

제가 준비한 주제는 스프링부트 로그 찍기입니다. 어떤 주제를 할까 고민을 많이 하다가, 제가 직접 프로젝트에 도입해보고 굉장히 편리한 기능이라고 느낀 로깅이라는 기능을 공유하고 싶어서 준비해보았습니다.

목차는 다음과 같습니다. 먼저 로깅에 대해 소개를 하고 자바에서 사용하는 logback 프레임워크란 무엇인지, 어떻게 파일을 설정하고 어떤 구조로 되어있는지 소개를 할 예정입니다. 그리고 나서 직접 로그를 찍어보고 마지막으로 프린트문과의 차이점을 소개하며 밢표를 마무리할 예정입니다.

로깅이란 무엇일까요? 로깅이란 애플리케이션이 동작하는 동안 시스템의 상태나 동작 정보를 시간순으로 기록하는 것을 뜻합니다. 하지만 로깅은 비기능 요구사항입니다. 클라이언트나 사용자에게 필요한 기능은 아니지만 시스템 운영을 판단하기 위해 시스템에 부과된 요구 사항이라는 것이지요. 서비스의 동작 상태를 파악하거나 발생한 장애를 알고 싶을 때 아주 유용한 기능입니다. 유지보수를 위해서도 필요하지요.

오늘 소개해드릴 로깅 프레임워크는 Logback입니다. 이는 자바 진영에서 가장 많이 사용되는 프레임워크로 로깅을 위한 좋은 기능을 제공하고 있죠. 스프링부트를 사용하게 되면 spring intializr에서 spring-boot-starter-web의존성만 추가해주면 따로 다른 의존성을 추가해줄 필요 없이 로그를 사용할 수 있게 됩니다. 아주 편리하지 않나요?

피피티에는 적지 않았지만 Logback의 특성에 대해서 몇 가지 소개해드리면

(1) 로깅을 빠르게 구현할 수 있으며 상대적으로 적은 메모리 공간을 사용하고

(2) I/O 장애로부터 원활한 복구가 가능합니다. 애플리케이션 재가동없이 설정 변경이 가능합니다.

(3) 별도의 프로그램 없이 로그 파일 압축이 가능하고 저장된 로그 파일 보관 관리가 편리합니다.

원래는 log4j라는 프레임워크를 사용했는데, log4j보다 월등한 성능을 보장하기 때문에 많이 사용된다고 하네요!

앞서 설명 드린 특징 이외에도 중요한 특징이 있는데요, 바로 Logback5개의 로그 레벨을 제공합니다. 위에서부터 ERROR, WARN, INFO, DEBUG, TRACE입니다.

각각에 대해서 알아보자면,

ERROR : 로직 수행 중 시스템에 심각한 문제가 발생하여 애플리케이션 작동이 불가한 경우를 의미합니다.

WARN : 시스템 에러의 원이이 될 수 있는 경고 레벨입니다. ERROR보다는 치명적이지 않은 경우이지요.

INFO : 가장 흔히 사용되는 레벨로, 애플리케이션의 상태 변경과 같은 정보를 전달하는 레벨입니다. 저도 이 레벨을 주로 사용합니다.

DEBUG : 이름에서부터 알 수 있다싶이 애플리케이션의 디버깅을 위한 메시지를 표시하는 레벨입니다.

TRACE : 위의 DEBUG 레벨보다 더 상세한 메시지를 전달하기 위한 레벨입니다.

그렇다면 스프링부트에서 Logback을 사용해서 로그를 찍으려면 어떤 설정파일을 작성해야 할까요? 일반적으로 자바나 스프링 프로젝트에서는 logback.xml이라는 참조하지만 스프링부트에서는 logback-spring.xml 파일을 참조한다고 합니다. , 스프링부트 프로젝트 실행 시, 일반적으로 src/main/resources/logback-spring.xml 이라는 경로로 파일을 작성하면 되는 것이지요.

이제 직접 로그를 찍어볼까요?

먼저 buill.gradle파일에 spring-boot-starter-web의존성을 추가해준 것을 보실 수 있습니다. 다음으로는 logback-spring.xml파일입니다. 사실 저 코드에서 중요한 것은 Appender영역입니다. Appender영역은 로그의 형태를 설정하고 어떤 방법으로 출력할지 설정하는 곳입니다. Appender는 사실 하나의 인터페이스이고, 하위에 여러 구현체가 존재하는 모습입니다. , 각 구현체를 등록해서 로그를 원하는 형식으로 출력할 수 있게 됩니다.

코드를 보시면 <class = “.ConsolAppender”> 부분이 있는데요, 이 부분은 콘솔에 로그를 출력하는 패턴을 지정하는 부분입니다. <pattern>을 보시면 그 곳에 로그의 표현 형식을 정의할 수 있습니다. 저 코드에서는 %d를 사용해서 로그의 기록 시간을 기록하고 %-5level을 사용해서 로그 레벨을 기록하고, %thread를 사용해서 현재 스레드명을 기록하고, %logger{30}을 사용해서 로거의 이름을 기록하고 마지막으로 %msg를 사용해서 로그 메시지를 기록하는 로그의 형식을 표현하고 있습니다. 복잡해보이지만 여러분들께서 콘솔 창에서 매우 자주 보셨던 형식으로 출력이 된답니다!

밑의 <RollingFileAppender>은 여러 개의 파일을 순회하면서 로그를 저장하는 구현체입니다. <file>안의 경로에 로그파일을 저장하겠다는 의미입니다. 여기서는 위에서 정의한 logdirD드라이브 안의 Workspace안의 LogFiles이므로 그곳에 저장되겠네요! <rollingPolicy>는 보관 정책으로 우리는 TimeBaseRollingPolicy를 사용하고 있습니다. 가장 많이 사용하는 롤링 정책으로 일별 또는 월별과 같이 시간을 기준으로 롤오버 정책을 정의하는 정책입니다. maxHistory는 파일의 최대 저장 기한으로 여기서는 30일을 기준으로 하고 있습니다. 그 후는 오래된 파일부터 삭제합니다.

마지막으로 Root 영역을 보실 수 있습니다. 설정 파일에 정의된 Appender를 활용하려면 Root영역에서 Appender를 참조하여 로깅 레벨을 설정합니다. <appender-ref ref=“console”, “INFO-LOG” />를 설정해주었기 때문에 애플리케이션을 작동하면 해당 Appender 구현체가 작동합니다. 콘솔에 로그가 출력되고, 저의 D드라이브에 info-log파일이 저장되죠.

그럼 이제 로그가 찍히는지 정말 확인해볼까요?

 

Logback은 출력할 메시지를 Appender에게 전달할 Logger 객체를 각 클래스에 정의해서 사용합니다. 여기서 주의하실 사항은 Logger을 입력하면 다양한 인터페이스가 나오는데 저희는 org.slf4j.Loggerimport해서 사용해야합니다. 제가 이걸 다른 인터페이스를 import해서 이유도 모르고 헤맸던 기억이 나네요;; Logger 객체를 선언해주었습니다. 

먼저 가장 간단한 getHello() 메소드를 작성해보았습니다. 단순하게 “Welcome!”이라는 문자열을 출력해주는 메소드입니다. 그 안에 logger 객체를 사용하여 “getHello controller start!”라는 메시지를 적어주고 애플리케이션을 실행합니다. 알맞은 엔드포인트로 접속하면 아래와 같이 콘솔창에서 우리가 작성해준 로그 메시지를 보실 수 있습니다. 콘솔 창의 로그 형식이 우리가 위의 AppenderConsoleAppender에서 정한 출력 패턴입니다! 다음으로 로그를 통해 컨트롤러에 들어오는 값 확인도 가능합니다. 아래와 같이 getId() 메소드 코드를 작성해주고 애플리케이션을 실행하면 input id : 2023이라는 로그가 정상적으로 출력됩니다. 제가 값으로 2023을 넘겨준 것입니다.

마지막으로 System.out.println()과의 차이에 대해 알아보겠습니다. 결국 콘솔창에 출력하는 것은 동일한데 왜 굳이 Logback을 사용해야 하냐? 이 둘은 차이가 있습니다.

1. 프린트 메소드는 로그된 데이터를 콘솔에 출력할 뿐 저장하지 못합니다. , 나중에 모니터링이 불가하죠.

2. 또한 로그레벨을 제공하지 못합니다. 앞서 Logback의 특징이 5가지의 로그레벨을 제공한다는 것이었는데, 이를 프린트 메소드는 제공하지 못합니다. 프로젝트 규모가 커지고 상세한 디버깅이 필요할 때 좋지 못하겠죠?

3. 성능 저하의 원인이 될 수도 있다고 합니다. println()은 매 실행시마다 스트림을 생성하고 I/O 작업을 진행하므로 로컬에서는 크게 성능 차이를 느낄 수 없지만 실제 서버를 운영하게되면 큰 영향을 준다고 하네요

4. 마지막으로 저의 개인적인 생각으로는 직접 Logger 객체를 생성해서 로그를 찍어주는 것과 단지 System.out.println()을 사용하는 것은 그냥 코드를 보기에도 큰 차이가 있다고 생각합니다. 아무래도 직접 Logger 객체를 생성해서 로깅 기능을 사용하는 것이 훨씬 더 깔끔하지 않을까요?

사실 제가 준비한 발표에서는 로깅의 필요성을 딱히 못 느끼셨을 수도 있을 것 같습니다. 아주 간단한 controller에서 작동하는 것만 보여드렸기 때문에 그래서 이걸 왜 쓰는데? 라고 생각하실 수도 있을 것 같습니다. 그런데 프로젝트가 이보다 조금만 더 복잡해지더라도, 예를 들어 흔히 사용하는 domain, repository, service, controller가 나타나게 되면, 그 때는 훨씬 더 큰 도움이 됩니다. 적절한 위치에 적절한 로그 메시지를 찍어주면 확실히 개발 시간 단축에 도움이 되곤합니다.

만약 스프링부트 프로젝트를 하실 때 아직 로그를 찍어보신 적이 없으시다면 이번 기회에 한 번쯤 도입해보시는 것도 좋을 것 같습니다. 이것으로 발표를 모두 마치겠습니다. 들어주셔서 감사합니다!!!

 
 

 

'GDSC Sookmyung 활동 > 10 min Seminar' 카테고리의 다른 글

빅데이터를 알아보자  (0) 2023.03.13
GIT 브랜치 전략이란  (0) 2023.03.05
커스텀 데이터셋 만들기  (0) 2023.03.05
MVC vs MVVM  (0) 2023.02.27
JWT란 무엇인가  (0) 2023.02.27