코틀린-기초
코틀린의 개념과 기초 문법을 알아보자

코틀린(Kotlin) 기초 시리즈의 첫 번째 시간입니다. 이번 시간에는 코틀린을 소개하고 코틀린 프로그래밍을 위한 개발 환경을 설정하는 법 그리고 코틀린의 기초 문법을 알아보겠습니다.

코틀린-소개-기초
코틀린 소개 Marc Reichelt | Unsplash

소개

정의

코틀린이란 젯브레인(JetBrains)에서 만든 오픈 소스(open-source) 크로스 플랫폼(cross-platform) 언어입니다. 코틀린은 현재 구글(Google)의 안드로이드(Anroid) 공식 개발 언어입니다.

플랫폼(platform)
컴퓨터 CPU와 운영체제(operating system, OS)의 조합을 이르는 말로 플랫폼마다 고유한 언어를 사용함
크로스 플랫폼(cross-platform)
특정 운영체제에 제한되지 않고 여러 운영체제에서 동작할 수 있는 소스를 한 번에 프로그래밍하는 기술로 멀티 플랫폼(multi-platform)이라고도 부름
네이티브(native)
크로스 플랫폼에 반대되는 단어로 소프트웨어를 특정 기기 또는 운영체제에 맞춰 개발하는 기술

장점

코틀린은 크게 세 가지 장점을 지니고 있습니다. 첫째, 코틀린은 자바(Java)와 완벽하게 상호운영(interoperable)됩니다. 완벽하게 상호 운영된다는 뜻은 코틀린과 자바(Java)가 100% 호환된다는 의미입니다. 코틀린과 자바의 개발 환경에서는 서로의 라이브러리를 그대로 가져와 사용할 수 있습니다.

둘째, 코틀린은 프로그램의 안정성이 높습니다. 코틀린은 정적 언어(statically typed language)입니다. 프로그래밍 언어 종류와 언어별 특징에서도 살펴보았듯 정적 언어는 자료형을 컴파일 단계에서 미리 검사하여 확정하는 언어입니다. 자료형의 오류를 초기에 발견할 수 있기 때문에 안정적입니다.

널 포인터(Null pointer)로 인해 프로그램이 중단되지 않는 것도 코틀린의 장점입니다. 널 포인터 예외(NullPointerException, NPE)는 객체에 값이 할당되지 않았을 때, 즉 null 값이 할당되었을 때 해당 객체를 호출하면 발생하는 오류입니다.

다른 프로그래밍 언어에서는 참조된 변수가 null이면 프로그램이 중단됩니다. 하지만 코틀린에서는 기본적으로 null 값을 허용하지 않습니다. 대신 nullable 변수를 사용합니다. nullable 변수는 null을 데이터 타입(data type)으로 갖는 변수입니다. 그래서 이 변수는 참조해도 오류가 발생하지 않습니다. 코틀린에서 nullable 변수를 사용하는 법은 아래 기초 문법에서 살펴보겠습니다.

마지막으로 코틀린을 사용하면 코드를 간결하게 작성할 수 있습니다. 코틀린은 객체지향 프로그래밍(Object-Oriented Programming, OOP)과 더불어 함수형 프로그래밍(Functional Programming, FP)을 지원하는 다중 패러다임(paradigm) 언어입니다. 함수형 프로그래밍의 기법을 사용하면 코드를 단순화하고 같은 양의 코드로도 더 많은 일을 할 수 있습니다. 또한 코틀린에서는 최신 프로그래밍의 언어 경향에 따라 코드 끝에 관례로 사용하던 세미콜론(;)을 붙이지 않아도 됩니다. 그래서 코드가 더 깔끔합니다.

플랫폼 기술

이처럼 많은 장점이 있는 코틀린은 안드로이드 앱 개발 외에도 풀스택(full-stack) 웹과 iOS앱, 임베디드 IoT 등 다양한 분야의 개발을 지원합니다. 코틀린이 사용하는 플랫폼은 다음과 같습니다.

  • Kotlin/JVM: JVM에서 동작하는 자바(Java) 기반 애플리케이션을 만드는 기술
  • Kotlin/JS: 자바스크립트(JavaScript, JS)로 웹 브라우저에서 동작하는 애플리케이션을 만드는 기술
  • Kotlin/Native: LLVM 컴파일러를 이용해 멀티 플랫폼을 타깃으로 하는 애플리케이션을 만드는 기술

