[Swift] Swift 동시성 사용하기

2026. 2. 9. 19:08·Apple/Swift
728x90
반응형

안녕하세요! 오웬입니다 🎵

 

최근에 개발을 하면서 동시성에 관련한 오류나 경고를 많이 보고 있는데 정작 이런 동시성에 관한 문제가 왜 생기는 건지 제대로 이해를 못 하고 있는 것 같고 AI에 너무 의존하고 있는 것 같아서 제대로 공부를 해보려고 합니다.

 

해당 내용에 대해서 나도 제대로 공부해보고 싶다 하시는 분들은 아래 링크 참고하면 좋을 것 같습니다!

https://developer.apple.com/kr/videos/play/wwdc2025/268/

 

Swift 동시성 사용하기 - WWDC25 - 비디오 - Apple Developer

Swift의 주요 동시성 개념을 알아보세요. 동시성은 앱 반응성과 성능을 향상하는 데 도움이 되고 Swift는 비동기성 및 동시성 코드를 올바르게 작성하기 쉽도록 설계되었습니다. 단일 스레드에서

developer.apple.com

 

Swift 동시성 모델은 단순히 "코드를 동시에 실행하는 것"이 목표가 아닙니다.

 

"어떻게 하면 안전하고 예측 가능하게 실행 위치를 제어할 것인가?"가 핵심입니다. 

 

전통적인 프로그래밍에서는 멀티스레드 환경에서 공유 데이터에 접근할 때 런타임에 발생하는 Data Race가 가장 큰 문제였습니다.

@MainActor

Swift는 이를 해결하기 위해서 @MainActor라는 개념을 도입했습니다.

 

메인 스레드와 그 데이터는 MainActor로 표현되고 MainActor에는 동시성이 적용되지 않는데 메인 스레드에서만 이를 실행할 수 있기 때문입니다. 

 

Swift는 MainActor 코드가 메인 스레드에서만 실행되고 거기서만 그 데이터에 액세스하도록 보장합니다. 

  • 격리(Isolation): 클래스에 @MainActor를 선언하면, 해당 클래스의 모든 프로퍼티와 메서드는 메인 액터에 격리됩니다.
  • 컴파일 타임 체크: 외부에서 이 데이터에 접근하려고 하면 컴파일러가 에러를 냅니다. 
@MainActor
class ProfileViewModel {
    var userName: String = "Guest" // 이 데이터는 메인 스레드 전용

    func updateName(to newName: String) {
        self.userName = newName
    }
}

// 만약 백그라운드 스레드에서 ProfileViewModel에 직접 접근하려 한다면?
// 컴파일러 에러: "Main actor-isolated property 'userName' can't be referenced from a non-isolated context"

 

 

 

비동기

우리가 만드는 앱은 기본적으로 메인 스레드라는 곳에서 실행이 됩니다. 메인 스레드는 2가지의 일을 동시에 담당합니다.

  1. 사용자의 터치 입력 받기 및 화면 그리기 (UI)
  2. 데이터 계산, 파일 읽기, 네트워크 통신 (로직)

근데 여기서 문제가 발생합니다.

 

만약 우리가 네트워크에서 이미지를 가져오는 함수를 만들었는데 이 함수가 동기 방식으로 실행하면 어떻게 될까요?

import Foundation

struct Image {
}

final class View {
  func displayImage(_ image: Image) {
  }
}

final class ImageModel {
  var imageCache: [URL: Image] = [:]
  let view = View()

  func fetchAndDisplayImage(url: URL) throws {
    let (data, _) = try URLSession.shared.data(from: url)
    let image = decodeImage(data)
    view.displayImage(image)
  }

  func decodeImage(_ data: Data) -> Image {
    Image()
  }
}

final class Library {
  static let shared: Library = Library()
}

 

URL이 주어지면 URLSession API로 네트워크를 통해 데이터를 가져옵니다. 메인 스레드에서 이 메서드를 실행하면 데이터 다운로드를 마칠 때까지 UI가 정지됩니다. 그렇게 되면 사용자는 "앱이 멈췄다"고 느끼게 됩니다.

개발자에게는 앱의 반응성 유지가 중요합니다. 즉, UI가 끊기거나 멈출 정도로 오랫동안 메인 스레드를 점유하지 않도록 주의해야 합니다.

 

Swift 동시성은 네트워크 요청과 같은 네트워크 요청 등 데이터가 오기를 기다릴 때 메인 스레드를 점유하지 않고 비동기 작업을 사용합니다. 멈춤을 방지하려면 네트워크 접근이 비동기여야 합니다.

import Foundation

class Image {
}

final class View {
  func displayImage(_ image: Image) {
  }
}

final class ImageModel {
  var imageCache: [URL: Image] = [:]
  let view = View()

  func fetchAndDisplayImage(url: URL) async throws {
    let (data, _) = try await URLSession.shared.data(from: url)
    let image = decodeImage(data)
    view.displayImage(image)
  }

  func decodeImage(_ data: Data) -> Image {
    Image()
  }
}

