"API 디자인은 한번의 기회만 있다"
어제 참여했던 API design study group에서 들은 말이다. 오늘은 스터디 그룹에서 공부한 내용을 적어보기로 하겠다.
그동안 개발을 하며 추상적으로 생각해 왔던, Windows API나 MFC, Java 를 이용하면서
"왜 이런 구조/코드를 사용했을까?"
"c에서 제공하는 api 중 memcpy와 memset등 첫번째 인자로 dst가 오는것은, 우연일까"
라는 생각을 해본적이 있는가?
API design study group는 이러한 질문에 대한 해답을 찾고자 시작된 study group이다.
흔히 API라고 한다면 윈도우 개발자는 MSDN에 자세히 나와있는 Windows API를 생각할 것이고, 리눅스 개발자라면 man페이지와 같은 곳에 자세히 나와있는 c library + linux kernel API를 생각할 것이고, JSP 개발자라면 JDK 문서에 언급된 Java API를 생각 할 것이다.
그런데 여기서 말하는 API는 위에서 설명한 API 같이 광범위한 API 뿐만이 아닌 외부에 표출되는 API도 포함이 되는데 예를 들면 옆의 사람에게,
"이러한 인자들을 주면 이런식으로 동작하는 함수를 만들어줘"
라고 하여 옆 사람이 a()라는 함수를 제작하였다고 해 보자.
a()를 다른 사람이 이용한다면 해당 함수는 API가 될 수 있다는 의미이며, 이 때부터 a()를 제작한 사람의 긴장(^^;)은 시작된다.
보통 API가 제작되는 순서는
1. 요구사항 수집 (무엇때문에 API를 사용하려고 하나?)
2. 명세
3. feedback
4. 살을 붙이면서 예제코드 작성
과 같은 과정을 거치는데, 이 때 요구사항 수집을 할 때에는 실제 API가 어떻게 사용되어야 하는지를 알아야 한다.
지금까지 함수 쯤이야 대충 만들면 되지 (물론 발가락으로 만드는 대충이라는 의미는 아니다. 이런걸 고려 하지 않았다는 말이다) 라고 생각해왔는데 생각을 해보니 (그렇다면 평소에 생각을 안한것이란 말인가? 그건 또 아니다) 맞는 말이다.
API는 기능 구현도 우선시 되어야 하지만 사용자(=개발자)의 편의성도 있어야 한다.
생각하면 당연하지 않은가?
하지만, 당장 지금까지 자신이 짰던 API들을 보라. 사용하기가 쉬운가?
몇년 전에 많이 하던 wrapper class 만드는 작업을 하면서 어떻게 클래스를 만들어야 나중에 사용하기가 쉬울까? 라는 생각을 한 적이 있다.
이런건 학교에서는 절대 가르쳐 주지 않는 내용이기 때문에 자신이 스스로 터득해 나가야 된다고 생각을 하며 "나름대로" 공부를 했는데..
처음에는 클래스를 생성할 때 생성자로 데이터를 준 뒤 객체를 초기화 하는 방식의 API를 즐겨 사용하였다.
그 다음에는 set/get을 주로 사용하였고, 이제는 두가지를 병행해서 사용한다.
어느 패턴이 좋은지는 상황마다 다르지만, 객체를 생성할 때 같이 초기화 하는 방법은 Java에서는 좋은 방법일 지 모르지만, C++에서는 불편한 방법이었기 때문에 set/get를 주로 사용하였다.
좀 더 구체적으로 설명하자면 Java에서는 null 상태의 레퍼런스 변수만 만들어 놓고 나중에 new 하면 되기 때문에 문제가 될 께 없다지만 C++에서는 변수의 생성과 함께 객체가 초기화 되어 버리니, 멤버 변수로 객체를 선언하면 문제가 발생할 수 있다. C++에서 has-a 관계를 만드려고 하는데.. 생성자에서 초기화 하려면 이건 포함되어 있는 클래스에 붙어서 동작하는 객체같이 보여지게 되므로, 생성자로 초기화 하는 방법을 쓰는게 애매하다는거다. 포인터 변수를 이용하면 Java를 따라할 수 있지만 포인터 관리가 문제가 된다.
이럴 때 어떤 방식으로 API나 class를 디자인 하는게 좋을까? 라는 생각을 갖게 되는데, 스터디 그룹에서 이러한 상황에서 사용할 수 있는 원칙을 제시하였다.
1. 3개의 vender에 맞게 먼저 제작하여 본다. (3의 법칙)
2. API는 한가지 일을 하게 한다.
3. 기능은 최대한으로 하지만 확실하지 않는 기능은 추가하지 않는다. (중요!)
4. Conceptual weight를 줄인다.
- Conceptual weight: 개발자가 API를 이용하기 위해 새로 만들어야 하는 개념. class나 function의 개수와는 별개이다.
5. 구현 세부사항을 알리지 않으며 기능대비 복잡도를 유지한다. Java에서 hash는 내용이 spec에 나와 있어서 알고리즘을 바꿀 수 없다. 다시 말해, 추 후 성능이 좋은 hash 알고리즘이 나오더라도 바꿀 수 없다.
6. Wire format? (serialize 할 때)
7. Exception 처리는 API 내부에서 모두 처리해야 한다. API가 수행될 때 외부로 예외가 발생하면 안된다.
8. add/remove, get/set, register/unregister와 같이 대칭을 이루어야 하며, 대칭이 되지 않았다면 문서화 해야 한다.
9. 언어마다의 규칙이 있는데, 이것이 꼬이지 않게 한다.
이 중 3번은 굉장히 중요한 사실인데, API를 제작하고 난 뒤 기능의 추가는 가능하지만 기존에 제작된 기능은 불가능 하기 때문이다.
실제로 Java를 보면 많은 method가 deprecated 된 것을 볼 수 있는데, 처음 설계를 잘못했기(했다고 하기에도 뭐하지만) 때문이다.
지금까지 API라고 생각하면 그냥 함수 만들듯 만들면 되는게 아닌가? 라고 생각을 하였지만,
생각해보면 너무나도 당연한 3번, 4번을 생각하지 못하며 API를 디자인 하고 있었다는 게
자칫 큰 실수를 일으켰을지도 모른다는 생각을 하였다.
앞으로 개발을 많이 하게 되겠지만, 내가 제작하게 될 프로그램들을 위해 API 디자인은 꼭 공부해놔야 되는 (디자인 패턴같이) 학문(?) 이라는 생각이 든다.
P.S. 많은 내용을 배울 수 있는 기회를 만들어 주신 서광열(http://skyul.tistory.com/) 님께 감사의 말씀을 드립니다~
P.S. 첫 study 모임은 2007/09/18일 모임이었지만, 프로젝트 마무리 때문에 포스팅이 많이 늦게 되었습니다(__) 정신없이 쓴 글이라 두서없더라도 이해해 주시면 감사드리겠습니다.



Attribution/Share Alike 2.0 license






