»
S
I
D
E
B
A
R
«
새로운 툴을 대하는 자세
Sep 27th, 2010 by Wegra Lee

회사 생활을 하면서 나는 참 많은 툴들을 팀에 전파하려 노력하였고 또 전파한 편이다. 테스트 자동화, 분산 컴파일, 테스트 커버리지 측정, 프로파일링, 정적 코드 분석, 컨티뉴어스 빌드, 애플리케이션 라이프 사이클 관리 등 그 분야도 소프트웨어 개발 전반에 걸쳐있다.

매번 새로운 툴을 도입하려 할 때마다 사람들의 반응은 제각각이다. 툴이 제공하는 기능 리스트(무엇을 할 수 있다)만 보는 사람, 자신에게 당장 필요한 핵심 기능 몇 가지에만 관심을 갖는 사람, 이전에 사용해본 유사한 툴과 똑같은 취급하는 사람 등 많은 부류가 있다. 하지만 아쉬운 점은 정작 그 툴의 진정한 가치에 관심을 보이는 사람은 찾기 힘들다는 점이었다.

좋은 툴들엔 그 툴이 현재의 모습을 갖추기까지 거친 수많은 시행 착오로부터 얻어진 노하우와 설계자의 철학이 담겨 있다. 고객들로부터 받은 엄청난 양의 피드백 역시 툴의 기능과 동작 방식에 지대한 영향을 미친다.

툴을 제대로 익힌다는 것은 이러한 경험과 노하우를 함께 습득한다는 의미를 갖는다.

툴 제작자들은 고객이 별다른 교육 없이도 최대한 쉽게 사용하게 만들기 위해 많은 노력을 기울이지만, 여기엔 분명한 한계가 있다. 아무것도 없는 야구공 하나도 잘 던지라면 제대로 쥐는법부터 시작해 많은 학습과 연습이 필요하다. 하물며 복잡한 소프트웨어 개발을 위해 수많은 사람들이 함께 사용해야할 툴이라면 오죽하랴.

툴을 제대로 활용할 줄 모르는 사람에게는 어떤 툴을 제공하건 생산성에 큰 변화가 없다. 이들은 대부분 기본적인 기능만 사용하게 되는데, 기본 기능만 놓고 본다면 이름 있는 유사 툴들 사이에 큰 차이를 찾아보긴 어렵기 때문이다. 병목 현상이 발생하던 부분에서 성능이 대폭 개선되는 경우(분산 컴파일, 저장소 프락시 등)나, 개발자가 할 일을 대신해주는 경우(자동 빌드, 정적 코드 분석)와 같이 한정된 상황에서만 그나마 효과를 얻게 된다.

여기까지는 양호한 편이다. 심한 경우 툴이 만들어진 의도와 다르게 활용되는 경우도 많다. 툴의 설계자는 일반적인 사람들의 잘못된 습관을 바로잡기 위해 기능과 인터페이스를 설계했는데, 바로 그 잘못된 습관대로 일을 진행하면서 오히려 툴이 잘 맞지 않는다고 불만을 토로하기도 한다. 방법을 바꾸면 더 큰 효과를 얻을 수 있음에도, 왜 툴이 내 마음과 맞지 않는지는 깊게 생각하지 않는다.

이전보다 훨씬 다양하고 강력한 기능을 갖춘 툴을 보여줘도, 기존에 사용해본 유사 툴의 틀에 갖혀 장점을 보지 못하는 경우도 많다. ‘이 툴은 이런 거야’라고 일찌감치 단정짓고 더 이상 파고들려 하지 않는다.

이러한 태도를 버리지 못한다면 새로운 툴 도입이란 똑같은 일을 하기 위한 방법을 하나 더 익히는 것 이상도 이하도 아니게 된다. 결국 아무리 좋은 툴이 새로 나오더라도 도입에 보수적이게 되는 원인 중 하나가 된다.

팀원 전체가 그렇지는 못하다면, 개인적으로라도 툴에 녹아있는 사상을 공부하고, 제대로된 활용법을 익히려 노력해보자.

과거의 유사 툴이나 시장의 경쟁 툴들과 어떻게 다른지, 왜 다른지 파헤쳐보라. 같은 일이라도 다양한 접근법이 존재함을 배우고, 각각의 장단점을 깨달아 내가 처한 상황에서 최선의 방법을 찾을 수 있는 눈을 갖게 될 것이다.

내가 생각한 것과 다르게 동작한다면 왜 그렇게 만들었졌지 알아보라. 내가 하던 방식에서 많은 문제점을 발견하고 더 나은 방식을 배우는 계기가 될 것이다.

설계자가 그 툴을 만든 목적과 배경은 무엇인지, 어디에 주안점을 두었는지 알아보라. 내가 속한 팀, 나의 환경이 그들이 해결하고자 하던 문제를 그대로 앉고 있는가? 그렇다면 툴 도입이 해당 문제에 대한 확실한 해결책이 되도록 노력해보자. 문제를 인식/공유하고 툴 도입의 목적을 명확히 하자. 그리고 툴을 제대로 활용하고 있는지, 문제는 잘 개선되고 있는지 정기적으로 평가해보자.

툴 제작자가 권장하는 활용 방법(Best Practice)은 절대 빼먹어서는 안된다. 가이드라인을 잘 숙지하고 최대한 맞춰보려 노력해보자. 충분히 익숙해진다면 내가 생각하는 방식이 곧 툴이 동작하는 방식이 된다. 머리 따로 몸 따로 움직이던 시절을 떠올리며 여유 가득한 미소를 짓게될 것이다.

이러한 과정을 거치면서 툴 도입 효과가 극대화됨은 물론, 자신의 역량도 함께 성장한다. 제대로 활용할 줄 아는 툴이 많은 것은 좋은 개발자가 갖춰야할 덕목 중 하나이기도 하다.

IT 스터디.. 이렇게 진행해 보는 것은?
Jun 19th, 2010 by Wegra Lee

나는 작년에 마음에 맞는 사람 몇 명과 제법 참신하고 효과적인 스터디를 만들어 운영해보았다. 그 경험은 값진 기억이 되었고, 추후 여건이 다시 갖추어지면 유사한 형태로 재도전해보고 싶다. 여기 그 방식에 대해 간략히 소개해볼 터이니, 관심 있는 사람은 직접 주변인들과 시도해보길 권해본다.

내가 만들었던 스터디는 조금 특이했다.

가장 큰 특징은 참여자들이 준비해올 것이 없었다는 점. 스터디 자료로는 Google Tech Talk, iTunes U(niversity), Podcast, 인터넷상의 각종 기술 세미나, 최신 툴 데모 동영상, 유명인 인터뷰 동영상 등이었다. 세어보진 않았지만, 대략 백여 개의 흥미로운 자료들을 모을 수 있었다.

1시간 정도 동영상을 함께 보고, 30분 정도 토론한다. 부족한 정보는 그때그때 인터넷에서 검색해볼 수 있고, 더 깊은 지식을 원하거나 직접 해보고 싶은 것들은 action item으로 빼서 스터디 외 시간에 진행하기도 한다. 흥미로운 결과가 나오면 정리해서 스터디 팀 외의 사람들과도 공유했고, 실제 현업에 도움이 되는 일을 찾아 진행하기도 했다.

또한, 서로의 task plan(멤버 각자가 현업에서 실제 쓰는)을 리뷰하면서 planning 기술을 늘려가기도 하고, 코드 리뷰로부터 나온 유용한 패턴들을 공유하는 자리로도 활용했다.

별다른 준비 없이도 참여만 하면 지식과 지혜를 얻어갈 수 있는 모임이었고, 이런 특성이 스터디를 지속하는 데 큰 도움이 되었다고 믿는다.

매달 마지막 모임 때는 회고(retrospective)를 진행했다. 회고에서는 지난 한 달 동안 공부한 내용을 되짚어보고, 다음 한 달간 공부할 커리큘럼을 짠다. 스터디 진행 방식에 있어 개선이 필요한 부분을 논의해서 적용해본다. 주기적으로 동기를 부여하고, 부족한 부분을 지속 보강하여 모임의 생명력을 유지하고 발전시키는 수단이 되었다고 자평해본다.

