Kotlin 치트 시트
마지막 업데이트
Hello World와 기초
Kotlin 프로그램은 최상위 main 함수에서 시작합니다. 클래스는 필요 없습니다.
| 문법 | 의미 |
|---|---|
fun main() { println("Hello") } | 프로그램의 진입점 |
println("text") | 표준 출력에 한 줄 출력 |
print("text") | 끝에 줄바꿈 없이 출력 |
// comment | 한 줄 주석 |
/* comment */ | 여러 줄 주석 |
val input = readLine() | stdin에서 한 줄 읽기 (nullable String?) |
val line = readln() | stdin에서 한 줄 읽기 (non-null, Kotlin 1.6+) |
변수 (val / var)와 타입
val을 우선하고, 값이 바뀌어야 할 때만 var를 쓰세요.
| 문법 | 의미 |
|---|---|
val name = "Ada" | 읽기 전용 변수, 타입은 String으로 추론 |
var count = 0 | 가변 변수 |
val age: Int = 30 | 명시적 타입 표기 |
Int, Long, Double, Float | 숫자 타입 (42, 42L, 3.14, 3.14f) |
Boolean, Char, String | true/false, 'a', "text" |
val big = 1_000_000 | 읽기 쉬운 리터럴을 위한 밑줄 |
"3".toInt(), "2.5".toDouble() | 문자열을 숫자로 변환 |
42.toString() | 숫자를 문자열로 |
const val MAX = 10 | 컴파일 타임 상수 (최상위 또는 object 안) |
문자열과 템플릿
문자열 템플릿이 거의 모든 곳에서 연결을 대체합니다.
| 문법 | 의미 |
|---|---|
"Hi, $name" | 문자열에 변수 삽입 |
"Total: ${price * qty}" | 식 삽입 |
s.length | 문자 수 |
s.uppercase(), s.lowercase() | 대소문자 변경 |
s.trim() | 앞뒤 공백 제거 |
s.contains("x"), s.startsWith("a") | 문자열 내부 검색 |
s.substring(0, 3) | 인덱스 범위로 자르기 |
s.split(",") | List<String>으로 분할 |
s[0] | 특정 인덱스의 문자 |
"""raw text""" | 이스케이프 없는 여러 줄 원시 문자열 |
"%.2f".format(3.14159) | 형식화된 출력 (3.14) |
널 안전성
타입은 기본적으로 non-null입니다. ?를 붙이면 null을 허용하고, 컴파일러가 그 처리를 강제합니다.
| 문법 | 의미 |
|---|---|
var s: String? = null | nullable 타입 |
s?.length | 안전 호출: s가 null이면 null 반환 |
s?.length ?: 0 | 엘비스 연산자: null일 때 대체 값 |
s!!.length | non-null 단언: s가 null이면 예외 발생 |
if (s != null) s.length | 검사 안에서 non-null로 스마트 캐스트 |
s?.let { println(it) } | null이 아닐 때만 블록 실행 |
val n = s?.toIntOrNull() | 변환하고, 잘못된 입력이면 null |
lateinit var x: String | 나중에 초기화하는 non-null 변수 |
연산자
| 연산자 | 의미 |
|---|---|
+ - * / % | 산술 (Int끼리의 /는 정수 나눗셈) |
+= -= *= /= %= | 복합 대입 |
++ -- | 증가 / 감소 |
== != | 구조적 동등성 (equals 호출) |
=== !== | 참조 동등성 (같은 객체) |
< > <= >= | 비교 |
&& || ! | 논리 and / or / not |
a in 1..10 | 범위 또는 컬렉션 포함 여부 |
x as Int, x as? Int | 캐스트 / 안전한 캐스트 (실패 시 null) |
x is String | 타입 검사 (성공 시 스마트 캐스트) |
제어 흐름 (if / when)
if와 when은 식이며 값을 반환합니다.
| 문법 | 의미 |
|---|---|
if (a > b) x else y | 식으로서의 if (Kotlin에는 삼항 연산자 ?:가 없음) |
if (c) { ... } else if (d) { ... } else { ... } | 연결된 분기 |
when (x) { 1 -> "one"; 2 -> "two"; else -> "other" } | 값과 비교 |
when (x) { 1, 2 -> "low" } | 한 분기에 여러 값 |
when (x) { in 1..9 -> "digit" } | 범위와 비교 |
when (x) { is String -> x.length } | 타입과 비교 (스마트 캐스트 포함) |
when { x < 0 -> "neg"; x == 0 -> "zero"; else -> "pos" } | 대상 없는 when, if/else if처럼 사용 |
val label = when (x) { ... } | 식으로서의 when (else 필요) |
반복문과 범위
| 문법 | 의미 |
|---|---|
for (i in 1..5) | 1부터 5까지 포함 범위 |
for (i in 1 until 5) | 1부터 4까지 (끝 제외) |
for (i in 0..<5) | 동일, 끝을 제외하는 범위 문법 (Kotlin 1.9+) |
for (i in 10 downTo 1 step 2) | 단계별로 카운트다운 |
for (item in list) | 컬렉션 순회 |
for ((i, item) in list.withIndex()) | 인덱스와 요소를 함께 |
for (ch in "abc") | 문자열의 문자 순회 |
while (cond) { ... } | 참인 동안 반복 |
do { ... } while (cond) | 최소 한 번 실행 |
break, continue | 반복 종료 / 다음 반복으로 건너뛰기 |
repeat(3) { println(it) } | 블록을 n번 실행 |
함수
| 문법 | 의미 |
|---|---|
fun add(a: Int, b: Int): Int { return a + b } | 함수 선언 |
fun add(a: Int, b: Int) = a + b | 단일 식 함수, 반환 타입 추론 |
fun greet(name: String = "world") | 매개변수 기본값 |
greet(name = "Ada") | 이름 있는 인자 |
fun log(vararg items: String) | 가변 개수 인자 |
fun say(): Unit | 반환 값 없음 (Unit은 생략 가능) |
val square = { x: Int -> x * x } | 변수에 저장한 람다 |
fun String.shout() = uppercase() + "!" | 확장 함수 |
fun apply(f: (Int) -> Int) = f(2) | 매개변수로서의 함수 |
list.map { it * 2 } | 후행 람다, it이 유일한 매개변수 |
리스트
listOf는 읽기 전용, mutableListOf는 변경 가능.
| 문법 | 의미 |
|---|---|
val nums = listOf(1, 2, 3) | 읽기 전용 리스트 |
val items = mutableListOf("a") | 가변 리스트 |
nums[0], nums.first(), nums.last() | 요소 접근 |
nums.getOrNull(9) | 안전한 접근, 범위를 벗어나면 null |
items.add("b"), items.remove("a") | 추가 / 제거 |
items[0] = "z" | 인덱스로 교체 |
nums.size, nums.isEmpty() | 크기와 비어 있는지 여부 |
x in nums, nums.contains(x) | 포함 여부 |
nums.indexOf(2) | 요소의 위치, 없으면 -1 |
nums.subList(0, 2) | 부분 리스트 (끝 제외) |
nums.take(2), nums.drop(1) | 처음 n개 / 처음 n개를 제외한 전부 |
nums.sorted(), nums.reversed() | 정렬된 / 뒤집힌 새 리스트 |
nums.sum(), nums.average(), nums.maxOrNull() | 집계 |
nums.joinToString(", ") | 하나의 문자열로 결합 |
Pair(1, "a"), 1 to "a", Triple(1, 2, 3) | 작은 고정 크기 튜플 |
맵과 셋
| 문법 | 의미 |
|---|---|
val m = mapOf("a" to 1, "b" to 2) | 읽기 전용 맵 |
val mm = mutableMapOf<String, Int>() | 가변 맵 |
m["a"] | 조회 (없으면 null) |
m.getOrDefault("z", 0) | 대체 값이 있는 조회 |
mm["c"] = 3 | 삽입 또는 갱신 |
for ((k, v) in m) | 구조 분해로 항목 순회 |
m.keys, m.values | 키 / 값 컬렉션 |
"a" in m | 키 포함 여부 |
val s = setOf(1, 2, 2) | 고유 값의 셋 ({1, 2}) |
mutableSetOf<Int>() | 가변 셋 |
컬렉션 연산 (람다)
| 문법 | 의미 |
|---|---|
list.map { it * 2 } | 각 요소 변환 |
list.filter { it > 2 } | 조건에 맞는 요소만 남기기 |
list.forEach { println(it) } | 각 요소에 대해 작업 |
list.any { it > 2 }, list.all { it > 0 } | 어떤 / 모든 요소가 조건에 맞는지 |
list.count { it % 2 == 0 } | 일치 개수 세기 |
list.find { it > 2 } | 첫 일치 또는 null |
list.sortedBy { it.length } | 키로 정렬 |
list.groupBy { it.first() } | Map으로 그룹화 |
list.distinct() | 중복 제거 |
list.fold(0) { acc, x -> acc + x } | 초기값으로 축약 |
list.zip(other) | 두 리스트를 짝짓기 |
list.flatMap { it.toList() } | 변환 후 평탄화 |
클래스와 데이터 클래스
| 문법 | 의미 |
|---|---|
class User(val name: String, var age: Int) | 주 생성자와 프로퍼티가 있는 클래스 |
val u = User("Ada", 36) | 인스턴스 생성 (new 없음) |
u.name, u.age = 37 | 프로퍼티 읽기 / 쓰기 |
data class Point(val x: Int, val y: Int) | equals, hashCode, toString, copy 자동 제공 |
p.copy(y = 5) | 일부 프로퍼티를 바꿔 복사 |
val (x, y) = p | 구조 분해 선언 |
open class Base, class Sub : Base() | 상속 (클래스는 기본적으로 final) |
override fun toString() = "..." | 멤버 오버라이드 |
object Config { val debug = true } | 싱글턴 |
companion object { fun create() = User("", 0) } | 정적 멤버에 해당 |
enum class Color { RED, GREEN } | 열거형 |
sealed class Result | 제한된 클래스 계층, when에서 완전 검사 |
interface Shape { fun area(): Double } | 인터페이스 |
오류 처리
| 문법 | 의미 |
|---|---|
try { ... } catch (e: Exception) { ... } | 예외 잡기 |
try { ... } finally { ... } | 항상 정리 코드 실행 |
val n = try { s.toInt() } catch (e: NumberFormatException) { 0 } | 식으로서의 try |
throw IllegalArgumentException("bad") | 예외 던지기 |
require(x > 0) { "x must be positive" } | 인자 검사 (IllegalArgumentException 발생) |
check(ready) { "not ready" } | 상태 검사 (IllegalStateException 발생) |
s.toIntOrNull() | 예외 대신 null 반환 |
runCatching { risky() }.getOrDefault(0) | 호출을 Result로 감싸기 |
스코프 함수
블록 안에서 객체를 다루기 위한 다섯 가지 작은 도우미.
| 문법 | 의미 |
|---|---|
x.let { it + 1 } | it이 객체, 블록 결과 반환 |
x.run { this + 1 } | this가 객체, 블록 결과 반환 |
x.apply { field = 1 } | this가 객체, 객체 자체 반환 |
x.also { println(it) } | it이 객체, 객체 자체 반환 |
with(x) { field } | 확장 함수 아님. this는 x, 블록 결과 반환 |
x?.let { ... } | 관용적인 널 검사 + 사용 |
찾고 있는 모든 Kotlin 문법을 한 페이지에. 이 Kotlin 치트 시트는 Android와 현대 JVM 상당 부분을 이끄는 언어의 빠른 참조입니다. 변수 선언, 널의 안전한 처리, when으로 분기, 범위 순회, 함수 작성, 리스트와 맵 다루기를 담았습니다.
여기 있는 모든 것은 표준 Kotlin이며 JVM, Android, Kotlin Multiplatform에서 똑같이 동작합니다. 필요한 부분을 복사하거나 Kotlin 플레이그라운드에서 바로 실행해 보세요. 스니펫을 붙여 넣고 Run을 누르면 브라우저에서 출력을 확인할 수 있습니다.
Kotlin 치트 시트 FAQ
이 Kotlin 치트 시트는 무료인가요?
when 분기, 널 안전성 연산자, 컬렉션 메서드를 확인해야 할 때마다 돌아오세요.Kotlin에서 val과 var의 차이는 무엇인가요?
val은 읽기 전용 참조를 선언합니다. 한 번 할당하면 다시 할당할 수 없습니다. var는 재할당할 수 있는 가변 변수를 선언합니다. val은 참조만 고정한다는 점에 주의하세요. val list = mutableListOf(1)에는 여전히 요소를 추가할 수 있습니다. Kotlin 스타일은 어디서나 val을 쓰고 재할당이 정말 필요할 때만 var로 바꾸는 것입니다.Kotlin의 널 안전성은 어떻게 동작하나요?
String은 절대 null을 담을 수 없고 String?은 담을 수 있습니다. 컴파일러는 null 경우를 처리하지 않은 채 nullable 값의 메서드를 호출하도록 두지 않습니다. 안전 호출 (s?.length), 엘비스 연산자의 대체 값 (s?.length ?: 0), 스마트 캐스트가 되는 널 검사 (if (s != null)), 그리고 최후의 수단으로 값이 실제로 null이면 NullPointerException을 던지는 non-null 단언 s!!가 있습니다.Kotlin에서 if 대신 when을 써야 할 때는 언제인가요?
when을, 단일 예/아니오 조건에는 if/else를 사용하세요. 둘 다 식이므로 val label = when (x) { ... }와 val label = if (c) a else b 모두 관용적입니다. 식으로 쓰이는 when은 모든 경우를 다뤄야 하며, 보통 else 분기를 추가하면 됩니다 (sealed 클래스와 enum은 else 없이도 가능합니다).Kotlin을 온라인에서 연습할 수 있나요?
when, 반복문, 함수, 컬렉션까지 안내하고 마지막에 무료 수료증을 발급합니다.