VisualWorksTutorial1:Page10

From 흡혈양파의 번역工房
Jump to navigation Jump to search

cincom_tutorial_vwlogo
cincom_tutorial_cincomlogo

웹로그 통계 레슨 9
인기 컨테스트

cincom_tutorial_stlogo

| 목차 | 레슨8 | 레슨10 |
cincom_tutorial_openbook 앞서 유니크한 Hit수(중복되는 Hit는 포함하지 않는)를 카운트 했습니다. 이번에는 가장 인기있는 웹사이트 페이지를 조사 하겠습니다. 우선 수작업으로 페이지 억세스 횟수를 카운트 해보십시오. 카운트 하는 건 간단합니다. 로그파일 안에 있는 Hit를 보기만 하면 됩니다. 여기서 중요한 작업으로서 로그파일에는 JPG, GIF파일 등의 웹페이지에 부속된 그래픽 파일도 기술되어 있다는 점입니다. 이것들은 카운트 하고 싶지 않습니다.


각 웹페이지의 횟수를 카운트한 것 만으로는 불충분합니다. 상사는 가장 인기있는 페이지나 TOP10까지 페이지 등을 알고싶어 하지 않을까요. 따라서, 여기서는 카운트에 따른 정렬도 합시다.

cincom_tutorial_certificate 이 레슨에서는 두 가지 새로운 Collection 클래스를 도입하겠습니다. 바로 Bag과 SortedCollection입니다. 이 컬렉션(집합)을 왜 사용하는가에 대한 이유로서, 이 컬렉션들이 문제를 어떻게 해결해주는지 설명하겠습니다.
cincom_tutorial_design 웹사이트에 있는 모든 웹페이지의 확장자는 ASP입니다. 따라서 로그파일을 한줄씩 불러와서, 그 행에 ASP페이지가 기술되어 있는 경우에는 카운트할 필요가 없습니다.


Smalltalk에서는 이 계산을 매우 간단히 할 수 있습니다. Smalltalk 배열에서 컬렉션 객체중 하나인 Bag(가방)이 있습니다. Bag 안에는 무엇이든 넣을 수 있습니다. 그리고 Bag에 넣은 내용을 유지합니다. 확실히 가방과 물리적으로 닮았습니다. 더욱이, 집어 넣은 아이템이 중복될 경우 Bag 안에 있는 각 아이템수를 카운트합니다.


계산을 하면 다음으로는 정렬이 필요합니다. Smalltalk에서는 정렬용 컬렉션이 있습니다. 그것은 SortedCollection이라 불리웁니다. Smalltalk는 상이한 컬렉션 내용을 간단히 카피(이동)할 수 있습니다. 따라서, 지금은 두 종류의 컬렉션을 사용하겠습니다. 이로인해 매우 인기있는 웹 페이지를 간단히 찾아낼 수 있습니다.


그러면, 파일 맨 처음부터(수작업 하듯) 시작하겠습니다. "로직"은 다음과 같습니다.

  • 로그파일에서 맨 처음행을 취득합니다. ASP페이지인지 확인합니다. 만일 ASP페이지라면 Bag 객체를 넣습니다.
  • 파일 맨 끝까지 계속 읽어들입니다. 그리고 파일을 닫습니다.
  • Bag 객체 내용을 SortedCollection 객체에(페이지 이름도 같이) 카피합니다.
  • SortedCollection 객체의 TOP10 아이템을 표시합니다.

시작하기 전에 Bag와 SortedCollection 입문서를 읽어주십시오. 이 레슨에서는 각 컬렉션의 상세설명은 하지 않겠습니다.

cincom_tutorial_dominoes.gif 입문서 : Bag
cincom_tutorial_dominoes.gif 입문서 : SortedCollection
cincom_tutorial_question 웹서버 로그파일을 보겠습니다.
그림 9-1. VisualWorks 파일 에디터에 표시된 웹서버 로그파일(3-9그림과 동일그림)

