타입 클래스 기초

하스켈 타입 클래스(type class)오버로딩을 지원하기 위한 방법입니다. 여기서 오버로딩은 하나의 심볼(함수 혹은 연산자)를 타입에 따라 여러 의미로 사용하는 것을 의미합니다.대표적인 예로 하스켈의 Show 타입 클래스를 살펴보겠습니다. Show 타입 클래스는 show라는 함수를 정의하고 있습니다. show 함수는 인자를 받아 그 인자를 String으로 변환한 결과를 리턴합니다. 아래와 같이 다양한 인자 타입에 대해서 잘 동작하는 것을 확인할 수 있습니다.

> show 1
"1"
> show False
"False"
> show "Hello World"
"\"Hello World\""

IntString으로 변환하는 방법과 BoolString으로 변환하는 방법은 다를 수 밖에 없는데, show는 어떻게 IntBool, String등 여러 타입의 인자를 모두 변환할 수 있는 것일까요? 궁금즘을 해결하기 위해 show의 타입을 살펴보겠습니다.

> :t show
show :: Show a => a -> String

타입을 보면 일반적인 함수와 달리 a -> String 함수 타입 왼쪽에 Show a =>라는 컨텍스트(context)가 추가되어 있는 것을 볼 수 있습니다. 여기서 => 앞에 나오는 Show aa라는 타입이 Show라는 타입 클래스를 구현한 타입이어야 한다는 뜻입니다.

Show 타입 클래스를 구현한다는 것이 어떤 의미인지를 알아보기 위해, Show 타입 클래스가 어떻게 정의되었는지 먼저 살펴보겠습니다. 다음은 Prelude 모듈에 정의된 Show의 정의입니다. (간단한 설명을 위해 showsPrec등 다른 함수들은 생략했습니다.)

class  Show a  where
   show      :: a   -> String

위 정의의 의미는 타입 aShow라는 타입 클래스의 인스턴스가 되기 위해서는a -> String이라는 타입을 가진 show 함수를 제공해야 한다는 뜻입니다. 여기서 Show타입 클래스는 Show 타입 클래스를 구현하는 타입들이 갖춰야 하는 일종의 인터페이스라고 이해할 수 있습니다.

우리가 Int, Bool, String 등의 인자에 대해 show 함수를 호출할 수 있는 이유는 각각의 타입에 대해 Show 타입 클래스 인스턴스가 미리 정의되어 있기 때문입니다. 인스턴스 정의는 다음과 같이 instance 키워드를 사용합니다.

instance Show Int where
    show = ...

instance Show Bool where
    show = ...

instance Show String where
    show = ...

이처럼 show라는 함수는 실제로는 각 타입별로 구현이 다르지만, 우리는 타입별로 구분할 필요 없이 show라는 하나의 심볼만 사용하면 하스켈이 알아서 인자 타입에 맞는 실제show 함수 구현을 찾아서 불러주는 것입니다.

앞서 설명한 것처럼 이렇게 하나의 심볼을 타입에 따라 서로 다른 의미로 사용하는 방법을오버로딩이라고 부르고, 하스켈의 타입 클래스는 오버로딩을 구현하기 위한 방법입니다.

이해를 돕기 위해 직접 타입 클래스를 하나 만들어 보겠습니다. 아래에 정의한 Mergeable타입 클래스는 두 개의 인자를 받아서 하나로 합치는 함수 merge를 정의하고 있습니다.

class Mergeable a where
  merge :: a -> a -> a

ghcimerge 함수의 타입을 확인해보면 아래와 같이 Mergeable a라는 컨텍스트가 함수 타입 왼쪽에 표시되는 것을 볼 수 있습니다.

> :t merge
merge :: Mergeable a => a -> a -> a

이제 merge 함수를 Int 타입과 List 타입에 대해 정의해 보겠습니다.

instance Mergeable Int where
  merge x y = x + y

instance Mergeable [a] where
  merge x y = x ++ y

Int 타입의 merge 함수는 두 인자의 합을 계산하도록 했고, List 타입의 merge 함수는 두 인자를 병합하도록 하였습니다. ghci를 이용해 테스트해보면 아래와 같이 인자 타입에 따라 서로 다른 merge 함수가 호출되는 것을 확인할 수 있습니다.

> merge (1 :: Int) (2 :: Int)
3
> merge [1,2,3] [4,5,6]
[1,2,3,4,5,6]

추가로 Bool 같이 우리가 인스턴스를 정의하지 않은 타입에 대해서는 에러가 나는 것도 볼 수 있습니다.

> merge False True

<interactive>:23:1:
    No instance for (Mergeable Bool) arising from a use of ‘merge’
    In the expression: merge False True
    In an equation for ‘it’: it = merge False True

하스켈의 PreludeShow 외에도 Eq, Ord, Read, Enum, Bounded, Num, Integral,Floating 등 다양한 타입 클래스를 정의하고 있습니다. 이에 대한 설명은 다음 글에서 이어가도록 하겠습니다.

왜 하스켈을 배워야 하는가?

하스켈은 배우기 어려운 언어로 정평이 나있습니다. 순수(pure) 함수지연 연산(lazy evaluation), 하스켈 타입 시스템(System F와 type class 등등), 병렬 처리 등은 그저 시작에 불과합니다.

하스켈을 제대로 쓰기 위해서는 카테고리 이론에서 빌려온 Functor, Applicative Functor, Monad 같은 개념들을 익혀야 합니다. IO뿐만 아니라 대부분의 하스켈 코드가 이런 개념들을 활용해서 작성되어 있기 때문에 하스켈을 제대로 쓰려면 중요 개념들을 제대로 이해하고 있어야 합니다.

Monad 몰라도 IO 코드는 충분히 작성할 수 있고 MaybeEither, List 타입들을 사용할 수 있습니다만, Monad를 모를 거면 굳이 왜 하스켈을 배워야 할까요? 이미 Clojure, F#, Scala 등 대중적인 함수 언어들이 나와 있고, Java 8이나 C#, Swift, ES6에도 함수 언어의 요소가 상당히 반영되어 있는 상황에서 굳이 더 어렵기만 한 하스켈을 배워야 할 이유가 없어 보입니다.

우리가 사용하는 대중적인 프로그래밍 언어들은 어차피 모두 튜링 컴플리트(Turing complete)하기 때문에 계산가능성(computability)에 있어서는 차이가 없습니다. 한 언어가 계산할 수 있는 것을 다른 언어가 계산하지 못하지 않는다는 뜻입니다. 하스켈이 강력하다고 하지만 본질적으로 C++이나 Java가 할 수 없는 일을 할 수 있지는 않습니다.

왜 하스켈을 배워야 하는지 설명하기에 앞서 프로그래밍의 본질이 무엇인지 질문을 던져볼 필요가 있습니다. 프로그래밍은 기본적으로 컴퓨터에 일을 시키는 것입니다. “메모리 x에 있는 값을 레지스터에 로드해서 1을 더하고 다시 메모리 x에 저장해라”도 일종의 프로그램입니다.

하지만 이게 끝은 아닙니다. 우리가 푸는 문제는 메모리에서 값을 꺼내 1을 더한 다음 다시 메모리에 저장하는 수준이 아니라 현실 세계에서 발생하는 훨씬 더 크고 복잡한 문제이기 때문입니다. 그래서 프로그래밍의 본질은 한 번에 풀 수 없는 크고 복잡한 문제를 작은 문제들로 나누어서 해결하고 그렇게 나온 결과물들을 조합하여 다른 문제를 해결하는 것을 말합니다.

여기서 좋은 프로그램의 가장 중요한 특성으로 조합성(composability)이 등장합니다. 우리는 계속해서 크고 복잡한 문제를 풀어야 하고, 또한 비슷하지만 조금은 다른 문제들을 풀어야 합니다. 앞서 만들어 놓은 산출물을 쉽게 조합하여 새로운 문제를 해결할 수 있다면 프로그래머의 생산성은 비약적으로 늘 수 있기 때문입니다.

프로그래밍의 패러다임 변화는 조합성을 끌어올리려는 노력의 연속입니다. goto 문을 사용하던 어셈블리에서 서브루틴, 블록 구조, for/while 루프를 강조하는 구조적 프로그래밍(structured programming)이 나온 이유는 풀어야 하는 문제의 복잡도가 증가하여 프로그램을 조합할 더 좋은 방법이 필요했기 때문입니다.

