Notepad

스프링 부트 개발자 도구

코드를 수정할 때 애플리케이션을 자동으로 재시작하는 방법
브라우저 새로고침을 자동으로 실행하는 라이브 리로드 사용법
프로젝트 리액터 디버깅 도구

애플리케이션 시작 시간 단축

  • 예전에는 코드 수정 시 애플리케이션에 반영하기 위해 애플리케이션을 계속 재시작을 해야 했기에 불편했음
  • 스프링 부트가 나오기 전부터 스프링 프레임워크는 무거운 애플리케이션 서버 대신 서블릿 컨테이너를 선택해서 재시작 문제 해결을 시도함
  • 위의 시도에도 충분하지 않음
  • 스프링 부트가 나오면서 내장형 서블릿 컨테이너라는 혁신을 이뤄냄
    • 기존의 WAR 파일을 만들어서 톰켓과 같은 서블릿 컨테이너에 애플리케이션을 배포하는 방식이 아닌 애플리케이션에 서블릿 컨테이너를 포함하는 방식
  • 서블릿 컨테이너를 애플리케이션에 포함하며 생긴 변화
    • 애플리케이션 시작 속도를 높임
    • 배포 개념의 변화
      • 더 이상 운영 담당자가 설치하고 운영하는 서블릿 컨테이너에 종속되지 않음
      • 서블릿 컨테이너를 어떻게 사용할지 주체적으로 선택해서 지정 가능
      • JVM만 설치되어 있으면 어떤 장비에도 JAR 파일을 배포해서 서블릿 컨테이너가 포함된 애플리케이션 실행
  • 스프링 부트 개발팀은 여기에서 그치지 않고 애플리케이션을 더 빨리 실행할 수 있는 개선 방법 연구함

개발자 도구(DevTools)

  • 스프링 부트 개발팀은 내장형 서블릿 컨테이너의 혁신에 이어 DevTools라는 새로운 개발자 도구를 만듦
  • 개발자 도구 포함 기능
    • 애플리케이션 재시작(restart)과 리로드(reload) 자동화
    • 환경설정 정보 기본값 제공
    • 자동 설정(autoconfiguration) 변경사항 로깅
    • 정적 자원 제외
    • 라이브 리로드(LiveReload) 지원
  • 의존 관계 추가
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

자동 재시작과 리로딩

  • 스프링 부트 개발팀에서 애플리케이션 개발 속도를 높이기 위해 사용자 코드 변화를 감지하고 애플리케이션을 재시작하는 기능 추가함
  • 재시작과 리로딩
    • 개발자가 작성한 코드를 하나의 클래스 로더가 로딩하고 서드파티 라이브러리는 별도의 클래스 로더로 로딩
    • 애플리케이션이 재시작되면 개발자가 작성했던 코드를 로딩했던 클래스 로더도 종료되고 새로운 클래스 로더가 사용됨
    • 서드파티 라이브러리를 로딩했던 클래스 로더는 그대로 남음
    • 개발자 코드만 새로 리로드 하여 모든 것을 새로 시작하는 콜드(Cold) 시작 방식보다 훨씬 빨리 애플리케이션을 재시작할 수 있음
    • 리로드 개선 효과를 최대로 끌어내려면 JRebel 같은 자바 에이전트 솔루션 필요

정적 자원 제외

  • 스프링 부트는 기본적으로 다음과 같은 자원에는 변경이 발생해도 재시작을 하지 않음
    • /META-INF/maven
    • /META-INF/resources
    • /resources
    • /static
    • /public
    • /templates
  • 정적 자원 변경 내용은 대부분의 웹 기술에서 재부팅 없이도 서버에 반영할 수 있음으로 애플리케이션 재시작을 유발하지 않음
  • 변경사항이 재시작을 유발하지 않게 하는 경로를 변경하는 방법
