‘좋은 걸 어떡해’··· 손이 가는 프로그래밍 일탈 10가지

InfoWorld
규칙을 어기면 약간의 스릴을 느낄 수 있다. 때로는 더 좋고 더 효율적인 코드를 작성할 수 있기도 하다.
 
ⓒ Image Credit : Getty Images Bank


우리 모두 해본 적이 있다. 엄마가 보지 않을 때 쿠키를 집어 들고, 저녁으로 와인을 좀 많이 마시고, 차를 애매한 곳에 잠깐 살짝 주차하는 것 등등 말이다. 종종 제한속도를 초과하기도 한다. 

그리고 맞다, 우리 개발자 모두는 프로그래밍의 기본적인 규칙들을 위반한 적 있다. 모두가 동의하는 규칙들이 걸리적거렸기에 우리 모두는 몰래 그렇게 하는 것을 좋아했다. 나쁜 코드를 입력하기도 하면서 우리는 살아왔다. 

그럼에도 불구하고 프로그래밍 신이 번개를 내리지는 않았으며, 데스크톱은 폭발하지 않았다. 사실, 우리 코드는 컴파일된 채 발송됐고, 고객들은 충분히 행복해 보였다.

왜냐하면 나쁜 프로그래밍은 예를 들어, 전기 울타리를 핥거나 호랑이의 꼬리를 잡아당기는 것과 같지 않기 때문이다. 대부분의 경우 잘 작동된다. 규칙은 방침이나 양식상의 제안인 경우가 더 많지, 반드시 따라야 하거나 죽음이 수반되는 융통성 없는 지침이 아니다. 

물론, 당신의 코드가 비웃음을 살 수도 있고, 어쩌면 공개적으로 조롱당할 수도 있다. 하지만 당신이 규칙을 어기고 있다는 사실은, 근엄한 사회적 관행을 살짝 뒤집는다는 유쾌한 스릴을 더해주기도 한다.

규칙을 어기는 것이 더 나은 경우도 있다. 코드가 더 깔끔하게 나온다. 심지어 더 빠르고 더 간단할 수도 있다. 규칙은 보통 너무 광범위한데, 숙련된 프로그래머라면 규칙을 어김으로써 코드를 개선할 수도 있다. 상사에게 자랑할 정도는 아닐지라도 가끔은 당신 방식대로 코딩하는 게 말이 된다. 

다음은 사람에 따라 도저히 납득할 수 없겠지만, 우리들 중 대부분이 성공적으로 그리고 즐겁게 자주 어기는 규칙 10가지다.

나쁜 프로그래밍 습관 No. 1 : 베끼기(Copying) 
학교에서는 분명히 금지된 행동이다. 단 직장에서는 그리 분명하지 않다. 차용하지 말아야 할 코드 블록이 좀 있기는 하다. 만약 독점 코드에서 나온 것이라면, 당신의 스택에 넣지 말아야 한다. 특히 저작권 메시지가 표시되어 있다면 더더욱 그렇다. 탐이 날지라도 자신만의 버전을 써야 한다. 그것이 회사가 당신에게 돈을 주는 이유 중 하나다. 

더 애매한 경우는 원작자가 공유하고 싶어할 때다. 아마도 그것은 온라인 프로그램 포럼 중 하나에 올라와 있을 것이다. 아마도 두어 가지 기능 차용을 허용하는 (BSD, MIT) 라이선스를 가진 오픈소스 코드일 터다. 이 행동을 막을 법적인 이유는 없다. 그리고 당신은 문제를 해결하라고 돈을 받는 것이지, 창조적 활동을 하라고 돈을 받는 것이 아니다.

대부분의 경우 베끼기의 장점은 강력하다. 약간의 주의만 기울이면 단점도 극복할 수 있다. 당신이 명망 있는 출처에서 얻은 코드에는 적어도 일정 고민의 결과물이 이미 적용되어 있었다. 원저자는 해결책을 찾다가 무언가를 발견했다. 루프 불변 및 데이터 흐름이 해결됐다.

까다로운 문제는 어떤 미발견 버그 또는 역할이나 기초 데이터에 대한 다른 가정들이 있는가 하는 것이다. 아마도 당신의 코드는 눌 포인터(null pointer)에 섞여 있을 것이다. 그럼에도 불구하고 만약 당신이 문제를 해결할 수 있다면, 그것은 당신의 상사가 두 프로그래머로부터 성과를 얻는 것과 같다. 그것은 협업 책상이 없을 뿐인 페어 프로그래밍(pair programming )이다.

