자바로 개발을 하다 보면 가장 자주 발생하는 오류 중 하나가 NullPointerException 이다. (진짜...)

 

NullPointerException은 프로그램이 '값이 없다'는 상태인 null 값을 사용하려고 할 때 발생하는 오류이다. 정말 초보 개발자일때 뿐만 아니라 숙련이 되더라도 언제든 큰 골칫거리가 될 수 있다고 생각한다.

 

Kotlin은 이러한 문제를 근본적으로 해결하기 위해 Null Safety라는 개념을 도입했다. 

 

우선 Null에 대해서 정의를 해보자

 

 

Null이란 무엇인가?

Null은 "값이 없음"을 의미하는 특별한 값이다.

프로그래밍에서 어떤 변수에 값이 할당되지 않았거나, 사용할 수 없는 상태를 표현할 때 null을 사용한다. 예를 들어, 친구 목록을 저장하는 변수에 친구가 한 명도 없다면, 그 변수는 null일 수 있다.

var friend: String? = null // 친구가 없는 상태

 

여기서 중요한 점은 null을 허용하는 변수와 허용하지 않는 변수가 있다는 점이다.

 

만약 null인 값을 잘못 사용하면 프로그램은 오류를 내며 멈춰버릴 수 있다. 실제로, 많은 프로그래밍 오류는 이런 null 관련 문제에서 발생한다.

 

 

Kotlin의 Null Safety 기능

Kotlin은 변수 선언 시, 해당 변수가 null을 가질 수 있는지 여부를 명확하게 표시한다. 이를 통해 컴파일러가 자동으로 null 관련 오류를 사전에 경고해준다.

 

위에서 언급했던 Non-Nullable 변수와 Nullable 변수에 대해서 알아보자

  • Non-Nullable 변수 : 변수 선언 시에 기본적으로 null을 허용하지 않는다. 예를 들어, var name: String = "Alice" 라고 선언하면, 이 변수는 null 값을 가질 수 없다.
  • Nullable 변수 : 변수 뒤에 ?를 붙여 선언하면 null 값을 허용한다. 예를 들어, var friend: String? = null 이라고 선언하면, 이 변수는 null일 수도 있고, 실제 문자열 값을 가질 수도 있다.
fun main() {
    // Non-Nullable 변수: null 할당 불가
    var name: String = "Alice"
    // name = null  // 컴파일 에러 발생

    // Nullable 변수: null 할당 가능
    var friend: String? = null
    println("친구: $friend")
}

 

안전 호출 연산자 (?.)

Nullable 변수에 접근할 때, 직접 접근하면 null이 반환될 수 있어 오류가 발생할 수 있다. Kotlin에서는 안전 호출 연산아 "?." 를 사용하여 null일 때 안전하게 처리할 수 있다.

fun main() {
    var friend: String? = null
    // friend가 null이면 toUpperCase()를 호출하지 않고 전체 결과가 null로 처리됨
    println(friend?.toUpperCase())  // 출력: null

    friend = "Bob"
    println(friend?.toUpperCase())  // 출력: BOB
}

 

여기서 friend?.toUpperCase()는 friend가 null이 아니기 때문에 대문자로 변환하고, null이면 null을 그대로 반환한다.

 

 

엘비스 연산자 (?:)

안전 호출 연산자와 함께 엘비스 연산자 "?:" 를 사용하면, null인 경우에 기본 값을 제공할 수 있다.

fun main() {
    var friend: String? = null
    // friend가 null이면 "알 수 없음"을 대신 출력
    val friendName = friend ?: "알 수 없음"
    println("친구 이름: $friendName")
}

 

이 예제에서 friend가 null이기 때문에, "알 수 없음"이 friendName에 할당되어 출력된다.

 

 

let 함수

Kotlin에서는 null이 아닐 때만 특정 작업을 실행하는 간단한 방법으로 let 함수를 제공한다.

fun main() {
    var friend: String? = "Charlie"
    
    // friend가 null이 아닐 때만 블록 안의 코드 실행
    friend?.let {
        println("친구 이름 길이: ${it.length}")
    }
    
    friend = null
    // friend가 null이면 let 블록은 실행되지 않음
    friend?.let {
        println("이 코드는 실행되지 않습니다.")
    }
}

 

위처럼 let 함수는 nullable 변수가 null이 아닐 때 안전하게 해당 값을 사용할 수 있도록 도와준다.

