Singleton Design Parttern

🤔 Flutter에서 정말 Thread Safety가 필요할까?

많은 개발자들이 "Flutter는 Single Thread 아닌가?"라고 생각하지만, 실제로는 여러 상황에서 동시성 문제가 발생할 수 있습니다.

📍 동시성 문제가 발생하는 실제 상황들

1. async/await를 사용한 비동기 초기화

가장 흔한 상황입니다. 데이터베이스나 API 클라이언트를 초기화할 때:

dart

class ApiClient {
  static ApiClient? _instance;
  
  static Future<ApiClient> getInstance() async {
    if (_instance == null) {
      // 네트워크 호출이나 파일 I/O 등 시간이 걸리는 작업
      await Future.delayed(Duration(seconds: 1));
      _instance = ApiClient._internal();
    }
    return _instance!;
  }
}

// 문제 상황: 앱 시작 시 여러 화면에서 동시 호출
void main() async {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            UserProfile(),  // ApiClient.getInstance() 호출
            PostList(),     // ApiClient.getInstance() 호출 (동시에!)
            CommentList(),  // ApiClient.getInstance() 호출 (동시에!)
          ],
        ),
      ),
    );
  }
}

결과: 3개의 서로 다른 ApiClient 인스턴스가 생성될 수 있음!

2. Isolate를 사용하는 경우

dart

3. 앱 라이프사이클 이벤트 처리

dart

⚡ 실제 테스트로 문제 확인하기

dart

🎯 상황별 해결책

1. Completer를 사용한 해결 (권장)

dart

2. 동기적 생성 + 비동기 초기화 (심플)

dart

3. GetIt 패키지 사용 (최고 권장)

yaml

dart

4. Provider와 함께 사용

dart

📊 성능 비교와 권장사항

🎯 결론 및 Best Practice

권장사항

  1. 간단한 싱글톤: 동기적 생성 + 별도 초기화 메서드

  2. 복잡한 의존성: GetIt 패키지 사용

  3. 앱 시작 시: 모든 싱글톤을 미리 초기화

  4. 테스트: 항상 동시성 테스트 코드 작성

피해야 할 것

  1. async/await 없이 비동기 싱글톤 구현

  2. 앱 실행 중 동적 싱글톤 생성

  3. Isolate간 싱글톤 공유 시도

💡 핵심 포인트

Flutter는 Single Thread이지만, async/await와 Future를 사용하면 동시성 문제가 발생할 수 있습니다. 특히 앱 시작 시 여러 위젯에서 동시에 싱글톤을 요청할 때 Race Condition이 발생하기 쉽습니다.

Last updated