우리가 여기서 다룰 것은 Kotlin/JVM 기술입니다. 코틀린은 자바와 마찬가지로 자바 바이트코드(Java Bytecode)로 컴파일(compile)되어 자바 가상 머신(Java Virtual Machine, JVM)에서 동작합니다. 자바 바이트코드는 JVM이 이해할 수 있는 이진 코드입니다. JVM은 자바 플랫폼에서 자바 프로그램을 실행하는 가상 소프트웨어 시스템이고요.

자바가 등장하기 전 개발자들은 플랫폼별로 매번 컴파일 작업을 수행해야 했습니다. 사용하는 운영체제(operating system, OS)마다 서로 다른 컴파일 결과물을 만들어냈죠. 한 플랫폼에서 컴파일된 결과물은 다른 플랫폼에서 다시 사용할 수 없었습니다.

바이트코드
CPU가 아닌 가상 머신이 이해할 수 있는 이진 코드(binary code) 언어
컴파일(compile)
여러 소스 코드를 하나의 목적 파일(object file)로 번역하는 과정
LLVM
여러 플랫폼을 위한 중간 언어(비트코드)를 생성해 ARM(iOS, Android), x86(일반 데스크톱 PC 환경 - MacOS, Windows, Linux) 등에서 모두 실행할 수 있는 코드를 만드는 도구

JVM은 바이트코드라는 수단을 통해서 컴파일 과정의 플랫폼 종속성 문제를 해결합니다. 특정 운영체제에 종속되지 않는 공용어, 소스 코드 파일과 기계어 파일의 중간 코드인 바이트코드를 통해서 말이죠. JVM은 컴파일 과정의 결과물이 여러 운영체제로 나뉘기 전에 일종의 중앙 허브 역할을 해서 바이트코드를 각 운영체제에 적합한 기계어로 번역해줍니다. 이제는 사용하고 있는 운영체제에 상관없이 컴파일 과정을 1번만 수행하면 됩니다.

JVM-허브
바이트코드의 집결지 JVM Ian Battaglia | Unsplash

물론 JVM은 운영체제별로 다릅니다. 제조사들은 각 운영체제에 맞는 JVM을 개발해야 합니다. 하지만 개발자들은 자유로워졌습니다. 운영체제를 고려하지 않고 프로그램을 개발하고 JVM 위에 자바 프로그램을 돌리기만 하면 되기 때문입니다. JVM과 바이트코드는 운영체제마다 달랐던 프로그래밍 언어의 컴파일 과정을 단일화하면서 개발 생산성을 높입니다.

지금까지 코틀린을 간략하게 소개하고 코틀린의 장점과 플랫폼 기술을 살펴보았습니다. 그럼 지금부터는 코틀린으로 실습하기 위해 프로그래밍 환경 설정법을 알아보겠습니다.

환경 설정

코틀린을 실습하기 위해서는 두 가지 준비물이 필요합니다. 하나는 자바 개발 키트(Java Development Kit, JDK)이고 다른 하나는 통합 개발 환경(Integrated Development Environment, IDE)입니다.

JDK는 자바 기반의 소프트웨어를 "개발"을 위해 필요한 도구 패키지입니다. JDK는 JVM 상에서 코틀린을 실행하고 자바 라이브러리를 이용하기 위해 필요합니다. JDK의 구성 요소는 다음과 같습니다.

  • 자바 런타임 환경(Java Runtime Environment, JRE): JVM에서 실행하기 위한 자바 애플리케이션을 로드하는 온디스크 프로그램으로 JVM 포함
  • 자바 컴파일러(Java compiler): javac.exe로 자바 소스 코드 파일(.java)을 자바 바이트코드(Java Bytecode) 파일(.class)로 번역
  • 디버깅 툴(debugging tools)
  • 각종 자바 개발 툴(javap 등)

