플러터(Flutter)는 구글이 개발한 오픈소스 UI 소프트웨어 개발 키트(SDK)로, 사용자가 현대적이고 고성능의 애플리케이션을 쉽게 개발할 수 있도록 돕습니다. 이 강좌에서는 객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 기본 개념인 클래스(class), 객체(object), 그리고 인스턴스(instance)에 대해 깊이 있게 다루어 보겠습니다. 이러한 개념들은 플러터 애플리케이션을 구조화하고 관리하는 데 필수적인 요소입니다.
1. 객체 지향 프로그래밍이란?
객체 지향 프로그래밍(OOP)은 프로그래밍 패러다임 중 하나로, 데이터를 객체라는 단위로 묶어 관리하는 방식을 말합니다. 객체는 상태(state)와 행동(behavior)을 포함하며, 이러한 객체를 정의하는 것이 바로 클래스입니다. OOP의 주요 특징은 다음과 같습니다:
- 캡슐화(encapsulation): 객체의 속성과 메서드를 묶어 외부로부터 내성을 제공
- 상속(inheritance): 기존 클래스를 바탕으로 새로운 클래스를 정의하여 코드 재사용을 촉진
- 다형성(polymorphism): 서로 다른 클래스가 동일한 행동을 할 수 있도록 인터페이스를 정의
- 추상화(abstraction): 복잡한 시스템을 효율적으로 단순화하여 다루기 쉽게 만듦
이제 이러한 OOP 개념이 플러터에서 어떻게 활용되는지 살펴보겠습니다.
2. 클래스(Class)
클래스는 객체를 생성하기 위한 틀(template)입니다. 클래스에는 데이터 변수를 정의할 수 있으며, 해당 데이터를 조작하는 방법을 정의하는 메서드가 포함될 수 있습니다. 플러터에서 클래스를 정의하는 방법은 다음과 같습니다:
class Car {
String color;
String model;
Car(this.color, this.model);
void display() {
print('차량 모델: $model, 색상: $color');
}
}
위의 예제에서 Car
라는 클래스를 정의했습니다. 이 클래스에는 color
와 model
이라는 두 개의 속성이 있으며, 생성자를 통해 초기화됩니다. display
메서드는 차량의 정보를 출력합니다. 다음으로, 이 클래스를 사용하여 객체를 생성해 보겠습니다.
3. 객체(Object)와 인스턴스(Instance)
객체는 클래스의 인스턴스를 의미합니다. 즉, 클래스로부터 생성된 실제 데이터 집합을 지칭합니다. 여러 개의 객체를 생성할 수 있으며, 각 객체는 고유한 상태를 가질 수 있습니다. 예를 들어, Car
클래스의 인스턴스를 다음과 같이 생성해 볼 수 있습니다:
void main() {
Car car1 = Car('빨간색', '스포츠카');
Car car2 = Car('파란색', '세단');
car1.display(); // 출력: 차량 모델: 스포츠카, 색상: 빨간색
car2.display(); // 출력: 차량 모델: 세단, 색상: 파란색
}
위 코드에서는 car1
과 car2
라는 두 개의 Car
객체를 생성했습니다. 각 객체는 생성 시 제공된 색상과 모델 정보를 저장하고 있으며, display
메서드를 호출함으로써 각 차량의 정보를 출력할 수 있습니다.
4. 클래스의 다양한 구성요소
클래스는 다양한 구성요소를 포함할 수 있습니다. 여기에는 생성자, 메서드, 필드, 접근제어자 등이 포함됩니다. 자세한 내용을 알아보겠습니다.
4.1 생성자(Constructor)
생성자는 객체가 생성될 때 호출되며 객체의 초기화를 담당합니다. Dart(플러터의 프로그래밍 언어)에서는 기본 생성자 외에도 명명된 생성자(named constructor)를 지원합니다:
class Person {
String name;
int age;
Person(this.name, this.age); // 기본 생성자
Person.named(this.name, this.age); // 명명된 생성자
}
명명된 생성자는 다른 초기화 방법을 제공하며, 예를 들어 Person.named('홍길동', 30)
처럼 사용할 수 있습니다.
4.2 메서드(Method)
메서드는 클래스 내에서 정의된 함수입니다. 객체가 수행할 행동을 정의합니다. 메서드는 클래스의 상태를 변경하거나, 연산을 수행할 수 있습니다:
class Animal {
String name;
Animal(this.name);
void speak() {
print('$name가 소리칩니다.');
}
}
4.3 필드(Field)
필드는 클래스에 속하는 데이터 변수를 의미합니다. 객체의 상태를 유지하는 데 사용됩니다. 필드는 인스턴스 변수(instance variable)와 정적 변수(static variable)로 나눌 수 있습니다:
class Circle {
static const double pi = 3.14; // 정적 변수
double radius; // 인스턴스 변수
Circle(this.radius);
}
4.4 접근 제어자(Access Modifier)
Dart에서는 다양한 접근 제어자를 통해 클래스의 필드와 메서드에 대한 접근 제한을 설정할 수 있습니다. 주로 public
과 private
개념이 사용됩니다. 예를 들어, 필드 앞에 _
를 붙이면 private으로 설정됩니다:
class BankAccount {
double _balance; // private 변수
BankAccount(this._balance);
void deposit(double amount) {
_balance += amount;
}
double get balance => _balance; // public 메서드
}
5. 클래스 상속(Inheritance)
상속은 기존 클래스를 기반으로 새로운 클래스를 만드는 기능입니다. 이를 통해 코드 재사용과 구조적 계층화를 더 쉽게 할 수 있습니다. 다음은 클래스 상속의 예입니다:
class Vehicle {
void start() {
print('차량 시작');
}
}
class Bike extends Vehicle {
void ringBell() {
print('자전거 벨 소리!');
}
}
위 예제에서 Bike
클래스는 Vehicle
클래스를 확장(상속)합니다. 즉, Vehicle
클래스의 메서드인 start
를 사용할 수 있습니다:
void main() {
Bike bike = Bike();
bike.start(); // 출력: 차량 시작
bike.ringBell(); // 출력: 자전거 벨 소리!
}
6. 다형성(Polymorphism)
다형성이란 서로 다른 클래스의 객체가 동일한 메서드를 호출할 수 있는 능력을 의미합니다. 이를 통해 코드의 유연성과 재사용성을 높일 수 있습니다. 예를 들어:
class Shape {
void draw() {
print('모양을 그립니다.');
}
}
class Circle extends Shape {
@override
void draw() {
print('원을 그립니다.');
}
}
class Square extends Shape {
@override
void draw() {
print('정사각형을 그립니다.');
}
}
여기서 다양한 모양의 타원과 정사각형이 Shape
클래스를 상속하고, 각 클래스에 대해 draw
메서드를 오버라이드하여 각 모양에 맞는 결과를 출력합니다.
결론
이번 강좌에서는 플러터에서의 클래스, 객체, 인스턴스 개념에 대해 구체적으로 살펴보았습니다. 객체 지향 프로그래밍은 앱 개발의 기반이 되는 중요한 개념으로, 플러터 애플리케이션의 구조를 이해하고 설계하는 데 매우 유용합니다. 앞으로의 강좌에서도 이러한 객체 지향 개념을 활용하여 실용적인 애플리케이션을 작성하는 방법을 계속 탐구해 보겠습니다.
이 글이 여러분의 플러터 학습에 도움이 되길 바랍니다!