Groo

Kotlin 코틀린의 프로그램 흐름 제어 본문

프로그래밍 언어/Kotlin

Kotlin 코틀린의 프로그램 흐름 제어

김주엽 2020. 3. 6. 15:57

안녕하세요, 오늘은 Kotlin 코틀린 언어에서의 프로그램 흐름 제어에 대해 알아보려고 합니다.
프로그램 흐름 제어의 대표적으로 조건문, 반복문이 존재하며 추가로 예외 처리에 대해서 또한 알아보려고 합니다.

🚦 프로그램 흐름 제어는 무엇인가?

프로그램은 여러 줄의 문장으로 구성됩니다. 보통 순차적으로 문장이 수행됩니다. 그러나 문장의 흐름을 바꾸고 싶다면 특정 조건과 반복 등을 다양하게 구성할 수 있습니다. 이와 같이 프로그램의 흐름에 영향을 미칠 수 있는 영향력이 있는 것들에 대해 배우겠습니다.

 

프로그램 흐름 제어와 관련된 도로의 신호등, 표지판 사진입니다. (출처 : Pixbay)

프로그램 흐름 제어에는 조건문, 반복문, 예외 처리 등 다양한 것이 존재합니다. Kotlin 또한 다른 언어들과 프로그램 흐름 제어 부분에서 비슷한 내용이 많이 존재합니다. 그러나 이번에는 Kotlin 언어만이 가진 특별한 내용에 대해서 주로 설명하도록 하겠습니다.

📊 조건문에 대해서 자세히 알아보자!

조건문은 주어진 조건에 따라 다른 결과를 반환하는 기법입니다. Kotlin 언어에서는 제어문들을 간략하게 사용할 수 있는 표현어들이 다른 언어들에 비해 많아 간략화된 표현 방법과 새로운 연산자에 대해 알아보는 시간을 먼저 가지도록 하겠습니다.

# if문과 if ~ else문

if문은 주로 조건을 판단하기 위해 사용하며 판단의 값은 true, false 두 가지의 Boolean 자료형으로 판단합니다. 판단의 값에 따라 다른 수행 동작을 설정할 수 있습니다. 그럼 if문과 if ~ else문을 사용하여 일반적인 변수에 값을 넣는 예시를 들어보겠습니다.

fun main() {
    val a = 12
    val b = 7

    val max = if(a > b){
        println("a 선택")
        a
    }else{
        println("b 선택")
        b
    }
    println(max)
}

위는 max라는 변수에 값을 넣는 장면입니다. 하지만 if ~ else문으로 조건의 결과에 따라 다른 값을 넣고 있습니다. 만약 a의 값이 b의 값보다 크다면 a를, 그렇지 않다면 b의 값을 넣습니다. 앞에서 말했듯이 조건 본문 안에서 가장 마지막 식이 변수에 할당됩니다.

# else if문

else if문은 여러 가지 조건을 적용하기 위해 주로 사용하며 위의 if문, if ~ else문과 함께 사용이 많이 됩니다. 또한 else if문을 사용한다면 조건을 여러 개 적용할 수 있어 조건문의 중첩을 구성할 수 있습니다. 그럼 이번에는 else if문을 활용한 간단한 예제 보겠습니다.

fun main(){
    print("Enter the Score")
    val score = readLine()!!.toDouble()
    var grade : Char = if(score >= 90.0) 'A'
                       else if(score >= 80.0 && score <= 89.9) 'B'
                       else if(score >= 70.0 && score <= 79.9) 'C'
                       else 'F'
    println("Score : $score, Grade : $grade")
}

위의 프로그램은 사용자로부터 점수를 입력받아 점수에 따른 등급을 grade 변수에 저장을 하는 것입니다. if문, else if문, if ~ else문 총 3가지의 조건문을 모두 사용하였으며 각 조건의 결과에 따라 다른 등급의 값을 grade변수에 저장하는 모습을 볼 수 있습니다.

 

else if문을 사용한 구문을 자세히 보면 score 변수의 값의 범위를 나타내기 위해 논리합 연산자를 사용한 모습을 볼 수 있습니다. 여러분이 볼 때는 위의 코드가 효율적이라고 생각하시나요? 저는 그렇지 않다고 생각합니다. 위의 논리합 연산자를 대체해보겠습니다.

# in 연산자와 범위 연산자

위의 else if문의 예제에서는 범위를 구성하기 위해 논리 연산자를 사용하였습니다. 그러나 코틀린에서는 이 논리 연산자의 복잡함을 해결하기 위해 in 연산자와 2개의 점으로 구성된 범위 연산자라는 새로운 연산자의 개념을 제공합니다. 사용 방법을 한 번 보시죠!

 

in 연산자와 범위 연산자의 사용 방법

