Category Archives: Tech

[개봉기] 네이버 웨이브 포장이 왜 이 모양??

네이버 웨이브가 왔다. 카카오 미니는 서버가 난리나서 주문 실패했는데, 웨이브는 카카오 미니보다 먼저 예약을 받았음에도 여유있게 신청했다는 후문..

암튼 도착했다. 두둥. 생각보다 박스가 크네.

마침 함께 도착한 구글 홈과 비교샷.

그건 그렇다 치고, 문제는 여기서부터.

박스 뚜껑을 열면, SKT 누구 미니스런 자태를 뽐내고 있어 일단 실망을 자아낸다 (사실 SKT 누구 미니는 더 심각하다. 사용설명서가 별도로 와서 택배박스에 덩그러니 들어있었다는..).

게다가 더 이해할 수 없는 개봉 방식.

응? PULL? 손잡이를 당겨서 안쪽 박스를 꺼내라는 건가? (사진은 손잡이를 이미 당긴 상태이니 저렇게 보이지만 처음에는 약간 당황) 그러기엔 무게가 있어 종이가 뜯어질 것 같은데.. 아니면 뚜껑만 따로 분리되는 방식인가? 박스가 잘 안나와서 손잡이를 당겨도 보고 뒤집어도 보고 해서 결국 꺼내긴 했는데..

이렇게 생겼다. 가운데 부분을 뜯어서 내용물을 꺼내는 방식.. (한 번 개봉하면 박스 상태 따위 신경쓰지 말라는) 저렇게 생긴 것을 보면 쉽게 예상할 수 있다시피 이렇게 열린다.

하도 기가막혀서 내용물을 꺼낸다음 찍은 사진이지만, 박스를 조잡하게 결합해서 끼워맞추는 방식인데다가 뚜껑의 말대로 PULL하면 각도가 저런 식으로 열리기 때문에 다시 바깥쪽 박스에 담고 나면 손잡이를 잡고 꺼낼 수가 없다.

내가 포장을 제대로 이해못하고 있나 싶어서(내가 이걸 왜 연구하고 있어야 하는거지?) 좀 더 실험해보다가 더 기가막혔던 것은, 박스를 순간 놓쳤더니 아래 부분까지 펼쳐져서 바닥에 퍼져 버린다! 중고등학교 시절 박스 접기 도면 보는 줄..

 

반면 구글 홈은 바깥쪽 박스부터 안쪽 포장까지 나름 고민을 한 흔적이 보인다.

슬라이딩 방식의 박스 개봉부터,

내부 포장의 견고한 느낌까지.

 

애플 만큼은 아니어도 구글마저 신경쓰는 박스 디자인을, 네이버는 왜 이리 소홀했는지. 하드웨어 경험이 없으니 그럴 거라 이해할 수는 있지만 이런 디바이스는 개봉할 때의 느낌이 제품에 대한 인상을 좌우하는 부분이 작지 않은데 상당히 아쉽다. 대체 포장 담당한 팀 어디야!

샐리야, 송혜교 몇 살이야?

네이버 웨이브가 와서 테스트해보고 있다. 이 중 먼저 궁금했던 것은, 크게 두 가지다.

  1. 지식베이스 기반의 질의응답이 가능한지
  2. 대화 맥락 관리는 어떠한지

1번의 커버리지는 데이터를 차곡차곡 쌓으면 되는 문제이니 신경 쓰이지 않지만 그런 의지가 있는지가 궁금했다. 참고로 SKT 누구는 이런 개념 자체를 말아먹으셨다 제대로 탑재하지 않았다.

일단 이렇게 테스트 해봤다.

웨이브가 지식베이스에 기반한 질의응답은 대응하려는 것으로 보인다 (SKT 누구의 경우엔 “위키에서 찾아줘”라고 해야 한다). 다만 무슨 이유에선지 송혜교 나이는 위키에서 찾고 송중기의 나이는 그냥 답변한다. 짧은 테스트라 확언할 수는 없지만 잠깐 유추해보건데, 질의응답에 대응하는 데이터베이스가 양분(클로바 지식베이스와 위키데이터)되어 있는 듯 하고 이 중 클로바 지식베이스에는 송혜교의 나이가 없거나(큐레이션 되어 있지 않거나) 혹은 둘 다 있더라도 위키데이터를 우선적으로 답변하는 프로세스이거나 한 것 같다. (송중기 송혜교 결혼한지가 언젠데…)

