SqueakByExample:6.5: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
mNo edit summary
(번역수정)
 
(26 intermediate revisions by the same user not shown)
Line 1: Line 1:
==디버거(Debugger)==
==디버거(Debugger)==


디버거는 틀림없이 스퀵 도구 스위트(Squeak tool suite)에서 가장 강력한 도구입니다
스퀵의 디버거는 틀림없이 스퀵 도구 모음중에서 가장 강력한 도구입니다. 디버가는 디버깅에만 사용되는 것이 아니라, 새로운 코드를 작성하는 작업에도 사용됩니다. 디버거는 버그를 입력함으로서 시작할 수 있습니다.
이것은 디버깅에만 사용되는 것이 아니라, 새로운 코드를 작성하는 작업에도 쓰입니다. 디버거를 나타내려면, bug를 타이핑함으로써 시작합니다..




{{CommentSqueak|브라우저를 사용하여, 클래스 {{Template:HighlightBold|String(문자열)}}에 다음 메소드를 추가합니다:}}
{{CommentSqueak|시스템 브라우저를 사용하여, 클래스 {{Template:HighlightBold|String}}에 다음 메서드를 추가하십시오:}}




[[image:PreDebugWindow.png|none|451px|thumb|그림 6.24: PreDebugWindow는 버그를 알립니다.]]
메서드 6.1 버그가 있는 메서드


메소드 6.1 Buggy 메소드
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
suffix
suffix
Line 17: Line 14:
   | dot dotPosition |
   | dot dotPosition |
   dot := FileDirectory dot.
   dot := FileDirectory dot.
   dotPosition := (self size to: 1 by: --1) detect: [ :i | (self at: i) = dot ].
   dotPosition := (self size to: 1 by: -1) detect: [ :i | (self at: i) = dot ].
   ↑ self copyFrom: dotPosition to: self size
   ↑ self copyFrom: dotPosition to: self size
</syntaxhighlight>
</syntaxhighlight>




물론 우리는 이러한 trivial 메소드가 작동할 것을 확신하며, 그러므로, SUnit test를 작성하는 대신에, 워크스페이스에서 "readme.txt" 접미사를 타이핑하고 {{Template:HighlightGray|print it (p)}} 할 것입니다. 놀랍지 않습니까? 기대했던 답변 ‘txt’를 얻는 대신에, 그림 6.24에서 보이는 것처럼, PreDebugWindow가 팝업 됩니다.  
물론 작업한 메서드는 당연히 작동할 거라 예상하고, SUnit test 를 작성하는 대신에, Workspace 에서 {{HighlightBold|'readme.txt' suffix}} 를 입력한 다음 {{Template:HighlightGray|print it (p)}} 을 진행합니다. 이야! 대단하네요? 기대했던 결과인 'txt'를 얻는 대신에, 그림 6.24에서 보이는 것처럼, PreDebugWindow 를 볼 수 있습니다.
 


'''PreDebugwindow'''는 어떤 에러가 발생하였는지를 우리에게 말해주는 타이틀바(title-bar)를 갖고 있으며, 에러를 발생시켰던 메시지의 스텍 흔적(a stack trace)을 보여드립니다. 흔적(the trace)의 아래에서 시작한, '''UndefinedObject»DoIt'''는, 우리가 워크스페이스에서 ‘readme.txt’ 접미사를 선택할 때 컴파일되고 실행된 코드를 나타내고, 스퀵에게 {{Template:HighlightGray|print it}} 을 요청합니다. 이 코드는 물론, ByteString 오브젝트(‘readme.txt’)에 메시지 suffix를 발송했습니다. 이 작업은 실행을 위해 클래스 String에 suffix 메소드를 상속되도록 하며, 모든 정보는 스텍 흔적(the stack trace) '''ByteString(String)»suffix'''의 다음 라인에 인코딩 됩니다. 스텍을 불러일으키면, 우리는 발송된 suffix는 detect:. . . 되고 마침내 발송된 detect:ifNone 가 errorNotFound 되었음을 볼 수 있습니다.
[[image:PreDebugWindow.png|none|451px|thumb|그림 6.24: PreDebugWindow는 버그를 알려줍니다.]]


점(dot)을 발견하지 못했습니다. 이유를 알아보기위해서는 디버거가 필요하기때문에 {{Template:HighlightBox|Debug}} 버튼을 클릭합니다.


'''PreDebugwindow'''에는 어떤 오류가 발생했는지에 대해 알려주는 타이틀바(title-bar)가 있으며, 오류를 발생시켰던 메시지의 stack trace 상태를 출력합니다. trace의 출력은 아래쪽부터 시작해서 위쪽으로보면 됩니다.  '''UndefinedObject>>DoIt''' 은 실행되는 컴파일된 코드를 나타내며, 사용자가 Workspace 에서 '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 가 전송됨을 확인할 수 있습니다.


