๐Ÿ›ธ [Flutter&Dart] copyWith์— NULL ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

Tygerยท2024๋…„ 3์›” 3์ผ
1

Flutter

๋ชฉ๋ก ๋ณด๊ธฐ
54/57

๐Ÿ›ธ copyWith์— NULL ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

copyWith๋กœ Nullable ๋ณ€์ˆ˜ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์ด๋ฒˆ์—๋Š” Flutter์—์„œ class ์‚ฌ์šฉ์‹œ ๊ฐ์ฒด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ธ copyWith ์‚ฌ์šฉ์‹œ NULL ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

์ด์ „์— ์ด๋ฏธ copyWith๋กœ Nullable ๋ณ€์ˆ˜ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ ํฌ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•œ ์ ์ด ์žˆ์—ˆ๋Š”๋ฐ, ์—…๋ฐ์ดํŠธ ๋‚ด์šฉ์ด ์žˆ์–ด ์ถ”๊ฐ€๋กœ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

copyWith๋Š” Flutter์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ class copy ๋ฐฉ๋ฒ•์œผ๋กœ ์ด๋ฏธ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค.

copyWith๋Š” ๋ชจ๋ธ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“œ๋Š” ํŒจํ‚ค์ง€์— ํฌํ•จ๋˜๋Š” ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ด๋ฉฐ, ํ”ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” TextStyle์ด copyWith๋ฅผ ์‚ฌ์šฉํ•ด ์Šคํƒ€์ผ ๋ณ€๊ฒฝ์„ ํ•˜๋„๋ก ๊ฐœ๋ฐœ๋˜์–ด ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ชจ๋ธ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ์—๋Š” copyWith ์™ธ์—๋„ json ๋ณ€ํ™˜ ๋“ฑ์˜ ๊ธฐ๋Šฅ๋„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์ด๋ฅผ Code Generator๋กœ ํŽธํ•˜๊ฒŒ ์ƒ์„ฑํ•ด ์ฃผ๋Š” ํŒจํ‚ค์ง€์ธ freezed๊ฐ€ ์žˆ์œผ๋‹ˆ ํ•œ ๋ฒˆ์ฏค ์‚ฌ์šฉํ•ด ๋ณด๋Š” ๊ฒƒ๋„ ์ข‹๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” freezed ๋“ฑ์˜ ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ง์ ‘ class๋ฅผ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์˜ copyWith์— ๋Œ€ํ•ด์„œ ๋‹ค๋ฃจ๋Š” ๋‚ด์šฉ์ด ๋  ๊ฒƒ์ด๋‹ค.

copyWith ?

๋จผ์ €, copyWith์— NULL ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” copyWith๋ฅผ ์–ด๋–ป๊ฒŒ ๋งŒ๋“œ๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

๋Œ€๋ถ€๋ถ„์ด copyWith ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๊ณผ ์ž‘๋™ ๋ฐฉ์‹์— ๋Œ€ํ•ด ์ดํ•ดํ•˜๊ณ  ์žˆ์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•˜์ง€๋งŒ, ํ˜น์‹œ๋ผ๋„ ๋ชจ๋ฅด์‹œ๋Š” ๋ถ„๋“ค์ด ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ค„๋ณด์ž.

์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์ด ์žˆ๋Š” User ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ๋„๋ก ํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ์ด๋ฆ„์„ nullable ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด ์ฃผ๋„๋ก ํ•˜์ž.

class User {
  final String email;
  final String? name;
  
  const User({
    required this.email,
    required this.name,
  });
}

ํ•ด๋‹น User ํด๋ž˜์Šค์— copyWith๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์ž.

copyWith๋Š” ์ƒ์„ฑ๋œ User ํด๋ž˜์Šค์—์„œ ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ nullable ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด null์ธ ๊ฒฝ์šฐ ๊ธฐ์กด User ํด๋ž˜์Šค์˜ ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•˜๋Š” ์›๋ฆฌ๋ฅผ ํ†ตํ•ด์„œ copy ๊ธฐ๋Šฅ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

User copyWith({
	final String? email,
    final String? name,
  }){
    return User(
      email: email ?? this.email,
      name: name ?? this.name,
    );
  }

์ด์ œ copyWith๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ณ€๊ฒฝํ•ด ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

User _tyger = User(email: "tyger@tyger.com", name: "tyger");
_tyger = _tyger.copyWith(email: "tyger1@tyger.com");
print(_tyger.email);

// tyger1@tyger.com

copyWith๋Š” ํ•ด๋‹น ํด๋ž˜์Šค ์•ˆ์— ๋„ฃ์–ด์„œ ์„ ์–ธํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

