Dart 기초 문법
이 문서 주변 탐색
주제 태그, 링크, 시리즈 흐름을 중심으로 옆으로 이동할 수 있습니다.
시리즈 흐름
이 문서는 아직 읽기 시리즈에 연결되지 않았습니다.
관련 문서
같이 읽을 만한 관련 문서가 아직 없습니다.
이 문서를 참조하는 문서
이 문서를 참조하는 다른 문서가 아직 없습니다.
Dart는 Google이 만든 객체지향 언어고, Flutter의 핵심 언어임
온라인 실습 환경: DartPad
1. 변수와 타입
기본 타입
Dart 주요 기본 타입은 이런 것들이 있음
타입 | 설명 | 예시 |
|---|---|---|
| 정수 |
|
| 실수 (부동소수점) |
|
| int와 double의 상위 타입 |
|
| 불리언 (true/false) |
|
| 문자열 |
|
출력하기
void main() {
print('Hello, Dart!');
String name = '모리';
int age = 25;
// 문자열 보간 (String Interpolation)
print('이름: $name'); // 단순 변수
print('나이: ${age}살'); // 중괄호 사용
print('내년 나이: ${age + 1}살'); // 표현식 사용
// ⚠️ 주의: 프로퍼티 접근 시 반드시 중괄호 필요
print('타입: ${name.runtimeType}'); // ✅ 올바름
// print('타입: $name.runtimeType'); // ❌ 잘못됨 - "모리.runtimeType" 출력
}변수 스코프 규칙
void main() {
String name = '모리';
// String name = '철수'; // ❌ 에러! 같은 스코프에서 중복 선언 불가
if (true) {
String name = '철수'; // ✅ 다른 스코프라서 가능
print(name); // 철수
}
print(name); // 모리
}2. var vs 명시적 타입 vs dynamic
🔍 핵심 차이점
특성 |
| 명시적 타입 ( |
|
|---|---|---|---|
타입 추론 | 초기값으로 추론 | 직접 지정 | 없음 (모든 타입 허용) |
타입 변경 | ❌ 불가 | ❌ 불가 | ✅ 가능 |
컴파일 타임 체크 | ✅ 있음 | ✅ 있음 | ❌ 런타임에만 |
사용 권장 | 지역 변수 | API, 클래스 필드 | 피하는 게 좋음 |
var - 타입 추론
void main() {
var name = '모리'; // String으로 추론됨
var age = 25; // int로 추론됨
var height = 175.5; // double로 추론됨
print(name.runtimeType); // String
print(age.runtimeType); // int
// ⚠️ 한번 추론된 타입은 변경 불가
name = '철수'; // ✅ String → String (가능)
// name = 123; // ❌ 에러! String → int (불가)
// 초기값 없이 선언하면 dynamic이 됨
var something; // dynamic으로 추론
something = '문자열';
something = 123; // ✅ 가능 (dynamic이니까)
}명시적 타입 선언
void main() {
String name = '모리';
int age = 25;
double height = 175.5;
bool isStudent = true;
// 명시적 선언의 장점
// 1. 코드 가독성 향상
// 2. 의도 명확히 표현
// 3. IDE 자동완성 지원 향상
}dynamic - 모든 타입 허용
void main() {
dynamic value = '문자열';
print(value.runtimeType); // String
value = 123; // ✅ 타입 변경 가능
print(value.runtimeType); // int
value = true; // ✅ 또 변경 가능
print(value.runtimeType); // bool
// ⚠️ dynamic의 위험성
dynamic something = '모리';
// something.someMethod(); // 컴파일은 되지만 런타임 에러!
}💡 언제 뭘 써야 할까?
// ✅ var 사용 - 지역 변수, 타입이 명확할 때
void processData() {
var items = <String>[]; // List<String>으로 추론
var count = 0; // int로 추론
}
// ✅ 명시적 타입 - 클래스 필드, 함수 파라미터, 반환 타입
class User {
String name; // 명시적 선언
int age; // 명시적 선언
User(this.name, this.age);
}
// ⚠️ dynamic 사용 - JSON 파싱 등 불가피한 경우에만
Map<String, dynamic> json = {
'name': '모리',
'age': 25,
'scores': [90, 85, 95],
};
3. Null Safety
Dart 2.12부터 도입된 Null Safety는 null 관련 에러를 컴파일 타임에 방지해줌
Nullable vs Non-nullable
void main() {
// Non-nullable (기본값) - null 불가
String name = '모리';
// name = null; // ❌ 에러!
// Nullable - null 허용 (? 사용)
String? nickname = '모리';
nickname = null; // ✅ 가능
// int, double 등 모든 타입에 적용됨
int? age; // null로 초기화됨
print(age); // null
}
Null 처리 연산자
void main() {
String? name;
// 1. Null 체크 후 사용
if (name != null) {
print(name.length); // 안전하게 사용
}
// 2. Null assertion (!) - null이 아님을 확신할 때
String? value = '확실히 있음';
print(value!.length); // ⚠️ null이면 런타임 에러!
// 3. Null-aware 연산자 (?.)
String? text;
print(text?.length); // null이면 null 반환, 에러 없음
// 4. Null 병합 연산자 (??)
String? nickname;
String displayName = nickname ?? '익명'; // null이면 '익명' 사용
print(displayName); // 익명
// 5. Null 병합 할당 (??=)
int? count;
count ??= 0; // null이면 0으로 할당
print(count); // 0
count ??= 10; // 이미 0이라서 변경 안됨
print(count); // 0
}
late 키워드
class User {
// late - 나중에 초기화할 거라는 약속
late String name;
void setName(String value) {
name = value;
}
void printName() {
print(name); // 초기화 전 접근하면 런타임 에러
}
}
// lazy initialization (지연 초기화)
late String heavyData = loadHeavyData(); // 처음 접근할 때만 실행됨
String loadHeavyData() {
print('데이터 로딩 중...');
return '무거운 데이터';
}
4. final과 const
기본 개념
void main() {
// final - 런타임에 한 번만 할당
final String name = '모리';
final age = 25; // 타입 추론 가능
// name = '철수'; // ❌ 재할당 불가
// const - 컴파일 타임 상수
const double pi = 3.14159;
const gravity = 9.8; // 타입 추론 가능
// pi = 3.14; // ❌ 재할당 불가
}
🔍 final vs const 핵심 차이
특성 |
|
|
|---|---|---|
값 결정 시점 | 런타임 | 컴파일 타임 |
DateTime.now() | ✅ 가능 | ❌ 불가 |
생성자에서 초기화 | ✅ 가능 | ❌ 불가 |
인스턴스 필드 | ✅ 가능 | ❌ 불가 (static만) |
void main() {
// final - 런타임 값 가능
final now = DateTime.now(); // ✅ 실행 시점의 시간
final random = getRandomNumber(); // ✅ 함수 호출 결과
// const - 컴파일 타임에 알 수 있는 값만
const pi = 3.14159; // ✅ 리터럴
const doubled = pi * 2; // ✅ 컴파일 타임 계산 가능
// const time = DateTime.now(); // ❌ 에러! 런타임에만 알 수 있음
}
int getRandomNumber() => 42;
// 클래스에서의 사용
class Circle {
final double radius; // ✅ 인스턴스마다 다를 수 있음
static const double pi = 3.14159; // ✅ const는 static만 가능
Circle(this.radius);
double get area => pi * radius * radius;
}const 생성자와 컴파일 타임 최적화
class Point {
final int x;
final int y;
// const 생성자
const Point(this.x, this.y);
}
void main() {
// const 객체는 동일한 값이면 같은 인스턴스 공유함
const p1 = Point(1, 2);
const p2 = Point(1, 2);
print(identical(p1, p2)); // true - 같은 메모리!
// 일반 생성자는 매번 새 인스턴스
var p3 = Point(1, 2);
var p4 = Point(1, 2);
print(identical(p3, p4)); // false - 다른 메모리
}5. 연산자
산술 연산자
void main() {
int a = 10;
int b = 3;
print(a + b); // 13 (덧셈)
print(a - b); // 7 (뺄셈)
print(a * b); // 30 (곱셈)
print(a / b); // 3.333... (나눗셈, double 반환)
print(a ~/ b); // 3 (정수 나눗셈)
print(a % b); // 1 (나머지)
// 증감 연산자
int count = 0;
count++; // 후위 증가
++count; // 전위 증가
count--; // 후위 감소
--count; // 전위 감소
// 복합 할당 연산자
int num = 10;
num += 5; // num = num + 5
num -= 3; // num = num - 3
num *= 2; // num = num * 2
num ~/= 4; // num = num ~/ 4
}비교 연산자
void main() {
print(5 == 5); // true (같음)
print(5 != 3); // true (다름)
print(5 > 3); // true (크다)
print(5 < 3); // false (작다)
print(5 >= 5); // true (크거나 같다)
print(5 <= 3); // false (작거나 같다)
}타입 검사 연산자
void main() {
var value = 42;
// is - 타입 확인
print(value is int); // true
print(value is String); // false
// is! - 타입이 아닌지 확인
print(value is! String); // true
print(value is! int); // false
// as - 타입 캐스팅
num number = 42;
int integer = number as int; // ⚠️ 실패하면 런타임 에러
}논리 연산자
void main() {
bool a = true;
bool b = false;
print(a && b); // false (AND)
print(a || b); // true (OR)
print(!a); // false (NOT)
}Null-aware 연산자
void main() {
String? name;
// ?? - null이면 오른쪽 값 사용
print(name ?? '기본값'); // 기본값
// ??= - null이면 할당
name ??= '모리';
print(name); // 모리
// ?. - null이면 null 반환, 아니면 접근
String? text;
print(text?.length); // null
// ?[] - null-aware 인덱스 접근
List<int>? numbers;
print(numbers?[0]); // null
}삼항 연산자
void main() {
int age = 20;
String status = age >= 18 ? '성인' : '미성년자';
print(status); // 성인
}캐스케이드 연산자 (..)
class Person {
String name = '';
int age = 0;
void introduce() {
print('안녕하세요, $name입니다. $age살이에요.');
}
}
void main() {
// 캐스케이드 없이
var p1 = Person();
p1.name = '모리';
p1.age = 25;
p1.introduce();
// 캐스케이드 사용 - 메서드 체이닝
var p2 = Person()
..name = '철수'
..age = 30
..introduce();
// null-aware 캐스케이드 (?..)
Person? p3;
p3?..name = '영희'
..age = 28; // p3가 null이면 아무것도 실행 안됨
}6. 컬렉션
List (배열)
void main() {
// 리스트 생성
List<String> names = ['모리', '철수', '영희'];
var numbers = <int>[1, 2, 3, 4, 5]; // 타입 추론
// 빈 리스트 생성
List<int> emptyList = [];
var anotherEmpty = <String>[];
// 기본 연산
print(names[0]); // 모리 (인덱스 접근)
print(names.length); // 3
print(names.first); // 모리
print(names.last); // 영희
print(names.isEmpty); // false
// 요소 추가
names.add('민수'); // 끝에 추가
names.addAll(['지영', '수진']); // 여러 개 추가
names.insert(0, '첫번째'); // 특정 위치에 삽입
// 요소 제거
names.remove('철수'); // 값으로 제거
names.removeAt(0); // 인덱스로 제거
names.removeLast(); // 마지막 요소 제거
// 검색
print(names.indexOf('모리')); // 인덱스 반환 (없으면 -1)
print(names.contains('영희')); // true/false
// 유용한 메서드
var nums = [3, 1, 4, 1, 5, 9, 2, 6];
nums.sort(); // 정렬 (원본 변경)
print(nums.reversed.toList()); // 역순
// 함수형 메서드
var doubled = nums.map((n) => n * 2).toList();
var evens = nums.where((n) => n % 2 == 0).toList();
var sum = nums.reduce((a, b) => a + b);
}
Spread 연산자 (...)
void main() {
var list1 = [1, 2, 3];
var list2 = [4, 5, 6];
// 스프레드 연산자로 합치기
var combined = [...list1, ...list2];
print(combined); // [1, 2, 3, 4, 5, 6]
// null-aware 스프레드 (...?)
List<int>? nullableList;
var safe = [0, ...?nullableList, 10];
print(safe); // [0, 10]
// 컬렉션 if
bool showExtra = true;
var items = [
'item1',
'item2',
if (showExtra) 'extraItem',
];
// 컬렉션 for
var squares = [
for (var i = 1; i <= 5; i++) i * i,
];
print(squares); // [1, 4, 9, 16, 25]
}
Set (집합)
void main() {
// Set 생성 - 중복 불허, 순서 없음
Set<String> fruits = {'사과', '바나나', '오렌지'};
var numbers = <int>{1, 2, 3, 4, 5};
// 빈 Set (주의: {}는 Map이 됨!)
var emptySet = <String>{};
Set<int> anotherEmpty = {};
// 요소 추가/제거
fruits.add('포도');
fruits.add('사과'); // 중복은 무시됨
fruits.remove('바나나');
// 집합 연산
var a = {1, 2, 3, 4};
var b = {3, 4, 5, 6};
print(a.union(b)); // {1, 2, 3, 4, 5, 6} (합집합)
print(a.intersection(b)); // {3, 4} (교집합)
print(a.difference(b)); // {1, 2} (차집합)
// List → Set (중복 제거)
var listWithDupes = [1, 2, 2, 3, 3, 3];
var uniqueSet = listWithDupes.toSet();
print(uniqueSet); // {1, 2, 3}
}
Map (딕셔너리)
void main() {
// Map 생성
Map<String, String> capitals = {
'Korea': 'Seoul',
'Japan': 'Tokyo',
'China': 'Beijing',
};
// 타입 추론
var scores = {
'math': 90,
'english': 85,
'science': 95,
};
// 빈 Map
var emptyMap = <String, int>{};
// 값 접근 및 수정
print(capitals['Korea']); // Seoul
capitals['USA'] = 'Washington'; // 추가
capitals['Korea'] = 'Seoul City'; // 수정
// 여러 개 추가
capitals.addAll({
'France': 'Paris',
'Germany': 'Berlin',
});
// 안전한 접근
print(capitals['Unknown']); // null
print(capitals['Unknown'] ?? '없음'); // 없음
// 유용한 메서드
print(capitals.keys); // 모든 키
print(capitals.values); // 모든 값
print(capitals.entries); // 키-값 쌍
print(capitals.containsKey('Korea')); // true
print(capitals.containsValue('Seoul')); // false (수정됨)
// 순회
capitals.forEach((key, value) {
print('$key의 수도는 $value');
});
// Map 변환
var upperCaseCapitals = capitals.map(
(key, value) => MapEntry(key, value.toUpperCase()),
);
}
7. 조건문과 반복문
if-else 문
void main() {
int score = 85;
// 기본 if-else
if (score >= 90) {
print('A등급');
} else if (score >= 80) {
print('B등급');
} else if (score >= 70) {
print('C등급');
} else {
print('재시험');
}
// 삼항 연산자 (간단한 조건)
String result = score >= 60 ? '합격' : '불합격';
}
switch 문
void main() {
String grade = 'A';
switch (grade) {
case 'A':
print('우수');
break;
case 'B':
print('양호');
break;
case 'C':
case 'D': // fall-through
print('보통');
break;
default:
print('노력 필요');
}
// Dart 3.0+ switch expression
String message = switch (grade) {
'A' => '우수합니다!',
'B' => '잘했습니다!',
'C' || 'D' => '조금 더 노력하세요.',
_ => '재시험이 필요합니다.',
};
}
for 반복문
void main() {
// 기본 for 문
for (int i = 0; i < 5; i++) {
print('반복 $i');
}
// for-in (컬렉션 순회)
List<String> fruits = ['사과', '바나나', '오렌지'];
for (String fruit in fruits) {
print(fruit);
}
// for-in with index가 필요할 때
for (int i = 0; i < fruits.length; i++) {
print('$i: ${fruits[i]}');
}
// forEach (함수형)
fruits.forEach((fruit) {
print(fruit);
});
// forEach with arrow
fruits.forEach((fruit) => print(fruit));
}
while 반복문
void main() {
// while
int count = 0;
while (count < 3) {
print('while: $count');
count++;
}
// do-while (최소 1번 실행)
int num = 0;
do {
print('do-while: $num');
num++;
} while (num < 3);
}
반복 제어
void main() {
// break - 반복문 탈출
for (int i = 0; i < 10; i++) {
if (i == 5) break;
print(i); // 0, 1, 2, 3, 4
}
// continue - 다음 반복으로
for (int i = 0; i < 5; i++) {
if (i == 2) continue;
print(i); // 0, 1, 3, 4 (2 건너뜀)
}
// 라벨 사용 (중첩 반복문)
outer: for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) break outer;
print('$i, $j');
}
}
}8. Enum
기본 Enum
enum Status {
pending,
approved,
rejected,
}
void main() {
Status status = Status.pending;
print(status); // Status.pending
print(status.name); // pending
print(status.index); // 0
// 모든 값 접근
print(Status.values); // [Status.pending, Status.approved, Status.rejected]
// switch와 함께 사용
switch (status) {
case Status.pending:
print('대기 중');
break;
case Status.approved:
print('승인됨');
break;
case Status.rejected:
print('거절됨');
break;
}
}
Enhanced Enum (Dart 2.17+)
enum HttpStatus {
ok(200, 'OK'),
notFound(404, 'Not Found'),
internalError(500, 'Internal Server Error');
final int code;
final String message;
const HttpStatus(this.code, this.message);
// 메서드 추가도 가능함
bool get isSuccess => code >= 200 && code < 300;
@override
String toString() => '$code: $message';
}
void main() {
var status = HttpStatus.ok;
print(status.code); // 200
print(status.message); // OK
print(status.isSuccess); // true
print(status); // 200: OK
// 값으로 찾기
var found = HttpStatus.values.firstWhere(
(s) => s.code == 404,
orElse: () => HttpStatus.internalError,
);
print(found); // 404: Not Found
}
9. 함수
기본 함수
// 반환 타입 명시
int add(int a, int b) {
return a + b;
}
// void - 반환값 없음
void greet(String name) {
print('안녕하세요, $name님!');
}
// 화살표 함수 (한 줄 표현식)
int multiply(int a, int b) => a * b;
void sayHello() => print('Hello!');
void main() {
print(add(3, 5)); // 8
greet('모리'); // 안녕하세요, 모리님!
print(multiply(4, 5)); // 20
}
선택적 매개변수
// 1. 위치 기반 선택적 매개변수 (대괄호)
String introduce(String name, [int? age, String? city]) {
var result = '이름: $name';
if (age != null) result += ', 나이: $age';
if (city != null) result += ', 도시: $city';
return result;
}
// 기본값 설정
int addNumbers(int a, [int b = 0, int c = 0]) {
return a + b + c;
}
void main() {
print(introduce('모리')); // 이름: 모리
print(introduce('모리', 25)); // 이름: 모리, 나이: 25
print(introduce('모리', 25, '서울')); // 이름: 모리, 나이: 25, 도시: 서울
print(addNumbers(10)); // 10
print(addNumbers(10, 20)); // 30
print(addNumbers(10, 20, 30)); // 60
}명명된 매개변수 (Named Parameters)
// 중괄호 사용, required 또는 기본값 필요
void createUser({
required String name,
required int age,
String country = 'Korea',
String? nickname,
}) {
print('이름: $name, 나이: $age, 국가: $country');
if (nickname != null) print('별명: $nickname');
}
void main() {
// 순서 상관없이 이름으로 전달
createUser(
age: 25,
name: '모리',
);
createUser(
name: '철수',
age: 30,
country: 'Japan',
nickname: '철이',
);
}혼합 사용
// 위치 필수 + 명명된 선택적
void example(int required1, String required2, {int? optional1, String optional2 = 'default'}) {
print('$required1, $required2, $optional1, $optional2');
}
void main() {
example(1, 'two');
example(1, 'two', optional1: 3);
example(1, 'two', optional2: 'custom', optional1: 3);
}일급 객체로서의 함수
void main() {
// 함수를 변수에 할당
int Function(int, int) operation = add;
print(operation(3, 5)); // 8
operation = multiply;
print(operation(3, 5)); // 15
// 함수를 인자로 전달
print(calculate(10, 20, add)); // 30
print(calculate(10, 20, multiply)); // 200
// 익명 함수
var numbers = [1, 2, 3, 4, 5];
var doubled = numbers.map((n) => n * 2).toList();
print(doubled); // [2, 4, 6, 8, 10]
}
int add(int a, int b) => a + b;
int multiply(int a, int b) => a * b;
int calculate(int a, int b, int Function(int, int) operation) {
return operation(a, b);
}클로저 (Closure)
void main() {
var counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
print(counter()); // 3
var anotherCounter = makeCounter();
print(anotherCounter()); // 1 (새로운 클로저)
}
Function makeCounter() {
int count = 0; // 클로저가 캡처하는 변수
return () {
count++;
return count;
};
}10. typedef와 함수 타입
typedef 정의
// 함수 타입 별칭 정의
typedef Operation = int Function(int x, int y, int z);
typedef StringCallback = void Function(String message);
typedef Predicate<T> = bool Function(T value);
// 함수 구현
int add(int x, int y, int z) => x + y + z;
int multiply(int x, int y, int z) => x * y * z;
void main() {
// typedef를 타입으로 사용
Operation op = add;
print(op(1, 2, 3)); // 6
op = multiply;
print(op(1, 2, 3)); // 6
// 고차 함수에서 활용
print(calculate(10, 20, 30, add)); // 60
print(calculate(10, 20, 30, multiply)); // 6000
}
// typedef를 매개변수 타입으로 사용
int calculate(int x, int y, int z, Operation operation) {
return operation(x, y, z);
}제네릭 typedef
typedef Mapper<T, R> = R Function(T input);
typedef Reducer<T> = T Function(T a, T b);
void main() {
// String → int 매퍼
Mapper<String, int> stringLength = (s) => s.length;
print(stringLength('Hello')); // 5
// int → String 매퍼
Mapper<int, String> intToString = (n) => 'Number: $n';
print(intToString(42)); // Number: 42
// Reducer
Reducer<int> sum = (a, b) => a + b;
var numbers = [1, 2, 3, 4, 5];
print(numbers.reduce(sum)); // 15
}실전 활용 예시
// 콜백 패턴
typedef OnSuccess<T> = void Function(T data);
typedef OnError = void Function(String error);
void fetchData({
required OnSuccess<String> onSuccess,
required OnError onError,
}) {
try {
// 데이터 가져오기 시뮬레이션
var data = '가져온 데이터';
onSuccess(data);
} catch (e) {
onError(e.toString());
}
}
void main() {
fetchData(
onSuccess: (data) => print('성공: $data'),
onError: (error) => print('에러: $error'),
);
}11. DateTime과 Duration
DateTime - 날짜와 시간
void main() {
// 현재 시간
var now = DateTime.now();
print(now); // 2026-02-01 14:30:45.123456
// 특정 날짜 생성
var birthday = DateTime(1995, 12, 25); // 1995년 12월 25일
print(birthday); // 1995-12-25 00:00:00.000
// 시간까지 지정
var meeting = DateTime(2026, 3, 15, 14, 30, 0); // 2026년 3월 15일 14:30:00
print(meeting);
// UTC 시간
var utcNow = DateTime.now().toUtc();
var utcDate = DateTime.utc(2026, 1, 1, 12, 0, 0);
}DateTime 속성
void main() {
var now = DateTime.now();
print(now.year); // 2026
print(now.month); // 2 (1-12)
print(now.day); // 1 (1-31)
print(now.hour); // 14 (0-23)
print(now.minute); // 30 (0-59)
print(now.second); // 45 (0-59)
print(now.millisecond); // 123 (0-999)
print(now.microsecond); // 456 (0-999)
print(now.weekday); // 6 (1=월요일, 7=일요일)
print(now.isUtc); // false
}DateTime 비교
void main() {
var date1 = DateTime(2026, 1, 1);
var date2 = DateTime(2026, 6, 15);
var date3 = DateTime(2026, 1, 1);
// 비교 연산자
print(date1.isBefore(date2)); // true
print(date1.isAfter(date2)); // false
print(date1.isAtSameMomentAs(date3)); // true
// compareTo
print(date1.compareTo(date2)); // -1 (date1 < date2)
print(date2.compareTo(date1)); // 1 (date2 > date1)
print(date1.compareTo(date3)); // 0 (같음)
}DateTime 연산
void main() {
var now = DateTime.now();
// 더하기 (Duration 사용)
var tomorrow = now.add(Duration(days: 1));
var nextWeek = now.add(Duration(days: 7));
var inTwoHours = now.add(Duration(hours: 2));
// 빼기
var yesterday = now.subtract(Duration(days: 1));
var lastMonth = now.subtract(Duration(days: 30));
// 두 날짜 사이의 차이 (Duration 반환)
var date1 = DateTime(2026, 1, 1);
var date2 = DateTime(2026, 3, 15);
var difference = date2.difference(date1);
print(difference.inDays); // 73
print(difference.inHours); // 1752
}Duration - 시간 간격
void main() {
// Duration 생성 방법들
var oneDay = Duration(days: 1);
var twoHours = Duration(hours: 2);
var thirtyMinutes = Duration(minutes: 30);
var fiveSeconds = Duration(seconds: 5);
var hundredMs = Duration(milliseconds: 100);
// 여러 단위 조합
var complex = Duration(
days: 1,
hours: 2,
minutes: 30,
seconds: 45,
);
print(complex); // 26:30:45.000000
// 총 시간 얻기
print(complex.inDays); // 1
print(complex.inHours); // 26
print(complex.inMinutes); // 1590
print(complex.inSeconds); // 95445
print(complex.inMilliseconds); // 95445000
}Duration 연산
void main() {
var d1 = Duration(hours: 2);
var d2 = Duration(minutes: 30);
// 더하기, 빼기
print(d1 + d2); // 2:30:00.000000
print(d1 - d2); // 1:30:00.000000
// 곱하기, 나누기
print(d1 * 2); // 4:00:00.000000
print(d1 ~/ 2); // 1:00:00.000000 (정수 나눗셈)
// 비교
print(d1 > d2); // true
print(d1 < d2); // false
print(d1 == Duration(minutes: 120)); // true
// 절대값
var negative = Duration(hours: -5);
print(negative.abs()); // 5:00:00.000000
// 음수 체크
print(negative.isNegative); // true
}비동기에서 Duration 사용
Future<void> main() async {
print('시작: ${DateTime.now()}');
// 2초 대기
await Future.delayed(Duration(seconds: 2));
print('2초 후: ${DateTime.now()}');
// 500ms 대기
await Future.delayed(Duration(milliseconds: 500));
print('0.5초 후: ${DateTime.now()}');
}날짜 포맷팅
void main() {
var now = DateTime.now();
// 기본 toString
print(now.toString()); // 2026-02-01 14:30:45.123456
// ISO 8601 형식
print(now.toIso8601String()); // 2026-02-01T14:30:45.123456
// 직접 포맷팅
String formatted = '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
print(formatted); // 2026-02-01
// 시간 포맷팅
String timeFormatted = '${now.hour}:${now.minute.toString().padLeft(2, '0')}';
print(timeFormatted); // 14:30
}
// 💡 복잡한 포맷팅은 intl 패키지의 DateFormat 쓰면 됨
// import 'package:intl/intl.dart';
// DateFormat('yyyy-MM-dd HH:mm').format(now);문자열 → DateTime 파싱
void main() {
// ISO 8601 형식 파싱
var parsed = DateTime.parse('2026-02-01 14:30:00');
print(parsed); // 2026-02-01 14:30:00.000
// 다양한 형식 지원
var date1 = DateTime.parse('2026-02-01');
var date2 = DateTime.parse('2026-02-01T14:30:00');
var date3 = DateTime.parse('2026-02-01T14:30:00Z'); // UTC
// tryParse - 실패하면 null 반환 (에러 없음)
var invalid = DateTime.tryParse('invalid-date');
print(invalid); // null
var valid = DateTime.tryParse('2026-03-15');
print(valid); // 2026-03-15 00:00:00.000
}실전 예시
void main() {
// 나이 계산
var birthday = DateTime(1995, 12, 25);
var today = DateTime.now();
var age = today.year - birthday.year;
if (today.month < birthday.month ||
(today.month == birthday.month && today.day < birthday.day)) {
age--; // 아직 생일 안 지남
}
print('나이: $age세');
// D-day 계산
var targetDate = DateTime(2026, 12, 31);
var daysLeft = targetDate.difference(today).inDays;
print('D-$daysLeft');
// 특정 요일 구하기
var weekdays = ['', '월', '화', '수', '목', '금', '토', '일'];
print('오늘은 ${weekdays[today.weekday]}요일');
// 월의 마지막 날
var lastDayOfMonth = DateTime(today.year, today.month + 1, 0);
print('이번 달 마지막 날: ${lastDayOfMonth.day}일');
}🤖
본 글은 AI의 도움을 받아 작성되었습니다.
읽기 시리즈
현재 위치를 확인하고, 흐름을 따라 바로 다음 읽기로 이어갈 수 있습니다.