구조적 프로그래밍이 많은 문제점을 해결했음에도 불구하고 80-90년대 우리가 풀어야 할 문제의 복잡도가 또 다시 가파르게 상승하면서 새롭게 등장한 패러다임이 객체지향 프로그래밍(object-oriented programming)입니다. 클래스나 객체의 개념, 캡슐화(encapsulation), 정보 은닉(information hiding) 등이 나온 이유도 더 복잡해진 문제를 풀기 위해 더 좋은 조합 방법이 필요했기 때문입니다.

21세기에 들어와 함수 언어가 주목 받고 있는 이유는 우리가 풀어야 할 문제가 또 다시 더욱 복잡해지면서 클래스나 객체가 제공했던 수준 이상의 조합성이 필요해졌기 때문입니다. 함수 언어의 중요한 특징인 고차 함수(higher order function), 다형 함수(polymorphic function) 등이 궁극적으로 해결하고자 하는 문제도 결국 조합의 문제입니다.

하스켈을 배워야 하는 이유는 Functor, Bifunctor, Profunctor, Applicative functor, Monoid, Monad, Arrow, Lense, F-algebra, Adjunction 같은 수학적 개념들이 또 다시 복잡해지고 있는 문제를 풀 수 있는 새로운 조합 방법의 단서를 제공하기 때문입니다. 그리고 하스켈은 이런 측면에서 다른 함수 언어와도 비교하기 힘들 정도로 발전해 있습니다.

물론 하스켈을 통해 배우지 않아도 우리는 이미 많은 개념들을 알고 있고 실제로 사용하고 있습니다. ES6의 Promise, C#의 널 전파 연산자(null propagation operator), Python의 리스트 컴프리헨션(list comprehension) 등은 전혀 다른 기능처럼 보이지만, 내부적으로 Monad라는 같은 구조를 가지고 있습니다. 하스켈을 공부하면 이렇게 서로 상이해 보이는 개념들에 존재하는 공통 구조를 쉽게 발견할 수 있습니다.

정리하면, 프로그래밍의 본질은 조합을 얼마나 쉽게 할 수 있느냐에 있습니다. 그리고 하스켈은 프로그램을 조합에 사용할 수 있는 새로운 개념과 도구들을 제공합니다. 하스켈을 배워야 하는 이유는 이런 도구들을 습득하여 앞으로 닥칠 더 크고 복잡한 문제를 해결하는 프로그램을 작성하는 능력을 배양하는 데에 있습니다.

마지막으로 하스켈을 배워야 하는 또 다른 이유는 그 자체로 재미있기 때문입니다. 어렵기 때문에 재미가 없는 것이 아니라 퍼즐을 푸는 것처럼 새로운 것을 알아가고 깨닫는 재미가 있습니다. 특히, 이미 오랜 세월 개발을 해서 매너리즘에 빠지신 분들은 하스켈을 통해서 다시 프로그래밍의 재미를 느껴보시길 권합니다.

권력 중독

파워(power)는 우리가 일상에서 자주 사용하는 단어입니다. 우리말로 힘, 권력, 권한, 능력 등으로 번역되는 파워는 다양한 맥락에서 다양한 뜻으로 사용됩니다. 슈퍼맨이나 스파이더맨 같은 슈퍼 영웅들은 슈퍼 파워를 가지고 세상을 구하고, 개발 팀장은 개발 팀장에게 주어진 권한으로 프로젝트를 수행합니다.

개발 팀장에게 부여된 권한의 유일한 목적은 주어진 프로젝트를 훌륭하게 완수하는 것에 있습니다. 여러 사람이 모여서 같이 일을 하다 보면, 갈등이 생기기 마련이고 대화와 설득에도 불구하고 의견 조율에 실패한다면 팀장은 본인에게 부여된 권한을 이용하여 의사결정을 내리게 됩니다. 물론 팀원들의 의견을 무시하고 매번 팀장의 권한만을 내세우면 팀원들의 반발을 사고 신뢰를 잃게 되므로 이런 부작용을 잘 알고 불가피한 경우에만 선택적으로 사용해야 합니다.

하지만 반지의 제왕에서 절대반지를 얻은 골룸의 예처럼, 일단 작은 권력이라도 맛을 보게 되면 사람이 달라집니다. 팀장이 되면 주변 사람들의 반응이 달라집니다. 팀원 시절과 똑같은 의견을 이야기해도 팀원들이 좀 더 귀담아 듣고 지지해 주는 경우가 많습니다. “내가 팀장이니깐 시키는 대로 해”라는 말을 할 필요도 없습니다. 권위에 약하고 남들과 다른 의견을 제시하는 것을 껄끄러워 하는 우리 조직 문화에서는 알아서 팀장 말을 따라주기 때문입니다. 당연히 팀장은 점점 자기중심적이 됩니다.

일단 권력에 중독되고 나면 생기는 가장 큰 문제는 현실을 있는 그대로 바라보는 현실 감각을 잃는 데 있습니다. 프로젝트에서 발생하는 여러 문제들에 대해 객관적인 진단을 내리고 해결책을 찾는 대신, 본인 상상 속에서 상황을 진단하고 해결하려고 합니다. 물론 일부 팀원들이 문제를 제기하지만 듣기 싫어합니다. 이미 듣기 좋은 말을 듣는 데 익숙해졌기 때문입니다.

또한 일단 팀장이 권력에 의존하기 시작하면 모든 문제를 권력의 문제로 바라보게 됩니다. 팀원들이 지시를 잘 따르지 않으면 이를 본인 권력에 도전하는 것으로 여깁니다. 또한 모든 문제의 원인이 본인에게 주어진 권한이 부족한 탓으로 생각하고 더 많은 권한을 얻기 위해 위만 쳐다보게 됩니다. 이런 팀장은 더 이상 프로젝트 성공이 목적이 아니라 더 많은 권력을 얻는 것이 목적이 됩니다. 이른바 권력 중독입니다.

누구든 권력의 유혹에서 자유로울 수는 없지만, 특별히 더 권력지향적인 사람들이 있습니다. 이런 사람들은 두 가지 모습을 동시에 보입니다. 자신보다 지위가 높은 사람에게는 철저하게 순종하고, 지위가 낮은 사람들을 지배하려고 합니다. 본인의 무능함을 지적 당하면, 자신은 실력이 있는 사람인데 주어진 권한이 부족한 탓이라고 변명합니다. 개발 팀장이 이런 모습을 보이기 시작하면 더 이상 개발 팀장이 아니라 정치 팀장이 됩니다.

일반적으로 권력은 강함의 표현이라고 생각하지만, 인간 심리를 보면 사실 정반대입니다. 권력에 대한 집착은 강함이 아니라 약함에 있습니다. 홀로 세상을 마주할 강인한 자아가 없기 때문에 외부에 의존할 대상을 찾는 것인데 이런 욕구는 권력에 대한 집착으로 나타납니다. 개발 팀장 버전으로 바꿔서 풀어보면, 개발 실력과 프로젝트 관리 능력으로 팀을 이끌 자신이 없기 때문에 권력에 대한 집착이 나옵니다.

개발 팀장은 실력으로 승부하는 자리입니다. 팀원들은 지배해야 할 부하가 아니라 프로젝트를 같이 만들어 나가는 동료들입니다. 절대반지를 끼면 모습이 보이지 않게 되므로 그 힘으로 무엇을 하는지 알 수 없지만, 팀장이 휘두르는 권력은 모든 팀원들이 지켜보고 있습니다. 아주 작은 권력에 집착해 무엇보다 소중한 팀원들의 신뢰를 잃는 팀장이 되지 않기를 바랍니다.

프로세스 개선

권한을 가진 사람의 책무는 쓸모 없는 전통을 제거하고 새로운 전통을 만드는 것이다. 아무 것도 변하지 않는 조직은 일터가 아니라 살아있는 박물관이다.

The responsibility of people in power is to continually eliminate useless traditions and introduce valuable ones. An organization where nothing ever changes is not a workplace but a living museum.

스콧 버쿤(Scott Berkun), The Years Without Pants: WordPress.com and the Future of Work

소프트웨어 개발에는 프로세스가 있습니다. 새로 작성한 코드를 저장소에 반영하기 전에는 반드시 동료 리뷰를 받아야 한다든지, 버그 수정 시에는 반드시 테스트 케이스를 추가해야 한다든지 하는 예가 그렇습니다. 이런 프로세스는 합리적인 이유가 있습니다. 동료 리뷰를 통해 코드의 품질을 높일 수 있고, 테스트 케이스를 추가하여 회귀 버그를 줄일 수 있기 때문입니다.

