엄지월드

[8차시] 최신 최적화 사례와 트렌드 탐구 : 성능 향상을 위한 지속적 개선 본문

기타

[8차시] 최신 최적화 사례와 트렌드 탐구 : 성능 향상을 위한 지속적 개선

킨글 2025. 3. 23. 17:46

스프링 부트 비동기 및 병렬 처리

  • Spring Boot에서 비동기 처리 도입
    • @EnableAsync 어노테이션으로 비동기 처리 활성화
    • @Async 어노테이션으로 비동기 메서드 정의
    • 기본적으로 Executor가 비동기 작업을 처리
    • 반환 타입으로 Future, CompletableFutre 사용
  • 비동기 처리의 성능 최적화
    • ThreadPoolTaskExecutor를 사용해 비동기 스레드 풀 설정
    • corePoolSize, maxPoolSize, queueCapacity 등의 파라미터 최적화
    • 작업이 과도할 때 RejecctedExecutionHandler 설정
    • 적절한 스레드 풀 사이즈를 통해 리소스 사용 최적화
@Bean
public Exeucutor() {
	ThreadPoolTaskExcutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(20);
    executor.setQueueCapacity(500);
    executor.setTHreadNamePrefix("Asyn-");
    executor.initialize();
    
    return executor;
}
  • 비동기 작업 예외 처리
    • AsyncUncaughtExeceptionHandler로 비동기 예외 처리
    • CompletableFuture의 handle 메서드를 사용해 예외 처리
    • 비동기 작업 실패 시 Fallback 메커니즘 구현
@Async
public CompletableFuture<String> asyncMethodWithException() {
	if (new Random().nextInt(10) < 5) {
    	throw new RuntimeException("Error ocurred");
    }
	return CompletableFuture.completedFuture("Success");
}

@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
	System.out.println("Exception async method : " + ex.getMessage());
}
  • Spring에서 병렬 처리 도입
    • Parallel Streams를 사용해 데이터 스트림을 병렬 처리
    • CompletableFuture를 사용해 병렬 작업 구현
    • ForkJoinPool을 활용해 대규모 병렬 작업 관리
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream().forEach(number -> {
	System.out.println("Processing number:" + number);
});
  • Fork/Join framework를 활용한 병렬 처리 최적화
    • Fork/Join Framework로 작업을 여러 하위 작업으로 나누어 병렬 처리
    • RecursiveTask 또는 RecursiveAction을 사용해 작업 분할
    • 병렬 처리 후 결과를 병합하여 최종 처리
public class SumTask extends RecursiveTask<Integer> {
	private final int[] array;
    private final int start, end;
    
    public SumTask(int[] array, int start, int end) {
    	this.array = array;
        this.start = start;
        this.end = end;
    }
}
  • 비동기 및 병렬 처리의 주의점
    • 데드락 발생 방지 : 동기화된 자원 접근 관리
    • 스레드 풀 과부하 방지 : 적절한 스레드 수 관리
    • 상태 관리 주의 : 상태 변화가 많은 객체의 비동기 처리에서 동기화 고려
    • 비동기 및 병렬 작업에서 오류 처리 및 타임아웃 설정
  • 비동기 처리와 병렬 처리의 차이점
    • 병렬 처리는 여러 작업을 동시에 실행하여 처리 속도를 높임
    • 비동기 작업은 주로 I/O 작업에 적합, 병렬 작업은 CPU 집약적 작업에 적합
    • 비동기 작업은 비동기 호출의 실행 흐름을 관리하는 것이 핵심,
      병렬 처리는 리소스 사용을 최적화하는 것이 중요
  • 실시간 시스템에서의 비동기 및 병렬 처리 활용
    • 대규모 트래픽 처리 : 비동기 작업을 통해 처리량 증가
    • 데이터 스트리밍 : 병렬 처리를 통해 대규모 데이터를 실시간으로 처리
    • 실시간 알림 시스템에서 비동기 작업을 통한 빠른 응답
    • 병렬 처리를 활용한 배치 처리 작업의 성능 최적화

스프링 부트 로그 관리 및 성능 최적화

  • 로그 관리 도구와 외부 시스템 연동
    • ELK Stack(Elasticsearch, Logstash, Kibana)을 사용한 로그 중앙화
    • Graylog, Splunk와 같은 외부 시스템을 통한 로그 수집 및 분석
    • 로그 데이터를 JSON 형식으로 저장하여 분석 용이
    • Spring Boot에서 Logstash로 로그 전송
