블로그를 옮겼습니다: [디자이너(기획자)가 체화된 인지 관점에서 배울 점 읽기]

저작자 표시
신고

Object Oriented Programmer's Productivity 를 읽고 씁니다. 글에서 주장하는 바를 간략히 요약하자면 이렇습니다:

  1. OOP 하면 동적 디스패치(dynamic dispatch, 대충 쉽게 말해서 다형성)가 가장 먼저 떠오르는데,
  2. 동적 디스패치는 코드를 읽기 어렵게 만들기 때문에 위험하고, OOP는 이를 장려하기 때문에 결국 문제가 된다.
  3. OOP의 장점은 다형성에 있다기 보다는 캡슐화에 있는데, 그 이유는 구조적 프로그래밍에 비해 전역 변수를 덜 쓰도록 장려하기 때문이다.

1)번은 동의하는 바이고(OOP란 조건문을 줄이는 것 참고), 2,3번에는 동의하지 않습니다. 하나씩 따져보면


1. "동적 디스패치는 코드를 읽기 어렵게 만든다"는 주장에 대해

이 문제는 정적인 프로그램(즉 소스 코드)과 동적인 프로세스(dynamic process) 사이의 간극이 넓어지면 프로그램을 분석하기가 어려워진다는 문제의 한 가지 사례인데요(혹시 부연 설명이 필요하시면 Goto 문과 AOP, 그리고 Subtext 참고), 간단히 말해서 코드를 읽다 말고 실제로 무엇이 실행되는지 파볼(drill-down) 필요가 생기지만 않는다면 문제될 것이 없습니다. 그러니깐, 설계를 잘 하면 된다는 말입니다.

이 맥락에서 올바른 설계란... 대충 중요한 것을 꼽자면 첫째, 일반화(generalization)가 올바르게 되어 있고(즉, LSP 혹은 contract - design by contract에서 말하는 - 를 잘 지키고 있고), 둘째, 클래스 및 인스턴스의 이름이 적절히 지어져 있으며(intention revealing), 3) 해당 코드의 주변부와 추상화의 수준(level of abstraction)에 일관성이 있는 것을 말합니다.

이런 상황이라면 특정 오퍼레이션(operation)에 대한 구현 코드(method)의 내용이 무엇인지 궁금해할 일이 없습니다.

사실 동적 디스패치가 코드 읽기를 어렵게 만든다는 식의 문제 제기라면 글쓴이가 좋아하는 Haskell(함수형 언어의 일종입니다)도 문제가 되는데요, 왜냐하면 higher-order programming이라는 것 자체가 함수를 인자로 넘기거나 함수를 반환값으로 받아서 쓰는 것이고 이렇게 되면 결국 늘상 일어나는 일이 동적 디스패치거든요.


2. "OOP의 장점은 다형성에 있다기 보다는 캡슐화에 있는데, 그 이유는 구조적 프로그래밍에 비해 전역 변수를 덜 쓰도록 장려하기 때문이다"는 주장에 대해

다형성은 문제이고 캡슐화가 진정한 장점이라는 얘긴데 그럴거면 OOP라는 것이 있을 이유가 없고 그냥 모듈화 프로그래밍(modular programming)이라는 말이면 충분하겠죠.

캡슐화라는 것은 구조적 프로그래밍이나 객체지향 프로그래밍과 직교적(orthogonal)인 개념으로 보는 것이 자연스럽습니다. 대부분의 구조적 언어는 모듈화 프로그래밍을 지원하고 있고, 객체지향 언어 또한 마찬가지라서 프로그래머가 어떻게 잘 쓰느냐에 따라 캡슐화가 잘 될 수도 있고 아닐 수도 있습니다.

이를테면 코드와 데이터를 묶으려면 구조체에 함수 포인터 넣어두고, 각 함수는 첫번째 인자로 자신이 속한 구조체를 받으면(python의 self와 유사) 되는 것이죠. 구조체가 없거나 함수 포인터가 없는 언어(구조적 언어의 요건은 모두 갖추었으나 함수 포인터가 없는 언어로는... 이를테면 QBasic이나 QuickBasic이 그렇습니다)라면 prefix나 postfix로 그룹핑을 하는 관습을 만들면 그만입니다(실제로 널리 쓰이던 관습입니다. namespace 개념이 없거나 약했거든요).

