# 스위프트 async await 사용법, 콜백 지옥 벗어나기

- 게시: 2026.07.13 · 최종 업데이트: 2026.07.14
- 원문(HTML): https://trendbrief.news/articles/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-async-await-%EC%BD%9C%EB%B0%B1%EC%97%90%EC%84%9C-%EC%88%9C%EC%B0%A8-%EC%BD%94%EB%93%9C%EB%A1%9C.html
- 발행: [트렌드브리프](https://trendbrief.news/)

![스위프트 async await 사용법, 콜백 지옥 벗어나기](https://trendbrief.news/assets/articles/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-async-await-%EC%BD%9C%EB%B0%B1%EC%97%90%EC%84%9C-%EC%88%9C%EC%B0%A8-%EC%BD%94%EB%93%9C%EB%A1%9C/hero-1.jpg)

> async/await는 콜백 중첩 대신 비동기 작업을 순차 코드로 쓰게 해 주는 스위프트 문법입니다. 함수에 async, 호출부에 await를 붙이는 기본 문법부터 async let 병렬 실행, 콜백 API 전환, 취소와 액터까지 실무 관점으로 정리했습니다.

async/await는 스위프트에서 네트워크 요청이나 파일 읽기처럼 시간이 걸리는 비동기 작업을 위에서 아래로 읽히는 순차 코드로 쓰게 해 주는 문법입니다. 콜백 함수를 여러 겹 중첩하던 방식을 값을 그대로 반환받는 형태로 바꿔, 실행 흐름과 오류 처리를 눈으로 따라가기 쉽게 만듭니다. 2026년 7월 기준 이 방식은 스위프트 동시성(Swift Concurrency)의 표준으로 자리 잡은 것으로 안내되며, 문법과 동작 규칙은 [스위프트 공식 문서](https://www.swift.org)에 정리되어 있습니다. async/await는 2021년 9월 20일 공개된 Swift 5.5에서 언어에 추가됐습니다.

이 글은 Swift 5.5 이상을 기준으로 async/await의 기본 문법, 콜백 API 전환, 실무에서 챙길 점을 다룹니다. 액터 격리 규칙의 세부나 Combine 프레임워크와의 비교는 범위를 넘어서므로 여기서는 다루지 않습니다.

## async await는 어떤 문제를 푸는가

콜백이 겹겹이 쌓이는 문제와 흩어진 오류 처리를 정리합니다. 예전에는 비동기 함수의 결과를 completion handler(작업이 끝난 뒤 호출되는 콜백 함수)로 받았습니다. 요청이 하나둘 이어질수록 클로저가 안쪽으로 계속 들어가 코드가 오른쪽으로 밀렸고, 성공과 실패 분기를 각 단계마다 따로 챙겨야 했습니다. 이 문제 인식은 언어 설계 문서에 그대로 남아 있습니다. async/await를 도입한 스위프트 진화 제안 SE-0296은 이런 콜백 중첩을 '파멸의 피라미드(pyramid of doom)'라고 부르며, 오류 처리가 어려워지고 completion 호출을 빠뜨리기 쉽다는 점을 도입 이유로 명시합니다.

async/await를 쓰면 이 흐름이 평평해집니다. 결과를 반환값으로 받고, 오류는 스위프트의 일반 오류 처리(try/catch)로 한곳에서 잡습니다. 콜백 안에서 self를 약하게 참조하거나 completion 호출을 빠뜨리는 실수도 줄어듭니다.

## async와 await는 어떻게 쓰나

함수 선언에 async를 붙이고, 그 함수를 부르는 자리에 await를 씁니다. await는 "여기서 결과가 올 때까지 기다린다"는 표시이며, 기다리는 동안 스레드를 막지 않고 다른 작업에 양보합니다.

```
func fetchUser(id: Int) async throws -> User {
 let url = URL(string: "https://api.example.com/users/\(id)")!
 let (data, _) = try await URLSession.shared.data(from: url)
 return try JSONDecoder().decode(User.self, from: data)
}
```

예제에 쓴 URLSession.data(from:)는 애플 개발자 문서 기준 iOS 15·macOS 12부터 제공되는 async 버전 API입니다. 언어 기능의 배포 하한은 이보다 낮습니다. 애플은 Xcode 13.2 릴리스 노트에서 async/await를 포함한 스위프트 동시성을 iOS 13·macOS 카탈리나 10.15 배포 대상까지 지원한다고 밝혔습니다.

async 함수는 다른 async 문맥에서만 부를 수 있습니다. 버튼 탭 핸들러처럼 동기 코드에서 시작할 때는 Task로 비동기 문맥을 엽니다.

```
Task {
 do {
 let user = try await fetchUser(id: 1)
 updateUI(with: user)
 } catch {
 showError(error)
 }
}
```

## 여러 작업을 동시에 실행하려면

서로 의존하지 않는 두 요청은 async let으로 동시에 띄운 뒤 한 번에 기다립니다. 아래처럼 쓰면 두 요청이 병렬로 나가고 await 시점에서 둘 다 모이므로, 순서대로 await하는 것보다 전체 시간이 짧아집니다. 예를 들어 1초씩 걸리는 요청 두 개를 순서대로 기다리면 약 2초, async let으로 함께 띄우면 긴 쪽에 맞춘 약 1초에 끝납니다.

```
async let profile = fetchProfile(id: id)
async let posts = fetchPosts(id: id)
let (loadedProfile, loadedPosts) = try await (profile, posts)
```

작업 개수가 가변이거나 결과를 모아야 하면 TaskGroup을 씁니다. 이렇게 부모 작업이 끝나면 자식 작업도 함께 정리되는 구조를 구조적 동시성(structured concurrency)이라고 부릅니다. 이 구조를 설계한 스위프트 진화 제안 SE-0304는 '자식 작업은 자신을 만든 부모 작업보다 오래 살 수 없다'를 구조적 동시성의 제1규칙으로 명시합니다. 작업이 새어 나가 계속 도는 상황을 언어 차원에서 막는 근거가 이 규칙입니다.

세 방식의 쓰임새를 정리하면 이렇습니다.

| 방식 | 작업 개수 | 알맞은 상황 |
| --- | --- | --- |
| 순차 await | 고정 | 앞 결과가 다음 요청의 입력일 때 |
| async let(SE-0317) | 코드에 쓴 만큼 고정 | 서로 독립인 요청 2~3개 |
| TaskGroup(SE-0304) | 실행 중 가변 | 배열 순회처럼 개수가 런타임에 정해질 때 |

## 콜백 기반 API를 async로 바꾸기

completion handler를 쓰는 옛 API는 withCheckedContinuation(스위프트 진화 제안 SE-0300으로 추가)으로 감싸 async 함수로 만들 수 있습니다. continuation의 resume를 정확히 한 번 호출하는 것이 규칙입니다. 애플 개발자 문서에 따르면 checked 계열은 이 규칙 위반을 런타임에 검사해서, resume를 두 번 부르면 실행을 그 자리에서 중단시키고 한 번도 부르지 않은 채 버려지면 경고를 출력합니다. 빠뜨린 쪽은 호출부가 영원히 기다리게 되므로, 모든 분기에서 resume 호출이 빠짐없이 일어나는지 확인해야 합니다.

```
func currentLocation() async throws -> Location {
 try await withCheckedThrowingContinuation { continuation in
 locationManager.request { result in
  switch result {
  case .success(let location):
  continuation.resume(returning: location)
  case .failure(let error):
  continuation.resume(throwing: error)
  }
 }
 }
}
```

기존 콜백 코드를 한꺼번에 걷어내기 어렵다면, 이렇게 경계 함수만 async로 감싸고 호출부부터 조금씩 옮기는 방식이 무난합니다.

## 실무에서 챙길 점

UI 갱신은 메인 스레드에서 일어나야 합니다. 뷰나 뷰모델을 @MainActor로 표시하면 그 안의 코드는 메인 스레드에서 실행되도록 격리되고, 컴파일러가 이 격리 규칙을 검사합니다. await 이후에는 실행 스레드가 바뀔 수 있으니, 화면을 건드리는 코드가 메인에서 도는지 확인하는 편이 안전합니다.

공유 상태를 여러 작업이 동시에 건드릴 때, 예전에는 [세마포어나 뮤텍스로 직접 잠금을 걸었지만](https://trendbrief.news/articles/%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4%EC%99%80-%EB%AE%A4%ED%85%8D%EC%8A%A4-%EC%96%B8%EC%A0%9C-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%93%B0%EB%82%98.html), 스위프트 동시성에서는 액터(actor)가 상태 접근을 한 줄로 세워 데이터 경쟁을 막는 역할을 맡습니다. swift.org의 Swift 6 발표에 따르면 2024년 공개된 Swift 6 언어 모드는 잠재적 데이터 경쟁을 경고가 아니라 컴파일 오류로 진단하며, 그 전 버전인 Swift 5.10에서도 -strict-concurrency=complete 컴파일러 플래그를 켜면 같은 검사를 경고로 미리 받아볼 수 있습니다.

취소도 함께 설계해야 합니다. Task는 취소 신호를 전달하지만 실행을 강제로 멈추지는 않습니다. 긴 루프 안에 try Task.checkCancellation() 확인 지점을 두면 취소된 시점에 CancellationError를 던져 스스로 빠져나올 수 있습니다. 서버 쪽도 다르지 않습니다. [Vapor로 스위프트 서버를 만들 때](https://trendbrief.news/articles/vapor%EB%A1%9C-swift-%EC%84%9C%EB%B2%84-%EA%B0%9C%EB%B0%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0.html)도 라우트 핸들러가 async로 동작해, 같은 문법이 앱과 서버 양쪽에 그대로 쓰입니다.

## 관련 글

- [serve-sim 사용법, iOS 시뮬레이터를 브라우저로 스트리밍하기](https://trendbrief.news/articles/serve-sim-%EC%82%AC%EC%9A%A9%EB%B2%95-ios-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%A1%9C-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D%ED%95%98%EA%B8%B0.md)
- [OfficeCLI 사용법: 설치부터 AI 에이전트 연동까지](https://trendbrief.news/articles/officecli-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%84%A4%EC%B9%98%EB%B6%80%ED%84%B0-ai-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EC%97%B0%EB%8F%99%EA%B9%8C%EC%A7%80.md)
- [스위프트 기본 문법, 옵셔널·컬렉션까지 예제로 익히기](https://trendbrief.news/articles/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%98%B5%EC%85%94%EB%84%90-%EC%BB%AC%EB%A0%89%EC%85%98%EA%B9%8C%EC%A7%80-%EC%98%88%EC%A0%9C%EB%A1%9C-%EC%9D%B5%ED%9E%88%EA%B8%B0.md)
- [스위프트 에볼루션 제안 읽는 법, SE-0532로 연습](https://trendbrief.news/articles/se-0532%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EC%97%90%EB%B3%BC%EB%A3%A8%EC%85%98-%EC%A0%9C%EC%95%88-%EC%9D%BD%EA%B8%B0.md)
- [클로드 유튜브 영상 분석, 자막 MCP로 연결하는 법](https://trendbrief.news/articles/%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%9C%A0%ED%8A%9C%EB%B8%8C-%EC%98%81%EC%83%81-%EB%B6%84%EC%84%9D-%EC%9E%90%EB%A7%89-mcp%EB%A1%9C-%EC%97%B0%EA%B2%B0%ED%95%98%EB%8A%94-%EB%B2%95.md)