final class Library {
  static let shared: Library = Library()
}

 

비동기 호출을 처리하도록 fetchAndDisplayImage를 변경하려면 함수를 'async'로 정의하고 'await'로 URL session API를 호출해야 합니다. await는 함수가 중단되는 경우를 나타내며 현재 스레드에서 대기 중인 이벤트가 발생할 때까지 실행이 중지된 다음 실행이 재개됩니다.

 

지금까지 코드는 비동기 함수 하나만 실행하고 있습니다. 비동기 함수는 작업에서 실행되고 작업은 다른 코드와는 독립적으로 실행되며 특정 연산을 처음부터 끝까지 수행하도록 만들어집니다.

  func onTapEvent() {
    Task {
      do {
	try await fetchAndDisplayImage(url: url)
      } catch let error {
        displayError(error)
      }
    }
  }

위 코드를 보면 버튼 누르기 이벤트에 답하여 작업을 생성합니다. 이 경우 작업은 전체 fetch-and-display image 연산을 수행합니다.

 

하지만 우리가 만드는 앱에는 비동기 작업이 여러 개일 수 있습니다.

 

fetch-and-display image 작업 외에 뉴스를 가져와서 표시한 다음 새로 고칠 때까지 대기하는 작업을 또 하나 추가해 보겠습니다.

각 작업은 처음부터 끝까지 순서대로 연산을 완료합니다. Fetch는 백그라운드에서 실행되지만 다른 연산은 모두 메인 스레드에서 한 번에 하나의 연산만 실행됩니다.

 

각 작업은 서로 간에 독립적이므로 메인 스레드에서 교대로 실행됩니다. 메인 스레드는 실행 준비가 되는 대로 각 작업의 일부를 실행합니다.

 

단일 스레드가 여러 작업을 번갈아가며 '교차 실행'하면 리소스를 가장 효율적으로 사용함으로써 성능이 향상됩니다. 스레드는 단일 연산을 대기하는 동안 스레드를 유휴 상태로 두기보다는 가능한 한 빨리 아무 작업이든 진행합니다. 

Fetch news전에 Fetch image가 완료되면 메인 스레드는 뉴스를 표시하기 전에 이미지를 디코딩하여 표시합니다. 

Fetch news가 먼저 끝난다면 이미지를 디코딩하기 전에 뉴스를 먼저 표시합니다.

 

 

앱이 여러 개의 독립적인 연산을 동시에 수행해야 할 때는 여러 개의 비동기 작업이 유리한데 이 부분은 다음 포스팅에서 다루도록 하겠습니다!


감사합니다.

 

잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!

궁금하신 부분은 댓글로 질문 부탁드립니다!

728x90
반응형

'Apple > Swift' 카테고리의 다른 글

[Swift] 3D 스캔 앱을 로컬 서버와 연결하기  (2) 2025.12.30
[Swift] SwiftData 모델 구조 변경 시 런타임 에러 해결하기  (0) 2025.10.13
[Swift] 앱 인텐트 알아보기  (5) 2025.09.03
[Swift] Multipeer Connectivity 톺아보기  (5) 2025.08.23
[Swift] Swift Testing 톺아보기  (6) 2025.07.02
'Apple/Swift' 카테고리의 다른 글
  • [Swift] 3D 스캔 앱을 로컬 서버와 연결하기
  • [Swift] SwiftData 모델 구조 변경 시 런타임 에러 해결하기
  • [Swift] 앱 인텐트 알아보기
  • [Swift] Multipeer Connectivity 톺아보기
P_Piano
P_Piano
Apple 생태계 개발자가 되기 위한 학습과 경험의 기록
    반응형
    250x250
  • P_Piano
    피피아노의 개발 일지
    P_Piano
  • 전체
    오늘
    어제
    • 분류 전체보기 (223)
      • Apple (141)
        • iOS (25)
        • visionOS (5)
        • Swift (73)
        • UIKit (2)
        • SwiftUI (25)
        • RxSwift (2)
        • Xcode (5)
        • Metal (2)
      • C언어 (5)
      • C++ (8)
      • Dart (1)
      • Python (3)
      • JavaScript (17)
      • Git (1)
      • CS (39)
        • 디자인 패턴 (6)
        • 네트워크 (20)
        • 운영체제 (8)
        • Database (5)
        • 자료구조 (0)
      • IT 지식 (2)
      • IT 뉴스 (4)
      • 출처 표기 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    배열
    SWIFT
    UIKit
    ios
    티스토리챌린지
    Initializers
    네트워크
    디자인패턴
    코딩테스트
    프로그래머스
    연산자
    변수
    운영체제
    스위프트
    자바스크립트
    비동기
    Vision Pro
    프로퍼티 래퍼
    visionOS
    Apple
    swiftUI
    Xcode
    함수
    옵셔널
    이니셜라이저
    데이터베이스
    클래스
    오블완
    제어문
    프로세스
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
P_Piano
[Swift] Swift 동시성 사용하기
상단으로

티스토리툴바