본문 바로가기

Better SW Development

불신풍조 만연 프로그래밍 vs 너님 잘못임 프로그래밍

얼마전에 있어던 모 서비스의 장애에 대한 이야기입니다.

어느날 갑자기 svn 서버가 정상적으로 동작하지 않는 사건이 발생했습니다. 현상을 조사해 봤더니 서버 인증파일을 읽어들일때 파싱(parsing)이 실패해서 계정 정보를 읽어들어야 하는 SVN이 정상동작하지 않는 것이었습니다. 

인증파일을 조사해 봤지만 처음엔 별다른 이상한 점이 보이지 않았습니다. 그런데 가만 살펴보니 파싱 문제가 생긴 부분 윗줄에 적혀있는 계정 ID의 맨 앞에 공백이 하나 살짝 붙어 있는 걸 발견했습니다.

'이것때문이었나?'

vi로 계정파일을 직접 열어서 ID앞에 붙어있는 공백을 지워봤습니다.

- I'll FIX IT!!


그러자 서비스가 다시 정상적으로 동작하기 시작했습니다.

'아! 이런! 이거였군!'

첫 글자가 공백인 unix id가 존재한다는 건 말이 안되고, 더더군다나 계정파일에 들어가는, 발생할 수 없는 (발생해선 안되는?) 상황이 발생한 것이었습니다.

그래서 당연한 수순으로 SVN에서 사용하는 계정 정보의 생성시점에 대해 알아보았습니다. 그 결과 계정정보는 해당 서비스에서 직접 만드는 것이 아니고, 연계서비스에서 일정 시간마다 배치로 넘겨받아서 파일에 작성하는 식이라는 걸 알게 되었습니다. 

그러는 사이 또 얼마간의 시간이 지났고, 다시 서비스가 다시 동작하지 않게 되었습니다. 파일을 열어보니 문제를 일으킨 ID에 다시 공백이 붙어 있는걸 볼 수 있었습니다. 계정정보를 새로 넘겨받아서 인증 파일이 다시 갱신되어버린 겁니다.

'다시 배치가 돌았군. cron job을 살펴봐서 주기가 얼마인지 확인해보자'

15 * * * * batchxxx


15분 간격(Every 15 Minutes)

'이제부턴 앞으로 15분 간격으로 파일을 열어서 공백을 계속 없애야겠군.... 이 아니라 이런식이라면 앞으로 15분 이내에 대책을 세우지 않으면 또 서비스가 멈춰버리고 말아!!'


...

우선은 옆자리 대리님을 시켜서 코드를 임시로 고치게 만들고 다시 고민에 들어갔습니다.

"서.. 설마, 연계 쪽 서비스에서 가입시에 아이디 앞에 공백을 붙여 가입할 수 있었던 건가?"

만약 정보를 저희쪽으로 넘겨준 쪽에서 서비스 가입시에 정말 그런 일이 일어난다면, 그건 예사로운 문제가 아니었습니다.

"음.. 하지만, 예전엔 없던 현상인데 갑자기 왜 이런일이? 최근 그 쪽(?)에서 뭔가 코드를 변경하면서 이런 일이 발생한걸까?"

연락을 해서 그 쪽(.. 자꾸 그 쪽이라고 쓰려니 어색+귀찮군요. ... 여튼)에 실제 DB에 저장된 정보를 확인해 달라고 요청했습니다. 공백이 들어간 ID가 있는지 여부를 말입니다.

확인결과는 "공백이 ID 앞에 들어가 있는 ID는 없음. 그것뿐 아니라 중간이나 뒤에 들어가 있는 경우도 없음. 즉, 우리쪽 문제는 아님"으로 연락을 받았습니다.

하지만 저희쪽은 계정정보 자체를 생성하지 않기 때문에, 곤란한 상황에 빠졌습니다. 


넘겨주는 쪽과 넘겨 받는 쪽

저희 입장에서는 소위 말하는 spec에 맞춰 넘겨받는 걸 기대했기 때문에 spec에 맞춰있지 않은 데이터를 가정하고 있지가 않았습니다. 그런데 보내주는 쪽에선 '우린 문제없다'인 상황이니...

더더더 코드를 파고 들어가서 살펴보면 원인을 알아낼 수 있었겠지만, 그럴만한 여력이나 인력이 없었기에 쉬운 방법을 택했습니다. 저희쪽 코드를 수정해서 넘겨받은 데이터에 공백이 있을 경우 trim을 하는 것으로 말입니다. 우선 현상적인 문제는 해결되었습니다만, 석연친 않은 엔딩인거죠.

물론, 경험이 많으신 분들이 곧잘 이렇게 말하십니다.

"당연히 이런저런 검사를 했어야지! 스팩? 절대 믿으면 안되아! 넘겨주는 데이터를 어떻게 믿남? 방어형(defensive) 프로그래밍 몰라?"


방어형 프로그래밍

에.. 그러니까... 모르진 않습니다만...

