난 '아는 척'하는 걸 굉장히 싫어한다. 내가 알고 있는 걸 말하기는 좋아하지만, 아는 척 하는 건 싫어한다. 그래서 내가 확실히 알지 못하는 내용은 '모른다'라고 말해준다. 그래서 어렸을 때 부모님은 "넌 대체 아는게 뭐냐"라고 종종 구박을 하곤 했다.
같은 맥락에서, 난 2차 저작물을 별로 안좋아한다. 2차저작물을 읽고 원전의 내용을 알고 있는 것처럼 말하는 것은, 그냥 '아는척'일 뿐이다. 그래서, "플라톤이 말하긴 쏼라쏼라" "칸트가 말하길 이러쿵" "칼 포퍼가 미친 영향은 저러쿵" "아인슈타인의 상대성이론은 뭐시기" 이런 식으로 쓰여진 2차 저작물을 싫어한다.
그런데 지금껏 나는 2차 저작물에 담긴 죽은 지식을 채우고 있다는 걸 문득 깨달았다.
지금까지 내가 받은 교육은 전적으로 2차 저작물에 의존한 것이었다. 단적으로, 교과서자체가 내가 혐오해 마지 않는 2차저작물의 대표작인 것이다. 그래서 개인적으로 어렸을 때는 교과서를 신뢰하지 않았다. 무엇보다도 시대에 뒤쳐져 있는 것이 눈에 보였고, 게다가 2차저작물의 어쩔 수 없는 한계가 있기 때문이다. 2차저작물에는 새로운 내용은 물론이고 작가혼도, 시대정신도, 인간의 뇌를 깨우는 자극도 없다. 그냥 누군가가 만든 내용에 대한 '튀지 않는' 주석이 있을 뿐이다. 이 사람이 이런 업적을 만들어 냈는데, 보통 많은 사람들은 그에 대해 이렇게 생각하고 있다-라고 하는 주석들 뿐.
플라톤 철학을 알고 싶은가? 그럼 플라톤이 직접 쓴 책을 읽자. '국가' 번역본으로 시작하는게 좋겠다. 칸트의 철학을 알고 싶은가? 그럼 '순수이성비판'이 많이 번역되어 있다. 읽자. 특수상대성이론을 알고 싶다면, 지금은 어디서나 아인슈타인이 직접 쓴 '움직이는 물체의 전기동역학에 대하여'라는 논문을 구할 수 있다. 읽어보자. 어렵지 않다.
그래서, 30살이 되기 전에 다음의 책을 꼭 직접 (일부는 다시) 읽어보고자 한다. 현대인으로서 최소한의 교양은 가져야 하지 않겠는가.
국가
순수이성비판
팡세
성찰
방법서설
짜라투스투라는 이렇게 말했다
인간적인, 너무나 인간적인
자본론
공산당선언
열린사회와 그 적들
과학적 발견의 논리
추측과 논박
파인만 물리학 강의
이가운데 한 절반쯤은 (국가, 방법서설, 짜라투스투라는 이렇게 말했다, 공산당 선언, 열린사회와 그 적들) 이미 읽었지만 다시 봐야 할거
같고, 나머지도 올해가 가기 전에 봐야 겠다. 20대 마지막으로 머릿속은 좀 채우고 30대로 넘어가야 하지 않겠는가.
사실상 프로그래밍에서, 명명규칙은 종교적 신념에 가깝다. 어느것이 좋고 나쁘고는 개인적 신념에 따라 갈리기 때문에 '좋은' 명명규칙을 말하기란 불가능에 가깝다. 그럼에도 불구하고 (칼 포퍼의 철학을 따르자면) '나쁜' 명명규칙을 구분할 방법은 존재하고, 그 대표적 사례가 헝가리안 노테이션이다. 아니, 정확히는 '변질된 헝가리안 노테이션'이 나쁜 명명규칙의 대표적 사례이다.
조엘에 따르면,시모니가 만든 최초의 헝가리안 노테이션 규칙은 변수의 타입이 아닌, 변수의 '종류', 즉 쓰임새에 따라 접두어를 붙이는 방식이었는데, (행을 나타내는 변수는 rw, 열을 나타내는 변수는 col등의 접두어) 이에 대해선 유용하다는 것에는 별 이의가 없는듯 하다. 하지만 이렇게 유용했던 헝가리안 노테이션이 변수의 타입을 나타내는 접두어를 붙이는 것으로 변질되면서부터 모든 문제가 시작됐다. 구분하기 위해 원래의 규칙을 App Hungrian, 뒤의 것을 System Hungarian이라고 하자.
시스템 헝가리안의 문제는, 변수의 접두어가 특별한 정보를 전혀 담고 있지 못하다는 것에 있다.
void* p;
LPVOID lpvP; // (1)
여기서 (1)의 접두어 lpv는 그저 '타입이 없는 포인터'라는, 컴파일러가 이미 알고 있는 정보를 중복해서 적어 놓은 것 뿐이다. 선언부의 중복은 말할 것도 없지만, 이것을 실제로 쓰는 쪽에서는 저런 정보가 유용하지 않느냐는 주장도 있는데, 실제로 쓰는 쪽에서도 실제 타입을 인간이 알아야만 제대로 된 코드를 작성할 수 있는 경우는 드물다.
void* q1 = p; // (2)
void* q2 = lpvP; // (3)
현대적인 컴파일러에서 경고나 에러 없이 훌륭히 컴파일된 코드라면, (2)의 코드만으로도 p의 타입이 '타입이 없는 포인터'라는 점을 언제나 알 수 있다. 구태여 (3) 처럼 손가락 아프게 써 봐야 이미 알고 있는 정보를 중복해서 기재한 것 밖에는 안된다. 이는 함수호출시에도 마찬가지이다.
그리고 언제나 그렇지만, 중복은 혼란을 가져온다.
포인터 p가 void*였다가, 어느 순간 메모리 위치 계산을 위해 1바이트 형에 대한 포인터로 바꿔야 할 때가 올 수 있다. 이럴 때 시스템 헝가리안은 대 혼란을 몰고 온다.
시스템 헝가리안을 쓴다고 가정해 보자. (2)의 코드에서 LPVOID로 되어 있는 것을 LPBYTE로 바꾸면, 다음과 같이 된다.
LPBYTE lpvP;
void* q2 = lpvP; // (4)
여기서 (4)의 코드는 언제나 훌륭하게 컴파일 되지만, 변수명은 잘못된 정보를 나타내고 있다. 이제 더이상 lpvP는 void*타입이 아니라, char*타입인 것이다. 따라서 시스템 헝가리안은 변수타입을 바꿀 때 변수명도 함께 바꿔야 한다. 규칙 안에 정보가 중복되도록 마련된 규칙이기 때문이다.
그리고 무엇보다, 시스템 헝가리안은 타이핑하기에 불편하다.lpcsz, lplpdw, lplpci, lpctsz.. 이런 접두어를 치다가 손가락이 꼬일 지경이다.
wchar_t* name;
LPTCHAR lpszName;
이 경우에 단순 비교만 하더라도, 헝가리안 노테이션은 변수이름이 2배로 길어진다. 프로그래밍에서는 row, col, len 같은 간단한 이름의 변수를 절대적으로 많이 쓰는데, 일일이 시스템 헝가리안을 지켜가며 명명하기엔 상당히 번거롭다. 번거로우면 잘 안지키게 되며, 잘 안지키는 규칙은 쓸모가 없거나, 나쁜 규칙이다. 자세한 건 위에 있는 조엘의 링크를 따라가보라.
난 개인적으로 명명규칙은 다음과 같은 조건만 만족하면 된다고 생각한다.
1) 특별한 정보를 전달하지 못하는 접두어/접미어가 없을 것.
2) 멤버변수와 로컬변수를 구분할 수 있을 것.
3) 처음보는 사람도 알 수 있도록 과도한 축약표현을 하지 말 것.
이 이외의 조건은 명명규칙에 있어서는 불필요한 군더더기 제약일 뿐이라고 생각한다.
1) 그 어떠한 접두사도 붙이지 않는다.
2) 클래스/구조체의 멤버는 PascalCasing으로 모든 단어를 풀어서 명명한다.
3) 로컬변수/형식매개변수는 lowercasing으로 혼동이 없는 한에서 축약한다.