이 모임은 매일 아침 7:30에 모여 9시까지 약 4달간 유지되었고, 일부 열성 멤버는 토요일에 모여 별도의 스터디(iPhone application programming)를 진행하기도 했다. 그러다가 내가 다른 목표가 생겨 탈퇴하면서, 아쉽게도 현재는 운영되지 않는 상태다.

운영하면서 어려웠던 점들도 정리해보았다.

- 참여자 대부분이 서로 다른 팀의 맴버였고, 같은 팀 안에서도 개개인이 독립적으로 일하는 문화 때문에 시너지를 일으키는 데 한계를 많이 느꼈다.

- 업무상 건물 간 이동이 잦은 멤버는 왔다갔다하는 불편을 겪기도 했다.

- 정식 업무가 아니라, 이른 아침 시간을 선택.. 참여하고 싶어도 나오지 못하는 사람도 있었다. 멀리 사는 사람, 아침잠 많은 사람 등. 소수 인원으로 운영하다 보니 두 명만 빠져도 스터디 진행에 영향을 미쳤다.

- 팀이 (반 강제적으로) 늦게 퇴근하는 문화로 바뀌면서 아침 스터디에 대한 부담이 점차 커졌다.

내가 이 스터디를 주창한 가장 큰 이유는 평소 동료 개발자들이 세상 돌아가는 정보에 너무 무관심하다고 느꼈기 때문이만, 진행하면 할수록 나 역시도 다른 멤버로부터 많은 정보와 깨우침을 얻게 된 값진 경험이었다.

[updated] 직접보기.. 혁신을 이끌어내는 방법
Feb 2nd, 2010 by Wegra Lee

쉬어가기.. 혁신을 이끌어내는 방법 [1]‘ 에서는 개발자들에게 쉬어갈 수 있는 시간을 제공함으로써 창의와 혁신을 이끌어는내는 이야기를 해보았다. 이번에는 ‘직접보기’라는 주제로 비슷한 이야기를 해보려 한다.

‘직접보기’ 가 필요한 이유는 아래의 그림을 보고 생각해보자. 이 그림이 말하고자 하는 원목적은 완전히 동일하진 않지만, 실물을 보지 않고 커뮤니케이션 했을 때의 문제를 효과적으로 말해주고 있다.

정리하면 고객이 원하는 것을 각 사람/조직마다 다르게 이해하고 있으며 심지어 고객 스스로도 자신이 무엇을 필요로 하는지 알지 못한다 것이다.

시장 조사를 토대로 고객의 needs 를 모두 만족시킨 제품의 출시 후 반응이 그리 좋지 않은 수많은 사례들을 잘 설명할 수 있는 논리이기도 하다.

혁신적인 제품을 잘 만들어내기로 유명한 애플(Apple)사의 경우, 신제품을 만들 때 시장 조사를 아얘 하지 않는 것으로도 유명하다(You Can’t Innovate Like Apple [2]). 시장에 존재하지 않는 제품에 대해 물어봐야 가치 있는 대답을 구하기 어렵기 때문이다. 대신 스스로 계속해서 실제품 수준의 프로토타입을 수없이 만들어보면서 직접 만져보고 써보며 자신들이 정말 이 제품을 원하는가를 판단한다. 그 결과 애플의 제품들은 종종 시장에서 당연히 필요하다고 생각하는 기능이 빠지기도 하고 이미 더 나은 제품들이 수두룩한데~ 라고 평가절하되곤 한다.

이미 만들어진 제품에 대해서는 다르다. 직접 사용해본 사용자들의 피드백은 소중하다. 애플 리테일 스토어가 중요한 역할을 수행하는데, 리테일 스토어의 직원들은 고객이 와서 들려준 이야기들을 놓치지 않고 본사로 보고하게 되어 있다. 그래서 스티브 잡스는 아이디어 도둑(유명 마케터 이해선 대표의 메시지 [3])이라는 말이 나오기까지 했다.

고객의 소리를 듣는 방식에 있어 두 경우가 다르다고 이야기했지만, 사실 그 원리는 동일하다. 바로 제품을 직접 만져보고 사용해본 사람들의 소리를 듣는다는 것이다.

이는 사실 애자일, 전통 할 것 없이 거의 모든 소프트웨어 개발 방법론에 중요하게 다루는 주제이고, 결론 또한 항시 동일하다. 짧은 반복 주기로 매 주기마다 동작 가능한 제품을 내놓고, 이를 고객에게 보여주고 피드백을 받는다. ‘당신이 말한 것을 우리는 이렇게 이해했는데, 이것이 정말 당신이 원했던 것이오?’ 를 확인하는 가장 중요한 절차인 것이다. 진정 공존을 원한다면 이 과정에서 쓸데없는 과장과 화려한 프리젠테이션은 없어져야 한다. 그리고 프로젝트 진행에 관련된 주요 인력들이 다 참석하는 것이 좋다. 고객, 프로젝트 리더, 영업 담당자, 주요 개발자들 등이 포함된다. 이들이 자주 모여 현실을 냉정하게 보고 허물없는 이야기를 하다보면 다음과 같은 반응들을 심심치 않게 목격할 수 있을 것이다.

  1. 내가 말했던 건 이게 아니었어요. 이러저런 모습을 상상했었는데요. 다음 릴리즈땐 이렇게 고쳐봐주세요.
  2. 내가 의도했던게 이게 맞긴 한데.. 직접 써보니 좀 이상하군요. 다른 아이디어가 있을까요?
  3. 이 부분은 제 생각과 다르긴 하지만.. 솔직히 지금이 더 좋아 보이는군요. 이대로 갑시다.

직접 보기는 서로의 생각을 확인하고 공유할 수 있는 가장 좋은 방법이며, 다음 방향을 결정짓기 위한 논의를 시작하기 위한 믿음직한 베이스가 되어준다.

또한 개발자들에게는 자신들의 창의력과 열정을 어필할 수 있는 절호의 기회가 되기도 한다. 직접 구현하면서 가장 먼저 써보게 되는 개발자들은 가장 빠르게 피드백을 줄 수 있는 훌륭한 고객인 셈이다. 이해한 요구사항대로 구현했을 시 불편한 부분이 있거나 더 나은 안이 떠오르면 릴리즈 전에 그 아이디어를 정리해두자. 가능하다면 직접 구현해서 보여주는 것이 가장 좋다. 직접 사용해본 고객과 말이나 문서 정도로만 본 고객은 확연히 다른 반응을 보인다.

이런식으로 개발자들의 능력을 인정받고 발언권을 강화해두는 것이 조직 전체의 커뮤니케이션과 생산성 향상, 제품 혁신에 긍정적인 영향을 줄 것이다. 개발자들은 기본적으로 창의적인 인력들이며, 이에 더해 현실적이다. Sci-fi 영화에나 나올 법한 허무 맹랑한 꿈을 꾸지도 않고, 일부러 과장하려는 경향도 적다. 먼 과거와 달리 골방의 괴짜들이 모여 있는 집단도 아니다. 윗사람들보다 신세대이며 소비의 주체라는 장점도 있다.

결론?

조직은 제품을 직접 보고 함께 이야기하는 문화를 정착시킴으로써 많은 것을 얻을 수 있다. 단, 어설프게 릴리즈 압박용으로만 오용하지 않도록 주의하자. 상향식 변화는 실패할 것이며, 하향식 변화는 성공한 것 처럼 보일 것이다. ^^ [4]

[updated]

