본문 바로가기

오늘의 공부 & 기록

동기식 코드를 비동기식 코드로 변경하는 이유

1. 동기의 장점

 

1. 코드의 간결성과 가독성

  • 동기 코드는 작성 및 읽기가 쉽습니다. 작업이 순차적으로 진행되며, 로직을 따라가기 쉬워 디버깅과 유지보수가 편리합니다.
  • 특히, 비동기 코드에서는 flatMap 체인이나 Reactive Streams의 복잡한 흐름을 이해하기 어려울 수 있습니다.

 

2. 단순한 요청/응답 처리

  • 애플리케이션의 트래픽이 크지 않거나 I/O 작업이 많지 않은 경우, 비동기의 이점은 크지 않습니다.
  • 단순한 CRUD API 또는 내부 서비스 호출 등에서 동기 방식으로 구현하면 불필요한 복잡성을 줄일 수 있습니다.

 

3. 비동기의 필요성이 없는 경우

  • CPU 바운드 작업: 작업이 대부분 CPU 연산(예: 이미지 처리, 데이터 암호화 등)인 경우, 비동기 코드로 변경해도 성능 이점이 없습니다. 오히려 비동기 컨텍스트 전환 오버헤드가 성능을 저하시킬 수 있습니다.
  • 짧은 요청 시간: 작업이 매우 빠르게 끝나는 경우(예: 간단한 메모리 내 연산), 비동기를 사용할 필요가 없습니다.

 

4. 팀의 기술 스택과 이해도

  • 비동기 프로그래밍은 동기 방식보다 복잡하며, 팀원이 모두 비동기 프로그래밍을 이해하고 있어야 유지보수가 용이합니다.
  • 트러블슈팅: 비동기 코드에서는 에러의 흐름을 추적하기 어려울 수 있으며, 디버깅 도구 사용이 필요할 수 있습니다.

 

5. 트랜잭션 관리의 어려움

  • Spring에서는 전통적으로 @Transactional과 같은 동기 트랜잭션 관리가 널리 사용됩니다.
  • 비동기 방식에서는 트랜잭션 컨텍스트 관리가 까다롭습니다. Reactor에서 TransactionalOperator를 사용해야 하며, 이는 추가적인 학습과 구현 노력이 필요합니다.

 

6. 레거시 시스템과의 호환성

  • 기존에 동기 코드로 작성된 시스템과 통합해야 할 경우, 동기 방식으로 유지하는 것이 더 간단합니다.
  • DB 라이브러리나 서드파티 API가 동기 방식만 지원하는 경우에도 동기 코드가 더 적합합니다.

 

7. 비동기의 오버헤드

  • 비동기는 컨텍스트 스위칭Reactive Streams 관리에서 오버헤드가 발생합니다. 트래픽이 낮거나 작업량이 적은 환경에서는 성능 이점을 얻지 못하고 오히려 성능 저하를 초래할 수 있습니다.
  • 작은 프로젝트나 간단한 API에서는 동기 방식이 더 효율적일 수 있습니다.

 

 

2. 비동기의 장점

  • I/O 작업(WebClient 요청, DB 조회 등) 중에는 스레드가 블록되지 않아 더 많은 요청을 동시에 처리할 수 있습니다.
  • 트래픽이 높거나 I/O 바운드 작업이 많은 경우 비동기 방식을 선택할 수 있습니다.

 

 

3. 비동기 적용하기

1) 비동기 처리의 순서 유지 원리

  1. Mono와 Flux의 체이닝
    • Reactor의 Mono와 Flux는 체이닝을 통해 데이터를 순차적으로 처리할 수 있습니다.
    • 각 단계에서 데이터를 변환하거나 처리한 결과를 다음 단계로 전달합니다.
  2. FlatMap과 Map
    • map: 데이터를 변환하며, 비동기 처리가 필요 없는 경우 사용합니다.
    • flatMap: 비동기 작업을 수행하고, 그 결과를 다음 단계로 넘깁니다.
  3. Context 유지
    • Reactor는 체인의 순서를 자동으로 보장합니다. 이전 단계가 완료된 후에만 다음 단계가 실행됩니다.

 

2) 비동기 코드로 순차적 로직 유지하기

 

1.  기존 동기 코드 로직

현재 동기 코드는 다음 순서로 진행됩니다

  1. contentService.getContent를 통해 Content 조회
  2. 조회한 데이터를 가공하여 originIndex 생성
  3. referenceFilesRepository에서 referenceFiles 조회 및 가공
  4. WebClient로 요청 전송
  5. 요청 결과를 반환

 

3) 주요 변경 사항 설명

  1. 순차적으로 데이터 가공 유지
    • flatMap을 사용해 각 단계가 완료된 후에만 다음 단계로 넘어가도록 보장합니다.
    • 데이터 흐름이 Mono 체인에서 순서대로 전달됩니다.
  2. 데이터 가공 후 WebClient 요청
    • OriginIndex를 생성한 후에만 referenceFiles를 조회합니다.
    • referenceFiles 가공이 완료된 후에만 WebClient 요청을 보냅니다.
  3. 에러 처리
    • 각 단계에서 발생하는 에러는 체인에서 자동으로 전파되며, 필요시 doOnError를 사용해 로깅하거나 적절히 처리합니다.

 

 

4. 결론

현재 코드는 데이터베이스 조회 및 외부 API 호출 등 I/O 작업이 많은 전형적인 I/O 바운드 작업 흐름을 가지고 있습니다. 비동기 프로그래밍(WebFlux, Reactor)을 효과적으로 사용하면 현재 코드의 동시 처리 성능을 더욱 높일 수 있습니다.

특히, 데이터베이스와 같은 I/O 작업을 Reactive 방식으로 변환하면 성능 개선 효과를 기대할 수 있습니다.

비동기로 변환할 코드도 데이터 가공 및 요청 순서를 명확히 유지하며, 필요한 경우 체인을 끊거나 조합하여 세부적인 로직을 커스터마이즈할 수 있습니다. 현재 동기코드를 수정하여 비동기 로직으로도 원하는 대로 동작할 수 있습니다. 적절한 순차 처리와 비동기의 장점을 살려 코드를 수정합니다.