spring.devtools.restart.exclude=static/**,public/**
  • 개발자 도구에 의한 재시작 자체를 비활성화
spring.devtools.restart.enabled=false

개발 모드에서 캐시 비활성화

  • 스프링 부트와 통합되는 많은 컴포넌트는 다양한 캐시 수단을 가지고 있음
    • ex. 어떤 템플릿 엔진의 경우 컴파일된 템플릿을 캐시함
  • 스프링 부트에서 타임리프 캐시 설정 비활성화
spring.thymeleaf.cache=false

부가적 웹 활동 로깅

  • 특정 패키지 내의 모든 파일에 사용된 모든 로깅 파일을 변경하거나, 클래스 수준 디버깅 설정을 뒤져보지 않아도 간단하게 web 로깅 그룹의 로그를 출력해서 웹 수준에서 어떤 일이 수행되는지 확인 가능
  • 개발자 도구를 사용해 Web 로깅 그룹의 로그 수준을 DEBUG로 지정
logging.level.web=DEBUG

자동 설정에서의 로깅 변경

  • 스프링 부트 2부터 자동 설정의 기본값과 다르게 설정된 내용만 확인할 수 있도록 변경사항을 관리함
    • ex. 어떤 빈을 추가해서 자동설정 기본값과 다르게 구성됐다면 그 달라진 내용만 보여줌

라이브 리로드 지원

  • 개발자 도구에 라이브 리로드 서버가 내장돼 있음
  • 서버가 재시작됐을 때 웹 페이지를 새로 로딩하는 단순한 작업을 수행함
    • ex. 서버가 재시작됐을 때 브라우저의 새로고침 버튼을 자동으로 눌러줌
  • 라이브 리로드를 사용하라면 백엔드에서 서버를 실행해야 하고 브라우저에도 LiveReload 플러그인을 설치해야 함

리액터 개발자 도구

리액터 플로우 디버깅

  • 리액터 처리 과정은 일반적으로 여러 스레드에 걸쳐 수행될 수 있으므로 스택 트레이스를 통해 쉽게 확인할 수 없음
    • 리액터는 나중에 구독에 의해 실행되는 작업 흐름을 조립하는 비동기, 논블로킹 연산을 사용
  • 자바 스택 트레이스는 동일한 스레드 내에서만 이어지며, 스레드 경계를 넘어서지 못함
  • 리액터는 스택 트레이스를 통해 가능한 한 가장 먼 곳까지 따라가지만 다른 스레드의 내용까지 쫓아가지는 못함
  • 이 한계를 극복을 위해 Hooks.onPeratorDebug() 메소드 호출 필요
    • Hooks.onPeratorDebug() 를 호출하면 리액터가 처리 흐름 조립 시점에서의 호출부 세부 정보를 수집하고 구독해서 실행되는 시점에 세부정보를 넘겨줌
    • 리액터가 스레드 별 스택 세부 정보를 스레드 경계를 넘어서 전달하는 과정에서 굉장히 많은 비용이 발생
    • 운영환경 또는 실제 벤치마크에서는 Hooks.onPeratorDebug()를 절대로 호출하면 안 됨
    • 호출해야만 한다면 적절한 조건을 사용해서 해당 조건을 만족할 때만 실행되게 해야 함
  • 리액터는 확장성 있는 애플리케이션을 만들 수 있는 수단을 제공함과 동시에 개발자의 디버깅을 돕는 도구도 함께 제공

리액터 플로우 로깅

  • 리액터에서는 실행 후에 디버깅하는 것 외에 실행될 때 로그를 남길 수도 있음
  • 리액터 코드에 log.debug()를 사용해보면 전통적인 명령행 코드에서와 달리 원하는 대로 출력하는 것이 쉽지 않음
    • 리액티브 스트림이 비동기라는 특성 때문이 아닌 리액터가 적용한 함수형 프로그래밍 기법에서 비롯된 문제
  • 로깅 때문에 간결한 코드를 쓸 수 없음
    • 람다식에 로깅 코드를 추가하며 장황한 코드로 변경됨
  • 리액터는 이 문제에 대해 다른 방법 제시
    • 리액터 연산자 사이에. log() 추가
    • log()에 인자로 넘겨진 문자열 토큰과 리액티브 스트림 시그널 모두 출력 가능

블록 하운드를 사용한 블로킹 코드 검출

  • 블록하운드
    • 리액터 개발팀에 소속된 세르게이 에고로프가 만듦
    • 개발자가 직접 작성한 코드뿐만 아니라 서드파티 라이브러리에 사용된 블로킹 메소드 호출을 모두 찾아내서 알려주는 자바 에이전트
    • 블로킹 코드가 소스 어디에도 없고 관련 설정도 적절하다는 것을 보장
  • 의존관계 추가
<dependency>
    <groupId>io.projectreactor.tools</groupId>
    <artifactId>blockhound</artifactId>
    <version>1.0.3.RELEASE</version>
</dependency>
  • 블록 하운드 자체로는 아무 일도 하지 않지만 애플리케이션에 적절하게 설정되면 자바 에이전트 API를 이용해서 블로킹 메소드를 검출하고, 해당 쓰레드가 블로킹 메소드 호출을 허용하는지 검사할 수 있음
  • 블록하운드 등록
public static void main(String... args) {
    BlockHound.install(); // 스프링 부트 애플리케이션 시작할 때 블록하운드가 바이트코드를 조작할 수 있음

    SpringApplication.run(HackingSpringBootApplication.class, args); 
}
  • 블록하운드 옵션
    • 특정 블로킹 호출을 문제로 인식하지 않도록 허용 리스트에 등록
    • 특정 부분을 블로킹으로 인지되도록 금지 리스트에 등록
  • 샘플 코드
// BlockHound.install(); // 스프링 부트 애플리케이션 시작할 때 블록하운드가 바이트코드를 조작할 수 있음 
BlockHound.builder()
    // 허용 리스트 추가
    .allowBlockingCallsInside(
        TemplateEngine.class.getCanonicalName(), "process")
    // 커스텀 설정이 적용된 블록하운드가 애플리케이션에 심어짐 
    .install();

참고

profile

Notepad

@Apio

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!