🍎 코틀린
코틀린(kotlin)은 IntelliJ, PyCharm 등 다양한 IDE를 선보인 것으로 유명한 JetBrains 사에서 오픈소스 그룹을 만들어 개발한 프로그래밍 언어이다.
기존에는 안드로이드 앱을 자바로 개발했지만, 코틀린의 등장으로 점점 대체 되어 가는 추세다.
- 2017년 구글에서 안드로이드 공식 언어로 지정되었고,
- 2021년 신규 앱 개발시 코틀린으로 개발할 것을 권장되고 있다.
코틀린은 어떻게 Java를 대체할 수 있었을까? 바로 JVM 기반 언어라는 특성 때문이다. JVM 기반 언어를 다르게 얘기해보자면, 어떠한 언어도 자바 바이트 코드(.class)로 컴파일할 수 있다면 동작할 수 있는 것이다.
jvm - 자바 프로그램을 실행하기 위한 실행 환경을 만들어 주는 소프트웨어다.
이러한 JVM은 흔히 자바를 설치할 때 설치하는 JDK (Java Development Kit)의 JRE(Java Runtime Environment)에 포함되어 있다.
jvm 기반 언어 - 사실 jvm 기반 언어라는 특성은 호환성이 매우 좋아 특별하기 때문에 코틀린 외에도 굉장히 많다. 대표적인 언어로, 스칼라(scala), 그루비(Groovy), Jython(Python)가 있다.
🍎 코틀린 언어의 특징
- 표현력과 간결함(expressive and concise): 매우 간결한 문법을 제공한다.
- 안전한 코드(safer code): 코틀린에서는 변수 선언시 널 허용과 불허용을 구분해서 선언할 수 있다.
- 상호 운용성(interoperable): Java와 100% 호환된다.
- 구조화 동시성(structed concurrency): 코루틴(coroutines) 기법을 이용하면 비동기 프로그래밍을 간소화할 수 있다.
🍎 기본 문법
📌 변수
변수는 val과 var 키워드로 선언한다.
- val (value): 불변성 변수
- var (variable): 가변성 변수
val data1 = 10
var data2 = 10
fun main(){
data1 = 20 // 오류!
data2 = 20 // 성공!
}
코틀린은 일반적으로 정적 타이핑 언어이지만, 타입 추론이 가능한 경우에는 위와 같이 생략할 수 있다. ⇒ 동적 타이핑 기능(타입 추론) 제공.
또한 명시적으로 타입을 선언할 수 있다.
val data1:Int = 10
- lateinit - 변수를 선언하면 변수의 초기화를 미룰 수 있다. (var 키워드로 선언한 변수에만 사용 가능. Java에서 Primitive 타입으로 분류되는 Int, Long, Short, Double, Float, Boolean, Byte에는 사용할 수 없음.)
- lazy 키워드는 변수 선언문 뒤에 by lazy {}형식으로 선언하며, 변수가 최초로 이용되는 순간 중괄호로 묶은 부분이 자동으로 실행되어 그 결과 값이 변수의 초기 값으로 할당된다.
타입 - 코틀린의 모든 변수는 객체이다. 자바에서 제공하는 원시 타입 개념 존재하지 않으며, 코틀린에서는 해당 타입들은 모두 객체로 제공된다.
- 기초 타입 객체: Int, Short, Long, Double, Float, Byte, Boolean
- 문자와 문자열: Char, String
- 모든 타입: Any (Java로 치면 Object)
- 반환문이 없는 함수: Unit
- null이나 예외를 반환하는 함수: Nothing
⇒ Noting으로 선언한 변수는 null만 대입할 수 있다.
- 컬렉션 타입(collection type) - 여러 개의 데이터를 표현하는 방법이며 Array, List, Set, Map 이 있다.
- Array - 배열을 표현하는 클래스로, 배열에 접근할 때는 대괄호([])를 이용하거나, set(), get() 함수를 이용한다. -> 배열의 타입은 제네릭(generic), 배열의 타입이 기초 타입이라면 Array를 이용하지 않고 각 기초 타입의 배열을 나타내는 클래스를 이용할 수 있다. arrayOf() 함수를 이용하면 선언과 동시에 할당할 수 있다.
- List, Set, Map - Collection 인터페이스를 타입으로 표현한 클래스이며, 컬렉션 타입 클래스라고 한다. 각 클래스의 특징은 다음과 같다.
- List - 순서가 있는 데이터 집합. 데이터의 중복 허용
- Set - 순서가 없으며 데이터의 중복을 허용하지 않음
- Map - 키와 값으로 이루어진 데이터 집합으로 순서가 없으며, 키의 중복을 허용하지 않음
- 널 허용과 불허용코틀린에서는 변수를 선언할 때 null을 대입할 수 있는 변수인지 아닌지 명확하게 구분해서 선언해야 한다.
- 널 허용으로 선언하려면 타입 뒤에 물음표(?)를 추가해야 한다.
- 코틀린의 모든 변수는 객체이기 때문에 null 값이 들어갈 수 있습니다. null 은 값이 할당되지 않은 상황을 의미한다.
📌 함수
함수를 선언할 때는 fun 키워드를 사용한다. 아래는 함수 선언 형식이다.
fun 함수명(매개변수명: 타입): 반환 타입 { ... }
- 함수의 매개변수에는 val이나 var 키워드를 사용할 수 없으며, val로 자동 적용된다. (즉, 불변 변수, 대입 불가)
- 함수의 매개변수에는 기본 값을 선언할 수 있다. 기본 값을 선언했다면, 호출시 인자를 전달하지 않아도 된다.
- 함수 호출 시 매개 변수 명을 지정하면 매개변수 값의 순서를 바꿀 수 있다. (명명된 매개변수)
Kotlin에서는 if~else는 표현식으로도 사용할 수 있다. 그리고 when 조건문, for 반복문, while 반복문 등을 사용할 수 있다.
📌 클래스, 생성자
클래스
- 객체(Object)를 만들기 위한 설계도라고 보면 되겠다.
- 필드(속성) + 메서드(기능)를 함께 가진다.
생성자
- 객체를 생성할 때 실행되는 함수, 즉 "문 열고 들어가는 방식"이다.
- 코틀린에서는 크게 2가지 생성자가 있다:
주 생성자 | 클래스 헤더에 정의됨 | (val name: String) |
보조 생성자 | 클래스 본문 내부에서 여러 개 정의 가능 | constructor(...) |
init 블록 | 객체 생성 직후 초기화 작업 실행 | init |
기본값 | 생성자 매개변수에 기본값 부여 | = 값 |
📌 람다식과 고차 함수
람다(Lambda)
- 익명 함수로, 이름 없이 함수처럼 사용 가능하다.
- 간결하게 함수형 프로그래밍 가능하다.
val add = { a: Int, b: Int -> a + b }
println(add(3, 4)) // 출력: 7
고차 함수
- 함수를 매개변수로 받거나, 반환하는 함수
fun operate(x: Int, y: Int, op: (Int, Int) -> Int): Int {
return op(x, y)
}
val result = operate(10, 5) { a, b -> a * b }
println(result) // 출력: 50
📌 데이터 클래스 (data class)
특징
- 단순히 데이터를 저장하기 위한 클래스
- 코틀린에서 data 키워드를 붙이면 toString, equals, copy 같은 함수들이 자동 생성된다.
- 자동으로 만들어지는 함수들:
- toString(): 문자열 표현
- equals(): 객체 비교
- hashCode(): 해시 값
- copy(): 복사본 생성
- 코루틴 (Coroutine)
- 비동기 작업을 간단하게 처리할 수 있는 코틀린의 핵심 기능
- Thread보다 가볍고 효율적
- suspend, launch, async, delay 등이 주요 키워드
- 주요 키워드
- runBlocking: 코루틴 실행을 기다림 (main에서 주로 사용)
- launch: 새 코루틴 시작 (비동기 실행)
- delay: 비동기 대기
- async: 값을 반환하는 비동기 작업
🍎 Compose
Jetpack Compose는 안드로이드 UI를 훨씬 쉽고, 선언형으로, 간결하게 만들어주는 혁신적인 도구로, 클래식 XML UI보다 가독성도 좋고, 생산성도 훨씬 높아졌다.
개념 | 설명 |
선언형 UI | '무엇을 보여줄 것인지'만 선언하면 된다. |
Composable 함수 | UI를 구성하는 빌딩 블록 |
상태(State) 관리 | UI 상태 변화에 따라 자동으로 리렌더링 |
재구성(Recomposition) | 상태가 바뀌면 필요한 부분만 다시 그린다. |
Slot API | 구성 요소를 유연하게 끼워넣는 방식 |
Modifier | UI 스타일링 및 동작을 부여하는 체이닝 객체 |
- Composable 함수 - 모든 UI 구성 요소는 @Composable로 시작하는 함수 안에서 정의된다.
- 상태 (State) 관리 - UI가 반응형이 되려면 상태 변화에 따라 자동으로 UI가 업데이트되어야 한다.
- Modifier - UI 요소의 크기, 정렬, 배경색, 클릭 이벤트 등을 설정할 때 사용한다.
- Modifier는 체이닝 방식으로 연결 가능
- 매우 유연하고 선언형 스타일과 잘 맞음
Text(
text = "Hello!",
modifier = Modifier
.padding(16.dp)
.background(Color.Yellow)
.clickable { println("클릭됨!") }
)
📌 레이아웃 기본 요소
컴포넌트 | 설명 |
Column | 위에서 아래로 정렬 |
Row | 왼쪽에서 오른쪽으로 정렬 |
Box | 겹치게 배치 |
LazyColumn | RecyclerView 대체 (스크롤 가능한 리스트) |
@Composable
fun MyLayout() {
Column(
modifier = Modifier.padding(16.dp)
) {
Text("제목", fontSize = 20.sp)
Row {
Text("왼쪽")
Spacer(modifier = Modifier.width(10.dp))
Text("오른쪽")
}
}
}
- Slot API (슬롯을 전달하는 방식) - 컴포즈에서는 UI 조각들을 함수에 인자로 넘길 수 있다. 이것이 Slot API.
@Composable
fun MyCard(content: @Composable () -> Unit) {
Card(
modifier = Modifier.padding(8.dp),
elevation = 4.dp
) {
content()
}
}
@Composable
fun Usage() {
MyCard {
Text("카드 안에 들어간 텍스트")
}
}
- Scaffold – 전체 구조를 잡는 컴포넌트 -> Scaffold는 앱의 구조(상단 바, FAB 등)를 쉽게 구성할 수 있게 해준다.
- 왜 Scaffold를 사용할까? 아래 요소들을 각자 따로 배치하려면 복잡하고 번거롭지만, Scaffold를 사용하면 딱 정해진 틀에 쉽게 끼워 넣기만 하면 된다.
요소 | 예시 |
상단 앱 바 | TopAppBar |
하단 네비게이션 | BottomNavigation |
플로팅 버튼 | FloatingActionButton |
바디(내용 영역) | Scroll 가능한 내용 등 |
스낵바 | 사용자 알림 (Snackbar) |
@Composable
fun MyScreen() {
Scaffold(
topBar = {
TopAppBar(title = { Text("내 앱") })
},
floatingActionButton = {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Add, contentDescription = "추가")
}
}
) {
// 본문 내용
Text("내용입니다")
}
}
- 각 요소가 자리와 역할이 명확히 분리되어 있어서 유지보수가 쉽고,
- 다양한 화면에도 동일한 레이아웃 틀을 재사용할 수 있다.
Scaffold 없이 구성한다면
Column {
TopAppBar(title = { Text("내 앱") })
// FAB는 어디 두지... 레이아웃 신경 써야 함
// Snackbar도 별도 상태관리 필요
}
- 각 컴포넌트를 직접 배치해야 하고, FAB나 Snackbar 위치, 동작 관리가 까다롭다.