또 한가지 대화 맥락을 어느 정도는 대응할 수 있도록 한 것 같다. 가장 인상적이었던 것은 사용자 질문에 답변한 후 바로 비활성화되는 것이 아니라 바로 다음 질문 또는 명령을 기다릴 수 있도록 바로 활성화된다는 점. 가끔은 오히려 귀찮은 기능일 수도 있지만 타 스마트스피커들처럼 단순히 명령만 실행하는 것이 아니라 ‘대화’를 이어가려는 노력의 일환으로 보인다.

그런데 조금 전 영상에서도 확인할 수 있듯이 위키에서 참고해서 답변하는 경우에는 대화 맥락에 제대로 대응하고 있지 못하다. “송중기 나이는” → “송혜교는?” → “설현은?” 과 같이 질문하면 송혜교에 대해선 엉뚱한 답변을 함에도 설현의 나이는 제대로 답변한다. 즉, 위에서 예상한 바와 같이 위키 데이터를 바라보는 시스템은 대화 맥락 관리에서 제외되고 있는 것으로 보이고, 아직 이 부분에 대한 개선이 필요하다는 것을 시사한다. 아마 웨이브 개발팀에서도 이미 인지하고 있을 문제로 생각되지만.

참고로 SKT 누구의 관련 영상. (미니여서 그런지 모르겠지만 마이크 성능이나 음성처리 성능이 상당히 떨어져서 크게 말해야 한다. 똑같은 미니인 아마존 에코닷은 엄청 잘 알아듣는다)

 

모나드(monad)와 클로저(clojure)

 

브루스테이트의 세븐 랭귀지를 드디어 다 읽었다. 연습문제들은 그냥 스킵했기 때문에 완독이라는 표현은 양심에 찔리지만, 아무튼 7가지 각 언어들이 갖는 특성을 파악하는데는 큰 도움이 되었다. 여기서 내 관심을 끌었던 언어는 대학원 때 당시 아무 개념없이 수강신청했다가 고생했던 하스켈인데, 여기 모나드에 대한 내용이 언급되어 있어 그 개념 정리를 위해 포스팅해보기로 했다.

이 책에서 설명하기를, 모나드는 세 가지 요소를 갖고 있다고 한다.

  1. 어떤 자료형 컨테이너에 기초한 자료형 생성자
  2. 함수를 둘러싸서 컨테이너에 집어넣는 역할을 수행하는 return 이라는 이름의 함수
  3. 함수를 끄집어내는 >>=  라는 이름의 바인딩 함수