웹페이지는 각 행 맨 마지막 부근에 있습니다. 웹페이지는 슬레시(/)문자로 개시되기 때문에, 페이지 이름의 개시 위치를 알 수 있습니다. 하지만 슬레시 기호를 사용하는 것은 웹페이지 필드 뿐만이 아닙니다. 날짜 필드에도 사용됩니다. 어떻게든 해서 날짜 필드를 건어뛰면, 다음에 오는 슬레시 문자는 웹페이지 개시를 알리는 것이 됩니다. 그러면, 이것을 하기 위한 방법을 표시하겠습니다.

cincom_tutorial_steps 1. 우선 새로운 작업공간을 열어주십시오. 그리고 아래와 같이 텍스트를 입력해주십시오. 텍스트 전체를 반전시키고 <오퍼레이트 클릭>실행을 선택해주십시오.
|stream line | 
stream := 'ws000101.log' asFilename readStream. 
line := stream upTo: Character cr. 
line := line copyFrom: 50 to: line size. 
line := line copyFrom: (line indexOf: $/) to: line size. 
stream close. 
line inspect.

위 코드가 실제 로그파일의 최초행에서 모든 웹페이지를 뽑아낸 것을 인스팩터 화면에서 확인할 수 있습니다.

그림 9-2. 웹페이지 추출성공
cincom_tutorial_question 새로운 메서드가 나왔습니다. 다섯 번째 행은 특히 복잡해 보입니다. 그러면, 코드의 새로운 행을 차례대로 봅시다. 그리고 각 행이 무엇을 실행하고 있는지 이해해주십시오.
|stream line | 
stream := 'ws000101.log' asFilename readStream. 
line := stream upTo: Character cr.

위 행에 새로운 부분은 없습니다. 앞서 진행한 레슨을 봐주십시오. 간단히 설명하자면 Stream 객체로서 파일을 열고, 파일에서 첫줄을 불러옵니다. 실제로는 line 변수에 String 글래스의 인스턴스가 대입되어 끝납니다.


line := line copyFrom: 50 to: line size.

맨 처음 존재하는 슬래쉬(/) 문자를 뛰어넘기 위해 행 가운에서 50문자를 이동합니다. 이동하는 곳은 날짜필드에 있는 슬래쉬(/)를 뛰어넘은 위치가 되겠습니다. 여기서 copyFrom:to: 메서드는 처음 50문자를 잘라내고 51번째 문자에서부터 문자 맨 마지막(행의 맨 마지막)까지 뽑아냅니다.


line := line copyFrom: (line indexOf: $/) to: line size.

일단 보면 복잡해 보입니다만 실제로는 앞에 설명한 행보다 어렵지 않습니다. copyFrom:to: 메서드와 같습니다. copyFrom:에 파라메터 50을 사용하는 대신, 웹페이지 맨 처음에 있는 슬래쉬(/) 기호의 위치를 지정하고 있습니다. indexOf: 메서드는 문자를 파라메터로 받아들였을 경우(여기서는 슬래쉬(/)문자), 문자열 안에 있는 그 문자 위치를 반환합니다. 슬래쉬(/) 문자를 찾기 위해서는 달러($)기호를 슬래쉬(/) 문자 앞에 붙이는 것을 기억해주십시오. 다시 슬래쉬(/) 문자까지 삭제하였습니다. 남은 행(문자열)은 그대로 남아있습니다.


stream close. 
line inspect.

이 행에서 새로운 부분은 없습니다. 앞서 진행한 레슨을 봐주십시오. 간단히 설명하자면, 파일을 닫고(스트림을 닫음에 의해) Inspectorline변수에 보내도록 Smalltalk에 전달합니다.


좀 더 "정리"합시다.(인스팩터 화면에 표시된 콤마나 데쉬) 파일 혹은 모든 ASP 페이지를 수집하는 루프를 정리하겠습니다.