어떤 프로세스는 이유도 모르고 그저 전통이기 때문에 반복하고 있습니다. 이슈 트래커에서 누구나 확인할 수 있는 버그 수정 진척도를 매일 엑셀 문서로 만들어서 보고해야 한다든지, 코드 리뷰 후에 아무도 읽지 않는 코드 리뷰서를 별도로 작성해서 저장소에 올리는 일 등이 있습니다. 제가 지어낸 낸 사례가 아니라 모 대기업의 실제 사례입니다.

이런 일을 왜 하느냐고 물어보면 돌아오는 대답은 한결 같습니다.

원래 이렇게 한다. 다른 회사들도 다 이렇게 한다. (그리고 위에서 시키는 거니깐 어쩔 수 없다.)

소위 말해 전통입니다. 이유는 잘 모르겠지만, 나는 그렇게 하라고 배웠고 지금까지 해오고 있으니 너도 하라는 겁니다. 하지만 기존에 하던 대로만 해서는 아무런 발전도 할 수 없습니다. 천공카드가 인류가 발명한 최고의 저장매체라고 생각하고 기술의 진보를 멈췄다면, 우리 모두는 종이에 구멍 뚫어가며 프로그래밍하고 디버깅하는 시대에 살고 있을 겁니다.

천공카드

우리가 당연하게 생각하는 전통도 처음 시작은 혁명적인 아이디어였습니다. 일례로, 우린 8시간이 적절한 노동 시간이라고 생각합니다. 그런데 이 8이라는 숫자는 대체 어디서 나온 걸까요? 왜 6시간도 10시간도 아닌 8시간이 되었을까요? 산업 혁명 이후 18세기 후반이 되면 기업들은 이윤을 극대화하기 위해 공장을 일주일 7일, 하루 24시간 풀가동하기 시작합니다. 당연히 노동자들도 이에 맞춰 일을 하게 되었고, 하루 10-16시간씩 일을 하는 게 일반적이었습니다.

당시 영국의 로버트 오웬(Robert Owen)은 이런 방식이 지속 가능하지 않다고 보고 업무 시간을 하루 8시간으로 제한하자는 캠페인을 벌입니다. 그는 “8시간 노동, 8시간 놀이, 8시간 휴식”이라는 슬로건을 내걸었습니다. 하지만 실제로 8시간 근무를 도입한 건 20세기초 포드입니다. 1914년 포드는 근무 시간을 8시간으로 줄이고, 임금을 2배로 높이는 획기적인 조취를 했는데, 놀랍게도 2년 만에 이익이 2배로 늘어납니다. 이 사례는 이후 다른 기업들도 8시간 근무를 도입하는 계기가 되었습니다.

8시간 노동, 8시간 놀이, 8시간 휴식

8시간 근무는 20세기초 당시 공장 노동자들의 생산성을 극대화하기 위해 나온 아이디어입니다. 그런데 100년이 지난 지금 공장 노동자도 아닌 소프트웨어 개발자가 여전히 8시간 근무를 강요 받고 있습니다. 야근과 주말근무로 점철된 국내에서야 8시간도 감지덕지라고 생각할 수 있곘지만, 공장 노동자의 생산성을 극대화하기 위한 노동 시간과 대표적인 지식 노동인 소프트웨어 개발이 같을 수 있을지 의문이 듭니다. 하지만 왜 8시간 일해야 하냐고 물으면 똑같은 답을 들으실 겁니다.

원래 이렇게 한다. 다른 회사들도 다 이렇게 한다.

어떤 전통이든 시작할 때는 혁명적인 아이디어였고, 시간이 흘러 상황과 맥락이 변하면서 더 이상 유효하지 않게 됩니다. 소프트웨어 개발에 사용하는 기술이나 프로세스는 말할 것도 없습니다. 소프트웨어 산업의 짧은 역사를 생각하면 우리가 가지고 있는 전통은 대부분이 길어야 30-40년, 짧게는 5-10년밖에 되지 않은 혁명적인 아이디어들입니다. 그리고 새로운 아이디어로 빠르게 교체되고 있습니다. 이런 역사를 생각해보면 길어야 경력 10년 남짓의 개발자들이 전통을 운운하는 것이야 말로 우스운 일이 아닐 수 없습니다.

프로세스에 정답은 없습니다. 상황과 맥락에 따라 똑같은 프로세스가 좋은 프로세스일 수도 있고, 나쁜 프로세스일 수도 있기 때문입니다. 하지만 변하지 않는 프로세스는 나쁜 프로세스입니다. 상황과 맥락은 항상 변하기 때문입니다. 여러분은 일터로 출근하시나요? 아니면 박물관으로 출근하시나요?

직위, 직함과 소프트웨어 개발 조직

엄밀하게 구분해서 사용하는 사람이 드물긴 하지만 회사에는 직위, 직급, 직책, 직함이 있습니다. 네이버 국어 사전에서 각 단어의 뜻을 인용하면 다음과 같습니다.

  • 직위 (職位)
    1. 직무에 따라 규정되는 사회적ㆍ행정적 위치.
    2. <법률>직무와 직책에 의하여 규정되는 공무원의 위계.
  • 직급 (職級)

    직무의 등급. 일의 종류나 난이도, 책임도 따위가 상당히 비슷한 직위를 한데 묶은 최하위의 구분이다. 동일한 직급에 속하는 직위에 대하여서는 임용 자격, 시험, 보수 따위의 인사 행정에 동일한 취급을 할 수 있다. [비슷한 말] 직계3(職階).

  • 직책 (職責)

    직무상의 책임.

  • 직함 (職銜)

    벼슬이나 직책, 직무 따위의 이름.

우선 직위는 서열이나 순위를 의미합니다. 우리가 흔히 쓰는 사원, 대리, 과장, 차장, 부장이 여기에 해당합니다. 따라서 승진은 직위의 상승을 말합니다. 비슷한 말로 직급이 있는데, 직급은 직위를 좀 더 세부적으로 분류했다고 보면 됩니다. 일반 기업에서는 직위와 직급을 구분해서 사용하는 경우가 드물지만, 공무원 조직에서 말하는 “9급 5호봉”, “7급 4호봉” 등의 용어가 여기에 해당합니다.

직위나 직급과 구분하여 직무상의 책임을 뜻하는 말로 직책이 있습니다. 대표적으로, 팀장은 직위나 직급이 아닌 직책입니다. 조직의 규모와 성격에 따라 차장이 팀장을 할 수도 있고, 부장이 팀장을 할 수도 있으므로 직위, 직급과 직책은 구분됩니다. 하지만 회사에 따라 팀장을 직책이 아닌 직위로 사용하는 경우도 있습니다.

마지막으로 직함은 호칭을 말합니다. 김개똥 부장을 호칭할 때 “김개똥 부장님”이라고 부른다면 부장은 직위인 동시에 직함이 됩니다. 혹은 김개똥 부장을 호칭할 때 “김개똥 팀장님”이라고 부른다면 팀장은 직책인 동시에 직함이 됩니다. 외국계 기업이나 스타트업에서는 직위 대신 이름 뒤에 “님”만 붙이거나 영어 이름을 부르기도 하는데, 이것도 일종의 직함이라고 볼 수 있고, 직함이 없다고 말할 수도 있습니다.

한국, 일본 등 동아시아에는 직함을 존중의 의미를 담은 높임말로 쓰기 때문에 수평적인 의사소통을 가로 막는 요인이라는 비판이 많습니다. 이에 대한 반작용으로 직함을 폐지하거나 하나로 통일하는 회사들도 들고 있는데, 특히 협업과 의사 소통을 중시하는 소프트웨어 개발 회사에서는 직위를 직함으로 사용하지 않는 문화를 만들려고 시도하고 있습니다. 국내 대기업의 예로, SK플래닛은 모든 직함을 “매니저”로 통일하였고, 팀장 직책에 대해서만 팀장이라는 직함을 사용하고 있습니다.

물론, 직함을 매니저로 통일하였다고 해서 직위나 직급 자체가 없어진 것은 아닙니다. 인사 평가에 사용하기 위한 직위/직급은 여전히 남아 있습니다. 다만, 직위를 직함으로 사용하지 않게 되면 다른 사람의 직위를 알기가 어려워지는 효과가 있습니다. 이 부분은 회사의 문화에 따라 큰 차이가 있습니다. 모두가 다른 사람의 직위를 공공연하게 알지만 단순히 호칭만 하지 않을 수도 있고, 연봉과 마찬가지로 다른 사람의 직위 자체를 알기 어렵게 만들 수도 있습니다.