{{CommentSqueak|여러분은 the stack trace의 라인들 중 아무 라인이나 클릭하여, 디버거를 열 수 있습니다. 디버거는 the corresponding method(대응 메소드)에 이미 포커스된 상태로 열릴 것입니다.}}
점(dot)을 발견하지 못한게 문제가 됐습니다. 자세한 이유를 알아보기위해서 디버거가 필요하기때문에 {{Template:HighlightBox|Debug}} 버튼을 클릭하도록 합니다.




그림 6.25에 디버거가 보입니다; 이 디버거는 처음에 매우 어렵게 보이지만, 사용하기가 꽤 쉽습니다. 타이틀 바와 상단 패널은 우리가 '''PreDebugWindow''' 에서 보았던 것들과 매우 유사해 보입니다.
{{CommentSqueak|당신이 stack trace 의 정보들중 어떤 라인을 클릭해도, 디버거가 열립니다. 디버거는 문제가 생긴 해당 메서드에 이미 포커스된 상태로 열리게 됩니다.}}




Line 38: Line 36:




그럼에도 불구하고, 디버거(the debugger)는 메소드 브라우저와 스텍흔적(the stack trace)을 결합하여, 여러분이 스텍흔적에서 라인을 선택할 때, 대응 메소드(the corresponding method)가 아래의 패널에 보이게 만듭니다. 에러를 발생시킨 실행이, 여전히 여러분의 이미지에 있게 하고, 대기 상태에 있는 것을 인식하는 것이 중요합니다. 스택 추적의 각 라인은 실행을 지속시키기 위해 필요한 모든 정보를 포함하는 실행스텍(execution stack)에 프레임을 나타냅니다. 이것은 그것들의 인스턴스 변수들과 실행 메소드들의 모든 임시 변수들과 함께 연산에 관여된 모든 오브젝트를 포함합니다.
그림 6.25에 디버거가 보입니다; 이 디버거는 처음에 매우 어렵게 보이지만, 사용하기는 꽤 쉽습니다. 타이틀 바와 상단 패널은 '''PreDebugWindow''' 에서 보았던 것들과 매우 흡사합니다. 오류가 일어난 실행부분이 당신의 이미지에 그상태 그대로 존재한다는것은 중요한 사실입니다. 대기상태기는 하지만요. stack trace (추적) 각 라인은 실행 stack 의 프레임을 나타내는데 그 프레임이란것은 실행을 연속적으로 수행하기 위해 필요한 모든 정보를 포함하고 있습니다. 실행 stack 의 프레임은 인스턴스 변수들, 실행 메서드들의 모든 임시 변수들 및 연산에 관련된 모든 객체를 포함합니다.
 
그림 6.25 를 보면, 상단 패널에서 detect:ifNone: 메서드를 선택했습니다. 선택한 메서드의 내용은 중앙의 패널에 표시됩니다; 파란색으로 강조된 value 메세지는 현재 보이는 메서드에서 value 메세지를 보냈으며 답변을 기다리고 있음을 나타냅니다.
 
디버거의 아래에 있는 4개의 패널은 실제로 두 개의 mini inspector(Workspace 패널은 빠진상태)입니다. 왼쪽의 Inspector 는 현재 객체에 관련된 내용을 보여주며, 이 경우 현재 객체는 가운데 패널의 내용중 self 가 가리키는 객체를 의미합니다. 상단의 목록에서 다른 stack 프레임을 선택하면, self의 아이덴티티는 변경될 수 있으며, self-inspector의 내용도 바뀔 수 있습니다. 좌하단 패널에서 self를 클릭하면 이미 예상했던대로 self의 값은 인터벌 (10 to:1 by -1) 임을 알 수 있습니다. 디버거의 mini inspectors 에서는 Workspace 패널이 필요없는데, 왜냐하면 모든 변수들의 범위는 위쪽의 메서드 패널의 내용에 한정되기 때문입니다; 가운데의 메서드 패널에서 입력을 하거나 또는 표현식들을 선택하고 그것들을 실행해보는 작업을 마음대로 할 수 있기 때문이죠. 언제나, 메뉴 또는 CMD-i를 사용하면 변경사항을 {{Template:HighlightGray|cancel (l)}}(취소)할 수 있습니다.
 
우측 하단의 Inspector 는 현재 시점의 임시 변수를 보여줍니다. 그림 6.25에서, exceptionBlock 의 매개변수로 value가 보내졌습니다.
 


그림 6.25에서, 우리는 상단 패널에서 detect:ifNone: method를 선택하였습니다. 메소드 바디는 중앙 패널에 표시되며, 메시지 주위에 파랑색으로 강조된 value는 메시지 value에 발송된 현재 값을 보여드리며, 답변을 기다리고 있는 상태입니다.
{{CommentSqueak|이 파라미터의 현재 값을 보기 위해, 우측하단의 context Inspector 에서 exceptionBlock 을 클릭 합니다. 클릭하면 exceptionBlock 의 값이 [self errorNotFound: ...] 임을 알려줄겁니다. 변수의 값으로 해당되는 오류메세지가 보이는건 당연한 일입니다.}}


