SqueakByExample:6.5: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(번역수정)
(번역수정)
Line 55: Line 55:




[[image:RestartDetectIfNone.png|none|596px|thumb|그림 6.26: detect: ifNone: 메서드를 다시 시작한 후의 디버거입니다.]]
[[image:RestartDetectIfNone.png|none|596px|thumb|그림 6.26: detect:ifNone: 메서드를 다시 시작한 후의 디버거입니다.]]




Line 64: Line 64:




{{CommentSqueak|{{Template:HighlightBox|Into}}를 클릭하면, 스퀵은 이번 경우에 {{Template:HighlightBold|Collection>>do:.}}인 강조된 메시지 발송(message-send)에 대응하는 메서드로 들어가십시오.}}
{{CommentSqueak|{{Template:HighlightBox|Into}}를 클릭하면, 스퀵은 강조된 메시지 전송부분 으로 이동합니다. 이번의 경우는 {{Template:HighlightBold|Collection>>do:.}} 가 되겠군요.}}




그럼에도 불구하고, 이 작업도 많은 도움이 되지는 않습니다: 우리는 Collection»do가 망가지지 않았다는 것에 대해 어느 정도 자신감이 있습니다. 버그는 우리가 스퀵에게 요청한 작업에 있게 될 확률이 높습니다. {{Template:HighlightBox|Through}}는 이번 사례에서 사용할 적합한 버튼입니다: 우리는 do: 자체의 상세한 내용들을 무시하기를 원하고 인자 블록의 실행에 집중할 것입니다.  
이렇게 메서드로 이동을 하는것도, 많은 도움이 되지는 않습니다: 사실 Collection>>do가 망가지지 않았다는 것에 대해 어느 정도 자신감이 있으니까요. 버그는 메서드에서 스퀵에게 요청한 작업 내에 있을 확률이 높습니다. {{Template:HighlightBox|Through}}는 다음같은 경우에 사용하기 유용한 버튼입니다: 이제 do: 메서드 자체의 자세한 내용 을 무시하고 인자블록들의 실행에 집중해봅시다.


{{CommentSqueak|{{Template:HighlightBox|Through}}를 클릭합니다. 컨텍스트 창에서 여러분이 하는 방식으로 each를 선택하십시오. 여러분은 do: 메서드가 실행하는 10에서 부터 시작하는 각각의 카운트를 보실 수 있어야 합니다.}}
each가 7이 될 때, 우리는 ifTrue:block이 실행될 것을 기대할 수 있지만, 그것은 실행되지 않습니다. 무엇이 틀렸는지 보기 위해, 그림 6.27에 묘사된 것 처럼, value의 {{Template:HighlightBox|Into}} 실행으로 이동합니다.




{{CommentSqueak|{{Template:HighlightBox|Through}}를 클릭합니다. 컨텍스트 창에서 여러분이 하는 방식으로 each를 선택하십시오. 여러분은 do: 메서드가 실행하는 10에서 부터 시작하는 각각의 카운트를 보실 수 있어야 합니다.}}
[[image:steppingIntoValue.png|none|597px|thumb|그림 6.27: Through the do: 메서드의 단계를 여러 번 밟은 후의 디버거]]


each가 7이 될 때, 우리는 ifTrue:block이 실행될 것을 기대할 수 있지만, 그것은 실행되지 않습니다. 무엇이 틀렸는지 보기 위해, 그림 6.27에 묘사된 것 처럼, value의 {{Template:HighlightBox|Into}} 실행으로 이동합니다..