배경 설명은 마쳤고, 이제 핵심 질문을 던지겠습니다. 직함의 폐지(직위를 직함으로 사용하지 않는 것)가 소프트웨어 개발 조직에 긍정적인 영향을 미칠까요?

YES라고 답하는 사람들의 이야기를 들어보면 주로 외국 기업과 비교합니다. 구글, 페이스북, 마이크로소프트 등 중요 소프트웨어 기업들 중에 우리처럼 직위를 직함을 사용하는 곳이 없으므로, 소프트웨어를 잘 만들기 위해서는 직함을 폐지하는 것이 옳다는 주장입니다. 하지만 미국을 위시한 서양권 국가는 직함 문화 자체가 없고, 소프트웨어 기업뿐만 아니라 모든 기업이 마찬가지이므로 이렇게 단순 비교하는 것은 옳지 않습니다.

하지만 여전히 YES라고 답해야 할 것 같습니다. 많은 사람들이 소프트웨어 개발을 잘하기 위해서는 수평적인 조직 문화를 만드는 것이 중요하다고 생각하고 있기 때문입니다. 저도 동의합니다. 문제는 단순히 직함만 폐지한다고 해서 수평적인 조직 문화가 거저 생기는 게 아니라는 점입니다. 직함을 폐지하면 수평적인 의사소통이 될 거라고 생각하는 것은 인과관계를 뒤집은 것입니다. 직함으로 인해 수직적인 조직 문화가 생긴 것이 아니라, 우리사회를 지배하는 수직적인 조직 문화를 반영한 수많은 결과 중에 하나가 직함이기 때문입니다.

소프트웨어 개발팀만 놓고 보면 사원과 대리, 대리와 과장 사이에 위계 질서가 생길 이유가 전혀 없습니다. 사실 프로젝트를 진행함에 있어 직위 자체가 주는 고유의 권한이라는 게 없습니다. 권한에는 크게 두 가지 종류가 있습니다. 위에서 내려오는 권한과 스스로 획득하는 권한입니다. 팀의 경우 위에서 내려오는 권한은 전적으로 팀장이 가지고 있고, 필요에 따라 팀원들에게 위임됩니다. 스스로 획득하는 권한은 실력 혹은 전문성입니다. 내가 특정 분야의 전문가이면 팀장이라고 해도 그 분야의 의사 결정에 있어서는 내 의견을 완전히 무시할 수 없는 이유입니다. 하지만 대리나 과장이기 때문에 저절로 생기는 권한은 존재하지 않습니다.

모두가 이렇게 생각하면 직함이 있든 없든 수평적인 의사소통이 안 될 이유가 전혀 없습니다. 아무도 직위가 주는 권위를 인정하지 않으면 그런 권위는 존재하지 않기 때문입니다. 내 바로 옆에 있는 과장을 과장이라고 부르든 사장이라고 부르든 재벌 총수라고 부르든 달라지는 것은 전혀 없습니다. 반대로 모두가 직위 자체가 주는 권위가 있다고 생각하면 직위를 직함으로 불러주는 행위가 권위를 인정하는 표시가 됩니다.

이전에 일했던 회사에서 재미있는 사례가 있습니다. 당시에 개발팀의 직함을 SE(소프트웨어 엔지니어), 리드(소프트웨어 엔지니어)로 구분하고 있었습니다. 일반 회사로 치면 사원, 대리는 SE, 과장, 차장을 리드라고 압축해서 부른다고 생각하셔도 됩니다. 하지만 직함이 직위를 반영하고 있지는 않았습니다. 작은 기업이다보니 인사 시스템이 제대로 만들어지지 않아서 인사 평가나 연봉은 직함과 상관 없이 별도로 진행하였기 때문입니다. 바꿔 말해, SE에서 리드로 직함이 바뀐다고 해도 실질적으로 권한이 생기거나 연봉이 오르지 않는 시스템이었습니다. 리드가 되는 게 엄밀히 말해 승진이 아니었던 겁니다.

하지만 직원들이 이 시스템을 이해하는 방식은 크게 두 가지로 나뉘었습니다. 단순히 직함만 바뀌는 것뿐인데도, 리드를 승진으로 생각하고 직함 자체가 주는 어떤 권위나 권한이 있다고 생각하는 사람들이 있었고, 다른 한편으로 리드 직함을 단순 말장난이라고 생각하는 사람들도 있었습니다. 그래서 리드로 직함이 바뀌고 나면 승진 축하 파티를 여는 사람들도 있었고, 말장난 같은 리드라는 직함을 부끄러워 하는 사람들도 있었습니다.

두 부류의 사람들은 일하는 방식도 완전히 다릅니다. 리드라는 직함이 권위를 준다고 생각하는 사람들은 의사 결정을 할 때 “내가 리드니깐 내 말을 따라”라고 이야기합니다. 같은 생각을 가졌지만 리드가 되지 못한 SE는 자신에게 주어진 일이 자신의 권한을 넘는다고 생각하면 “제가 리드도 아닌데 그렇게 해도 되나요?”라고 말합니다. 반대로 리드가 말장난이라고 생각하는 사람들은 이런 행동에 대해 강한 거부감을 느낍니다. 권한은 실제로 권한이 있는 사람에게 위임 받았거나, 실력에서 나오는 것이라고 생각하기 때문입니다.

이런 혼란을 없애기 위해 제가 맡은 팀들에서 한해서만 실험적으로 직함을 폐지하였습니다. 리드라는 직함을 없애고 이름 뒤에 님만 붙이는 걸로 호칭을 통일하였습니다. 하지만 직함을 폐지했다고 당초 두 부류로 나뉘었던 사람들의 성향이 바뀌지는 않았습니다. 수직적인 조직 문화에 익숙한 사람은 수평적인 문화가 불편합니다. 자신의 직위에서 나오는 권한을 제대로 행사하지 못하는 것에 대해 불만도 생깁니다.

정리하면, 조직과 사람은 그대로인데 직함만 바꿔서 어떤 효과를 기대하기는 어렵습니다. 제대로 소프트웨어를 만들 수 있는 수평적인 조직을 만들고 싶다면 그에 맞는 사람만 뽑아서 팀을 따로 구성해야 합니다. 하지만 수직적인 조직 문화가 팽배한 우리 사회에 이런 성향의 사람들은 많지 않습니다. 어릴 때부터 학교와 군대, 사회 생활을 통해 철저하게 학습된 사고 방식이 하루 아침에 바뀔 수는 없습니다. 오히려 유능한 조직이 되려면 철저하게 이런 수직적 조직 문화를 잘 활용하는 것이 답일 수도 있습니다. 대기업이나 악명 높은 일부 게임 개발사들처럼 말입니다. (물론 옳은 일이라고 생각하는 건 아닙니다.)

직함은 우리사회에 뿌리 깊은 수직적 조직 문화의 그림자 같은 것입니다. 그림자를 지운다고 본체가 사라지는 것이 아닙니다. 직함을 폐지한다고 저절로 수평적인 조직이 되는 것도 아니고, 직함이 존재한다고 해서 반드시 수직적인 조직인 것도 아닙니다. 누군가의 권위는 그 사람한테 나오는 것이 아니라, 사람들의 생각에서 나오는데 그 생각을 바꾸기가 쉽지 않습니다. 우리 스스로 우리가 싫어하는 권위를 인정하고 있기 때문입니다.

프로젝트와 휴가

회사마다 제도의 차이는 있겠지만, 보통 팀장이 팀원들의 휴가를 승인합니다. 휴가는 법적으로 보장된 권리이지만, 하나의 팀으로 같이 프로젝트를 수행하는 입장에서 팀원의 휴가가 프로젝트 일정에 문제가 없도록 미리 조정할 수 있게 하려는 것이 원래 취지입니다. 이 점을 염두에 두고 다음 사례를 살펴봅시다.

당신은 얼마 전 팀장으로 승진한 초보 팀장입니다. 오후 3시 나른한 오후 팀원A가 당신에게 슬쩍 찾아와서 다음과 같이 묻습니다.

“팀장님 내일 오전에 주민센터에 서류 뗄 일이 있는데, 오전 반차 사용해도 될까요?”

