동기 / 비동기

2coconut·2025년 7월 24일
post-thumbnail

동기와 비동기, 정확히 무엇일까?

동기 (Synchronous)

동기 방식은 하나의 작업이 완전히 끝날 때까지 다음 작업이 시작되지 않는 방식이다. 마치 한 줄로 서서 차례대로 처리되는 은행 업무와 같다.

예시: 음식점에서 주문을 받는 직원이 한 명뿐인 상황
1. 첫 번째 손님의 주문을 받고 음식을 준비한다
2. 음식이 완성되어 손님에게 전달될 때까지 다른 손님은 기다린다
3. 첫 번째 손님의 일이 모두 끝나면 두 번째 손님의 주문을 받는다

비동기 (Asynchronous)

비동기 방식은 하나의 작업을 시작한 후, 그 작업이 끝나기를 기다리지 않고 다른 작업을 동시에 진행하는 방식이다. 여러 창구가 동시에 운영되는 은행과 같은 개념이다.

예시: 패스트푸드점의 주문 시스템
1. 계산대에서 주문을 받는다
2. 주문을 주방에 전달하고 바로 다음 손님의 주문을 받는다
3. 주방에서는 독립적으로 음식을 준비한다
4. 음식이 완성되면 번호를 불러 손님이 가져간다


동기 vs 비동기 특징 비교

동기 방식의 특징

  • 순차적 실행: 작업이 순서대로 하나씩 처리된다
  • 결과 보장: 이전 작업의 결과를 확실히 받을 수 있다
  • 간단한 로직: 코드의 흐름을 이해하기 쉽다
  • 블로킹: 하나의 작업이 전체 프로세스를 막을 수 있다

비동기 방식의 특징

  • 병렬 실행: 여러 작업이 동시에 진행될 수 있다
  • 논블로킹: 하나의 작업이 다른 작업을 막지 않는다
  • 효율성: 전체적인 처리 시간을 단축할 수 있다
  • 복잡성: 작업 간의 관계와 상태 관리가 복잡하다

장단점 분석

동기 방식

장점

  • 단순함: 코드 작성과 디버깅이 쉽다
  • 예측 가능: 실행 순서가 명확하다
  • 데이터 일관성: 각 단계에서 확실한 결과를 보장한다

단점

  • 성능 저하: 하나의 느린 작업이 전체를 지연시킨다
  • 자원 낭비: 대기 시간 동안 CPU가 놀고 있다
  • 사용자 경험: 응답이 느려 사용자가 답답함을 느낄 수 있다

비동기 방식

장점

  • 높은 성능: 전체 처리 시간을 크게 단축할 수 있다
  • 응답성: 사용자 인터페이스가 멈추지 않는다
  • 확장성: 더 많은 요청을 동시에 처리할 수 있다

단점

  • 복잡성: 코드가 복잡해지고 이해하기 어려워진다
  • 디버깅 어려움: 실행 순서를 예측하기 힘들다
  • 동기화 문제: 여러 작업 간의 데이터 공유 시 주의 필요

Blocking vs Non-blocking과의 차이

동기/비동기와 함께 자주 언급되는 개념이 Blocking/Non-blocking이다.

관점의 차이

  • 동기/비동기: "작업의 완료 시점에 대한 관심사"

    • 동기: 작업이 완료될 때까지 기다림
    • 비동기: 작업 완료를 기다리지 않고 다른 일을 함
  • Blocking/Non-blocking: "제어권의 반환 시점"

    • Blocking: 호출된 함수가 작업을 마칠 때까지 제어권을 반환하지 않음
    • Non-blocking: 호출된 함수가 즉시 제어권을 반환함

4가지 조합

  1. 동기 + Blocking: 일반적인 함수 호출 방식
  2. 동기 + Non-blocking: 계속 결과를 확인하는 방식 (폴링)
  3. 비동기 + Blocking: 드문 경우 (예: select 함수의 일부 사용법)
  4. 비동기 + Non-blocking: 콜백, Promise, async/await 등