뭐 구현 가능성에 대해서는 그렇다치고, 구조적 프로그래밍에 비해 객체지향 프로그래밍이 캡슐화를 좀 더 강조하고 있다는 주장도 있는데 이 또한 별 설득력이 없습니다(시스템을 모듈로 나누는 기준에 대하여 참고. 추측컨데 저자가 구조적 프로그래밍을 충분히 경험해보지 못한 것이 아닌가 싶습니다).

저자는 캡슐화의 단적인 예로 전역 변수(global variable) 문제를 들고 있는데 이에 대해서는 의존의 자기유사성을 참고하시면 좋겠습니다. 전역 변수 문제라는 것은 사실 스케일의 차이만 있을 뿐 일반적인 의존성 문제의 하나이고, 객체지향 프로그래밍을 하건 구조적 프로그래밍을 하건, 전역 변수가 있건 없건 항상 존재하는 문제이고 신경 써야 하는 문제입니다.

게다가 자바처럼 전역 변수라는 개념이 아예 없는 언어에서도 전역 변수 문제는 여전히 존재할 수 있고(전역 변수가 없지만 전역 변수 문제는 존재한다는 표현이 좀 이상하지만 뭐 맞는 말입니다) 실제로 많은 개발자들이 이 문제로 허덕이고 있습니다. 이에 대해서는 모드 없는 소스코드 중 "모드와 숨은 변수들" 섹션 참고하시기 바랍니다.

OOP 덕에 전역 변수가 많이 줄었다는 것은 아쉽게도 착각입니다. 일례로 Singleton Pattern을 보세요. 이건 사용이 장려되고 있는 (디자인 패턴이라는 탈을 쓰고 나타난) 전역 변수 아닌가요?


3. 결론 및 부연

첫째, 동적 디스패치가 일어나는 부분이 읽기 어렵다면 그건 설계/코딩을 잘못했기 때문입니다. 아무리 좋은 도구라도 쓰는 사람이 잘 쓰지 못하면 문제가 생기는거죠.

둘째, 캡슐화라는 것은 그걸 ADT(Abstract Data Type)라고 표현했건, 모듈이라고 표현했건 간에 객체지향프로그래밍과 무관하게 예전부터 있어 왔던 개념과 별 다른 점이 없습니다. 애초에 모든 (쓸모있는) 프로그래밍 언어는 조합(combination)과 추상화(abstraction) 요소를 갖추고 있는데(Structure and Interpretation of Computer Programs), 객체지향언어도 예외는 아닌 것이죠.

저작자 표시
신고

요즘 존경하는 Kent Beck 횽아가 삼강협회(Three River Institute)좋은 글을 많이 올려주시는군요. Toby님의 감상평도 재미있고요. 참 많은 생각거리를 던져 주는 좋은 글들입니다. 이런 글들을 읽다보면 (내공은 짧지만) 저도 뭔가 쓰고 싶고 그렇단 말이죠.

하지만 돌이켜보면 한 달 평균 코딩 시간이 5시간도 되지 않은 채로 한 반 년을 보낸 것 같습니다. 하물며 의도적 수련 10,000시간은 요원하기만 한 얘기.

코딩은 안하고 말로만 주절거리기를 오래 하면 이런 취급을 받기 딱 좋죠:

 

뭐… 물론 Ward Cunningham 쯤 되는 초고수라면 다를 겁니다:

 

하지만 저 같은 범인은 아무래도 이 상태로 계속 반 년만 더 지나면 코딩을 못하게 될 것만 같아요. 취미로라도 뭔가 코딩을 계속 해야할 것 같습니다:


네, 감을 읽어버리지 않도록 말이죠.

하여간, 그래서… 좋은 글을 소개는 하면서, 제 생각을 굳이 떠들지는 않으면서, 포스팅 길이도 짧지 않게 유지하면서, 제가 좋아하는 배가본드도 소개하면서, 끝.

우왕ㅋ

(그나저나, JUnit Max라는 것 상당히 탐나는군요. 예전에 Contributing to Eclipse에서 만들다만거, 드디어 완성시킨 것인가요? ㅎㅎ)

신고