{{Template:HighlightBox|Into}}를 클릭한 다음, 우리는 그림 6.28에 보이는 위치에 있는 우리 자신을 발견할 것입니다. 이 장면은 마치 우리가 suffix 메서드로 돌아간 것처럼 보이며, 그 이유는 우리가 현재, detect: 에 대한 인수로서 제공된 suffix인 블록을 실행하고 있기 때문입니다. 만약 여러분이 컨텍스트 인스펙터에서 i를 선택하셨다면, 여러분은 그것의 현재 값을 보실 수 있을 것이며, 여러분이 만약 지금까지 잘 따라오셨다면, 그 값은 반드시 7이 되어야 합니다. 그 다음 여러분은, self-인스펙터로부터 self의 해당 구성요소를 선택할 수 있습니다. 그림 6.28에서, 여러분은 문자열의 구성요소 7이 문자 46임을 볼 수 있으며, 이것은 실제로 dot 입니다. 만약 여러분이 컨텍스트 인스펙터에서 dot를 선택하셨다면, 그 dot의 값이 '.' 임을 보시게 될 것입니다. 그리고 지금 여러분은 왜 이 값들이 동일하지 않은지를 알 수 있습니다: dot가 문자열(String)인 것에 대 대비, ‘readme.txt’의 일곱 번째 문자는 물론 문자입니다.
{{Template:HighlightBox|Into}}를 클릭한 다음, 우리는 그림 6.28에 보이는 위치에 있는 우리 자신을 발견할 것입니다. 이 장면은 마치 우리가 suffix 메서드로 돌아간 것처럼 보이며, 그 이유는 우리가 현재, detect: 에 대한 인수로서 제공된 suffix인 블록을 실행하고 있기 때문입니다. 만약 여러분이 컨텍스트 인스펙터에서 i를 선택하셨다면, 여러분은 그것의 현재 값을 보실 수 있을 것이며, 여러분이 만약 지금까지 잘 따라오셨다면, 그 값은 반드시 7이 되어야 합니다. 그 다음 여러분은, self-인스펙터로부터 self의 해당 구성요소를 선택할 수 있습니다. 그림 6.28에서, 여러분은 문자열의 구성요소 7이 문자 46임을 볼 수 있으며, 이것은 실제로 dot 입니다. 만약 여러분이 컨텍스트 인스펙터에서 dot를 선택하셨다면, 그 dot의 값이 '.' 임을 보시게 될 것입니다. 그리고 지금 여러분은 왜 이 값들이 동일하지 않은지를 알 수 있습니다: dot가 문자열(String)인 것에 대 대비, ‘readme.txt’의 일곱 번째 문자는 물론 문자입니다.
Line 80: Line 84:




[[image:steppingIntoValue.png|none|597px|thumb|그림 6.27: Through the do: 메서드의 단계를 여러 번 밟은 후의 디버거]]
 





Revision as of 13:57, 13 March 2013

디버거(Debugger)

스퀵의 디버거는 틀림없이 스퀵 도구 모음중에서 가장 강력한 도구입니다. 디버가는 디버깅에만 사용되는 것이 아니라, 새로운 코드를 작성하는 작업에도 사용됩니다. 디버거는 버그를 입력함으로서 시작할 수 있습니다.


Squeak comment.png시스템 브라우저를 사용하여, 클래스 String에 다음 메서드를 추가하십시오:


메서드 6.1 버그가 있는 메서드

suffix
  "assumes that I'm a file name, and answers my suffix, the part after the last dot"
  | dot dotPosition |
  dot := FileDirectory dot.
  dotPosition := (self size to: 1 by: -1) detect: [ :i | (self at: i) = dot ].
   self copyFrom: dotPosition to: self size


물론 작업한 메서드는 당연히 작동할 거라 예상하고, SUnit test 를 작성하는 대신에, 워크스페이스에서 'readme.txt' suffix 를 입력한 다음 print it (p) 을 진행합니다. 이야! 대단하네요? 기대했던 결과인 'txt'를 얻는 대신에, 그림 6.24에서 보이는 것처럼, PreDebugWindow 를 볼 수 있습니다.


그림 6.24: PreDebugWindow는 버그를 알려줍니다.


