洪民憙 (홍민희) 블로그

이하의 글은 2012년에 쓴 것입니다. 오래된 글인 만큼, 현재의 생각과 전혀 다른 내용도 많이 포함되어 있고, 당시와는 상황이 많이 달라진 점도 있습니다. 또한, 그 당시에 잘못 알려졌던 정보도 포함되어 있을 수 있습니다. 어찌됐든 저는 제 오래된 글이 회자되는 것을 저어합니다. 읽기에 앞서 양해를 부탁드립니다.

Capabilities, primitives and levels

RDBMS 연구자들이나 DBA들이 하는 흔한 얘기중 하나는 어떤 자료구조든 RDBMS에서 표현 가능하다는 말이다. 예를 들어, 트리(tree)는 nested set model을 통해 관계형 데이터베이스(relational database)로 인코딩될 수 있다. 그리고 이어져서 나오는 이야기가 꼭 있다. 관계형 데이터베이스는 고수준의 자료 구조라는 것이다. 우리가 컴퓨터 과학을 입문할 때 배우는 많은 자료 구조는 저수준이고, 관계형 데이터베이스는 고수준이다. 어째서 이런 얘기를 하는가?

많은 사람들이 가능성(capability)과 수준(level)을 착각한다. 가능성은 주어진 프리미티브(primitive)에 대한 성질이고, 수준은 구현하려는 것과 구현하는데 사용할 프리미티브의 거리에 대한 표현이다.

가끔 이런 얘기를 한다. 관계형 데이터베이스는 강력한 프리미티브예요. 이 말은 관계형 데이터베이스가 제공하는 프리미티브의 조합이 얼만큼의 가능성을 가지고 있는지에 대한 이야기이다. 하지만 그게 관계형 데이터베이스가 고수준이라는 것을 뜻하지는 않는다. 고수준이라는 것은 언제나 구현에 사용할 프리미티브와 구현할 대상이라는 두 가지를 모두 따져야한다. 우리는 A와 B가 친하다고는 얘기할 수 있지만, A가 친하다는 말은 말은 매우 이상하게 들린다. 친하다가 두 사람에 관한 말이듯, 수준도 하나의 프리미티브와 하나의 구현 대상에 관한 말이다.

정수와 그에 대한 덧셈과 곱셈 연산이 주어지면 순서쌍(pair)을 인코딩할 수 있다.1 순서쌍이 있으면 리스트나 트리를 인코딩할 수 있다.2 하지만 트리를 인코딩한 정수는 매우 복잡하다. 마찬가지로 정수가 없어도 리스트로 정수를 인코딩할 수 있지만, 정수를 인코딩한 리스트는 매우 복잡하다. 둘 중 어떤 것이 더 고수준인가?

동영상과 이미지는 여러 방법을 통해 바이트열로 인코딩될 수 있다. UTF-8 등의 유니코드 인코딩은 유니코드 문자열을 바이트열로 표현할 수 있게 해준다. 하지만 마찬가지로 동영상을 인코딩한 바이트열은 매우 복잡하다. 반대로 바이트열은 까맣고 하얀 장면들의 동영상으로 인코딩할 수 있다. (PC통신을 하던 시절 모뎀이 하던 일이 무엇이라고 생각하는가?) 바이트열은 동영상보다 고수준인가?

컨티뉴에이션(continuation)이 있다면 코루틴(coroutine)이나 서브루틴(subroutine)을 구현할 수 있다. 하지만 서브루틴을 이용한 프로그램을 서브루틴 없이 컨티뉴에이션만 가지고 프로그래밍하는 것은 goto만 가지고 서브루틴 없이 프로그램을 짠다는 소리와 같다. 컨티뉴에이션은 서브루틴보다 고수준인가?