이에 대한 반응으로 옳은 것은?

  1. 무슨 서류 떼야 하는데? 그거 인터넷으로도 뗄 수 있는 거 아냐?
  2. 내일 오전 반차를 지금 가져오면 어떻게 해? 미리 얘기할 수 없었어?
  3. 굳이 오전 반차를 사용해야 해? 아침에 조금 늦어도 괜찮으니 볼 일 보고 출근해.
  4. 안 돼.

1, 2, 3, 4번 모두 우리가 회사에서 흔히 들을 수 있는 반응들입니다. 무조건 안 된다는 4번 반응을 제외하면 각각의 반응은 팀장 나름대로 합리적인 선에서 절충안을 찾거나 대안을 주려고 노력하고 있기도 합니다. 물론 휴가 사유에 대해 꼬치꼬치 캐묻는 게 사생활 침해가 될 소지도 충분히 있습니다. 따라서 모두 오답입니다.

이 상황의 문제점은 팀장과 팀원 모두 프로젝트가 아닌 휴가 자체에만 집중하고 있다는 점입니다. 휴가 사용 시에 프로젝트 일정에 차질은 없는지, 차질이 있다면 어떤 식으로 조정할 것인지에 대해 논의하지 않고 휴가 사용 여부에 대해서만 이야기를 하기 때문에 팀장 입장에서는 무조건 휴가를 못 쓰게 하는 방법밖에 없고, 팀원 입장에서는 이러한 간섭이 부당하게 느껴질 수밖에 없습니다.

팀원이 휴가를 요청하는 방식에도 문제가 있습니다. “주민센터에 서류를 떼야 한다는” 휴가의 사유만 설명하고 있고, 본인이 프로젝트에서 맡고 있는 일에 대한 언급이 전혀 없습니다. 팀원이 휴가 사유에 대한 이야기만 하고 있기 때문에 팀장도 자연스럽게 휴가 사유가 적절한지에 대해서만 이야기할 가능성이 큽니다.

예를 들어, 내일 여자 친구와 100일이어서 하루 휴가를 쓰고 싶다면? 팀원 A는 여자친구를 너무나 사랑하기 때문에 당연히 휴가를 내야할 사유지만, 회사 일을 중시하는 팀장 입장에서는 전혀 납득할 수 없는 요즘 애들의 개념 없음이 됩니다. 조금만 더 생각해보면, 팀장 입장에서 팀원의 휴가 사유가 무엇인지는 전혀 중요하지 않습니다. 팀장에게 중요한 것은 팀원 A의 휴가로 인해 진행 중인 프로젝트에 차질이 있는지 없는지 여부입니다.

팀원 A가 휴가를 내는 올바른 방법은 다음과 같습니다.

“팀장님 내일 개인 사정으로 오전 휴가를 사용하겠습니다. 내일 끝내기로 한 기능 X는 80% 정도 완료되어, 오후에만 작업해도 일정에는 크게 차질이 없을 것으로 생각됩니다. 다만, 팀원 B가 X에 대해 리뷰하기로 되어 있는데, 전달 시점이 반나절 정도 늦어질 것 같아서 팀원 B와 작업 순서를 조정하겠습니다.”

팀원 A가 제시한 안이 충분하다고 생각하면 바로 휴가를 승인하면 되고, 부족한 부분이 있다면 보완할 방법을 찾아주는 것이 팀장이 역할입니다. 우선순위가 낮은 일은 다음 마일스톤으로 미루고 팀원 A의 일 일부를 다른 팀원에게 나눠줄 수도 있습니다. 미팅이 있거나 같이 진행해야 할 업무가 있다면 미리 연락하여 조정하는 것도 중요합니다.

물론 이것만으로 모든 문제가 해결되지 않습니다. 휴가를 사용하면 어쨌거나 업무 시간 줄고, 애시당초 프로젝트 일정에 휴가를 고려한 일종의 “버퍼”가 전혀 없다면 업무를 조정한다는 것 자체가 말이 안 되기 때문입니다. 또한, 프로젝트 관련 지식을 팀원들이 서류 공유하고 있지 않으면, 팀원 한 명이 자리를 비우면 대체할 사람이 없으므로 휴가를 가고 싶어도 갈 수 없는 상태가 됩니다.

정리하면, 휴가를 낼 때 “휴가의 사유”가 중요한 것이 아니라 “프로젝트의 일정”이 중요합니다. 팀원은 팀장이 보기에 그럴싸한 휴가 사유를 생각하는데 시간 쓰지 말고, 내가 맡고 있는 일에 문제가 생기지 않게 책임감 있게 챙기는 자세가 더 중요합니다. 팀장도 휴가를 무조건 못 쓰게만 할 게 아니라 휴가로 인해 프로젝트에 공백이 생기지 않도록 프로젝트를 잘 관리해야 합니다.

버그 수정 일정 산출 요령

프로젝트가 후반부에 접어들면 신규 기능 구현보다는 버그 수정이 주 업무가 됩니다. 사내 테스트, 알파 테스트, 베타 테스트 등 출시 일정도 잡히게 됩니다. 끝날 것 같지 않던 프로젝트의 끝이 보이기 시작하면서 지쳐 있던 팀원들도 마지막 힘을 내 스퍼트를 하고 있습니다.

하지만 개발 PM 입장에서는 말 못할 고민이 하나 있습니다. 이미 출시 일정은 잡혔는데, 버그 수정에 관해서는 도무지 일정을 추정할 수 없기 때문입니다. 다급한 마음에 팀원들을 재촉하지만 반응이야 뻔합니다.

버그가 언제 잡힐지 어떻게 알아요? 해봐야 알죠.

기능 구현에 비해 버그 수정은 일정 산출이 훨씬 어렵습니다. 일정을 물어보면 대부분의 PM은 모른다고 답하거나, 스스로도 지키지 못할 약속만 할뿐입니다. 사실 버그 원인도 모르는데 수정에 얼마나 시간이 걸릴지 추정한다는 건 의미가 없습니다. 겉으로 보기에 간단한 버그라도 원인 파악부터 수정까지 상당한 시간이 걸릴 수도 있습니다.

예전에 같이 일했던 모 대기업의 수석님이 본인이 노하우를 살짝 공개하신 적이 있습니다.

버그가 30개, 사원이 5명이 있다고 칩시다. 사원들한테 각자 버그를 6개씩 나눠주고 내일 아침까지 잡아오라고 하면 됩니다. 아무 말도 안 하고 있으면 버그가 안 잡히는데, 이렇게 명시적으로 버그를 나눠주고 기한을 주면 어떻게든 버그가 잡혀요.

이 말을 듣고 어떤 표정을 지어야 할지 난감했던 기억이 있습니다. 그리고 그날 울 것 같은 표정으로 선임들 옆을 멍하니 서성이던 신입 사원들의 표정을 잊을 수가 없습니다. 사실 특별히 유난스런 사례도 아닙니다. 이 단계가 되면 대부분의 사람들이 대동단결하여 이렇게 외칩니다.

난 잘 모르겠고, 어쨌거나 출시까지 버그 다 고쳐와.

버그 수정은 작업의 특성상 일정 산출이 어려운 것이 사실이지만, 다행히 일정 산출의 리스크를 줄일 수 있는 방법이 있습니다. 버그를 우선순위에 따라 분류하고, 버그 해결을 원인 파악과 수정 두 단계로 나눠서 생각하는 것입니다.

우선, 모든 버그를 분류합니다. 각각의 버그는 우선순위심각도를 표시합니다. 여기서 우선순위는 해결해야할 버그의 순서를 말합니다. 우선순위가 높을수록 먼저 해결해야 하는 버그입니다. 보통 상, 중, 하로 구분합니다. 심각도는 소프트웨어에 미치는 여파를 말합니다. 크래시가 나거나 멈추는 버그는 심각도가 높고, 폰트가 1px 정도 밀리는 버그는 심각도가 낮습니다.

이렇게 버그를 우선순위와 심각도에 따라 분류하는 것을 버그 트리아지(bug triage)라고 부르는데, 트리아지라는 말 자체가 병원에서 치료 우선순위를 정하기 위한 부상자 분류에서 온 말입니다. 예를 들어, 큰 사고가 생겨 많은 환자가 한꺼번에 병원 응급실로 이송되어 온 상황입니다. 물론 도의적 차원에서야 환자 한명 한명이 소중한 생명이고 모두를 살리는 것이 가장 좋지만, 투입할 수 있는 의사나 간호사의 인력은 제한되어 있고 약품이나 물자도 부족하기 때문에 환자 중에 누구를 먼저 치료할 것인지 부상의 정도나 긴급성에 따라 분류를 하지 못하면 더 많은 희생을 치를 수밖에 없습니다.