PreDebugwindow에는 어떤 오류가 발생했는지에 대해 알려주는 타이틀바(title-bar)가 있으며, 오류를 발생시켰던 메시지의 stack trace 상태를 출력합니다. trace의 출력은 아래쪽부터 시작해서 위쪽으로보면 됩니다. UndefinedObject>>DoIt 은 실행되는 컴파일된 코드를 나타내며, 사용자가 워크스페이스 에서 'readme.txt' suffix 를 이용해서 스퀵에게 print it을 요청한 시점에서 실행됩니다. UndefinedObject>>DoIt 에서 확인할 수 있는 'readme.txt' suffix 라는 코드는 당연히, ByteString 오브젝트('readme.txt')에게 메시지 suffix 를 전송한다는 의미입니다. 이런 과정들을 거쳐 String 클래스에서 ByteString 으로 상속된 suffix메서드가 실행이 됩니다; ByteString(String)>>suffix에 관련된 모든 정보는 stack trace 의 다음줄부터 부호화(encoding) 됩니다. stack이 진행되면서, suffix 매서드는 그 안에서 자신에게 detect:... 메시지 를 보내고 결국 detect:ifNone 에 의해 errorNotFound 가 전송됨을 확인할 수 있습니다.

점(dot)을 발견하지 못한게 문제가 됐습니다. 자세한 이유를 알아보기위해서 디버거가 필요하기때문에 Debug 버튼을 클릭하도록 합니다.


Squeak comment.png당신이 stack trace 의 정보들중 어떤 라인을 클릭해도, 디버거가 열립니다. 디버거는 문제가 생긴 해당 메서드에 이미 포커스된 상태로 열리게 됩니다.


그림 6.25: 디버거


그림 6.25에 디버거가 보입니다; 이 디버거는 처음에 매우 어렵게 보이지만, 사용하기는 꽤 쉽습니다. 타이틀 바와 상단 패널은 PreDebugWindow 에서 보았던 것들과 매우 흡사합니다. 오류가 일어난 실행부분이 당신의 이미지에 그상태 그대로 존재한다는것은 중요한 사실입니다. 대기상태기는 하지만요. stack trace (추적) 의 각 라인은 실행 stack 의 프레임을 나타내는데 그 프레임이란것은 실행을 연속적으로 수행하기 위해 필요한 모든 정보를 포함하고 있습니다. 실행 stack 의 프레임은 인스턴스 변수들, 실행 메서드들의 모든 임시 변수들 및 연산에 관련된 모든 오브젝트를 포함합니다.

그림 6.25 를 보면, 상단 패널에서 detect:ifNone: 메서드를 선택했습니다. 선택한 메서드의 내용은 중앙의 패널에 표시됩니다; 파란색으로 강조된 value 메세지는 현재 보이는 메서드에서 value 메세지를 보냈으며 답변을 기다리고 있음을 나타냅니다.

디버거의 아래에 있는 4개의 패널은 실제로 두 개의 mini inspector(워크스페이스 패널은 빠진상태)입니다. 왼쪽의 인스펙터는 현재 오브젝트에 관련된 내용을 보여주며, 이 경우 현재 오브젝트는 가운데 패널의 내용중 self 가 가리키는 오브젝트를 의미합니다. 상단의 목록에서 다른 stack 프레임을 선택하면, self의 아이덴티티는 변경될 수 있으며, self-inspector의 내용도 바뀔 수 있습니다. 좌하단 패널에서 self를 클릭하면 이미 예상했던대로 self의 값은 인터벌 (10 to:1 by -1) 임을 알 수 있습니다. 디버거의 mini inspectors 에서는 워크스페이스 패널이 필요없는데, 왜냐하면 모든 변수들의 범위는 위쪽의 메서드 패널의 내용에 한정되기 때문입니다; 가운데의 메서드 패널에서 입력을 하거나 또는 표현식들을 선택하고 그것들을 실행해보는 작업을 마음대로 할 수 있기 때문이죠. 언제나, 메뉴 또는 CMD-i를 사용하면 변경사항을 cancel (l)(취소)할 수 있습니다.

우측 하단의 인스펙터는 현재 시점의 임시 변수를 보여줍니다. 그림 6.25에서, exceptionBlock 의 매개변수로 value가 보내졌습니다.