(변수 이름) in (시작 값) .. (마지막 값)
fun main(){
    print("Enter the Score")
    val score = readLine()!!.toDouble()
    var grade : Char = if(score >= 90.0) 'A'
                       else if(score in 80.0..89.9) 'B'
                       else if(score in 70.0..79.9) 'C'
                       else 'F'
    println("Score : $score, Grade : $grade")
}

위의 방법에 따라 이전의 논리 연산자를 사용한 예제를 in 연산자와 범위 연산자를 사용하여 다시 한번 구성해보았습니다. 위의 코드에서 첫 번째 else if문의 조건은 score 변수가 80.0에서 89.9 사이 값의 범위에 포함하고 있는지 확인하고 있습니다.

# when문 

when문은 코틀린 언어에서만 존재하는 특별한 조건문이라고 할 수 있습니다. when문은 다른 언어에서의 switch문과 거의 비슷한 기능을 수행하지만 약간 다른 부분이 존재합니다. 그럼 먼저 when문의 사용방법에 대해서 알아보도록 하겠습니다. 아래를 보시죠!

 

when문 사용 방법

when(인자) {
       인자에 일치하는 값 혹은 표현식 -> 수행할 문장
}
fun main() {
    when(1){
        1 -> println("Int : 1")
    }
}

when문 사용 방법과 같이 when 키워드의 매개변수로 인자 값을 전달해줍니다. 그 후 when 블록 안을 보면 화살표 왼쪽에는 인자와 일치하는 값, 표현식, 범위로 조건을 나타내며 화살표 오른쪽에는 조건에 해당할 시 수행할 문장을 작성해줍니다. 

fun main() {
    when(x){
        0, 1 -> println("x == 0 or x == 1")
        else -> print("기타")
    }
}
fun main() {
    when(x){
        in 1..10 -> println("x는 1 이상 10 이하입니다.")
        !in 10..20 -> println("x는 10 이상 20 이하의 범위에 포함되지 않습니다.")
    }
}
fun main() {
    when(str){
        is String -> println("문자열입니다.")
    }
}

위 내용과 같이 when문을 사용한다면 다양한 조건을 구성할 수 있다는 장점이 있습니다. 개인적으로 사용을 해보면서 저는 switch문에서는 매번 한 개의 조건에는 한 개의 break문을 작성해야 했지만 이제는 When 문을 통해 대체를 할 수 있습니다.

📆 반복문에 대해서 자세히 알아보자!

반복문은 반복문 블록 안에 있는 코드를 반복하여 실행하는 명령문입니다. 만약 여러분이 특정 코드의 부분을 반복적으로 수행하여야 하는 경우 반복문을 이용하며 반복문 또한 여러 가지 종류가 존재합니다. 대표적으로 for문, while문, do~while문이 존재합니다.

# for문

for문은 거의 대부분의 프로그래밍 언어에서도 사용하는 대표적인 반복문 중 한 개입니다. 그러나 Kotlin 언어에서는 이 for문을 다른 언어들과 달리 약간 다른 형식으로 이용합니다. 코틀린은 앞에서 배운 in 연산자와 함께 for문을 사용합니다. 아래 방법을 보시죠!

 

for문 이용 방법

for (요소 변수 in 범위) {반복할 구문}
fun main(){
  for(x in 1..5) println(x)
}

위 코드는 x의 값이 1부터 5까지 오름차순으로 1씩 증가하는 코드입니다. 그럼 만약 오름차순이 아닌 내림차순으로 값을 설정하고 싶거나 또는 값의 증가 또는 감소량을 2로 하고 싶다면 어떡해야 할까요? 아래와 같이 downTo, step 키워드를 포함시킵니다.

fun main(){
  for(x in 5 downTo 1) println(x)
}
fun main(){
  for(x in 5 downTo 1 step 2) println(x)
}

downTo, step 등의 키워드를 for문에서 함께 사용한다면 for문을 활용하여 여러분이 원하는 다양한 활동들을 수행할 수 있습니다. 일반적인 다른 언어들과 for문을 사용하는 방식이 많이 달라 처음에는 헷갈리시겠지만 적응을 한다면 더욱 편리할 것입니다.

# while문, do ~ while문

이번에는 for문이 아닌 다른 반복문 While문과 do ~ While문에 대해서 보겠습니다. 이 반복문들은 다른 언어들과 같은 형식으로 코틀린에서도 사용하기 때문에 다른 부분이 한 개도 존재하지 않습니다. 그렇기 때문에 간단히 사용 방법만 보고 넘어가겠습니다.

 

while문 사용 방법

while (조건식) {
      본문
}
fun main() {
    var number = 1

    while(number <= 5){
        println("$number")
        ++number
    }
}

while문은 위의 내용과 같이 조건식에 대해 값이 true일 경우에 반복 본문을 수행하고 그렇지 않다면 수행하지 않습니다. 또한 while문은 백그라운드에서 실행하면서 종료되지 않고 지속적으로 무엇인가를 수행하는 데몬을 생성할 때 많이 이용합니다.

 

do ~ while문 사용 방법

