비동기 프로그래밍으로 시스템 성능 개선하기

정태경·2024년 7월 21일
post-thumbnail

비동기 프로그래밍이란

비동기 프로그래밍은 작업을 순차적으로 실행하는 대신, 작업이 완료될 때까지 기다리지 않고 다른 작업을 실행할 수 있는 프로그래밍 방식이다. 이를 통해 프로그램의 성능과 효율성을 향상시킬 수 있다. 이와 반대되는 개념은 동기 프로그래밍인데 동기 프로그래밍은 작업이 순차적으로 실행하며 이전 작업이 완료될 때까지 기다렸다가 다음 작업을 실행하는 방식이다.

비동기 프로그래밍의 장점

  1. 성능과 응답성 향상
    비동기 프로그래밍을 사용하면 여러 작업을 동시에 처리할 수 있어 전체적인 성능 향상을 기대할 수 있다. 블로킹되는 시간을 최소화하고 작업을 병렬로 처리함으로써 프로그램의 처리량과 응답 속도를 향상시킬 수 있기 때문이다. 이는 사용자 입력이나 외부 이벤트에 빠르게 반응하여 사용자 경험을 향상시킬 수 있다는 이야기이기도 하다.
  2. 자원 효율성
    비동기 프로그래밍을 사용하면 블로킹되는 시간 동안 다른 작업을 수행할 수 있기 때문에 시스템 자원을 효율적으로 활용할 수 있다. 이는 시스템의 확장성을 높이고 자원 소비를 최적화할 수 있는 장점을 제공한다.
  3. 확장성
    비동기 방식은 여러 작업을 병렬로 처리할 수 있기 때문에 시스템의 확장성을 높일 수 있다. 대용량 트래픽을 처리하거나 대규모 시스템을 구축할 때 비동기 프로그래밍은 중요한 역할을 한다.
  4. 비동기 코드의 재사용성
    비동기 코드는 일반적으로 동기 코드보다 재사용성이 높을 수 있다. 비동기 함수를 사용하여 여러 작업을 동시에 처리하거나 조합하여 더 큰 기능을 구현할 수 있기 때문에 코드의 모듈화와 유지보수가 용이하기 때문이다.

나의 비동기 프로그래밍 활용 사례

나는 팀 내 업무 효율성을 높이기 위해 업비트에서 제공하는 Open API로 다양한 애플리케이션(서버)을 만들어 서빙하고 있다. 이 과정에서 수많은 API 호출이 일어나게 되는데 동기 프로그래밍 방식은 응답 대기 시간으로 인해 응답성이 저하되고 시스템 성능이 저하되는 문제를 야기한다.

예를 들어, 입금할 수 있는 디지털 자산 목록을 받아오는 API와 출금할 수 있는 디지털 자산 목록을 받아오는 API, 그리고 주문할 수 있는 디지털 자산 목록을 받아오는 API를 가지고 현재 거래 가능한 디지털 자산 목록을 반환하는 서비스를 만들었다고 가정해 보자.

일반적인 동기 프로그래밍의 경우, 먼저 입금 가능한 디지털 자산 목록을 조회하는 요청을 보내고 해당 응답을 받을 때까지 기다린다. 그리고 응답을 받으면 두 번째 API를 요청하는 순서로 진행된다. 그러나 첫 번째 API 요청과 응답에 지연이 발생하면, 두 번째 API는 첫 번째 API 응답을 기다리는 동안 대기하게 된다. 이러한 동기 프로그래밍 방식은 응답성 저하와 시스템 성능 저하에 직접적인 영향을 미친다.

이때, 비동기 프로그래밍 방식을 활용하면 다음과 같이 개선해 볼 수 있다.
각각의 API 호출을 비동기적으로 처리하여 하나의 API 요청이 응답을 기다리는 동안에도 다른 API 요청을 동시에 처리한다. 이를 통해 API 호출 간의 응답 대기 시간을 최소화하고 시스템의 응답성을 향상할 수 있다. 이는 멀티 스레드와 같이 병렬 처리가 가능한 시스템으로 구축하면 더욱 효율적인 애플리케이션 운용이 가능하다.

샘플 코드

간단한 예시로 동기 프로그래밍과 비동기 프로그래밍을 살펴보자.

동기 프로그래밍 실행 결과
동기 프로그래밍은 Task 1이 시작되고 완료된 후 Task 2가 시작되는 것을 볼 수 있다.

import time


def sync_task(name):
    print(f"{name} 작업 시작")
    time.sleep(2)  # 2초 동안 작업 수행
    print(f"{name} 작업 완료")


# 동기적으로 두 작업을 순차적으로 실행
sync_task("Task 1")
sync_task("Task 2")

---
# 실행 결과
Task 1 작업 시작
Task 1 작업 완료
Task 2 작업 시작
Task 2 작업 완료

비동기 프로그래밍 실행 결과
그에 반해 비동기 프로그래밍은 Task 1과 Task 2가 동시에 시작되는 것을 확인할 수 있다.

import time


import asyncio


async def async_task(name):
    print(f"{name} 작업 시작")
    await asyncio.sleep(2)  # 2초 동안 작업 수행
    print(f"{name} 작업 완료")


async def main():
    # 비동기적으로 두 작업을 동시에 실행
    task1 = async_task("Task 1")
    task2 = async_task("Task 2")
    await asyncio.gather(task1, task2)


asyncio.run(main())
---
# 실행 결과
Task 1 작업 시작
Task 2 작업 시작
Task 1 작업 완료
Task 2 작업 완료

profile
두나무 업비트 QA 엔지니어

0개의 댓글