πŸ“š Today I Learned 22회차 - Dart Factory μƒμ„±μž 🏭

4 λΆ„ μ†Œμš”

μ•ˆλ…•ν•˜μ„Έμš”! μ˜€λŠ˜μ€ Flutter κ°œλ°œμžλ“€μ΄ 데이터 λͺ¨λΈμ„ λ‹€λ£¨κ±°λ‚˜ λ””μžμΈ νŒ¨ν„΄μ„ κ΅¬ν˜„ν•  λ•Œ ν•„μˆ˜μ μœΌλ‘œ μ‚¬μš©ν•˜λŠ” Dart μ–Έμ–΄μ˜ νŠΉλ³„ν•œ μƒμ„±μž ν‚€μ›Œλ“œ, factory에 λŒ€ν•΄ 정리해 λ³΄κ² μŠ΅λ‹ˆλ‹€.

일반적인 μƒμ„±μžμ™€ factory μƒμ„±μžλŠ” 겉보기엔 λΉ„μŠ·ν•˜μ§€λ§Œ, λ‚΄λΆ€μ μœΌλ‘œλŠ” 객체λ₯Ό μƒμ„±ν•˜λŠ” 방식과 역할에 큰 차이가 μžˆμŠ΅λ‹ˆλ‹€.

🎯 ν•™μŠ΅ λͺ©ν‘œ

  • Factory μƒμ„±μžμ˜ κ°œλ…κ³Ό 일반 μƒμ„±μžμ™€μ˜ 차이점 μ΄ν•΄ν•˜κΈ°
  • Factory μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜λŠ” 3κ°€μ§€ 핡심 이유 ν•™μŠ΅ν•˜κΈ°
  • 싱글톀 νŒ¨ν„΄, JSON νŒŒμ‹±, μ„œλΈŒν΄λž˜μŠ€ λ°˜ν™˜ λ“± μ‹€μ œ ν™œμš© 사둀 νŒŒμ•…ν•˜κΈ°
  • Factory μƒμ„±μžλ₯Ό ν™œμš©ν•œ μ‹€μ „ μ½”λ“œ μž‘μ„± 읡히기

πŸ“š μ£Όμš” λ‚΄μš©

πŸ“– 1. factoryλž€ 무엇인가?

factory μƒμ„±μžλŠ” μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” 과정을 직접 μ œμ–΄ν•  수 μžˆλŠ” μƒμ„±μžμž…λ‹ˆλ‹€. μ΄λŠ” μƒμ„±μžμ²˜λŸΌ λ³΄μ΄μ§€λ§Œ, μ‹€μ œλ‘œλŠ” ν•΄λ‹Ή 클래슀 μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜λŠ” 정적 λ©”μ„œλ“œ(Static Method)와 μœ μ‚¬ν•˜κ²Œ μž‘λ™ν•©λ‹ˆλ‹€.

πŸ’‘ 핡심 포인트: 일반 μƒμ„±μžμ™€ 달리, factory μƒμ„±μžλŠ” 호좜될 λ•Œ λ°˜λ“œμ‹œ μƒˆ μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 μ˜λ¬΄κ°€ μ—†λ‹€λŠ” μ μž…λ‹ˆλ‹€.

⭐ 2. factoryλ₯Ό μ‚¬μš©ν•˜λŠ” 결정적인 이유 3κ°€μ§€

factory ν‚€μ›Œλ“œκ°€ μ—†λ‹€λ©΄ ν•΄κ²°ν•˜κΈ° μ–΄λ ΅κ±°λ‚˜ λΉ„νš¨μœ¨μ μΈ 상황듀이 μžˆμŠ΅λ‹ˆλ‹€.

β‘  μΈμŠ€ν„΄μŠ€ 캐싱 및 싱글톀 κ΅¬ν˜„ (객체 μž¬ν™œμš©) ♻️