예전부터 고민하던 문제인데 자꾸 미루게 되어서 일단 질러놓고 봅니다. 연재 형식으로 쓰려고 하며, 예상 독자는 게임 기획자나 웹 기획자 혹은 기획에 관심있는 개발자 입니다. 주제는 아래와 같으며 순서나 일정은 미리 정하지 않았습니다.

  • 복잡한 논리의 흐름을 몇 가지 기본적인 단위로 분해하는 방법
  • 거대한 개념을 작은 단위로 나누는 다양한 방법
  • 구체화되지 않은(혹은 의도적으로 구체화하지 않은) 매우 추상적인 개념을 효과적으로 다루는 방법
  • 서로 다른 여러 개념 사이의 공통점을 찾아내는 방법
  • 생각을 정리하기 위한 정보 시각화 방법
  • 효과적인 의사소통을 위한 정보 시각화 방법
  • 특정한 기획 요소의 변화가 다른 기획 요소에 미치는 영향을 추적하고 관리하는 방법

거의 모든 주제가 “논리적이고 체계적인 사고”에 대한 것입니다. 따라서 독자께서 이런 생각을 하실 수도 있습니다:

기획은 논리만으로 되는 것이 아니다. 기획자는 사용자의 이성적 측면 뿐 아니라 감성적 측면도 고려해야 한다.

이러한 의문에 대해서는 두 가지 생각을 가지고 있습니다.

첫째, 기획의 모든 면을 다루는 것은 이 연재의 목적이 아닙니다. 그러기에는 제 지식이나 경험이 턱없이 부족합니다.

둘째, 감성과 논리는 대치되는 개념이 아닙니다. 논리라고 하면 보통은 ‘이성’에 대한 것으로 한정지어 생각하는 경향이 있는데 감성이나 도덕에도 논리가 있습니다. 감성적 측면을 다루는 능력이 진정 중요하다고 생각한다면 인간의 감성을 지배하는 논리를 파악하고 이를 체계적으로 다루는 기술을 연마해야 한다고 생각합니다. 이 중 감성의 논리를 파악하기 위해 필요한 내용(심리학, 사회학, 생물학, 미학 등)은 아마도 이번 연재의 범위에 포함되지 않을 것이고, 파악된 논리를 체계적으로 분석하고 활용하기 위해 필요한 내용을 주로 다룬다고 보면 될 것 같습니다.

이런 종류의 글을 쓰기에 더 적합할 것 같은 분들이 주위에 좀 계셔서 부담스럽지만 스스로 공부도 할겸 제가 먼저 깝쭉 나대보려고 합니다. ㅋ

앞으로 연재가 진행되는 동안 댓글로 의견/제안/질문 등을 남겨주시면 큰 도움이 되겠습니다. :-)

신고

설계가 완벽할 수 없는 이유를 읽고 씁니다.

저는 윗 글에 동의합니다. 설계는 완벽할 수 없습니다(사실 명제). 그리고 저는 설계를 완벽하게 하는 것이 가능하다고 하더라도 그렇게 해서는 안된다고 생각합니다(가치 명제).


1. 설계가 완벽할 수 없는 이유

소프트웨어는 소프트한가?에서도 인용한 적이 있고, 윗 글에도 나와있지만 짧게 요약하자면 세상이 바뀌거나, 내가 더 똑똑해지기 때문에 완벽한 설계란 좀처럼 얻어내기가 힘듭니다.

하지만 가정을 해봅시다. 세상(and/or 요구사항)이 바뀔 일도 없고, 나는 이미 천재이고 모든 도메인 지식을 다 가지고 있기 때문에 더이상 똑똑해질 일도 없다고 칩시다. 그렇다면 설계가 완벽해질 수 있을 것입니다. 그런데, 설계를 완벽하게 하고나서 코딩을 시작하는 것이 과연 좋을까요?


2. 설계가 완벽하지 않아야 하는 이유

2.1. 완벽한 설계의 가치

완벽한 설계(design)라는 가정 이면에는 완벽한 분석(analysis)이 전제되어 있습니다. 하지만 설계는 설계일 뿐 작동하지 않습니다(Executable UML 같은 시도도 있지만 뻘짓으로 간주하겠습니다). 그리고 작동하지 않는 소프트웨어는 고객에게 아무런 가치를 전달하지 못합니다.

즉, 완벽한 분석에 이어 완벽한 설계를 하느라 프로젝트 기간의 (이를테면) 절반 정도가 지나도 프로젝트는 아무런 가치를 창출하지 못합니다. 그 이후에 구현 조금, 테스트 조금, 배포 조금 하는 방식을 따른다면 이 때부터 가치가 창출되기 시작하겠지만 선행 분석/설계를 하는 프로젝트의 대부분은 끝까지 그 스타일을 유지하죠. 완벽한 설계 이후에는 완벽한 구현과 완벽한 테스트 단계가 오고, 그제야 완벽한 배포를 하는 소위 완벽한 폭포수(waterfall) 프로세스가 완성됩니다. 결국 프로젝트가 완전히 끝나야 가치가 전달되기 시작합니다.

