📚 학습 기록/Dart & Flutter 기초

Dart 언어 마스터하기: 기초 개념부터 고급 패턴까지

zenjoydev 2025. 4. 20. 22:01

안녕하세요! 오늘은 플러터 개발의 기반이 되는 Dart 언어의 핵심 개념들을 정리해보려고 합니다. 자바 개발자이신 분들께서 Dart로 넘어오실 때 특히 유용한 정보들을 담았으니, 차근차근 따라오시면 Dart의 매력에 푹 빠지실 거예요!

1. 자료구조: 리스트(List)와 맵(Map)

리스트(List)

Dart에서 리스트는 자바의 ArrayList와 비슷하지만, 더 강력한 기능들을 제공합니다

// 리스트 선언과 초기화
List<int> numbers = [1, 2, 3, 4, 5];
List<String> fruits = ['사과', '바나나', '오렌지'];
List<dynamic> mixed = [1, '문자열', true, 4.5];

리스트의 강력한 메서드들

// forEach: 모든 요소 순회
fruits.forEach((fruit) => print('과일: $fruit'));

// map: 각 요소를 변환하여 새 리스트 생성
List<String> upperFruits = fruits.map((fruit) => fruit.toUpperCase()).toList();

// where: 조건에 맞는 요소만 필터링
List<int> evenNumbers = numbers.where((number) => number % 2 == 0).toList();

// reduce: 요소들을 누적하여 하나의 값으로 만들기
int sum = numbers.reduce((value, element) => value + element);

맵(Map)

키-값 쌍으로 데이터를 저장하는 자료구조로, 자바의 HashMap과 유사합니다.

// Map 선언과 초기화
Map<String, dynamic> person = {
  'name': '홍길동',
  'age': 30,
  'isEmployed': true,
  'skills': ['Flutter', 'Dart', 'Firebase']
};

// 값 접근하기
print('이름: ${person['name']}');

// 값 추가하기
person['email'] = 'hong@example.com';

// 여러 값 한번에 추가
person.addAll({'phone': '010-1234-5678', 'address': '서울시 강남구'});

// 키 존재 확인
print(person.containsKey('email'));

// Map 순회하기
person.forEach((key, value) {
  print('$key: $value');
});

// 값 삭제하기
person.remove('isEmployed');

2. 함수와 파라미터

Dart에서는 함수를 다루는 방식이 자바보다 훨씬 유연합니다.

함수 파라미터의 종류

1. 포지셔널 파라미터 (기본)

int addNumbers(int a, int b) {
  return a + b;
}
print(addNumbers(5, 3)); // 8

2. 옵셔널 파라미터

대괄호로 감싸고 기본값을 지정할 수 있습니다.

String greet(String name, [String greeting = '안녕하세요']) {
  return '$greeting, $name님!';
}
print(greet("홍길동")); // 안녕하세요, 홍길동님!
print(greet("홍길동", "반갑습니다")); // 반갑습니다, 홍길동님!

3. 네임드 파라미터

중괄호로 감싸고 이름으로 값을 전달합니다. 플러터에서 매우 많이 사용됩니다.

String createUser({required String name, int age = 30, String? email}) {
  return 'User: $name, Age: $age${email != null ? ", Email: $email" : ""}';
}

print(createUser(name: "홍길동")); // User: 홍길동, Age: 30
print(createUser(name: "김철수", age: 25)); // User: 김철수, Age: 25
print(createUser(name: "이영희", age: 28, email: "lee@example.com")); 
// User: 이영희, Age: 28, Email: lee@example.com

화살표 함수 (Arrow Function)

한 줄로 표현 가능한 함수는 화살표 구문으로 간결하게 작성할 수 있습니다.

// 일반 함수
int multiply(int a, int b) {
  return a * b;
}

// 화살표 함수로 변환
int multiplyArrow(int a, int b) => a * b;

// 함수를 변수에 저장
var operation = (int x, int y) => x * y + x;
print(operation(3, 4)); // 15

3. typedef와 함수 타입

typedef는 함수 타입에 별칭을 부여하여 코드의 가독성을 높이고 타입 안전성을 강화합니다.

// 수학 연산을 위한 typedef
typedef MathOperation = int Function(int a, int b);

// 다양한 계산 함수들
int add(int a, int b) => a + b;
int subtract(int a, int b) => a - b;

// typedef를 매개변수로 사용하는 함수
int calculate(int a, int b, MathOperation operation) {
  return operation(a, b);
}

// 사용 예
print(calculate(10, 5, add)); // 15
print(calculate(10, 5, subtract)); // 5

함수를 다른 함수의 매개변수로 전달하거나, 변수에 저장하거나, 반환값으로 사용할 수 있는 유연성이 Dart의 큰 장점입니다.

4. Null 안전성과 Nullable 타입

Dart는 null 안전성(Null Safety)을 제공하여 런타임 에러를 방지합니다.

// Nullable 변수 선언 (값이 null일 수 있음)
String? nullableName = '홍길동';
nullableName = null; // 가능

// null 조건부 연산자 '?.'
String? nullableStr = null;
print(nullableStr?.length); // null이면 null 반환

// null 병합 연산자 '??'
String result = nullableStr ?? '기본값';
print(result); // '기본값'