cincom_tutorial_steps 2. 다음과 같이 수정해주십시오. 그리고 텍스트 전체를 반전시키고 <오퍼레이트 클릭>실행을 선택해주십시오.
|stream line bag xFound| 
bag := Bag new. 
stream := 'ws000101.log' asFilename readStream. 
[ stream atEnd ] whileFalse: [ 
line := stream upTo: Character cr. 
line := line copyFrom: 50 to: line size. 
line := line copyFrom: (line indexOf: $/) to: line size. 
line := line copyUpTo: $,. 
xFound := line findString: '.asp' startingAt: 1. 
xFound > 0 
ifTrue:[ bag add: line. ]. ]. 
stream close. 
bag inspect.

Inspector 화면을 보면 위 코드가 로그파일(self를 클릭)에서 모든 ASP 페이지를 추출한 것을 확인할 수 있습니다. 또한 content를 클릭하면 각 페이지 카운터를 볼 수 있습니다.

그림 9-3. ASP 페이지의 컬렉션(Bag)
그림 9-4. Bag 객체에 있는 각 ASP 페이지의 카운트
cincom_tutorial_design 다음으로는 카운트를 정렬할 필요가 있습니다. 정렬하기 위해서는 Bag 객체의 내용을 SortedCollection 객체에 카피할 필요가 있습니다. 하지만 Bag 객체는 단순한 구조가 아닙니다. 그림 9-4를 봐주십시오. content를 클릭했을 때, InspectorBag 객체가 Dictionary 객체를 보유하고 있는 것을 나타내고 있습니다. 즉 Bag 객체는 한 가지 이상의 컬렉션(Set 객체와 상이한)을 보유하고 있다는 것입니다. 실제로는 두 가지 컬렉션이 있습니다. 하나는 웹페이지를, 또 하나는 웹페이지에 관련된 카운트를 보유하고 있습니다. 따라서, Bag 객체를 Set 객체에 이행할 수는 없습니다.


다행히 Smalltalk 창조자들도 이 문제에 직면했었습니다. 그리고 그 딜레마를 해결하였습니다. Association이라 불리우는 연결을 창조하였습니다. 실제, 웹페이지와 관련된(associated) 카운터로 이미 사용되었습니다. Bag 객체의 컬렉션 요소를 반복할 때, 웹페이지와 카운트를 어떻게 같이 뽑아내야 할까요. 물론 Smalltalk 창조자들은 이 문제를 알고 있었습니다. 그리하여, Bag 객체 안에서 양쪽 내용을 뽑아내기 위한 단순한 do: 메서드를 대신하기 위해 valuesAndCountsDo:라 불리우는, 보다 세련된 메서드를 작성하였습니다.


설명은 충분히 했으니 코드를 보도록 합시다.

cincom_tutorial_steps 3. 다음과 같이 수정해주십시오. 그리고 텍스트를 전부 반전시키고 <오퍼레이트 클릭>실행을 선택해주십시오.
|stream line bag xFound sort| 
bag := Bag new. 
stream := 'ws000101.log' asFilename readStream. 
[ stream atEnd ] whileFalse: [ 
line := stream upTo: Character cr. 
line := line copyFrom: 50 to: line size. 
line := line copyFrom: (line indexOf: $/) to: line size. 
line := line copyUpTo: $,. 
xFound := line findString: '.asp' startingAt: 1. 
xFound > 0 
ifTrue:[ bag add: line. ]. ]. 
stream close. 
sort := SortedCollection sortBlock: [:a :b| a <= b]. 
bag valuesAndCountsDo: [ :each :count | 
sort add: (Association key: count value: each)]. 
sort do: [ :each | Transcript cr; show: each printString.].


Transcript 화면을 보면, 위 코드가 웹페이지의 카운트를 순서대로 정렬(작은순)해서 나타내고 있는 것을 확인할 수 있습니다.

