SqueakByExample:6.5

From 흡혈양파의 번역工房
Revision as of 07:36, 13 March 2013 by Onionmixer (talk | contribs) (번역수정)
Jump to navigation Jump to search

디버거(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 trace 에서 보기 원하는 라인을 선택할 때, 해당 메서드를 아래의 패널에 출력합니다. 오류를 발생시킨 실행이, 여전히 여러분의 이미지에 있지만, 그것이 대기 상태에 있다는 것을 인식하는 것은 중요합니다. stack trace 의 각 라인은 실행을 지속시키기 위해 필요한 모든 정보를 포함하는 실행 스택의 프레임을 나타냅니다. 실행 스택의 프레임은 인스턴스 변수들, 실행 메서드들의 모든 임시 변수들과 함께 연산에 관련된 모든 오브젝트를 포함합니다.

그림 6.25 를 보면, 상단 패널에서 detect:ifNone: 메서드를 선택했습니다. 메서드의 내용은 가운데 패널에 표시되며, 메시지 주위에 파랑색으로 강조된 value는 메시지 value에 발송된 현재 값을 보여주며, 답변을 기다리고 있는 상태임을 나타냅니다.

디버거의 아래에 있는 4개의 패널은 실제로 두 개의 mini inspector(워크스페이스 패널이 제외된)입니다. 왼쪽의 인스펙터는 중심 창에서 self라 작명된 현재 오브젝트를 보여드립니다. 여러분이 stack 프레임을 선택하면, self의 아이덴티티는 변경될 수 있으며, self-inspector의 내용도 바뀔 수 있습니다. 아래 왼쪽 패널에서 여러분이 self를 클릭하면 self는 인터벌 (10 to:1 by -1), 이며 예상했던대로라는것을 알 수 있습니다. 워크스페이스 패널은 디버거의 미니 인스펙터에는 필요없습니다. 왜냐하면 모든 변수들이 메서드 패널의 영역에 있기때문에, 이 패널에서 입력을 하거나 또는 표현식들을 선택하고 그것들을 실행해보는 작업을 자유롭게 할 수 있기 때문입니다. 언제나, 메뉴 또는 CMD-i를 사용하여 여러분의 변경사항들을 cancel (l)할 수 있습니다.

오른쪽에 있는 인스펙터는 현재 상황의 임시 변수를 보여줍니다. 그림 6.25에서, 파란색으로 강조된 부분덕에 value는 파라미터 exceptionBlock 에 발송되었다는걸 알 수 있습니다.

이 파라미터의 현재 값을 보기 위해, 상황 인스펙터에서 exceptionBlock 을 클릭 합니다. 이것은 exceptionBlock 이 [self errorNotFound: ...] 임을 알려줄겁니다. 그러므로 대응하는 오류메시지를 보더라도 놀라지 마세요. 만약 우연히 미니 인스펙터에 보이는 변수들 중의 하나에 풀-인스펙터(full inspector) 또는 오브젝트 탐색기를 열기를 원하는경우, 변수의 이름에 더블클릭을 하거나 또는 변수의 이름을 선택하고 노랑 버튼 메뉴에서 inspect (i) 또는 explore (I) 를 선택하시면 됩니다. 이렇게 디버깅중 inpect 또는 explore 하는 작업은 다른 코드를 실행하는 동안 변수가 변하는 방식을 보기원할때 유용합니다.

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


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


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


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


Squeak comment.pngInto를 클릭하면, 스퀵은 이번 경우에 Collection»do:.인 강조된 메시지 발송(message-send)에 대응하는 메서드로 들어가십시오.

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


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


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

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

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

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


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


그림 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-를 입력하여 언제든지 응급 디버거를 불러올 수 있다는 것을 아는 것 또한 중요합니다.