Altruistic Programmer's Blog (KR)

이타주의 프로그래머의 블로그

Archive for the ‘test’ tag

MVC vs MVP

without comments

[옛날 블로그 글입니다. 2009.05.23]
[이 글은 MVC와 MVP에 대한 명확한 차이를 잘 모르고 쓴 글입니다. -_-;; ]
[MVC와 MVP에 대해 공부를 해서 비교해 놓은 글이 있으니 먼저 봐주세요~ ^^]

제가 좋아하는 구글 테스팅 블로그에 GUI 테스팅의 MVP가 되자라는 글이 있어서요. 좋은 내용이니 한 번씩 읽어보세요. 참 공감가는 얘기입니다.

설명하려면 그림은 하나 있어야겠는데, 구글 블로그에 있는 것 가져오려니 좀 찜찜하고 해서 그려봤습니다.

여기서 화살표는 데이타의 흐름이나 함수 호출이 아닙니다. 의존성이에요. 예를 들어, MVC에서 Model에서 상태 변화 이벤트가 나와서 View까지 갈 수 있지만, Model은 View를 몰라요. 의존성이 없지요. 그런 그림입니다. ( 사실 구글 블로그에 있는 그림이 더 좋아요 ㅎ)

결론은 오른쪽이 좋다는 거구요. 아무래도 의존성이 적으니 좋겠지요. View를 다른데서 재사용할 수도 있고, 유닛 테스트하기도 편하구요. 그런 얘기입니다. 구글 블로그에서 하는 말이요.

저도 참 공감합니다. 한 4년전에 C++로 MVC 기반의 라이브러리를 만들었었는데, 좀 어려웠어요. 자료도 많지 않고, 요새 나오는 언어나 프레임워크처럼 대놓고 MVC(MVP)를 지원하지도 않았구요. 그 이후로도 정답이 뭔지는 잘 몰랐습니다.

그런데, 이번에 애플의 코코아 프레임워크를 보니 MVC(MVP)를 잘 쓰고 있더군요. 그 4년전에 참고했던 돌고래 스몰토크의 구조와 비슷한 느낌이었습니다. 그 때는 잘 몰랐는데 이번엔 좀 알겠더라구요.

이번에 한 생각이 ‘아 View가 생각보다 독립적이어야 겠구나’ 입니다. OS의 커먼컨트롤들 만큼이나 순수하게 독립적이고 재사용하기 쉬운 모습의 View가 되면, MVC에서 겪은 문제들이 많이 없어지겠구나 싶었습니다. 그래서 이제야 MVC를 이해하겠구나라고 생각했었는데, 그게 아니라 MVP라는 이름으로 진화된 녀석이 있었나 봅니다. MVC와 MVP의 비교는 공부좀 해보고 다음 번에 올려보겠습니다~ ^^

Written by muscly

May 23rd, 2009 at 12:48 pm

Posted in 설계

Tagged with , , , , , ,

Writing Testable Code 4/4

with 2 comments

[옛날 블로그 글입니다. 2009.01.27]

구글의 가이드라인 Writing Testable Code를 정리하는 그 마지막 글입니다. 오늘은 짧네요.

다른 편은 아래 링크에서 확인 가능합니다.
결함 #1: 생성자가 실제적인 일을 한다
결함 #2: 협력 오브젝트 속으로 파고들기
결함 #3: 깨지기 쉬운 전역 상태&싱글톤

결함 #4: 클래스가 너무 많은 걸 한다

클래스가 너무 많은 책임들을 가지면 책임들 사이의 인터랙션은 클래스 안에 묻힙니다. 한 번에 하나의 책임만 건드리기 힘든 디자인이 됩니다. 또 테스트는 이런 인터랙션 사이의 명확한 솔기(경계)를 갖지 못합니다. 

여러분의 클래스가 의존하는 컴포넌트를 생성하는 것(오브젝트 그래프의 생성같은)은 클래스의 진짜 책임과는 구별되어야 하는 또 하나의 책임입니다. 디펜던시 인젝션을 사용해서 미리 준비해둔 객체를 넘기세요. 하나의 책임을 갖는 각각의 클래스로 추출해내세요.