1. 변수와 상수

Kotlin에서는 변수를 선언할 때 var 와 상수를 선언할 때 val 키워드를 사용한다.

  • var : 값이 변경 가능한 변수 
  • val : 값이 변경 불가능한 변수
fun main() {
    var age: Int = 20       // 가변 변수
    val name: String = "Alice"  // 불변 변수 (상수)

    println("이름: $name, 나이: $age")

    age = 21  // 값 변경 가능
    println("변경된 나이: $age")
}

 

 

2. 타입 추론

Kotlin은 변수의 타입을 자동으로 추론한다. 명시적으로 타입을 지정하지 않아도, 초기 값으로부터 타입을 유추할 수 있다.

fun main() {
    val language = "Kotlin"  // String 타입으로 추론됨
    val version = 1.5        // Double 타입으로 추론됨

    println("언어: $language, 버전: $version") // "언어 : Kotlin, 버전 : 1.5"
}

 

따라서 타입을 생략해도 컴파일러가 올바른 타입을 추론해주므로 코드가 간결해진다.

 

 

 

3. 문자열 템플릿

Kotlin에서는 문자열 내에서 변수 값을 쉽게 사용할 수 있는 문자열 템플릿 기능을 제공한다.

fun main() {
    val name = "Bob"
    val greeting = "안녕하세요, $name 님!"
    println(greeting)
}

 

문자열 내에 $name 과 같이 사용하면 해당 변수의 값이 문자열에 삽입된다.

 

 

 

4. 조건문

if 문

Kotlin의 if 문은 값으로 반환될 수 있어, 삼항 연산자 대신에 많이 사용된다.

fun main() {
    val score = 85
    val result = if (score >= 90) "합격" else "불합격"
    println("시험 결과: $result")
    
    
    // 식으로도 사용 가능
    val max = if( a > b) {
    	print("Choose a") // "Choose a" 를 출력하고 max에 a를 대입
        a
    } else {
    	print("Choose b") // "Choose b" 를 출력하고 max에 b를 대입
        b
    }
}

 

when 문

when 문은 여러 조건을 처리할 때 사용하며, Java의 switch 문보다 더 유연하다.

fun main() {
    val day = 3
    val dayName = when(day) {
        1 -> "월요일"
        2 -> "화요일"
        3 -> "수요일"
        4 -> "목요일"
        5 -> "금요일"
        6 -> "토요일"
        7 -> "일요일"
        else -> "알 수 없음"
    }
    println("오늘은 $dayName 입니다.")
    
    
    
    // 값 비교
    val x = 5

	when (x) {
    	1 -> println("x는 1이다")
    	2, 3 -> println("x는 2 또는 3이다")
    	in 4..10 -> println("x는 4와 10 사이에 있다")
    	else -> println("x는 다른 수이다")
	}
    
    
    // 타입 비교
    val data: Any = "Hello, World!"

    when (data) {
        is String -> println("data is a String: $data")
        is Int -> println("data is an Int: $data")
        is Boolean -> println("data is a Boolean: $data")
        else -> println("data is something else: $data")
    }


	// 조건식
    val y = 7

    val z = when {
        y == 5 -> "y는 5이다"
        y == 7 -> "y는 7이다"
    	else -> "y는 다른 수이다."
    }
    println(z)



}

 

 

 

5. 반복문

for 문

for 문은 컬렉션이나 범위(range)를 쉽게 순회할 수 있다.

fun main() {
    for (i in 1..5) {  // 1부터 5까지의 범위
        println("현재 값: $i")
    }
    
    val fruits = listOf("사과", "바나나", "체리")
    for (fruit in fruits) {
        println("과일: $fruit")
    }
}

 

 

while 문

기본적인 while 루프도 Java와 동일하게 사용할 수 있다.

fun main() {
    var count = 0
    while (count < 3) {
        println("카운트: $count")
        count++
    }
}

 

 

 

6. 함수

Kotlin에서 함수는 fun 키워드를 사용해 정의한다.

// 인사를 출력하는 함수
fun sayHello(name: String): String {
    return "안녕하세요, $name 님!"
}

fun main() {
    val message = sayHello("Charlie")
    println(message)
}

 

또, 함수의 몸체가 단 한줄이라면 중괄호와 return 키워드를 생략할 수도 있다.

fun add(a: Int, b: Int) = a + b

