21. 좋은 API는 왜 아름다운가
order. 까지만 치면 자동완성 목록이 뜬다. 그 목록에서 getUser()를 고르고 나면, 다음 순간 . 을 다시 눌렀을 때 getCompany()가 목록 위쪽에 떠 있다. 굳이 문서를 열어 반환 타입을 확인할 필요가 없다. 방금 받은 것이 User라는 사실을 이미 알고 있고, User로 무엇을 할 수 있는지도 손이 먼저 알고 있다. 코드는 거의 저절로 이어진다.
반대의 경험도 있다. 어떤 라이브러리는 자동완성 목록에 메서드가 스무 개씩 뜨는데, 그중 어떤 것을 다음에 불러야 하는지는 목록만 봐서는 짐작이 안 간다. fetch()를 불렀는데 그 결과를 바로 쓰면 되는지, resolve()를 한 번 더 거쳐야 하는지, 아니면 commit()까지 불러야 값이 반영되는지는 매번 문서로 돌아가 확인해야 한다. 같은 자동완성 기능인데 한쪽에서는 손이 먼저 움직이고, 다른 쪽에서는 매번 멈춰 선다.
이 차이를 짜증이나 익숙함의 문제로 넘기면 놓치는 것이 있다. order.getUser()가 자연스러운 이유는 이 API가 특별히 친절해서가 아니다. 6장에서 함수는 한 대상에서 다른 대상으로 건너가는 통로라고 했다. getUser()가 돌려주는 자리와 getCompany()가 받아들이는 자리가 정확히 같은 모양이기 때문에, 앞선 함수가 남긴 결과를 뒤의 함수가 아무 거부감 없이 받아 안는다. 자동완성 목록에서 헤매는 라이브러리는 대개 이 자리가 어긋나 있다. 반환값의 모양과 다음 함수가 기대하는 모양이 미묘하게 다르거나, 같은 모양인데도 하나는 값을 바로 쓰고 다른 하나는 한 번 더 감싸야 하는 식으로 규칙이 오락가락한다. 사용자는 그 어긋남을 매번 손으로 메워야 한다.
좋은 API를 쓸 때 느끼는 편안함은 결국 새로운 것을 다시 배우지 않아도 된다는 감각이다. 나쁜 API는 메서드마다 새로운 규칙을 하나씩 얹는다. 이 함수는 결과를 바로 쓰고, 저 함수는 콜백을 넘겨야 하고, 어떤 함수는 예외를 던지고 어떤 함수는 null을 돌려준다. 배운 것이 다음 메서드 앞에서 매번 무력해지면, 사용자는 결국 API를 몸으로 익히기를 포기하고 문서에 의지하는 쪽을 택한다.
8장에서 타입을 콘센트 규격에 비유했다. 모양만 맞으면 뒤에서 무슨 회로가 돌아가는지 몰라도 연결된다는 이야기였다. 좋은 API는 이 규격을 낱개의 함수 하나하나에서만 지키는 것이 아니라, 함수를 이어 붙이는 방식 전체에 걸쳐 지킨다. 함수 하나의 입력과 출력이 맞는 것으로는 부족하다. 그 함수들을 이어 붙였을 때의 결과가, 사용자가 이미 알고 있던 이어 붙이기의 규칙과 어긋나지 않아야 한다. 이것은 결국 낱개의 약속뿐 아니라, 약속을 조합하는 방식 자체를 보존하는 일이었다.
그런데 이 약속이 지켜지는 한, 약속 뒤편에서 무슨 일이 벌어지는지는 자유롭다. getUser()가 데이터베이스를 조회하든 캐시를 먼저 확인하든, 사용자에게는 상관없는 일이다. 그렇다면 이 뒤편을 통째로 다른 것으로 바꿔치기해도, 앞쪽의 약속만 그대로라면 아무 일도 일어나지 않아야 하는 것 아닐까.