λ©”λͺ¨λ¦¬ 효율이 μ€‘μš”ν•  λ•Œ, λ™μΌν•œ 객체λ₯Ό 맀번 μƒˆλ‘œ λ§Œλ“œλŠ” λŒ€μ‹  이미 μ‘΄μž¬ν•˜λŠ” 객체λ₯Ό μž¬μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • 일반 μƒμ„±μž: 호좜될 λ•Œλ§ˆλ‹€ 무쑰건 μƒˆ 객체 생성.
  • factory μƒμ„±μž: λ‚΄λΆ€ λ‘œμ§μ„ 톡해 이미 μƒμ„±λœ μΈμŠ€ν„΄μŠ€(예: 싱글톀 μΈμŠ€ν„΄μŠ€)κ°€ μžˆλŠ”μ§€ ν™•μΈν•˜κ³ , μžˆλ‹€λ©΄ μƒˆλ‘œ λ§Œλ“€μ§€ μ•Šκ³  κΈ°μ‘΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

β‘‘ μ™ΈλΆ€ 데이터(JSON) 처리 및 λ³΅μž‘ν•œ μ΄ˆκΈ°ν™” 둜직 βš™οΈ

μ™ΈλΆ€ 데이터(특히 JSON Map)λ₯Ό 클래슀 객체둜 λ³€ν™˜ν•˜λŠ” 과정은 데이터 검증, νƒ€μž… λ³€ν™˜(int -> double), κΈ°λ³Έκ°’ ν• λ‹Ή λ“± λ³΅μž‘ν•œ λ‘œμ§μ„ μš”κ΅¬ν•©λ‹ˆλ‹€.

  • 일반 μƒμ„±μž: λ³΅μž‘ν•œ λ‘œμ§μ„ μƒμ„±μžμ—μ„œ μ²˜λ¦¬ν•˜κΈ° μ–΄λ ΅κ³ , return 문을 μ‚¬μš©ν•  수 μ—†μ–΄ μœ μ—°μ„±μ΄ λ–¨μ–΄μ§‘λ‹ˆλ‹€.
  • factory μƒμ„±μž: ν•¨μˆ˜ λ³Έλ¬Έ 내에 if/else와 같은 λͺ¨λ“  μ œμ–΄ 흐름 및 λ‘œμ§μ„ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 데이터가 λˆ„λ½λ˜κ±°λ‚˜ 잘λͺ»λœ νƒ€μž…μœΌλ‘œ 듀어왔을 λ•Œ μ•ˆμ „ν•˜κ²Œ 처리 ν›„ λͺ…μ‹œμ μœΌλ‘œ returnν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ’‘ μ‹€μ œ ν™œμš© 사둀: Product.fromJsonκ³Ό 같은 νŒ¨ν„΄μ—μ„œ json['key'] ?? 'κΈ°λ³Έκ°’' λ˜λŠ” νƒ€μž… μΊμŠ€νŒ…μ„ 톡해 μ•ˆμ „ν•œ 객체 생성이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

β‘’ 쑰건뢀 μ„œλΈŒν΄λž˜μŠ€ μΈμŠ€ν„΄μŠ€ λ°˜ν™˜ (좔상화) 🧱

νŒ©ν† λ¦¬ μƒμ„±μžλŠ” 호좜된 클래슀 μžμ²΄κ°€ μ•„λ‹Œ, ν•΄λ‹Ή 클래슀λ₯Ό 상속받은 λ‹€λ₯Έ ꡬ체적인 μ„œλΈŒν΄λž˜μŠ€μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • μ˜ˆμ‹œ: μƒμœ„ 좔상 클래슀의 factory μƒμ„±μžλ₯Ό ν˜ΈμΆœν–ˆμ„ λ•Œ, μž…λ ₯된 쑰건(νŒŒλΌλ―Έν„°)에 따라 μžμ‹ 클래슀(Circle, Square λ“±) 쀑 ν•˜λ‚˜λ₯Ό μ„ νƒν•˜μ—¬ λ°˜ν™˜ν•¨μœΌλ‘œμ¨ 생성 λ‘œμ§μ„ 좔상화할 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ“Š 3. 일반 μƒμ„±μž vs Factory μƒμ„±μž 비ꡐ

