Knock!Knock! 코루틴 #2

Jooyung Han (한주영)
3 min readDec 12, 2017

--

이전 글: Stackful/Stackless 코루틴

간단하지만 동작하는 예제 코드 덕분인지 첫 댓글이 달렸습니다!! 이 글은 원래 글의 곁다리입니다.

이벤트 핸들러를 코루틴으로 만들어본 위 예제에 Jeong-Ik Cha님이 “시간"을 고려해보면 어떻겠냐고 댓글을 달아주셨네요.

이전 글의 데모에서는 노크코드의 길이만큼 입력 이벤트를 받아서 성공/실패를 판단하였습니다. 그런데 노크코드의 길이가 드러나는 것은 문제가 있습니다. 아무래도 보안상 취약하겠죠? 편의성을 조금 저해하더라도 입력 pause가 일정 시간이 넘을 때 성공/실패를 판단하기로 해 보죠.

저라면 Rx로 어떻게든 엮어보겠지만 이 글은 “코루틴을 쓴다면 어떤 모양이 될까” 알아보는 게 취지인 만큼 기존 코루틴 코드를 요구사항에 맞게 바꿔보기로 했습니다.

처음엔 딱히 방법이 떠오르지 않아서 우선 변경된 로직이 어떤 모양으로 구현되어야 할지 생각하면서 위처럼 작성해 봤습니다. 처음 입력을 받으면 그 다음부터는 1초 동안의 타임아웃을 가지고서 입력을 기다립니다. 타임아웃되기 전에 입력이 있다면 계속 다음 입력을 기다립니다.

입력에 대한 타임아웃을 어떻게 할까 하다가 처음엔 redux-saga의 영향으로 다음처럼 select를 고려했는데, 너무 오버인것 같더군요.

yield select('click', timeout(1000))

변경 내용을 보면 yield를 통해 이벤트 핸들러로 전달되는 이벤트를 가져오는 것은 변함 없습니다. 하지만 이번에는 yield 인자로 타이머를 전달하는 기능이 추가되었습니다. 마치 Java의 동시성 패키지들이 wait 함수들에서 타임아웃 시간을 지정할 수 있는 것과 같지요.

타이머는 그냥 단순 객체(eg, {type: ‘timeout', duration: 1000})로 만들어서 코루틴 드라이버가 이를 해석하도록 할까 하다가 Promise 객체로 해 봤습니다. 이렇게 해서 yield timeout는 마치 Async 코루틴처럼 동작할 수 있는 거죠. yield timeout에서 Promise가 먼저 resolve되면 그 값이 기본값으로 반환됩니다.

원래의 coro() 함수는 단순히 이벤트가 발생하면 코루틴을 resume하기만 했습니다. 이제 coro()가 할 일이 많아졌습니다. 타임아웃을 처리해야죠.

wireTimeout() 함수가 코루틴에서 전달할 지 모를 Promise객체를 이용하여 타임아웃을 설정합니다. 이때 타임아웃보다 이벤트가 먼저 발생하는 경우를 고려해야 합니다. 해당 타임아웃을 무효화해야 합니다. 조금 복잡해 보이긴 하지만 나름 괜찮게 합쳐진것 같네요.

완성된 코드와 데모는 아래에서 보실 수 있습니다.

--

--

Jooyung Han (한주영)

가끔 함수형 프로그래밍 관련 글을 쓰거나 번역합니다. “개미 수열을 푸는 10가지 방법"이란 책을 썼습니다. https://leanpub.com/programming-look-and-say