class User {
  final String email;
  final String? name;
  const User({
    required this.email,
    required this.name,
  });

  User copyWith({
    final String? email,
    final String? name,
  }) {
    return User(
      email: email ?? this.email,
      name: name ?? this.name,
    );
  }
}

NULL

์™œ copyWith๋Š” Nullable ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ผ๊นŒ?

์ •ํ™•ํžˆ๋Š” Nullable ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉ์„ ๋ชปํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ, null ๊ฐ’์œผ๋กœ๋Š” ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€ํ•˜๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

์œ„์—์„œ copyWith ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๋ฉด ๋ชจ๋“  ๋ณ€์ˆ˜๋ฅผ nullable ํƒ€์ž…์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๊ธฐ์กด ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋Š” ๊ตฌ์กฐ๋กœ ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ธฐ ๋•Œ๋ฌธ์— null ๊ฐ’์„ ๋„ฃ์–ด๋„ null๋กœ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ณ  ๊ธฐ์กด ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์— nullable ํƒ€์ž…์— ๊ฐ’์ด ์ง€์ •๋˜๋ฉด ๋‹ค์‹œ null๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด๋‹ค.

User tyger = User(email: "tyger@tyger.com", name: "tyger");
tyger = tyger.copyWith(name: null);
print(tyger.name);

// tyger

์ด ๋ถ€๋ถ„์˜ name์ด null์ด๋ฉด, this.name์„ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋˜๋ฉด์„œ null ๊ฐ’์œผ๋กœ ๋‹ค์‹œ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด๋‹ค.

name: name ?? this.name,

copyWith ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๊ณผ ์›๋ฆฌ์— ๋Œ€ํ•ด์„œ๋Š” ๊ฐ€๋ณ๊ฒŒ ์•Œ์•„ ๋ดค๊ณ , ์™œ null ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”์ง€์— ๋Œ€ํ•ด์„œ๋„ ์•Œ์•„ ๋ดค๋‹ค.

์ด์ œ๋ถ€ํ„ฐ๋Š” null ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ฒ ๋‹ค.

1,2 ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ์ด์ „ ํฌ์ŠคํŠธ์—์„œ ๋‹ค๋ค„๋ณธ ๋‚ด์šฉ์ด๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ์†Œ๊ฐœํ•˜๋Š” 3๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ด ์ƒˆ๋กœ ์—…๋ฐ์ดํŠธํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.

copyWith๋กœ Nullable ๋ณ€์ˆ˜ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

1. Function

์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ Function์„ ์‚ฌ์šฉํ•ด null ๊ฐ’์„ ์ฃผ๋Š” ๋ฐฉ๋ฒ•์ธ๋ฐ, ์‚ฌ์‹ค ์ œ๊ฐ€ ๊ฐ€์žฅ ์ถ”์ฒœํ•˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์ด๊ธฐ๋„ ํ•˜๋‹ค.

copyWith์—์„œ nullable ๋ณ€์ˆ˜์— function ๊ฐ’์„ ํ—ˆ์šฉํ•˜์—ฌ null์ธ ๊ฒฝ์šฐ function์„ ์ „๋‹ฌ ๋ฐ›๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

Product ๊ฐ์ฒด์˜ sale ๋ณ€์ˆ˜๋ฅผ nullable ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด ์ฃผ๋„๋ก ํ•˜์ž.