ꡬ뢄 일반 μƒμ„±μž factory μƒμ„±μž
객체 생성 항상 μƒˆ μΈμŠ€ν„΄μŠ€ 생성 κΈ°μ‘΄ μΈμŠ€ν„΄μŠ€ λ°˜ν™˜ κ°€λŠ₯
핡심 μ—­ν•  ν•„λ“œ μ΄ˆκΈ°ν™” 생성 ν”„λ‘œμ„ΈμŠ€ μ œμ–΄ 및 관리
ν‚€μ›Œλ“œ μ—†μŒ factory
λ©”μ„œλ“œ λ³Έλ¬Έ μ œν•œμ  (μ΄ˆκΈ°ν™” 리슀트) 자유둜운 둜직 및 return μ‚¬μš© κ°€λŠ₯
λ°˜ν™˜ νƒ€μž… ν˜„μž¬ 클래슀만 μ„œλΈŒν΄λž˜μŠ€ λ°˜ν™˜ κ°€λŠ₯

πŸ’» μ‹€μŠ΅ 및 예제

πŸ”§ 예제 1: 싱글톀 νŒ¨ν„΄ κ΅¬ν˜„

// 싱글톀 νŒ¨ν„΄ - μ•± μ „μ—­μ—μ„œ ν•˜λ‚˜μ˜ μΈμŠ€ν„΄μŠ€λ§Œ μ‚¬μš©
class Logger {
  static final Logger _instance = Logger._internal();

  // Private μƒμ„±μž
  Logger._internal();

  // Factory μƒμ„±μž - 항상 λ™μΌν•œ μΈμŠ€ν„΄μŠ€ λ°˜ν™˜
  factory Logger() {
    return _instance;
  }

  void log(String message) {
    print('[LOG] $message');
  }
}

void main() {
  var logger1 = Logger();
  var logger2 = Logger();

  // λ™μΌν•œ μΈμŠ€ν„΄μŠ€μΈμ§€ 확인
  print(identical(logger1, logger2)); // true
  logger1.log('싱글톀 νŒ¨ν„΄ ν…ŒμŠ€νŠΈ'); // [LOG] 싱글톀 νŒ¨ν„΄ ν…ŒμŠ€νŠΈ
}

πŸ”§ 예제 2: JSON 데이터 νŒŒμ‹±

// JSON 데이터λ₯Ό μ•ˆμ „ν•˜κ²Œ 객체둜 λ³€ν™˜
class Product {
  final String name;
  final double price;
  final String category;

  Product({
    required this.name,
    required this.price,
    required this.category,
  });

  // Factory μƒμ„±μžλ‘œ JSON νŒŒμ‹± 및 검증
  factory Product.fromJson(Map<String, dynamic> json) {
    // 데이터 검증 및 κΈ°λ³Έκ°’ 처리
    return Product(
      name: json['name'] ?? 'Unknown Product',
      price: (json['price'] ?? 0).toDouble(), // intλ₯Ό double둜 λ³€ν™˜
      category: json['category'] ?? 'General',
    );
  }
}

void main() {
  // 정상 데이터
  var product1 = Product.fromJson({
    'name': 'Flutter Book',
    'price': 25000,
    'category': 'Books'
  });

  // λΆˆμ™„μ „ν•œ 데이터 - κΈ°λ³Έκ°’μœΌλ‘œ μ•ˆμ „ν•˜κ²Œ 처리
  var product2 = Product.fromJson({'name': 'Widget'});

  print('${product2.name}, ${product2.price}, ${product2.category}');
  // 좜λ ₯: Widget, 0.0, General
}

πŸ”§ 예제 3: 쑰건뢀 μ„œλΈŒν΄λž˜μŠ€ λ°˜ν™˜

// 좔상 클래슀
abstract class Shape {
  void draw();

  // Factory μƒμ„±μž - νƒ€μž…μ— 따라 λ‹€λ₯Έ μ„œλΈŒν΄λž˜μŠ€ λ°˜ν™˜
  factory Shape(String type) {
    if (type == 'circle') {
      return Circle();
    } else if (type == 'square') {
      return Square();
    }
    throw ArgumentError('Unknown shape type: $type');
  }
}

class Circle implements Shape {
  @override
  void draw() {
    print('β—‹ 원을 κ·Έλ¦½λ‹ˆλ‹€');
  }
}

class Square implements Shape {
  @override
  void draw() {
    print('β–‘ μ‚¬κ°ν˜•μ„ κ·Έλ¦½λ‹ˆλ‹€');
  }
}

