반응형
Flutter 개발을 위한 Dart 언어 기초 시리즈 두 번째 시간입니다. 함수 작성부터 객체 지향 프로그래밍까지, 실무에서 꼭 알아야 할 내용들을 다룹니다.
이 글에서 배우는 것
- 함수 선언과 호출의 모든 것
- void vs 반환값이 있는 함수
- 클래스와 객체의 기본 개념
- 생성자의 다양한 형태
- 상속과 메서드 오버라이드
- Dart만의 독특한 문법 특징
함수(Function): 코드를 재사용 가능하게 만들기
반환값이 없는 함수 (void)
void greetUser(String name, int age) {
print('안녕하세요! $name님, $age살이시군요.');
// return 문이 없음 (void이므로)
}
// 사용법
greetUser('홍길동', 25); // 안녕하세요! 홍길동님, 25살이시군요.
void는 "아무것도 반환하지 않는다"는 의미입니다. 주로 출력, 데이터 수정 등의 작업에 사용합니다.
반환값이 있는 함수
int addNumbers(int a, int b) {
return a + b; // 계산 결과를 반환
}
// 사용법
int sum = addNumbers(10, 20);
print('10 + 20 = $sum'); // 10 + 20 = 30
실용적인 계산 함수 예제
// BMI = 체중(kg) / 키(m)²
double calculateBMI(double weight, double height) {
double heightInMeters = height / 100; // cm를 m로 변환
return weight / (heightInMeters * heightInMeters);
}
// 사용법
double result = calculateBMI(70, 175);
print('BMI: ${result.toStringAsFixed(2)}'); // BMI: 22.86
클래스(Class): 객체의 설계도 만들기
클래스는 데이터(속성) + 기능(메서드)를 하나로 묶는 객체 지향 프로그래밍의 핵심입니다.
기본 클래스 구조
class Person {
// 속성(필드) - 클래스가 가지는 데이터
String name; // 이름
int age; // 나이
// 생성자 - 객체를 만들 때 호출되는 특별한 함수
Person(this.name, this.age);
// 메서드 - 클래스가 가지는 행동/기능
void introduce() {
print('안녕하세요! 저는 $name이고, $age살입니다.');
}
void haveBirthday() {
age++; // 나이 1 증가
print('생일축하합니다! 이제 $age살이 되었습니다.');
}
}
객체 생성과 사용
// 객체 생성 (인스턴스 만들기)
Person person1 = Person('김철수', 30);
// 메서드 호출
person1.introduce(); // 안녕하세요! 저는 김철수이고, 30살입니다.
person1.haveBirthday(); // 생일축하합니다! 이제 31살이 되었습니다.
person1.introduce(); // 안녕하세요! 저는 김철수이고, 31살입니다.
Dart의 독특한 생성자 문법
// ✅ Dart 스타일: 간결한 생성자
Person(this.name, this.age);
// 다른 언어 스타일이라면:
Person(String name, int age) {
this.name = name;
this.age = age;
}
this.name은 "매개변수를 바로 속성에 할당하라"는 Dart만의 축약 문법입니다.
상속(Inheritance): 기존 클래스 확장하기
상속 클래스 정의
class Student extends Person { // Person의 모든 기능을 물려받음
String major; // 전공 (Student만의 고유 속성)
String? grade; // 성적 (null일 수 있음)
// 기본 생성자
Student(String name, int age, this.major) : super(name, age);
// ↑ 부모 생성자 호출
// Named 생성자 - 성적까지 포함해서 생성
Student.withGrade(String name, int age, this.major, this.grade)
: super(name, age);
}
핵심: : 와 super()의 역할
다른 언어와 다른 Dart의 독특한 문법입니다.
// Java/C# 방식이라면:
Student(String name, int age, String major) {
super(name, age); // 본문 안에서 호출
this.major = major;
}
// Dart 방식: 본문 실행 전에 초기화
Student(String name, int age, this.major) : super(name, age);
// ↑ 초기화 리스트
실행 순서
Student생성자 매개변수 받음: super(name, age)- 부모 클래스 먼저 초기화this.major- Student의 고유 속성 초기화- 생성자 본문 실행 (있다면)
메서드 오버라이드
class Student extends Person {
String major;
Student(String name, int age, this.major) : super(name, age);
// @override: 부모 클래스의 메서드를 재정의한다는 표시
@override
void introduce() {
super.introduce(); // 부모의 introduce() 먼저 호출
print('전공은 $major입니다.'); // 추가 정보 출력
}
// Student만의 고유 메서드
void study() {
print('$name 학생이 $major를 공부하고 있습니다.');
}
}
사용 예제
// 기본 생성자 사용
Student student1 = Student('이영희', 20, '컴퓨터공학');
student1.introduce(); // 부모 + 자식 메서드 모두 실행
student1.study(); // Student만의 메서드
// Named 생성자 사용
Student student2 = Student.withGrade('박민수', 22, '경영학', 'A+');
student2.introduce();
student2.showGrade(); // 성적 표시
Named 생성자: 다양한 생성 방법 제공
class Student extends Person {
String major;
String? grade;
// 1. 기본 생성자
Student(String name, int age, this.major) : super(name, age);
// 2. Named 생성자 - 성적 포함
Student.withGrade(String name, int age, this.major, this.grade)
: super(name, age);
// 3. Named 생성자 - 편입생용
Student.transfer(String name, int age, this.major, int transferYear)
: super(name, age) {
print('$transferYear년에 편입한 $name 학생입니다.');
}
}
// 사용법
Student freshMan = Student('김신입', 19, '컴공');
Student withGrade = Student.withGrade('이우등', 21, '경영', 'A+');
Student transfer = Student.transfer('박편입', 23, '물리', 2023);
null 체크와 안전한 코드
class Student extends Person {
String major;
String? grade; // nullable
Student.withGrade(String name, int age, this.major, this.grade)
: super(name, age);
void showGrade() {
if (grade != null) { // null 체크
print('현재 성적: $grade');
} else {
print('아직 성적이 없습니다.');
}
}
// 삼항 연산자 활용
String getGradeStatus() {
return grade != null ? '성적: $grade' : '성적 미입력';
}
}
실무에서 자주 사용하는 패턴들
1. Factory 생성자 패턴
class ApiResponse {
final bool success;
final String message;
final dynamic data;
ApiResponse(this.success, this.message, this.data);
// Factory 생성자 - 성공/실패 응답 생성
factory ApiResponse.success(dynamic data) {
return ApiResponse(true, 'Success', data);
}
factory ApiResponse.error(String message) {
return ApiResponse(false, message, null);
}
}
// 사용법
ApiResponse successResponse = ApiResponse.success({'users': []});
ApiResponse errorResponse = ApiResponse.error('서버 오류 발생');
2. Getter와 Setter
class Rectangle {
double _width; // private 속성 (언더스코어로 시작)
double _height;
Rectangle(this._width, this._height);
// Getter - 속성값 가져오기
double get area => _width * _height;
double get perimeter => 2 * (_width + _height);
// Setter - 속성값 설정하기
set width(double value) {
if (value > 0) _width = value;
}
set height(double value) {
if (value > 0) _height = value;
}
}
// 사용법
Rectangle rect = Rectangle(10, 5);
print('넓이: ${rect.area}'); // 50
print('둘레: ${rect.perimeter}'); // 30
rect.width = 15; // setter 호출
print('새 넓이: ${rect.area}'); // 75
클래스 설계 베스트 프랙티스
1. 단일 책임 원칙
// ✅ Good: 각 클래스가 하나의 역할만
class User {
String name;
String email;
User(this.name, this.email);
bool isValidEmail() => email.contains('@');
}
class UserService {
List<User> users = [];
void addUser(User user) => users.add(user);
User? findUser(String email) => users.firstWhere((u) => u.email == email);
}
// ❌ Bad: 너무 많은 책임
class UserEverything {
// 사용자 데이터 + 네트워킹 + 파일 저장 + 이메일 발송...
}
2. 생성자 활용
class BankAccount {
final String accountNumber; // 변경 불가능한 계좌번호
double _balance; // private 잔액
// 기본 생성자
BankAccount(this.accountNumber, this._balance);
// Named 생성자 - 신규 계좌
BankAccount.newAccount(this.accountNumber) : _balance = 0;
// Named 생성자 - VIP 계좌
BankAccount.vip(this.accountNumber) : _balance = 1000000;
double get balance => _balance;
void deposit(double amount) {
if (amount > 0) _balance += amount;
}
}
함수 vs 메서드 차이점
| 구분 | 함수 | 메서드 |
|---|---|---|
| 위치 | 클래스 외부 | 클래스 내부 |
| 호출 방식 | 함수명() |
객체.메서드명() |
| 데이터 접근 | 매개변수만 | 클래스 속성 접근 가능 |
| 예시 | calculateBMI() |
person.introduce() |
반응형
'flutter' 카테고리의 다른 글
| Flutter는 어떻게 작동할까? 아키텍처와 네이티브 개발 완전 분석 (0) | 2025.09.22 |
|---|---|
| Flutter 시작하기: 크로스 플랫폼 앱 개발의 첫걸음 (0) | 2025.09.22 |
| Dart 기초 마스터하기 #4 List와 Map으로 데이터 다루기 (0) | 2025.09.21 |
| Dart 기초 마스터하기 #3 조건문과 반복문으로 프로그램 제어하기 (0) | 2025.09.21 |
| Dart 기초 마스터하기 #1 변수와 데이터 타입 완전 정복 (0) | 2025.09.21 |