비동기 함수를 하나만 사용할 때에는 그냥 completionHandler를 사용하면 되지만,
비동기 함수를 여러개 호출 후 가장 마지막에 끝나는 함수가 종료된 후에 completionHandler를 호출시킬 수 있다.
어떤 상황이냐면,

비동기 함수를 하나만 호출할 때는 completionHandler에 함수가 종료된 후 호출될 함수를 넣어주면 정상적으로 동작하지만,
여러개의 비동기 함수를 호출했을 때(for문을 써서 호출하는 등)의 상황을 보면 저런식일거다.

비동기함수는 먼저 호출한 함수가 먼저 종료된다는 보장도 되어있지 않고, for문을 이용해서 같은 함수를 보낸다면 어떤 한 함수에만 completionHandler를 넣어주는것도 이상하고 원하는 대로 작동한다는 보장도 되어있지 않을것이다.
그렇다고 모든함수에 completionHandler를 넣어주면 호출한 함수의 개수만큼 여러번 호출될것이다.
이럴때에는 DispatchGroup을 이용할 수 있다.
DispatchQueue를 통해서 비동기함수를 호출할 때는 직접 group에 넣어줄 수 있지만, 비동기적으로 작동하도록 이미 지정되어 group을 지정할 수 없는 함수들(firebase나 urlsession 등등)은 enter(), leave()함수를 이용할 수 있다. 후자만 설명하겠다.

사용법은 사진과 같은 구조로 이루어진다.
각 비동기 함수를 호출할 때에는 enter()를 호출해서 DispatchGroup에게 아직 모든 함수가 종료되지 않았음을 알린다.
그리고 각 비동기함수에 completionHandler가 있을것이므로 completionHandler에 leave()를 호출해서 자신의 함수가 끝났음을 알린다.
그러면 DispatchGroup은 모든 함수가 종료되었음을 알고 notify함수를 호출해 completionHandler와 같은 동작을 해줄 수 있다.
코드로 살펴보면,
let dispatchGroup = DispatchGroup() for i in 0..<4 { dispatchGroup.enter() fetchPost(forPost: document.documentID) { post in dispatchGroup.leave() } } dispatchGroup.notify(queue: .main) { print("모든 함수 종료되었을 때 호출할 함수") }