예를 들어, 모바일 게임을 만들고 있는데, 스플래시 스크린의 게임 제목에 오타가 발견되었습니다. 이 버그는 심각도는 낮지만 우선순위는 매우 높습니다. 오타 있어서 게임 플레이에는 아무런 지장이 없지만, 모두가 보는 첫화면에 오타가 있으므로 빨리 고쳐야 하는 긴급한 버그로 분류됩니다. 반대의 예도 있습니다. 게임 캐릭터가 특정한 아이템을 착용하고, 특정한 위치에서 필살기를 사용했을 때 게임이 멈추는 버그가 발견되었습니다. 이런 버그는 심각도는 높지만, 우선순위는 낮은 것으로 분류됩니다. 자주 일어나는 증상이 아니기 때문입니다.

우선순위는 상대적입니다. 버그를 상, 중, 하로 분류한다고 해서 하로 분류된 버그는 안 고쳐도 되는 버그란 뜻이 아닙니다. 다만, 버그를 잡을 수 있는 시간과 인력에 제한이 있으므로 우선순위가 높은 버그를 먼저 잡기 위해서는 분류가 필요한 것뿐입니다. 제가 같이 일해본 어떤 회사는 상, 중, 하 분류법으로는 부족하다고 생각했는지 최상, 긴급, 초긴급, 초초긴급, 당일 해결, 즉시 해결 등등을 만들어내기 시작했습니다. 하지만 이런 건 그저 다급함이 만들어낸 웃지 못할 촌극일뿐 실제로 문제 해결에 필요한 건 상대적 우선순위일 뿐입니다.

하지만 버그 트리아지만으로는 버그 수정 일정을 산출할 수는 없습니다. 어떤 버그를 언제까지 잡을지 일정을 산출한 것이 아니라 시간이 모자랄 경우 어떤 버그를 포기할 것인지만 정했을 뿐입니다. 버그 수정 일정 산출의 불확실성은 버그의 원인을 모르기 때문에 발생합니다. 일정을 산출하기 위해서는 버그의 원인부터 파악해야 합니다. PM 입장에서 아직 원인도 모르는 버그는 일정을 전혀 추산할 수 없는 블랙박스나 마찬가지고, 일단 원인 파악이 끝난 버그는 신규 기능 작성과 마찬가지로 명확한 일이 되기 때문입니다.

해당 버그가 기획 단계에서 미처 생각하지 못했던 코너 케이스라면 해당 케이스에 대해 기획을 보강하고 코드를 추가로 구현해야 할 것이고, 코딩 작업에서 발생한 단순 실수라면 해당 코드만 수정하면 될 것입니다. 혹은 코드의 구조적인 문제로 생긴 버그라면 상당량의 코드를 새로 작성하지 않는 이상 해결할 수 없을 수도 있습니다. 따라서 트리아지된 버그를 하나씩 개발자에게 할당하고 해결할 것이 아니라, 일단 버그들의 원인부터 먼저 파악하고 수정 방식이나 여부는 따로 결정하는 것이 좋습니다.

버그 원인을 알면 버그 수정이 쉬울지 어려울지 예상 가능하기 때문에 버그 수정에도 도움이 됩니다. 수학 시험을 볼 때 일단 전체적으로 문제를 훑어보고 쉬운 문제 먼저 풀고 어려운 문제를 나중에 푸는 것처럼, 버그들의 원인을 먼저 파악하고 쉬운 버그, 어려운 버그를 구분하면 쉬운 버그 먼저 잡고 어려운 버그를 나중에 잡을 수 있기 때문입니다. 물론 우선순위를 무시하고 쉬운 버그 먼저 잡으라는 뜻이 아니라, 비슷한 우선순위의 버그라면 쉬운 버그를 먼저 잡으란 뜻입니다.

버그가 10개가 있는데, 한번에 하나씩 버그를 수정하는 방식으로 진행을 하면 남은 버그 수정에 시간이 얼마나 더 걸릴지 전혀 알 수가 없습니다. 하지만 10개 버그의 원인 먼저 파악하고, 누가 어떻게 수정할지 계획이 있는 상태면 10개 버그 수정에 시간이 얼마나 걸릴지 대략적인 예측이 가능합니다. 물론 어떤 방식이든 절대적으로 버그 수정에 걸리는 시간은 똑같을 수도 있고, 경우에 따라 원인만 먼저 파악하는 방식이 오히려 시간이 더 걸릴 수도 있습니다. 하지만 원인을 먼저 파악하면 일정의 불확실성을 줄일 수 있습니다.

프로젝트에 있어 가장 큰 리스크는 불확실성입니다. 그리고 일정 관리란 일을 더 빨리하는 방법이 아니라 불확실성을 줄이는 방법입니다. 버그 수정도 예외는 아닙니다. PM의 역할은 버그 하나라도 더 잡으라고 개발자들을 독촉하는 것이 아니라 문제가 발생하지 않도록 미리 대비하는 것입니다.

개발 팀장과 PM

PM 스쿨은 소프트웨어 개발팀 관점에서 프로젝트 관리 방법에 대한 이야기를 합니다. 국내 회사는 대부분 개발 팀장이 PM 역할도 병행하기 때문에 PM 스쿨에서는 “개발 팀장”과 “PM”을 혼용해서 쓰고 있지만 맥락에 따라 엄밀히 구분하는 것도 가능합니다.

예를 들어, 한 팀에 PM이 여러 명이 있을 수도 있고, 개발 팀장이 아닌 별도의 PM을 둘 수도 있습니다. 심지어 팀원들이 돌아가면서 PM을 할 수도 있습니다. 각각의 경우에 대해 자세히 살펴보겠습니다.

한 팀에 PM이 여러 명인 경우

이전 회사에서 하나의 팀이 동시에 2개 이상의 프로젝트를 동시에 진행하게 된 경우가 있었습니다. 같은 엔진이지만 서로 요구사항이 다른 고객사 2곳과 동시에 작업을 해야 했기 때문입니다. 한 프로젝트는 개발 팀장이 직접 PM을 맡고, 다른 프로젝트는 팀원 중에 시니어 개발자 한 명이 PM을 맡았습니다. 완전히 별도의 팀으로 나누지 않는 이유는 고객사 대응 자체가 한시적인 일이고, 일부 인원은 양쪽 모두 유동적으로 대응을 해야했기 때문이었습니다.

이 경우 개발 팀장과 PM 사이에 명확히 역할 구분을 해주는 것이 중요합니다. 정해진 답은 없습니다. 효율적인 프로젝트 관리에 필요한 만큼 의사 결정 권한을 개발 팀장이 PM에게 적절하게 위임해야 합니다. 규칙을 꼼꼼하게 정할 필요는 없습니다. 예상치 못한 상황이 나오면 미리 정한 규칙에 따르기 보단 대화를 통해 해결해야 하는 것이 낫기 때문입니다.

이때 신규 PM은 PM 경험이 부족하기 때문에 좀 더 PM 경험이 많은 개발 팀장이 PM 멘터 역할을 해주는 것이 중요합니다. 좋은 PM을 만드는 체계적인 방법 같은 건 없습니다. 직접 PM 역할을 수행하면서 좀 더 경험 많은 PM에게 이런 저런 조언을 받는 게 좋은 PM이 되는 가장 빠른 방법입니다.

개발 팀장이 PM이 아닌 경우

우리나라에서 “개발 팀장”은 굉장히 복합적인 의미입니다. 이상적인 상황이라면 개발 실무 능력이 검증되었고, 프로젝트 관리도 수행할 수 있는 종합적인 인재가 팀장이 되어야 합니다. 하지만 보통 개발 실력은 출중하지만 PM 능력은 검증되지 않았거나 혹은 관리 능력이 없는 사람을 승진시킬 수밖에 없는 상황이 있습니다. 개발 팀장이 단순 역할이 아니라 연봉이나 직급 면에서 승진을 뜻하기 때문입니다.