class Product {
  final String name;
  final int price;
  final double? sale;

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

copyWith์— nullable ํƒ€์ž…์— ํ•ด๋‹นํ•˜๋Š” sale ๋ณ€์ˆ˜์˜ ๊ฐ’์„ Function ํ•จ์ˆ˜ ํ˜•ํƒœ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•ด์ฃผ์ž.

Fuction()์„ ์ „๋‹ฌ ๋ฐ›๋Š”๋ฐ, ๋ฆฌํ„ด ๊ฐ’์œผ๋กœ double? ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•˜๊ณ  ์žˆ๋‹ค.

sale ๋ถ€๋ถ„์˜ sale ๊ฐ’์ด null์ด ์•„๋‹Œ ๊ฒฝ์šฐ function()์ธ sale()์„ ๋„ฃ์–ด์ฃผ๊ณ , null์ด๋ฉด ๊ธฐ์กด ๊ฐ’์„ ์‚ฌ์šฉํ•ด ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก this.sale์„ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์—ˆ๋‹ค.

Product copyWith({
	final String? name,
	final int? price,
	final double? Function()? sale,
  }) {
	return Product(
      name: name ?? this.name,
      price: price ?? this.price,
      sale: sale != null ? sale() : this.sale,
    );
}

์ด๋ ‡๊ฒŒ ์ƒ์„ฑํ•˜์—ฌ copyWith์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ์—๋„ function ํ•จ์ˆ˜ ํ˜•ํƒœ๋กœ ์ „๋‹ฌํ•˜๋ฉด ๋œ๋‹ค.

์ด ๋ฐฉ๋ฒ•์˜ ๊ฐ€์žฅ ํฐ ๋‹จ์ ์€ ๋ณ€์ˆ˜ ํƒ€์ž…์„ ์ „๋‹ฌํ•  ์ˆ˜ ์—†๊ณ , ํ•ญ์ƒ function ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ copyWith์—์„œ NULL ๊ฐ’์„ ์ „๋‹ฌํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ๋ณ€์ˆ˜ ํƒ€์ž…์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ์ ์—์„œ ์ถ”์ฒœํ•˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

void main() {
  Product _product = Product(
    name: "product",
    price: 1000,
    sale: 0.2,
  );
  print(_product.sale);
  // 0.2
  
  _product = _product.copyWith(sale: () => 0.5);
  print(_product.sale);
  // 0.5
  
  _product = _product.copyWith(sale: () => null);
  print(_product.sale);
  // null
}

2. Variable

์ด๋ฒˆ์—๋Š” null ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ถ”๊ฐ€ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

๋™์ผํ•˜๊ฒŒ Product ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์ž.

copyWith ์ƒ์„ฑ์‹œ nullable ํƒ€์ž… ๋ณ€์ˆ˜์—๋Š” boolean ํƒ€์ž…์˜ ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜ ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์—ˆ๋‹ค.

boolean ํƒ€์ž…์˜ nullSale ๋ณ€์ˆ˜๋ฅผ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ false๋กœ ์ฃผ์–ด ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

sale ๋ณ€์ˆ˜์— nullSale์ด true์ธ ๊ฒฝ์šฐ์— null ๊ฐ’์„ ์ฃผ๊ณ , ์•„๋‹Œ ๊ฒฝ์šฐ์—๋Š” ๋‹ค๋ฅธ ๋ณ€์ˆ˜๋“ค๊ณผ ๋™์ผํ•˜๊ฒŒ sale ?? this.sale ๊ฐ’์„ ์ฃผ๋„๋ก ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