Squeak comment.png이 파라미터의 현재 값을 보기 위해, 우측하단의 context 인스펙터 에서 exceptionBlock 을 클릭 합니다. 클릭하면 exceptionBlock 의 값이 [self errorNotFound: ...] 임을 알려줄겁니다. 변수의 값으로 해당되는 오류메세지가 보인다 해도 놀랄일은 아닙니다.


그건 그렇고, 디버그창 아래의 mini inspector 에 보이는 변수들중 한가지에 대해 풀-인스펙터(full inspector) 또는 오브젝트 탐색기를 열기 원하는 경우에는, 변수의 이름을 더블클릭(inspect)을 하거나 또는 변수의 이름을 선택하고 노랑 버튼 메뉴에서 inspect (i) 또는 explore (I) 를 선택하면 됩니다. 이렇게 디버깅중 변수를 inpect 또는 explore 하는 작업은 다른 코드를 실행하는 동안 어떻게 변수가 변하는지 보고싶을때 유용합니다.

위쪽의 stack trace창에서 suffix 부분을 클릭한후 메서드가 표시되는 패널을 보면, 문자열 'readme.txt' 에 대해 메서드의 끝에서 두 번째 라인이 점(dot)을 찾아낼거라 생각했습니다만, 메서드가 동작되는 일련의 작업은 마지막 라인까지 진행되지 못했음을 알 수 있습니다. 스퀵은 디버깅중 역실행을 허용하지 않고, 메서드를 처음부터 재시작하게 하며, 사실 이런방법은 오브젝트를 변형시키는게 아니라 새로운 오브젝트를 만드는 코드에서 매우 잘 동작한다.


Squeak comment.pngRestart를 클릭하면, 가운데 패널에서 현재 stack trace를 통해 선택한 메서드의 실행이 다시 진행되며 과정이 첫 번째 표현(statement)으로 돌아가는걸 확인할 수 있습니다. 파란색으로 강조된 부분을 통해 다음 메시지가 do:에 전송될것이라는걸 보게됩니다 (그림 6.26을 보십시오.)


그림 6.26: detect:ifNone: 메서드를 다시 시작한 후의 디버거입니다.


IntoOver를 통해서 두가지 서로다른 실행방법을 진행할 수 있습니다. 만약 Over 버튼을 클릭하면, 스퀵은 오류가 없을 경우, 첫 번째 단계에서 현재 선택된 메시지 발송(이번 경우엔 do:) 을 실행합니다. 이후 Over 를 다시 누르면 현재 메서드에 내에서 다음차례의 메시지 발송을 진행하는 위치로 진행되며, 그 대상은 value: 가 됩니다. value 는 디버깅을 시작한 지점이기때문에 더이상 도움이 되지 않을겁니다. 이제 해야 할 일은 do: 메서드가 사용자가 찾으라고 지정한 문자를 찾지 못하는 이유를 알아내는 것입니다.


Squeak comment.pngOver를 클릭하고 그림 6.26에 보이는 상황으로 돌아가기 위해 Restart를 클릭하십시오.


Squeak comment.pngInto를 클릭하면, 스퀵은 강조된 메시지 전송부분 으로 이동합니다. 이번의 경우는 Collection>>do:. 가 되겠군요.


이렇게 메서드로 이동을 하는것도, 많은 도움이 되지는 않습니다: 사실 Collection>>do가 망가지지 않았다는 것에 대해 어느 정도 자신감이 있으니까요. 버그는 메서드에서 스퀵에게 요청한 작업 내에 있을 확률이 높습니다. Through는 다음같은 경우에 사용하기 유용한 버튼입니다: 이제 do: 메서드 자체의 자세한 내용 을 무시하고 인자블록들의 실행에 집중해봅시다.


Squeak comment.pngThrough를 클릭합니다. 컨텍스트 창에서 여러분이 하는 방식으로 each를 선택하십시오. 여러분은 do: 메서드가 실행하는 10에서 부터 시작하는 각각의 카운트를 보실 수 있어야 합니다.


each가 7이 될 때, 우리는 ifTrue:block이 실행될 것을 기대할 수 있지만, 그것은 실행되지 않습니다. 무엇이 틀렸는지 보기 위해, 그림 6.27에 묘사된 것 처럼, value의 Into 실행으로 이동합니다.


