Kotlin 기초 - 프로그래밍 처음이어도 괜찮아
Kotlin 문법을 처음 배우는 입장에서 이해하기 쉽게 정리한다. 변수, 함수, 클래스, null 처리.
Kotlin이란?
코드를 작성해서 프로그램을 만드는 언어다. JetBrains(IntelliJ 만든 회사)가 2011년에 만들었고, 지금은 서버 개발과 Android 앱 개발에 많이 쓰인다.
이 시리즈는 Kotlin으로 Spring Boot 서버를 만드는 과정을 다룬다. 서버란 앱이나 웹사이트가 데이터를 요청하면 응답해주는 프로그램이다.
[앱 / 웹] ──요청──▶ [서버 (Spring Boot)] ──▶ [데이터베이스]
◀──응답──
변수 - 데이터를 담는 상자
변수는 값을 저장해두는 공간이다.
val name = "김철수"
var age = 25
val: 한 번 넣으면 바꿀 수 없다var: 나중에 값을 바꿀 수 있다
val name = "김철수"
name = "이영희" // 오류! val은 변경 불가
var age = 25
age = 26 // OK
언제 val, 언제 var?
바뀔 일이 없는 값은 val, 바뀔 수 있는 값은 var. 기본적으로 val을 쓰고, 바꿔야 할 때만 var로 바꾸는 게 좋다.
타입
값의 종류를 타입이라고 한다.
val name: String = "김철수" // 문자열
val age: Int = 25 // 정수
val height: Double = 175.5 // 소수
val isStudent: Boolean = true // true / false
보통은 값만 넣어도 Kotlin이 알아서 타입을 파악한다.
val name = "김철수" // String이라는 걸 자동으로 앎
val age = 25 // Int라는 걸 자동으로 앎
Null - "값이 없음"을 표현하는 방법
null은 "값이 없다"는 뜻이다.
var nickname: String? = "KS"
nickname = null // "값이 없음" 상태로 변경
타입 뒤에 ?를 붙이면 null을 넣을 수 있고, 안 붙이면 null을 넣을 수 없다.
var a: String = "안녕"
a = null // 오류! ? 없으면 null 불가
var b: String? = "안녕"
b = null // OK
왜 이게 중요하냐면, null인 변수에서 뭔가 꺼내려 하면 프로그램이 터진다.
val nickname: String? = null
println(nickname.length) // 오류! null에서 length를 꺼낼 수 없음
Kotlin은 이 상황을 컴파일 단계에서 막아준다. null일 수 있는 변수는 그냥 쓰지 못하고, 반드시 처리해야 한다.
null 처리 방법
val nickname: String? = null
// 방법 1: if로 확인
if (nickname != null) {
println(nickname.length) // null이 아닐 때만 실행
}
// 방법 2: ?. (물음표 점) - null이면 그냥 건너뜀
println(nickname?.length) // null → 출력 안 함
// 방법 3: ?: (엘비스 연산자) - null이면 기본값 사용
val len = nickname?.length ?: 0 // null이면 0을 대신 사용
?: 이 기호가 엘비스 같이 생겨서 "엘비스 연산자"라고 부른다.
함수 - 코드 묶음에 이름 붙이기
같은 코드를 반복해서 쓰는 대신, 함수로 만들어두고 이름으로 호출한다.
fun greet(name: String) {
println("안녕하세요, $name!")
}
greet("철수") // 안녕하세요, 철수!
greet("영희") // 안녕하세요, 영희!
fun: 함수를 만든다는 표시greet: 함수 이름name: String: 받을 값(파라미터). 이름과 타입을 씀{ ... }: 실행할 코드
결과를 돌려주는 함수
fun add(a: Int, b: Int): Int {
return a + b
}
val result = add(3, 4)
println(result) // 7
뒤에 : Int는 이 함수가 Int 타입 값을 돌려준다는 뜻이다.
한 줄짜리는 =으로 줄일 수 있다.
fun add(a: Int, b: Int) = a + b
기본값
fun greet(name: String, message: String = "안녕하세요") {
println("$message, $name!")
}
greet("철수") // 안녕하세요, 철수!
greet("영희", "반갑습니다") // 반갑습니다, 영희!
파라미터에 기본값을 넣어두면 생략할 수 있다.
문자열 템플릿
문자열 안에 변수 값을 넣을 때 $를 쓴다.
val name = "철수"
val age = 25
println("이름: $name, 나이: $age")
// 이름: 철수, 나이: 25
// 계산이 필요하면 {} 사용
println("내년 나이: ${age + 1}")
// 내년 나이: 26
조건문
if
val score = 85
if (score >= 90) {
println("A")
} else if (score >= 80) {
println("B")
} else {
println("C")
}
결과를 변수에 바로 담을 수 있다.
val grade = if (score >= 90) "A" else if (score >= 80) "B" else "C"
when
여러 경우를 비교할 때 쓴다.
val day = "토"
when (day) {
"토", "일" -> println("주말")
"월" -> println("월요일 힘내자")
else -> println("평일")
}
범위로도 쓸 수 있다.
val score = 85
val grade = when {
score >= 90 -> "A"
score >= 80 -> "B"
score >= 70 -> "C"
else -> "F"
}
반복문
for
// 1부터 5까지
for (i in 1..5) {
println(i) // 1, 2, 3, 4, 5
}
// 1부터 4까지 (5 제외)
for (i in 1 until 5) {
println(i) // 1, 2, 3, 4
}
// 목록을 순서대로
val names = listOf("철수", "영희", "민수")
for (name in names) {
println(name)
}
while
조건이 참인 동안 반복한다.
var count = 0
while (count < 3) {
println("카운트: $count")
count++
}
// 카운트: 0
// 카운트: 1
// 카운트: 2
리스트 - 여러 값을 순서대로 담기
val fruits = listOf("사과", "바나나", "딸기")
println(fruits[0]) // 사과 (0번부터 시작)
println(fruits.size) // 3
// 추가/삭제가 필요하면
val mutableFruits = mutableListOf("사과", "바나나")
mutableFruits.add("딸기")
mutableFruits.remove("바나나")
자주 쓰는 함수들이 있다.
val numbers = listOf(3, 1, 4, 1, 5, 9, 2, 6)
val sorted = numbers.sorted() // [1, 1, 2, 3, 4, 5, 6, 9] 정렬
val evens = numbers.filter { it % 2 == 0 } // [4, 2, 6] 짝수만
val doubled = numbers.map { it * 2 } // 각 값을 2배
val sum = numbers.sum() // 31 합계
it은 현재 원소를 가리킨다.
클래스 - 관련된 데이터를 묶기
여러 변수를 하나로 묶어서 관리할 때 클래스를 쓴다.
class Person(val name: String, var age: Int)
val person = Person("철수", 25)
println(person.name) // 철수
println(person.age) // 25
person.age = 26 // 변경 가능
함수도 넣을 수 있다
class Person(val name: String, var age: Int) {
fun introduce() {
println("안녕하세요, 저는 $name이고 ${age}살입니다.")
}
fun isAdult(): Boolean {
return age >= 18
}
}
val person = Person("철수", 25)
person.introduce() // 안녕하세요, 저는 철수이고 25살입니다.
println(person.isAdult()) // true
data class
데이터를 담는 용도의 클래스다. Spring Boot에서 DB 데이터를 담거나 API 응답을 만들 때 많이 쓴다.
data class User(
val id: Long,
val name: String,
val email: String
)
val user = User(1, "철수", "chul@test.com")
println(user)
// User(id=1, name=철수, email=chul@test.com)
// 일부만 바꾼 복사본 만들기
val newUser = user.copy(name = "영희")
// User(id=1, name=영희, email=chul@test.com)
일반 class와 달리 toString(), 내용 비교(==), copy()가 자동으로 된다.
인터페이스
인터페이스는 클래스가 반드시 구현해야 할 함수 목록을 정의하는 것이다. 함수의 이름과 형태만 정하고, 실제 내용은 구현하는 클래스가 채운다.
interface Greetable {
fun greet(): String // 어떻게 인사할지는 정하지 않음. 형태만 정의
}
class KoreanGreeter : Greetable {
override fun greet() = "안녕하세요!" // 한국어로 구현
}
class EnglishGreeter : Greetable {
override fun greet() = "Hello!" // 영어로 구현
}
:는 "이 인터페이스를 구현한다"는 뜻이다. override는 "인터페이스에서 정의한 함수를 여기서 구현한다"는 표시다.
val greeter: Greetable = KoreanGreeter()
println(greeter.greet()) // 안녕하세요!
// 구현체를 바꿔도 사용하는 코드는 그대로
val greeter2: Greetable = EnglishGreeter()
println(greeter2.greet()) // Hello!
greeter 변수의 타입은 Greetable이다. 실제로 어떤 구현체가 들어있는지 몰라도 greet()를 호출할 수 있다. 이것이 추상화다.
인터페이스가 Spring에서 왜 중요한지는 다음 편(IoC/DI)에서 다룬다.
정리
| 개념 | 예시 | 용도 |
|---|---|---|
val | val name = "철수" | 바꾸지 않을 값 |
var | var age = 25 | 바꿀 수 있는 값 |
String? | var n: String? = null | null 허용 타입 |
fun | fun add(a: Int, b: Int) = a + b | 함수 정의 |
if / when | - | 조건 분기 |
for / while | - | 반복 |
listOf | listOf(1, 2, 3) | 읽기 전용 리스트 |
class | class Person(val name: String) | 데이터 + 함수 묶기 |
data class | - | 데이터 담는 클래스 |
기본 문법은 이 정도면 충분하다. 이 내용을 바탕으로 Spring Boot 서버를 만들고 싶다면 아래 시리즈를 참고하자.
Spring Boot 서버 만들기
이 문법들이 실제로 어떻게 쓰이는지 보고 싶다면 → Kotlin + Spring Boot 입문 시리즈