Menu
Coddy logo textTech

비동기 생성자

Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 79번째.

Dart의 생성자는 async로 표시될 수 없습니다. 생성자는 Future가 아니라 클래스의 인스턴스를 즉시 반환해야 하기 때문입니다. 그렇다면 데이터베이스에서 데이터를 로드하거나 설정을 가져오는 것과 같이 비동기 초기화가 필요한 객체는 어떻게 생성할까요?

해결 방법은 정적 비동기 팩토리 메서드를 사용하는 것입니다. 생성자를 private으로(또는 간단하게) 유지하고, 비동기 작업을 수행하고 해당 클래스의 Future를 반환하는 정적 메서드를 제공하세요:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // 프라이빗 생성자
  
  static Future<UserProfile> create(String userId) async {
    // 데이터 가져오기 시뮬레이션
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

프라이빗 생성자인 UserProfile._는 실제 객체 생성을 처리하며, 정적 create() 메서드는 모든 비동기 작업을 처리합니다. 호출자는 await를 사용하여 완전히 초기화된 객체를 얻습니다.

이 패턴은 객체를 확보한 시점에 즉시 사용할 수 있도록 보장합니다. 즉, 부분적으로 초기화된 상태나 누락된 데이터가 없습니다. 리소스를 로드하거나, 서비스에 연결하거나, 시간이 걸리는 설정을 수행해야 하는 클래스에서 흔히 사용됩니다.

challenge icon

챌린지

쉬움

클래스에서 비동기 초기화를 처리하는 방법을 보여주는 설정 로더(configuration loader)를 만들어 봅시다! 생성자는 비동기(async)가 될 수 없으므로, 데이터를 "로드"하는 데 시간이 필요한 객체를 생성하기 위해 정적 팩토리 메서드 패턴을 사용하게 됩니다.

코드를 두 개의 파일로 구성합니다:

  • config_loader.dart: 설정 값을 비동기적으로 로드하는 것을 시뮬레이션하는 ConfigLoader 클래스를 만듭니다. 클래스에는 다음이 포함되어야 합니다:
    • 세 개의 final 필드: appName (String), version (String), maxUsers (int)
    • 세 필드를 모두 초기화하는 private 생성자
    • String configId 매개변수를 받고 Future<ConfigLoader>를 반환하는 load라는 이름의 정적 비동기(static async) 팩토리 메서드. 이 메서드는 다음을 수행해야 합니다:
      • 로딩 시간을 시뮬레이션하기 위해 100밀리초 동안 Future.delayed()를 사용합니다.
      • configId를 기반으로 설정 값을 생성합니다:
        • appName: configId 뒤에 " App"을 추가합니다.
        • version: "1.0." 뒤에 configId의 길이를 붙입니다.
        • maxUsers: configId의 길이에 10을 곱합니다.
      • 이 값들을 사용하여 새로운 ConfigLoader 인스턴스를 반환합니다.
    • 설정 정보를 Config: [appName] v[version] (max [maxUsers] users) 형식으로 출력하는 displayInfo() 메서드
  • main.dart: 설정 로더를 임포트하고 비동기 초기화 패턴을 시연합니다:
    • Loading configurations...를 출력합니다.
    • configId가 "Production"인 설정을 로드하고 displayInfo()를 호출합니다.
    • configId가 "Dev"인 설정을 로드하고 displayInfo()를 호출합니다.
    • All configs loaded!를 출력합니다.

정적 load 메서드가 모든 비동기 작업을 처리하는 반면, private 생성자는 단순히 값을 할당하는 방식에 주목하세요. 호출자는 await를 사용하여 완전히 초기화된 객체를 얻게 됩니다!

예상 출력:

Loading configurations...
Config: Production App v1.0.10 (max 100 users)
Config: Dev App v1.0.3 (max 30 users)
All configs loaded!

치트 시트

Dart의 생성자는 Future가 아닌 클래스의 인스턴스를 즉시 반환해야 하므로 async로 표시할 수 없습니다.

비동기 초기화가 필요한 객체를 생성하려면 정적 비동기 팩토리 메서드(static async factory method)를 사용하세요:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Private constructor
  
  static Future<UserProfile> create(String userId) async {
    // Perform async work here
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

사용법:

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

_로 표시된 프라이빗 생성자는 객체 생성을 담당하고, 정적 메서드는 비동기 작업을 처리합니다. 이를 통해 객체가 사용되기 전에 완전히 초기화되도록 보장할 수 있습니다.

직접 해보기

import 'config_loader.dart';

void main() async {
  // TODO: "Loading configurations..."를 출력하세요
  
  // TODO: configId가 "Production"인 구성을 로드하세요
  // 그리고 그것의 displayInfo() 메서드를 호출하세요
  
  // TODO: configId가 "Dev"인 구성을 로드하세요
  // 그리고 그것의 displayInfo() 메서드를 호출하세요
  
  // TODO: "All configs loaded!"를 출력하세요
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