2019.05.22 오늘의 구글링

오늘의 구글링

기존 lombok 코드들이 kotlin에서 동작이 안되네.

  • query : kotlin lombok
  • 결과 : delombok..하는 수 밖에
    • mapstruct도 안된다면 이건 우리 프로젝트에서 좀 커다란 문제..kotlin에서는 kotlin용으로 해야하나? 찾아봐야겠다.

MYSQL : You can’t specify target table ‘table’ for update in FROM clause

---------------------------------------- "2019.05.22 오늘의 구글링" 끝 ----------------------------------------
WebFlux 알아보기

WebFlux???

  • Spring 5가 나오면서 추가된 request 비동기 처리 방식.
  • functional programming에 관심을 가지게되어 자연스럽게 Reactive programming에도 관심을 가지게되었고 Spring stack으로 먹고 살다보니 결국 WebFux에 왔다. WebFlux가 어떤 방식으로 동작하는 것이고 기존의 Servlet 방식보다 좋은 점은 무엇인지 공부할거다.
  • 그 기초 지식을 학습하기위해 찾아보면서 자료들을 정리한다.

공부하면서 본 것들

Blogs

Youtube

Github

  • AoJ: ADBA over JDBC : Oracle이 JavaOne에서 발표했던 New Asynchronous DB Connections에 대한 샘플 코드 - first commit이 2012년 부터. 처음시작때는 kotlin이 아니고 scala네 ㅋ

  • jasync sql : Kotlin!으로 작성된 Async Mysql driver. Kotlin 코드 샘플을 구경할겸. 겸사겸사. 실제 이걸로 작업을 안해봐서 동작이나 성능이 어떤지는 모름.

  • MiXiT : Spring Boot 2 + WebFlux + Kotlin 데모 사이트 코드!

  • Lite Rx API Hands-on : Reactor Java 연습용 코드 예제

  • Lite Reactor API and Reactor Kotlin Extensions Hands-on : Reactor Kotlin 연습용 코드 예제

stack overflow

stack overflow를 뒤져보면 내가 궁금해했던 것들이 많아서 이것들이 정답일지 아닌지는 모르지만 참고용.

  • Spring MVC (async) vs Spring WebFlux : 이것이 WebFlux 공부한다면 찾아보는 제일 첫번째 질문 ㅋ 뭐가 다른거야? 뭐가 좋은거야?

  • Is asynchronous jdbc call possible : 2011년에 질문한 자료. 현재 상황과는 다르게 절망적인 상황을 자체 라이브러리로 해결하겠다는 작성자의 이야기와 달려있는 댓글들을 보면 이 모든 것들이 신규 개념이 아닌 오래된 것들이 근래에 구체화되어 나오고 있다는 것이란 것을 다시 깨닫게함.

인간레퍼런스

  • WebFlux 뽐뿌를 넣어준 사람 : Thanks to 김기현

공부하면서 궁금해진 것들. (답을 찾으면 업데이트)

  • DB Connection 숫자는 어떻게 되는지? Event loop안에서 사용하는 Worker 별로 생기는 것인지 해당 Worker들도 DB Connection Pool로 한번 더 감싸진 것인지?

  • repository async로 인해서 request의 thread 점유 시간이 줄어든다면 서버 운용 측면에서 thread 자원을 아낄 수 있게 되어 서버 instance 숫자를 줄 일 수 있을 것 같은데, thread 자원을 아끼는 대신 추가로 cpu 사용률이 높아져서 scale out이 되지는 않을지?

  • 설명들을 보면 아직 aync rdb jdbc가 없다는데, 우린 적용하기 힘든건가?

    • async JDBC spec은 발표되었는데, 아직 지원되는 상용드라이버는 안보인다. 하지만 몇몇 github driver들이 찾아지긴함.
---------------------------------------- "WebFlux 알아보기" 끝 ----------------------------------------
비동기 프로그래밍 테크닉

비동기 프로그래밍 테크닉 (Asynchronous Programming Techniques)

번역

해당 번역은 정식 번역이 아닌 학습을 위한 개인 번역입니다. 참고 부탁드립니다. nurinamu

원문 Asynchronous Programming Techniques

이 튜토리얼은 비동기 프로그래밍의 다른 접근 방법을 설명한다.

