iOS프로젝트/Funch(넥스터즈)

Swift Concurrency를 적용하면서 발생한 동시성 문제

lgvv 2024. 9. 20. 20:58

Swift Concurrency를 적용하면서 발생한 동시성 문제


사내에서도 동일한 이슈가 발생했었어서 해당 부분에 대해서 정리하고자 함.

기존 Combine으로 작성된 코드를 async-await으로 교체하는 작업을 진행.

 

 

글의 순서

  • Combine으로 작성된 코드 살펴보기
  • Swift Concurrency로 단계적으로 전환하기
  • Swift Concurrency로 안전성 확보하기
  • Combine에서 Swift Concurrency 적용

 

Combine으로 작성된 코드 살펴보기

기존 코드는 캐싱을 위해 캐시 프로퍼티가 존재하며, Combine을 기반으로 작성되어 있었음.

  • Combine을 선택한 이유는 RxSwift가 익숙하여, RxSwift와 닮은 애플 퍼스트파티 선택에서 Combine을 선택

 

기존 Combine 기반 코드

 

 

Swift Concurrency로 단계적으로 전환하기

 

레포지토리에 async 인터페이스 추가

 

asnyc을 메소드에서 Result로 반환하는 이유

  • Swift 6에서 부터는 throw에 에러 타입을 지정할 수 있으나, 그 이전 버전에서는 사용할 수 없음.
  • 기존 프로젝트를 Swift 6로 마이그레이션 하는데 시간이 걸리기 때문에 Swift 5.10 기준으로 작업을 위해 Result로 반환
  •  

Repository 인터페이스 수정

 

 

UseCase에도 async 인터페이스 추가

 

useCase에도 동일하게 추가해서 구현.

 

 

위의 코드에서는 불완전한 부분이 존재


외부 상황에 따라 매칭 부분이 동작하는 cache 프로퍼티가 읽기/쓰기 상황에 대한 데이터 경합이 발생하여 동시성 문제가 발생할 수 있음.

즉, cache 값이 데이터 경합 상황에서 안전하지 않음.

 

Swift Concurrency로 안전성 확보하기

 

데이터 경합 상황에 대한 안전성을 확보하기 위해 아래의 작업을 진행

  • `class`에서 `actor`로 변경
  • `actor` 변경함에 따라 UseCase 프로토콜을 개선
    • 향후 사용하지 않게 되면 삭제하면 좋고, SDK로 제공하는 서비스라면 버전에 따라 deprecated 혹은 unavailable 처리
  • Combine을 사용하는 곳은 Future + Task 조합을 활용하여 안전성 확보
    • 구현체는 새로 사용하는 한곳을 바라보도록 개선

안전성을 확보한 코드

 

해당 방법 외에 다른 방법은 어떤게 있을까?

  • 딕셔너리를 Swift에서 지원하는 타입이 아닌 actor 방식으로 동작할 수 있는 나만의 딕셔너리를 만들어서 사용할 경우 class로도 충분히 안정적으로 사용할 수 있음.
  • 다만, actor로 swift에서 지원해주고 있어서 actor로 변경하는게 더 낫다고 판단됨.

 

Combine에서 Swift Concurrency 적용

 

사용부는 아래처럼 달라짐.

 

기존 코드 변경점.