나쁜 프로그래밍 습관 No. 2 : 논-펑셔널 코드(Non-functional code)
지난 10여 년간 펑셔널 패러다임(functional paradigm )이 발전해왔다. 일련의 기능 집합으로부터 당신의 프로그램을 구축하게 해주는 이 조수는, 어떻게 코드가 예전 방식의 변수와 루프보다 더 안전하고 버그가 없는지를 잘 보여준다. 이 모든 것이 프로그래머를 행복하게 만들 수 있다. 일부 사람들이 코드 리뷰와 풀 리퀘스트에 있어 논-펑서널 접근법을 비난하곤 하는 이유다. 

물론 그들이 옳을 수 있다. 하지만 가끔은 그냥 강력 접착 테이프 한 통을 꺼내야 할 때가 있다. 훌륭하게 설계되고 우아하게 계획된 코드는 고안 및 구축하고 나중에 탐색하는 데도 시간이 걸린다. 그 모든 레이어들은 복잡도를 더하고, 복잡도는 비싸다. 아름다운 펑셔널 코드를 개발하려면 미리 계획을 세우고 모든 데이터가 적절한 경로를 따라 전달되도록 해야 한다. 

때로는 손을 뻗어 변수를 바꾸는 것이 더 쉽다. 아마 설명하기 위해 코멘트로 붙일 것이다. 미래 세대들에게 전하는 장문의 사과를 더하는 코멘트에 더하는 것이, 올바른 방식으로 하기 위해 전체 시스템을 다시 설계하는 것보다 더 신속하다.

나쁜 프로그래밍 습관 No. 3 : 비표준적 띄어쓰기(Non-standard spacing)
소프트웨어에서 빈칸 대부분은 프로그램 작동에 큰 영향을 미치지 않는다. 파이썬(Python)과 같이 코드 블록을 나타내기 위해 띄어쓰기를 사용하는 몇 가지 언어를 제외하고서는 그렇다. 그럼에도 불구하고, 그것들을 유의미하고 중요하다고 주장하는 강박적인 프로그래머들이 있다. 

그 중 한 사람은 내가 ‘비표준 코드’를 쓰고 있다고 아주 심각한 말투로 내 상사에게 말한 적 있다. 내 죄가 무엇이었냐고? 이퀄 사인(equal sign) 양쪽에 빈칸을 두지 않음으로써 ‘ESLint space-infix-ops’ 규칙을 위반했다는 것이었다. 

개발자의 집중력은 제한적인 자원이다. 빈칸을 두는 것보다 더 심오하고 중요한 것에 대해 생각할 필요가 있다. 데이터베이스가 과부하 되는 것을 걱정하고 있을 수도 있다. 어쩌면 눌 포인터가 코드를 망가뜨리는 방법에 대해 걱정하고 있을지도 모른다. 사실 코드의 거의 모든 부분이 빈칸보다 더 중요하다. 참견하고 군림하기를 좋아하는 표준위원회가 이러한 빈칸이나 탭의 배치에 관한 규칙을 제정했다고 해도 말이다.

놀라운 것은 잘 정의된 린팅 규칙(linting rule)을 준수하도록 당신의 코드를 자동으로 다시 포맷해주는 몇 가지 좋은 도구들이 있다는 것이다. 사람이 이것에 대해 시간을 쓸 필요를 없애준다. 만약 그것이 그렇게 중요하다면, 도구를 실행시켜서 문제를 해결하면 된다.

나쁜 프로그래밍 습관 No. 4 : ‘고투’(goto) 사용
고투(goto)의 사용을 금지하는 관행은 여러 구조화된 프로그래밍 도구들이 존재하기도 전 시대로 거슬러 올라간다. 과거 프로그래머들이 루프를 만들거나 다른 루틴으로 점프하려면, ‘GOTO’를 입력한 다음 줄 번호를 입력해야 했다. 몇 년 후 컴파일러 팀들은 프로그래머들이 줄 번호 대신 스트링 레이블을 사용하도록 허용했다. 그 당시로서는 인기 기능이 새롭게 돌아온 것으로 여겨졌다.