JDK는 코틀린으로 안드로이드 앱 개발을 위해 필요한 종합 개발 키트라고 생각하면 됩니다. 여기서는 JDK로 OpenJDK인 Zulu를 IDE로는 인텔리제이 IDE(IntelliJ IDEA)를 사용하겠습니다. Zulu와 인텔리제이 IDE의 설치 방법은 Kotlin 코틀린 프로그래밍 환경 설정하기(JDK, IDE 설치)를 참조해주세요. 인텔리제이 IDE 외에 다양한 종류의 개발 환경을 알고 싶은 분들은 통합 개발 환경(IDE), 텍스트 에디터 인기 순위 (2022년)를 참조해주세요.

JDK와 IDE 준비를 완료하셨다면 지금부터는 코틀린의 기초 문법을 알아보겠습니다.

기초 문법

기본 코드 구조

코틀린에서 가장 기본적인 코드는 fun main()입니다. main 함수는 코틀린 앱의 진입점(entry point)입니다. 이 함수에 코드를 넣으면 코드가 실행됩니다. 코드는 함수 다음에 나오는 중괄호({}) 안에 작성합니다. 예제로 살펴보겠습니다.

화면에 "Hello, World!"와 "Have a nice day."라는 문자열을 출력하려고 합니다. 문자열을 출력하려면 println 함수를 사용하면 됩니다. println은 코틀린의 출력 함수로 작성한 코드를 한 줄씩 출력합니다. 예제 코드는 다음과 같습니다.

kotlin
fun main() {
println("Hello, World!")
println("Have a nice day.")
}
/* 결과 */
Hello, World!
Have a nice day.

만약 문자열을 한 줄에 모두 출력하고 싶다면 print 함수를 사용하면 됩니다. print 함수는 문자열을 한 줄에 모두 출력해주는 함수입니다. 예제 코드는 아래와 같습니다.

kotlin
fun main() {
print("Hello, World! ")
print("Have a nice day.")
}
/* 결과 */
Hello, World! Have a nice day.

두 문자열 사이에 간격을 두기 위해 "Hello, World! "에서 느낌표(!) 다음에 공백을 넣었습니다. 만약 print 함수를 사용하면서 문자열을 한 줄씩 출력하려면 아래와 같이 개행 문자 \n을 사용하면 됩니다.

kotlin
fun main() {
print("Hello, World!\n")
print("Have a nice day.")
}
# 결과
Hello, World!
Have a nice day.

주석

위에서 살펴본 코드는 코틀린의 기본 코드 구조입니다. 여기에 코드를 설명하는 주석을 달고 싶다면 // 또는 /* */ 기호를 이용하면 됩니다.

  • //: 한 줄 주석
  • /* */: 여러 줄 주석

예제 코드는 다음과 같습니다.

kotlin
fun main {
// 한 줄 주석
var greeting = "Hello"
/* 여러 줄 주석
* greeting에 다시 "Hi" 값 할당
*/
greeting = "Hi"
}

지금까지 코틀린의 기본 코드 구조를 살펴보았습니다. 다른 프로그래밍 언어에서와 마찬가지로 코틀린에서도 값을 참조하는 매커니즘인 변수(variables)를 이용하면 코드를 더 간략하게 표현할 수 있습니다. 코틀린에서 변수는 어떻게 사용할 수 있을까요?

변수

코틀린에서 변수를 사용하려면 선언 방법과 선언 규칙을 이해해야 합니다. 먼저 변수를 선언하는 방법부터 알아보겠습니다.

선언 방법

코틀린에서 변수를 선언할 때는 val 또는 var 키워드를 사용하면 됩니다. 두 키워드를 나누는 기준은 "변수에 할당된 값의 변경 여부"입니다. 결론부터 말하면 val은 값을 변경할 수 없는 변수를 선언할 때 사용합니다. 반면 var은 값을 변경할 수 있는 변수를 선언할 때 사용합니다. 먼저 val부터 알아보겠습니다.

val

val은 변경 불가능한(immutable) 참조를 저장하는 변수입니다. val은 값을 뜻하는 "value"에서 따왔습니다. val로 선언한 변수는 초기화하고 나면 변수에 할당된 값을 변경할 수 없습니다. 다음은 기본 코드 구조에서 살펴본 예제에서 "Hello, World!"를 출력하는 코드를 val을 이용해 작성한 예제입니다.

