본문 바로가기
Dev/Python

[Python] 동기(Synchronous) & 비동기(Asynchronous)

by Yoon_estar 2025. 9. 20.
728x90
반응형

개요 

개발을 하다보면 꼭 마주치는 개념이 바로 동기와 비동기입니다. API 호출, 파일 입출력, 데이터베이스 쿼리 등 다양한 상황에서 이 개념이 적용됩니다. 처음에는 헷갈리기 쉽지만, 이해해두면 시스템 설계와 성능 최적화에 큰 도움이 됩니다. 

동기(Synchronous)

Synchronous는 Syn + chrono의 합성어로, Syn은 그리스어로 함께라는 뜻이고, chrono는 시간이라는 뜻이다.

동기는 사전적으로 '동시에 일어난다.'라는 의미를 갖고 있습니다. 프로그래밍에서 동기는 작업이 순차적으로 진행되는 것을 의미합니다. 즉 한 작업이 시작되면 해당 작업이 완료될 때까지 다음 작업이 기다려야합니다. 동기 방식은 호출한 함수 또는 작업이 반환될 때까지 대기하는 동안 실행 흐름이 차단되는 특징이 있습니다.

동기방식은 간단하고 직관적인 코드에 유용하나 여러 작업이 동시에 실행되어야하는 경우, 작업 완료를 기다리는 동안 시간이 소요되어 프로세스의 전체 성능이 저하될 수 있습니다.

비동기(Asynchronous)

Asynchronous는 부정을 의미하는 A라는 접두사가 붙어 함께 + 시간 을 부정하는 말이다.

비동기는 사전적으로 '동시에 일어나지 않는다'라는 의미를 갖고 있습니다. 프로그래밍에서 비동기는 작업이 독립적으로 실행되며, 작업의 완료 여부를 기다리지 않고 다른 작업을 실행할 수 있는 방식을 의미합니다.

즉, 비동기 방식은 작업이 시작되면 해당 작업이 완료될 때까지 기다리지 않고 다음 코드를 실행할 수 있습니다.

비동기 방식은 주로 I/O 작업니다 네트워크 요청과 같이 시간이 오래 걸리는 작업에 유용합니다. 여러 작업이 동시에 작동하여 전체적인 성능이 향상됩니다. 비동기 방식은 콜백(callback) ,프라미스(Promise), async/await 등 메커니즘을 통해 구현될 수 있습니다.

 

동기 / 비동기 흐름 예시

  • 동기 방식 예
    • A의 계좌는 10,000 원을 뺄 생각을 하고 있다.
    • A의 계좌가 B의 계좌에 10,000원을 송금한다.
    • B의 계좌는 10,000원을 받았다는 걸 인지하고, A 계좌에 10,000원을 받았다고 전송한다.
    • A,B 계좌에 각각 차람과 증가가 동시에 발생한다.
  • 비동기 방식 예
    • 학생은 시험지를 푼다.
    • 시험문제를 모두 푼 학생은 선생에게 전송한다.
    • 선생은 학생의 시험지를 채점한다.
    • 선생이 채점을 하는 동안 학생은 다른 과목 공부를 할 수 있다.
    • 채점이 다된 시험지를 학생에게 전송한다.
    • 학생은 선생이 전송한 시험지를 받아 결과를 확인한다.

동기 vs 비동기

요청한 작업에 대해 완료 여부를 신경써서 작업을 순차적으로 수행할지 아닌지에 대한 관점

비동기의 성능 이점

보통 비동기 특징을 성능과 연관지어 말한다. 요청한 작업에 대하여 완료 여부를 신경쓰지 않고 자신의 그 다음 작업을 수행한다는 것은, I/O 작업과 같은 느린 작업이 발생할 때, 기다리지 않고, 다른 작업을 처리하면서 동시에 처리하여 멀티 작업을 진행할 수 있기 때문이다. 이는 전반적인 시스템 성능 향상에 도움을 줄 수 있다.

동기 코드 예시

## 동기
import time
import requests

def main():
    urls = ["https://httpbin.org/delay/1"] * 3
    start = time.time()
    
    for url in urls:
        r = requests.get(url)  # 끝날 때까지 기다림
        print(url, r.status_code)
    
    print("총 소요:", time.time() - start, "초")

main()

비동기 코드 예시

## 비동기
import time
import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as resp:
        return url, resp.status

async def main():
    urls = ["https://httpbin.org/delay/1"] * 3
    start = time.time()

    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for url, status in results:
            print(url, status)

    print("총 소요:", time.time() - start, "초")

await main()

 

실무에서의 선택 기준 : I/O 바운드 vs CPU 바운드

동기와 비동기 중 어떤 방식을 선택할지는 작업의 성격에 따라 달라집니다.

1. I/O 바운드(I/O-Bound) 작업

정의 : 작업 시간의 대두분을 입출력(I/O) 대기에 소비하는 작업

예시 : 네트워크를 통한 API 호출, 데이터베이스 쿼리, 대용량 파일 읽기/쓰기

선택 : 이 경우 비동기 방식이 압도적으로 유리합니다. 비동기 코드는 대기하는 시간에 따른 I/O 작업을 처리하여 시스템의 처리량을 극대화합니다. 파이썬의 asyncio 라이브러리가 대표적인 예시입니다.

2. CPU 바운드(CPU-Bound) 작업

정의 : 작업 시간의 대부분을 CPU 연산에 소비하는 작업

예시 : 복잡한 수학 계산, 이미지/동영상 처리, 데이터 압축/암호화

선택 : 이런 작업에는 동기 방식이나 멀티프로세싱(Multiprocessing)이 적합합니다. 비동기는 CPU 연산 자체를 빠르게하지는 못하며 여러개의 독립된 프로세스가 각각의 CPU 코어를 활용하는 멀티 프로세싱이 성능 향상에 더 효과적입니다.

 

마무리

동기와 비동기는 단순히 코드를 작성하는 스타일을 넘어, 시스템의 성능과 구조를 결정하는 중요한 의사 결정 포인트입니다.

  • 동기는 순차적이고 예측 가능한 흐름이 필요할 때
  • 비동기는 외부 자원의 대기 시간을 활용하여 시스템의 효율성을 높여야 할 때 선택해야합니다.
반응형