하지만 곧 일부는 이 결과를 ‘스파게티 코드’라고 불렀다. 추후 이러한 코드를 읽고 실행 경로를 따라가기가 불가능했다. 그것은 영원히 뒤엉켜 있는 실타래였다. 에제르 디크스트라(Edsger Dijkstra)는 ‘유해한 것 같은 고투 진술서’(Goto Statement Considered Harmful) 라는 익살맞은 제목의 원고를 통해 그 명령어를 금지했다.

하지만 절대 분기(absolute branching)가 문제인 것은 아니다. 그러한 결과가 나온 것은 얽혀진 타래 때문이다. 교묘한 브레이크나 리턴은 그 위치에서 코드가 하고 있는 것에 대해 매우 분명한 진술을 제공할 수 있다. 때때로 사례 진술(case statement)에 고투를 추가하면 보다 적절하게 구성된 목록의 조건문 블록보다 더 간단하게 이해할 수 있는 것을 만들 수 있다.

반례들도 있다. 애플의 SSL 스택에 있는 ‘고투 실패’ 보안 구멍은 대표적인 사례 중 하나다. 하지만, 사례 설명과 루프의 몇몇 까다로운 문제를 피하려고 조심한다면, 우리는 독자들이 무슨 일이 일어나고 있는지 이해하기 쉽게 해주는, 훌륭하고 절대적인 점프를 삽입할 수 있다. 더 깨끗하고 모두에게(고투를 싫어하는 사람들을 제외) 더 기분 좋은 ‘브레이크’나 ‘리턴’을 넣을 수 있다.

나쁜 프로그래밍 습관 No. 5 : 유형을 선언하지 않기(Not declaring types)
타입드 랭기지(typed languages)를 좋아하는 데에는 이유가 있다. 각 변수의 데이터 유형에 대한 명확한 선언을 추가함으로써 더 좋고 더 버그가 없는 코드를 작성할 수 있다. 유형을 설명하기 위해 잠깐 멈추면, 코드가 실행되기 전에 컴파일러가 멍청한 오류를 표시하도록 하는데 도움이 된다. 고통스러울 수도 있지만 도움이 된다. 그것은 버그를 막는 조심스럽고 안전한 프로그래밍 접근법이다.

그러나 시대가 변했다. 더 최신의 컴파일러들 중 다수는 코드를 보고 유형을 추론할 만큼 똑똑하다. 그들은 변수가 ‘string’이나 ‘int’ 또는 다른 어떤 것이어야 한다는 것을 확신할 수 있을 때까지 코드를 분석할 수 있다. 그리고 이러한 추론된 유형이 정렬되지 않으면 컴파일러는 오류 플래그를 발생시킬 수 있다. 이를 통해 우리는 변수를 더 이상 타이핑할 필요가 없다.

이것은 이제 간단한 선언들을 생략함으로써 몇 개의 비트를 더 쉽게 저장할 수 있다는 것을 의미한다. 코드는 좀 더 깨끗해지며, 독자(the reader)는 보통 루프에 대한 a의 i라는 변수가 정수(integer)라는 것을 짐작할 수 있게 됐다.


나쁜 프로그래밍 습관 No. 6 : 요요 코드(Yo-yo code)
프로그래머들이 ‘요요 코드’라고 부는 것이 있다. 먼저 그 값들은 ‘strings’로 저장된다. 그런 다음 그들은 정수(integers)로 구문 분석된다. 그리고 나서 그들은 다시 스트링으로 변환된다. 그것은 대단히 비효율적일 수 있다. 당신은 모든 추가적인 부하 하에서 CPU의 고군분투를 느낄 수도 있을 것이다. 빠른 코드를 쓰는 똑똑한 프로그래머들은 변환을 최소화하도록 그들의 아키텍처를 설계한다. 그들의 코드는 그들의 계획 때문에 더 빠르게 실행된다.

하지만 믿거나 말거나, 때로는 요요 코드가 합리적이다. 때때로 당신은 독점적인 블랙박스 안에서 수십 억 개의 지능적인 것들을 해내는 훌륭한 라이브러리를 가지고 있다. 때때로 사장은 그 블랙박스 안에 있는 모든 천재들에게 라이선스를 주기 위해 100만 달러가 넘는 수표를 쓰기도 했다. 만약 라이브러리가 스트링으로 된 데이터를 원한다면, 당신은 그것을 최근에 정수로 변환했더라도 스트링으로 라이브러리에 보낸다.