kotlin
fun main() {
val greeting = "Hello, World!"
println(greeting)
}
/* 결과 */
Hello, World!

val로 선언한 변수에 할당된 값을 변경하려고 하면 Val cannot be reassigned라는 오류가 발생합니다. 이 오류는 val로 선언한 변수의 값은 재할당될 수 없다는 의미입니다. 예제 코드는 다음과 같습니다.

kotlin
fun main() {
val greeting = "Hello, World!" // val은 한 번 지정하면 변경할 수 없는 변수
greeting = "Hi"
print(greeting)
}
/* 결과 */
Val cannot be reassigned
var

반면 var은 변경가능한(mutable) 참조를 저장하는 변수입니다. var은 변수를 뜻하는 "variable"에서 가져왔습니다. var로 선언된 변수에 할당된 값은 언제든 변경할 수 있습니다. 다음은 var로 선언한 변수의 값을 바꾸는 예제입니다.

kotlin
fun main() {
var greeting = "Hello, World!" // var은 값을 업데이트할 수 있는 변수
greeting = "Hi"
print(greeting)
}
/* 결과 */
Hi

위 코드를 실행하면 변경한 값인 "Hi"가 출력됩니다. 변수의 키워드로 값을 재할당할 수 있는 var을 사용했기 때문이죠.

선언 규칙

코틀린에서 변수를 선언하는 데는 몇 가지 지켜야 할 규칙이 있습니다.

표기법

코틀린에서는 변수를 선언할 때 첫 단어만 소문자로 시작하고 나머지는 대문자로 표기하는 카멜(camel) 표기법을 사용합니다.

자료형 선언과 생략

코틀린에서 변수를 사용할 때 원칙적으로는 변수의 자료형을 선언합니다. 자료형은 데이터를 식별하는 분류 체계입니다. 자료형의 종류에 대해서는 아래에서 자세히 다루겠습니다. 여기서는 가장 기본적인 정수 자료형을 예로 들어보겠습니다. 정수 자료형인 값을 참조하는 변수를 선언하고 싶다면 초기화 식을 이용해 다음과 같이 작성할 수 있습니다.

kotlin
val myValue: Int = 5

초기화 식은 변수가 참조할 값의 자료형을 추론하기 위해 필요합니다. 초기화 식이 없으면 컴파일러가 변수에 대해 아무런 정보를 가지고 있지 않기 때문에 에러가 발생합니다. 만약 초기화 식을 사용하지 않고 변수를 선언하려면 반드시 변수의 자료형을 미리 명시해야 합니다. 다음은 초기화 식을 사용하지 않고 변수 타입을 선언하는 예제입니다.

kotlin
val myValue: Int // 변수 자료형 미리 명시
myValue = 5

그런데 코틀린을 실무에서 사용할 때는 보통 변수의 자료형을 생략합니다. 코틀린에서는 타입 추론(type inference)이 가능합니다. 타입 추론(또는 자료형 추론)이란 변수나 함수를 선언하거나 연산이 이루어질 때 자료형을 코드에 명시하지 않아도 코틀린에서 자료형을 자동으로 추론해주는 기능입니다.

코틀린의 컴파일러는 변수를 선언하는 시점에 초기화 식으로부터 변수의 타입을 추론합니다. 그리고 변수 선언 이후 변수에 값을 재할당할 때는 이전에 추론한 변수의 자료형을 바탕으로 대입문의 자료형을 검사합니다. 위에서 살펴본 코드는 아래와 같이 자료형을 생략해서 더 간단하게 작성할 수 있습니다.

kotlin
val myValue = 5
val 기본 사용

코틀린으로 코드를 작성할 때는 기본적으로 모든 변수를 val 키워드를 이용해 불변 변수로 선언하는 것이 좋습니다. 변수를 var 키워드로 선언하면 변수에 할당된 초기값의 자료형과 다른 자료형을 지닌 값으로 변경했을 때 컴파일 오류가 생길 수 있습니다. 예제로 살펴보겠습니다. 다음 코드를 실행하면 오류가 발생합니다.