수십년간, 우리 같은 개발자들은 앱이 블럭킹되는 것을 어떻게 방지할 것인지에 대한 문제를 맞닥드리고 있다. 데스크탑, 모바일, 심지어 서버사이드 앱들을 개발할 때에도 우리는 사용자가 대기하거나 앱이 버틀넥 이슈로 나빠지는 것을 방지하기 위해 스케일링 되는 것을 피하고 싶어한다.

여기 이런 문제를 해결하기 위한 다양한 접근 법들이 있다 :

  • Threading
  • Callbacks
  • Futures, Promises et al.
  • Reactive Extensions
  • Coroutines

코루틴(Coroutines)이 무엇인지에 대하여 설명하기 이전에 몇가지 다른 솔루션들을 간단하게 짚어보자.

Threading

쓰레드는 아마도 가장 잘 알려진 블러킹 회피 방법일 것이다.

fun postItem(item: Item) {
    val token = preparePost()
    val post = submitPost(token, item)
    processPost(post)
}

fun preparePost(): Token {
    // makes a request and consequently blocks the main thread
    return token
}

위의 코드에서 preparePost는 처리가 오래걸리고 사용자 인터페이스가 블럭된다고 가정하자. 이것은 쓰레드를 분리하여 실행 시킬 수 있다. 이것은 UI가 블럭킹되는 것을 회피할 수 있게한다. 매우 일반 적인 방법이지만 문제점들을 가지고 있다 :

  • 싸지가 않다. 쓰레드는 고비용의 context 변경을 필요로 한다.
  • 한계가 있다. OS가 정한 한계 숫자 안에서만 실행 가능하다. 서버 영역에서는 이 것이 주요 병목 원인이 될 수 있다.
  • 항상 가능한 것이 아니다. 자바스크립트 같은 몇몇 플랫폼에서는 쓰레드를 지원조차 하지 않는다.
  • 쉽지가 않다. 경쟁 상황을 피하기 위한 쓰레드들을 디버깅 하는 것은 멀티 쓰레드 프로그램에서 자주 겪는 힘든 일이다.

Callbacks

콜백(callbacks)은 하나의 함수를 다른 함수로 파라미터로 전달하는 것이다. 이것은 완료시점에 한번만 호출된다.

fun postItem(item: Item) {
    preparePostAsync { token -> 
        submitPostAsync(token, item) { post -> 
            processPost(post)
        }
    }
}

fun preparePostAsync(callback: (Token) -> Unit) {
    // make request and return immediately 
    // arrange callback to be invoked later
}

이 원리는 아주 우아한 해결책으로 느껴진다 하지만 이것도 몇가지 문제가 있다:

  • 내부 콜백의 어려움. 보통 콜백으로 사용되는 함수는 결국 자체 콜백을 필요로 하게된다. 이것은 이해하기 어려운 코드를 만들어 내는 연쇄 내부 콜백을 야기하게된다. 이 패턴은 종종 크리스마스 트리라는 이름으로 언급이 된다.

  • 애러처리가 복잡하다. 내제 모델은 애러 처리와 애러 처리 전파를 더 복잡하게 한다.

콜백은 자바스크립트와 같은 이벤트 루프 아키텍쳐에서는 매우 일반적인 것이다. 하지만 자바스크립트에서 조차도 보통 프로미스나 리액티브 익스텐션 같은 다른 접근 법을 사용하려고 옮겨간다.

Futures, Promises 등과 나머지

futures나 promises(언어/플랫폼에 따라 불리우는 다른 단어들도 있다)의 아이디어 배경은 호출했을 때, 특정 시점에 Promise로 불리는 처리가능한 객체를 반환하게다고 보장하는 것이다.

fun postItem(item: Item) {
    preparePostAsync() 
        .thenCompose { token -> 
            submitPostAsync(token, item)
        }
        .thenAccept { post -> 
            processPost(post)
        }
         
}

fun preparePostAsync(): Promise<Token> {
    // makes request an returns a promise that is completed later
    return promise 
}