2.2. Self-funding point와 BEP

애자일 방법론에서는 사용자에게 가치를 주는 작은 기능들을 단위로 하여 약간의 분석/설계/개발/테스트/배포를 짧은 주기로 반복합니다. 따라서 프로젝트 초창기부터 작동하는 소프트웨어를 고객에게 전달할 수 있게 됩니다. 다음은 폭포수 방식과 애자일 방식의 차이를 극명하게 보여주는 그래프입니다:


출처: The Origin of Value

참고로, Self-funding point란 프로젝트에서 발생하는 수익이 프로젝트를 지속하기 위한 비용과 일치하는 지점을 말합니다. 이 지점을 지나면 프로젝트는 소위 "밥벌이"를 한다고 볼 수 있습니다. 한편, 프로젝트에 현재 투입되고 있는 비용을 지속적으로 자체 충당(self-funding)하면서, 그 이전에 투입된 비용까지 다 갚아버리는 지점을 BEP - 손익분기점 - 라고 합니다.

빨간 선은 프로젝트에 투입되는 비용입니다. 위 그래프에서는 애자일이나 폭포수나 투입되는 비용이 동일한 것으로 간주하고 있습니다. 애자일 방법론을 실천하는 사람들은 애자일 방법론이 더 저비용일 것이라고 주장할 것이고, 폭포수 방법론을 실천하는 사람들은 폭포수 방법론이 더 저비용일 것이라고 주장할 것인데, 이 부분에서는 일단 중립을 지켜보도록 하죠.

녹색선은 애자일 프로젝트가 만들어내는 가치입니다. 짧은 이터레이션을 돌면서 잦은 릴리즈를 하기 때문에 프로젝트 초반부터 가치를 생산하기 시작합니다. 파란선은 폭포수 프로젝트가 만들어내는 가치입니다. 앞에서 설명한 이유로 인해 프로젝트 막바지가 되어야 가치가 생산되기 시작합니다.

녹색선은 애자일 방법론에 유리한 방식으로 약간 편향되어 있는데, 좀 더 공평하게 하자면 기울기가 초기에는 낮다가 점점 높아지는 꼴을 취해야 합니다. 왜냐하면 초반에는 기능의 수가 적기 때문이죠. 그래프를 그런 식으로 수정하고 나면 애자일 프로젝트의 self-funding point가 조금 더 늦춰지겠지만 여전히 폭포수 프로젝트에 비해서는 빠르겠죠. 그리고 이 상태가 유지된다면 애자일 프로젝트가 폭포수 프로젝트에 비해 훨씬 이른 시기에 BEP(break-even point. 손익분기점)에 도달합니다.

추가: 위 그래프에서 애자일 방법론의 self-funding point라고 한 부분은 사실 BEP로 표시되어야 하는데 잘못 표시한 것이 아닐까 싶습니다. self-funding point는 녹색선의 기울기가 빨간선의 기울기와 같아지는 지점이어야겠죠. 이 그림보다 예쁜 그림이 Software by Numbers 본문에 나오죠:

이 그림에서는 기울기의 변화와 면적을 보시면 됩니다. 초기 투자 기간(Investment period)의 기울기가 서서히 변하고 있는 까닭은 초기부터 가치를 전달하고 있기 때문입니다. 하지만 아직은 비용이 더 크기 때문에 전체적인 가치(면적)는 마이너스 입니다. 하지만 특정 지점(self-funding point)에 이르르면 프로젝트에 투입되는 비용과 프로젝트가 창출하는 가치가 같아집니다(기울기가 0). 그 이후엔 투입되는 비용은 고정된 반면 창출되는 가치는 점점 늘기 때문에(지속적인 릴리즈) 기울기가 점점 높아집니다. 이 기간동안 초기에 투입된 비용을 갚아나갑니다(payback period). 이 상태가 지속되면 어느 순간 손익분기점(break-even time)에 도달하고 그 이후부터는 수익이 발생하기 시작합니다.


2.3. 프로젝트 중단