do {
   본문
} while(조건식)
fun main() {
    var number = 1

    do {
        println("$number")
        ++number
    } while(number <= 5)
}

반면에 do ~ while문은 while문과 약간 다릅니다. 반복문에 진입 전 조건을 검사하였던 while문과는 달리 do ~ while문은 무조건 한 번은 반복 구문을 수행 후 아래의 while문에서 조건을 검사하여 그 조건의 결과 값에 따라 계속 진행을 할지 말지에 대해 결정하죠!

📬 예외처리에 대해서 알아보자!

프로그램 코드를 작성하다 보면 해당 코드가 제대로 작동하지 않거나 중단되는 현상이 발생합니다. 이 현상이 바로 예외입니다. 대부분의 오류는 코드를 작성하는 도중인 컴파일러가 잡을 수 있지만 메모리 부족, 파일 손상 등 잠재적인 오류까지는 검사할 수 없습니다.

 

따라서 프로그램을 실행할 때 발생할 수 있는 예외를 대비하기 위해 try ~ catch라는 구문이 존재합니다. 지금부터 이 구문의 사용 방법과 다양한 예외에 대해서 알아보고 저희가 직접 예외를 만들어보기도 하겠습니다. 먼저 try ~ catch의 내용과 사용방법 보시죠!

 

try ~ catch 구문 사용 방법

try {
   (예외 발생 가능성 있는 문장)
} catch (e : 예외 처리 클래스 이름) {
   (예외를 처리하기 위한 문장)
} finally {
   (반드시 실행되어야 하는 문장)
}

위의 내용이 살짝 복잡할 수 있습니다. 보통 try 블록 안에는 예외 발생 가능성이 있는 문장들을 작성하고 catch 구문에서는 try 구문에서 만약 예외가 발생하였을 때 어떻게 처리할 것인지에 대한 코드를 작성합니다. 또한 finally 구문에서는 예외 발생 여부와 상관없이 반드시 실행되어야 하는 문장을 작성합니다. 그러나 finally 구문은 필수가 아닌 생략이 가능합니다. 그럼 간단한 예시를 보시죠!

fun main() {
    val a = 6
    val b = 0
    val c : Int

    try{
        c = a / b
    } catch (e : Exception){
        println("Exception is handled.")
    } finally {
        println("finally 블록은 항상 실행됨")
    }
}

위 코드는 try 구문에서 c라는 변수에 a와 b의 값을 나누는 것을 볼 수 있습니다. 위는 실행 중 예외가 발생하며 catch 구문에 들어가 catch 구문 안에 존재하는 문장이 출력된 후 finally 구문의 문장이 마지막으로 수행되고 프로그램이 종료되는 것을 볼 수 있습니다.

 

catch 구문을 자세히 보겠습니다. catch 구문에는 현재 Exception이라는 클래스로 예외를 담당하고 있습니다. 이 예외는 다양한 클래스 예외들을 모두 관리하는 역할을 하여 주로 많이 사용하며 저 위치에 다른 일반적인 예외들 또한 문제없이 작성을 할 수 있습니다.

 

사용자 정의 예외 사용 방법

class <사용자 예외 클래스 이름> (message : String) : Exception(message)
class InvalidNameException(message : String) : Exception(message)

fun main() {
    var name = "Juyeop1329"

    try{
        validateName(name)
    } catch (e : InvalidNameException){
        println(e.message)
    } catch (e : Exception){
        println(e.message)
    }
}

fun validateName(name : String){
    if(name.matches(Regex(".*\\d+.*"))){
        throw InvalidNameException("Your name : $name : contains numerals.")
    }
}

위의 코드는 사용자 정의 예외를 구현한 것입니다. try 구문 안에 name 변수의 값을 validateName 매서드의 매개변수로 값을 전달하고 있으며 그 값에 만약 숫자가 포함되어있다면 throw 예약어를 통해 미리 생성해둔 예외를 발생시키고 있습니다. 그러면 첫 번째 catch 구문에서 예외를 인식하고 본문 안에 포함된 기능을 처리하는 것을 볼 수 있습니다. (catch 구문은 여러 개 생성 가능합니다.)

👍 글을 마치며

오늘은 코틀린의 프로그램 흐름 제어에 대해서 알아보았습니다. 이 부분은 다른 프로그래밍 언어들과 공통된 내용이 많이 존재합니다. 그래서 공통된 부분에 대해서는 조금 간략히 설명을 하거나 생략을 하였습니다. 하지만 when, in 등 몇몇의 기능들은 코틀린에서만 존재하는 특별한 기능이여 처음 사용하고 경험을 해볼 것이라고 생각하여 다른 것들보다 더욱 자세히 설명하였던 것 같습니다. 오늘 배운 내용은 코틀린 프로그래밍을 하면서 앞으로 자주 사용할 것이고 중요하니 꼭 잘 적응을 하는 것이 중요합니다. 

 

참고 : Do it 코틀린 프로그래밍

Comments