디버거의 아래에 있는 4개의 패널은 실제로 두 개의 miniinspector입니다. 워크스페이스 패널들 없이) 왼쪽의 인스펙터(inspector)는 중앙 창(the center pane)에서 self라 작명된 현재 오브젝트를 보여드립니다. 여러분이 스텍 프레임(stack frames)을 선택함에 따라, self의 아이덴티티는 변경될 수 있으며, 그리고 self- 인스펙터(the self-inspector)의 컨텐츠도 변경될 수 있습니다. 아래 왼쪽 패널에서 여러분이 self를 클릭하면 self가 인터벌( 10 to:1 by—1),이며 우리가 기대했던 것이라는 것을 알게 될 것입니다. 워크스페이스 패널은 디버거의 미니 인스펙터에 필요하지 않습니다. 그 이유는 모든 변수들은 메소드 패널의 범위(scope)에 있게 될 것이므로, 이 패널에서 타이핑을 하거나 또는 표현식들을 선택하고 그것들을 평가하는 작업을 자유롭게 할 수 있습니다. 여려분은 항상, 메뉴 또는 CMD-i를 사용하여 여러분의 변경사항들을 {{Template:HighlightGray|cancel (l)}}할 수 있습니다.


오른쪽에 있는 인스펙터는 현재 상황에서의(current context) 임시 변수를 보여드립니다. 그림 6.25에서, value는 파라미터 exceptionBlock에 발송되었습니다.  
그건 그렇고, 디버그창 아래의 mini inspector 에 보이는 변수들중 한가지에 대해 full inspector 또는 오브젝트 탐색기를 열기 원하는 경우에는, 변수의 이름을 더블클릭(inspect)을 하거나 또는 변수의 이름을 선택하고 노랑 버튼 메뉴에서 {{Template:HighlightGray|inspect (i)}} 또는 {{Template:HighlightGray|explore (I)}} 를 선택하면 됩니다. 이렇게 디버깅중 변수를 inpect 또는 explore 하는 작업은 다른 코드를 실행하는 동안 어떻게 변수가 변하는지 보고싶을때 유용합니다.


이 파라미터의 현재 값을 보기 위해, 컨텍스트 인스펙터(context inspector)에서 exceptionBlock을클릭 합니다. 이것은 여러분에게 exceptionBlock가 [self errorNotFound: ...]임을 말해드릴 것입니다. 그러므로 우리가 대응하는 에러메시지를 보게 되는 것은 놀랄만한 일이 아닙니다. 만약 여러분히 우연히 미니 인스펙터에 보이는 변수들 중의 하나에 풀-인스펙터(full inspector) 또는 익스플로러를 열기를 원하신다면, 변수의 이름에 더블클릭을 하거나 또는 변수의 이름을 선택하고 노랑 버튼 메뉴에서 {{Template:HighlightGray|inspect (i)}} 또는 {{Template:HighlightGray|explore (I)}} 를 선택하셔야 합니다. 이 작업은 여러분이 다른 코드를 실행하는 동안 변수가 변하는 방식을 보기를 원하실 때 유용합니다.  
위쪽의 stack trace창에서 suffix 부분을 클릭한후 메서드가 표시되는 패널을 보면, 문자열 'readme.txt' 에 대해 메서드의 끝에서 두 번째 라인이 점(dot)을 찾아낼거라 생각했습니다만, 메서드가 동작되는 일련의 작업은 마지막 라인까지 진행되지 못했음을 알 수 있습니다. 스퀵은 디버깅중 역실행을 허용하지 않고, 메서드를 처음부터 재시작하게 하며, 사실 이런방법은 객체를 변형시키는게 아니라 새로운 객체를 만드는 코드에서 매우 잘 동작합니다.


메소드 창 보기로 돌아가면, 우리는 문자열 ‘readme.txt’에 있는 dot를 찾기 위해 메소드의 끝에서 두 번째 라인을 기대했으며, 그리고 그 실행은 결코 마지막 라인에 도달하지 못했다는 것을 알 수 있습니다. 스퀵은 우리가 실행한 다음 뒤로 가도록 허용하지 않으며, 메소드를 다시 시작하여, 오브젝트를 변형하지 않고 새로운 오브젝트를 만드는 작업에 매우 잘 들어맞도록 안내합니다.


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


{{CommentSqueak|{{Template:HighlightBox|Restart}}를 클릭하면, 현재 메소드의 첫 번째 표현(statement)로 돌아가는 실행의 중심 자리를 보게 될 것입니다. 파랑색 강조는 다음 메시지가 {{Template:HighlightBold|do:}}에 발송될 것이라는 것을 보여드립니다. (그림 6.26을 보십시오.)}}


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