업로드중..


Spring Boot에서의 비동기 처리

@Async 어노테이션이란?

Spring Framework에서 제공하는 @Async 어노테이션은 메서드를 비동기로 실행할 수 있게 해주는 강력한 도구이다.

핵심 원리:

  • 메서드에 @Async를 붙이면 별도의 스레드에서 실행된다
  • 호출자는 메서드 완료를 기다리지 않고 즉시 다음 코드를 실행한다
  • Spring AOP(Aspect Oriented Programming)를 기반으로 동작한다

간단한 예시

@Service
public class NotificationService {
    
    // 동기 방식 - 이메일 발송이 끝날 때까지 대기
    public void sendEmailSync(String email) {
        // 3초가 걸리는 이메일 발송
        emailSender.send(email);
        System.out.println("이메일 발송 완료");
    }
    
    // 비동기 방식 - 이메일 발송을 백그라운드에서 처리
    @Async
    public void sendEmailAsync(String email) {
        // 3초가 걸리는 이메일 발송 (별도 스레드에서 실행)
        emailSender.send(email);
        System.out.println("이메일 발송 완료");
    }
}

사용 시나리오:

  • 이메일/SMS 발송: 사용자는 즉시 응답을 받고, 메시지는 백그라운드에서 발송
  • 로그 기록: 비즈니스 로직 실행 후 로그는 별도로 처리
  • 데이터 동기화: 외부 시스템과의 데이터 동기화를 백그라운드에서 처리

기본 설정

@EnableAsync
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

비동기 메서드 구현

@Service
public class EmailService {
    
    @Async
    public CompletableFuture<String> sendEmail(String to, String subject) {
        // 이메일 발송 로직 (시간이 오래 걸리는 작업)
        try {
            Thread.sleep(3000); // 3초 시뮬레이션
            return CompletableFuture.completedFuture("이메일 발송 완료");
        } catch (InterruptedException e) {
            return CompletableFuture.failedFuture(e);
        }
    }
}

스레드 풀 설정

@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean(name = "emailExecutor")
    public Executor emailExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);        // 기본 스레드 수
        executor.setMaxPoolSize(10);        // 최대 스레드 수
        executor.setQueueCapacity(100);     // 대기열 크기
        executor.setThreadNamePrefix("Email-");
        executor.initialize();
        return executor;
    }
}

언제 어떤 방식을 사용할까?

동기 방식을 사용하는 경우

  • 간단한 CRUD 작업: 데이터베이스의 기본적인 읽기/쓰기
  • 순차적 처리가 중요한 경우: 계좌 이체, 재고 관리 등
  • 프로토타입 개발: 빠른 개발이 필요한 초기 단계

비동기 방식을 사용하는 경우

  • I/O 집약적 작업: 파일 읽기/쓰기, 네트워크 통신
  • 사용자 인터페이스: 화면이 멈추지 않아야 하는 경우
  • 대용량 처리: 많은 요청을 동시에 처리해야 하는 경우
  • 외부 API 호출: 응답 시간이 불확실한 경우

주의사항

비동기 처리 시 고려사항

  1. 오류 처리: 예외 상황에 대한 적절한 처리 방법 필요
  2. 상태 관리: 여러 비동기 작업의 상태를 추적하고 관리
  3. 메모리 사용: 너무 많은 비동기 작업은 메모리 부족 야기 가능
  4. 디버깅: 실행 순서가 불규칙해 문제 파악이 어려울 수 있음

Spring @Async 사용 시 주의점

  • 프록시 제약: 같은 클래스 내에서 @Async 메서드 호출 시 동기로 실행됨
  • public 메서드: @Async는 public 메서드에만 적용됨
  • 반환 타입: void 또는 Future 타입 사용 권장
profile
컴공학생

0개의 댓글