이 경우 개발 팀장이 PM을 맡지 않는 것도 방법입니다. 여기서도 역할 구분이 중요합니다. 개발 팀장은 아키텍처나 중요 기술 이슈에 대해 의사 결정을 하거나 기술 이슈가 생겼을 때 트러블슈팅하는 것에 집중하고, 프로젝트 관리 능력이 있는 팀원을 PM으로 지정하여 프로젝트 일정이나 이슈 관리, 사업팀과 커뮤니케이션 등 수행하게 해야합니다.

지난 회사에서 이런 방식으로 PM을 정한 적이 있습니다. 개발 팀장으로 승진한 친구는 굉장히 깊이 있는 엔지니어링 지식을 가지고 있고, 동료 개발자들의 존경을 받았지만 프로젝트 관리 능력은 없었고 본인 스스로도 개발에만 더 집중하고 싶어하였습니다. 하지만 당시에 팀에 비슷한 경력이나 실력의 개발자가 없었기 때문에 이 친구를 팀장으로 승진시킬 수밖에 없었고, 대신 PM은 오히려 연차가 적지만 프로젝트나 관리나 커뮤니케이션에 센스가 있는 친구에게 맡기는 방식을 취했습니다.

이 관계가 유지되려면 개발 팀장과 PM 사이의 신뢰가 무척 중요합니다. 서로의 장점과 단점을 이해하고 역할을 존중해주지 않으면 성립할 수 없는 관계이기 때문입니다. 마찰이 있을 때는 더 높은 직급의 중간 관리자가 둘 사이의 역할과 책임을 적극적으로 조정해 주는 것도 방법입니다. 개발 팀장 입장에서 권한을 빼았겼다는 생각이 들지 않도록 개발 전문성을 존중해주고, PM이 권한이 아니라 프로젝트를 원할하게 돌아가게 하기 위한 역할임을 팀 전체에 명확히 해야 합니다.

팀원이 돌아가면서 PM을 하는 경우

일단 개발 팀장이 꼭 PM일 필요가 없으면 한 단계 더 나아가서 모두가 돌아가면서 PM 역할을 수행할 수도 있습니다. 물론 PM 일이라는 것도 나름의 센스와 전문성이 필요하기 때문에 돌아가면서 맡는 건 위험한 일입니다. 이게 용납되는 상황은 팀에서 통제할 수 없는 외부 요인이 PM 일의 8할을 단순반복 작업으로 만들 때입니다.

예를 들어, 회사가 실무 입장에서 판단했을 때 불필요해 보이는 문서나 자료를 계속해서 요구할 경우 팀장이나 PM이 여기에 매달리기 시작하면 낭비가 너무 커집니다. 이 경우, 중고등학교 때 돌아가면서 주번하듯이 PM을 뽑아서 기계적인 대응을 하는 것도 방법입니다. 정해진 순서대로 할 수도 있고, 지각이나 빌드 브레이크 등의 벌칙으로 PM을 수행하게 만들 수도 있습니다.

외부 업체와 일을 할 때도 비슷한 상황이 있을 수 있습니다. 보통 갑을 관계에서 갑이 프로젝트 관리를 하겠다고 불필요하게 자주 회의를 소집하거나 문서나 자료를 요구하는 경우가 있는데, 이 경우에도 팀장이나 PM이 실제로 필요한 일에 집중할 수 있도록 팀원들이 번갈아가면서 대응할 수 있습니다. 물론 외부에 커뮤니케이션 채널이 여러 개가 되면 혼란을 초래할 수도 있으니, 외부 대응 하나의 창구로 하되 실제 작업은 당번 PM이 수행합니다.

실제로 이런 방식으로 대응을 한 적이 있습니다. 당시 고객사는 어차피 버그 트래커 보면 알 수 있는 버그 진행 사항을 매일 아침 10시마다 컨퍼런스 콜로 보고하기를 원했고, 일과 중에는 2시간 간격으로 진척도를 물었습니다. 덕분에 개발 팀장이나 PM이 이 일을 챙기면 다른 일을 못 하는 상황이었습니다. 궁여지책으로 팀원들이 돌아가면서 PM을 했는데, 이 방식의 최대 장점은 팀원들이 PM 역할의 소중함을 뼈저리게 체감한다는 점입니다 🙂

초보 개발 팀장이 겪는 문제들

초보 팀장은 눈코 뜰 새 없이 바쁩니다. 회사 일을 혼자 다하는 사람처럼 하루종일 분주하게 움직였는데도 업무가 줄어드는 것 같지 않습니다. 그런데 팀 전체가 바쁜 것은 아닙니다. 팀원들은 오히려 뭘 해야할지 몰라 멀뚱멀뚱 시간만 보내고 있습니다.

우리가 회사에서 흔히 볼 수 있는 광경입니다. 팀원 시절 실무 능력을 인정 받아 팀장으로 승진했는데, 업무 스타일은 팀원 시절 그대로이기 때문에 발생하는 현상입니다. 초보 팀장은 팀에 주어진 업무를 본인에게 주어진 업무라고 착각합니다. 예전보다 더 열심히 일하지만, 늘어난 업무량을 감당하기는 쉽지 않습니다.

팀원 2-3명 정도 수준의 작은 팀인 경우 팀장 개인 역량만으로도 팀의 업무를 소화하는 경우가 드물지 않게 있습니다. 팀장은 정말 바쁜데, 팀원들은 할 일이 없어 여유롭습니다. 다행히 외부에서 보기에는 큰 문제가 없습니다. 팀에 할당된 업무를 정상적으로 소화하고 있기 때문입니다. 개인 역량이 뛰어난 팀장일수록 더 많은 업무량을 미련하게 소화합니다. 야근이나 주말 근무도 불사합니다.

팀원이 4-5명을 넘어가기 시작하면 상황이 달라집니다. 실무 능력이 아무리 뛰어난 팀장이라도 4-5명 분의 일을 혼자 감당하기는 쉽지 않기 때문입니다. 울며겨자 먹기로 일을 시켜보지만, 기대한 만큼 해내지 못한 팀원들을 보며 답답해 합니다. 여전히 중요 업무는 본인이 하고, 그다지 중요하지 않은 곁다리 일들만 팀원들에게 나눠줍니다.

외부 평가도 나빠지기 시작합니다. 기대만큼 업무 성과가 나오지 않기 때문입니다. 팀원들의 불만도 쌓여갑니다. 널널한 팀이라고 마냥 좋아하기에는 팀 자체의 평판이 나빠지고 있기 때문입니다. 일부 팀원들은 팀장의 리더십에 불만을 제기하기 시작합니다. 실무 능력이 좋은 건 알겠지만, 개발 팀장으로서는 실격이라는 말이 나옵니다.

실무 능력이 출중한 개발 팀장일수록 이런 경향이 강합니다. 본인보다 실력이 좋은 사람을 본 적이 별로 없기 때문에 기본적으로 업무에 있어서 다른 사람을 신뢰하지 못합니다. 팀원에게 나눠준 업무로 결과가 만족스럽지 못하면 피드백을 주고 개선하는 대신 본인이 다시 처음부터 일을 합니다.

또한 사람 좋은 팀장의 경우 팀원에게 일을 시키는 것을 부담스러워 합니다. 자신의 업무 지시가 무리한 요구일까봐 걱정하고, 다소 무리한 일은 될 수 있으면 본인이 처리하는 게 마음이 편합니다. 때문에 중요하고 급한 일이 생기면 팀원들은 퇴근하고 팀장 혼자 야근하게 됩니다.

초보 팀장이 겪는 이런 문제점들의 해결책은 본인 역량이 한계를 인정하는 데서 출발합니다. 프로젝트의 규모상 어차피 나 혼자 모든 일을 할 수 없다는 걸 인정하고 나면, 그저 열심히 하는 게 해결책이 아니라는 걸 알게 됩니다. 내가 더 열심히 해서 해결될 문제가 아니라 팀원들이 같이 일을 해야 한다는 걸 인정하고 나면 자연스럽게 팀원들에게 일을 더 잘 나눠줄 방법을 찾을 수 있게 됩니다.

또한 내가 팀원들에게 마냥 좋은 팀장이 될만큼의 성인군자가 아니라는 것도 인정해야 합니다. 내 역량으로 감당 못할 프로젝트를 혼자 끌고 가다 나중에 문제가 생기고 문책이라도 받게 되면 누구라도 스트레스 받고 주변에 책임을 돌리게 됩니다. 더 이상 사람 좋은 팀장도 아니게 되는 겁니다. 차라리 처음에 조금 어렵더라도 팀원들을 더 적극적으로 참여시키고, 싫은 소리 듣더라도 프로젝트가 성공할 수 있는 방향으로 의사 결정을 내려야 합니다.

