프로그램들의 군상

프로젝트마다 각각의 일면은 다를 지 몰라도, 결국 프로그램은 제작자의 손을 떠나 다수의 사용자 혹은 후임자에 의해 유지보수 및 개발된다. 이 말인 즉슨 내가 지금 만든 프로그램이 (당장 쓰고 버리는 스크립트 느낌의 프로그램이 아닌 이상에야 - 심지어 이런 프로그램마저도 시간이 모자라 실제 프로젝트에 투입되는 경우가 잦다.) 언젠가는 내 손을 떠나 누군가에 의해 읽히고, 관리된다는 뜻이다. 여기, 몇 가지 예제를 보자.

B 프로그래머의 이야기

B 프로그래머는 입사한 지 2개월 된 프로그래머이다. 그는 이번에 새로운 기능을 추가하고 싶어서, 기존 클래스를 살펴보던 중 이런 클래스를 발견했다.

public abstract class Policy {
	public boolean isPolicyApplyed(Employee employee){
		...
	}
}

B 프로그래머는 이 코드를 보고 이렇게 생각한다.

‘abstract? 나중에 이 추상 클래스를 상속받는 애들은 같은 코드를 재사용하고 싶었나 보군. 최대한 이 코드를 재사용 해 보자.’

B 프로그래머는 이 클래스가 가지고 있는 메서드를 이용하여 새로운 정책을 만들었고, 이 정책을 정책 리스트에 추가하였다. 그런데 갑자기 에러가 발생하는 것이다! B 프로그래머는 혼란스럽다. ‘이게 무슨 상황이지?’

A 프로그래머의 이야기

A 프로그래머는 interface, abstract에 대한 정확한 구분 없이, interface는 다이아몬드 상속을 하기 위해, abstact는 단일 상속을 위해 존재한다고 알고 있다. A 프로그래머는 이번에 자사 제품에 새로운 정책이 필요하다는 요구사항을 받았다. 그는 이렇게 생각한다.

‘Policy는 여러 자식이 하나의 부모를 상속하니까 abstract를 써야지!’

그리고 Policy Class 를 만든 뒤, 이 class를 상속받아 여러 정책들을 만들었다. 그러다가 문제가 발생했다. 정책 5개 중 3개는 공유하나 2개는 공유하지 않는 메서드가 하나 생기게 된 것이다. A 프로그래머는 이렇게 생각한다.

코드를 깔끔하게 유지하려면 중복을 제거해야 해. 이 메서드를 Policy로 올리자!

A 프로그래머는 이 코드를 Policy로 올린다. 단순히 2개에서 이 public method를 사용하지 않는다면 괜찮을 것이라고 생각하는 것이다. 잘 동작하는 걸 보고 A 프로그래머는 이 일을 Done 처리 해버린다.

어디서 문제가 발생했는가?

A 프로그래머는 OOP의 원칙 중 ‘개방-폐쇄의 원칙’ 과 ‘리스코프 치환 법칙’ 을 무시했다. 한 패러다임을 사용한다는 것의 장점은 코드의 동작을 크게 이해하지 않아도 이름 혹은 구조만 보고도 이 프로그램의 동작 방식을 알 수 있고, 코드를 빠르게 재사용하여 원하는 것을 얻어낼 수 있다는 장점이 있다는 것이다. 하지만 이처럼 패러다임에 맞지도 않으면서, 의도도 남기지 않았다면 이는 뒷 사람(혹은 미래의 자신)에게 고통을 주는 일이 될 것이다.

B 프로그래머의 이야기 - 2

B 프로그래머는 시간이 지나 다른 코드들도 볼 수 있는 시간을 얻었다. 그러다가 이런 코드를 발견했다.

// 응답을 3초 기다려야 함
public int getStaticTime(){
	return 3
} 

B 프로그래머는 자기도 모르게 내뱉는다. ‘XX!!!’ 유추하건데 이 메서드가 있는 클래스에서 무언가를 요청할 때 무슨 이유가 있어서 3초 대기하는 것 같다. 찾아 보니 정작 이 메서드는 아무데서도 사용하고 있지 않는 것 같다. 심지어 3초 대기하지도 않는다.

이거 지워도 되는 거야?

지웠더니 에러는 안 난다. 그런데 불안하다. 욕지거리가 치밀어오른다. B 프로그래머는 앞으로 이 메서드에서 요청이 나갈 때 이 메서드를 사용하기로 결정한다.

A 프로그래머의 이야기 - 2

최근에 버그가 자꾸 발생해서 보니, 다른 곳에 API를 요청해서 받아오는 쪽에서 자꾸 Timeout이 난다. A 프로그래머는 귀찮아서 3초 기다리게 만들었다. 잘 동작한다. 며칠 뒤 Timeout이 발생하지 않을 거 같아서 기능을 지워보았다. 잘 동작한다.

뭐, 3초 기다리는 건 비효율적이긴 하지.

A 프로그래머는 자기가 3초 줄였다는 생각에 찌꺼기 제거를 마저 하지 않고 싱글벙글하게 이슈를 종료한다.

어디서 문제가 발생했는가?

일단, 코드 자체가 의도를 제대로 나타내고 있지 않다. 왜 기다려야 하는지? 3초라는 시간을 지정해 놓은 이유는 무엇인지? 아무 것도 코드에 나타나 있지 않다. 심지어 필요 없는 메서드를 지우지 않아 후발자가 혼란을 겪게 만들어 버렸다. 필요 없는 코드는 제때제때 지워야 한다. 특히 앞서 말한 예시같은 경우 정적 언어가 아닌 동적 언어 (Python, Ruby) 등에서 더욱 잘 발생하는 현상인데, 이 언어들은 저 메서드들을 덕타이핑으로 불러 올 경우 IDE도 모르는 참조가 생길 수 있기 때문이다. 이런 언어 사용자는 특히나 의도를 잘 남겨야 한다.

마치며

쓰기 전에는 뭔가 아이디어가 많았는데, 정작 쓰고 나니 몇 가지 생각이 안 난다. 다음에는 아이디어가 떠오를 때마다 메모를 잘 해둬야 겠다. 중요한 건 의도이다. 의도를 남기고, 깔끔한 코드가 중복이 없는 코드만을 의미하는 것은 아니라는 것을 잘 알아야 한다. 코드는 자기가 고민한 만큼 읽기 쉬워진다.