Shunz Android Dev Note

코틀린의 반응형 프로그래밍 #1 - 패러다임 본문

Kotlin

코틀린의 반응형 프로그래밍 #1 - 패러다임

_Shun_ 2023. 11. 24. 23:19

 

이번 포스팅은 코틀린의 함수형 반응형 프로그래밍(이하 FRP, Functional Reactive Programming)과 코틀린 Flows 작업에 대하여 몇가지 시리즈로 작성해보려고 합니다. 시리즈의 첫 번째 주재로 함수형 프로그래밍과 반응형 프로그래밍의 개념을 소개하는것부터 시작하겠습니다.

 

 

절차적 프로그래밍 (Procedural Programming)

우선 FRP의 일반적인 대안을 설명하는 것부터 시작하는 것이 중요한데 그것이 바로 명령형 프로그래밍 또는 절차적 프로그래밍 패러다임이라고 볼 수 있습니다. 절차적 프로그래밍은 해결책을 찾기 위해 일련의 과정을 단계적으로 동작시켜 코드에 작업을 수행하는 방법과 수행할 작업을 알려주는 것을 포함합니다. 여기 절차 코드 블록의 예가 있습니다. 읽기 쉽지만 매우 상태적이며 작업 세부 사항을 설명하는 데 중점을 두고 있습니다.

val numbers = (1..10).shuffled().toMutableList()

// 배열의 짝수 인덱스에 있는 값을 제거
var index = 0
while(index < numbers.count()) {
    if(numbers[index] % 2 == 0) {
        numbers.removeAt(index)
    } else {
        index += 1
    }
}

// 배열의 값을 두배 증가
for(i in numbers.indices) {
    numbers[i] = numbers[i] * 2
}

// 오름차순 정렬
for (i in (1 until numbers.count())) {
    val temp = numbers[i]
    var swap = i
    while(swap> 0 && numbers[swap- 1] > temp) {
        numbers[swap] = numbers[swap- 1]
        --swap
    }
    numbers[swap] = temp
}

// 결과 출력
for(num in numbers) {
    println(num)
}

 

 

함수형 프로그래밍 (Functional Programming)

선언형 프로그래밍과 밀접하게 관련이 있는 함수형 프로그래밍은 광범위한 주제입니다.

그 핵심에는 재사용이 가능한 방식으로 데이터를 변환 및 처리함으로써 상태 의존도를 줄이는것이 중요한 아이디어입니다. 이는 수행되는 모든 기능들이 멱등성을 가지거나 순수(혹은 둘다)해야 함을 의미합니다. 즉, 주어진 입력에 대해 동일한 출력을 가져야 하며 어떤 부작용도 결과에 영향을 미치지 않아야 합니다. 함수형 프로그래밍 스타일을 사용하도록 업데이트된 이전 예제 코드입니다. 함수형 스타일에서 코드는 데이터에 대해 실행되는 일련의 작업을 선언하는 반면, 일련의 단계에서 데이터가 업데이트되는 방법을 정확히 지정하는 절차적 스타일을 선언합니다.

val numbers = (1..10).shuffled()

// 짝수 제거
val oddNumbers = numbers.filter { it % 2 != 0 }

// 두배 곱셈 연산
val doubledNumbers = oddNumbers.map { it * 2 }

// 오름차순 정렬
val sortedNumbers = doubledNumbers.sorted()

// 결과 출력
sortedNumbers.forEach { println(it) }

 

 

반응형 프로그래밍 (Reactive Programming)

절차적 코드와 비반응 코드에서 입력은 실행 시에 알 수 있습니다. List는 비반응형 데이터 타입의 한 예입니다. 여기에는 어떤 조치를 취하지 않으면 변경되지 않는 고정된 요소들이 포함되어 있습니다. 반응형 모델에서 데이터는 흐름에 따라 스트림으로 액세스되며, 흐름에 있는 각 요소는 개별적으로 조작될 수 있습니다. 이 예에서는 단순화를 위해 Sequence를 사용하도록 코드를 다시 작성 할 것이지만, 다음 포스팅에서는 더 강력한 흐름 구조를 사용해볼 것입니다. Sequence는 필요에 따라 값을 내보내고, 우리의 경우 정렬이 필요하기 때문에 즉시 출력하지만, 그 외의 경우에는 부분 집합이나 특정 값만 출력 할 수도 있습니다. 아래 코드에서 마지막에 모든 값을 출력하는 대신, 새로운 요소에 대해 반응하여 도착하는대로 처리후 출력하도록 합니다. Sequence는 lazy하기 때문에 Sequence를 평가하고 예상대로 각 요소가 출력 되도록 하려면 터미널 연산자인 .toList() 호출이 필요합니다.

val numbers = (1..10).shuffled().asSequence()

numbers
    // 짝수 제거
    .filter { it % 2 != 0 }

    // 두배 곱셈
    .map { it * 2 }

    // 오름차순 정렬
    .sorted()

    // 출력
    .onEach { println(it) }

    // 시퀀스를 List로 변환
    .toList()

 


결론

반응형 프로그래밍은 자연스럽게 함수형 코드를 작성하게 되므로 함수형 프로그래밍과 반응형 프로그래밍을 결합하는 데 추가 섹션을 수행할 필요가 없습니다. 위의 예제를 참조하면 각 요소가 기능에 의해 작동하고 새로운 값이 도착하면 반환이 됩니다. 그 패러다임에서 작업을 시작하기만 하면 됩니다.

 

모든 소프트웨어 패턴과 마찬가지로 이것이 새로운 해머이지만 모든 문제가 해결되는 것은 아니라는 점을 유의하셔야 합니다. Functional Reactive 프로그래밍은 읽기 쉽고 유지보수가 용이하며 테스트 가능한 방식으로 데이터의 비동기 스트림을 처리하는 데 유용한 도구이지만, 하나의 데이터만 생성하거나 프로그램이 지속적인 상태 변화에 대응해야 하는 환경이 아니라면 오버 스펙일수 있습니다.