{{Template:HighlightBox|Into}} 와 {{Template:HighlightBox|Over}}는 실행을 위한 두 개의 다른 방법을 제공합니다. 만약 여러분이 {{Template:HighlightBox|Over}} 버튼을 클릭하면, 스퀵은 에러가 없을 경우, 첫 번째 단계에서 현재 메시지 발송[the current message-send, (이번 경우엔 do):]을 실행합니다. 그러므로 {{Template:HighlightBox|Over}}는 현재 메소드에서 다음 메시지 발송(the next message-send)으로 데려갈 것이며, 그것은 값(value)이 됩니다: 이 방법은 정확히 우리가 시작했었던 방법이기에 이 지면에서 더 많은 도움이 필요 없을 것입니다. 우리가 해야 할 작업은 do:가 우리가 찾고 있는 문자를 찾지 못하는 이유를 알아내는 것입니다.


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


{{CommentSqueak|{{Template:HighlightBox|Over}}를 클릭하고 그림 6.26에 보이는 상황으로 돌아가기 위해 {{Template:HighlightBox|Restart}}를 클릭합니다.}}


{{CommentSqueak|{{Template:HighlightBox|Over}}를 클릭하고 그림 6.26에 보이는 상황으로 돌아가기 위해 {{Template:HighlightBox|Restart}}를 클릭하십시오.}}


{{CommentSqueak|{{Template:HighlightBox|Into}}를 클릭하면, 스퀵은 이번 경우에 {{Template:HighlightBold|Collection»do:.}}인 강조된 메시지 발송(message-send)에 대응하는 메소드로 들어갑니다.}}


그럼에도 불구하고, 이 작업도 많은 도움이 되지는 않습니다: 우리는 Collection»do가 망가지지 않았다는 것에 대해 어느 정도 자신감이 있습니다. 버그는 우리가 스퀵에게 요청한 작업에 있게 될 확률이 높습니다. {{Template:HighlightBox|Through}}는 이번 사례에서 사용할 적합한 버튼입니다: 우리는 do: 그 자체의 상세한 내용들을 무시하기를 원하고 인수 block(the argument block)의 실행에 집중할 것입니다.  
{{CommentSqueak|{{Template:HighlightBox|Into}}를 클릭하면, 스퀵은 강조된 메시지 전송부분 으로 이동합니다. 이번의 경우는 {{Template:HighlightBold|Collection>>do:.}} 가 되겠군요.}}


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


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


{{CommentSqueak|{{Template:HighlightBox|Through}}를 클릭합니다. 컨텍스트 창(the context window)에서 여러분이 하는 방식으로 each를 선택합니다. 여러분은 do: 메소드가 실행하는 10에서 부터 시작하는 each 카운트(count)를 보실 수 있어야만 합니다.}}


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


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


이제 우리는 버그(bug)를 보게 됩니다, 버그수정(fix)은 명확합니다: 우리는 검색을 시작하기 전에 dot를 문자(Character)로 변환시켜야만 합니다.
each가 7이 될 때, 우리는 ifTrue:block이 실행될 것을 기대할 수 있지만, 그것은 실행되지 않습니다. 무엇이 틀렸는지 보기 위해, 그림 6.27 처럼, each의 값이 7일때 value의 {{Template:HighlightBox|Into}} 실행으로 이동합니다.


[[image:steppingIntoValue.png|none|597px|thumb|그림 6.27: Through 버튼으로  do: 메서드의 단계를 여러 번 진행한 후의 디버거]]


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


{{Template:HighlightBox|Into}}를 클릭한 이후, 그림 6.28처럼 메서드 내에서 강조된 위치를 확인할 수 있습니다. 이 상황은 마치 suffix 메서드로 돌아간 것처럼 보이게되는데, 왜냐하면 현재, detect: 에 대한 인수로서 제공된 suffix인 블록을 실행하고 있기 때문입니다. 만약 우측하단의 context inspector 에서 i를 선택한다면, 선택한 i의 현재 값을 보실 수 있으며, 당신이 만약 지금까지 잘 따라오셨다면, 그 값은 반드시 7이 되어야 합니다. 그 다음, 좌측 하단의 self 변수가 있는 인스펙토에서 self의 해당 구성요소를 선택하면 됩니다. 그림 6.28에서, 문자열의 구성요소 7이 문자 46임을 볼 수 있으며, 이것은 실제로 dot문자 입니다. 만약 context inspector 에서 dot를 선택한다면, 변수 dot의 값이 '.' 임을 보게됩니다. 그리고 지금 왜 이것들이 같지 않은지를 확인했습니다: dot은 String인에 비해서 'readme.txt'의 일곱번째 문자는 Character 이기 때문입니다.


[[image:dotIsAString.png|none|596px|thumb|그림 6.28: 'readme.txt' at: 7 가 dot와 동일하지 않은 이유를 보여주는 디버거]]
[[image:dotIsAString.png|none|596px|thumb|그림 6.28: 'readme.txt' at: 7 가 dot와 동일하지 않은 이유를 보여주는 디버거]]