물론, 변환을 최소화하기 위해 당신의 모든 코드를 다시 쓸 수도 있지만, 그렇게 되면 시간이 걸릴 것이다. 코드를 다시 작성하는 데 훨씬 더 많은 시간이 걸릴 수 있기 때문에 때로는 코드가 1분, 1시간, 하루 또는 심지어 1주일 더 추가적으로 실행되는 것도 괜찮다. 그렇다. 때로는 기술 부채를 늘리는 것이 애초에 제대로 구축하는 것보다 더 싸다.

종종 라이브러리가 독점적인 코드가 아닐지언정 오래 전에 당신이 직접 쓴 코드인 경우가 있다. 때때로 데이터를 한 번 더 변환하는 것이 그 라이브러리에 있는 모든 것을 다시 쓰는 것보다 더 빠를 때가 있다. 그렇기 때문에 당신은 계속 진행하여 요요 코드를 쓴다.  그것도 좋다. 우리 모두가 그렇게 해봤었다.

나쁜 프로그래밍 습관 No. 7 : 나만의 데이터 구조 작성(Writing your own data structures)
표준 규칙 중 하나는 데이터 구조 과정을 갓 마친 프로그래머가 데이터 저장을 위한 코드를 작성해서는 안 된다는 것이다. 다른 누군가가 우리가 필요로 할 모든 데이터 구조를 이미 작성했고, 그들의 코드는 수년 동안 테스트되고 다시 테스트됐다. 그것은 언어와 함께 묶여서 아마 무료일 것이다. 당신의 코드에는 버그가 가득할 수 있다. 

그러나 때로는 데이터 구조 라이브러리가 약간 느리기도 한다. 때때로 그들은 우리를 표준적이지만 우리의 코드에 맞지 않는 구조로 강요하기도 한다. 또 그 라이브러리는 우리가 그 구조를 사용하기 전에 우리의 데이터를 재구성하도록 몰아붙이기도 한다. 일부 라이브러리에는 쓰레드 잠금과 같은 기능이 있는 뛰어난 보호들이 포함되곤 하는데, 우리의 코드는 그것들을 필요로 하지 않는다.

그러한 일이 발생하면, 우리만의 데이터 구조를 쓸 때이다. 경우에 따라 훨씬 더 빠르다. 그리고 때로는 데이터를 정확하게 다시 포맷하기 위한 추가 코드를 모두 포함하지 않기 때문에 코드를 훨씬 더 깨끗하게 만든다.

나쁜 프로그래밍 습관 No. 8 : 구식 루프(Old-fashioned loops)
오래 전에, C언어를 만든 어떤 사람은 하나의 간단한 구성으로 모든 추상적 가능성(abstract possibilities )을 요약하기를 원했다. 처음부터 해야 할 일도 있었고, 루프를 통해 매번 해야 할 일도 있었고, 모든 일이 언제 끝났는지 알려주는 방법도 있었다. 당시에는 무한한 가능성을 포착하기 위한 완벽하게 깨끗한 구문처럼 보였다.

그러나 시대가 다소 달라졌다. 문제를 일으킨다고 지적하는 이들이 최근 나타났다. 진행되는 일이 너무 많을 수 있다. 좋은 것에 대한 모든 가능성이 똑같이 나쁠 수도 있다. 그것은 읽기와 공감(grokking)을 훨씬 더 어렵게 만든다. 루프가 없고, 단지 목록에 적용되는 기능과 어떤 데이터에 매핑된 연산 템플릿이 있는 더 기능적인 패러다임을 좋아하는 이들이 나타났다.

루프리스 방식이 더 깨끗할 때가 있는데, 특히 단 하나의 깔끔한 기능과 배열만 있을 때는 더욱 그렇다. 그러나 훨씬 더 많은 것을 할 수 있기 때문에 구식 루프가 훨씬 더 단순할 때가 있다. 예를 들어, 첫 번째 매치를 찾는 것은 그것이 발견되는 즉시 멈출 수 있을 때 더 간단하다.

