iloy’s blog

// by iloy 2020.04.11
// 방어적인 프로그래밍

말단 개발자로 15년이 넘는 기간 동안 10개가 넘는 회사를 다니면서 다양한 사람들을 많이 만났다. 나는 이 사람들을 크게 2가지로 구분할 수 있을 것 같다.

개발을 잘하는 사람과 그렇지 못한 사람들.

여기에서 개발을 잘하지 못하는 사람들이 만든 프로그램이 문제를 일으키는 경우의 상당수가 방어적인 프로그래밍과 관련된 부분이었다. 그래서 언젠가 방어적인 프로그래밍에 관한 글을 한 번 써보고 싶었다. 대부분 개발자가 처음 되면 프로그램이 동작하는 로직을 구현하는데 급급하기 마련인데, 어느 정도 실력이 쌓인 이후에는 머리속으로 생각하는 걸 그대로 코드로 옮길 수 있는 수준이 되면서 프로그램의 로직 자체를 구현하는 것에는 크게 문제가 없게 된다. 이 정도 수준에 오면 그때부터는 방어적인 프로그래밍의 수준에서 결과물의 차이가 생긴다고 생각한다. 사실 프로그래밍 이후 시스템 설계 수준에서도 이러한 접근 방식의 차이가 결과물의 차이를 만든다고 생각한다.

방어적인 프로그래밍에 관해서는 이미 좋은 글들이 많이 있다. 위키피디아구글 검색 만 봐도 수많은 글이 있는 걸 알 수 있다.

문제 상황의 대부분은 다음과 같았다.

프로그램/함수에 예상하지 못한 입력이 들어온 경우

이 문제 상황에서 개발을 잘하지 못하는 사람들이 만든 프로그램들은 다음과 같은 경우가 많았다.

여기에서 개발 의도와 다른 동작을 하면서 정상 종료 하는 것이 최악인 이유는 명확하다. 대부분의 경우 프로그램이 정상 종료하면 사용자는 프로그램이 제대로 돌았다고 생각하기 때문에 프로그램에 문제가 있다는 것을 훨씬 나중에 알아채는 경우가 많고 그동안 했던 일이 무의미하게 되기 때문이다. 실제로 옆 팀에서 machine learning 에 사용할 training data 를 생성하는 프로그램이 잘못 동작한다는 사실을 1주일이 지나서야 알아채서 그만큼의 시간을 고스란히 날린 것도 보았다.

팀에서 다른 사람들이 작성한 프로그램에 방어적인 프로그래밍과 관련된 문제가 발생했을 경우에 내가 주로 했던 이야기는 하나였다.

당신이 생각한 정상적인 조건이 아니라면 무조건 오류 메시지를 내면서 바로 프로그램을 종료해라.

사람들이 작성하는 프로그램의 코드를 대부분 논리적인 형태로 보면

if (내가 생각한 정상 조건 1)
  정상 조건 1 처리;
else if (내가 생각한 정상 조건 2)
  정상 조건 2 처리;
else if (내가 생각한 정상 조건 3)
  정상 조건 3 처리;
else if (내가 생각한 정상 조건 4)
  정상 조건 4 처리;
.
.
.
else
  에러 메시지 출력하고 종료

혹은

if (내가 생각한 에러 조건 1)
  에러 조건 1 메시지 출력하고 종료;
else if (내가 생각한 에러 조건 2)
  에러 조건 2 메시지 출력하고 종료;
else if (내가 생각한 에러 조건 3)
  에러 조건 3 메시지 출력하고 종료;
else if (내가 생각한 에러 조건 4)
  에러 조건 4 메시지 출력하고 종료;
.
.
.
else
  프로그램 동작 코드

와 같은 형태를 띄고 있다.

이 두 가지 방식을 차례대로 방식1, 방식2라고 하면, 이 두 개 사이에는 프로그램이 돌아가는 조건을 인식하는데 커다란 차이가 있다.

다른 식으로 표현을 하자면

프로그램을 계속 개발하고 요구 조건이 변화하고 실행 환경이 바뀌는 상황에서 방식1방식2 보다 훨씬 더 에러 처리를 잘할 것이다. 프로그래머가 개발하는 도중에 생각할 수 있는 조건의 범위가 유한한 반면 실제 프로그램이 사용되는 환경에서의 조건의 범위는 거의 무한에 가깝게 넓기 때문에 이런 상황에서 방식1방식2보다 훨씬 에러 처리에 강할 것이라는 것을 예상할 수 있다.

새로운 회사나 팀에 갈 때마다 기존에 만들어 놓은 프로그램의 대부분은 방어적인 프로그래밍이 전혀 되어있지 않은 상태였고 그 프로그램에 계속 기능 추가/개선을 해야하는데 그 때마다 너무나 많은 에러가 터져나와서 개발 진행이 너무 느리거나 아예 되지 않는 상황을 너무 많이 겪었다. 기능이 추가/개선되면서 프로그램이 동작하는 조건이 바뀌게 되는데 방식2 혹은 에러 처리가 전혀 안되어있는 코드에서는 그 조건들을 찾아내고 확인한 다음 수정하는데 시간이 더 많이 들기 때문이다. 사람들이 흔히 이야기하는 기술 부채 라는 단어로 표현되는 것의 한 가지 형태라고 볼 수도 있을 것이다.

방어적인 프로그래밍이 제대로 되어있지 않은 프로그램이 죽는 상황에서 많은 개발자들은 다음과 같은 말을 하더라.

제가 했을 때는 잘 돌던데요.

프로그램의 주요 로직 구현에 마음이 급한 초보적인 개발자 수준에서 벗어나지 못하고 방어적인 프로그래밍의 중요성을 이해하고 실천할만한 수준에 올라오지 못한 사람들이 하는 대표적인 변명이다.

자신이 만든 프로그램이 아주 제한된 좁은 범위의 조건에서만 제대로 동작을 하고 나머지 조건에서는 에러 메시지를 뱉으며 동작하지 않는다고 부끄러워할 개발자가 있을 수도 있을 것 같은데, 이건 전혀 부끄러워할 일이 아니다. 제대로 동작하지 않으면서 동작하는 척 하다가 나중에 들키는 것이 훨씬 부끄러운 일이다! 그리고 당신은 그걸 피해갔다. 이후에 프로그램이 정상 동작하는 조건의 범위를 계속 넓혀가면 된다. 자신이 감당할 수 있는 상황 안에서 하나씩 하나씩 차근차근.

본인의 능력이 보잘것없다는 사실을 겸손하게 받아들이고 조건을 하나씩 하나씩 추가해가면서 조심스럽게 프로그래밍을 하는 것이 한밤중에 프로그램이 죽을까봐 걱정하지 않고 잠을 푹 잘 수 있게 해준다고 믿는다.