경고 신호

  • 클래스가 하는 일을 요약하는데 “그리고” 라는 단어가 포함된다  
  • 새 동료가 클래스를 빨리 읽고 이해하기 어렵다  
  • 클래스가 일부의 메소드에서만 사용되는 필드(멤버변수)를 갖는다  
  • 클래스가 매개변수만 사용하는 스태이틱 메소드를 갖는다  

왜 결함일까

클래스가 넓은 범위의 책임과 기능을 가질때, 결국 이런 코드를 갖게 됩니다.

  • 디버그하기 어려운  
  • 테스트하기 어려운  
  • 확장하기 어려운 시스템  
  • Noogler(구글 신입사원)에게 어렵고 넘겨주기 힘든  
  • 표준 메커니즘(데코레이터, 스트레티지, 서브클래싱)을 통해 행동을 수정할 수 없는. 결국 조건문을 하나 더 추가하게 된다.  
  • 클래스를 설명하는 이름을 붙이기 어려운. 이름을 가지고 고심할 때마다, 책임이 명확하지 않다는 뜻이다. 오브젝트가 분명한 이름을 가지고 있으면, 중요한 부분에 집중해서 불필요한 부분을 덜어내기 쉽다.  

우리는 이런 코드를 원하지 않지요. 그리고 CL Reviewer(뭘까요?)로서 너무 많은 책임을 갖는 클래스에대해 변경을 요청하는 것은 여러분의 책임이랍니다. 

이 결함은 이런 문구로도 묘사된다

클래스가 너무 많은 책임을 갖는 경우를 얘기하는 또 다른 말들을 알려주고 있네요. 지금 당장은 아니더라도 시간이 지나면 아래서 하는 말이 딱 맞게 된답니다. 

  • 부엌 싱크 ('부엌 싱크빼고 다있다'라는 표현에서 온 것 같은데요. 과도한 포괄을 의미하는)  
  • 쓰레기 하치장  
  • 클래스가 하는 일에 너무 많은 “그리고”를 갖는 클래스  
  • 첫번째 일은 모든 매니저(클래스)를 죽이는 것이다 (셰익스피어 패러디)  
  • 신(God) 클래스  
  • 당신은 이 하나의 클래스를 빼고는 모든 것을 볼 수 있다 (뭔소린지 이해 안되네요)  

마무리

얼마전에 제가 있는 곳에서 전사에서 쓸 코딩 컨벤션을 만들어서 발표한 적이 있습니다. 물론 훌륭하게 잘 만들었지만, 왠지 기쁘지는 않더군요. 그리고 얼마 후에 구글에서 테스트하기 좋은 코드에 대한 가이드라인을 공개했습니다. 왠지 좀 부러웠습니다. 좋은 코드를 만드는 가이드라인이 있다니요! LoL

그럼 오늘도 좋은 코드 만드세요~

Written by muscly

January 27th, 2009 at 10:27 am

Posted in 프로그래밍

Tagged with ,

Writing Testable Code 3/4

without comments

[옛날 블로그 글입니다. 2009.01.21]

구글의 가이드라인 Writing Testable Code를 정리하는 그 3번째 글입니다. 글 쓰신 분이 바뻐서 대충 쓰셨는지 3편 원문은 필요 이상으로 기네요. 여기서는 가능한 줄여봤습니다. ^^

다른 편은 아래 링크에서 확인 가능합니다.
결함 #1: 생성자가 실제적인 일을 한다 
결함 #2: 협력 오브젝트 속으로 파고들기
결함 #4: 클래스가 너무 많은 걸 한다

결함 #3: 깨지기 쉬운 전역 상태&싱글톤

생성자나 메소드 안에서 전역적으로 공유되는 상태에 접근하면 코드를 읽는 사람의 입장에서는 디펜던시를 파악하기가 어렵습니다. 전역 상태와 싱글톤은 API의 디펜던시를 속입니다. 진짜 디펜던시를 확인하려면 모든 소스코드를 읽어봐야 합니다.

또, 원격 유령 현상(부록1)의 원인이 되는데, 테스트 수트를 실행 할 때 한 테스트에서 전역 상태를 변경하면 이어지는 테스트나 동시 실행중인 테스트가 실패해버리게 됩니다. 정적인(메소드 안에서 하드코딩된) 디펜던시를 디펜던시 인젝션(메소드의 인자로 직접 넘기기)을 사용해서 끊어버리세요. 직접 코딩하거나  Guice(부록2)를 사용할 수 있습니다.  

