이번 시간에는 copyWith 사용시 Nullable 타입의 변수를 사용하는 방법에 대해서 살펴보려고 한다.
copyWith는 class 복사 기능으로 객체의 변수를 변경하는데 사용된다. 하지만 copyWith 선언을 보면 Null 타입을 받을 수 없게 되어있고, 실제로도 Null 값을 주어도 값이 변하지 않는다는 것을 알 수 있다.
먼저 테스트를 위해 Person 객체를 하나 만들어 보자.
여기서 age, city 변수는 Nullable 타입으로 선언하였다.
class Person {
final int id;
final String name;
final int? age;
final String? city;
const Person({
required this.id,
required this.name,
this.age,
this.city,
});
}
로그 출력을 위해 Person 객체의 toString() 함수를 재정의 해주자.
toString() => "id : $id, name : $name : age : $age, city : $city";
tyger라는 Person 객체를 아래와 같이 생성해서 출력해보자. 정상적으로 출력이 된다.
void main() {
Person tyger = const Person(id: 1, name : "Tyger", age : 20, city : "Seoul");
print(tyger.toString());
}
// id : 1, name : Tyger : age : 20, city : Seoul
이번에는 copyWith를 추가해주도록 하자. 혹시라도 copyWith 기능에 대해서 잘 모르시는 분들은 아래 댓글 남겨주시면 copyWith에 대한 글도 따로 작성해서 공유하도록 하겠습니다.
Person copyWith({
final int? id,
final String? name,
final int? age,
final String? city,
}){
return Person(
id : id ?? this.id,
name : name ?? this.name,
age : age ?? this.age,
city : city ?? this.city,
);
}
이제 copyWith를 사용해서 tyger 변수의 id 값을 2로 변경해 주도록 하자. 잘 실행이 된다.
void main() {
Person tyger = const Person(id: 1, name: "Tyger", age: 20, city: "Seoul");
print(tyger.toString());
tyger = tyger.copyWith(id: 2);
print(tyger.toString());
// id : 1, name : Tyger : age : 20, city : Seoul
// id : 2, name : Tyger : age : 20, city : Seoul
}
그럼 이번엔 age의 값을 30으로 변경해 보도록 하자.
void main() {
Person tyger = const Person(id: 1, name: "Tyger", age: 20, city: "Seoul");
print(tyger.toString());
tyger = tyger.copyWith(age : 30);
print(tyger.toString());
}
// id : 1, name : Tyger : age : 20, city : Seoul
// id : 1, name : Tyger : age : 30, city : Seoul
이번에는 id, age 값을 각각 3, 40으로 변경해 보겠다. 문제 없이 잘 출력이 되고 있다.
void main() {
Person tyger = const Person(id: 1, name: "Tyger", age: 20, city: "Seoul");
print(tyger.toString());
tyger = tyger.copyWith(id : 3, age : 40);
print(tyger.toString());
}
// id : 1, name : Tyger : age : 20, city : Seoul
// id : 3, name : Tyger : age : 40, city : Seoul
지금까지는 별다른 문제가 없는데.. 이제부터가 문제다.
city 값을 null로 변경해 보자. city 값이 null로 변경되지 않고 여전히 "Seoul"로 되어있다.
왜 변하지 않는 걸까?
void main() {
Person tyger = const Person(id: 1, name: "Tyger", age: 20, city: "Seoul");
print(tyger.toString());
tyger = tyger.copyWith(city : null);
print(tyger.toString());
}
// id : 1, name : Tyger : age : 20, city : Seoul
// id : 1, name : Tyger : age : 20, city : Seoul
copyWith를 직접 생성해서 사용하시는 분들은 이런 상황을 경험해 보셨을 거다. 이러한 현상은 copyWith 기능을 만드는 부분에서 파악할 수 있다.
해당 부분을 보면 city 값이 없을 때에 this.city 값을 넣도록 되어 있기에, 우리는 객체를 수정할 때 원하는 값만 넣어서 수정해줄 수 있었던 것이다.
city: city ?? this.city,
그렇다면 Nullable 타입을 사용할 수 없는 걸까 ? 아니다. 사용할 수 있다. 다만 코드를 수정해 주어야 한다.
Nullable 타입을 사용하는 방법은 크게 2가지 정도가 있고, 그 외에도 개발자 각자의 스타일대로 객체를 생성해서 할 수도 있기 때문에 보편적으로 사용하는 2가지 방법만 살펴보도록 하겠다.
첫 번째 방법은 타입을 함수형으로 전달 하게끔 변경하는 방법이다. 방법부터 살펴보도록 하자.
기존 copyWith의 city 변수 부분만 수정해보자. city 변수를 함수 형태로 전달받게 되면 함수가 null을 리턴하게 null 값을 전달할 수 있게 된다.
Person copyWith({
final int? id,
final String? name,
final int? age,
final String? Function()? city,
}) {
return Person(
id: id ?? this.id,
name: name ?? this.name,
age: age ?? this.age,
city: city != null ? city() : this.city,
);
}
실제 사용할 때는 아래와 같이 사용해야 하는데, 이 부분이 단점이다 ㅠㅠ 함수 형태로 값을 전달해줘야 한다.
void main() {
Person tyger =
const Person(id: 1, name: "Tyger", age: 20, city: "Seoul");
print(tyger.toString());
tyger = tyger.copyWith(id:3, city: ()=>null);
print(tyger.toString());
}
// id : 1, name : Tyger : age : 20, city : Seoul
// id : 3, name : Tyger : age : 20, city : null
제가 가장 자주 사용하는 방법인데, 단순히 변수 하나를 추가해 주는 것이다.
nullCity라는 불리언 변수를 사용하는 방법인데, 사용할 때도 함수 형태보다 간단해서 주로 사용하고 있다.
Person copyWith({
final int? id,
final String? name,
final int? age,
final String? city,
final bool nullCity = false,
}) {
return Person(
id: id ?? this.id,
name: name ?? this.name,
age: age ?? this.age,
city: nullCity ? null : city ?? this.city,
);
}
city에 null 값을 주고 싶을 때는, "city : null"로 사용하지 않고 "nullCity : true" 라고만 값을 넣어줘도 null 값으로 변경시킬 수 있다.
void main() {
Person tyger = const Person(id: 1, name: "Tyger", age: 20, city: "Seoul");
print(tyger.toString());
tyger = tyger.copyWith(id: 2);
print(tyger.toString());
tyger = tyger.copyWith(id: 3, nullCity : true);
print(tyger.toString());
}
// id : 1, name : Tyger : age : 20, city : Seoul
// id : 2, name : Tyger : age : 20, city : Seoul
// id : 3, name : Tyger : age : 20, city : null
이 외에도 다양한 방법들이 있으며, 객체를 자유롭게 생성하여 사용할 수 있다. 이런 부분이 불필요하고 코드 작성이 너무 많다면, freezed와 같은 코드 제너레이터를 사용하면 간단하게 copyWith를 사용할 수 있다.