한때 저도 방어형 프로그래밍의 중요성을 이야기 했었고 열심히 터렛코드(turret code)를 작성했었습니다. 하지만 결국 만들어진 코드를 보면 로직은 얼마안되는데 방어 코드들만 잔뜩인 어지러운 모양이 되곤 했었습니다. 시간이 지날수록, 그리고 문제 생길때마다 if 조건이 하나씩 더 붙게 됩니다. 

문득 문득 이런 생각이 들곤 했었습니다. 

"이럴거면 스펙을 왜 만드나? 넘겨받은 데이터를 활용하는 건 넘겨준 쪽 모듈에 대한 신뢰를 기반으로 이루어져야 하는거 아닌가?"

그래서 한 번은 새로운 코드를 작성할 때, 눈 딱 감고 방어형 코드들을 전.혀. 작성하지 않았습니다. 네트워크 자체의 신뢰성 의문에 대한 코드를 제외한 모든 방어코드를 뺐습니다. 처음엔 그래도 별 문제 없는 것 같았는데, 때때로 null 넘어오기 시작하고 공백 들어오고 있어야 하는 필드값 없고~ 이런 저런 문제가 생기더군요. 그리고 야단도 맞았습니다. 프로그램의 기초가 안되어 있다며 말입니다. 사실 지금 생각해보면 안되어 있긴 했었죠. ㅎㅎ

뭐, 여튼 그 상황에서 저의 대응은 이랬습니다.

"고쳐주세요. 값 넘기기 전에 검사해서.. 음..  넘겨주세요."

넘겨주는 모듈을 만든쪽도 사실 100% 과실없음은 아니다 보니 뭐라뭐라 하면서 고쳐줍디다. 그리고 저도

"고치자. 값 넘기기 전에 검사하고 넘기자"

로 응대했습니다.


믿는 사회 밝은 사회

즉, 넘겨주는 모듈을 작성하는 쪽에 그렇게 요청했고, 저도 넘겨주기 전에 발생할 수 있는 예상외 상황에 대한 검사코드를 작성했습니다. 이런식으로 작업을 한 부분이 많은 건 아니었지만 (원체 타격이 커서..-,- 여하튼)  나름 꽤 괜찮았습니다. 코드, 함수, 메소드 시작지점에서 각종 if문이 사라졌고, 로직이 끝난 다음 필요하다 생각되는 정도로만 신뢰코드를 넣었습니다. 대표적으로 trim 코드라던가 null 대신 null object를 넘긴다던가 하는 식으로 말입니다. 

그리고 보통 신뢰코드를 작성하는 것이 방어코드를 작성하는 것 보다 덜 지저분 하고 비용이 덜 듭니다. 왜냐하면 받는 쪽에서는 작성자가 아니면 해당 데이터가 어떤 방식으로 가공되어 넘어오는지 예측하기가 훨씬 힘들기 때문에 이것저것 찔러보듯 다양한 경우를 가정하고 방어하게 됩니다. (열 경찰이 도둑 하나 못 잡는다 격언대로죠)

여튼 작은 방식의 변화로 불신풍조 만세~! 를 벗어나는 상황을 만드는 노력을 했습니다. '믿지 말자. 너님 잘못!'에서 '남탓하지 말자. 내가 잘하자. 넘겨주는 데이터를 믿을 수 있도록 신뢰를 주자!'로 말입니다.

별것 아닐 수 있지만, 이것 또한 저에게 많은 인사이트를 주었습니다. 이건 언어자체가 DbC를 지원하고 안하고의 기술적인 문제가 아니라, 사람과 조직에 대한 신뢰 문제의 하나라는 걸 느끼게 되었습니다. 


역사는 반복된다더라더라더라

여튼 다시 최근의 장애 상황으로 돌아가 보겠습니다.

현재 해당 서비스는 땜빵코드로도 잘 돌아가고 있습니다. 특별히 건드리는 일이 없다면 앞으로 새로운 버그가 생기기 전까지는 문제없이 돌아갈 겁니다. 하지만 이런 히스토리가 있다는 걸 뒤에 오는 사람들은 모르겠죠. (부끄러워서 알려주기도 싫어!) 그리고 공백이 있는지 검사하는 코드와 트림코드를 볼 겁니다. 그리고 그와 비슷한 친구들 수준의 코드들을 볼겁니다. 그리고는 제가 기존 코드 선배님들아!의 코드들을 봤을 때와 같은 대사들을 또 할 겁니다.

"아... 이건 완.전. (쓰)레거시인데~~ 척 보기에도 위.. 위험해 보여. 최대한 건들지 말아야겠어..."

라고 말입니다. 그럴때 부디 혹여라도 Hall of un-fame(이라고 읽고 주석이라고 불리는 곳)에서 저와 제 옆 자리 대리의 이름을 보게 되는 일은 생기지 않길 바랄뿐입니다.


이미지출처http://me2day.net/rainygirl/2012/02/06/pyamvg6-3j9