경고 신호

  • 싱글톤을 추가하거나 사용하고 있다
  • (자바나 C#의 경우)스테이틱 필드나 스테이틱 메소드를 추가하거나 사용하고 있다
  • (자바의 경우)정적(스테이틱) 초기화 블록을 추가하거나 사용하고 있다
  • 레지스트리를 추가하거나 사용하고 있다
  • 서비스 로케이터를 추가하거나 사용하고 있다

왜 결함인가?

전역적인 상태는 디자인을 더럽힌다

전역 상태의 근본적인 문제점은 전역적으로 접근 가능하다는 것이죠. 이상적인 세상에서, 오브젝트는 인자로 전달된 오브젝트하고만 소통할 수 있어야 합니다. 다시 말해, 제가 오브젝트 A와 B를 만들고, A에서 B로 레퍼런스를 넘겨주지 않았다면, A와 B 모두 상대방을 소유하거나 상대방의 상태를 건드려서는 안됩니다.

그런데, 전역 변수와 싱글톤은 그렇지 않지요. 오브젝트 A가, 개발자 모르게, 싱글톤 C를 가지고 그것을 건드리고 있다고 치면. 만약 오브젝트 B가 생성되고, 역시 싱글톤 C를 가지고 있다면, A와 B는 C를 통해 서로 영향을 줄 수 있습니다

전역적인 상태는 원격 유령 현상을 유발한다

원격 유령 현상이란 우리가 격리되어 있다고 믿는(우리가 아무런 레퍼런스도 넘기지 않았으니까) 어떤 것을 실행할 때, 그 오브젝트에 대해 알려주지 않은 원격 시스템에서 예기치않은 인터랙션이나 상태 변화가 발생하는 걸 말합니다. 이건 전역적인 상태를 통해서만 발생할 수 있지요.

원격 유령 현상때문에 개발자는 잠재적인 인터랙션을 찾기 위해 모든 소스코드를 봐야만 하니, 개발자의 생산성을 떨어트리고, 새로 온 동료를 혼란스럽게 합니다. 스태이틱 필드나 메소드 등을 사용하지 마시고, 필요로 하는 구체적인 협력 오브젝트를 직접 넘겨주는(디펜던시 인젝션) 방식을 사용하세요.

전역적인 상태와 싱글톤은 불안정한 어플리케이션(그리고 테스트)를 만든다

  • 정적인(스태이틱 메소드 등을 통한) 접근은 서브클래스나 래핑된 클래스와의 협업을 방해합니다. 디펜던시를 하드코딩하므로써, 폴리모피즘의 강력함과 유연함을 잃어버립니다.
  • 전역 상태를 사용하는 모든 테스트는 바람직한 상태에서 시작될 필요가 있고, 그렇지 못하면 실패합니다. 그런데 이전 테스트에서 전역 상태를 변경시켰을지도 모릅니다.
  • 전역 상태는 종종 병렬적인 테스트 실행을 방해하고, 이는 테스트 수트의 실행을 느리게 만듭니다.
  • 만약 새로운 테스트(전역 상태를 정리해 두지 않는)를 추가했는데 이 테스트가 테스트 수트의 중간에서 실행되고 있다면, 그 뒤에 실행되는 다른 테스트는 실패할지도 모릅니다.
  • 싱글톤은 “싱글톤스러움” 때문에 결국 문제가 됩니다.
  • 소위 싱글톤이라는 것에는 reset()이나 setForTest(..)같은 변경 메소드를 자주 볼 수 있습니다. 테스트 동안에 인스턴스를 바꿀 필요가 있으니까요. 테스트 후에 싱글톤을 리셋하는 걸 까먹으면, 다음 번 사용할 때 실패할 수 있지요, 디버깅도 힘들고요.

전역 상태와 싱글톤은 API를 거짓말쟁이로 만든다

생성자나 메소드의 시그니쳐에는 없는 클래스를 쓰는 것이니 거짓말쟁이라는 얘기입니다. 대신에 직접적으로 인자로 넘기기, 즉 디펜던시 인젝션을 강추하고 있습니다.

싱글톤은 양의 탈을 쓴 전역 상태다

대부분의 소프트웨어 엔지니어는 전역상태가 바람직하지 않다는데 동의할 겁니다. 하지만, 싱글톤은 전역 상태를 만들지만, 꽤 많은 사람들이 여전히 새로운 코드에 싱글톤을 사용합니다. 유행과 싸우세요. 사람들이 전통적인 JVM 싱글톤(C++이라면 스태이틱 변수를 통한 구현) 대신에 다른 매커니즘을 사용하게 도와주세요.

정말로 싱글톤이 필요한 게 아닌 경우가 많습니다(요즘은 오브젝트의 생성은 매우 부하가 적습니다) 어플리케이션에 하나의 공유된 인스턴스를 보장받을 필요가 있다면, Guice의 싱글톤 스코프를 사용하세요. (Guice는 자바용이지만 철학은 따올 수 있겠죠)

전역 상태는 유닛 테스팅에서 단독으로는 가장 큰 두통거리입니다!

부록 1. 원격 유령 현상 Spooky Action at a Distance

최근의 양자 역학의 실험에 따르면 얽힌 양자 2개를 수십키로미터 떨어트린 후에 하나의 양자를 관찰하면, 그 관찰의 행위가 다른 양자에 실시간으로 영향을 미친다고 합니다. 귀신이 곡할 노릇이지요. 그래서 아인슈타인이 이 현상을 어느 편지에서 Spooky Action at a Distance라고 표현했답니다.

아인슈타인은 광속보다 빨리 이동할 수 있는 물질은 없다고 생각했는데, 마치 어떤 신호가 엄청나게 빨리 전달된다고 밖에 볼 수 없었으니까요. 아래 보면 그 유령같은 신호의 속도가 광속보다 1만배 빠르다고 추정하는 논문에 대해 요약되어 있습니다. 시공간상에서는 불가능하므로 시공간 바깥의 또 다른 차원의 일인 것같다라는 코멘트가 눈에 띄네요. ^^
http://www.studybusiness.com/dir/dir/Download/SPA/1571.html

관심있는 분은 안톤 차일링거의 아인슈타인의 베일을 한 번 읽어보세요. 일반인을 위한 책인데 저자와의 지식차이가 커서 그런지 완전히는 이해 못하겠네요. -_-;;
http://www.yes24.com/Goods/FTGoodsView.aspx?goodsNo=2362849&CategoryNumber=001001002016003

부록 2. Google Guice

가이스가 아니라 쥬스라고 읽는답니다. 디펜던시 인젝션을 하는 경우는 생성자나 메소드의 인자로 협력 오브젝트를 넘겨주어야 하니까, 사용상의 편의를 위해서 팩토리 클래스를 만드는 게 일반적입니다. 팩토리가 구현이 뻔한데 직접 손으로 일일이 작성하려면 귀찮고, 관리도 잘 안되니까 이걸 좀 편하게 해주려고 만든거랍니다. 단순히 소스 코드상에 몇 줄 어노테이션을 해주면 Guice가 팩토리 관련 코드를 자동으로 생성해준다고 하네요.
Guice 사용자 매뉴얼

그럼, 오늘도 좋은 코딩 되시길~

Written by muscly

January 21st, 2009 at 10:24 am

Writing Testable Code 2/4

with 2 comments

 [옛날 블로그 글입니다. 2009.01.15]

구글의 가이드라인 Writing Testable Code를 정리하는 그 2번째 글입니다. 다른 편은 아래 링크에서 확인하세요.
결함 #1: 생성자가 실제적인 일을 한다 
결함 #3: 깨지기 쉬운 전역 상태&싱글톤
결함 #4: 클래스가 너무 많은 걸 한다

결함 #2: 협력 오브젝트 속으로 파고들기

“holder”나 “context” 같이 다른 오브젝트를 보관하는 가방 역할을 하는 오브젝트를 피하라는 얘기입니다. 필요로 하는 바로 그 오브젝트를 인자로 넘겨주세요,그 오브젝트를 소유한 오브젝트가 아니라. 다른 오브젝트를 얻기 위한 오브젝트를 사용하지 마세요. 

경고 신호

  • 오브젝트가 인자로 전달되지만 직접 사용되지는 않는다(다른 오브젝트를 얻으려는 용도로만 사용된다)   
  • Law of Demeter(부록1) 위반: 함수를 호출하기 위해서 하나의 점(.) 보다 많은 점을 사용한다. (C++이라면 ->도 포함)   
  • 의심스런 이름: context, environment, principal, container, manager   

왜 결함인가?

거짓말하는 API

여러분의 API가 String 타입의 신용카드번호만 필요하다고 말해놓고, 몰래 CardProcessor 클래스를 찾으려고 파고든다면, 진짜 디펜던시가 불명확해지겠죠. 디펜던시를 API에 나타내세요. 진짜로 필요로하는 협력 오브젝트를 명시하세요. (메소드의 시그니처나 오브젝트의 생성자에서)

망가지기 쉬운 코드를 만든다

무언가 바꿀 필요가 있다고 가정해보죠. 그리고 여러분이 이런 “Middle Men”을 파고들어서 필요한 오브젝트를 얻고 있다면, 새로운 인터랙션을 수용하기 위해서 “Middle Men”도 변경될 필요가 있습니다. 이렇게 하는 대신에, 필요로하는 바로 그 객체를, 필요로 하는 곳에서 바로 얻도록 시도해보세요. ( 이 과정에서 클래스가 하나 이상의 책임을 가지고 있어서 분리해야 하는 경우도 있습니다. Single Responsibility Principle을 고수하는 걸 두려워 하지 마세요) 

또한, 필요한 객체를 찾으러 파고드는 동안 다른 중간 오브젝트를 알게되고, 컴파일을 위한 디펜던시가 생기게 됩니다.

코드를 부풀리고 진짜로 하는 일을 알기 어렵다

필요한 것을 찾으려 파고드는 이런 중간 단계 때문에, 코드가 더 혼란스러워 집니다. 그리고 필요 이상으로 길어집니다.

테스트하기 어렵다

context 오브젝트를 받아들이는 메소드를 테스트 해야 한다면, 그 메소드가 호출될 때 context에서 어떤 것이 뽑아져 사용되고, 어떤 것은 신경안써도 되는지 알기 힘들죠. 보통은 빈 context를 전달하고, 널 포인터가 나오면, 그 부분에 값을 채우고 다시 시도해봅니다. 모든 널 포인터가 없어질때까지 반복해야 합니다.

부록1. Law of Demeter (Principle of Least Knowlede)

위키 피디아 링크
간단히 말해서 “니 친구한테만 말해라” 라는 법칙입니다. 친구의 친구는 안되고요. 프로젝트에 적용해본 적은 없지만 간단하면서 효과적인 규칙이라고 들었습니다. 

예를 들어, 메소드를 위한 규칙을 보면 아래에 해당하는 오브젝트, 변수 외에는 접근하면 안됩니다. 

  1. 오브젝트 자기 자신 
  2. 메소드의 인자로 넘겨온 오브젝트 
  3. 메소드 안에서 생성된 오브젝트 
  4. 오브젝트 자기 자신이 직접 가지고 있는 오브젝트 (멤버 변수)

이 규칙 대로라면 인자로 넘겨진 오브젝트에서 다른 오브젝트를 얻어서 메소드를 호출하면 안되는 거죠.   

그럼, 오늘도 테스트 하기 좋은 코드 만드세요~ ^^

Written by muscly

January 15th, 2009 at 10:22 am

Posted in 프로그래밍

Tagged with , ,

Writing Testable Code 1/4

with 4 comments

[옛날 블로그 글입니다. 2009.01.10]

Google에서 사용하는 코딩 가이드라인이 있네요. 테스트하기 좋은 코드를 만들기 위한 가이드라인으로 크게 4가지 얘기를 하고 있습니다. 바쁘고 영어 싫어하시는 분들을 위해서 간단히 정리해보겠습니다. 원문은 아래 주소에서 보세요~  http://misko.hevery.com/code-reviewers-guide/

결함 #1: 생성자가 실제적인 일을 한다

생성자에서 너무 많은 일을 하면 테스트가 어려워 집니다. 테스트를 위해 오브젝트를 생성하기도 어렵고, 협력 오브젝트로를 바뀌치는 것도 어렵죠. 

경고 신호

  • 생성자에 있는 new 키워드 ( Java/C#이라면 필드 선언에 있는 new 키워드) 
  • 생성자에서 스테이틱 메소드 호출 (역시 필드에서의 호출도)  
  • 필드(멤버 변수) 대입 이상의 모든 행위  
  • 생성자가 끝나도 오브젝트가 완전히 초기화되지 못했다 (initialize 메소드를 주의할 것)  
  • 생성자에 있는 제어 코드(조건문이나 반복 로직)  
  • 생성자에서 복잡한 오브젝트 그래프를 생성한다, 팩토리나 빌더를 사용하는 대신에
  • (Java의)초기화 블럭을 사용한다   

왜 결함인지

생성자에서 협력 오브젝트를 생성하거나 초기화하면, 유연하지 못하고 너무 일찍 커플링된 디자인이 되기 쉽습니다. 테스트용의 협력 오브젝트로 바꿔칠 기회도 잃어버립니다.

Single Responsibility Principle에 위배된다.

클래스는 하나의 책임만 가져야 합니다. 오브젝트 그래프를 생성하는 것도 충분히 하나의 책임으로 볼 수 있으므로 위배.

직접 테스트 하기 어렵다.

테스트에서 오브젝트를 생성할 때도 생성자가 샐행되지요. 생성자에 있는 협력 오브젝트가 외부 리소스(파일, 네트웍, DB)에라도 접근한다면, 협력 오브젝트의 작은 변화도 생성자에 영향을 줍니다. 그런데 협력 오브젝트의 테스트가 어려우니까 테스트 코드를 빼먹었을 수도 있겠죠. 그럼 또 내 오브젝트가 테스트가 충분히 안된 셈이니 악순환.

테스트를 위한 상속과 오버라이딩은 여전히 결함을 갖는다.

생성자에서 거의 아무것도 안하고 오버라이드될 함수에 일을 떠넘기는 경우. 복잡한 생성 문제는 피할 수 있을지 몰라도, 테스트를 위한 상속은 최후의 보류로 하는게 좋답니다. 또 오버라이드된 함수를 테스트 할 수 없게 되는데, 이 함수가 많은 일을 하고 있으므로 반드시 테스트 되었어야 합니다.

협력 오브젝트를 강요한다.

뭐 계속 했던 얘기. 협력 오브젝트는 생성자의 인자로 넘기는 것을 강추. 그리고 테스트 용의 생성자를 따로 만드는 것은 비추. 그 클래스를 쓰는 또 다른 클래스를 테스트 하려면 피곤해지니까요.

결론

결국 독립적인 생성 혹은 테스트용의 협력 오브젝트를 사용한 생성이 얼마나 어려운가의 문제입니다. 

  • 어렵다면, 생성자에서 너무 많은 일을 하는 겁니다!   
  • 쉽다면, 스스로를 칭찬하세요.    

원문을 보시면 아래처럼 더 읽어보실만한 내용이 있습니다. 직접 확인해주세요 ^^ 

  • 결함 인식하기  
  • 결함 고치기   
  • 구체적인 코드 예 (Before and After)  

나머지 3개의 결함은 다음에 포스팅 하겠습니다. ^^ 

결함 #2: 협력 오브젝트 속으로 파고 들기 
결함 #3: 깨지기 쉬운 전역 상태&싱글톤
결함 #4: 클래스가 너무 많은 걸 한다

마무리

테스트 하기 좋은 코드는 좋은 코드입니다. 말이 좀 이상한가요 -_-;; 테스트 하기 좋게 코드를 작성하면 자연스레 좋은 품질의 코드가 만들어진다는 의미인데요. 어찌보면 당연하기도 합니다. 내가 만든 클래스를 가져다 쓰기가 쉽다는 말이 되니까요.

또 반대로 품질이 안좋은 코드는 테스트 하기가 어렵습니다. 그래서 우리도 한 번 유닛테스트 도입해보자! 라고 큰맘먹고 해보려고 해도 좀처럼 쉽지 않은 경우가 많은데요, 유닛테스트가 어려운 것으로 탓하고 포기하지 마시고 조금씩 리펙토링 하면서 테스트를 만들어 보시면 재밌습니다. ^^

그럼 가이드라인 참조하셔서 좋은 코드 만드세요~

Written by muscly

January 10th, 2009 at 10:08 am