fun main() {
    println("5 + 3 = ${add(5, 3)}")
}

 

 

 

7. 클래스와 객체

Kotlin에서는 클래스를 사용해 객체 지향 프로그래밍을 할 수 있다. 

// Person 클래스 정의
class Person(val name: String, var age: Int) {
    fun introduce() = "제 이름은 $name이고, 나이는 $age살입니다."
}

fun main() {
    val person = Person("David", 30)
    println(person.introduce())
    
    // age 값 변경 가능 (var로 선언)
    person.age = 31
    println("나이 업데이트: ${person.age}")
}

 

Person 클래스를 정의하고, 생성자를 통해 name과 age 값을 초기화하며, 멤버 함수 introduce()로 자기소개를 한다.

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

Spring Boot Auto Configuration 이해해보기  (2) 2025.03.10
Kotlin의 Null Safety  (2) 2025.03.08
Spring Boot와 Kotlin: 기본 개념과 시작하기  (3) 2025.03.01
REST 와 RESTful  (0) 2024.02.07
Swagger  (1) 2023.11.08

복잡한 시스템을 신속하고 안정적으로 구축하는 것이 중요하다고 생각한다. 이에 따라 개발 생산성과 유지보수성을 극대화할 수 있는 도구와 언어의 선택은 필수적이다. Spring Boot 와 Kotlin은 이런 요구에 부합하는 강력한 조합으로 주목받고 있다.

 

Spring Boot란 무엇인가?

Spring Boot는 전통적인 스프링 프레임워크의 복잡한 설정 문제를 해결하기 위해 나온 경량화된 프레임워크입니다.
주요 특징과 장점을 구체적으로 살펴보면:

1. 자동 구성 (Auto-Configuration)

  • 설정의 단순화:
    Spring Boot는 애플리케이션 시작 시 프로젝트의 클래스패스와 설정 파일을 기반으로 자동으로 적절한 빈(Bean)들을 구성합니다.
    이는 개발자가 일일이 XML이나 복잡한 자바 설정을 작성할 필요 없이 비즈니스 로직에 집중할 수 있도록 도와줍니다.
  • 스프링 부트 스타터 (Starters):
    다양한 기능별 스타터 패키지를 제공하여, 의존성 관리가 간단하고, 필요한 라이브러리를 일괄 추가할 수 있습니다. 예를 들어, 웹 애플리케이션을 위한 spring-boot-starter-web이나 데이터베이스 연동을 위한 spring-boot-starter-data-jpa 등이 있습니다.

2. 독립 실행형 애플리케이션

  • 내장 서버:
    Tomcat, Jetty 등 내장 웹 서버가 포함되어 있어 별도의 서버 설치나 설정 없이 바로 실행할 수 있습니다.
    이로 인해 배포가 용이하고, 초기 개발 및 테스트 환경 구축이 신속해집니다.
  • 실행 파일:
    Spring Boot 애플리케이션은 단일 JAR 파일로 패키징되어, 명령어 한 줄로 실행할 수 있어 운영 환경에 배포하기 편리합니다.

3. 생산성과 유지보수성

  • 간결한 설정:
    복잡한 XML 설정이나 방대한 보일러플레이트 코드를 제거하여, 코드의 가독성과 유지보수성을 높입니다.
  • 모듈화 및 확장성:
    필요한 기능만 선택적으로 사용할 수 있도록 설계되어, 프로젝트가 커져도 쉽게 확장할 수 있습니다.

 

Kotlin의 기본 개념

Kotlin은 JetBrains에서 개발한 현대적인 프로그래밍 언어로, Java와 100% 호환되면서도 코드의 간결성과 안정성을 극대화합니다.
주요 개념과 특징은 다음과 같습니다.

1. 간결한 문법

  • 타입 추론:
    Kotlin은 변수 선언 시 타입을 명시하지 않아도 자동으로 타입을 추론하여 코드의 길이를 줄여줍니다.
    예를 들어, val name = "Kotlin"처럼 간단하게 선언할 수 있습니다.
  • 함수형 프로그래밍 지원:
    람다식, 고차 함수, 컬렉션 처리 등의 기능을 기본 제공하여, 반복적이거나 복잡한 로직을 짧고 명확하게 표현할 수 있습니다.
  • 데이터 클래스:
    단순히 데이터를 저장하기 위한 클래스를 작성할 때, data class 키워드를 사용하면 equals(), hashCode(), toString() 등 유용한 메서드를 자동 생성해 줍니다.