심리학에 메타인지 능력이라는 말이 있습니다. 주어진 문제를 열심히 푸는 능력이 아니라 내가 문제를 푸는 능력이 어느 정도 수준인지를 객관적으로 볼 수 있는 능력을 말합니다. 초보 개발 팀장이 “초보”라는 딱지를 떼는 가장 빠른 방법은 내가 “초보”라는 사실을 인지하고 실무자 시절과 같은 방법으로는 더 이상 문제를 해결할 수 없다는 사실을 재빨리 깨닫는 것입니다. 자신의 개발 실력을 자신할수록 이런 깨달음은 느릴 수밖에 없고 더 오랫동안 시행착오를 겪게 되기 때문입니다.

슈퍼 프로그래머

소프트웨어 엔지니어링 분야의 고전 맨먼스 머신에는 일반적인 개발자에 비해 생산성이 2배, 10배, 심지어 100배 이상 뛰어난 슈퍼 프로그래머에 대한 이야기가 나옵니다. 생산성을 정의하는 방법에 따라 구체적인 숫자는 바뀔 수 있겠지만, 프로그래밍은 다른 분야와 달리 물리적인 제약이 없는 만큼 개인의 생산성 차이가 다른 분야에 비해 클 수 있다는 주장에는 많은 사람들이 공감하는 것 같습니다.

하지만 제 경험상 PM 입장에서 슈퍼 프로그래머의 존재는 좋아할 일만은 아닙니다. 소프트웨어 개발 문화나 프로젝트 관리 능력이 제대로 갖추어지지 않은 조직에 슈퍼 프로그래머가 있다면, 개인의 개발 생산성이 정말로 뛰어난 경우보다는 PM의 관리 능력에 문제가 있는 경우가 더 많기 때문입니다.

일례로 예전 직장에 모바일 액션 RPG 게임을 만들던 M팀이 있었는데, 이 팀에는 이른바 슈퍼 프로그래머로 알려진 K군이 있었습니다. K군은 중고등학교 때부터 정보 올림피아드에 출전하여 입상한 경험이 있고 수상 실적을 바탕으로 명문대를 특례 입학한 친구입니다. K군은 병역 특례로 지원을 했는데 회사 면접에서도 출중한 실력을 보여서 만장 일치로 선발하였습니다. K군은 팀은 클라이언트 개발자로 팀의 두 번째 멤버였는데, 첫 번째 멤버가 다른 회사로 옮기면서 입사 후 얼마되지 않아 M팀의 메인 개발자가 되었습니다.

M팀의 팀장은 이런 K군을 애지중지하며 K군이 일반적인 개발자 6명 이상의 몫을 한다며 그를 치켜세우곤 했습니다. (6이라는 숫자가 구체적으로 어떻게 도출되었는지는 알 수가 없습니다.) 2배도 아닌 6배의 생산성을 내는 K군은 슈퍼 프로그래머로 분류하기에 무리가 없어 보입니다. 실무에 무지한 사장도 팀장의 평가만 믿고 K군을 회사의 보배라고 생각하고 신입인 K군에게 파격적인 연봉을 줍니다. 이런 K군의 회사 내 별명은 “왕의 아들”이 됩니다.

M팀은 장르가 RPG인만큼 개발자가 10명 이상되는 중간 규모의 팀이었고, 이후 클라이언트 개발자를 충원하여 3-4명 수준을 유지하게 됩니다. 이후 합류한 멤버 중에는 10년 경력의 개발자도 있었고, K군과 비슷한 수준의 대학을 나온 또 다른 병역 특례 개발자도 있었습니다. 객관적으로 봤을 때 K군보다 못할 이유가 없는 개발자들이지만, 팀에 합류 후에 이렇다 할 생산성을 내지 못하였고 팀장은 더더욱 K군이 슈퍼 프로그래머임을 확신하여 K군에만 의지하여 개발을 진행하게 됩니다.

하지만 프로젝트가 중반 이후로 가면서 생산성에 문제가 생기기 시작합니다. 그다지 복잡할 것도 없는 요구사항 변경에 대응하는 속도가 현저히 느려지기 시작하였기 때문입니다. 팀의 일정이라는 것도 의미가 없어집니다. 모든 일정은 K군이 할 수 있는지 없는지에 달려 있기 때문입니다. K군이 3일을 부르면 3일이 일정이 되고, 일주일을 부르면 일주일이 일정이 되는 상황이 됩니다. 다른 개발자를 투입하려고 해도 소용이 없었습니다. K군이 작성한 코드는 K군 외에는 아무도 알아 볼 수가 없었고, 다른 개발자가 겨우 코드를 파악해서 수정을 해도 여기저기서 예상치 못한 문제가 추가로 생기는 경우가 많았기 때문입니다.

설상가상으로 영특한 K군은 이런 상황을 악용합니다. 본인이 아니면 프로젝트를 완성해서 출시할 수 없다는 사실을 알고 자기 마음대로 행동하기 시작합니다. 출근 시간을 지키지 않거나 업무 시간에 2-3시간씩 자리를 비우는 것은 약과고, 업무 시간에 다른 회사의 외주 일을 하는 경우까지 있었습니다. 참다 못한 M팀의 다른 개발자가 이 상황을 팀장과 사장에게 이야기했으나 묵살됩니다. K군이 없으면 프로젝트 출시를 못할 수도 있으니 다소 부당하게 느껴지더라도 참으라는 것입니다.

이 사례는 프로젝트 관리에서 위험 관리에 실패했을 때 어떤 일이 일어나는지 잘 보여줍니다. 이 사례가 극단적인 사례도 아닙니다. 부끄럽지만 저도 옆에서 지켜본 일이고, 주변에서도 한 번쯤 들어보셨을 흔한 일이기도 합니다. 그리고 팀장과 사장의 대처에도 볼 수 있듯이 PM이나 경영진은 본인의 실수를 인정하고 싶어하지 않아 합니다.

문제의 근본적인 원인은 K군이 아니라 팀장의 관리 능력에 있습니다. M팀의 팀장은 위험 관리를 전혀 하지 않았습니다. K군이 나쁜 마음을 먹고 이 상황을 이용하지 않았더라도 프로젝트를 진행하면서 어떤 일이 생길지 알 수가 없습니다. K군이 더 좋은 조건을 받아 다른 회사로 옮길 수도 있고, 심지어 출근하다가 교통 사고가 나서 죽을 수도 있습니다.

소프트웨어 개발 프로젝트의 가장 큰 위험 요소 중 하나는 지식이 한 명에게 집중되는 것입니다. M팀의 프로젝트는 개발 관련 지식이 K군에게 집중되어 있고, 또한 그렇기 때문에 더더욱 K군에게만 의존하게 되었습니다. 이 문제를 방지하려면 M팀의 팀장이 다른 개발자들도 코드를 파악할 수 있도록 업무 분담을 시키거나, 설계 리뷰, 코드 리뷰 등을 통해 팀내 지식이 자연스럽게 전파될 수 있도록 프로세스를 만들었어야 합니다.

또한 다른 개발자들이 코드 파악을 못하거나 생산성이 낮은 것을 보고 문제에 대한 힌트를 얻었어야 합니다. 하지만 팀장은 거꾸로 같은 상황을 보고 K군에 비해 다른 개발자들의 역량이 부족하다고 판단하였습니다. 실상은 K군이 다른 사람이 이해하기 쉽게 코드를 짜는 능력이 부족한 것이었습니다. 프로젝트 후반에 가서 생산성이 떨어진 이유는 코드가 너무 복잡해지면서 K군 본인도 본인이 작성한 코드를 100% 이해하지 못하기 시작하면서 발생한 일입니다.

슈퍼 프로그래머는 적당히 잘하는 개발자와 못하는 PM이 만들어내는 일종의 콤비 플레이입니다. 개발자가 소프트웨에 엔지니어링 원칙에 충실하게 누구나 쉽게 유지보수 할 수 있는 이해하기 쉬운 코드를 작성했다면 이런 일이 벌어질 수 없었을 것이고, 개발 팀장 혹은 PM이 이런 위험 요소를 관리하기 위한 프로세스를 도입했어도 이런 일이 벌어질 수 없었을 겁니다.

주변에 슈퍼 프로그래머가 있으시다고요? 그 팀의 팀장님은 어떤 사람인가요?