이 접근은 프로그래밍 방법의 몇가지 변화가 필요하다. 특히.

  • 다른 프로그래밍 모델. 콜백과 같은 프로그래밍 모델은 폭포수 접근법을 떠나 연쇄 호출과 합성 모델로 이동한다. 반복문, 예외처리 등 같은 전통적인 프로그래밍 구조체는 더이상 이러한 모델에 맞지가 않는다.

  • 다른 API. 플랫폼 별로 다양한 thenCompose, thenAccept 같은 완전히 새로운 API를 학습해야한다.

  • 명시된 반환 type. 반환 타입은 우리가 필요로하는 실제 데이터 대신에 값을 내재한 새로운 타입인 Promise로 반환한다.

  • 오류 처리가 복잡해질 것이다. 애러의 전파와 처리가 항상 직접적이지가 않다.

Reactive Extensions

리덱티브 익세텐션(Rx)는 Erik Meijer에 의해 C#에서 소개되었다. 한동안 .NET 플랫폼 에서만 사용되어지고, Netflix가 RxJava라는 이름으로 Java에 포팅하기 전까지는 딱히 주류가 되지는 않았었다. 이후 자바스크립트 - RxJS 를 포함하여 다양한 플랫폼을 위해 여러 경로들이 제공되어졌다.

Rx의 아이디어 배경은 데이터를 이제는 스트림(무한한 양의 데이터)으로 고려하고 이 스트림은 관찰되어질 수 있다는 것이다 그리고 observable streams라 불리는 것을 어딘가로 전달하는 것이다. 실제 항목으로 Rx는 간단히 데이터 처리를 위해 몇가지 확장들을 포함한 관찰자(Observer) 패턴이다.

접근 방법으로 보면 Future와 매우 유사하지만 Future는 단일 데이터를 반환하고 Rx는 스트림을 반환한다. 또한 프로그래밍 모델에 대하여 완전히 새로운 사고 방식을 제시한다. 유명한 문구로

“모든 것은 스트림이고 관찰 가능하다.”

문제들을 접근하는 다른 방법과 우리가 사용하는 것에서 동기화 코드를 작성하는 시점으로 매우 중요한 변화가 있다는 것을 의미한다. Futures와 반대로 한가지 장점은 여러 플랫폼에 포팅되어있다는 것이다. C#, Java, JavaScript나 Rx가 가능한 다른 어떤 언어를 사용하던 간에 일관된 API 경험을 할 수 있다.

게다가, Rx는 애러 핸들링에 대한 좋은 접근을 소개한다.

Coroutines

비동기 코드를 작성하기 위한 코틀린의 접근은 연기 가능한(suspendable) 연산들의 아이디어(함수가 특정 포인트에서 실행을 연기할 수 있고 이후 다시 실행할 수 있는 아이디어)인 코루틴을 사용하는 것이다.

개발자들에게 코틀린이 등장하였을 때 가장 좋은 장점은 논블럭킹 코드를 작성하는 것이 블럭킹 코드 작성하는 것과 동일하다는 것이다. 프로그래밍 모델 자체는 크게 바뀌지 않는다.

다음의 코드는 객체를 가져오는 것이다.

fun postItem(item: Item) {
    launch {
        val token = preparePost()
        val post = submitPost(token, item)
        processPost(post)
    }
}

suspend fun preparePost(): Token {
    // makes a request and suspends the coroutine
    return suspendCoroutine { /* ... */ } 
}

이 코드는 메인 쓰레드가 블럭킹 되지 않고 오랬동안 동작할 것이다. preparePost는 연기 가능한 함수(suspendable function)이라 불린다. 그래서 suspend 머릿말을 붙였다. 위에서 의미하는 것은 함수가 특정 시점마다 실행되고 실행이 멈추고 재시작 된다는 것이다.

함수 표현형태는 기존과 동일하다. 다른 점 한가지는 suspend가 추가된 것이다. 반환 type은 요구하는 형태 그대로이다. 코루틴(다른 챕터에서 확인할)을 시작하는 launch라는 함수를 사용한 것 이외에는 코드가 특별한 문법이 필요하지 않고 여전히 동기화 코드를 사용하는 것처럼 작성된다. 프로그래밍 모델과 API는 동일하다. 반복문, 예외처리 등을 계속 사용할 수 있다. 그리고 새로운 API 세트를 학습할 필요가 없다. 코루틴은 플랫폼 독립적이다. JVM, Javascript 또는 어떤 플랫폼위에 있더라도, 코드는 동일하게 작성된다. 내부적으로 컴파일러가 각 플랫폼에 맞춰 처리한다. 코루틴은 새로운 컨셉도 아니고 코틀린에서 단독으로 발명한 것도 아니다. 코루틴은 수십년동안 존재해왔고 Go와 같은 다른 여러 언어에서 인기가 있다. 코틀린으로 구현 된 것을 주목해야할 것은 대부분의 기능들이 라이르러리로 되어졌다는 것이다. 사실 suspend 키워드 이외의 추가된 키워드가 없다. async, await 같은 문법을 가진 C#과 같은 언어들과의 차이이다. 코틀린에서는 단지 라이브러리 함수일 뿐이다.

