💡 개발자들을 대상으로 조사를 한 ‘Stack Overflow의 Developer Survey’와 ‘JetBrain의 Developer Ecosystem’을 통해서 Kotlin 언어에 대해서 개발자들은 어떻게 생각하고 사용하고 있는지에 대해서 기술적인 동향으로 확인합니다.
💡 매년 Stack Overflow 내에서 Developer Survey로 ‘Most popular technologies’를 투표로 결정합니다. 이는 매년 가장 인기 있는 기술에 대해서 투표하는 내용이며 해당 글에서는 2021년과 2022년을 비교하는 내용만 언급하였습니다.
💡 미비한 비교지만 Java의 사용량은 35.35% → 33.27%로 줄었고, Kotlin의 경우 8.32% → 9.16%로 상승하였습니다. (이는 모바일 환경이 아닌 웹 환경만 비교가 된 사항입니다.) 💡 해당 Survey에서는 급격하게 Java 언어를 사용하지 않고 Kotlin을 사용하고 있다라고 이야기할 수 없는 비교 자료이긴 하지만 요즘 개발자들은 어떤 것에 더 관심을 갖는지 확인할 수 있는 자료가 됩니다.
💡 Kotlin 이란 - 2011년 JetBrain 사에서 공개한 오픈소스 프로그래밍 언어이며 ‘자바 및 기타 JVM 언어와 호환되는 새로운 언어’이며 프로그래밍 언어입니다. - Kotlin은 ‘타입 추론’을 지원하는 ‘정적 언어'입니다. 그렇기에 코드가 컴파일될 때 타입이 결정되며 런타임 중에는 타입 변경을 할 수 없습니다.
1. *.kt 파일의 소스코드는 Kotlin Complier(kotlinc)를 통해서 컴파일이 됩니다. 2. 컴파일이 수행되면 *. class의 바이트 코드 파일로 생성이 됩니다. 3. 바이트 코드 형태의 파일은 JVM을 통해서 수행이 됩니다.
[ 더 알아보기 ]
💡 .class 파일 - Java와 Kotlin에서 각각의 소스코드들은 컴파일러를 통해서 *.class 파일로 변환이 되며 해당 파일은 바이트코드로 구성이 되어 있습니다.
💡 코틀린 컴파일러(kotlinc) - Kotlin 코드를 컴파일하는 데 사용되며 kotlinc.bat 또는 kotlinc.sh로 실행됩니다.
💡JVM(Java Virtual Machine) 이란? - 자바 프로그램을 실행시키기 위한 가상 머신입니다. 자바 언어로 작성된 소스 코드는 컴파일러를 통해 바이트 코드로 변환되고 JVM에서 실행됩니다. JVM은 운영체제와 자바 프로그램 사이에서 중개자 역할을 하며 자바 프로그램이 운영체제에 구애받지 않고 독립적으로 실행될 수 있도록 합니다.
💡 Kotlin 소스코드는 Java와 동일하게 JVM에 의해 컴파일 되어 수행이 되므로 Kotlin 프로젝트 내에서 Java 언어와 병행하여 사용이 가능하며 서로 호출을 하여 사용이 가능합니다. 💡 간단한 설정을 한다면 Kotlin → Java / Java → Kotlin 언어를 사용할 수 있습니다.
2.1. Kotlin에서 Java를 호출 (Kotlin → Java)
💡 Java 코드를 실행하기전에 build.gradle 파일 내에 java코드에 대한 plugins와 sourceSets로 경로를 지정해야 합니다.
- sourceSets는 Gradle 빌드 스크립트에서 사용되는 요소 중 하나입니다. sourceSets는 프로젝트의 소스 코드 디렉터리를 구성하기 위한 요소입니다. - 소스 셋이 구성되면, Gradle은 소스 코드를 컴파일하고 실행 가능한 바이너리를 생성할 때 이를 고려합니다.
💡 Kolitn 프로젝트 내에서 Java 파일로 CommonUtils.kt를 생성하였습니다.
💡 Nullable 타입과 Non-nullable 타입을 구분하여 컴파일러가 NullPointException을 런타임에서 방지할 수 있습니다. 💡 Kotlin은 null에 대해 안정성을 지원하는 언어로 컴파일을 수행할 때 null 참조를 방지하여 런타임 중에 발생할 수 있는 NullPointException을 방지할 수 있습니다. 💡“?” 연산자를 사용하여 변수가 null 일 수 있다는 것을 나타냅니다.
4.1. Nullable 타입
💡 변수의 값이 null이 될 수도 있고 아닐 수도 있다는 Nullable은 “?“ 를 이용하여 구성합니다.
var nullableString: String? = "Hello, world!"
nullableString = null// This is allowedif (nullableString != null) {
println(nullableString.length)
} else {
println("nullableString is null")
}
4.2. Not-nullable 타입
💡 변수의 값이 null 일수 없다는 Not-nullable은 “?” 연산자를 제외하면 됩니다. 💡 이를 통해 컴파일 단계에서 null에 대한 방지를 할 수 있으며 런타임에서 발생하는 NullPointException을 방지할 수 있습니다.
💡 Kotlin에서는 확장 함수와 확장 속성을 지원하여 기존의 클래스의 함수나 속성을 재구성하여 사용함으로써 코드의 재사용성과 가독성 증가합니다.
5.1. 확장 함수(Extension function)
💡 확장 함수(Extension function)란?
- 기존 클래스를 수정하지 않고도 새로운 기능을 추가할 수 있도록 해줍니다. - 이를 통해 코드의 가독성을 높일 수 있고 코드 중복을 줄이며 유지보수성을 높일 수 있습니다. - 확장 함수를 사용하면 이미 존재하는 클래스에 새로운 함수를 추가할 수 있지만 확장 함수는 해당 클래스의 내부 구조나 멤버 변수에 직접적인 접근이 불가능합니다. 즉, 해당 클래스에서 사용 가능한 함수들만 사용할 수 있습니다.
/**
* 확장 함수(Extension function)를 구성합니다.
* String.toTitleCase()의 경우 첫글자만 대문자로 바꾸는 함수입니다.
* 이를 이용하여 스페이스바를 기준으로 첫번째 문자를 대문자로 변경하고 반환하는 함수를 재 구성합니다.
*/fun String.toTitleCase(): String {
returnthis.split(" ").joinToString(" ") { it.capitalize() }
}
/**
* 확장 함수를 출력합니다.
*/@GetMapping("/extendFunc")funextensionFunc(): String {
val str = "hello world"
println(str.toTitleCase()) // 출력: "Hello World"return"hello"
}
[ 더 알아보기 ]
💡 String.toTitleCase() - 문자열의 각 단어의 첫 글자를 대문자로 바꾸는 함수입니다.
💡 String.capitalize() - 문자열의 첫 문자를 대문자로 변경하는 함수입니다.
5.2. 확장 속성(Extension property)
💡 확장 프로퍼티(Extension property)란? - 기존의 클래스에 “확장 속성”을 추가하여 기존 클래스의 속성을 확장할 수 있습니다. - 이를 통해 기존 클래스의 기능을 확장하거나 수정할 수 있는 강력한 방법입니다.
/**
* 1. 확장 프로퍼티(Extension property)를 구성합니다. : 짝수인지 여부를 판단하는 함수
*/
val Int.isEven: Boolean
get() = this % 2 == 0/**
* 확장 함수를 출력합니다.
*/@GetMapping("/extendFunc")
fun extensionFunc(): String {
valnum=4if (num.isEven) {
println("$num is even")
} else {
println("$num is odd")
}
return"hello"
}
/**
* 2. 확장 프로퍼티(Extension property)를 구성합니다. : 첫번쨰와 마지막 요소 값을 가져오는 함수
*/
val <T> MutableList<T>.firstElement: T
get() = this[0]
val <T> MutableList<T>.lastElement: T
get() = this[this.size - 1]
/**
* 확장속성을 이용하기 위한 리스트를 구성하고 첫번째 요소와 마지막 요소를 가져옵니다.
*/vallist= mutableListOf(1, 2, 3, 4, 5)
valfirst= list.firstElement // 1vallast= list.lastElement // 5
💡 코루틴(Couroutine)이란? - 코루틴은 비동기적으로 실행되는 경량 스레드입니다. - 코루틴은 스레드보다 더 가볍고 효율적인 방식으로 비동기 코드를 작성할 수 있도록 도와줍니다. - 일반적인 스레드와 달리 코루틴은 자체적으로 CPU 자원을 소비하지 않기 때문에 오버헤드가 적습니다. 또한, 코루틴은 멀티태스킹을 가능하게 하여 동시에 여러 작업을 수행할 수 있습니다.
💡 프로세스 내에서 1000개의 스레드를 생성하여 실행하는 경우 많은 CPU 자원을 소비하는 반면에 1000개의 코루틴을 실행하는 경우 적은 CPU 자원을 소비합니다. 💡 코루틴의 경우 하나의 Thread에 수천 개의 코루틴이 수행될 수 있습니다. 코루틴은 Thread 없이는 수행이 불가능 하지만 Thread를 옮겨가면서 수행할 수 있습니다.
💡 결론적으로 여러 개의 Thread를 수행하는 경우 CPU자원을 소비하지 않기 때문에 오버헤드가 적고 동시에 여러 작업을 할 수 있다(동시성 작업)는 장점이 있습니다.
💡 Kotlin은 JVM(Java Virtual Machine)에서 실행되는 언어이지만 JavaScript로도 컴파일할 수 있습니다. 따라서 Node.js 환경에서도 kotlin 코드를 컴파일하여 실행할 수 있습니다.
💡 Kotlin 컴파일러는 자바스크립트 코드를 생성하기 위해 필요한 모든 파일을 생성하며 이 파일들은 Node.js 환경에서 실행될 수 있습니다. 또한, Kotlin은 Node.js의 모든 기능을 사용할 수 있습니다.
💡 따라서, Kotlin 언어를 사용하여 Node.js 애플리케이션을 개발할 수 있습니다.
💡 Node.js의 Express.js와 함께 Restful API 형태로 구성이 가능합니다. 이를 위해서 Kotlin에서 Node.js의 http 모듈을 사용하여 서버를 만들어서 Express.js를 사용하여 라우팅 처리를 합니다. 이러한 방식으로 Kotlin 코드를 사용하여 Node.js 애플리케이션을 개발할 수 있습니다.
import http.*
import express.*
funmain(args: Array<String>) {
val app = express()
app.get("/", { req, res ->
res.send("Hello, World!")
})
http.createServer(app).listen(3000)
println("Server listening on port 3000")
}
💡 Kotlin은 다중상속을 지원하지 않지만 인터페이스를 이용하여 다중상속의 효과를 얻을 수 있습니다.
💡 해당 예시에서는 인터페이스 A, B를 구성하고 각각의 인터페이스 함수인 a(), b()를 구성하였습니다. 그리고 클래스 C에서 실제 A, B의 구현체로 구성을 하였습니다. 최종 main() 함수에서 구성된 C 클래스의 인스턴스를 구성하고 각각의 함수를 호출하여 다중상속 효과를 구성합니다.
interfaceA {
funa()
}
interfaceB {
funb()
}
classC : A, B {
overridefuna() {
println("A")
}
overridefunb() {
println("B")
}
}
funmain(args: Array<String>) {
val c = C()
c.a()
c.b()
}