<appendern name="LOGSTASH"
	class="net.logstash.logback.appender.LogstashTcpSocketAppender">
    <destination>localhost:5000</destination>
</appender>
  • 로그 샘플링 및 필터링
    • 샘플링 : 빈번하게 발생하는 로그의 비율을 조정하여 성능 최적화
    • 필터링 : 특정 조건을 만족하는 로그만 기록
    • 중요하지 않은 로그 수준을 조정하여 성능 저하 방지
    • Logback에서 로그 샘플링 설정
<appender name="ASYNC_FILE" class="ch.qos.logbak.classic.AsyncAppender">
	<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
    	<evaluator>
        	<expression>return event.getLevel().toInt() == WARN</expression>
        </evaluator>
        <OnMatch>ACCEPT</OnMatch>
        <OnMismatch>DENY</OnMismatch>
    </filter>
</appender>

Virtual Threads 소개

  • Project Loom 개요
    • Virtual Thread는 매우 가벼운 스레드
    • 동시성 처리의 성능과 복잡성을 단순화
    • 기존의 OS 스레드 대비 훨씬 많은 수의 스레드를 효율적으로 처리
    • Project Loom은 Java 21부터 공식적으로 도입됨
  • 기존 스레드 모델의 한계
    • OS 스레드는 고정된 자원을 사용하며, 수천 개 이상의 스레드를 생성하는 것이 비효율적
    • Context Switching으로 인해 성능 저하 발생
    • 블로킹 I/O에서 자원이 낭비되는 문제
    • 비동기 코드 작성의 복잡성 증가
  • Virtual Threads의 개념
    • Virtual Threads는 JVM 레벨에서 생성되고 관리되는 경량 스레드
    • 매우 작은 메모리 공간을 사용하며, 필요할 때만 CPU 리소스를 사용
    • 기존 스레드 풀을 대체하여 동시성 처리가 더 쉬워짐
    • 스레드 생성의 오버헤드가 거의 없음
  • Virtual Threads의 사용법
    • Thread.ofVirtual() 메서드를 통해 Virtual Thread 생성
    • Virtual Threads를 사용한 간단한 예시
    • Thread vt = Thread.ofVirtual().start(() ->
      { System.out.println("Virtual Thread 실행"); });
    • Excutors.newVirtualThreadPerTaskExecutor()로 여러 작업 병렬 처리
    • var executor = Excutors.newVirtualThreadPerTaskExecutor();
      executor.submit(() > { // 작업 내용 }); executor.shutdown();
    • 기존 스레드 API와 호환 가능
  • Virtual Threads의 장점
    • 메모리 효율성 : 수십만 개의 스레드를 생성해도 성능 저하가 거의 없음
    • I/O 작업 최적화 : 블로킹 I/O에서도 리소스 낭비 없이 효율적
    • 간단한 코드 : 기존 비동기 처리에 비해 코드 복잡성 감소
    • 높은 동시성 처리 : 많은 요청을 병렬로 처리 가능
  • Virtual Threads와 기존 동시성 모델 비교
    • 기존 OS 스레드는 고정된 리소스 사용
    • 비동기 코드를 작성할 때, 복잡한 콜백 및 Future 처리
    • Virtual Threads는 더 많은 작업을 동시에 처리할 수 있음
    • 비동기 작업과 동일한 성능을 제공하지만 코드가 더 단순
  • Virtual Threads의 성능 비교
    • 메모리 사용량 : OS 스레드보다 훨씬 적은 메모리 사용
    • Context switching 비용 : Virtual Threads는 스케줄링 비용이 낮음
    • 수천 개의 스레드에서 응답 시간이 더 빠름
    • 실제 성능 테스트 결과 : Virtual Threads는 블로킹 I/O 작업에서도 효율적
  • Virtual Threads 사용 시의 고려 사항
    • 기존 스레드 풀 기반 동시성 처리와는 다른 패턴 요구
    • Virtual Threads는 스레드 풀보다는 직접 실행에 가까운 방식으로 동작
    • 기존 코드와의 호환성 유지 필요
    • Virtual Threads는 블로킹 I/O 작업을 효율적으로 처리하지만,
      비동기 작업이 항상 더 빠를 수는 없음
    • Virtual Threads 사용 시 적절한 메모리 관리 및 설정 필요
  • Virtual Threads와 병렬 처리
    • Virtual Threads는 병렬 처리 작업에서 유용
    • 각 작업을 별도의 Virtual Thread로 처리하여 동시성 극대화
    • CPU 집약적인 작업에서도 높은 성능 제공
    • 기존의 비동기 처리와 병렬 처리를 함께 사용하여 최적화된 성능 구현
  • Virtual THreads의 실제 적용 사례
    • 대규모 웹 어플리케이션에서 수십만 개의 동시 요청 처리
    • 고성능 데이터 처리 시스템에서 대규모 병렬 작업 처리
    • 기존 비동기 처리 모델을 단순화하여 복잡성 감소
    • 대규모 I/O 작업이 많은 서비스에서 블로킹 I/O 작업 최소화
  • Spring Boot에서 Virtual Threads 활성화
    • Spring Boot 3.2와 Java 21에서 Virtual Threads 사용 가능
    • application.properties 설정 예시 : spring.threads.virtual.enabled=true
    • Virtual Threads는 기존의 스레드 풀을 대체하여 동시성 처리를 더 효율적으로 관리
    • Virtual Threads 활성화를 통해 더 많은 요청을 동시에 처리 가능
@Bean
public AsyncTaskExcutor taskExecutor() {
	return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor()); 
}

어플리케이션 성능 최적화

  • WebP와 같은 최신 이미지 포맷 사용
    • 이미지 크기 압축 : JPEG, PNG 파일 압축
    • Lazy Loading : 스크롤 시점에 이미지를 로드
    • Responsive Images : 화면 크기에 맞춘 이미지 제공
  • 코드 압축과 최소화
    • CSS와 JS 코드 압축(Minification)
    • Whitespace 제거 및 변수명 축약
    • Babel, UglifyJS 등의 도구를 사용한 코드 변환
    • HTML 압축 및 인라인 스타일 최소화
  • 파일 병합
    • 여러 개의 CSS 파일을 하나로 병합
    • JS 파일 병합으로 요청 수 줄이기
    • HTTP/2 프로토콜을 사용하면 병합의 이점이 다소 감소하지만 여전히 유용함
    • CSS 및 JS 모듈화를 위한 Bundling 도구 사용 (예 : Webpack)
  • CDN 활용 - 웹 성능 최적화의 핵심 도구
    • 전 세계 분산 서버를 통해 사용자에게 빠른 콘텐츠 제공
    • 이미지, CSS, JavaScript와 같은 정적 파일을 캐싱
    • Cloudflare, Akamai와 같은 CDN 제공 업체 사용
    • 캐싱 정책 설정을 통해 더 빠른 응답 제공
  • 브라우저 캐싱 설정
    • Cache-Control 헤더 설정으로 브라우저 캐싱 활성화
    • Expires 헤더로 캐싱 만료 시간 설정
    • CSS, JS, 이미지와 같은 정적 파일 캐싱
    • 자주 변경되지 않는 파일에 대해 장기 캐싱 적용
  • 폰트 로딩 최적화
    • font-display 속성 사용으로 폰트 로딩 제어
    • woff2 포맷을 사용해 파일 크기 감소
    • Fallback 폰트를 지정해 폰트 로딩 시 사용자 경험 개선
    • Google Fonts와 같은 외부 폰트 캐싱 활용
  • 비디오 및 미디어 최적화
    • 비디오 스트리밍 서비스 사용(예 : 유투브)
    • 비디오를 Lazy Load로 설정
    • HTML5 비디오 태그를 사용한 적응형 스트리밍
    • 적절한 미디어 포맷 선택(예 : MP4, WebM)
  • 캐싱 전략을 통한 성능 개선
    • 브라우저 캐싱을 통해 자주 요청되는 리소스 캐시
    • Content Delivery Network (CDN) 캐싱 활용
    • 서비스 워커를 사용해 오프라인 캐싱 및 빠른 로딩
    • 캐시 무효화 전략 (TTL 설정, ETag 활용)
  • Progressive Web App(PWA) 적용
    • PWA의 오프라인 모드로 빠른 로딩 제공
    • 서비스 워커를 통해 백그라운드에서 작업 처리
    • 앱 캐싱과 프리로드를 사용하여 빠른 데이터 제공
    • 푸시 알림을 통한 사용자 참여 증대 및 성능 향상
Comments