코루틴과 다른 가능성들에 관한 추가 정보는 레퍼런스 문서를 참고해라

---------------------------------------- "비동기 프로그래밍 테크닉" 끝 ----------------------------------------
생각처럼 되지 않는 코드 작성

Eureka!.. 하지만

지난 밤 꿈속에서 그동안 머리속을 어지럽히던 모나드의 필요성에 대해서 번뜩임과 함께 사용처를 이해하게 되었다. 잊지 않기위해 바로 keep에 draft를 작성하고, 블로그 쓸거리가 생겼다고 신나하면서 생각났던 아이디어를 마구마구 코드로 써봤다.

그런데!!

안된다.. 헛 이게 아닌데. 뭔가 잘못생각했나. 당황하면서 이것저것 다시 글들을 찾아보고 더 공부해본다. 틀렸다. 잘못생각한거다. 그냥 나의 상상속의 결론이었지 현실에서는 안되는 것이었다. 근데 가능할 것 같은데 왜 안되는걸까. 아쉬워하면서 신나게 작성하던 블로그 포스트는 지우기는 아까워서 sub branch에 push 해버리고 차후에 내 생각을 구현해줄 실제 코드와 함께 살려내는 것으로.

아쉬움을 일단 사이버공간에 토로차 글 남긴다.

---------------------------------------- "생각처럼 되지 않는 코드 작성" 끝 ----------------------------------------
Functional Programming과 Lambda Calculus의 관계

람다대수(Lambda Calculus)가 왜 자꾸 함수형 프로그래밍(Functional Programming)에서 언급이 될까?

FP를 공부하면 끊임없이 쫓아오는 것이 람다대수 이야기다. 처음 공부할 때는 람다가 익명함수고 람다 대수는 그 연산이니까 함수형 프로그래밍이랑 비슷한 개념정도로 막연하게 이해하고 있었다. 하지만 공부를 하면서 조금씩 그 의미와 그로 인해 이해되는 것들이 있어 몇가지 정리해본다. 물론 아직도 공부 중이기에 더 얻는 깨달음이 있으면 업데이트는 말고 추가로 글을 쓰자.

우선 Labmda는 수학에서 함수를 의미한다. 우리가 중학교때부터 배워왔던 y=ax+b 와 같은 함수. 이것을 람다로 쓰면 𝜆x.ax+b 로 된다. 그래서 수학에서 사용하던 함수는 람다였고, 보는 것처럼 수학적 표현에서 함수명을 표기할 방법이 없다는 것을 보면, 프로그래밍상 익명 함수가 수학에서 사용한 함수 즉 람다가 된다. 함수형 프로그래밍은 수학식의 프로그래밍을 표방하고 있고, 이 수학적 표현을 모두 안고 만들어진 언어가 Haskell이다. Haskell 소개의 처음에 나오는 것이 Haskell == Lambda Calculus, 즉 함수의 연산을 표현하는 언어란거다.

그래서 함수형 프로그래밍에 최적화된 언어인 Haskell이 람다대수를 표현한 언어이기에 함수형 프로그래밍이란 람다대수를 표현하기 위한 것으로 이해해도 무방해보인다.

함수형 프로그래밍을 위한 함수형 언어의 특징은 뭘까?

함수형 프로그래밍은 위에 내가 이해한 바에 따르면 함수들의 연산을 프로그래밍으로 만들어내는 것이다. 이미 익숙한 언어들도 따지고 보면 함수로 만들어지는 것인데 뭐가 다르냐라는 반문을 나도 했었다. 하지만 여기에는 수학적 함수 표현을 위한 몇가지 조건들이 있다. (퓨어함수라던가, 일차함수라던가 하는 함수형 프로그래밍에서의 함수 기본 조건은 설명된 다른 글들이 많으니 그건 여기서 스킵)