  Product copyWith({
    final String? name,
    final int? price,
    final double? sale,
    final bool nullSale = false,
  }) {
    return Product(
      name: name ?? this.name,
      price: price ?? this.price,
      sale: nullSale ? null : (sale ?? this.sale),
    );
  }

sale ๋ณ€์ˆ˜์— null ๊ฐ’์„ ์ฃผ๊ณ  ์‹ถ์œผ๋ฉด, ์ถ”๊ฐ€ํ•œ nullSale ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ true๋กœ ์ฃผ๋ฉด ๋œ๋‹ค.

์ด ๋ฐฉ๋ฒ•์˜ ๋‹จ์ ์€ ๋ณ€์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค๋Š” ์ ์— ์žˆ๋‹ค. ๋งŒ์ผ nullable ํƒ€์ž…์˜ ๋ณ€์ˆ˜๊ฐ€ ๋งŽ๋‹ค๋ฉด boolean ํƒ€์ž…์˜ ๋ณ€์ˆ˜๋ฅผ ๋˜‘๊ฐ™์ด ์ƒ์„ฑํ•ด ์ฃผ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๊ฒŒ ๋œ๋‹ค.

void main() {
  Product _product = Product(
    name: "product",
    price: 1000,
    sale: 0.2,
  );
  print(_product.sale);
  // 0.2

  _product = _product.copyWith(nullSale: true);
  print(_product.sale);
  // null

  _product = _product.copyWith(sale: 0.4);
  print(_product.sale);
  // 0.4
}

๋˜ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ null์ผ ์ˆ˜๋„ null์ด ์•„๋‹ ์ˆ˜๋„ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ boolean ํƒ€์ž…์—๋„ ์ค‘๋ณต์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด ๋‹จ์ ์ด๋‹ค.

double? getSale(double? data) => data;

์ด๋ ‡๊ฒŒ null์ผ์ง€ ์•„๋‹์ง€ ๋ชจ๋ฅด๋Š” ์ผ€์ด์Šค์˜ ๋ณ€์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ ์ž ํ•  ๋•Œ์—, ํ•ญ์ƒ ๊ธฐ์กด sale ๋ณ€์ˆ˜์™€ null ๊ฐ’์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ nullSale boolean ๋ณ€์ˆ˜๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ฒŒ ๋œ๋‹ค.

void main() {
  Product _product = Product(
    name: "product",
    price: 1000,
    sale: 0.2,
  );
  print(_product.sale);
  // 0.2

  double? _newSale = getSale(null);

  _product = _product.copyWith(sale: _newSale, nullSale: _newSale == null);
  print(_product.sale);
  // null
}

3. Abstract class

์ด๋ฒˆ์— ์ถ”๊ฐ€๋˜๋Š” ์ƒˆ๋กœ์šด ๋ฐฉ๋ฒ•์ด ๊ฐ€์žฅ ์ถ”์ฒœํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

์œ„์—์„œ ์‚ดํŽด๋ณธ ๋ฐฉ๋ฒ• ์ฒ˜๋Ÿผ function() ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ณ , boolean ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

๊ธฐ์กด ๊ฐ์ฒด ๊ทธ๋Œ€๋กœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋งŽ๋‹ค๋Š” ์ ์ด ๋‹จ์ ์ผ ์ˆ˜๋„ ์žˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ ์กฐ๊ธˆ ๋ณต์žกํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์ถ”์ƒ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๊ฐœ๋…์ด ์žˆ์–ด์•ผ ํ•ด์„œ ์ดํ•ด๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์ฒœ์ฒœํžˆ ํ•˜๋‚˜์”ฉ ํ•ด๋ณด๋ฉด์„œ ์ดํ•ดํ•˜์‹œ๋ฉด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

์ถ”์ƒ ํด๋ž˜์Šค์™€ ํด๋ž˜์Šค๋ฅผ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ํ˜ธ์ถœ๋˜๋„๋ก ํ•˜๋Š” method๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ธ๋ฐ, ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์ ์—์„œ ๋ฉ”๋ฆฌํŠธ๊ฐ€ ๊ฐ€์žฅ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ๋ณธ๋‹ค.

๋จผ์ € ๊ฐ์ฒด๋ฅผ ๋™์ผํ•˜๊ฒŒ ์ƒ์„ฑํ•ด ๋ณด๋„๋ก ํ•˜์ž.

class Product {
  final String name;
  final int price;
  final double? sale;

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

์ด๋ฒˆ์—” copyWith ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๊ธฐ ์ „, ProductCopyWith๋ผ๋Š” ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ๋จผ์ € ์ƒ์„ฑํ•ด์ฃผ๋„๋ก ํ•˜๊ฒ ๋‹ค.

์ถ”์ƒ ํด๋ž˜์Šค์— ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” call() ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

abstract class ProductCopyWith {
  Product call({
    String name,
    int price,
    double? sale,
  });
}

์ƒ์„ฑํ•œ ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ implements๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ์†๋ฐ›์€ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๋„๋ก ํ•˜์ž.

ProductCopyWith ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›๋Š” ๊ฐ์ฒด๋Š” _ProductCopyWith ๋ผ๋Š” ๊ฐ์ฒด๋กœ ์ƒ์„ฑํ•ด ์ฃผ๋„๋ก ํ•˜๊ฒ ๋‹ค.

์—ฌ๊ธฐ์„œ ์ถ”์ƒ ํด๋ž˜์Šค๋‚˜ ์ƒ์† ๋ฐ›์€ ํด๋ž˜์Šค์˜ ์ด๋ฆ„์€ ์ž์œ ๋กญ๊ฒŒ ํ•ด๋„ ๋œ๋‹ค. ๊ผญ ์ € ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๋Š” ๋ฒ•์€ ์—†์œผ๋‹ˆ, ํŽธํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ํด๋ž˜์Šค ๋ช…์„ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค.

class _ProductCopyWith implements ProductCopyWith {
	// ...
}

์ƒ์† ๋ฐ›์€ _ProductCopyWith ํด๋ž˜์Šค์—๋Š” Product ๊ฐ์ฒด์™€ Object ํƒ€์ž…์˜ ๊ฐ์ฒด 2๊ฐœ๋ฅผ ์ถ”๊ฐ€ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

class _ProductCopyWith implements ProductCopyWith {
  final Product value;
  static const Object _undefined = Object(); 
  