그림 6.27: Through the do: 메서드의 단계를 여러 번 밟은 후의 디버거


Into를 클릭한 다음, 우리는 그림 6.28에 보이는 위치에 있는 우리 자신을 발견할 것입니다. 이 장면은 마치 우리가 suffix 메서드로 돌아간 것처럼 보이며, 그 이유는 우리가 현재, detect: 에 대한 인수로서 제공된 suffix인 블록을 실행하고 있기 때문입니다. 만약 여러분이 컨텍스트 인스펙터에서 i를 선택하셨다면, 여러분은 그것의 현재 값을 보실 수 있을 것이며, 여러분이 만약 지금까지 잘 따라오셨다면, 그 값은 반드시 7이 되어야 합니다. 그 다음 여러분은, self-인스펙터로부터 self의 해당 구성요소를 선택할 수 있습니다. 그림 6.28에서, 여러분은 문자열의 구성요소 7이 문자 46임을 볼 수 있으며, 이것은 실제로 dot 입니다. 만약 여러분이 컨텍스트 인스펙터에서 dot를 선택하셨다면, 그 dot의 값이 '.' 임을 보시게 될 것입니다. 그리고 지금 여러분은 왜 이 값들이 동일하지 않은지를 알 수 있습니다: dot가 문자열(String)인 것에 대 대비, ‘readme.txt’의 일곱 번째 문자는 물론 문자입니다.

이제 우리는 버그를 보게 됩니다, 수정할 내용은 명확합니다: 우리는 검색을 시작하기 전에 dot를 문자로 변환시켜야만 합니다.



그림 6.28: 'readme.txt' at: 7 가 dot와 동일하지 않은 이유를 보여주는 디버거


그림 6.29: 디버거에서 suffix 메서드를 변경하기: 내부 블록으로부터 나가기(exit) 확인(confirmation)을 요청


Squeak comment.png디버거 오른쪽에 있는 코드를 변경하면, 할당이 dot := FileDirectory dot asCharacter 를 읽고 변경사항을 accept 하게 됩니다.


우리가 detect: 내부의 블록 내부에 코드를 실행하고 있기 때문에 여러 개의 스텍 프레임은 이 변경을 만들기 위해 포기되어야만 할 것입니다. 스퀵은 이 작업이 우리가 원하는 것인지를 물어보며(그림 6.29를 보십시오) 만약 우리가 yes를 클릭한다고 가정하면, 새로운 메서드를 저장(그리고 컴파일)할 것입니다.


Squeak comment.pngRestart를 클릭하면 Proceed됩니다: 디버거 창이 사라질 것이며, 표현식 'readme.txt' suffix의 처리가 완료되고, 그리고 답변 ‘.txt’를 인쇄하게 될 것입니다.


이 답변이 정확합니까? 안타깝게도 우리는 확신 있게 말할 수 없습니다. Suffix(접미사)가 .txt 가 되어야 할까요 또는 txt가 되어야 할까요? suffix에 있는 메서드 주석은 아주 정확한 것은 아닙니다. 이 어느정도 이러한 문제를 피하는 방법은 답변을 정의하는 SUnit test를 작성하는 것입니다.


메서드 6.2: suffix 메서드를 위한 간단한 테스트

testSuffixFound
  self assert: 'readme.txt' suffix = 'txt'


워크스페이스에서 동일한 테스트를 실행하는 것보다 좀더 많은 노력이 필요했지만, Sunit을 사용하는 것은 실행 가능한 문서로서 테스트를 저장하고, 다른 사람들이 그 테스트를 실행하기 쉽게 만듭니다. 더욱이, 만약 여러분이 메서드 6.2를 클래스 StingTest에 추가하고, Sunit과 함게 그 테스트 수트를 실행하였다면, 디버깅 오류로 매우 신속하게 돌아갈 수 있을 것입니다. Sunit은 실패하는 주장(the failing assertion)에 디버그를 열지만, 여러분은 단지 스텍으로 다시 돌아가 한 프레임을 낮추기만 하시면 되며, 테스트를 Restart 하고, suffix 메서드로 Into 하면, 우리가 그림 6.30에서 하였던 것처럼, 오류를 수정할 수 있습니다. 그 다음, 두 번째로 해야 할 작업은 단지 Sunit Test Runner에 있는 Run Failures 버튼을 클릭하고, 지금 패스중인 테스트를 확인(confirm)하는 것입니다.


