Dart vs Kotlin 비교 정리

Hyunsoo Jung·2024년 11월 3일

Flutter VS Native

목록 보기
1/1

Dart는 Flutter에서 주로 사용하는 언어이고, Kotlin은 Android Native 개발에서 주로 사용하는 언어다.
둘 다 모바일 앱 개발에 많이 사용되지만 UI를 구성하는 방식과 문법에는 차이가 있다.

Dart
- Flutter 앱 개발에 사용
- Widget 중심 UI 구성
- Android, iOS, Web, Desktop 개발 가능

Kotlin
- Android Native 앱 개발에 사용
- View 또는 Compose 중심 UI 구성
- Android 개발에 최적화

이번 글에서는 Dart와 Kotlin을 다음 기준으로 비교한다.

1. 위젯과 뷰
2. 위젯 트리와 뷰 트리
3. 변수 선언
4. Null Safety
5. dynamic과 Any
6. late, lateinit, lazy
7. const, final, val

1. 위젯이란?

Flutter에서 UI를 구성하는 기본 단위는 Widget이다.

Android XML 기반 개발에서는 TextView, ImageView, Button 같은 View를 사용한다.

Flutter / Dart
- Text
- Image
- Icon
- ElevatedButton
- Column
- Row

Android / Kotlin
- TextView
- ImageView
- Button
- LinearLayout
- ConstraintLayout

즉, Flutter에서는 화면에 보이는 모든 요소를 위젯으로 표현한다.

Text('Hello Flutter')

Android XML에서는 다음처럼 View를 사용한다.

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello Android" />

정리하면 다음과 같다.

Dart / Flutter
- UI 요소를 Widget으로 구성

Kotlin / Android XML
- UI 요소를 View로 구성

Flutter에서는 화면에 보이는 요소뿐만 아니라 정렬, 여백, 레이아웃도 모두 위젯이다.

Flutter에서 Widget인 것
- Text
- Image
- Button
- Padding
- Center
- Column
- Row

2. Dart의 위젯 트리

Flutter에서는 여러 위젯이 부모-자식 관계로 연결된다.

이 구조를 위젯 트리라고 한다.

MaterialApp
   |
   v
Scaffold
   |
   v
Column
   |
   |-- Text
   |-- TextField
   |-- ElevatedButton

예시는 다음과 같다.

MaterialApp(
  home: Scaffold(
    appBar: AppBar(
      title: const Text('Login'),
    ),
    body: Column(
      children: const [
        Text('아이디'),
        Text('비밀번호'),
      ],
    ),
  ),
)

위 코드는 다음과 같은 위젯 트리 구조를 가진다.

MaterialApp
   |
   v
Scaffold
   |
   |-- AppBar
   |    |
   |    v
   |   Text
   |
   v
Column
   |
   |-- Text
   |-- Text

위젯 트리의 장점은 다음과 같다.

위젯 트리 장점
- UI 구조를 코드로 쉽게 표현 가능
- 위젯 단위로 재사용 가능
- 상태가 있는 위젯과 없는 위젯을 구분 가능
- 변경된 부분 중심으로 UI 업데이트 가능
- Hot Reload로 빠르게 화면 확인 가능

3. Kotlin의 뷰 트리

Android XML 기반 개발에서는 여러 View가 부모-자식 관계로 연결된다.

이 구조를 뷰 트리라고 한다.

ConstraintLayout
   |
   |-- TextView
   |-- EditText
   |-- Button

예시는 다음과 같다.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="아이디" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="로그인" />

</LinearLayout>

Kotlin 코드에서는 XML에 정의한 View를 가져와서 동작을 연결한다.

val loginButton = findViewById<Button>(R.id.loginButton)

loginButton.setOnClickListener {
    println("로그인 버튼 클릭")
}

흐름은 다음과 같다.

XML에서 UI 정의
   |
   v
Activity 또는 Fragment에서 View 찾기
   |
   v
Kotlin 코드로 이벤트 연결

정리하면 다음과 같다.

Flutter
- Dart 코드에서 Widget으로 UI 구성

Android XML
- XML에서 View 구성
- Kotlin에서 View 동작 처리

4. Widget Tree와 View Tree 차이

Dart와 Kotlin의 UI 구조를 비교하면 다음과 같다.

Flutter Widget Tree
- UI를 Dart 코드로 선언
- Widget 중심
- 상태 변화에 따라 build 재실행
- Hot Reload에 강점

Android View Tree
- XML 또는 Kotlin 코드로 UI 구성
- View 중심
- Activity / Fragment에서 View 제어
- XML 기반에서는 UI와 로직이 나뉘는 경우가 많음

간단히 보면 다음과 같다.

Dart / Flutter
Widget을 조합해서 화면을 만든다.

Kotlin / Android XML
View를 XML에 배치하고 Kotlin에서 제어한다.

다만 Kotlin도 Jetpack Compose를 사용하면 Flutter처럼 선언형 UI 방식으로 작성할 수 있다.