프로젝트가 잘 굴러가고 있다가도 갑자기 중단되는 경우가 있습니다. 일선 관리자들이나 개발자 같은 실무자들은 대체 왜 그런 결정이 내려졌는지 자세히 알지 못하는 경우가 많지만 어쨌건 빈번하게 발생하는 일입니다. 위 그래프에서 x축으로 10 정도 되는 지점에서 프로젝트가 중단되었다고 칩시다.

그래프 상으로 애자일 프로젝트는 이미 BEP를 지나 수익을 발생시키고 있지만 폭포수 프로젝트는 아직 투자 단계입니다. 사실 재무/회계 관점에서 소프트웨어 프로젝트가 거대한 블랙박스로 인식되지 않고 릴리즈 단위로 인식될 수 있다면 애자일 프로젝트는 self-funding point 이후엔 어지간해서는 중단될 이유가 없어집니다(재무/회계 관점에서 당장 그렇게 인식되지 않는다 하더라도 적어도 방어 논리를 약간은 만들어낼 수 있을겁니다).

여기서 강조하고 싶은 이야기는, 재무/회계 관점에서 소프트웨어 프로젝트가 좀 더 정밀하게 분석되면 될수록 폭포수 방식의 문제가 더 크게 부각될 것이라는 점입니다.

2.4. 재고와 도요타 생산 방식

과잉 재고는 나쁜 것입니다. 1) 재고를 최소화하고, 2) 공정 상의 흐름이 끊기지 않게 하고, 3) Push 방식이 아닌 Pull 방식을 채택하면 일단 표면적인 도요타 생산 방식이 만들어집니다. 이 중에서 이 논의와 특히 관련이 깊은 것은 앞의 두 가지 입니다. 소프트웨어에서의 재고란 노력이 투입되었으나 아직 최종 사용자의 가치와 연결되지 않은 모든 것을 말합니다. 완벽한 설계는 전형적인 재고입니다. 둘째, 공정 상의 흐름이 끊어지지 않으려면 각 단계에서 작은 단위로 끊임없이 가치가 흘러야 합니다. 앞 단계에서 거대한 작업이 끝날 동안 뒷 단계에서 손 놓고 대기하고 있으면 흐름이 끊어지는 것입니다. 완벽한 설계는 흐름을 끊기게 하는 주요 원인입니다. 완벽한 분석, 완벽한 구현,완벽한 테스트도 마찬가지 입니다.

(얼마 전에 프로젝트 시작부터 개발자가 바글바글이라는 글을 읽었는데, 프로젝트 시작부터 개발자가 바글바글한 것이 문제인 이유는 공정 상의 흐름이 끊기기 때문입니다. 즉, 앞의 공정(완벽한 분석과 설계)이 끝나지 않으면 개발자가 놀아야 하는 것이 문제인거죠. 이에 대한 해법은 두 가지입니다. 하나는, 처음엔 개발자 없이 시작해서 프로젝트 중간에 개발자를 투입하는 것, 또 하나는 공정이 끊기지 않게 해서 초반부터 개발자가 일을 할 수 있게 하는 것.

하지만, 전자의 방식대로 하려면 조직 구조가 인력 풀 방식 혹은 직군별 팀 방식으로 운영되어야 하고 각 프로젝트의 특정 단계에 소위 "투입"되거나 "파견" 나갔다가 해당 단계가 끝나면 "철수"하는 식으로 일을 해야 합니다.

이러한 조직 구성이 효과적인 면도 분명 있겠지만 저는 별로 인간적인(humane) 방식이 아니라고 생각하고, 인간은 인간적인 방식으로 일해야 즐겁고 효과적으로 일할 수 있을 것이라고 믿습니다. 이 문제는 다음 기회에 더 쓰도록 하겠습니다. Ray님 블로그의 글은 잘 읽고 있습니다만 워낙 서로의 경험이 다르기 때문인지 의견 차이가 큰 것 같습니다. 그래서 더 열심히 읽고 이해하려고 노력하고 있습니다.)

이 밖에 "The Goal"의 제약이론도 관점이 약간 다르지만 이와 유사한 내용을 담고 있죠. 이에 대해서는 도요타 방식과 제약이론이라는 글을 참고해주세요.


3. 결론

이러한 이유로 저는 완벽한 설계가 가능하다고 하더라도 완벽한 설계를 하지 않는 것이 더 좋다고 생각합니다.


저작자 표시
신고
신고
< Newer     Older >

티스토리 툴바