사례를 몇 가지 추가해보기로 하였다.

  • Developing Torchlight [5] – Runic Games 사에서 Torchlight 라는 게임을 제작하는 방식을 이야기한다. 그들은 짧은 주기로 항시 play 가능한 게임을 만들어 개발자, QA 팀, 심지어 그드의 가족, 친구들까지 초대해서 게임을 즐기게 했다고 한다. 시장에서의 성공 여부는 아직 판가름하기 이르지만 기대를 가지고 지켜보고 있다.
  • 와우 성공 요인은? [6] – 초창기 와우 개발을 이끌었던 블리자드의 수석 PD 인 셰인 다비리 를 인터뷰한 내용이다. 초창기 가장 어려웠던 점 중 하나는 블라지드로써는 낯선 장르였던 MMORPG 의 비전을 경영진에 설득하는 것이었다고 한다. 직접 만들어 알파 버전을 보여주니, 절대 성공할 수 없다고 말하던 사람마저 하루 아침에 자신의 편이 되었다 한다.
  • Eclipse [7] 와 Jazz/RTC [8] – 오픈소스 개발 환경 프로젝트 중 역사상 가장 성공적인 프로젝트 중 하나인 Eclipse 와 그 개발 과정에서 얻은 노하우까지 툴에 녹이고 있는 Jazz/RTC 프로젝트도 좋은 예가 될 수 있다. 이들은 1년 주기의 정식 릴리스 사이에 6주 정도의 간격으로 다수의 안정적인 Milestone 버전을 내놓는다. 그리고 이전 릴리스 대비 어떤 기능이 개선되었는지 알기 쉽게 보여주는 New & Noteworthy 를 함께 알려주어서 사용자들이 정식 릴리스를 기다리지 않고도 새로운 기능들을 빠르게 접해볼 수 있다. Milestone 버전은 충분히 안정적이기 때문에 critical 한 프로젝트가 아니면 큰 부담 없이 새 milestone 을 테스트해본다. 이런 방식으로 사용자 커뮤니트의 빠른 피드백을 유도해 지속적으로 다음 릴리스에 반영해나간다.
  • Mobile SecondLife [9] – 내가 참여해 진행하다 중단된 프로젝트다. 과제 초창기부터 개발진에서는 도저히 성공 가능성이 없다고, 이걸 누가 쓰겠냐며 과제의 의미를 찾지 못하고 있었다. 링크의 데모는 연구 성격의 개념적 시연이어서 상당히 제한적인 환경에서만 동작 가능했다. 이를 바로 상품화하려 하니 현실적인 제약들 때문에 흥미로운 개념들의 거의 모두를 다 들어낼 수 밖에 없었다. 남은 것만으로는 정말 시도할 가치가 없는 과제였다. 하지만 그 목소리가 경영진에까지 전파되지는 못했다. 수개월간의 고생 끝에 만들어진 베타 버전을 임원에게 시연한 바로 다음날 과제는 바로 중단되었다.

References

  1. 쉬어가기.. 혁신을 이끌어내는 방법 (wegra.org)
  2. You Can’t Innovate Like Apple (Pragmatic Marketing)
  3. 유명 마케터 이해선 대표의 메시지 (제레미의 TV 2.0 이야기기)
  4. Bad Team Culture – 변화의 시작.. 상향식? 하향식? (wegra.org)
  5. Agile Approach in Game Development (wegra.org)
  6. 와우 성공 요인은? 전 수석 PD 셰인 다비리 인터뷰 (Inven Communications)
  7. Eclipse (Eclipse Foundation)
  8. Jazz/RTC (IBM Rational)
  9. Mobile SecondLife (Samsung)
쉬어가기.. 혁신을 이끌어내는 방법
Jan 11th, 2010 by Wegra Lee

우리는 창의력과 혁신을 강조하는 시대에 살고 있다. 소프트웨어 개발도 당연히 그 중심에 서 있다. 하지만 우리의 소프트웨어 개발 문화는 창의력을 심히 제한하는 방식으로 굳어져 있다.

일반적으로 우수한 편에 속하는 지적 능력을 가지고 있고, 새로운 것을 만들어내는 것을 즐기는 소프트웨어 개발자들. 그들은 회사에서 어떤 대우를 받고 있나. 조직이 조금만 커져도 기획은 별도의 부서에서 진행하고, 소프트웨어 개발은 몇몇 관리자들이 이끌게 된다. 소프트웨어 개발자들은 창의력을 발휘하기 보다는 시키는 일만 열심히 해야 하는 위치에 놓이게 된다. 코드를 찍어내고 타의에의해 변경된 요구사항들 때문에 수정/테스트를 반복하는 나날을 보내며 발언권은 점점 작아진다.

기술 개발을 중시하는 몇몇 소수 기업을 제외하고는 대부분(대기업)의 윗사람들은 아직까지 소프트웨어 개발자들을 단순 노동자 취급하고 있다. 이를 빗대어 나는 ‘소프트웨어 제조업’ 에 종사하고 있다고 얘기하곤 한다.

반면, 개발자들의 능력을 믿고 그들에게 스스로 혁신을 일으킬 수 있도록 지원해주는 대표적인 기업들로 Google, Apple, Rally Software 등을 들 수 있다.

Google 은 20% 제도로 유명하다[1]. 자신에게 주어진 시간 중 20% 정도를 주업무가 아닌 다른 일에 할애하도록 권장하는 제도로, 다양한 형태로 응용 가능하다. 매일매일 20%의 시간을 다른 일에 투자하는 것은 대부분의 경우는 현실적이지 않다. 프로젝트 마감이 코앞인데 다른 일에 정신 팔기도 힘들고, 하루 1~2시간씩 해서는 진도도 나가지 않기 때문이다. 대신 그들은 1주일에 하루, 1달에 1주, 혹은 6개월에 1달, workaholic 이라면 주말을 이용 등등.. 자유롭게 변형해서 일한다. 과제가 바쁠때는 열심히 그 일에 매달렸다가 한가할 타임에 그동안 못 쓴 20%를 쓰는 식이다.

20%의 시간에는 어떤 일을 할까? 이것도 아주 자유롭다. 전혀 새로운 과제를 수행할 수도 있고, 관심있는 다른 팀 과제를 지원할 수도 있고, 자신의 주 과제를 진행하면서 불편했던 부분을 자동화하거나 유용한 유틸리티를 만들거나, 최적화/리펙토링을 할 수도 있고, gmail/chrome browser 등에 유용한 플러그인을 만들어넣을 수도 있다. 공부를 할 수도 있다.

이렇게 해서 나온 결과들은 팀의 개발 생산성을 향상시키고, 제품의 품질을 좋게하고, 새로운 것을 배우게 만들고, 운이 좋으면 구글의 미래를 이끌어갈 제품으로 거듭날 수도 있다. Gmail, Google News, Google Talk, Orkut, Google Sky [2], Go programming language [3] 등은 모두 20% 시간에 시작된 프로젝트들이다.

Apple 의 경우 1년에 1달 가량 자신이 원하는 일을 할 수 있게 해준다지만.. 자세한 내용은 잘 알려지지 않았다[4].

마지막으로 Rally 의 경우 8주의 개발 사이클 중 마지막 1주는 회사의 미션과 관련된 일이라면 어떤 것이든 할 수 있는 권한이 주어진다[5]. 기간이 일정하게 주어져 있기 때문에 융통성이 조금 부족하지 않을까 우려되긴 하지만 한 숨을 돌리며 자신들의 과거를 뒤돌아볼 수 있다는 점만으로도 확실한 장점이 될 것이다.

다시 암울한 우리의 이야기로 돌아와보자.

나는 현 회사의 윗사람으로부터 ‘관지자가 할 일은 개발자가 놀지 않게 하는 것’ 이란 얘기를 들었다. 여기서 논다는 것은 ‘주업무와 직접적으로 관련 없는 모든 일’을 지칭한다. 내게는 ‘공장 가동을 멈추지 말 것’ 정도로 들렸다. 공식적인 주업무 외에는 회사에 기여하지 못하는 것으로 여겨기기 때문에 일거리를 만들어서라도 주지 않으면 능력 없는 관리자가 되는 것이다.

개발자 입장에서도 마찬가지다. 좋은 아이디어가 있더라도 side job 은 업적으로 인정받기 어렵다. 놀고 있는 것으로 받아들여지므로 드러내놓고 할 수도 없다. 결국 몰래 하거나 개인 시간을 희생해서 짬짬이 하게 되고, 인정받지 못하기 때문에 의욕이 생길리 만무하다.