@Composable
fun LoginScreen() {
    Column {
        Text(text = "아이디")
        Button(onClick = {}) {
            Text(text = "로그인")
        }
    }
}

즉, 비교 기준에 따라 다음처럼 볼 수 있다.

Flutter
- Widget Tree

Android XML
- View Tree

Android Compose
- Composable Tree

5. 변수 선언 비교

Dart와 Kotlin은 변수 선언 방식이 다르다.

Dart는 타입을 변수 이름 앞에 쓴다.

String name = 'hyunsoo';
int age = 24;

Kotlin은 타입을 변수 이름 뒤에 쓴다.

val name: String = "hyunsoo"
val age: Int = 24

정리하면 다음과 같다.

Dart
- 타입을 변수 이름 앞에 작성
- String name

Kotlin
- 타입을 변수 이름 뒤에 작성
- val name: String

6. var, final, val 비교

Dart와 Kotlin 모두 타입 추론을 지원한다.

즉, 타입을 직접 쓰지 않아도 컴파일러가 타입을 추론할 수 있다.

Dart

var name = 'hyunsoo';
final age = 24;

Dart에서 var는 값을 변경할 수 있다.

var name = 'hyunsoo';
name = 'hyunsoo24'; // 가능

final은 한 번 값을 넣으면 변경할 수 없다.

final age = 24;
age = 25; // 에러

Kotlin

var name = "hyunsoo"
val age = 24

Kotlin에서 var는 값을 변경할 수 있다.

var name = "hyunsoo"
name = "hyunsoo24" // 가능

val은 한 번 값을 넣으면 변경할 수 없다.

val age = 24
age = 25 // 에러

차이를 정리하면 다음과 같다.

Dart
- var: 값 변경 가능
- final: 값 변경 불가능

Kotlin
- var: 값 변경 가능
- val: 값 변경 불가능

즉, Dart의 final은 Kotlin의 val과 비슷하게 이해할 수 있다.

Dart final
≈ Kotlin val

7. Null Safety 비교

Dart와 Kotlin 모두 Null Safety를 지원한다.

기본적으로 null이 들어갈 수 없는 타입과 null이 들어갈 수 있는 타입을 구분한다.

Dart

String name = 'hyunsoo';
String? nickname = null;

Kotlin

val name: String = "hyunsoo"
val nickname: String? = null

둘 다 ?를 붙이면 null이 가능한 타입이 된다.

String
- null 불가능

String?
- null 가능

null 가능 타입을 사용할 때는 null 체크를 해야 한다.

Dart

String? name = null;

print(name?.length);

Kotlin

val name: String? = null

println(name?.length)

?.는 값이 null이 아닐 때만 접근한다.

name이 null이 아니다
-> length 접근

name이 null이다
-> null 반환

정리하면 다음과 같다.

Dart와 Kotlin 공통점
- ?를 붙이면 null 가능
- ?.로 안전하게 접근 가능
- null 관련 오류를 줄이기 위해 사용

8. dynamic과 Any 비교

Dart에는 dynamic이 있다.

dynamic은 어떤 타입이든 들어올 수 있는 변수다.

dynamic value;

value = 'hello';
value = 123;
value = true;

하지만 Kotlin에는 Dart의 dynamic과 완전히 같은 일반적인 개념은 없다.

대신 어떤 타입이든 담고 싶을 때는 Any를 사용할 수 있다.

var value: Any = "hello"

value = 123
value = true

Dart의 dynamic과 Kotlin의 Any는 비슷해 보이지만 차이가 있다.

Dart dynamic
- 컴파일 시 타입 검사를 많이 우회함
- 존재하지 않는 메서드를 호출해도 컴파일 단계에서 놓칠 수 있음
- 런타임 에러 가능성이 커짐

Kotlin Any
- 모든 타입의 최상위 타입
- 사용하려면 타입 체크나 캐스팅이 필요함
- Dart dynamic보다 타입 안정성이 높음

예를 들어 Dart에서는 다음 코드가 컴파일될 수 있지만 런타임에서 에러가 날 수 있다.

dynamic value = 123;

print(value.length); // 런타임 에러 가능

Kotlin에서는 Any 타입에서 바로 length를 사용할 수 없다.

val value: Any = 123

// value.length // 컴파일 에러

타입 확인 후 사용해야 한다.

val value: Any = "hello"

if (value is String) {
    println(value.length)
}

정리하면 다음과 같다.

Dart dynamic
- 매우 유연하지만 위험할 수 있음

Kotlin Any
- 모든 타입을 담을 수 있지만 타입 확인이 필요함

9. late, lateinit, lazy 비교

Dart와 Kotlin 모두 변수를 나중에 초기화하는 방법이 있다.

하지만 문법과 사용 상황이 다르다.

Dart late

Dart에서는 late를 사용한다.

late String name;

void main() {
  name = 'hyunsoo';
  print(name);
}

late는 선언 시점에는 값을 넣지 않지만, 사용하기 전에는 반드시 값을 넣겠다는 의미다.