kotlin
var vegetableName = 52 // 정수
vegetableName = "오이" // 문자열

위 코드에서 변수 선언 시점에 컴파일러가 추론한 자료형은 정수형(integer)입니다. 하지만 변수에 값을 재할당할 때는 자료의 타입이 문자열(string)입니다. 처음 선언되었을 때 추론된 자료형과 이후 값을 변경했을 때 변수의 자료형이 달라서 오류가 발생합니다. 따라서 이후에 값을 변경하는 특별한 경우를 제외하고서는 변수를 선언할 때 val을 사용하는 것이 좋습니다.

값 할당

코틀린에서 변수를 선언할 때는 반드시 변수에 값을 할당해야 합니다. 만약 변수에 값을 할당하지 않으면 아래와 같이 오류가 발생합니다.

kotlin
fun main() {
var carrots: Int
println(carrots) // 값이 할당되지 않은 변수를 사용하려고 함
}
/* 결과 */
Variable 'carrots' must be initialized

변수를 선언하는 시점에 반드시 변수에 값을 할당해야 하는 것은 아닙니다. 변수를 참조하여 실행하기 전까지만 값을 할당하면 됩니다. 다음 코드는 오류 없이 작동합니다.

kotlin
fun main() {
var carrots: Int
carrots = 5
println(carrots)
}
/* 결과 */
5
null 값

상황에 따라 변수에 값이 할당되지 않았다는 것 자체를 정보로 사용하고 싶은 경우도 있습니다. 코틀린의 장점에서 살펴본 것처럼 null을 값 자체로 갖는 nullable 변수를 선언하는 것입니다. nullable 변수를 선언하려면 변수 자료형 다음에 물음표(?) 기호를 사용하면 됩니다. 예제는 다음과 같습니다.

kotlin
var myValue: Int? = null // 자료형(Int) 다음에 물음표
fun main() {
println(myValue)
}
/* 결과 */
null

지금까지 코틀린에서 변수를 선언하는 방법과 변수 선언 규칙을 살펴보았습니다. 지금부터는 코틀린의 자료형을 살펴보겠습니다.

자료형

코틀린의 원시 자료형(primitive data type)에는 숫자 자료형, 문자 자료형, 문자열 자료형, 논리 자료형이 있습니다. 먼저 숫자형 자료형부터 알아보겠습니다. 숫자형 자료형에는 정수형 자료형과 실수형 자료형이 있습니다.

숫자 자료형: 정수

정수 자료형은 자료형이 정수입니다. 정수 자료형의 종류와 각 자료형의 크기, 그리고 표기할 수 있는 숫자의 범위는 다음과 같습니다.

  • Byte: 8비트 (최소 -128 ~ 최대 127)
  • Short: 16비트 (최소 -32768 ~ 최대 32767)
  • Int: 32비트 (최소 -2^31 ~ 최대 2^31 - 1)
  • Long: 64비트 (최소 -2^63 ~ 최대 2^63 - 1)

코틀린에서 숫자는 10진수, 2진수, 16진수 리터럴로 표기할 수 있습니다. 8진수는 지원하지 않습니다. 먼저 10진수를 표기하는 방법부터 알아보겠습니다.

10진수 리터럴

코틀린에서 10진수를 표기하는 방법은 우리가 일상생활에서 숫자를 표기하는 방법과 같습니다. 예제 코드는 다음과 같습니다.

kotlin
fun main() {
// Byte(8비트)
val byteValue: Byte = 5
// Short(16비트)
val shortValue: Short = 5
// Int(32비트)
val intValue: Int = 5
// Long(64비트)
val longValue: Long = 5L
}

64비트인 Long 타입의 10진수에는 더 큰 메모리를 사용하는 정수라는 것을 표시하기 위해 숫자 뒤에 L을 붙입니다.

16진수 리터럴

코틀린에서 16진수를 표기할 때는 16진수 앞에 0x를 붙입니다. "x"는 16진수를 의미하는 "hexadecimal number"에서 "hex"의 x를 의미합니다. 예제 코드는 다음과 같습니다.

kotlin
fun main() {
var intValueByHex: Int = 0x1af // 16진수 표기
}
2진수 리터럴