  const _ProductCopyWith(this.value);
}

_ProductCopyWith ๊ฐ์ฒด์— ์ƒ์†๋ฐ›์€ call() ๊ธฐ๋Šฅ์„ ์žฌ์ •์˜ํ•ด ์ฃผ๋„๋ก ํ•˜์ž.

abstract class์—์„œ call() ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•  ๋•Œ์— name, price, sale์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ๋” ์ƒ์„ฑํ–ˆ์—ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ํƒ€์ž…์„ Object nullable ํƒ€์ž…์œผ๋กœ ํ•ด์ฃผ๊ณ , ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ _undefined๋ฅผ ์ •์˜ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

์ž…๋ ฅ๋ฐ›์€ ๋ณ€์ˆ˜๊ฐ€ _undefined์™€ ๊ฐ™๋‹ค๋ฉด _ProductCopyWith ์—์„œ ๋ฐ›์•„์˜จ Product์˜ ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋ฉด ๋˜๊ณ , ์•„๋‹Œ ๊ฒฝ์šฐ์—๋Š” copy() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’์„ ๋ฆฌํ„ดํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์ด ๋•Œ Object๋ฅผ ํƒ€์ž… ์บ์ŠคํŠธํ•˜์—ฌ ๋ฆฌํ„ดํ•ด์•ผ ํ•œ๋‹ค.

  
  Product call({
    Object? name = _undefined,
    Object? price = _undefined,
    Object? sale = _undefined,
  }) {
    return Product(
      name: name == _undefined ? value.name : name as String,
      price: price == _undefined ? value.price : price as int,
      sale: sale == _undefined ? value.sale : sale as double?,
    );
  }

์ง€๊ธˆ๊นŒ์ง€ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ nullable ํƒ€์ž…์˜ ๋ณ€์ˆ˜์— null ๊ฐ’์„ ์ฃผ๋ฉด ๋œ๋‹ค.

void main() {
  Product _product = Product(name: "product", price: 1000, sale: 0.2);
  print(_product.sale);
  // 0.2

  _product = _product.copyWith(sale: null);
  print(_product.sale);
  // null
  
  _product = _product.copyWith(sale: 0.1);
  print(_product.sale);
  // 0.1
}

์ด๋ฒˆ์— ์‚ดํŽด๋ณธ ๋ฐฉ๋ฒ•์€ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•˜๊ณ  ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ๋‹จ์ ์ผ ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ๊ฐ€์žฅ ์‹ฌํ”Œํ•˜๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์ ์—์„œ ๊ฐ€์žฅ ์ถ”์ฒœํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

Source Code

์ „์ฒด ์ฝ”๋“œ์ด๋‹ค.

class Product {
  final String name;
  final int price;
  final double? sale;

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

  ProductCopyWith get copyWith => _ProductCopyWith(this);
}

abstract class ProductCopyWith {
  Product call({
    String name,
    int price,
    double? sale,
  });
}

class _ProductCopyWith implements ProductCopyWith {
  final Product value;
  static const Object _undefined = Object();

  const _ProductCopyWith(this.value);

  
  Product call({
    Object? name = _undefined,
    Object? price = _undefined,
    Object? sale = _undefined,
  }) {
    return Product(
      name: name == _undefined ? value.name : name as String,
      price: price == _undefined ? value.price : price as int,
      sale: sale == _undefined ? value.sale : sale as double?,
    );
  }
}

๋งˆ๋ฌด๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ class์˜ copyWith ๊ธฐ๋Šฅ์„ ์ง์ ‘ ์ƒ์„ฑํ•  ๋•Œ์—, nullable ํƒ€์ž…์˜ ๋ณ€์ˆ˜์— NULL ๊ฐ’์ด ์ „๋‹ฌ๋˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ NULL ์‚ฌ์šฉ์— ๋Œ€ํ•œ 3๊ฐ€์ง€์˜ ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ดค๋‹ค.

์ด์ „ ๊ธ€์—์„œ 2๊ฐ€์ง€์˜ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์ด๋ฏธ ์‚ดํŽด๋ดค๊ณ , ์ถ”๊ฐ€๋กœ ์ƒˆ๋กœ์šด ๋ฐฉ๋ฒ•์„ ์ œ์‹œํ•˜๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ, ์ œ๊ฐ€ ๊ฐ€์žฅ ์ถ”์ฒœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ด๋ฒˆ์— ์ถ”๊ฐ€๋œ ๋ฐฉ๋ฒ•์ด๊ธด ํ•˜๋‚˜ ๊ฐœ๋ฐœ์ž ๋ณธ์ธ์˜ ์„ ํƒ์œผ๋กœ ์›ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋œ๋‹ค.

์ถ”๊ฐ€๋กœ ์ƒˆ๋กœ์šด ๋ฐฉ๋ฒ•์„ ์ฐพ๊ฒŒ ๋˜๋ฉด ๊ณต์œ ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

profile
Flutter Developer

0๊ฐœ์˜ ๋Œ“๊ธ€