소프트웨어 공학

[Design Go] 좋은 코드와 디자인 패턴

좋은 코드?

농부는 좋은 작물을 얻기 위해 노력한다. 책 저자는 좋은 책을 쓰기 위해 노력한다. 프로그래머는 좋은 코드를 짜기 위해 노력한다. 그렇다면 '좋은 코드'라는 것은 대체 무엇일까?

좋은 코드란?

다양한 의견이 있을 수 있다. 일단 성능이 좋을 수록 좋은 코드이다. 블로그 이름처럼 단순하고 깔끔한 코드는 대부분 좋은 코드이다. 이런 맥락으로 가독성이 높은 코드는 좋은 코드이다. 하지만, 혼자서든 여러 사람과 함께든 작업을 이어가다 보면, 유지보수하기 쉬운 코드는 좋은 코드의 필요 조건이라는 것을 알게 된다.

왜 유지보수하기 쉬운 코드인가?

프로젝트를 진행하다 보면 당연히 처음에는 새로운 기능을 추가하는 데에 시간을 많이 쓰게된다. 그렇게 그 프로젝트의 기본적인 요구 기능들이 완성되고 배포까지 완료하면 상황이 변한다. 새로운 기능 추가 외에 시간을 쓰는 경우가 많아진다.

우선 버그를 잡는 경우가 있다. 아무리 철저한 테스트 절차를 걸쳐도 모든 버그를 걸러낼 수는 없다. 몇몇 기능이나 인터페이스를 변경하는 경우도 있다. 배포된 후에야 보이는 것들이 있기 마련이다. 구동 환경의 변화에 따라 레거시가 된 코드를 변경하는 경우도 있다. 프로그램을 업데이트 할 예정이 없다고 해도 구동 환경이 OS 업데이트, api의 변경(흔하지는 않다) 등에 의해 바뀌면 배포한 프로그램이 아예 작동하지 않는 경우도 있기 때문에 결국 코드를 수정해야 할 것이다.

이러한 일련의 작업들들을 통해 프로그램을 유지보수한다. 사실 배포 이후의 기능 추가 역시 수요를 만족시키기 위해 프로그램을 보강하는 것이므로 유지보수라고 볼 수 있다. 즉, 유지보수는 프로그램이 배포될 때부터 수명이 다할 때까지 진행된다.

유지보수하기 쉬운 코드란?

그렇다면 유지보수하기 쉬은 코드란 무엇일까? 이는 위의 '좋은 코드'에서 논의한 조건에서 '좋은 성능'을 제외한 모든 부분이 관련되어 있다. '좋은 성능' 부분은 알고리즘이 깊게 연관되어 있지만 이 글(및 시리즈)에서 다룰 주제는 아니다. 앞으로 이 글에서의 '좋은 코드'라는 단어는 '유지보수하기 쉬운 코드'를 지칭한다.

간단하게 생각하자. 좋은 코드는 코드의 변경이 쉽다. 유지 및 보수 작업을 위해서는 당연히 코드를 변경해야 하기 때문이다. 기능 단위로 보면 유지보수하기 쉬운 코드는 기능의 추가, 변경, 삭제가 쉽다. 원하는 기능을 추가하고 변경하고 싶은 기능을 변경하고, 삭제하고 싶은 기능을 삭제하는 것이 유지보수이다.

객체 지향 프로그래밍은 좋은 코드를 작성하기 위해 나온 개념이다. 객체는 하나의 클래스는 하나의 문제 해결을 위해 묶인 데이터와 메소드의 집합이다. 클래스를 내부는 서로 긴밀하게 연결된 강한 응집력을, 외부는 독자적으로 행동하는 약한 결합력을 갖도록 하는 것이 바람직한 객체지향 설계이자 좋은 코드이다. 그리고 이렇게 유지보수하기 쉬운 코드를 패턴화 시킨 것이 디자인 패턴이다.

디자인 패턴

역사

디자인 패턴의 개념 정립 이전에도 디자인 패턴에 있는 패턴들은 이미 쓰이고 있었다. 다만 이러한 좋은 코드들은 의식적이든 무의식적이든 쓰이기만 했을 뿐 정립되지 않고 곳곳에 산재되어 있었다.

GoF(Group of Four, 에릭 감마, 리처드 헬름, 랄프 존슨, 존 블라디시스 4명을 지칭)는 크리스토퍼 알렉산더가 건축 분야에서 시도한 패턴화 작업, 즉 좋은 관습을 패턴화하는 작업에 영감을 받아 소프트웨어 분야에 적용시키기로 한다. 위의 좋은 코드들을 수집 및 분석한 후 패턴으로 만들어 명명하는 일련의 과정을 반복하며 디자인 패턴이라는 개념을 정립하게 된다. 그리고 이들이 정립한 23개의 패턴에 대해 직접 쓴 'GoF의 디자인 패턴'은 디자인 패턴의 바이블이 되었다.

분류

GoF는 23개의 디자인 패턴을 정의했다. 각 패턴은 GoF의 분류 방식에 따라 생성, 구조, 행동 패턴으로 나눌 수 있다. 필자는 조만간 각각의 패턴에 관한 내용을 작성해 해당 패턴에 링크를 걸 예정이다.

생성 패턴

생성 패턴은 객체를 생성하는 과정을 다룬다.

구조 패턴

구조 패턴은 객체 및 클래스의 합성을 다룬다.

행동 패턴

행동 패턴은 객체 및 클래스의 상호 작용을 다룬다.

  • 해석자
  • 템플릿 메소드
  • 책임 연쇄
  • 명령
  • 중재자
  • 메멘토
  • 감시자
  • 상태
  • 전략
  • 방문자

Desgin Go 시리즈를 시작하며...

해당 시리즈에는 필자가 'GoF의 디자인 패턴'을 통해 배운 내용을 정리해 작성할 것이다. 예제는 (필자의 역량 부족으로😅) 'GoF의 디자인 패턴'의 것을 이용할 것이고 Go언어에 맞춰 변형할 것이다. 추가로, Udemy에 있는 Dmitri Nesteruk의 Design Patterns in Go 강좌 (참고 자료에 있다) 역시 참고할 예정이다.

Go언어는 일반적인 객체지향 언어와는 거리가 있기 때문에 들어맞지 않는 부분이 상당수 있다. 때문에 일반적인 객체지향 언어라면 쓸 수 있는 패턴을 Go언어에서는 쓰기 힘든 경우도 있으니 감안하길 바란다. 또한 필자가 Go언어로 변환하는 과정에서 패턴을 잘못 해석하는 경우가 있을 수 있다. 만약 이러한 오류를 발견한다면 댓글로 지적해주길 바란다.

주의사항

패턴에 집착하지 말자. 소위 패턴병이라고 불리는데 모든 코드를 디자인 패턴에 맞춰 쓰려는 경우가 있다. 디자인 패턴을 사용하면 좋은 코드를 짤 수 있지만 때로는 패턴을 쓰지 않는 것이 좋은 코드인 경우도 있다. 특히 객체의 크기가 작은 경우 확장 가능성이 높다고 해도 당장은 디자인 패턴을 쓸 필요가 없다. 그 코드는 확장할 때 디자인 패턴을 써도 늦지 않다. 항상 좋은 코드가 무엇인지 본질을 생각하자.

참고 자료