Semaphore를 사용한 네트워크 통신 동기화

Donghee Lee·2022년 8월 24일
2

iOS-Swift

목록 보기
16/20
post-thumbnail

Semaphore를 사용한 네트워크 통신 동기화


바쁜 사람들을 위한 해결책

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 앱 개발도중 한 기기에서 로그인하면 다른 기기에서 데이터를 불러올 수 없는 현상이 발생했다.

원인을 알아내기까지 꽤나 오랜시간이 걸렸는데 문제는 다음과 같다.

  1. 앱 실행 시 SceneDelegate에서 AccessToken의 Object가 존재하는지 체크
  2. 없을 경우 LoginViewController로 이동
  3. 있는 경우 HomeViewController에서 네트워크 통신 시작
  4. 이 때 만료된 액세스토큰으로 접근할 경우 RequestInterceptor를 사용하여 키체인에 저장된 리프레시 토큰으로 JWT 재발급
  5. 갱신된 JWT로 HomeViewController의 데이터 리퀘스트 요청

여기서 문제가 발생했다.

한 기기로 로그인 - 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의 실행까지 멈추게 되는 데드락현상이 발생

참고

진의

qiita

profile
Better than Yesterday

0개의 댓글