고급 인력인 개발자들의 좋은 두뇌를 활용하지 못하고, 반대로 창의성을 저하시키는 이런 문화는 하루 빨리 고쳐져야 한다. 말로만 소프트웨어가 중요하다고 하지 말고, 소프트웨어 개발의 특성과 개발자들을 이해하려는 노력이 절실하다.

두서 없지만 이 글은 이쯤에서 마무리하기로 하고, 유사 주제로 작성하고 있는 글이 있으니 그 쪽에서 정리를 시도해보기로 하겠다. ^^


References

  1. Pros and Cons of Google’s 20 percent time concept? (Ask MetaFilter)
  2. Top 20 Percent Projects at Google (eWeek.com)
  3. The Go Programming Language (Google, golang.org)
  4. 애플 “한국을 모바일 테스트베드로 활용” (아시아 경제)
  5. Principles of Agile Architecture (wegra.org)
Testable Design Anti-Patterns
Dec 17th, 2009 by Wegra Lee

한 4년쯤 전에 정리를 시작했던 주제로, 지인 몇 명과 논문으로 써볼까 기고를 할까 고민하다 제 때 빛을 보지 못했다. 실례를 찾아 보강하는 것이 가장 큰 숙제였는데, 불행히도 그 후 테스트 관련 업무를 접할 기회가 없었어서 기고까지는 하지 못했다.

Testable Design Fundamentals

In other to make software testable, designer should follow some rules. If you are interested in software design, some of them must be familiar to you. That’s because they are rules for improving software ‘design‘ in testability perspective. Lets look into the rules one by one.

  1. Clear Specification: Specification should cover all possible situations even for illegal conditions. Moreover, it must be clearly defined without any ambiguous sentences.
  2. Controlability: Target should provide mechanisms to read/write the conditions or to run the operations which are required to verify its functionality. It should be easy enough to implement.
  3. Modularity: Adequate modularization is one of the fundamental requirements not only for design, implementation, and reuse, but also for test. For an example, a module which has many relationships with other modules is hard to test independently. It should use stub/mock object of wait until the dependant modules are implemented.
  4. Readability: Easy and intuitive naming reduces human errors and decreases design/implementation time. It makes overall testing time shorter.
  5. Consistency: All of the above rules should be reflected consistently so that the software can be look like designed and implemented by one person.

Software which satisfies these rules can be called testable. However, it is very hard to measure quantitatively how well the rules are reflected. I’ll remain this issue for other dedicated articles.

(in Korean)

테스트 가능한 소프트웨어를 만들기 위해서는, 설계자는 몇 가지 규칙을 따라야 한다. 소프트웨어 설계에 관심있는 사람이라면, 친숙한 이름의 규칙들을 찾을 수 있을 것이다. 이유인 즉, 이 규칙들은 테스트의 관점에서 소프트웨어의 ‘설계‘를 향상시키기 위한 지침이기 때문이다. 그럼 그 규칙들을 하나씩 살펴보도록 하자.

  1. 명확한 기능 명세: 소프트웨어 명세서는 잘못된 상황까지 포함한 모든 가능한 상황을 기술해야 한다. 이 때, 의미가 분명한 문장만을 사용해야 한다.
  2. 조작성: 테스트 대상은 그 기능 동작 여부를 판단할 수 있는 정보를 읽고/쓰거나 기능을 동작시킬 수 있는 메커니즘을 제공해야 한다. 또한 소프트웨어 적으로 쉽게 구현할 수 있어야 한다.
  3. 모듈화: 적절한 모듈화는 소프트웨어 설계, 구현, 재활용 뿐 아니라 테스트를 위해서도 꼭 필요하다. 예를 들어, 다수의 다른 모듈과 종속성이 있는 모듈은 독립적으로 테스트하기 어렵다. Stub/Mock Object를 사용하거나, 타 모듈들이 구현되기까지 기다려야 한다.
  4. 가독성: 쉽고 직관적인 이름(모듈, 함수 등 모든 경우에 해당됨)은 휴먼 에러(human error)를 줄여주고, 설계와 구현 시간을 단축시킨다. 결과적으로 테스트에 소요되는 전체 시간을 단축시키는 효과가 있다.
  5. 일관성: 이상의 모든 규칙들은 마치 한 사람의 설계하고 구현한 것처럼 일관성 있게 적용되어야 한다. 각각의 모듈마다 그 정도가 다르다면 휴먼 에러(human error)를 증가시킬 것이다.

이상의 규칙들이 충족된 소프트웨어라면 ‘테스트할 수 있다’고 얘기할 수 있을 것이다. 단, 이런 규칙들이 얼마나 잘 반영되었는지를 정량적으로 측정하는데는 분명한 한계가 있다. 측정 이슈는 주제의 범위를 벗어나므로 본 글에서는 더 자세히 다루지 않을 것이다.

(Read the full article)

Dependency Injection
Dec 7th, 2009 by Wegra Lee

Spring Framework 관련 한글책[1]을 보다가 Dependency Injection(DI)의 번역에 대한 역자주에서 생각해볼 만한 것이 있어 보충 설명 겸, 잠시 끄적여본다. DI의 개념이 아직 모호한 사람이라면 이 글을 먼저 읽어본 후 참조한 wikipedia[2]의 글을 보면 전체적인 윤곽을 잡는데 도움이 될 것이다.

역자주: 종속객체 주입, 즉 DI(dependency injection)는 스프링에서 가장 기본이 되면서도 매우 중요한 의미를 갖는다. 기존에 가장 많이 쓰이던 번역은 ‘의존성 주입’이었다. 그러나 역자가 보기에 이 번역은 ‘없는 의존성을 만들어 주입한다’는 오해를 일으키고, 이 때문에 DI의 이해가 어려웠다고 생각한다. DI는 없는 의존성을 주입하는 것이 아니라 의존성은 이미 존재하되, 실제 객체가 필요로 하는 종속객체를 주입하는 것을 의미한다. 따라서 ‘의존성 주입’보다 뜻이 명확하도록 ‘종속객체 주입’이라고 번역했다. .. (후략)

일단.. 이 역자가 잘 봤다고 말하고 싶다. DI에서 dependency를 의존성으로 번역한 것은 확실히 잘못된 것이다. UML, OOAD 등 지금까지 IT 기술 관련 대부분의 상황에서는 모듈 간 관계를 명시하기 위해 dependency라는 용어를 사용해왔기 때문에 여기서도 습관적으로 의존성이라는 단어를 선택했으리라 생각한다. 하지만 단어는 문맥에 따라 여러 가지 의미를 가지고 있음을 잊어서는 안 된다.

DI를 직역하더라도 의존성 주입은 아니다. 오히려 위 역자가 사용한 종속객체 주입이 더욱 적절하다. 약간 아쉬운 게 있다면 DI는 object 세상에 국한되지 않기 때문에 ‘객체’라는 말은 여전히 논란의 소지가 있다는 점 정도. 범위를 제한하지 않으려면 module 정도의 추상적인 용어를 사용할 수 있겠지만, object라는 용어도 꼭 OOP에서 말하는 object로 제한되는 것은 아니니 상관없을 듯하다.

그럼 다시.. 왜 직역을 했는데도 의존성은 틀린 것일까? 이는 DI 패턴의 구조[2]를 살펴보면 쉽게 이해가 된다. DI 패턴은 세 개의 요소로 구성된다. 적절한 한글 대용어를 찾기 어려워 대강 번역해보면 이렇다.

이 패턴은 최소 3개의 구성 요소로 이루어진다: dependent와 그의 dependencies, 그리고 injector (혹은 provider, container). Dependent 는 컴퓨터 프로그램에서 작업(task)을 수행해야 할 소비자(consumer)다. 작업 완료를 위해선 특정 부작업들(sub-tasks)을 수행하는 다양한 서비스(dependencies)들을 활용해야 한다. 마지막으로 Injector는 dependent와 그에 필요한 dependency들을 조합해서 작업을 수행할 준비를 갖추는 컴포넌트로써, 이 객체들의 전반적인 라이프 사이클을 관리하기도 한다.

