안녕하세요 조금 늦었습니다.
오늘은 Async/Await에 대해 알아보려고 합니다.
Async는 asynchronous, 즉 비동기의 줄임말입니다.
동기와 비동기 들어보신 분들도 계시겠지만
정확히 설명하라고 하면 헷갈리시는 분들도 계실 거라 생각합니다. 저처럼
그럼 같이 공부해 보도록 하겠습니다.
1. 동기 / 비동기
그럼 제일 처음 알아야 하는 것은 동기/비동기가 무엇이냐?입니다.
저는 보통 동기/비동기를 설명할 때 줄 서기로 비유합니다.
우리가 줄을 서서 물건을 산다고 산다고 합시다.
앞사람이 물건의 모든 바코드를 찍고 계산 절차를 거치고 물건까지 다 챙겨야
다음 사람이 차례가 옵니다.
이것이 "동기화"입니다.
"컴퓨터 작업들의 수행 시기를 맞추는 것, 컴퓨터 시스템들 간에 진행 상황을 일치시키는 것."
그렇다면 비동기는 반대로
앞사람을 기다리지 않고 나도 같이 물건을 구매하는 것입니다.
이번에는 코드로 한번 보도록 하겠습니다.
print("1")
print("2")
print("3")
"1"
"2"
"3"
코드는 기본적으로 앞의 코드가 실행이 되어야
다음 코드가 실행이 됩니다.
그렇기에 "1", "2", "3"이 순서대로 출력됩니다.
print("1")
//10초
print("2")
print("3")
"1"
//10초 경과
"2"
"3"
그런데 만약 "1"을 출력하는데 10초가 걸린다면 어떨까요?
"2"를 출력하는 데는 10초가 넘게 걸릴 거고
"3"을 출력하는 데는 더 오랜 시간이 걸릴 것입니다.
앞의 작업이 다 끝나지 않았기에 뒤의 작업도 실행하지 못하는 것입니다.
우리가 어떤 온라인 쇼핑몰 웹사이트를 들어간다고 생각해 봅시다.
온라인 쇼핑몰 웹사이트를 키는 순간
온갖 종류의 데이터(물건 리스트, 사용자 정보 등)를 서버에서 가져오게 됩니다.
그런데 만약 동기식이라면 모든 데이터를 가져올 때까지 웹사이트는 멈추게 됩니다.(모든 데이터를 가져와야 다음 작업을 할 수 있기 때문)
그렇다면 오래 걸리거나 혹은 매번 얼마나 걸리지 모르는 작업 때문에 다른 작업들까지 멈추게 되는 것입니다.
2. 콜백(Callback)
이러한 문제를 해결하기 위해 콜백함수를 사용합니다.
콜백함수는 "결과가 나오면 알려줘"라고 예약을 걸어두는 함수입니다.
func add(a: Int, b: Int) -> Int {
var result: Int = a + b
return result
}
add(a: 1, b: 2) //3
두 정수를 더하는 간단한 함수가 있습니다.
func add(a: Int, b: Int) -> Int {
var result: Int = a + b
//10초
return result
}
add(a: 1, b: 2)
//10초 경과
//3
만약 두 정수를 더하는데 10초가 걸린다면 어떨까요?
3이라는 출력 값이 나오는데 10초가 넘는 시간이 걸리게 되고
다른 작업들은 멈추게 됩니다.
이때 콜백 함수를 사용합니다.
func add(a: Int, b: Int, completion: (Int) -> Void) {
let result = a + b
completion(result)
}
일반적인 함수에 return이 없어지고, completion이 생겼습니다.
completion은 값을 바로 반환하는 return과 다르게
"결과가 나오면 전달해 주는 함수"라고 생각하시면 됩니다.
swift는 매개변수로 함수를 받을 수 있기에
completion을 매개변수로 사용할 수 있고
completion은 Int 값을 받아서 사용하고 return값은 없는(Void) 함수인 것입니다.
그렇다면 이 콜백함수를 어떻게 사용할까요?
func add(a: Int, b: Int, completion: (Int) -> Void) {
let result = a + b
completion(result)
}
func handleResult(result: Int) {
print(result)
}
add(a: 1, b: 2, completion: handleResult)
일단 add함수를 호출하고 매개변수를 전달해 줍니다.
이때 completion 값으로 handleResult 함수를 전달합니다.
두 값을 더하는 작업이 끝나면 completion(result)를 실행합니다.
이때 completion = handleResult이기에
handleResult(result)가 실행되고
result값이 출력됩니다.
상당히 복잡한 과정입니다.
그래서 콜백 함수를 사용할 때는 클로저를 이용해서 간단히 사용합니다.
func add(a: Int, b: Int, completion: (Int) -> Void) {
let result = a + b
completion(result)
}
add(a: 1, b: 2) { result in
print(result)
}
이렇게 간단하게 사용할 수 있습니다.
좋습니다. 이번에는 이런 함수가 있다고 생각해 봅시다.
func fetchData(completion: (String) -> Void) {
//오래 걸리는 작업
}
작업이 오래 걸리는 콜백 함수가 있습니다.
이러한 작업이 여러 개라고 합시다.
func fetchDataA(completion: (String) -> Void) {
//오래 걸리는 작업
}
func fetchDataB(completion: (String) -> Void) {
//fetchDataA 다음 실행하는 오래 걸리는 작업
}
func fetchDataC(completion: (String) -> Void) {
//fetchDataB 다음 실행하는 오래 걸리는 작업
}
func fetchDataD(completion: (String) -> Void) {
//fetchDataC 다음 실행하는 오래 걸리는 작업
}
이 함수들을 사용해 보도록 하겠습니다.
fetchDataA { resultA in
fetchDataB { resultB in
fetchDataC { resultC in
fetchDataD{
print(resultA, resultB, resultC, resultD)
}
}
}
}
앞의 작업이 완료되어야 다음 작업을 할 수 있기에
클로저를 중첩해서 사용하는 모습을 볼 수 있습니다.
가독성이 매우 떨어지고 만약 오류 처리라도 해야 한다면 더더욱 코드가 복잡해집니다.
이를 콜백 지옥(CallBack Hell)이라고 합니다.
3. Async/Await
이러한 문제점을 해결하기 위해 Async/Await를 사용합니다.
async는 “이 함수는 비동기 작업을 한다”는 선언,
await “이 작업이 끝날 때까지 기다린다"라는 뜻입니다.
바로 코드로 보도록 하겠습니다.
func fetchData() async -> String {
return "데이터"
}
func load() async {
let result = await fetchData()
print(result)
}
함수명 뒤에 async가 붙었습니다.
또한 함수를 사용할 때도 await가 앞에 붙습니다.
이는 fetchData() 함수는 비동기 함수라는 것을 지칭하여
작업 도중 멈출 수 있다는 것을 의미하고
현재 실행을 잠시 멈추고 다른 작업을 수행하다가 결과가 오면 이어서 실행하라는 의미입니다.
즉 load() 함수에서 result 값에 "데이터"라는 값을 넣을 때 10초가 걸린다면,
10초 동안 다른 작업을 하고 있다가
10초 후 "데이터"값이 나오면 result에 넣으라는 뜻입니다.
4. Task
위와 같이 load()라는 비동기 함수를 실제로 사용하려면
load() // 불가능
Task {
await load()
}
//View
.task {
await load()
}
이렇게 Task 블럭 안에서 사용해야 합니다.
그래서 실제 앱을 만들때 뷰(View)에서 비동기 함수를 실행하려면
.task 모디파이어(modifier)를 사용해서 함수를 호출합니다.
'Swift' 카테고리의 다른 글
| Swift - 클래스(Class) (0) | 2026.04.15 |
|---|---|
| Swift - 1급 객체(First-Class Object) (0) | 2026.04.03 |
| Swift - 옵셔널(Optional) (0) | 2026.03.27 |
| Swift - Generic (0) | 2026.02.27 |
| Swift - ARC(Automatic Reference Counting) (0) | 2026.02.26 |