// null assertion 연산자 '!'
nullableName = '김철수';
String nonNullName = nullableName!; // nullableName이 null이 아님을 확신할 때

5. Enum과 Extension

Enum

Enum은 정해진 값들의 집합을 정의할 때 사용합니다.

enum Color { red, green, blue, yellow }

print(Color.red.name); // 'red'

Extension

Extension은 기존 클래스에 새로운 기능을 추가하는 방법입니다.

extension ColorExtension on Color {
  String get koreanName {
    switch (this) {
      case Color.red:
        return '빨강';
      case Color.green:
        return '초록';
      case Color.blue:
        return '파랑';
      case Color.yellow:
        return '노랑';
      default:
        return '알 수 없음';
    }
  }
  
  String get hexCode {
    switch (this) {
      case Color.red:
        return '#FF0000';
      case Color.green:
        return '#00FF00';
      case Color.blue:
        return '#0000FF';
      case Color.yellow:
        return '#FFFF00';
      default:
        return '#000000';
    }
  }
}

print(Color.red.koreanName); // '빨강'
print(Color.blue.hexCode); // '#0000FF'

6. 클래스와 생성자

Dart의 클래스는 자바와 유사하지만, 생성자 문법과 필드 초기화 방식에 차이가 있습니다.

class User {
  final String name;
  final int age;
  final Function(double, double) calculateBMI; // 함수를 필드로 사용

  // 생성자 (네임드 파라미터 활용)
  User({
    required this.name,
    required this.age,
    required this.calculateBMI,
  });

  String getInfo() {
    return '이름: $name, 나이: $age';
  }

  double getBMI(double height, double weight) {
    return calculateBMI(height, weight);
  }
}

// 상속 예시
class Employee extends User {
  final String employeeId;

  Employee({
    required String name,
    required int age,
    required this.employeeId,
  }) : super(
    name: name,
    age: age,
    calculateBMI: calculateBMI,
  );

  @override
  String getInfo() {
    return '${super.getInfo()}, 사원번호: $employeeId';
  }
}

7. 자바와 Dart 비교

항목DartJava

생성자 필수 여부 지정 필요 자동완성으로 가능
코드 생성 타입 지정 필요 타입까지 맞춰서 생성
변수의 타입 var, dynamic 값의 타입
파라미터 종류 포지셔널, 옵셔널, 네임드 포지셔널
함수의 활용도 파라미터, 타입, 필드 단순한 함수

8. 주의사항 및 팁

  1. Dart는 함수형 프로그래밍 지원: 자바와 달리 함수를 일급 객체로 취급합니다.
  2. Null 안전성: Dart 2.12부터 null 안전성이 기본 기능으로 포함되었습니다.
  3. 메인 메서드 위치: 자바는 클래스 내부에, Dart는 클래스 외부에 메인 메서드를 정의합니다.
  4. 생성자에서 필수 여부: required 키워드로 필수 파라미터를 명시해야 합니다.
  5. 리스트와 컬렉션 API: Dart의 컬렉션 API는 매우 풍부하고 직관적입니다.

종합 예제: 실전 코드

다음은 앞서 배운 개념들을 활용한 실전 코드입니다:

void main() {
  // 사용자 생성
  var user = User(
    name: '홍길동',
    age: 30,
    calculateBMI: (height, weight) => weight / ((height / 100) * (height / 100)),
  );
  
  print(user.getInfo());
  print('BMI: ${user.getBMI(175, 70).toStringAsFixed(2)}');
  
  // 직원 생성
  var employee = Employee(
    name: '김영희',
    age: 28,
    employeeId: 'E12345',
  );
  
  print(employee.getInfo());
  
  // 리스트 활용
  var programmers = [
    {'name': '홍길동', 'languages': ['Java', 'Kotlin', 'Dart']},
    {'name': '김철수', 'languages': ['JavaScript', 'TypeScript', 'Python']},
    {'name': '이영희', 'languages': ['Dart', 'Flutter', 'Firebase']},
  ];
  
  // Dart 사용자만 필터링
  var dartProgrammers = programmers
      .where((programmer) => 
          (programmer['languages'] as List).contains('Dart'))
      .map((programmer) => programmer['name'])
      .toList();
  
  print('Dart 프로그래머: $dartProgrammers');
}

결론

Dart는 자바 개발자가 비교적 쉽게 배울 수 있는 현대적인 언어입니다. 함수형 프로그래밍의 강점과 객체지향 프로그래밍의 명확함을 모두 갖추고 있어, 특히 Flutter와 함께 모바일 앱 개발에 매우 효과적입니다.

배울 내용이 많지만, 자바 경험이 있다면 Dart의 문법과 패턴에 빠르게 적응할 수 있을 것입니다. 실제 프로젝트를 진행하면서 이러한 개념들을 적용해보는 것이 가장 효과적인 학습 방법입니다.


여러분도 Dart 언어에 관심이 있으시거나, Flutter 개발을 시작하려고 계획 중이신가요? 질문이나 궁금한 점이 있으시면 언제든지 댓글로 남겨주세요! 함께 성장하는 개발 여정을 함께 하고 싶습니다. 🚀