completionHandler를 DispatchQueue.main 말고 다른 스레드에서 실행하면 되므로,
response의 매개변수로 실행하고자하는 queue를 얹으면 된다.
-> 기본적으로 Alamofire의 completion는 main에서 동작하지만
네트워크를 연쇄적으로 호출해야 한다면? 그럼 completion이 main으로 오는게 비효율적일 것 같다.
let semaphore = DispatchSemaphore(value: 0)
let queue = DispatchQueue.global(qos: .utility)
AF.request("url").response(queue: queue) { response in
//~~로직처리~~
semaphore.signal()
}
semaphore.wait()
FINPO 앱 개발도중 한 기기에서 로그인하면 다른 기기에서 데이터를 불러올 수 없는 현상이 발생했다.
원인을 알아내기까지 꽤나 오랜시간이 걸렸는데 문제는 다음과 같다.
여기서 문제가 발생했다.
한 기기로 로그인 - HomeViewCotroller - JWT 재발급 - Data Request/Response 로직은 수행되었지만
다른 기기로 다시 시뮬을 돌리게 되면 HomeViewController에서 데이터를 읽어올 수 없었다.
분명히 액세스 토큰/리프레시 토큰이 키체인에 저장되어 있고, 인터셉터도 잘 동작했는데 의문이었다.
원인은 리프레시 토큰은 로그인할 경우 재발급되어 이전의 리프레시 토큰은 사용할 수 없는 이유였다.
-> 한 기기에서 로그인/로그아웃의 경우 리프레시가 재발급되어 해당 기기의 키체인에 저장되지만, 다른 기기의 경우 리프레시 토큰과는 다른 것
같은 팀원이자 나의 정신적 선배인 지누크(AOS)에게 해결방안을 물어봤다.
앱 실행 시 JWT 재발급 API를 호출하여 결과에 따라 View를 다르게 뿌려주면 되는 것!
기존의 코드는 키체인의 액세스토큰 object 유무만 판단하여 View를 옮겼지만, 그 전에 통신을 해서 결과값에 따라 바꿔주면 된다.
하지만 여기서 또 문제가 발생했다.
네트워크 통신은 비동기로 작동하게 되는데, 통신 함수 호출 후 바로 그 다음 조건으로 분기되기 때문에 통신 결과를 기다리지 않고 코드가 절차적으로 넘어갔다.
Alamofire를 사용해서 비동기 통신을 동기화 하는 방법을 찾아봤다.
옛날에 공부했던 Semaphore가 있었는데...
// 비동기 처리의 동기화 -> 세마포어 사용
// 초기값을 0으로 가지는 세마포어 생성
let semaphore = DispatchSemaphore(value: 0)
// 세마포어를 대기시키는 함수
// wait() -> semaphore의 value 값이 감소되어 value 값만큼 대기
semephore.wait()
// 세마포어의 value를 1 증가시키는 함수
// 초기값이 0이었던 세마포어는 이 함수를 통해 value가 1인 세마포어가 됨
// signal() 호출 시 대기중에서 깨어남
semaphore.signal()
이를 통해 Alamofire 네트워크 통신 시 스레드에 락을 걸어 동기화시키려고 했다.
지금까진 좋았다...
하지만 터미널을 보니 네트워크 통신 함수 호출 후 런치스크린에서 넘어가질 않는다...ㅋ
(일명 데드락)
구글신께서 알려주시길
Alamofire의 completionHandler ({}) 부분이 DispatchQueue.main에서 실행되기 때문에, 같은 DispatchQueue.main으로 semaphore.wait()을 실행하게 되면 completionHandler의 실행까지 멈추게 되는 데드락현상이 발생