위 설명에서와 같이 DI의 dependency는 객체 간의 관계 속성을 의미하는 것이 아니라 서비스를 제공해주는 ‘실체’를 가리킨다. 그 실체를 (의미 그대로) dependent에서 주입시켜주는 것이다. 위 역자의 용어에 맞추어보면 대략 이렇게 되지 않을까 싶다.

  • Dependent: 의존객체
  • Dependency: 종속객체
  • Injector: 주입자
  • Dependency Injection: 종속객체 주입

이 용어들을 적용해 다시 번역해보면 아래처럼 되겠다. 깔끔하려나.. ^^

이 패턴은 최소 3개의 구성 요소로 이루어진다: 의존 객체(dependent)와 그의 종속 객체들(dependencies), 그리고 주입자(injector, provider, or container). 의존 객체는 컴퓨터 프로그램에서 작업을 수행해야 할 소비자다. 작업 완료를 위해선 특정 부작업들을 수행하는 다양한 서비스(종속객체)들을 활용해야 한다. 마지막으로 주입자는 의존객체와 그에 필요한 종속객체들을 조합해서 작업을 수행할 준비를 갖추는 컴포넌트로써, 이 객체들의 전반적인 라이프 사이클을 관리하기도 한다.


References

  1. Spring in Action SE (Craig Walls / 장시형, 전지훈 / Manning)
  2. Dependency injection (wikipedia)
The Go Programming Language
Nov 12th, 2009 by Wegra Lee

Noop 에 이어 구글이 또 하나의 프로그래밍 언어를 소개했다. Go 라는 이름의 언어로, 벌써 제법 성숙한 상태라, 과거 Turbo C 나 초창기 UltraEdit 로 Java 코딩하던 추억을 떠올리면 충분히 직접 사용해볼 수 있는 수준이다.

Go 언어는 크게 두 가지 관점에서 아주 마음에 든다.

처음부터 다시 시작했다.

현재의 것에 계속해서 군더더기를  붙여 나가다 보면 점차 비대해져 관리를 어렵게 하고 혁신을 가로막는다. 때문에 종종 처읍부터 다시 시작하는 것이 필요하다.

번외로.. 구글이나 애플과 같은 회사가 이런 일을 참 잘하는데, 최근 40살 된 e-mail 을 대체하겠다며 큰 기대를 받고 있는 Google Wave 나 이미 성숙할 대로 성숙한 핸드폰의 개념을 바꿔버린 iPhone 같은 것이 좋은 예이다.

덕분에 더 이상 필요 없거나 폐해가 더 큰 기능들을 과감히 없애 날렵해짐과 동시에 병렬 프로그래밍과 같이 새시대에 필요한 기능들을 실장하고 있다.

C/C++ 를 경량화시켰다.

내가 가장 혐오하는 프로그래밍 언어인 C++. C++ 는 기능 확장이 아니라 축소를 먼저 해야 한다고 내가 몇 년 전부터 얘기하고 다녔는지 모른다. 물론 Go 가 C++ 의 직접적인 축소판은 아니지만, 같은 C 계열 (호환이 아니라 파생의 의미로) 언어로써 정말 최소한부터 다시 시작하는 언어이다. 홈페이지에서 언급한 몇몇 설계상의 결정 사항들은 정말 내 가슴을 후련하게 해준다. 포인터 연산, exception, assertion, 오버로딩 등에 대한 설명은 나의 그간 경험에 비춰 특히 공감이 된다. 수많은 개발자들.. 아무리 설명을 해줘도 계속 교체되고, 새로온 사람들에게 또 설명하고.. 그러다 지쳐 포기했던 기억이.. ^^

Go 가 잘 발전해서 C++ 의 영향력이 감소하는데 한 몫 해주길 바래본다.


References

  1. Official Site
  2. Tech Talk (YouTube Video, 1 hour)
  3. PDF for the above Tech Talk

잡설.

소프트웨어 플랫폼 경쟁에 있어 우리 나라 기업들은 도저히 상대가 안되는게.. 애플이나 구글같은 회사는 핵심 소프트웨어 개발 역량이 정말 대단하다. 우리처럼 API 겉만 감싸는 수준이 아니라, 필요하면 언어 자체를 수정하거나(최근 GCD 를 위해 C 언어를 확장함) 심지어 만들어버리기까지 한다(Go, Objective-C). 애플은 LLVM/CLang 이라는 GCC 대체 프로젝트를 진행해 큰 성취를 보이고 있고, 구글 가상 머신을 뜯어 고쳤다(Dalvik VM). 소프트웨어 설계 자체도 참 대단하다. 이미 25년의 OS 개발 경험을 축적하고 있는 애플의 OS X 는 전신인 NextStep 시절부터 소프트웨어 설계의 바이블 격인 Design Patterns 책 저술에 지대한 영향을 미쳤다. 현재의 플랫폼도 깊이 들여다보면 ‘우와!’ 하는 감탄사가 절로 나오는 멋진 구조를 어렵지 않게 찾아볼 수 있다.

핵심 기술이 없으면 오픈 소스/커뮤니티에라도 적극 참여해 그들의 노하우를 최대한 빨리 흡수하며 기반을 쌓는 것이 큰 도움이 된다. 모토롤라의 이번 Droid 출시가 좋은 예이다. 작년, 큰 위기를 느낀 모토롤라는 진행중이던 프로젝트들을 대다수 취소하고 회사 전체를 환골탈퇴하는 모험을 감행했다. 덕분에 신규 제품이 나오지 않아 시장 점유율을 급격하게 떨어졌지만, 구글과의 밀접한 협력으로 Android 2.0 폰을 타 기업들보다 몇 개월 앞서 출시할 수 있었다. 결과는 더 지켜봐야겠지만, 구글과의 돈독한 관계뿐 아니라, Android 시장에서 적어도 당분간은 타 업체들을 선도할 수 있는 충분한 노하우를 얻었을 것이다.

허나 안타깝게도 우리 나라 기업들은 이런 방향으로의 가시적인 움직임은 보이질 않는다. 티맥스 윈도우나 삼성 바다의 예에서 보듯이, 오히려 독자적인 플랫폼을 시도하고 있다. 아직은 실체가 드러나지 않아 확언할 수는 없지만, 중학생들에게 ACM 이나 IEEE 논문을 쓰라는 격으로 보인다. 유사하거나 오히려 더 참신한 아이디어가 나올 수는 있으나, 그 깊이와 완성도는 비교할 수 없을 것이다. 유수의 석학들과 경쟁해 진정 그들을 뛰어 넘고자 한다면, 조급함을 억누르고 차근히 기초를 닦는 인내의 기간이 필요하다.

[개발자 역량] 예외 안전성
Nov 2nd, 2009 by Wegra Lee

이번엔 예외 안전성(exception safety)에 대해 간략히 정리해보겠다.

좋은  플랫폼/라이브러리/모듈 등을 만드는데 꼭 고려해야할 요소들 중, exception safety 는 그 인지도가 특히 낮다고 할 수 있다. 정상적인 상황에서는 부각되지 않고, 한 번 문제가 발생하면 원인을 찾기 어렵다는 점은 thread safety 와도 비슷하다. 이러한 특성 때문에 견고한 플랫폼을 만들고자 하는 사람들은 반드시 염두해 두어야 한다.

상대적으로 가벼운 프로젝트에서라도 틈틈히 적용하여 체화시켜두면 나중에 시행착오를 줄일 수 있다. 항시 완벽하게 만들려는 것은 노력 대비 얻는 것이 적을 수도 있으니, 코드 리뷰를 하면서 종종 exception safety 관점에서 들여다보는 방식을 권해본다.

다행히도 이 주제는 이미 Wikipedia 에 잘 정리되어 있으므로[1], 한글 번역 + 약간의 부연 설명 수준에서 마무리하겠다.


Exception safety