[[image:revertDialog.png|none|594px|thumb|그림 6.29: 디버거에서 suffix 메소드를 변경하기: 내부 블록(inner block)으로부터 나가기(exit) 확인(confirmation)을 요청]]
이제 버그를 확인했고, 수정해야 할 내용은 명확합니다: 'readme.txt' 에 대한 검색을 시작하기 전에 dot를 character 로 변환시켜야만 합니다.
 
 
{{CommentSqueak|디버거에서 {{Template:HighlightBold| dot := FileDirectory dot asCharacter}} 라고 올바르게 할당하는 내용으로 코드를 변경한후, {{Template:HighlightGray|accept}}를 진행해서 변경사항을 적용시킵니다.}}


[[image:revertDialog.png|none|594px|thumb|그림 6.29: 디버거에서 suffix 메서드를 변경하기: 내부 블록으로부터 exit에 대한 confirmation을 요청]]


{{CommentSqueak|디버거 오른쪽에 있는 코드를 변경하면, 할당이(the assaignment) {{Template:HighlightBold| dot &#58;&#61; FileDirectory dot asCharacter}} 를 읽고 변경사항을 {{Template:HighlightGray|accept}} 하게 됩니다.}}


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


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


{{CommentSqueak|{{Template:HighlightBox|Restart}}를 클릭한후 {{Template:HighlightBox|Proceed}} 버튼을 눌러 진행합니다: 디버거 창이 사라지며, 프로그램식 {{Template:HighlightBold|'readme.txt' suffix}} 의 처리가 완료된후, 원하던 결과물인 '.txt'를 'readme.txt' suffiix 를 실행한 원래의 Workspace 에 출력합니다.}}


{{CommentSqueak|{{Template:HighlightBox|Restart}}를 클릭하면 {{Template:HighlightBox|Proceed}}됩니다: 디버거 창이 사라질 것이며, 표현식 {{Template:HighlightBold|'readme.txt'}} suffix의 평가가 완료되고, 그리고 답변 ‘.txt’를 인쇄하게 될 것입니다.}}


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


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


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


메소드 6.2: suffix(접미사) 메소드를 위한 단순한 테스트
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
testSuffixFound
testSuffixFound
Line 105: Line 109:




워크스페이스에서 동일한 테스트를 실행하는 것보다 좀더 많은 노력이 필요했지만, Sunit을 사용하는 것은 실행 가능한 문서로서 테스트를 저장하고, 다른 사람들이 그 테스트를 실행하기 쉽게 만듭니다. 더욱이, 만약 여러분이 메소드 6.2를 클래스 StingTest에 추가하고, Sunit과 함게 그 테스트 스위트(test suite)를 실행하였다면, 디버깅 에러로 매우 신속하게 돌아갈 있을 것입니다. Sunit은 실패하는 주장(the failing assertion)디버그를 열지만, 여러분은 단지 스텍으로 다시 돌아가 한 프레임을 낮추기만 하시면 되며, 테스트를 {{Template:HighlightBox|Restart}} 하고, suffix 메소드로 {{Template:HighlightBox|Into}} 하면, 우리가 그림 6.30에서 하였던 것처럼, 에러를 수정할 수 있습니다. 그 다음, 두 번째로 해야 할 작업은 단지 Sunit Test Runner에 있는 {{Template:HighlightBox|Run Failures}} 버튼을 클릭하고, 지금 패스중인 테스트를 확인(confirm)하는 것입니다.  
[[image:fixOffByOne.png|none|770px|thumb|그림 6.30: 디버거에서 suffix 메서드 변경하기: SUnit assertion failure 이후, the off-by one 오류를 수정하기.]]
 
 
SUnit을 사용하는 방법은 Workspace 에서 동일한 테스트를 실행하는 것보다 좀더 많은 노력이 필요합니다만, SUnit을 사용하면 실행이 가능한 문서로서 테스트를 저장하고, 다른 사람들이 그 테스트를 실행하기 쉽게 만들어줍니다. 게다가, 만약 StringTest 클래스에 '''메서드 6.2''' 를 추가하고, Test Runner에서 SUnit의 테스트세트를 실행한다면, Workspace 에서 입력할때보다 디버깅 오류를 더 빠르게 불러낼 있습니다. Sunit은 실패한 검증사항<sup>the failing assertion</sup>대해 디버거를 엽니다만, 해야할일은 아까처럼 복잡하지 않으며 단지 디버거의 stack을 보고 stack 프레임을 한단계 낮추고, 낮춘 프레임의 내용에서 테스트를 {{Template:HighlightBox|Restart}} 하고, suffix 메서드로 {{Template:HighlightBox|Into}} 하면, 그림 6.30에서 볼수있듯이, 오류를 수정할 수 있습니다. 이렇게 오류를 수정한 후, 다음으로 해야 할 작업은 단지 Sunit Test Runner에 있는 {{Template:HighlightBox|Run Failures}} 버튼을 클릭하고, 지금 진행중인 테스트를 확인(confirm)하는 것입니다.
 


여기에 위의 메서드보다 더 발전된 테스트 내용이 있습니다:


여기에 더 나은 테스트 가 있습니다:
메서드 6.3 suffix 메서드를 위한 더 나은 테스트


메소드 6.3 suffix 메소드를 위한 더 나은 테스트
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
testSuffixFound
testSuffixFound
Line 118: Line 126:




왜 이 테스트가 더 나을 까요? 그 이유는 이 테스트는 타켓 문자열(String)에 하나의 (dot) 이상의 것이 있을 경우, reader에게 메소드가 무엇을 해야 할지를 알려주기 때문입니다.
왜 이 테스트가 더 좋은걸까요? 이 테스트는 대상 문자열에 하나 이상의 dot(점)이 있는 경우, 테스트메서드를 읽는<ref name="역자주1">이미 앞부분에서 설명했듯이 Test unit을 작성하는것은 실행가능한 문서로서의 의미이기때문에 이런 표현을 쓰지 않았나 싶습니다</ref> 사람에에게 메서드가 무엇을 해야 할지를 알려주기 때문입니다.
 
디버거에 오류와 주장 실패(assertion failures)를 잡아내는 작업 뿐만 아니라, 디버거에 들어가는 몇 가지 다른 방법들이 있습니다. 사용자가 무한 루프에 들어가는 코드를 실행하면, 그 코드를 인터럽트하고, '''CMD-.(dot)''을 (dot은 마침표 또는 구두점이라고 부르죠)<ref name="주석6-3">CMD-SHIFT-.(dot)를 입력하여 언제든지 비상용 디버거를 불러올 수 있다는 것을 알아두는것도 중요합니다.</ref>입력하여 연산진행중에 디버거를 열 수 있습니다. 또한 디버거를 열기위해 의심되는 코드에 self halt 를 삽입하는 방법도 있습니다. 예를들어, suffix 메서드를 편집한 내용을 보도록 하죠.


디버거에 에러와 주장 실패(assertion failures)를 잡아내는 작업 뿐만 아니라, 디버거에 들어가는 몇 가지 다른 방법들이 있습니다. 만약 여러분이 무한 루프(infinite loop)에 들어가는 코드를 실행하면, 그 코드를 인터럽트하고, '''CMD- ''를 (이것은 여러분이 어디서 영어를 배웠는 가에 따라 그것은 마침표 또는 하나의 구두점으로 부를 수 있습니다)<ref name="주석6-3">CMD-SHIFT-를 타이핑하여 언제든지 응급 디버거(emergency debugger)를 불러올 수 있다는 것을 아는 것 또한 중요합니다.</ref>타이핑하여 연산 중에 디버거를 열 수 있습니다. 여러분은 또한 self halt를 삽입하기 위해 의심스러운 코드를 편집할 수 있습니다.그러므로 예를 들어, 우리가 다음과 같이 읽힐 suffix 메소드를 편집할 수 있을 것입니다.


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


메소드 6.4 suffix 메소드에 halt 삽입하기
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
suffix
suffix
Line 135: Line 144:




우리가 이 메소드를 실행할 때, self halt의 실행은 우리가 진행할 수 있는 장소에 pre-debugger를 불러오거나 또는 디버거로 들어가 변수들을 보고, 연산을 진행하며(step), 코드를 수정시킵니다.
우리가 이 메서드를 실행할 때, self halt의 실행은 우리가 진행할 수 있는 장소에 pre-debugger를 불러오거나 또는 디버거로 들어가 변수들을 보고, 연산을 한 단계씩 진행하며(step), 코드를 수정합니다.
이것이 디버거에 관한 모든 내용이지만, suffix 메소드에 관한 모든 내용은 아닙니다. 초기 버그는 타켓 문자열(the target string)에 아무 dot도 없으며 suffix 메소드는 에러를 발생시킬 것이라는 것을 알게 해드렸어야 합니다.
 
 
[[image:fixOffByOne.png|none|770px|thumb|그림 6.30: 디버거에서 suffix 메소드 변경하기: SUnit assertion failure 이후, the off-by one 에러를 수정하기.]]


지금까지의 모든 내용은 디버거에는 해당됩니다만, suffix 메서드에 대해서 전부 설명이 된건 아닙니다. suffix 메서드를 실행했을때 문자열에서 아무런 dot(점)이 없는경우에 첫 버그가 발생된다는걸 알아야 하거든요. 이렇게 버그가 일어나는 원하는건 아니기때문에, 이번 예제에서는 dot(점)이 문자열에 없는경우, 어떤 일이 일어나야 할지를 지정하기 위한 두 번째 테스트 메서드를 추가하도록 하겠습니다.


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


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


메소드 6.5: 메소드를 위한 두 번째 테스트: 타켓은 suffix(접미사)를 갖고 있지 않습니다.
<syntaxhighlight lang="smalltalk">
<syntaxhighlight lang="smalltalk">
testSuffixNotFound
testSuffixNotFound
Line 152: Line 157:




{{CommentSqueak|메소드 6.5를 클래스 {{Template:HighlightBold|StingTest}}에 있는 테스트 스위트(the test suite)에 추가하고, 그 테스트가 에러를 발생시기는 것을 지켜봅니다. SUnit에서 에러가 있는 테스트를 선택하여 디버거에 들어가고, 코드를 수정하여, 테스트가 패스되게 합니다. 이 작업을 가장 쉽고 분명하게 하는 방법은, 두 번째 인수 블록(block)은 단순히 문자열 크기(the string size)를 리턴하는 위치인 {{Template:HighlightBold|detect: ifNone}}으로 {{Template:HighlightBold|detect:}} 메시지를 교체하는 것입니다.}}
{{CommentSqueak|메서드 6.5를 클래스 {{Template:HighlightBold|StingTest}}에 있는 테스트 세트에 추가하고, 그 테스트를 통해 오류를 발생하는 것을 지켜봅니다. SUnit에서 오류가 있는 테스트를 선택해서 디버거로 들어간후, 코드를 수정하여, 테스트를 통과되게끔 합니다. 오류를 수정하는 가장 쉽고 정확한 방법은, suffix 메서드의 {{Template:HighlightBold|detect:}} 대신, {{Template:HighlightBold|detect:ifNone:}}을 이용해서 detect:ifNone: 두번째 인수를 단순히 문자열의 크기를 반환하는걸로 처리하는 겁니다.<ref name="역자주2">제 경우는 이걸 '''detect:[ :i | (self at: i) = dot ] ifNone:[self size].''' 이런식으로 처리했습니다. 일단 에러는 안나는군요. 맞게처리한건지는 잘 모르겠습니다.</ref>}}
 


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


우리는 7장에서 SUnit에 관하여 좀더 배울 것입니다.





Latest revision as of 02:14, 17 September 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 를 작성하는 대신에, Workspace 에서 'readme.txt' suffix 를 입력한 다음 print it (p) 을 진행합니다. 이야! 대단하네요? 기대했던 결과인 'txt'를 얻는 대신에, 그림 6.24에서 보이는 것처럼, PreDebugWindow 를 볼 수 있습니다.


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


PreDebugwindow에는 어떤 오류가 발생했는지에 대해 알려주는 타이틀바(title-bar)가 있으며, 오류를 발생시켰던 메시지의 stack trace 상태를 출력합니다. trace의 출력은 아래쪽부터 시작해서 위쪽으로보면 됩니다. UndefinedObject>>DoIt 은 실행되는 컴파일된 코드를 나타내며, 사용자가 Workspace 에서 '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(Workspace 패널은 빠진상태)입니다. 왼쪽의 Inspector 는 현재 객체에 관련된 내용을 보여주며, 이 경우 현재 객체는 가운데 패널의 내용중 self 가 가리키는 객체를 의미합니다. 상단의 목록에서 다른 stack 프레임을 선택하면, self의 아이덴티티는 변경될 수 있으며, self-inspector의 내용도 바뀔 수 있습니다. 좌하단 패널에서 self를 클릭하면 이미 예상했던대로 self의 값은 인터벌 (10 to:1 by -1) 임을 알 수 있습니다. 디버거의 mini inspectors 에서는 Workspace 패널이 필요없는데, 왜냐하면 모든 변수들의 범위는 위쪽의 메서드 패널의 내용에 한정되기 때문입니다; 가운데의 메서드 패널에서 입력을 하거나 또는 표현식들을 선택하고 그것들을 실행해보는 작업을 마음대로 할 수 있기 때문이죠. 언제나, 메뉴 또는 CMD-i를 사용하면 변경사항을 cancel (l)(취소)할 수 있습니다.

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


Squeak comment.png이 파라미터의 현재 값을 보기 위해, 우측하단의 context Inspector 에서 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.png그림 6.26의 환경에서 Through를 클릭합니다. 이후 우측 하단의 컨텍스트 창에 each가 생기면 늘 하던 방식대로 each를 선택하십시오. do: 메서드가 실행되는 10에서 부터 시작되며 진행되는 각각의 카운트를 볼 수 있습니다.


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

그림 6.27: Through 버튼으로 do: 메서드의 단계를 여러 번 진행한 후의 디버거


Into를 클릭한 이후, 그림 6.28처럼 메서드 내에서 강조된 위치를 확인할 수 있습니다. 이 상황은 마치 suffix 메서드로 돌아간 것처럼 보이게되는데, 왜냐하면 현재, detect: 에 대한 인수로서 제공된 suffix인 블록을 실행하고 있기 때문입니다. 만약 우측하단의 context inspector 에서 i를 선택한다면, 선택한 i의 현재 값을 보실 수 있으며, 당신이 만약 지금까지 잘 따라오셨다면, 그 값은 반드시 7이 되어야 합니다. 그 다음, 좌측 하단의 self 변수가 있는 인스펙토에서 self의 해당 구성요소를 선택하면 됩니다. 그림 6.28에서, 문자열의 구성요소 7이 문자 46임을 볼 수 있으며, 이것은 실제로 dot문자 입니다. 만약 context inspector 에서 dot를 선택한다면, 변수 dot의 값이 '.' 임을 보게됩니다. 그리고 지금 왜 이것들이 같지 않은지를 확인했습니다: dot은 String인에 비해서 'readme.txt'의 일곱번째 문자는 Character 이기 때문입니다.

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


이제 버그를 확인했고, 수정해야 할 내용은 명확합니다: 'readme.txt' 에 대한 검색을 시작하기 전에 dot를 character 로 변환시켜야만 합니다.


Squeak comment.png디버거에서 {{{1}}} 라고 올바르게 할당하는 내용으로 코드를 변경한후, accept를 진행해서 변경사항을 적용시킵니다.

그림 6.29: 디버거에서 suffix 메서드를 변경하기: 내부 블록으로부터 exit에 대한 confirmation을 요청


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


Squeak comment.pngRestart를 클릭한후 Proceed 버튼을 눌러 진행합니다: 디버거 창이 사라지며, 프로그램식 'readme.txt' suffix 의 처리가 완료된후, 원하던 결과물인 '.txt'를 'readme.txt' suffiix 를 실행한 원래의 Workspace 에 출력합니다.


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


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

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


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


SUnit을 사용하는 방법은 Workspace 에서 동일한 테스트를 실행하는 것보다 좀더 많은 노력이 필요합니다만, SUnit을 사용하면 실행이 가능한 문서로서 테스트를 저장하고, 다른 사람들이 그 테스트를 실행하기 쉽게 만들어줍니다. 게다가, 만약 StringTest 클래스에 메서드 6.2 를 추가하고, Test Runner에서 SUnit의 테스트세트를 실행한다면, Workspace 에서 입력할때보다 디버깅 오류를 더 빠르게 불러낼 수 있습니다. Sunit은 실패한 검증사항the failing assertion에 대해 디버거를 엽니다만, 해야할일은 아까처럼 복잡하지 않으며 단지 디버거의 stack을 보고 stack 프레임을 한단계 낮추고, 낮춘 프레임의 내용에서 테스트를 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'


왜 이 테스트가 더 좋은걸까요? 이 테스트는 대상 문자열에 하나 이상의 dot(점)이 있는 경우, 테스트메서드를 읽는[1] 사람에에게 메서드가 무엇을 해야 할지를 알려주기 때문입니다.

디버거에 오류와 주장 실패(assertion failures)를 잡아내는 작업 뿐만 아니라, 디버거에 들어가는 몇 가지 다른 방법들이 있습니다. 사용자가 무한 루프에 들어가는 코드를 실행하면, 그 코드를 인터럽트하고, 'CMD-.(dot)을 (dot은 마침표 또는 구두점이라고 부르죠)[2]입력하여 연산진행중에 디버거를 열 수 있습니다. 또한 디버거를 열기위해 의심되는 코드에 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 메서드를 실행했을때 문자열에서 아무런 dot(점)이 없는경우에 첫 버그가 발생된다는걸 알아야 하거든요. 이렇게 버그가 일어나는 원하는건 아니기때문에, 이번 예제에서는 dot(점)이 문자열에 없는경우, 어떤 일이 일어나야 할지를 지정하기 위한 두 번째 테스트 메서드를 추가하도록 하겠습니다.


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

testSuffixNotFound
  self assert: 'readme' suffix = ''


Squeak comment.png메서드 6.5를 클래스 StingTest에 있는 테스트 세트에 추가하고, 그 테스트를 통해 오류를 발생하는 것을 지켜봅니다. SUnit에서 오류가 있는 테스트를 선택해서 디버거로 들어간후, 코드를 수정하여, 테스트를 통과되게끔 합니다. 오류를 수정하는 가장 쉽고 정확한 방법은, suffix 메서드의 detect: 대신, detect:ifNone:을 이용해서 detect:ifNone: 두번째 인수를 단순히 문자열의 크기를 반환하는걸로 처리하는 겁니다.[3]


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


Notes

  1. 이미 앞부분에서 설명했듯이 Test unit을 작성하는것은 실행가능한 문서로서의 의미이기때문에 이런 표현을 쓰지 않았나 싶습니다
  2. CMD-SHIFT-.(dot)를 입력하여 언제든지 비상용 디버거를 불러올 수 있다는 것을 알아두는것도 중요합니다.
  3. 제 경우는 이걸 detect:[ :i | (self at: i) = dot ] ifNone:[self size]. 이런식으로 처리했습니다. 일단 에러는 안나는군요. 맞게처리한건지는 잘 모르겠습니다.