Overview
오늘은 프로그램의 흐름 제어에서 핵심 개념 중 하나인 동기(Synchronous)와 비동기(Asynchronous) 처리 방식에 대해 알아보자.
이 두 개념은 입출력 처리, API 호출, 파일 작업, 네트워크 요청 등 다양한 상황에서 프로그램이 작업을 어떻게 처리할지를 결정짓는 매우 중요한 기준이다.
특히 Python과 같은 현대 프로그래밍 언어에서는 async/await, Promise, Coroutine 등의 비동기 문법이 본격적으로 사용되고 있어, 이를 명확하게 이해하는 것이 실무에서도 큰 도움이 된다.

📅 관련 글
2023.01.13 - [CS 지식] - [CS 지식1.] 웹 브라우저의 동작원리
2023.02.23 - [CS 지식] - [CS 지식2.] DNS의 동작원리(Domain Name System)
2023.03.06 - [CS 지식] - [CS 지식3.] HTTP / HTTPS 란?
2023.03.07 - [CS 지식] - [CS 지식4.] OSI 7계층 & TCP/IP 4계층이란?
2023.03.17 - [CS 지식] - [CS 지식5.] 가상화란?
2023.05.24 - [CS 지식] - [CS 지식6.] HTTP 메서드(Method)란? / HTTP Status Code
2023.12.05 - [CS 지식] - [CS 지식7.] Kubernetes 구성요소와 Pod 생성 방식이란?
2023.12.19 - [CS 지식] - [CS 지식8.] 프로세스(Process)와 스레드(Thread)란?
2023.12.30 - [CS 지식] - [CS 지식9.] 클라우드 컴퓨팅이란?(Public & Private Cloud / IaaS SaaS PaaS / Multitenancy)
2024.01.05 - [CS 지식] - [CS 지식10.] 웹1.0(Web1.0) vs 웹2.0(Web2.0) vs 웹3.0(Web3.0)
2024.02.02 - [CS 지식] - [CS 지식11.] NAT(Network Address Translation)란?
2024.05.22 - [CS 지식] - [CS 지식13.] 동기 및 비동기 처리란?
2024.05.23 - [CS 지식] - [CS 지식14.] 3tier 아키텍처란?
2024.08.28 - [CS 지식] - [CS 지식15.] SSR vs CSR vs ISR vs SSG
2024.11.09 - [CS 지식] - [CS 지식16.] stdin(표준입력) vs stdout(표준출력) vs stderr(표준에러)
2024.11.11 - [CS 지식] - [CS 지식17.] IPsec vs SSL/TLS
2024.11.22 - [CS 지식] - [CS 지식18.] Quantum Computing(양자 컴퓨팅)
동기 처리(Synchronous Processing)
동기 처리에서는 작업이 한 번에 하나씩 순서대로 완료된다. 이는 다음 작업을 시작하기 전에 작업을 완료해야 함을 의미한다. 이러한 유형의 처리는 간단하고 이해하기 쉽다.
동기 처리의 특징
- 차단(Blocking): 각 작업은 다음 작업이 시작되기 전에 완료되어야 하며, 실행중인 작업이 완료될 때까지 후속 작업을 차단한다.
- 선형 실행(Linear Execution): 작업은 코드에 나타나는 정확한 순서대로 실행된다.
- 단순성(Simplicity): 작업 실행의 순차적 특성으로 인해 프로그래밍 및 디버깅이 더 쉽다.
- 사용 예(Usage Examples): 파일 읽기 또는 쓰기, 데이터베이스 트랜잭션 또는 후속 작업이 이전 작업의 결과에 의존하는 모든 작업에 사용한다.
동기 예제: 파일 읽기
동기 예제에서 Python은 두 개의 텍스트 파일을 순서대로 읽는다. 프로그램은 두 번째 파일 읽기를 시작하기 전에 첫 번째 파일이 완전히 읽힐 때까지 기다린다.
txt
파일을 먼저 생성한다.
cat <<EOF > somaz1.txt
Hello Somaz1
EOF
cat <<EOF > somaz2.txt
Hello Somaz2
EOF
그리고 sync-test.py
를 생성해준다.
cat <<EOF > sync-test.py
def read_file(file_name):
with open(file_name, 'r') as file:
print(f"Reading {file_name}...")
data = file.read()
print(f"Finished reading {file_name}.")
print(f"Contents of {file_name}: \\\\n{data}\\\\n")
return data
def main():
data1 = read_file('somaz1.txt')
data2 = read_file('somaz2.txt')
print("Both files have been read.")
if __name__ == "__main__":
main()
EOF
실행해보면 아래와 같은 결과가 나온다.
python3 sync-test.py
Reading somaz1.txt...
Finished reading somaz1.txt.
Contents of somaz1.txt:
Hello Somaz1
Reading somaz2.txt...
Finished reading somaz2.txt.
Contents of somaz2.txt:
Hello Somaz2
Both files have been read.
비동기 처리(Asynchronous Processing)
비동기 처리를 통해 작업을 기본 프로그램 흐름과 독립적으로 실행할 수 있으므로 백그라운드에서 작업을 처리할 수 있다. 이러한 유형의 처리는 웹 서버나 사용자 인터페이스를 갖춘 애플리케이션과 같이 작업에 상당한 시간이 걸릴 수 있는 환경에 필수적이다.
Python의 비동기 실행은 Python 3.5부터 표준 라이브러리의 일부인 asyncio
라이브러리를 사용하여 수행된다. 이 모델을 사용하면 코드의 특정 부분을 동시에 실행할 수 있으며, 이는 I/O 바인딩 및 상위 수준의 구조화된 네트워크 코드에 특히 유용하다.
Python은 async
및 await
구문을 사용하여 비동기 함수를 정의하므로 기존 콜백 기반 접근 방식에 비해 비동기 코드를 더 쉽게 작성하고 유지 관리할 수 있다.
비동기 처리의 특징
- 비차단(Non-blocking): 작업은 독립적으로 시작하고 완료할 수 있으며, 프로그램은 작업을 시작하고 완료될 때까지 기다리지 않고 계속 진행할 수 있다.
- 동시 실행(Concurrent Execution): 여러 작업을 병렬로 처리할 수 있어 애플리케이션의 효율성과 응답성이 향상된다.
- 복잡성(Complexity): 경쟁 조건 및 교착 상태와 같은 잠재적인 문제로 인해 프로그래밍 및 디버그가 더 어렵다.
- 사용 예(Usage Examples): API 요청, 파일 업로드 또는 사용자 인터페이스를 정지하거나 다른 계산을 지연시키고 싶지 않은 장기 실행 I/O 작업에 사용한다.
비동기 예제: 파일 읽기
비동기 예제에서 Python은 비동기 파일 작업을 위해 aiofiles
라이브러리를 사용하여 두 개의 텍스트 파일을 동시에 읽는 비차단 접근 방식을 사용한다. 이를 통해 프로그램은 첫 번째 파일이 완료될 때까지 기다리지 않고도 두 번째 파일 읽기를 시작할 수 있다. 이 방법은 프로그램이 파일 읽기 프로세스가 완료되기를 기다리는 동안 다른 작업을 수행할 수 있는 I/O 바인딩 작업에 특히 효율적이다.
aiofiles가 아직 설치되지 않은 경우 설치한다.
pip install aiofiles
이제 두 개의 텍스트 파일을 읽는 비동기 Python 스크립트를 작성해 보겠다.
cat <<EOF > rsync-test.py
import aiofiles
import asyncio
async def read_file(file_name):
print(f"Starting to read {file_name}")
async with aiofiles.open(file_name, 'r') as file:
content = await file.read()
print(f"Finished reading {file_name}")
print(f"Contents of {file_name}: \\n{content}\\n")
async def main():
tasks = [read_file('somaz1.txt'), read_file('somaz2.txt')]
await asyncio.gather(*tasks)
print("Both files have been read.")
if __name__ == "__main__":
asyncio.run(main())
EOF
실행해보면 아래와 같은 결과가 나온다. 다음 작업을 시작하기 전에 하나가 완료될 때까지 기다리지 않고 somaz1.txt와 somaz2.txt를 동시에 읽는다. 이는 여러 I/O 작업을 처리해야 하고 단일 파일 액세스로 인해 차단되지 않는 이점을 얻을 수 있는 애플리케이션에 유용하다.
#1
python3 rsync-test.py
Starting to read somaz1.txt
Starting to read somaz2.txt
Finished reading somaz2.txt
Contents of somaz2.txt:
Hello Somaz2
Finished reading somaz1.txt
Contents of somaz1.txt:
Hello Somaz1
Both files have been read.
#2
python3 rsync-test.py
Starting to read somaz1.txt
Starting to read somaz2.txt
Finished reading somaz1.txt
Contents of somaz1.txt:
Hello Somaz1
Finished reading somaz2.txt
Contents of somaz2.txt:
Hello Somaz2
Both files have been read.
요약정리
동기 처리와 비동기 처리 사이의 선택은 애플리케이션 요구 사항에 따라 달라진다.
- 동기: 작업을 특정 순서로 완료해야 하거나 다음 작업에 각 작업의 출력이 필요할 때 사용
- 비동기: 특히 작업이 독립적이거나 병렬로 수행될 수 있는 경우 애플리케이션의 처리량과 응답성을 향상시키는 데 사용
동기와 비동기를 더 깊게 이해하고 싶다면?
동기 vs 비동기 비교 표
구분 | 동기(Synchronous) | 비동기(Asynchronous) |
실행 순서 | 순차적으로 하나씩 처리 | 병렬/동시 실행 가능 |
블로킹 여부 | 블로킹 (다음 작업 대기) | 논블로킹 (다음 작업과 병행 가능) |
코드 단순성 | 상대적으로 단순 | 상대적으로 복잡 (콜백, await 등 필요) |
에러 처리 | try/except로 직관적으로 처리 | 콜백 내부 또는 await 문에서 처리 필요 |
사용 사례 | 간단한 로직, CLI, 파일 입출력 | 웹 서버, 대규모 I/O, 네트워크 통신 |
언어 지원 | 거의 모든 언어에서 기본 지원 | 최신 언어에서 키워드로 지원 (async/await 등) |
실제 웹 서버 흐름 예시: Flask vs FastAPI
# Flask 동기 예시
from flask import Flask
import time
app = Flask(__name__)
@app.route("/sync")
def sync_route():
time.sleep(3) # 블로킹
return "동기 완료!"
# FastAPI 비동기 예시
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/async")
async def async_route():
await asyncio.sleep(3) # 논블로킹
return {"message": "비동기 완료!"}
- 결과: FastAPI는 요청이 동시에 여러 개 들어와도
asyncio
덕분에 처리가 병렬적으로 가능! - 반면 Flask는 하나의 요청 처리 중 나머지 요청은 대기함.
JS 기준 Promise, async/await 흐름
// Promise 예제
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
wait(1000).then(() => {
console.log("1초 후 실행 (Promise)");
});
// async/await 예제
async function run() {
await wait(1000);
console.log("1초 후 실행 (async/await)");
}
run();
async/await
는 Promise 기반 문법을 더 직관적이고 동기식처럼 작성할 수 있게 해줌!
코루틴(Coroutine)이란?
Python에서 async def로 선언한 함수는 코루틴 객체를 반환한다.
이 코루틴은 즉시 실행되지 않고, await
나 asyncio.run()
등의 방식으로 스케줄링되어야 실행된다.
import asyncio
async def greet():
return "안녕하세요!"
# 호출
coroutine_obj = greet()
result = asyncio.run(coroutine_obj)
print(result)
- 코루틴은 경량 스레드처럼 동작하며, 여러 작업을 효율적으로 전환하며 실행할 수 있음!
Node.js 비동기 흐름 도식 (브라우저 vs Node 차이)
구성 요소 | 브라우저 환경 | Node.js 환경 |
Call Stack | 동일: 현재 실행 중인 함수 보관 | 동일 |
Web APIs | DOM, setTimeout, fetch 등 브라우저 제공 API | File System, HTTP, Crypto 등 Node 내장 비동기 API |
Callback Queue | setTimeout, setInterval, 이벤트 핸들러 등 | setTimeout, fs.readFile() , process.nextTick() 등 |
Microtask Queue | Promise.then() , queueMicrotask() 등 |
Promise.then() , process.nextTick() 등 |
Event Loop | Stack이 비었을 때 Callback Queue에서 작업을 가져와 실행 | 동일하나, Microtask 우선순위 등 처리 순서 주의 |
주요 차이점
- 브라우저는 DOM 조작 및 UI 업데이트에 특화
- Node.js는 파일 시스템, 네트워크, 데이터베이스와의 I/O 중심 비동기 처리에 특화
process.nextTick()
은 Node.js에서만 존재하는 Microtask 처리 방식
Flask에서 gevent
, uvicorn
, gunicorn
을 통한 비동기 설정 예시
Python은 기본적으로 동기 프레임워크인 Flask를 사용하지만, 아래처럼 비동기 서버와 함께 사용하면 효율적인 비동기 처리가 가능하다.
1. gunicorn
+ gevent
pip install gunicorn gevent
gunicorn -w 4 -k gevent app:app
-k gevent
: 비동기 처리를 위한 워커 타입 지정-w
4: 워커 4개 실행
2. uvicorn
(FastAPI에 최적화된 비동기 서버)
pip install uvicorn
uvicorn app:app --host 0.0.0.0 --port 8000
- Flask보단 FastAPI나 Starlette과 함께 사용해야 진가 발휘됨
- 완전한
async
지원을 제공
3. Flask + eventlet
도 대안
pip install eventlet gunicorn
gunicorn -k eventlet -w 1 app:app
정리
- Flask 자체는 동기지만,
gevent
,eventlet
,uvicorn
등의 서버를 통해 비동기 구조로 실행 가능 - 대규모 요청을 처리하거나 I/O 작업이 많은 API라면 반드시 고려해야 함
Java vs Go: 비동기 처리 방식 비교
항목 | Java (스레드 기반) | Go (고루틴 기반) |
기본 실행 단위 | Thread (OS 스레드) | Goroutine (경량 사용자 스레드) |
병렬성 실행 방식 | Thread Pool, ExecutorService 사용 | Go runtime의 scheduler로 자동 관리 |
비동기 키워드/프레임워크 | CompletableFuture, Reactive Streams, Spring WebFlux | go 키워드, 채널(channel), select 등 |
메모리 비용 | 스레드당 수 MB 메모리 | 고루틴당 수 KB 메모리 |
동시성 제어 | synchronized, Lock, Semaphore 등 | channel, mutex, waitgroup 등 내장 동시성 도구 제공 |
핵심 요약
- Java는 전통적인 스레드 기반: 안정적이지만 복잡하고 리소스 부담 ↑
- Go는 경량 고루틴 기반: 수천 개의 병렬 작업도 손쉽게 실행 가능
Gunicorn / Uvicorn 설정 예시 (YAML 스타일)
config.yaml
로 관리하는 Gunicorn 설정 예시
# config.yaml
bind: "0.0.0.0:8000"
workers: 4
worker_class: uvicorn.workers.UvicornWorker
timeout: 120
loglevel: info
accesslog: "-"
실행 방법
gunicorn -c config.yaml app:app
TIP
worker_class: uvicorn.workers.UvicornWorker
→ FastAPI, Starlette 기반 앱에 최적- 설정 파일로 관리하면 배포 자동화에 유리
Go: 채널을 이용한 비동기 처리 예시
package main
import (
"fmt"
"time"
)
func fetchData(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "📦 데이터 수신 완료"
}
func main() {
ch := make(chan string)
go fetchData(ch)
fmt.Println("⌛ 데이터 기다리는 중...")
result := <-ch
fmt.Println(result)
}
go fetchData(ch)
는 백그라운드 고루틴 실행ch <- "데이터"
는 결과 전송result := <-ch
는 결과를 기다림 (비동기 + 동기 수신)
고루틴 + 채널 = Go의 비동기 철학
Java: CompletableFuture
체이닝 예제
import java.util.concurrent.CompletableFuture;
public class AsyncExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
// 비동기 시작
System.out.println("🚀 API 호출 중...");
return "응답 데이터";
})
.thenApply(data -> {
System.out.println("📦 응답 처리: " + data);
return data.length();
})
.thenAccept(length -> {
System.out.println("✅ 응답 길이: " + length);
});
System.out.println("💡 메인 스레드는 계속 실행됨");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
}
}
supplyAsync
: 비동기 시작thenApply
: 변환 처리thenAccept
: 최종 소비Thread.sleep(...)
는 메인 스레드가 너무 빨리 종료되지 않도록 대기
📌 CompletableFuture
는 Java 8 이후 등장한 비동기 처리의 핵심 도구
마무리
동기와 비동기 프로그래밍은 각각의 목적과 사용 환경이 분명하다.
- 동기(Sync)는 순차적 흐름과 단순한 에러 핸들링을 제공하며, 복잡도가 낮은 작업에 적합하다.
- 비동기(Async)는 처리량과 반응성을 최적화할 수 있으며, 특히 I/O 작업이 많은 애플리케이션에 필수적인 접근 방식이다.
비동기 프로그래밍은 학습 곡선이 있지만, asyncio
, aiofiles
같은 툴과 함께 학습하면 훨씬 수월하게 접근할 수 있다.
Reference
https://www.mendix.com/blog/asynchronous-vs-synchronous-programming/
https://www.geeksforgeeks.org/difference-between-synchronous-and-asynchronous-transmission/
'CS 지식' 카테고리의 다른 글
[CS 지식15.] SSR vs CSR vs ISR vs SSG (6) | 2024.09.05 |
---|---|
[CS 지식14.] 3tier 아키텍처란? (2) | 2024.06.10 |
[CS 지식12.] SSO(Single Sign-On)란? (With OAuth SAML OIDC) (0) | 2024.03.23 |
[CS 지식11.] NAT(Network Address Translation)란? (0) | 2024.02.21 |
[CS 지식10.] 웹1.0(Web1.0) vs 웹2.0(Web2.0) vs 웹3.0(Web3.0) (2) | 2024.01.09 |