2. Null 안전성 (Null Safety)

  • 컴파일 단계에서의 검사:
    Kotlin은 Null에 안전한 타입 시스템을 도입하여, NullPointerException을 사전에 방지할 수 있습니다.
    변수에 ?를 붙여 Nullable 타입을 지정하는 방식으로, 코드 작성 시 Null 처리를 명확하게 할 수 있습니다.
  • 엘비스 연산자와 안전 호출:
    ?.와 ?: 등의 연산자를 사용하여 Null 체크를 간편하게 할 수 있어, 코드의 안정성을 높입니다.

3. 확장 함수와 프로퍼티

  • 기존 클래스에 기능 추가:
    확장 함수를 사용하면, 기존 클래스의 소스 코드를 수정하지 않고도 새로운 메서드를 추가할 수 있습니다.
    이를 통해 코드의 재사용성과 가독성을 크게 향상시킬 수 있습니다.
  • 프로퍼티 확장:
    확장 프로퍼티를 통해 기존 클래스에 새로운 속성을 추가하는 방식으로, 코드의 유연성을 높입니다.

4. 상호 운용성

  • Java와의 완벽한 호환:
    Kotlin은 JVM 언어로, Java 라이브러리와 자연스럽게 통합됩니다.
    기존의 Java 기반 프로젝트에 Kotlin을 점진적으로 도입할 수 있으며, 두 언어의 장점을 모두 활용할 수 있습니다.

 

왜 Spring Boot와 Kotlin을 함께 배워야 할까?

두 기술을 결합하여 학습하면 다음과 같은 여러 가지 장점이 있습니다.

1. 개발 생산성 향상

  • 간결함과 효율성 극대화:
    Kotlin의 간결한 문법과 null 안전성 덕분에 코드 작성 시간이 단축되고, 오류 가능성이 줄어듭니다.
    Spring Boot의 자동 구성과 스타터 패키지 기능은 복잡한 환경 설정을 단순화하여 개발 초기 진입 장벽을 낮춥니다.
  • 빠른 프로토타입 제작:
    두 기술 모두 빠른 애플리케이션 개발에 최적화되어 있어, 아이디어를 신속하게 구현하고 테스트할 수 있습니다.

2. 최신 개발 트렌드 반영

  • 모던한 언어와 프레임워크:
    Kotlin은 함수형 프로그래밍 패러다임을 포함한 최신 언어 기능들을 제공하며, Spring Boot는 클라우드 및 마이크로서비스 아키텍처에 적합한 설계 방식을 채택하고 있습니다.
  • 지속적인 업데이트와 커뮤니티 지원:
    두 기술 모두 활발한 커뮤니티와 정기적인 업데이트를 통해 최신 개발 트렌드를 반영하고 있어, 미래에도 경쟁력을 유지할 수 있습니다.

3. 안정성과 확장성

  • 코드의 안정성 강화:
    Kotlin의 컴파일 타임 Null 검사 및 안전한 타입 시스템은 런타임 오류를 줄여줍니다.
    Spring Boot의 모듈화된 아키텍처는 애플리케이션이 성장해도 체계적인 관리와 확장이 용이합니다.
  • 비즈니스 로직의 효과적 관리:
    Kotlin의 함수형 프로그래밍 기법과 확장 함수를 활용하면 복잡한 비즈니스 로직을 명확하고 직관적으로 관리할 수 있습니다.

4. 커뮤니티와 생태계의 장점

  • 풍부한 학습 자료와 오픈소스 프로젝트:
    Spring Boot와 Kotlin 모두 전 세계적으로 활발한 커뮤니티가 존재하며, 수많은 튜토리얼, 샘플 코드, 오픈소스 프로젝트가 학습에 큰 도움을 줍니다.
  • 실무 적용 사례:
    많은 기업과 스타트업들이 이미 이 조합을 채택해, 신속한 개발과 유지보수, 높은 확장성을 경험하고 있습니다. 실제 사례를 참고하면, 기술 선택의 타당성을 쉽게 이해할 수 있습니다.

 

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

Kotlin의 Null Safety  (2) 2025.03.08
Kotlin 기본 문법  (1) 2025.03.05
REST 와 RESTful  (0) 2024.02.07
Swagger  (1) 2023.11.08
Post API  (0) 2023.11.02

+ Recent posts