void main() {
  // λ™μΌν•œ μƒμ„±μž ν˜ΈμΆœμ΄μ§€λ§Œ λ‹€λ₯Έ νƒ€μž…μ˜ 객체 λ°˜ν™˜
  Shape shape1 = Shape('circle');
  Shape shape2 = Shape('square');

  shape1.draw(); // β—‹ 원을 κ·Έλ¦½λ‹ˆλ‹€
  shape2.draw(); // β–‘ μ‚¬κ°ν˜•μ„ κ·Έλ¦½λ‹ˆλ‹€
}

πŸ” 심화 ν•™μŠ΅

πŸ€” Factory μƒμ„±μž ν™œμš© 팁

1. 캐싱 μ΅œμ ν™”

  • 자주 μ‚¬μš©λ˜λŠ” κ°μ²΄λŠ” Map에 μ €μž₯ν•˜μ—¬ μž¬μ‚¬μš©
  • λ©”λͺ¨λ¦¬ νš¨μœ¨μ„± ν–₯상

2. λΆˆλ³€ κ°μ²΄μ™€μ˜ μ‘°ν•©

  • const μƒμ„±μžμ™€ ν•¨κ»˜ μ‚¬μš©ν•˜μ—¬ 컴파일 νƒ€μž„ μƒμˆ˜ ν™œμš©
  • μ„±λŠ₯ μ΅œμ ν™” κ°€λŠ₯

3. 넀이밍 μ»¨λ²€μ…˜

  • fromJson, fromMap: 데이터 λ³€ν™˜μš©
  • create, build: λ³΅μž‘ν•œ 생성 둜직용

⚠️ μ£Όμ˜μ‚¬ν•­: Factory μƒμ„±μžλŠ” κ°•λ ₯ν•˜μ§€λ§Œ κ³Όλ„ν•œ μ‚¬μš©μ€ μ½”λ“œ λ³΅μž‘λ„λ₯Ό 높일 수 μžˆμŠ΅λ‹ˆλ‹€. λ‹¨μˆœν•œ κ²½μš°μ—λŠ” 일반 μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 더 λͺ…ν™•ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ“ 마무리

βœ… 였늘 배운 것

  • Factory μƒμ„±μžμ˜ 핡심: 객체 생성 과정을 μ œμ–΄ν•˜λ©°, 항상 μƒˆ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€ ν•„μš”κ°€ μ—†μŒ
  • 3κ°€μ§€ μ£Όμš” ν™œμš© 사둀:
    • ♻️ 싱글톀 νŒ¨ν„΄: λ©”λͺ¨λ¦¬ 효율적인 μΈμŠ€ν„΄μŠ€ μž¬μ‚¬μš©
    • βš™οΈ JSON νŒŒμ‹±: μ•ˆμ „ν•œ 데이터 λ³€ν™˜ 및 검증
    • 🧱 μ„œλΈŒν΄λž˜μŠ€ λ°˜ν™˜: μœ μ—°ν•œ 객체 생성 νŒ¨ν„΄
  • 일반 μƒμ„±μžμ™€μ˜ 차이: FactoryλŠ” return λ¬Έκ³Ό λ³΅μž‘ν•œ 둜직 μ‚¬μš© κ°€λŠ₯
  • μ‹€μ „ μ½”λ“œ: Logger 싱글톀, Product.fromJson, Shape νŒ©ν† λ¦¬ νŒ¨ν„΄ κ΅¬ν˜„

πŸš€ λ‹€μŒ κ³„νš

  1. Freezed νŒ¨ν‚€μ§€: Factory μƒμ„±μžλ₯Ό μžλ™μœΌλ‘œ μƒμ„±ν•΄μ£ΌλŠ” μ½”λ“œ 생성 라이브러리 ν•™μŠ΅
  2. λ””μžμΈ νŒ¨ν„΄ 심화: Factory Method νŒ¨ν„΄, Abstract Factory νŒ¨ν„΄ 적용
  3. λΆˆλ³€ 객체(Immutable): const μƒμ„±μžμ™€ Factory의 μ‘°ν•©μœΌλ‘œ μ„±λŠ₯ μ΅œμ ν™”
  4. μ‹€μ „ ν”„λ‘œμ νŠΈ: API 응닡 데이터 λͺ¨λΈλ§μ— Factory μƒμ„±μž μ μš©ν•˜κΈ°

λŒ“κΈ€λ‚¨κΈ°κΈ°