회사의 신입 개발자가 아래와 같은 함수를 만들었다. URL의 페이지를 긁어 오는 함수다. url에는 긁어올 웹 페이지 주소를 지정해서 함수를 호출하면 해당 웹 페이지를 긁어오는데 성공하면 check에 TRUE가 반환되고 string에는 페이지 내용이, 실패한 경우에는 check에 FALSE가 반환되고, string은 빈 문자열이 넘어온다.
std::string GetPage(WCHAR *url, BOOL *check);
이 디자인의 칭찬할 점은 함수의 리턴 값을 중의적인 내용으로 사용하지 않았다는 점에 있다. 리턴 문자열이 빈 문자열(“”)이면 오류라고 식별하는 것처럼 디자인해서 리턴 값에 성공/실패의 의미와 페이지 내용을 같이 담지는 않았다는 의미다.
하지만 둘 중에 어떤 것을 리턴 값으로 선택하는지에 대해서는 생각이 조금 짧았다. 왜냐하면 함수 내용을 살펴보면 check는 함수의 수행 지점에 상관 없이 항상 덮어쓰기가 일어나지만 리턴 문자열은 성공한 경우에만 덮어쓰면 되기 때문이다. 이런 점을 생각했다면 check를 리턴 값으로 삼는 것이 좋았을 것이다. check를 리턴 값으로 선택하면 실패한 경우에 문자열 복사가 발생하지 않기 때문이다.
일반적으로 개발자들이 많이 사용하는 함수 디자인 패턴이 있다. 그런 디자인 패턴에는 일반적으로 함수를 디자인한 사람의 생각과 그 함수를 사용하는 사람이 전제할 수 있는 내용들이 있다. 많이 사용되는 디자인을 살펴보면 다음과 같은 형태들이 있다.
BOOL GetPage(const WCHAR *url, std::string &buffer);
url에 const가 붙어있기 때문에 입력으로 사용되는 값임을 유추할 수 있다. buffer는 레퍼런스이기 때문에 반드시 값이 지정되어야 한다는 것을 의미한다. BOOL은 함수의 성공 여부를 리턴 한다고 기대할 수 있다. 실패한 경우에는 GetLastError같은 함수나 errno 같은 전역 변수를 사용해서 추가적인 오류 코드를 확인할 수 있도록 설계된 경우가 대부분이다.
HRESULT GetPage(const WCHAR *url, std::string &buffer);
이 경우에 바뀐건 BOOL이 HRESULT가 된 것 뿐이다. GetLastError를 사용할 필요 없이 실패 원인을 직접 반환하겠다는 의도다.
HRESULT GetPage(const WCHAR *url, std::string *buffer);
buffer가 레퍼런스에서 포인터로 변경됐다. 이 값이 필요 없는 경우에는 NULL을 지정해도 된다는 것을 의미한다. 물론 이 관습을 모든 함수가 따르는 건 아니다. 대체로 그렇다는 말이다.
HRESULT GetPage(const WCHAR *url, WCHAR *buffer, SIZE_T size);
buffer가 문자열 클래스에서 문자열 포인터로 변경됐다. buffer는 size 만큼을 담을 수 있다는 것을 의미한다. 이 경우에 size가 문자 개수냐 바이트 수냐는 애매하다. 함수 설명에 어떤 내용을 의미하는지 명시할 필요가 있다.
HRESULT GetPage(const WCHAR *url, WCHAR **buffer);
GetPage란 함수가 메모리를 할당해서 buffer에 반환하겠다는 의도다. 이렇게 디자인 된 함수는 통상적으로 어떤 함수를 사용해서 buffer를 해제해야 하는지를 명시하고 있는 경우가 대부분이다. buffer의 사용이 끝나면 해당 함수를 통해서 메모리 해제를 해주어야 한다.
WCHAR *GetPage(const WCHAR *url);
반환 값이 WCHAR *이 된 함수다. 이 함수 또한 GetPage 내부에서 메모리 할당을 하겠다는 생각이 깔려있다. 실패한 경우에는 통상적으로 NULL이 반한되고 세부적인 원인은 GetLastError등의 별도 함수를 사용해서 알 수 있도록 디자인된 경우가 대부분이다. 또한 앞선 함수와 마찬가지로 반환 값을 어떤 함수를 사용해서 해제해야 하는지를 명시하는 경우가 대부분이다. 반환값 사용이 끝나면 해당 함수를 통해서 메모리 해제를 해주어야 한다.
HANDLE GetPage(const WCHAR *url);
반환 값이 HANDLE이 된 함수다. 이 경우에는 핸들을 통해서 페이지 내용을 조회할 수 있는 함수와 핸들을 닫는 함수가 존재한다는 것을 의미한다. 각각 어떤 함수를 사용해야 하는지를 명시할 필요가 있다.