더욱이, 매핑 기능은 데이터에 대해 수행해야 할 여러 가지 것들이 있을 때 더 엉성한 코딩을 권장한다. 당신이 절대값과 각 숫자의 제곱근을 취하고자 한다고 상상해보라. 가장 빠른 해결책은 데이터에 대해 두 번 루핑을 함으로써 첫 번째 함수를 매핑한 다음 두 번째 함수를 매핑하는 것이다. 

나쁜 프로그래밍 습관 No. 9 : 중간에 루프에서 이탈하기(Breaking out of loops in the middle)
어느 땐가 한 규칙 제정 그룹이 모든 루프에는 ‘불변성(invariant)’이 있어야 한다고 선언했는데, 이것은 루프 전체에 걸쳐 진실된 논리적인 진술을 말하는 것이다. 불변성이 더 이상 사실이 아닐 때, 루프는 끝난다. 

이것은 복잡한 루프를 다루는 좋은 방법이지만, 우리가 루프 중간에 리턴이나 브레이크를 사용하는 것을 못하게 하는 것과 같은 말도 안 되는 금지로 이어진다. 이것은 고투 스테이트먼트를 금지하는 규칙의 일환이다.

이 이론은 훌륭하지만 가끔 좀더 복잡한 코드로 귀결된다. 테스트 통과 엔트리와 관련해 어레이를 스캔하는 간단한 케이스를 살펴보자.

while (i<a.length){
   ...
   if (test(a[i]) then return a[i];
   ...
}


루프 불변성 애호가라면, ‘now Found’라고 부를 것이다. 또 하나의 부울 변수(boolean variable)를 추가하는 것 대신 말이다. 아마 이를 다음과 같이 이용할 것이다.

while ((notFound) && (i<a.length){
   ...
   if (test(a[i])) then notFound=false;
   ...
}


만약 이 부울(Boolean)이 잘 이름지어졌다면, 셀프 도큐멘팅 코드의 탁월한 일부일 수 있다. 모든 이들이 잘 이해할 것이다. 그러나 이는 또한 복잡성을 추가한다. 그리고 또 하나의 로컬 변수를 할당하는 것을 의미하며, 컴파일러가 수정하지 못할 수 있는 레지스터 차단을 의미할 수 있다. 

즉, ‘고투’나 점프가 더 깨끗할 수 있다. 


나쁜 프로그래밍 습관 No. 10 : 연산자와 함수 재정의(Redefining operators and functions)
일부 재미있는 언어들은 개발자가 요소의 값를 재정의하는 것 등의 진정으로 비뚤어진 일들을 할 수 있게 해준다. 예를 들어, 파이썬은 최소한 버전 2.7과 그 이전 버전에서 TRUE=FALSE를 입력할 수 있다. 

이러한 시도가 어떤 종류의 논리 붕괴와 우주의 끝을 만들어 내는 것이 아니다. 그것은 TRUE와 FALSE의 의미를 바꾸는 것에 불과하다. 이와 같은 위험한 게임들을 C 프리 프로세서와 몇몇 다른 언어들에서 할 수 있다. 또 일부 언어들은 플러스 부호와 같은 연산자를 재정의할 수 있게 해준다.

이것은 일종의 편법(stretch)이지만, 소위 상수라고 불리는 것 중 하나 이상을 재정의하는 것이 더 빠를 때에 대비한 포인트들이 큰 코드 블록 안에 있을 수 있다. 

때때로 상사는 코드가 완전히 다른 작업을 하기를 원한다. 이 때 당신은 코드를 살펴보고 모든 사건을 바꿀 수도 있고, 아니면 현실을 재정의할 수도 있다. 그것은 당신을 천재처럼 보이게 할 수 있다. 거대한 라이브러리를 다시 쓰는 대신에, 약간 바꿔 주기만 하면 모두가 만족하는 결과가 나타난다.

여기서 글을 마치는 것이 좋을 것 같다. 영리하고 재미있는 시도일 수 있더라도 이것을 실제 비즈니스에 시도해서는 곤란하다. 솔직히 너무 위험하다. 

* Peter Wayner는 오픈소스 소프트웨어, 자율주행 차량, 개인정보 보호 강화, 디지털 트랜잭션, 스테가노그래피(steganography) 등 다양한 주제에 관한 16권 이상의 책을 저술한 저자다. ciokr@idg.co.kr