개발에서의 Leaky Abstraction
지금으로부터 15년전, 강산이 한번 변하고, 다시 한참 변하고 있을 때 쯔음에 Joel은 그의 블로그에 Leaky Abstraction이라는 주제로 글을 썼다. (참조: Link)
이것이 많은 개발자들에게 회자되면서 비로소 “개념화” 되었다.
그가 Leaky Abstraction을 설명하면서 든 예들을 잠시 훑어 보자.
TCP는 Reliable 한 네트워크 프로토콜이다. 그런데 아이러니는 Unreliable한 네트워크 프로토콜인 IP위에 얹어서 보내 진다. 패킷이 유실되어 보내지 않더라도, 잠시 특정 네트워크 딜레이가 발생해서 패킷의 순서가 꼬이더라도 TCP는 패킷의 순서가 보장해 준다. 그래서, 그 아래 IP 단에서 발생하는 많은 Unreliable한 결과들을 신경쓰지 않고, 그져 TCP가 순서대로 전달해주는 데이터를 잘 사용하기만 하면된다.
이것이 바로 Abstraction이라는 개념이다.
많은 컴퓨터 Software가 Abstraction 이라는 작업 위에 만들어진다.
예를들면 C++에서 string 라이브러리는 복잡한 char *(캐릭터 포인터)의 연산을 사용하지 않고도 간단하게 string을 다룰 수 있도록 해준다. Abstraction을 활용한 경우다. 하드 디스크에서 파일을 읽고 쓸 때에 우리는 하드디스크의 바늘이 어느 주소를 가르키고, 디스크 회전을 몇도로 해서 몇블럭을 읽어오는지 일일이 만들지 않아도 간단히 코드 한줄로 파일들을 읽어 올 수 있다. 역시 Abstraction이 제공해 주는 편리함이다.
그런데, TCP의 경우 네트워크 선을 집안의 강아지가 물어 뜻어 버렸다고 하자. 이럴 때 Abstraction은 제대로 동작하지 않는다. 누군가가 회사의 네트워크 허브를 먹통으로 만들었다면 역시 TCP는 극도로 느려진다. 이런 경우를 Leaky Abstraction이라고 한다.
Leaky Abstraction 의 더 다양한 예는 아래와 같다.
우리는 2차원 배열의 Iteration을 추상화해서 매우 편리하게 사용한다. 그런데, 2차원 배열의 검색 순서를 횡으로 하느냐, 종으로 하느냐에 따라서 특정 값을 찾아내는 속도는 매우 달라질 수 있다.
SQL은 어떠한가? 동일한 값을 DB에서 가지고 오는 경우에도 “where a=b and b=c” 쓰는 것보다, “where a=b and b=c and a=c” 이렇게 구문을 던지는 경우 훨씬 빨랐던 적이 있었다.
C++의 경우는 어떠한가? 우리는 Pointer의 개념을 몰라도, Abstraction의 편리함인 stl의 string class를 잘 쓴다. 그러나, “abc” + “cde”, 즉 string literial의 합이 컴파일 에러를 뿜는 동시에 Leaky Abstraction을 경험하게 되고, char *의 개념, 즉 Abstraction의 밑단 개념들을 파고 들어가야 한다.
비가 올 때 자동차 안은 비바람이 치는 환경으로부터 좋은 Abstraction을 제공한다. 마치 비가 오지 않고 바람이 불지 않는 경우 처럼 자동차 안의 환경을 만들어 준다. 그러나 비 바람이 어마어마하게 몰아쳐서 와이퍼의 속도가 비를 모두 쓸어내리지 못할 때나, 와이퍼가 돌맹이에 맞아서 부러져 나가는 경우에 더이상 Abstraction의 환경이 동작하지 않는다.
이러한 것들이 바로 Leaky Abstraction, 즉 추상화의 구멍이다.
이러한 점들로부터 우리가 인지해야할 점들은 다음과 같다.
첫째로, 이 세상에 존재하는 모든 Software에서의 Abstraction에는 이러한 Leak이 존재한다고 가정하는 것이다. 우리는 편리함으로 Abstraction을 많이 이용한다. 그러나 그것이 언제든지 Leak이 발생할 수 있다는 것을 인지해야 한다. 그것을 완벽하게 의지하거나, 마치 모든 것을 한방에 해결해주는 Silver Bullet으로 간주해서는 안된다.
둘째로, Leak이 존재하는 위험성 때문에 가능한한 Abstraction의 밑단을 더 알 필요가 있다는 점이다. 좋은 툴이 나와서 엄청 효율적으로 일을 할 수 있다고 하자. 그러나 많은 개발 Guru들은 우리에게 일단은 수동으로, Abstraction없이 그 기능을 하는 것을 만들어 보고, 그런 다음 툴들을 배우라는 조언을 많이 한다. 가능하다면 Unity나 Unreal을 만져보기 전에 Graphics와 OpenGL의 기본을 알고 그것들을 배우라고 말하는 것이다. 이러한 Abstraction의 밑단 동작은 Abstraction의 Leak이 발생했을 때 어떻게 대처해야하는 지 대응할 수 있게 해준다.
셋째로, 우리가 인지해야할 문제는 Abstraction이 우리를 편리하게는 해주지만, 배움에서는 크게 도움되지 않는다는 점이다. 더 좋은 툴, 더 좋은 엔진들이 나오더라도 결국 leaky abstraction을 해결하거나, 부딪혔을 때 이해하여 우회하는 것들을 해낼 수 있는 것을 지향해야 한다. 코드를 읽어 나갈 때 어떠한 심오한 이름의 API Call과 맞부딪히기 된다면, 그 이름이 이야기하는 의미만을 유추하여 다음 라인으로 넘어가지 말고, “정의로 이동”을 메뉴를 눌러보는 것도 좋은 습관이 될 것이다.
좋은 툴, 빠른 개발이 개발 문화의 일상이 된 우리 시대에 Leaky Abstraction의 위험성에 대한 인지는 새로운 깃발을 들고 다른 산으로 달려가는 돈키호테와 같은 느낌이다.