튜링 완전(Turing completeness)하다는 것은 무슨 의미인가? 어떤 언어가 튜링 완전하다는 것을 증명하는 가장 확실한 방법은 다른 튜링 완전한 언어를 그 언어로 구현해보는 것이다. BefungeBrainfuck은 튜링 완전한 언어이므로 저런 언어를 구현해보면 된다. 튜링 완전한 Befunge는 마찬가지로 튜링 완전한 Haskell 언어를 이론적으로 구현할 수 있으며, Haskell 언어로 작성 가능한 모든 프로그램은 Befunge로도 작성할 수 있다. 그렇다면 우리는 Befunge가 Haskell만큼 고수준의 언어라는 결론을 내릴 수 있는가?

관계형 데이터베이스가 고수준이라는 것은 우리가 현대에 사용하고 있는 전자 컴퓨터가 제공하는 프리미티브 위에서 복잡하게 구현해야 한다는 것을 뜻한다. 전자 컴퓨터가 제공하는 프리미티브는 우리가 수준을 얘기할 때 생략되지만 암시적으로 가리키는 것이다. CSS 렌더링 엔진은 Linux보다 고수준이다. 하지만 JavaScript 프로그래머 입장에서는 CSS 렌더링은 프리미티브에 한없이 가까운 저수준이며 jslinux는 매우 고수준의 달성 과제이다. 전자 컴퓨터 위에서 구현하는 간단한 트리는 관계형 데이터베이스보다 훨씬 저수준이지만, 당연히 관계형 데이터베이스 위에서 nested set model로 구현하는 트리는 아무 것도 하지 않아도 이미 그 자리에 있는 관계형 데이터베이스에 비해 고수준이다.

NoSQL이 함의하는 것도 이런 것이다. 모두가 구현하려는 대상이 다르다. 구현하려는 대상은 프리미티브를 고를 선택권이 있다면, 어떤 프리미티브를 선택하냐에 따라 간단하게도 달성할 수 있고 복잡하게도 달성할 수 있다. 해시테이블은 관계형 데이터베이스에서는 하나의 릴레이션과 두 개의 애트리뷰트, 하나의 인덱스가 필요하지만 memcached나 Redis에는 따로 구현할 필요가 없다. 영속성은 memcached 위에서는 굳이 구현하자면 매우 까다로운 검증 루틴과 주기적인 서비스가 필요하겠지만 관계형 데이터베이스 위에서는 이미 존재하는 어떤 것이다. 하지만 반대로 expiration은 memcached 위에서는 이미 존재하는 어떤 것이지만 관계형 데이터베이스 위에서는 하나의 시각 컬럼과 다른 여러가지가 필요한 구현 대상이 된다. NoSQL이 이야기하는 것은, 당신이 구현하려는 대상이 복잡하지 않고 간단하게, 그리고 효율적으로 구현될 수 있는 적절한 프리미티브를 선택하라는 것이다.

관계형 데이터베이스가 고수준이라는 이야기는 그러니까, 관계형 데이터베이스 자체는 우리가 수준을 얘기할 때 암시적으로 생략하는 디폴트 프리미티브인 전자 컴퓨터 위에서는 고수준이라는 뜻이기도 하고, 우리가 소프트웨어를 만들 때 흔히 구현해야 하는 많은 것들이 관계형 데이터베이스라는 프리미티브 위에서는 전자 컴퓨터라는 프리미티브 위에서보다 대체로 덜 고수준이라는 (즉, 비용이 적고 간단하다는) 뜻이다. 하지만 그냥 해시가 필요한데 관계형 데이터베이스를 쓰는 것은 굳이 C 대신 JavaScript로 Linux를 구현하려는 시도와 같다. 의미가 있다면 유머와 쇼의 의미에서이지, 효율과 합리의 관점에서가 아니다.


  1. (a, b) = (a + b) × (a + b) + a

  2. Lisp이 순서쌍에 대한 연산(cons, car, cdr)만으로 다른 자료구조를 어떻게 만들어내는지 보면 된다.