Dart late
- 나중에 초기화
- null이 아닌 타입에도 사용 가능
- 초기화 전에 사용하면 에러 발생

Kotlin lateinit

Kotlin에서는 lateinit을 사용할 수 있다.

lateinit var name: String

fun main() {
    name = "hyunsoo"
    println(name)
}

lateinit은 다음 조건에서 사용할 수 있다.

Kotlin lateinit
- var에만 사용 가능
- null이 아닌 타입에 사용
- primitive 타입에는 사용 불가
- 클래스 프로퍼티에서 주로 사용
- 초기화 전에 사용하면 에러 발생

예를 들어 Android에서 View나 객체를 나중에 초기화할 때 사용할 수 있다.

private lateinit var adapter: UserAdapter

Kotlin lazy

Kotlin의 lazy는 처음 사용할 때 값을 초기화한다.

val name: String by lazy {
    "hyunsoo"
}

lazy는 값 생성 비용이 크거나, 실제로 사용될 때까지 초기화를 미루고 싶을 때 사용한다.

Kotlin lazy
- val에 사용
- 처음 접근할 때 초기화
- 한 번 초기화되면 같은 값 재사용
- 무거운 객체 생성 지연에 유용

정리하면 다음과 같다.

Dart
- late

Kotlin
- lateinit
- lazy

사용 기준은 다음과 같다.

Dart late
- 나중에 값을 넣을 변수

Kotlin lateinit
- 클래스 프로퍼티를 나중에 초기화할 때
- var만 가능

Kotlin lazy
- 처음 사용할 때 초기화하고 싶을 때
- val에 사용

10. const, final, val 비교

Dart의 const는 컴파일 전에 이미 알고 있는 값을 의미한다.

const pi = 3.14;

Dart의 final은 실행 중에 값이 결정되어도 되지만, 한 번 정하면 변경할 수 없다.

final now = DateTime.now();

DateTime.now()는 실행 중에 알 수 있는 값이므로 const로 만들 수 없다.

const now = DateTime.now(); // 에러

Kotlin에서는 변경 불가능한 변수에 val을 사용한다.

val name = "hyunsoo"

그리고 컴파일 타임 상수에는 const val을 사용한다.

const val BASE_URL = "https://example.com"

const val은 보통 클래스 밖, object 안, companion object 안에서 사용한다.

object ApiConfig {
    const val BASE_URL = "https://example.com"
}

정리하면 다음과 같다.

Dart final
- 한 번만 할당 가능
- 실행 중 결정되는 값 가능

Dart const
- 컴파일 전에 값이 정해져 있어야 함

Kotlin val
- 한 번만 할당 가능
- 실행 중 결정되는 값 가능

Kotlin const val
- 컴파일 전에 값이 정해져 있어야 함

비슷한 개념끼리 비교하면 다음과 같다.

Dart final
≈ Kotlin val

Dart const
≈ Kotlin const val

11. 전체 비교 정리

Dart와 Kotlin은 둘 다 모바일 앱 개발에 많이 사용되는 언어지만 사용하는 환경과 UI 구성 방식이 다르다.

Dart
- Flutter 개발에 사용
- Widget Tree 기반 UI
- Android, iOS, Web, Desktop 지원

Kotlin
- Android Native 개발에 사용
- XML 기반 View Tree 또는 Compose 기반 UI
- Android 개발에 최적화

문법 비교는 다음과 같다.

변수 선언 위치

Dart
- String name = 'hyunsoo';

Kotlin
- val name: String = "hyunsoo"
값 변경 가능 여부

Dart
- var: 변경 가능
- final: 변경 불가능

Kotlin
- var: 변경 가능
- val: 변경 불가능
Null Safety

Dart
- String?
- name?.length

Kotlin
- String?
- name?.length
유연한 타입

Dart
- dynamic

Kotlin
- Any
나중에 초기화

Dart
- late

Kotlin
- lateinit
- lazy
컴파일 타임 상수

Dart
- const

Kotlin
- const val

12. 정리

Dart와 Kotlin의 핵심 차이는 다음과 같다.

Dart
- Flutter에서 사용
- 모든 UI를 Widget으로 구성
- Widget Tree 기반
- var, final, const, late 사용
- dynamic 제공

Kotlin
- Android Native에서 사용
- XML View 또는 Compose로 UI 구성
- View Tree 또는 Composable Tree 기반
- var, val, const val, lateinit, lazy 사용
- Any 제공

간단히 말하면 다음과 같다.

Flutter를 공부한다
-> Dart 문법과 Widget Tree 이해가 중요

Android Native를 공부한다
-> Kotlin 문법과 View / Compose 구조 이해가 중요

Dart와 Kotlin은 문법이 비슷한 부분도 많지만,
UI를 구성하는 방식과 일부 키워드의 의미가 다르기 때문에 비교하면서 공부하면 더 이해하기 쉽다.

profile
Jetpack Compose 리팩터링&운영 경력 및 Flutter 경험을 보유한 앱 개발자

0개의 댓글