코틀린에서 2진수를 표기할 때는 2진수 앞에 0b를 더해줍니다. 여기서 "b"는 2진수를 뜻하는 "binary number"의 첫 글자 b를 의미합니다.

kotlin
var intValueByBin:Int = 0b110110

지금까지 10진수, 16진수, 2진수 정수 자료형을 표기하는 법을 알아보았습니다. 이번에는 숫자 자료형 중 실수형 데이터를 표기하는 법을 알아보겠습니다.

숫자 자료형: 실수

실수형 자료형은 자료형이 실수(real number)입니다. 실수 자료형의 종류와 각 자료형의 크기는 다음과 같습니다.

  • float: 32비트
  • double: 64비트

실수 자료형의 기본값은 double입니다. 따라서 float을 따로 표기를 하지 않으면 기본적으로 double 형으로 선언됩니다. 만약 자료형을 float으로 선언하고 싶다면 값 끝에 F 또는 f를 붙이면 됩니다. 예제 코드는 다음과 같습니다.

kotlin
fun main() {
// Float(32비트)
val floatValue: Float = 13.37F // Float 형 선언
// Double(64비트)
val doubleValue: Double = 3.14159
val doubleValueWithExp: Double = 3.141e10 // 지수 표기법
}

문자 자료형과 문자열 자료형

문자 자료형은 자료형이 1개의 문자입니다. 문자 자료형은 Char로 선언합니다. 각 문자는 2바이트의 메모리 공간을 차지합니다. 코틀린에서 문자 한 개를 표기할 때는 작은따옴표('')를 사용합니다.

kotlin
fun main() {
val letterCharValue: Char = 'A'
val digitCharValue: Char = '$'
}

만약 문자열을 표기하고 싶다면 자료형으로 String을 사용해야 합니다. 문자열 자료형은 유니코드 중에서 UTF-16 BE 인코딩 방식을 사용하는데, 이 인코딩 방식에서는 문자 한 개가 2바이트입니다. 코틀린에서 문자열을 표기할 때는 큰따옴표("")를 사용합니다. 예제 코드는 다음과 같습니다.

kotlin
fun main() {
val stringValue: String = "Hello, World!"
var firstCharInStr = stringValue[0] // 문자열의 첫 번째 값에 접근
var lastCharInStr = stringValue[stringValue.length - 1] // 문자열의 마지막 값에 접근
var myLength = stringValue.length
println("First: " + firstCharInStr)
println("Last: $lastCharInStr")
println("Length: ${stringValue.length}")
}
/* 결과 */
First: H
Last: !
Length: 13

여러 줄로 이루어진 문자열을 사용하려면 3중 따옴표(""")를 사용하면 됩니다.

kotlin
fun main() {
val multilineStringValue = """Hello, World! Good to see you."""
println(multilineStringValue)
}
/* 결과 */
Hello, World! Good to see you.

논리 자료형

논리 자료형은 불린(Boolean)값을 가집니다. 불린 리터럴을 표기할 때는 true 또는 false를 사용하면 됩니다.

kotlin
fun main() {
var isSunny: Boolean = true
println(isSunny)
}
/* 결과 */
true

다음은 지금까지 배운 자료형을 정리한 내용입니다.

kotlin
fun main() {
// 숫자 자료형(정수)
var a1 = 1 // 10진수 정수 자료형 Int
var a2 = 0xABCD // 16진수는 Int로 추론됨
var a3 = 0b01231 // 2진수는 Int로 추론됨
var a4 = 1L // Long
// 숫자 자료형(실수)
var b1 = 1.5 // Double
var b2 = 1.5f // Float
// 문자 자료형과 문자열 자료형
var c1 = 'c' // 문자
var c2 = "Hello, World!" // 문자열
// 논리 자료형
var d = true // 불린
}

지금까지 코틀린의 기초 문법을 살펴보았습니다. 다음 시간에는 코틀린에서 함수(functions)를 사용하는 법을 살펴보겠습니다. 모두 수고 많으셨습니다.

참고 문헌

...

©2022 Snug Archive. All rights reserved.

Contact me at snugarchive@gmail.com.