특정 코드 블럭 안에서 실행중 실패(failure)가 발생해도 잘못된 작용을 일으키지 않는다면, 우리는 그 코드 블럭을 exception-safe 하다고 한다. 잘못된 작용의 예로는 메모리 누수, 변질된(garbled) 데이터/상태 저장, 잘못된 결과 반환 등이 있다. Exception safe 코드는 예외가 발생한 상황에서도 그 코드상에서의 불변성(invariant [2])을 만족시켜야 한다. 그럼 exception safety 를 레벨에 따라 몇 개로 나눠보자.

  1. Failure transparency (no throw guarantee): 예외 상황에서도 요청된 기능이 반드시 성공하고, 그 외 모든 요구사항(성능, 메모리  사용량 등)도 만족시킴을 보장한다. 발생한 예외도 위로 전파시키지 않고 투명하게 처리된다. (가장 이상적인 exception safety)
  2. Commit or rollback semantics (strong exception safety, no-change guarantee): 기능은 실패할 수 있으나, 그로 인한 어떠한 부작용(side effect)도 발생하지 않는다. 모든 데이터/상태는 원래의 값을 보존한다.
  3. Basic exception safety: 기능 수행 도중 일부 과정에서 부작용을 유발시킬 수 있다. 단 불변성(invariant)은 그대로 유지된다. 관련 데이터들 중 일부가 변경되었을 수는 있어도, 단위 데이터 각각은 유효한 값이어야 한다. 즉 invalid sate 에 빠지거나 스팩상 허용되지 않는 잘못된 데이터를 가지고 있어서는 안된다.
  4. Minimal exception safety (no-leak guarantee): 기능 실패의 결과 유효하지 않은 데이터를 가질 수도 있으나, 최소한 크래쉬, 자원 누수는 발생하지 않는다.
  5. No exception safety: 어떠한 것도 보장하지 않는다. (최악의 exception safety)

예를 들어, C++의 std::vector 나 Java 의 ArrayList 와 같은 벡터를 생각해보자. 아이템 x 를 벡터 v 에 넣으면, 벡터 v 는 x 가 내부 객체 리스트에 추가되고, 총 객체 수를 의미하는 count 값을 1 만큼 증가시켜야 한다. 또한 확보해놓은 메모리가 충분치 않다면 새로 메모리를 할당하는 작업도 필요하다. 이 메모리 할당 작업은 실패할 수 있고, 그렇다면 예외가 던져질 것이다. 마지막 이유로 벡터를 failure transparency 레벨로 구현하기란 굉장히 어렵거나 혹은 불가능할 수도 있다. 다행히 strong exception safety 정도를 제공하는 것은 그다지 어렵지 않은데, 동작에 실패하더라도 v 를 이전과 동일한 상태로만 유지하면 된다. 만약 basic exception safety 만 보장하도록 만들어진 벡터라면, v 는 x 를 포함할 수도, 아닐 수도 있다. 단, 어느 경우건 포함 여부와 count 값 사이는 일관된 상태를 유지한다. 반면 minimal exception safety 만 보장하는 벡터에서는 예외가 발생하면 v 는 잘못된 상태에 놓일 수 있다. 예를 들면, x 가 v 에 포함되지 못했음에도 count 는 증가된 상태로 남을 수도 있다. 마지막으로 no exception safe 백터는 프로그램을 크래쉬 시킬 수도 있다. 메모리 할당에 실패했음에도, 이를 확인하지 않고 잘못된 메모리 주소에 데이터를 쓰는 경우가 이에 해당한다.

일반적으로  ’최소한’ basic exception safety 는 보장해 주어야 한다. Failure transparency 가 이상적이긴 하나 구현하긴 만만치 않다. 어플리테이션에 대한 완벽한 정보게 제공되지 않는다면 라이브러리가 이를 보장하기란 대부분 불가능하다.


첨언..

아무런 생각 없이 구현했다면 no exception safety 라 말하는 것이 안전하다. 비록 API 에 따라서는 더 높은 safety 를 보장하는 경우도 많이 있더라도, 전체의 safety 는 가장 낮을 레벨에 좌우될 수 밖에 없다. 물론 모든 API 를 리뷰하여 최소 safety 가 어디인지를 파악한다면, 그 이상의 safety 를 보장한다고 말할 수 있다.

메모리 관리와 null pointer 체크 정도를 신경썼다면 minimal exception safety 정도라 이야기할 수 있다. 또한 대부분의 정적 분석 툴들[3]은 resource leak 과 잘못된 메모리 접근 문제를 검출해 주므로, minimal exception safety 보장하는데 많은 도움이 된다.

Basic exception safety 수준까지 끌어 올리려면 메모리와 포인터뿐 아니라 인스턴스의 속성(property, field, or private member variable)의 변화까지 신경써야 한다. 한 함수 내에서 두 개 이상의 속성을 다루고, 뒷의 속성 조작 중 예외가 발생하더라도 invariant 조건이 만족되도록 신경써야 한다.

Commit or rollback semantics 는 어느 단계에서 예외가 발생했더라도 원상태 그대로 복구시킬 수 있어야 한다. 즉, 문제가 된 동작을 애초부터 시도하지 않은 것과 동일한 결과를 낳아야 한다. 종종 invariant 를 신경쓰는 것보다 수월하게 구현할 수도 있지만, file 이나 database 를 건드리거나 네트워크로 서버에 요청을 보내는 등의 동작이 포함된다면 결코 쉬운 작업이 아니다. 더욱이 multi thread 환경이라면 동기화까지 고려되어야 한다.

아주 특별한 경우가 아니면 Failure transparency 보장을 요하는 경우는 없으니, 자신이 mission critical 분야에 종사하고 있다고 생각하지 않는다면 잊어도 좋다.

추천 전략

대부분의 프로젝트에서는 아래 정도의 전략이면 충분히 만족스러운 결과를 얻을 수 있을 것이라 믿는다.

  1. 전체 API 의 basic exception safety 를 기본 목표로 한다.
  2. 핵심이 되는 모듈과 주요 데이터를 다루는 모듈을 미리 식별하여 최대한 commit or rollback semantics 을 보장하도록 노력하되, 집착할 필요는 없다. 단, 보장 레벨이 달라질 경우 문서나 코드 상에 annotation 주석을 달아두자[4].
  3. 정적 분석 툴을 적극 도입하여 human error 를 빨리 잡아내고, 최소한의 방어책으로 활용한다.

References

  1. Exception safety (Wikipedia)
  2. Invariant (Wikipedia)
  3. List of tools for static code analysis (Wikipedia)
  4. [개발자 역량] 주석.. 다느냐 마느냐 그것이 문제로다 (Bug Inside)
권장 리팩터링 순서
Oct 29th, 2009 by Wegra Lee

최근 다른 사람이 작성한 소스를 리뷰하면서 리팩토링할 일이 있었다. 깊이 있는 리팩토링까지는 해보지 못했으나, 남의 코드 리팩토링은 오랫만이라 간단히 노하우를 정리해본다.

정체를 알 수 없는 임의의 소스 덩어리를 받게 된다면, 대략 아래와 같은 순서로 리팩토링하는 것이 효율적이다.

1. Make duplications

리팩토링의 첫 단계에서는 중복 코드를 생산해낸다. 헐~! 이게 뭔 소린가? 중복 코드를 만드는 데는 조건이 있다. 로직의 상당 부분이 동일 혹은 유사해야 한다.

코드를 살펴보면, 결국 똑같은 혹은 유사한 일을 하면서 따로 노는 모습을 심심치 않게 발견할 수 있다. 각 파트별 담당 개발자가 선호하는 방식이 달라서일 수도 있고, 같은 개발자더라도 나중에 더 나은 API를 찾거나 더 간소한 알고리즘이 떠올랐을 수도 있다. 혹은 전에 짰던 부분과 흡사하다는 걸 떠올리지 못한체 처음부터 다시 짤 경우도 같은 결과가 나타난다.

코드가 척 보기에도 너무 다르다면 눈썰미가 본좌급이거나 운이 좋아야 발견할 수 있다. 따라서 누구나 기계적으로 할 수 있는 방법을 알아보자. 바로 이미 유사한 코드를 찾아서, 동일한 로직으로 만들 수 있는지 보는 것이다. 시장에는 중복 코드를 찾아주는 툴들이 이미 많이 나와있다. 이들 중 더 발전된 것은 유사 코드를 찾아주기도 한다. Java 라면 PMD 나 CheckStyle 같은 툴을 기본으로 사용하고 있으리라 믿는다. 이들 툴의 여러 기능중 duplicated code 검출 기능도 있으니 이를 활용하자. C/C++ 의 경우 언어 한계상 지원 툴이 상당히 빈약한데.. 다행히 위의 PMD 가 C++ 역시 지원해준다. 사용법은 여기를 참조하기 바라며.. 본인도 이를 이용해 작업을 진행하였다. 버그는 좀 보이지만, 간단히 사용하기에는 부족하지 않다.