여기에 더 나은 테스트 가 있습니다:

메서드 6.3 suffix 메서드를 위한 더 나은 테스트

testSuffixFound
  self assert: 'readme.txt' suffix = 'txt'.
  self assert: 'read.me.txt' suffix = 'txt'


왜 이 테스트가 더 나을 까요? 그 이유는 이 테스트는 대상 문자열에 하나 이상의 점이 있을 경우, reader에게 메서드가 무엇을 해야 할지를 알려주기 때문입니다.

디버거에 오류와 주장 실패(assertion failures)를 잡아내는 작업 뿐만 아니라, 디버거에 들어가는 몇 가지 다른 방법들이 있습니다. 만약 여러분이 무한 루프에 들어가는 코드를 실행하면, 그 코드를 인터럽트하고, 'CMD- 를 (이것은 여러분이 어디서 영어를 배웠는 가에 따라 그것은 마침표 또는 하나의 구두점으로 부를 수 있습니다)[1]입력하여 연산 중에 디버거를 열 수 있습니다. 여러분은 또한 self halt를 삽입하기 위해 의심스러운 코드를 편집할 수 있습니다.그러므로 예를 들어, 우리가 다음과 같이 읽을 suffix 메서드를 편집할 수 있을 것입니다.


메서드 6.4 suffix 메서드에 halt 삽입하기

suffix
  "assumes that I'm a file name, and answers my suffix, the part after the last dot"
  | dot dotPosition |
  dot := FileDirectory dot asCharacter.
  dotPosition := (self size to: 1 by: --1) detect: [ :i | (self at: i) = dot ].
  self halt.
   self copyFrom: dotPosition to: self size


우리가 이 메서드를 실행할 때, self halt의 실행은 우리가 진행할 수 있는 장소에 pre-debugger를 불러오거나 또는 디버거로 들어가 변수들을 보고, 연산을 한 단계씩 진행하며(step), 코드를 수정합니다. 이것이 디버거에 관한 모든 내용이지만, suffix 메서드에 관한 모든 내용은 아닙니다. 초기 버그는 대상 문자열에 아무 점(.)도 없으며 suffix 메서드는 오류를 발생시킬 것이라는 것을 알게 해드렸어야 합니다.


그림 6.30: 디버거에서 suffix 메서드 변경하기: SUnit assertion failure 이후, the off-by one 오류를 수정하기.


이것은 우리가 원하는 동작이 아니므로, 이번 사례에서 어떤 일이 일어나야 할지를 지정하기 위한 두 번째 텍스트를 추가합시다.


메서드 6.5: 메서드를 위한 두 번째 테스트: 타켓은 suffix를 갖고 있지 않습니다.

testSuffixNotFound
  self assert: 'readme' suffix = ''


Squeak comment.png메서드 6.5를 클래스 StingTest에 있는 테스트 수트에 추가하고, 그 테스트를 통해 오류를 발생하는 것을 지켜봅니다. SUnit에서 오류가 있는 테스트를 선택하여 디버거에 들어가고, 코드를 수정하여, 테스트를 통과되게끔 합니다. 이 작업을 가장 쉽고 분명하게 하는 방법은, 두 번째 인수 블록은 단순히 문자열 길이를 반환하는 위치인 detect: ifNone으로 detect: 메시지를 교체하는 것입니다.


7장에서 SUnit에 대해 좀 더 배우도록 하겠습니다.

Notes

  1. CMD-SHIFT-를 입력하여 언제든지 응급 디버거를 불러올 수 있다는 것을 아는 것 또한 중요합니다.