그림 9-5. 가장 인기있는 웹페이지
cincom_tutorial_question 맨 마지막 4행을 설명하겠습니다. 그리고 각 행이 무엇을 하고 있는지 이해해주십시오.
sort := SortedCollection sortBlock: [:a :b | a <= b].

순서(작은순)을 역으로 정렬하고 싶기에, SortedCollection 클래스의 sortBlock: 메서드를 사용합니다. 우선 [:a :b 블록의 구문을 봅시다. 이 블록은 다음과 같이 생각해주십시오. SortedCollection(ab) 안에 있는 두 개의 아이템에 대해서, a보다 크거나 같거나를 b와 비교해서 아이템을 나란히 정렬합니다. 정렬 블록 이외는 SortedCollection의 일반적인 초기화입니다.


bag valuesAndCountsDo: [ :each :count |

이 식은 기본적으로 한 가지를 제외하고 do: 블록과 같습니다. valuesAndCountsDo:는 한 가지가 아닌 두 가지 구성요소를 보유하고 있습니다. 블록 맨 첫 부분에서는 두 가지 임시변수(:each:count)가 선언되어 있습니다. :each 파라메터는 웹페이지 이름에 격납되고, :count 파라메터는 카운트에 관련된 웹 페이지를 격납하고 있습니다.


sort add: (Association key: count value: each)].

이 행이 실제로 제일 열심히 작업을 행하고 있습니다. 맨 처음 key:value 메서드는 연결(Association객체)를 작성합니다. 이 메서드에는 두 가지 파라메터가 필요합니다. 페이지 카운트에 의해 정렬시키고 싶기에, 첫 key: 파라메터에 카운트를, value: 파라메터에 웹페이지를 설정합니다. 연결 후에는 SortedCollection(sort)에 추가하는 것 뿐입니다. 그리고 이 행에는 valueAndCountsDo: 반복 블록의 끝이 있습니다.


sort do: [ :each | Transcript cr; show: each printString

마지막으로 TranscriptSortedCollection 객체 내용을 표시합니다. 여기서는 단순한 do: 블록으로 충분합니다.

cincom_tutorial_lightbulb
이것으로 기본적인 작업은 끝났습니다. 로그파일에서 페이지 카운트를 계산하고, 카운트수로 오름차순 정렬을 하는 13행의 코드가 완성되었습니다.

웹Hit 예시에서 작업한 것 처럼, 프로그래밍에 대한 샹상심을 가지고 코드를 더 연마합시다. 우선, 첫 번째 로그파일의 입력 프롬프트를 표시합니다. 이것으로 시스템상의 모든 로그파일에 대해서 "인기 있는 페이지"의 카운트를 측정할 수 있습니다. 두 번째로, 결과표시를 Transcript가 아닌 다른 무언가에 합니다. 파일로 제출하는 것은 어떻습니까?

cincom_tutorial_steps 4. 다음과 같이 수정해주십시오. 그리고 텍스트 전체를 반전시키고 <오퍼레이트 클릭>실행을 선택해주십시오.
|stream line bag xFound sort file| 
bag := Bag new. 
file := (Dialog request: 'ファイルの入力' initialAnswer: 'ws000101.log') asFilename. 
stream := file readStream. 
[ stream atEnd ] whileFalse: [ 
	line := stream upTo: Character cr. 
	line := line copyFrom: 50 to: line size. 
	line := line copyFrom: (line indexOf: $/) to: line size. 
	line := line copyUpTo: $,. 
	xFound := line findString: '.asp' startingAt: 1. 
	xFound > 0 
	ifTrue:[ bag add: line. ]. ]. 
stream close. 
sort := SortedCollection sortBlock: [:a :b| a <= b]. 
bag valuesAndCountsDo: [ :each :count | 
sort add: (Association key: count value: each)]. 
sort do: [ :each | Transcript cr; show: each printString.].

로그파일의 이름을 입력합니다. 이것으로 첫 개량이 끝납니다. 외부 파일에 결과를 출력합시다.


5. 다음과 같이 수정해주십시오. 그리고 텍스트 전체를 반전시키고 <오퍼레이트 클릭>실행을 선택해주십시오.

|stream line bag xFound sort file out| 
bag := Bag new. 
file := (Dialog request: 'ファイルの入力' initialAnswer: 'ws000101.log') asFilename. 
stream := file readStream. 
[ stream atEnd ] whileFalse: [
	line := stream upTo: Character cr.
	line := line copyFrom: 50 to: line size. 
	line := line copyFrom: (line indexOf: $/) to: line size. 
	line := line copyUpTo: $,. 
	xFound := line findString: '.asp' startingAt: 1. 
	xFound > 0 
	ifTrue:[ bag add: line. ]. ]. 
stream close. 
sort := SortedCollection sortBlock: [:a :b| a <= b]. 
bag valuesAndCountsDo: [ :each :count | 
sort add: (Association key: count value: each)]. 
out := 'output1.txt' asFilename writeStream. 
sort do: [ :each | out cr; nextPutAll: each printString.]. 
out close.
cincom_tutorial_question 마지막 3행을 해설하겠습니다. 그리고 각 행이 무엇을 하는지 이해해주십시오.
out := 'output1.txt' asFilename writeStream.

파일을 연다/작성한다 구문은, 파일을 읽어들일때와 같습니다. 유일하게 다른 점은 readStream 메서드 대신 writeStream 메서드를 사용하고 있다는 것입니다. 만일 output1.txt 파일이 존재하는 경우에는 덮어씁니다. 존재하지 않는 경우에는 새롭게 파일을 작성합니다.


sort do: [ :each | out cr; nextPutAll: each printString.].

do: 메서드로 정렬 컬렉션의 요소를 반복하고, 템퍼러리 변수 each는 컬렉션의 각 요소 값을 보유합니다.


첫 번째 식은 출력파일에 cr을 써내었습니다. 세미콜론이 있는 것에 주의해주십시오. 세미콜론 다음에 오는 식에도 out객체가 리시버가 되는 것을 의미합니다. Stream 객체에 대한 nextPutAll: 메서드는, 객체(정렬 컬렉션의 주어진 요소를 each printString)에 설정된 파라메타를 취득하여, 스트림 안에 "모든 것을 넣는(put all)"것을 의미합니다. 이 처리는 컬렉션 마지막에 도달할 때 까지 반복됩니다.


out close.

스트림의 끝입니다. 스트림은 외부 파일이기 때문에 스트림을 닫습니다.

cincom_tutorial_steps 6. 출력한 파일을 봅시다. VisualWorks File Browser를 사용합니다.(VisualWorks의 메인 런쳐 화면에서) File>>File Browser 메뉴를 선택하거나, 툴바에서 맨 처음 버튼을 클릭하고 File Browser를 열어주십시오. 파일 표시 항목에 out*를 입력하고, '엔터' 혹은 '리턴'을 눌러주십시오. output1.txt 파일이 오른쪽 상단 상에 표시됩니다. 파일을 클릭(선택)해서, 파일 내용을 확인해주십시오. 그림 9-6 참조.


그림 9-6. 출력 파일의 내용
cincom_tutorial_certificate 정리


이 레슨에서는 페이지 이름이나 정렬된 카운트를 보유하기 위한 Bag, Association(연결), SortedCollection 클래스의 편리한 사용법을 학습하였습니다. 다음 워크샵에서는 주어진 디렉토리의 모든 로그파일에서 페이지 카운트를 행하겠습니다. 이것은 주, 월, 년에 의한 페이지 카운트 통계값을 제공합니다.


아래와 같은 내용을 학습하였습니다

  • Bag 컬렉션
  • SortedCollection 컬렉션
  • Association(연결)
  • Bag 객체 내의 아이템 수 카운트
  • 외부 파일의 작성

| 목차 | 레슨8 | 레슨10 |