또한 모든 모나드가 만족시켜야 하는 세 가지 규칙을 다음과 같이 언급하고 있다.

  1. 자료형 생성자를 통해 값을 저장할 수 있는 어떤 자료형을 사용하는 모나드를 만들 수 있어야 한다.
  2. 값을 손실하지 않으면서 값을 보관하거나 꺼낼 수 있어야 한다. (즉 monad >>= return = monad )
  3. 바인딩 함수를 중첩하는 것은 그것들을 연속적으로 호출하는 것과 동일한 결과를 낳아야 한다. (즉 (m >>= f >>= g = m >>= (\x -> f x >>= g) )

모나드에 대해 깊이있게 공부한 것이 아니기에 아래 설명이 오류가 있을지도 모르겠지만, 위의 내용이 모나드의 핵심이라고 본다면 그 개념을 이해하는 것이 크게 어렵지는 않은 것 같다.

모나드 개념의 시작은 “함수  f 의 결과를 함수  g 에 전달하여 수행하고 싶다”에서 출발하는 것 같다. 함수형 프로그래밍은 잠시 잊고 생각해본다면, 이를 위해 택할 수 있는 방법은 함수  f 의 결과를 함수 특정 공간에 저장한 다음 함수  g 가 해당 위치에 접근하여 이어나가는 방법을 들 수 있다.

 

 

쉽게 이해할 수 있다시피, 이 방식은 대부분의 프로그래밍 언어들에서 널리 사용되는 방법이다. 즉, 변수 또는 로컬디스크에 값을 저장하고 다시 그 위치에 접근하여 값을 불러온 다음 사용하는 방식이다. 당연히 이 방식의 문제는   f 나   g 외에 다른 함수가 저장 공간에 접근하게 되면   g 의 결과를 장담할 수 없게 되므로 함수형 프로그래밍에서는 권장하지 않는 방식일 뿐더러, 하스켈과 같은 순수 함수형 프로그래밍 언어는 이러한 상태 저장이나 부수 효과 자체를 원천적으로 차단되어 있어 애초에 불가능하다.

그래서 함수형 프로그래밍에서는 저장 공간을 경유하는 대신   f 와   g 를 합성하여 하나의 함수인 것처럼 동작하는, 즉 동일한 입력에 대해 항상 동일한 결과를 리턴하는 함수를 만들어버린다.

 

 

자, 이렇게 하면 함수형 프로그래밍의 개념을 유지하면서도 깔끔하게 해결된 것 같지만 사실 여기에 숨겨져 있는 문제는 ‘  f 와   g 간에 어떻게 정보를 전달할 것인가’이다. 이 문제는 위 그림처럼 단순하지 않은데, 왜냐하면 저마다 정의된 함수의 리턴값 형태(자료형)를 다른 함수에서 활용할 수 있도록 일반화된 방식이 필요하기 때문이다. 즉  f 의 리턴값을   g 가 활용할 수 있어야 한다.

자 이쯤에서, 이 글의 처음에 인용했던 내용을 기반으로 모나드 이해를 위한 사고흐름을 다시 정리해보자.

  1. 함수들 간에 특정 자료형을 처리할 수 있도록 미리 협의(개발자 입장에서는 이렇게 설계)해 두고
  2. 이러한 함수  f 를 감싸는 컨테이너를 만든 다음      ← 모나드
  3. 이 컨테이너를 다음 함수  g 에 전달하면      ← return
  4. 함수  g 는 함수  f 의 결과를 자연스럽게(협의된 자료형에 근거하여) 활용할 수 있게 된다      ← 바인딩 /   >>= 

사고흐름의 1, 2번을 함수  g가 모나드(컨테이너)를 입력받아 모나드를 리턴하는, 즉 모나드로부터 함수를 꺼내어(바인딩) 모나드를 리턴하는 것으로 도식화해보면 다음과 같다 (모나드를 노란상자로 표시):

 

 

다음으로 사고흐름 3, 4번을 해당하는, 즉 함수  g에 함수  f의 결과를 전달하여 수행하는 것을 도식화해보면 위 그림의 입력과 출력을 다음과 같이 바꿔 그릴 수 있다.

 

 

이 때 콘솔 출력과 같은 I/O 작업을 모나드로 처리하면 위의 흐름과 동일하게 함수  g의 결과를 받아 출력할 수 있게 된다 (또는 함수  g를 I/O 작업을 처리하는 함수로 본다면 함수  g는 함수  f를 감싼 모나드를 받아 콘솔에 출력하는 그림을 그려볼 수도 있겠다).

사실 따지고 보면 함수를 first-class citizen으로 취급하는 방식이 ‘자료형’에 의존하고 있기 때문에 발생하는 복잡성이라고 할 수 있다. 특히 하스켈의 경우 견고한 프로그램을 작성을 위해 자료형을 중요하게 생각하는데다 ‘순수’ 함수형 프로그래밍답게 상태변경이나 부수효과를 고집스러울만치 차단하고 있기 때문에 단순한 콘솔 출력도 모나드의 도움을 받아야 한다 (물론 콘솔 I/O와 같이 자주 사용되는 동작들은 내부적으로 모나드가 구현되어 있어 개발자가 일일이 신경 쓸 필요는 없다).

리습이나 클로저의 경우는 좀 다르다. 소스코드 자체가 리스트로 작성되어 있기 때문에 (code is data), 모나드와 같은 컨테이너나 다른 객체 등의 도움이 없이 코드 자체를 다른 함수에 넘겨버리면 그만이기 때문이다. 물론 넘어온 데이터(함수) 자체를 잘 사용해야 할 책임을 받는 쪽에서 지게 되는 단점이 있기는 하지만, 모나드와 같은 복잡한 구조를 고민하지 않아도 되기 때문에 구현에 있어서 사고흐름의 단순함을 유지할 수 있다는 것은 큰 장점이다. 또한 하스켈 등의 함수형 프로그래밍 언어들이 제공하는 함수 합성을 위한 구조들도 당연히 제공되며, 그 구조 자체도 하나의 데이터이기 때문에 상당히 유연한 프로그래밍이 가능하다.

 

결론은..

클로저(Clojure) 만쉐이!

 

Clojure 프로젝트 공유하기 (Gorilla REPL)

파이썬에 Jupyter가 있다면, 클로저에는 Gorilla REPL이 있다. 물론 Jupyter에도 클로저 커널을 올려서 사용할 수 있지만, 기본 REPL을 띄워서 연습하는 경우면 몰라도   project.clj에 dependencies나 기타 세팅을 포함한 프로젝트를 테스트하는데는 무리가 있다. 파이썬의 경우에는 virtual env에 세팅을 해버리면 그만이지만 클로저는 프로젝트 세팅을 참조해서 Leiningen REPL을 올려야 하기 때문이다.

물론 동적으로 의존성을 추가할 수 있는 라이브러리도 존재하긴 하지만 여전히 번거로운 작업이고, Jupyter나 Gorilla REPL과 같은 노트북 스타일을 사용하는 목적은 스크립트와 그 실행 결과를 Markdown 기반의 설명과 함께 공유하기 위함인데 노트북에 프로젝트 세팅하는 코드가 난무하는 것도 그리 바람직하지 못하다. 이런 면에서 클로저 프로젝트를 위해선 Jupyter보다는 Gorilla REPL을 사용하는 것이 그 목적에 더 부합하는 것 같다.

Gorilla REPL도 Jupyter와 유사하게 웹 UI상에서 클로저 코드를 실행하고 그 결과를 동적으로 렌더링해주며, Markdown 문법을 사용한 설명을 덧붙여 저장할 수 있는 기능을 제공한다.

이렇게 작성한 결과는 클로저 파일로 저장된다.

웹 UI 상에서 실행했던 코드와 결과들을 Gorilla REPL에서 정의한 notation들이 포함된 주석으로 감싸고 있다. 그런데 앞서 언급했듯이, 노트북 스타일의 결과물은 공유 기능이 상당히 중요한 부분 중 하나인데, Gorilla REPL의 웹 UI와 같이 HTML로 렌더링된 결과물을 공유하고자 할 때는 해당 프로젝트를 통째로 공유한 다음 다른 사용자가 Gorilla REPL을 띄워 확인하도록 해야 하는데 상당히 번거롭다. 공유 대상이 클로저 개발자라면 Gorilla REPL이 저장한 클로저 파일을 공유할 수도 있겠지만, 번잡스러운 주석이 걸리적거리는 것을 감수해야 한다.

Github에 프로젝트를 공개하는 경우 Jupyter 노트북의 저장 결과물인 ‘.ipynb’는 자동으로 렌더링되어 Markdown 형식으로 살펴볼 수 있지만 Gorilla REPL의 결과 클로저 파일은 렌더링되지 않기 때문에 변환과정을 거치는 수 밖에 없다. 다행히도 Gist에 Timothy Renner가 작성해둔 변환 코드가 공개되어 있길래, 이걸 조금 다듬어서 변환 프로젝트를 배포했는데, Gorilla REPL 기반의 프로젝트에 포함된 모든 노트북들을 한 번에 Markdown으로 변환해주는 프로젝트가 있으면 편리할 것 같아 아예 Leiningen 플러그인으로 배포해 보았다.

Gorilla REPL을 사용하는 프로젝트에  lein-gorildown 플러그인을 세팅하고 터미널에서   $ lein gorildown 을 실행해주면 Gorilla REPL 노트북만 골라내어 Markdown으로 변환해준다. 변환된 파일을 Github에 공유하거나 Markdown 뷰어를 사용하여 확인하면 되니 공유하기에도 편리하다.

open-korean-text-4clj 개발 시작

한글 자연어처리 라이브러리 중 관심을 갖고 있던 open-korean-text를 사용해보기 위해 Clojure용 wrapper를 만들어보기로 하고, 드디어 첫 배포를 완료했다.

https://github.com/open-korean-text/open-korean-text-4clj

문서와 테스트도 추가해야 하고, 기능측면에서도 개선/확장해보고 싶은 것이 산적해 있지만, 일단 시작한 것으로 만족.

open-korean-text 자체는 Scala로 개발되어 있고 중요 기능에 대해선 Java Wrapper를 제공하는데, 둘 모두 JVM에 기반하고 있기 때문에 Clojure에서 이들을 적절히 섞어 사용해가면서 Clojure스러운 라이브러리를 개발해가는 것이 목표다.

도전해보자 🙂