결과: 같은 일을 처리하는 코드는 모두 동일하게 작성되었다.

2. Remove duplications

다음 단계가 바로 1단계에서 만들어 놓은 (혹은 그 전부터 존재하던) 중복 코드 제거이다. 코드가 중복되어 있다면 리팩토링도 중복으로 해야하고, 각각의 리팩토링 과정을 완전히 동일하게 하기는 상당히 말처럼 쉽지 않다. 중복 코드를 개별적으로 수정하면, 중복된 코드라는 것 자체를 인지하기 어려워진다. 각 코드 블럭의 리팩토링 순간 사이에 시간적 격차가 있으므로, 같은 로직을 보더라도 다르게 리팩토링할 가능성이 생긴다(기계가 아닌 사람인지라). 논리적으로 똑같이 하려해도 human error 가 발생할 여지도 다분하다.

중복 코드 식별 역시 ‘다행히도’ 사람이 하기엔 너무나 벅찬 일이다. 그래서 툴이 필요한데, 1단계에서 사용한 툴들이 바로 그들이다.

결과: 모든 코드는 unique 하다.

(곧 별도의 글을 통해 중복 코드 제거를 위한 몇 가지 패턴은 선보이도록 하겠다.)

3. Remove unused code (variable, code block, method, class, etc)

사용되지 않는 코드는 분석 중 논리 흐름을 방해하며, 자칫 의미 없는 코드를 리팩토링하며 시간을 낭비하게 된다.

대부분 범용 언어는 툴이 잘 지원해주고 있으나, 검출 범위와 사용성은 편차가 좀 있는 편이니 자신의 환경에 접목 가능한 툴들을 한 번 쯤 조사해보는 것을 추천한다.

결과: 모든 코드는 의미를 갖는다.

4. Reformat and rename

코드의 기초 가독성을 높이는 작업이다. 맞춤법에 맞게 코드를 수정하고(reformating), 의미가 명확히 통하는 용어를 사용하게 한다(renaming). Renaming 의 대상은 변수명, (내부) 메서드/클래스명 등이다.

결과: 코드는 의미가 명확한 단위 요소로 구성된다.

5. Refine logic (including extract/inline method)

코드의 논리 흐름을 가다듬는 단계이다. 작문에 비유해보자. 쉬운 용어들을 사용하더라도 산만하거나 애매모호한 글에선 핵심을 찾기 어렵다. 반면 논리 정연한 글은 하고자 하는 바를 쉽게 이해할 수 있다. 따라서 그 논리에 결점이 있다면 바로 파악하여 수정할 수 있고, 논리를 수정하거나, 설명을 보강하는데도 용이하다.

이 과정에서는  이어진 긴 문장을 의미가 분명한 적절한 단위로 나누거나, 큰 문단을 통채로 하나의 추상적 문장으로 교체(extract method)하고, if/else/switch 등으로 복잡해진 흐름을 같은 의미의 더 간결한 논리로 수정하는 등의 작업이 이루어진다. 최종 목표는 주석 없이도 글을 읽듯이 코드를 술술 읽을 수 있도록 하는 것이다.

결과: 코드를 자연스럽게 읽을 수 있다.

Refactoring Home Page 에 가면 수많은 리팩토링 기법들을 찾을 수 있다. 관리자인 Martin Fowler 도 양해를 구하고 있듯이, 설명과 관리에 그리 많은 신경을 쓰고 있진 않지만, 틈틈히 살펴보면 제법 도움이 될 것이다. 내 나름의 기법과 함께 위의 각 단계에서 많이 쓰임직한 것들을 묶어서 정리해보는 것도 재미있을 듯하니, 나중에 시간이 나면 한 번 시도해봐야겠다.

[개발자 역량] 주석.. 다느냐 마느냐 그것이 문제로다
Oct 7th, 2009 by Wegra Lee

(특히) 팀으로써 소프트웨어를 개발하다보면 주석을 잘 달라는 이야기를 자주 듣는다. 하지만 반대로 주석이 필요없는 코드를 짜라는 이야기 역시 심심치 않게 들려온다. 얼핏 생각하기엔 서로 모순되는 주장 같기도 한 두 가이드들에 대해  정확히 이해해보고, 또 좋은 주석을 달기 위한 팁과 고려 사항들까지 일부 정리해보도록 하겠다.

주석의 역사

이야기를 시작하기 전에 주석에는 어떤 종류가 있는지부터 명확히 해야 하는데, 주석의 역사에서 시작하는 것이 자연스러울듯 하다.

놀랍게도(?) 태초의 소프트웨어 코드에는 주석이란 개념이 아얘 없었다. 그도 그럴 것이 종이카드에 0011000 구멍 뚫어서 프로그램하던 시절에 주석을 삽입한다는 것은 사치일뿐 아니라 기술적인 도적이었다. ^^ 이 시절 펜으로 종이 위에 이것저것 적어놓던 것이 주석의 시작이라고 보면 된다. (사실 이 부분은 직접 경험해보진 않아서 추측성임)

컴파일러(사실 전처리기)가 주석을 인식하기 시작한 것은 당연히 소스 코드를 디지털화시킨 이후이다. 이마저도 처음엔 지금과 같은 형태는 아니었다. 초창기 언어들은 주로 line by line 으로 해석되었기 때문에 주석 역시 single-line 형태가 먼저 등장했다. 복잡한 인터페이스 설명이나 알고리즘 설명 같은 것보다는 영역/블록 구분과 간단한 커맨트 중심이어서 그리 불편하지 않았다. 그 후 등장한 multi-line 주석은 편집 편의성 증대를 위한 욕구 해결 정도여서 전혀 신선하지도 혁신적이지도 않았다.

Java가 등장하면서 (최초인지는 모름) API 문서 생성 ‘표준’ 툴(Javadoc)을 SDK 가 내장하기 시작하였고, 이는 주석의 개념을 한 단개 발전(not 혁신)시키는 계기가 되었다. Javadoc 의 특징이라면 ‘스펙 기술용 주석‘과 ‘구현 설명용 주석‘을 명백히 구분하기 시작한 것이다. 물론 그 전부터 주석을 구분해 사용하고는 있었지만, 언어의 표준적인 방식으로 도입했다는 것이 큰 의미가 있다.

이를 계기로 주석에 대한 인식은 한층 개선하였고, 그 후 등장하는 대부분의 언어들도 같은 어프로치를 취하게 되었다. 하지만 아직까지 컴파일러는 스펙/구현 가리지 않고 모든 주석을 무시하였고, 스펙을 얻어내려면 별도의 툴을 돌려야만 하였다. 이는 ‘스펙 기술’ 이라는 오늘날에 있어 아주 중대한 개념을 ‘주석’ 이라는 사소한 범주에 함께 묶는 한계에서 아직 벗어나지 못했음을 의미한다.

이에 대한 개선은 최근 Noop 언어 프로젝트를 통해 가시화되고 있다. Noop 의 여러 특징 중 ‘Executable documentation that’s never out-of-date’ 는 주석을 프로그래밍 언어 스펙에 포함시키고 컴파일러가 컴파일 워닝/에러와 같은 수준에서 직접 핸들링한다는 의미이다. 형식적으로는 거의 변화가 없지만, 주석의 위상은 거의 코드 수준까지 격상되게 된다.

나아가 annotation 이라는 개념이 추가된다. Annotation 을 주석으로 한데 묶는데는 의 아해할 사람들이 있을 수 있겠으나, 수행 코드가 아니라는 점에서 주석의 범주에 속한다. 더구나 annotation 의 한글 해석 자체가 ‘주석( 달기)’이다. ^^ (Noop 에서 annotation 을 어떻게 처리할 지는 명시적인 언급은 없다.)

