기본적으로 모든 코드는 line by line로 동기적으로 처리된다. 그래서 따로 비동기 코드를 작성하지 않는 경우 동일한 스코프내에서는 하나의 코드가 일을 다 수행하면 다음 코드가 일을 수행한다.
외부에서 UI에 채워져야 하는 소스를 가져온다고 하면, 아래와 같은 업무적 병목이 생기고 유저가 당장 피해를 볼 수 있다.
private func syncImg(_ i: UIImageView) {
do {
let data = try Data(contentsOf: Some.photo)
// 이 과정에 마치 앱이 멈춘 느낌을 줄 수 있다.
i.image = UIImage(data: data)
} catch {
print(error)
}
}
1.
외부에서 소스를 받아오는 요청을 보낸다. (대충 10초 걸린다고 가정)
2.
소스가 넘어올 때까지 UI를 그리기 위해 준비해야 하기 때문에 유저가 다른 버튼 등을 터치할 수 없다.
3.
소스가 다 받아와지면 UI에 그린다. (유저는 계속 멈춘 UI를 보게 될 수 있다.)
극단적일 수 있지만, 용량이 크거나 서버에 이상이 있을 경우, 트래픽이 엄청 몰려서 요청 자체가 홀드되는 경우 등에는 sync한 업무 처리가 정말 큰 에러를 발생시킬 수 있다. 그래서 서버에 요청을 보내서 어떤 처리를 하는 경우, 그리고 그 결과에 따라 UI를 데이터로 입히는 과정은 비동기로 보통 처리한다. 요청에 대한 결과를 기다리는 동안에도 유저가 앱을 사용할 수 있어야 하기 때문이다.
물론, 동기적인 코드 실행이 무조건 잘못된 것은 아니다. 앞서도 말했지만 모든 코드는 동기적인 처리가 기본이다. 코드는 순서가 정말 중요하기 때문이다. 앞선 코드의 결과가 반드시 다음에서 사용되어야 하는 경우, 동기적으로 처리되어야만 한다. 비동기 코드라고 한다면, 앞선 처리를 뒤의 코드가 기다리게 해줘야 한다. (await)
동기적인 코드도 코드를 기반으로 '동시'적으로 여러 스레드에서 처리할 수는 있다. 다만, 정말 의미 없을 수 있는게 '동기적 처리'이기 때문에 다른 스레드는 앞선 업무가 끝나길 기다려야 하는건 마찬가지다. 그래서 이 경우에는 보통 main 스레드에서 어차피 일을 처리한다.