아래 수식을 프로그래밍해보면서 설명해보자

f(x)=x+1

위 함수는 입력된 값 x에 1을 더한 값을 반환하는 함수이다. 이걸 단순하게 코드로 치환하면 아래처럼 될거다.

fun f(x: Int) = x + 1

근데 우린 수학 함수를 변경하는 것이다. 수학에서는 또 아래같은 일이 발생한다.

x=2*z

x가 우리가 생각했던 것 처럼 하나의 값이 아닌 함수인 경우다. 그렇다 람다대수는 함수와 함수의 연산이다. 그래서 기본적으로 함수를 인자로 받을 수 있는 함수의 선언이 가능한 언어만이 함수형 프로그래밍을 지원할 수 있는 것이다. 그리고 우리도 함수의 파라메타를 함수로 받도록 함수를 작성해야 진짜 함수형 프로그래밍(람다대수가능 프로그래밍)을 한다고 볼 수 있다. 당연히 모든 함수 파라메타를 함수를 받도록 만드는 일은 어렵고 현실에서는 비효율적이거나 불합리할 수 있다. 가능한 파라메타 선언을 함수로 하는 것이 함수형 프로그래밍의 장점을 활용할 수 있다는 것 정도로 이해하자.

그래서 다시 아래 처럼 만들어질 수 있겠다.

fun f(x: (Int) -> Int) : (Int) -> Int = { x(it) + 1 }

fun xfunc(z: Int) = 2 * z

fun main(args: Array<String>) {
    println(f(::xfunc)(2)) //5
}

위의 것도 상황에 따라다를 수 있으니 일단 코드가 맞고 틀리고 보다는 어떻게 하는 것이 함수형 스럽게 만드는 것인지 이해하는데 집중하자.

함수형 프로그래밍에 필요한 함수 조건을 이해했으면 이와 연관되어 한가지 추론이 더 가능하다. 함수전달이 되어진다는 것은 값을 인자로 받는 것과 함수의 실행 시점의 시점이 변경됨을 알 수 있다. 그렇다 느낌이 온다. 함수형 프로그래밍을 통해 최종 연산되는 시점은 인자가 함수가 입력되는 시점이 아닌 leaf 함수에서 최종적으로 실제 값이 들어오는 시점이다. 일반적인 함수 실행 시점이 함수 호출 시점인 반면에 함수형 프로그래밍에서는 함수의 호출이 되더라도 인자가 값이 아닌 함수인 경우에는 해당 시점에 연산되지 않는다. 많이 들어본 lazy evaluation이 된다.

이렇게 함수형 프로그래밍이 가능한 언어는 최소한 함수를 인자로 받을 수 있는 함수 선언이 가능해야하고 lazy evaluation이 지원되야한다. 근데 지금 작성하고 보니 함수를 인자로 받을 수 있다는 것이 lazy evaluation을 지원 한다는 의미로 받아들여도 되지 않을까 한다.

함수형 프로그래밍은 수학수식으로 표현하기 위해 시작된 만큼 람다대수를 이해하는 것이 함수형 프로그래밍의 특장점을 살린 프로그래밍을 할 수 있는 것이라 생각된다. 사내에서 스터디중인 Haskell이 적응되면 될 수록, 함수형 프로그래밍의 장단점이 보이기 시작한다. 함수형 프로그래밍이 수학 함수의 표현을 위한 것이라고는 하지만 함수응용을 통한 프로그래밍이 가지는 프로그래밍적(?) 장점들이 있기에 이제는 꼭 수학 함수의 표현을 위해 프로그래밍 한다기 보다 함수형 프로그래밍의 장점때문에 함수형 프로그래밍이 인기가 있어진게 아닌가 생각된다.

프로그래밍 패러다임은 어느 것이 최고다라는 것이 없고, 내가 해결하려는 문제에 가장 적합한 패러다임인지에 대해서 평가한다. 다음 글에서는 내가 체득하고 있는 함수형 프로그래밍 패러다임의 장점들을 적어보고 어떤 상황에서 사용하면 좋은지에 대해 적어보겠다.

---------------------------------------- "Functional Programming과 Lambda Calculus의 관계" 끝 ----------------------------------------