주석의 중요성이 점차 커지는 것은 소프트웨어 산업의 변화 과정을 봤을 때 아주 자연스러운 결과이다. 개인이 다룰 수 있던 작은 소프트웨어에서, 이제는 수백 수천명이 수년에 걸쳐 만들고, 완전히 없어지기까지 또 수년이 더 필요한 수준까지 변화하였기 때문에, 공간과 시간을 초월한 개발자간의 커뮤니케이션이 그만큼 중요해진 것이다.

주석의 분류

이쯤에서 현재 우리가 주석이라 부를 수 있는 것들에는 어떠한 것들이 있는지 다시 정리해보자.

  • 문서화 주석 – API 명세(specification) 기술을 주목적으로 하며, 해당 소프트웨어를 black box 로 바라보는 제3의 개발자와의 커뮤니케이션을 담당한다.
  • Annotation 주석 – 코드를 처리하는 툴들에게 특정 지시를 내리는 주석으로, 이를 인식할 수 있는 툴에게만 의미가 있다. 툴의 목적에 따라 다양한 용도로 응용될 수 있다.
    또한 팀원간에 정해진 약속으로 사용하기도 좋다. 대표적인 사례로는 @todo @fixme 같은 것이 있다.
  • 구현 주석 – 내부 구현에 대한 주석으로 여타의 툴들에서는 완전히 무시된다. 이해하기 어려운 코드에 대한 설명, 추가로 해야할 일, 코드 수정 시 주의점 등, 코드 레벨에서 직접 봐야하는 개발자들 간의 디테일하고, 종종 자질구레한 커뮤니케이션을 담당한다.

이처럼 현재까지는 크게 3 가지의 대표적인 주석이 있으며, 소프트웨어 산업이 변화하면서 앞으로도 기존 주석이 변화/갈라지거나 새로운 형태의 주석이 추가될 것이다.

상세한 주석 vs. 주석이 필요 없는 코드

“상세한 주석이 좋다.” – 주로 문서화 주석에 해당하는 지침이다. 제공하는 소프트웨어 모듈의 동작 명세는 가능한 모든 케이스에 대해 명확하게 설명해 주는 것이 좋다.

“주석이 필요 없도록 코딩하라.” – 주로 구현 주석에 해당하는 지침이다. 구현 주석이 있다는 것은 코드만으론 의미 전달이 불충분하다는 반증이므로, 가능한 코드를 명확히 작성하여 구현 주석 없이 읽고 이해할 수 있게 하는 것이 좋다.

보통은 위와 같이 받아들이면 되지만, 반대의 경우도 얼마든지 있다.

문서화 주석이 최소화 되도록 코딩하라.” – 이는 문서화 주석을 생략하라는 의미보다, ‘문서화 주석에서 군더더기 설명이 필요 없도록 API 를 명확하게 만들라’ 정도로 이해해야 한다. 문서 없이 API 만 봤을 때 오해의 소지가 있다거나, 다양한 예외 상황이 존재하거나, 한 API 를 너무 다목적으로 사용할 수 있다거나, pre/post 조건을 주렁주렁 달고 있는 API 들은 이 가이드를 제대로 지키지 못한 예들이다.

“상세한 구현 주석이 좋다” – 어쩔 수 없이 구현 주석이 필요할 시에는 명확하고 상세한 설명을 달라는 의미이다. 최적화를 위해 어쩔 수 없이 복잡한 코드를 작성했다거나, 컨트롤 밖에 있는 외부 종속성 때문에 비효율적인 구조를 가져갈 수 밖에 없었다거나, 일정 등의 이유로 임시 코드를 넣어놨거나 일부 기능을 구현해놓지 않은 상황에서는 상세한 주석을 달아주어야 한다. (이 주석을 보게될 사람이 자신뿐이라는 가정은 절대 하지 말라.)

좋은 주석 작성을 위한 팁

좋은 주석 작성 팁까지 집대성 해놓으면 최고의 글이 되겠지만.. 이 글의 범주에서는 제외하도록 하겠다. (다른 누군가 집대성 해주시거나, 이미 잘 되어 있는 좋은 링크들을 보내주시면 해피할텐데 ㅎ)

  • 문서화 주석 작성에 대한 가이드는 표준화 단체에서 공개하고 있는 것이 많이 있다. 적어도 문서화가 잘 된 유명 플랫폼/라이브러리 등은 찾을 수 있다. 자신의 도메인과 가장 유사한 가이드를 찾아 적용해보는 것은 좋은 시작이 될 것이다. (e.g. Requirements for Writing Java API Specifications)
  • Annotation 주석 활용 – 같은 목적의 주석들에 동일 키워드를 사용하는 방법으로, annotation 이 정식으로 지원되지 않더라도 전혀 문제는 없다. 가장 추상화된 @todo (더 해야할 일) 부터 시작해서 @fixme (명백한 결함), @review (리뷰 커맨트), @pending (미결사항), @thread (스레드 안정성 보장 필요), @dependon (특정 기능/이슈 종속적 – 해당 기능/이슈 해결 후 일괄 검토 요구) 등 상황에 따라 적절히 응용하면 최소한의 주석으로 커뮤니케이션 효과를 극대화시킬 수 있다.
  • Aspect 를 고려한 코드 영역 구분 – 코드를 작성하다 보면 로그 작성, 접근 권한 체크, 멀티 스레딩을 위한 락 처리, 에러 처리, 부하 조정, 캐시 사용 등등 비즈니스 로직과 직접적으로 관련되지 않은 로직들로 코드가 지저분해지기 쉽다. 이 문제를 다루기 위해 Aspect Oriented 언어를 사용하는 방법도 있지만, 여건이 되지 않는 경우엔 어쩔 수 없이 모든 로직을 한데 몰아 두어야 한다. 이럴 때에는 아쉬운데로 부가적인 로직 처리 블럭과 비즈니스 로직 블럭을 구분해주는 안을 적용해볼 수 있다. 동일 룰이 코드 전반에 일괄적으로 적용되어 있다면, 코드를 보는 사람들은 목적에 맞는 코드 블럭만 정확히 찾아 검토할 수 있다.
    영역 구분을 위한 구현 주석이 필요할 수도 있지만, 그 외 구현 주석의 필요성을 감소시켜주는 효과가 있다.
  • 예외 처리 코드 주의 – 한 눈에 들어올 간단한 로직도 과도한 예외 처리가 가미되면 금새 난해한 코드로 둔갑한다. (필요악이라 할 수 있는 예외에 대한 다양하고 심도있는 관점은 C++ Exceptions: Pros and Cons 을 참조하기 바란다.) 가장 선행되어야 하는 노력은 물론 ‘불필요한 예외가 발생하지 않도록 설계’하는 것이고, 꼭 필요한 예외에 대해서 일반 로직과 명확히 구분되는 형태로 처리해주는 것이 좋다. 예를 들어 예외 발생 여부 확인을 위해 전용 매크로를 정의해 ‘if’ 절을 사용을 피하는 것도 한 방법이다. ‘if’ 절은 일반 로직에서도 등장하기 때문에 로직을 따라가다 rollback 하여 다시 시작하게 만든다.
  • 읽을 수 있는 코드 작성 – 당연하지만 너무 일반적인 가이드이다. 수많은 종속 팁들이 있을 수 있지만 너무 길어질듯 하여 생략한다. (Developer Capacities – Writing Program 참조)

결론

주석을 주석으로만 취급하는 시대는 끝났다(끝내야 한다^^). 하지만 아직도 대부분의 개발자들 주석에 대한 인식은 가볍기만 하고, 진정한 가치와 중요성을  제대로 교육/훈련시키는 모습은 찾아보기 쉽지 않다.

이제부터라도 주석과 관련한 명확한 개념 정리와, 용도별 좋은 주석 작성을 위한 가이드를 정리해 소프트웨어 입문 초기부터 익히고 생활화할 수 있도록 지도해야 한다. 지금과 같은 소셜 개발 시대에서 훌륭한 개발자가 되려면 시공간을 넘나드는 4차원적인 커뮤니케이션 능력도 갖춰야 한다.

»  Substance: WordPress   »  Style: Ahren Ahimsa