블로그 이미지
초딩입맛제주아재
하고 싶은 것만 하며 살고 싶다

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
2006. 10. 9. 10:15 Programing/Smalltalk

payroll := OrderedCollection new.

attendanceDate := Date fromString: '2006-10-1'.
attendanceTime := Time fromString: '10:00'.
attendanceDateTime := TimeStamp date: attendanceDate time: attendanceTime.

finishDate := Date fromString: '2006-10-1'.
finishTime := Time fromString: '19:00'.
finishDateTime := TimeStamp date: finishDate time: finishTime.


날짜와 시간으로 출근시간과 퇴근시간에 해당하는 timestamp 를 만들어서
patroll 에 한쌍으로 묶어서 넣으려고 하는데 들어가질 않습니다.

자동완성이 되지 않아 직접 타이핑으로
payroll add: #(attendanceDateTime finishDateTime).
입력을 했더니 입력은 됐으나 이상한 형태로 되었더군요.

#(#attendanceDateTime #finishDateTime)

어떠한 형태의 객체도 다 포함하는 줄 알았던 컬렉션 객체가 말썽을 부리니
멍해지네요 -_-;;

달룟님의 답변

지적하신 부분은 "어떠한 객체도 다 담을 수 있다."와는 상관이 없습니다. Smalltalk문법에서 #(변수명, 변수명) 는 허용되지 않는 표현입니다. #(표현식, 표현식) 역시 마찬가지입니다. #()속에는 literal만 올 수 있습니다. 이 부분은 Ruby에 비해서 불편한 점으로, Smalltalker들 중에는 이 문법을 고치자는 주장도 나오고 있습니다.
파란글씨로 표시된 것처럼, Symbol객체 2개로 된 배열로 인식됩니다. 컴파일된 소스색깔을 보시면 변수명이 아니라 literal로서 컴파일 되었음을 알 수 있습니다.

원하시는 표현은 Array with: attendanceDateTime with: finishDateTime 입니다.


#()는 Array객체를 간략한 표현으로 생성하는 문법입니다. #()로 OrderedCollection객체를 만들 수는 없습니다. 변환은 가능하죠.

#(1 2 3) asOrderedCollection.

혹은

OrderedCollection withAll: #(1 2 3).

with:with:는 Collection 클래스에 있는 매쏘드이기 때문에 Array, OrderedCollection모두 쓸 수 있습니다.

OrderedCollection with: attendanceDateTime with: finishDateTime.

이라고 하면 되죠.



이렇게 하니 된다.

payroll add: (Array with: attendanceDateTime with: finishDateTime).
payroll size. > 1
(payroll at: 1) size. > 2


Collection >> OrderedCollection,Array,SortedCollection...

'Programing > Smalltalk' 카테고리의 다른 글

사용자의 실수를 막아주는 queryCommand:  (0) 2006.10.28
가계부 만들기 - 05  (0) 2006.10.11
가계부 만들기 - 04  (0) 2006.10.08
가계부 만들기 - 03  (0) 2006.10.08
가계부 만들기 - 02  (0) 2006.10.08
posted by 초딩입맛제주아재
2006. 10. 8. 15:21 Programing/Smalltalk
- GUI 만들기 -


1.
Shell 클래스 아래 HouseKeepingBookShell 클래스를 만듭니다.(이것은 MVP에서 P인 Presenter에 해당합니다)

Workspace에서 HouseKeepingBookShell showOn: testBook 을 실행해 봅니다.


창이 새로 열리죠? 그것을 캡쳐해서 올려주세요.






2. Class Browser의 클래스트리에서 HouseKeepingBookShell 의 팝업메뉴 View... / New를 합니다. (이것은 MVP에서 V인 View에 해당합니다) 이름은 그냥 기본으로 있는 Default View로 엔터누릅니다. View Composer가 열렸죠. 1번 문제에서 봤던 창이 있을 겁니다. 이 창을 편집한다는 소리죠. View Composer로 왼쪽에 뷰툴박스를 열어서 ListPresenter.Default View를 찾아 이 창에다 추가해줍니다.(끌어다놓기하면 됩니다) 오른쪽 하단의 속성창에서 name이라는 항목을 찾아 문자열로 book list 라고 이름 지어줍니다. View Composer를 저장하고 닫습니다.


Workspace에서 HouseKeepingBookShell showOn: testBook 을 실행해 봅니다.


창이 새로 열리죠? 그것을 캡쳐해서 올려주세요.




3. HouseKeepingBookShell 에 items라는 객체변수를 만듭니다. createComponents라는 매쏘드를 검색(Definitions of...)해 봅니다. 그 매쏘드들을 보고 흉내내서 ListPresenter를 만들어 items변수에 대입하세요. View Composer로 지어줬던 이름을 정확히 문자열로 제공해야 한다는 점을 유의하세요. 작성하신 createComponents 매쏘드를 답으로 제출해 주세요.


createComponents
super createComponents.

items := self add:  ListPresenter new name: 'book list'.


Workspace에서 HouseKeepingBookShell showOn: testBook 해서 다시 창을 열어봅니다. 리스트가 추가 되었나요? 캡쳐해서 답으로 제출하세요.




4. 그런데 리스트박스가 비어있군요. 내용을 표시해야 합니다.
HouseKeepingBookShell에 model: 이라는 매쏘드를 추가합니다. 이것도 역시 3번 문제와 마찬가지로 model:의 정의를 참고합니다. model: 매쏘드는 이 GUI객체(Shell)에 모델을 넘길 때, 그것을 받아들이는 매쏘드입니다. 문제 3번에서 추가한 리스트는 Shell의 하위창이었죠? 이 하위창도 역시 MVP구조로 되어있기 때문에 모델을 공급해줘야 제대로 동작합니다. 무엇을 넘겨주면 좋을까요?

items model: ????????????????


HouseKeepingBookShell>>model: 매쏘드를 작성한 것을 답으로 제출하세요.


model: aHouseKeepingBook
items model: aHouseKeepingBook items.


Workspace에서 HouseKeepingBookShell showOn: testBook 를 캡쳐해서 답으로 제출하세요.





5. 이제 하위 창을 하나 더 추가 하겠습니다. 방법은 2번~4번에서 했던 것과 마찬가지로 진행하시면됩니다. 객체변수 pocketMoney를 만듭니다. Class Composer의 클래스트리에서 팝업메뉴 View/Edit···· 해서 View Composer를 엽니다. 뷰툴박스에서 NumberPresenter.Default View를 추가하고 이름을 pocket money로 짓고 저장한다음, HouseKeepingBookShell에서 createComponents매쏘드와 model:매쏘드를 수정해서 새 객체변수 pocketMoney도 items처럼 수정해줍니다.


수정된 createComponents매쏘드와 model:매쏘드를 답으로 제출하세요.


pocketMoney := self add: TextPresenter new name: 'pocket money'.


pocketMoney model: aHouseKeepingBook pocketMoney.



Workspace에서 HouseKeepingBookShell showOn: testBook 를 하여 원금과 가계부 항목이 잘 표시된 모습을 캡쳐해서 답으로 제출하세요.





6. 리스트에 아마 an HouseKeepingBookItem이라고만 표시되고 있을 겁니다. 그것은 리스트에게 받은 객체들을 어떤 형태로 표현할 지 말해주지 않았기 때문에 기본적으로 표시되는 문자열만 뵤여주고 있기 때문입니다. 다시 View Composer를 열어서 book list라는 리스트를 선택하면 오른쪽에서 속성으로 getTextBlock이라고 있습니다. selector에 #displayString이라고 되어 있을 겁니다. 그걸 #stringInTheListBox'라고 고쳐주고 저장한다음 View Composer를 닫습니다. 그런 다음 다시 showOn:해서 창을 열어봅니다. 에러가 날 겁니다. 디버거로 매쏘드를 추가하는 방법 기억하시죠? 그 방법으로 HouseKeepingBookItem에 매쏘드를 만들어줍니다. 방금 만든 그 매쏘드는 리스트가 자신의 아이템들인 HouseKeepingBook객체들에게 표시할 문자열을 요구하는 매쏘드입니다. HouseKeepingBookItem의 name과 value를 문자열로 답하는 매쏘드를 작성해보세요.(숙제하는 사람 마음대로) 다시 showOn:합니다.


HouseKeepingBookItems>>stringInTheListBox 매쏘드를 답으로 제출하세요.


stringInTheListBox
^self name,' : ',self value printString


showOn: 하여 리스트에 항목들이 제대로 표시된 모습을 캡처해서 제출하세요.





7. 합산을 표시하는 작은 창도 만들어 보겠습니다. View Composer를 열어 NumberPresenter.Default View를 추가하여 sum이라고 이름 짓습니다.
HouseKeepingBookShell에 sum이라는 객체변수를 만들고 createComponents와 model: 매쏘드를 다시 수정합니다. 모델인 HouseKeepingBook객체에게 sum을 물어서 그 것을 HouseKeepingBookShell의 sum에게 모델로 넘겨주면 되겠군요.


createComponents, model: 를 답으로 제출하세요.


sum := self add: NumberPresenter new name: 'sum'.

sum model: aHouseKeepingBook sum.



합산이 잘 표시된 창을 캡처해서 답으로 제출하세요.




8. 흑자와 적자를 표시하는 창도 만들어 보겠습니다. View Composer를 열어 TextPresenter.Stati text를 추가하여 status이라고 이름 짓습니다.
HouseKeepingBookShell에 status이라는 객체변수를 만들고 createComponents와 model: 매쏘드를 다시 수정합니다. 모델인 HouseKeepingBook객체에게 흑자인지 적자인지 문자열로 답해주는(그런 매쏘드가 있었지요?) 그것을 HouseKeepingBookShell의 status에게 모델로 넘겨주면 되겠군요.


createComponents, model: 를 답으로 제출하세요.


status := self add: TextPresenter new name: 'status'.

status model: aHouseKeepingBook statusString.



흑자/적자가 잘 표시된 창을 캡처해서 답으로 제출하세요.




'Programing > Smalltalk' 카테고리의 다른 글

가계부 만들기 - 05  (0) 2006.10.11
변수 이름으로 배열 만들기 - Array with: 변수명 with: 변수명  (0) 2006.10.09
가계부 만들기 - 03  (0) 2006.10.08
가계부 만들기 - 02  (0) 2006.10.08
가계부 만들기 - 01  (0) 2006.10.08
posted by 초딩입맛제주아재
2006. 10. 8. 15:16 Programing/Smalltalk

1. Class Browser로 Object의 하위 클래스로 HouseKeepingBookItem 클래스를 만드세요.
    객체변수는 name value date 3개입니다.
    아무런 매쏘드도 없는 빈 껍데기 클래스입니다.

Object subclass: #HouseKeepingBookItem
instanceVariableNames: 'name value date'
classVariableNames: ''
poolDictionaries: ''
classInstanceVariableNames: ''


2. 다음을 실행하세요

testItem := HouseKeepingBookItem new.
testItem name: '스포츠신문'.

에러가 납니까? 당연합니다! HouseKeepingBookItem에 name: 이라는 매쏘드를 안만들어 줬으니까요. "Debug"버튼을 누릅니다. 디버깅 창의 왼쪽 상단 리스트에 팝업메뉴를 불러 "implement·······"메뉴를 고릅니다. 어떤 클래스에다 만들지 물어보네요. 답해줍니다.
임시 매쏘드가 만들어졌을 겁니다. 객체변수 name에 인자를 저장하는 매쏘드로 바꿔주고 accept(Ctrl+S)해줍니다.

testItem name: '스포츠신문'.를 다시 한번 해봅니다. 에러가 납니까?

name: aString

name := aString.


3.
다음의 문장들도 하나씩 실행하고 2번과 같이 매쏘드를 만들어줍니다.

testItem value: 500.
testItem date: Date today.
testItem name.
testItem value.
testItem date.

name
^name

value: anInteger
value := anInteger.

value
^value

date: aDate
date := aDate.

date
^date


4.
기존에 Workspace에 housekeepingbook에 저장되어있던 항목들(OrderedCollection type)을 전부 HouseKeepingBookItem 객체로 대체합니다.

housekeepingbook := housekeepingbook collect: [:item| ?????????? ].
??????????에 들어갈 적당한 표현식을 찾으세요.

housekeepingbook := housekeepingbookcollect: [ :each |
         HouseKeepingBookItem  new
                name: (each at: 1);
                value: (each at: 2);
                date: (each at: 3)
].


5.
숙제 1과 2의 문제들을 다 다시 작성해보세요.^^(놀라지 마시고 살짝씩만 바꿔주면 됩니다.) 기존에 OrderedCollection을 쓸 때와 비교했을 때 어떤 점이 달라졌는지 느낌 점을 말씀해주세요.

원하는 값에 바로 접근이 가능해진것 같아요...
마치 연관배열처럼 키값에 의한 access 같은...좀더 편해졌다고 할까요..


6. Class Browser로 Object의 하위 클래스로 HouseKeepingBook 클래스를 만드세요.
    객체변수는 pocketMoney items 2개 입니다.
    아무런 매쏘드도 없는 빈 껍데기 클래스입니다.

Workspace에 있는 변수들 pocketMoney와 housekeepingbook을 각각 testBook의 pocketMoney와 items에 담습니다. 2번과 같은 방법으로 하시면 됩니다.

Object subclass: #HouseKeepingBook
instanceVariableNames: 'pocketMoney items'
classVariableNames: ''
poolDictionaries: ''
classInstanceVariableNames: ''

items: aItem
items := aItem.

items
^items.

pocketMoney: aInteger
pocketMoney := aInteger.

pocketMoney
^pocketMoney.


7. HouseKeepingBook 에 sum 이라는 매쏘드를 만들어서 금액의 총합을 리턴하게 하세요. (원금을 포함하도록 하세요)

sum

^self items inject: pocketMoney into: [
:sum :item |
sum + (item value)
]


8. HouseKeepingBook 에 본인이 흑자인지 물어보는 isSurplus라는 매쏘드를 만드세요. true/false를 리턴해야 합니다. 7번 문제의 sum 매쏘드를 사용해야 합니다.

isSurplus

^(self sum > 0)


9. HouseKeepingBook 에 statusString라는 매쏘드를 만들어, 흑자면, '아싸! 흑자' 적자면 '에고... 적자'라는 문자열을 답하게 하세요. 8번 문제의 isSurplus를 사용해야 합니다.

statusString

self isSurplus ifTrue: [^'아싸!흑자'] ifFalse: [^'에고...적자']


10.
HouseKeepingBook 에 itemsOfYear: 라는 매쏘드를 만들어, 그 해의 항목들을 답하도록 하세요. 숙제 2의 답을 활용하세요.

itemsOfYear: aYear

^self items select: [:each | each date year = aYear]



11. HouseKeepingBook 에 sumOfYear: 라는 매쏘드를 만들어, 그 해 항목들의 합을 답하도록 하세요. itemsOfYear: 매쏘드를 활용하세요. (원금은 포함하지 않습니다)

sumOfYear: aYear

^(self itemsOfYear: aYear) inject: 0 into: [:sum :item | sum + (item value)]

posted by 초딩입맛제주아재
2006. 10. 8. 15:13 Programing/Smalltalk

WorkSpace 에서 끄적거리기 두번째....

select: 의 활용

housekeepingbook := OrderedCollection  new.

housekeepingbook
add: #('복권' -5000 '2005-09-21') asOrderedCollection;
add: #('복권당첨' 50000 '2006-09-21') asOrderedCollection;
add: #('두부' -500 '2006-09-22') asOrderedCollection;
add: #('피자' -15000 '2006-09-23') asOrderedCollection;
add: #('보너스' 100000 '2004-09-24') asOrderedCollection;
add: #('화장지' -7000 '2006-09-24') asOrderedCollection;
add: #('전기세' -25000 '2006-09-24') asOrderedCollection;
add: #('키보드' -15000 '2005-09-25') asOrderedCollection;
add: #('화장품' -55000 '2006-09-25') asOrderedCollection;
add: #('전구' -500 '2004-09-26') asOrderedCollection.

"1.housekeepingbook의 3번째 원소를 Date객체로 바꾸기"
housekeepingbook do: [:each | each at: 3 put: (Date fromString: (each at: 3))].

"2.2004년 항목만 추출"
housekeepingbook select: [:each | (each at: 3) year = 2004].

"3.2004년의 금액 합계"
(housekeepingbook select: [:each | (each at: 3) year = 2004]) inject: 0 into: [:sum :item|  sum + (item at: 2) ].

"4.각 항목들의 요일"
housekeepingbook collect: [:each | (each at: 3) weekday].


"5.category 객체 생성 & 전기세의 항목 이름"
categories := OrderedCollection new.
categories
add: #('두부' '피자' '김치') -> '식료품';
add: #('복권' '복권당첨' '적금') -> '투자';
add: #('월급' '보너스') -> '급여';
add: #('전기세' '수도세') -> '생활요금';
add: #('화장지' '키보드' '화장품' '전구') -> '소모품'.

(categories detect: [:each | each key includes: '전기세']) value.

"6.각 항목의 분류"
housekeepingbook collect: [:item | (categories detect: [:each | each key includes: (item at: 1) ]) value].

"7.소모품으로 분류되는 항목의 합"
(housekeepingbook select: [:each | (categories detect: [:cat | cat value = '소모품']) key includes: (each at: 1) ])
inject: 0 into: [:sum :item | sum + (item at: 2)].

"8.2006년의 소모품의 합"
((housekeepingbook select: [:each | (categories detect: [:cat | cat value = '소모품']) key includes: (each at: 1) ])
select: [:each | (each at: 3) year = 2006]) inject: 0 into: [:sum :item | sum + (item at: 2) ].

"9.원금"
pocketMoney := 4000000.
housekeepingbook inject: pocketMoney into: [:sum :item | sum + (item at: 2) ].

'Programing > Smalltalk' 카테고리의 다른 글

가계부 만들기 - 04  (0) 2006.10.08
가계부 만들기 - 03  (0) 2006.10.08
가계부 만들기 - 01  (0) 2006.10.08
변수와 메세지 그리고 이벤트...  (0) 2006.05.15
재밌다...  (0) 2006.05.07
posted by 초딩입맛제주아재
2006. 10. 8. 15:07 Programing/Smalltalk

WorkSpace 에서 끄적거리기....



"1.OrderCollection 객체생성"
housekeepingbook := OrderedCollection  new.


"2~3.항목추가"
housekeepingbook add: #('복권' -5000 '2006-09-21');
add: #('복권당첨' 50000 '2006-09-21');
add: #('두부' -500 '2006-09-22');
add: #('피자' -15000 '2006-09-23');
add: #('보너스' 100000 '2006-09-24');
add: #('화장지' -7000 '2006-09-24');
add: #('전기세' -25000 '2006-09-24');
add: #('키보드' -15000 '2006-09-25');
add: #('화장품' -55000 '2006-09-25');
add: #('전구' -500 '2006-09-26').


"4.항목 갯수"
housekeepingbook size.


"5.첫번째 항목"
housekeepingbook at: 1.


"6.첫번째 항목의 금액"
(housekeepingbook at: 1) at: 2.


"7.전체 항목의 금액"
housekeepingbook collect: [:each| each at: 2 ]


"8.전체 금액의 합"
(housekeepingbook collect: [:each| each at: 2 ]) inject: 0 into: [:sum :each| each + sum ]


"9.항목의 평균 금액"
avg := ((housekeepingbook collect: [:each| each at: 2 ]) inject: 0 into: [:sum :each| each + sum ]) / housekeepingbook size


"10.흑자 또는 적자"
(avg > 0) ifTrue: ['흑자'] ifFalse: ['적자'].

'Programing > Smalltalk' 카테고리의 다른 글

가계부 만들기 - 04  (0) 2006.10.08
가계부 만들기 - 03  (0) 2006.10.08
가계부 만들기 - 02  (0) 2006.10.08
변수와 메세지 그리고 이벤트...  (0) 2006.05.15
재밌다...  (0) 2006.05.07
posted by 초딩입맛제주아재
2006. 9. 26. 13:12 Programing

출처 : 스몰토크와 객체지향이야기


유니코드란?

국제표준으로 제정된 2바이트계의 만국 공통의 국제 문자부호 체계(UCS: Universal Code System)를 말한다. 애플컴퓨터·IBM·마이크로소프트 등이 컨소시엄으로 설립한 유니코드(Unicode)가 1990년에 첫 버전을 발표하였고, ISO/IEC JTC1에서 1995년 9월 국제표준으로 제정하였다. 공식 명칭은ISO/IEC 10646-1(Universal Multiple-Octet Coded Character Set)이다.


데이터의 교환을 원활하게 하기 위하여 문자 1개에 부여되는 값을 16비트로 통일하였다. 코드의 1문자당 영어는 7비트, 비영어는 8비트, 한글이나 일본어는 16비트의 값을 지니는데, 이를 모두 16비트로 통일한 것이다. ISO/IEC 10646-1의 문자판에는 전세계에서 사용하고 있는 26개 언어의 문자와 특수기호에 대해 일일이 코드값을 부여하고 있다. 최대로 수용할 수 있는 문자수는 6만 5,536자이다. 이 가운데 3만 8,885자는 주요 국가의 언어를 구현하는 용도로 이미 할당되어 있고 6400자는 사용자 정의 영역으로, 나머지는 2만여 자는 새로 추가될 언어 영역으로 각각 비워두고 있다. 코드 할당비율을 보면 한자가 39.89%로 가장 많고, 한글 17.04%, 아스키 및 기호문자 10.39% 등의 순이다.

여기에 포함된 한글 코드 체계는 옛 한글의 자모를 포함한 한글자모 240자(HANGUL JAMO, 11열)와 한국표준인 KSC 5601의 조합형 한글자모 94자(HANGUL COMPATIBILITY, 31열), 한글에서 구현할 수 있는 최대 글자수 1만 1,172자를 가나다순으로 배열해 놓은 완성형(HANGUL, AC열∼D7열) 등 3종으로 되어 있다. 또한 각국의 문자를 2바이트로 수용하기 위해 우리나라와 중국·일본·타이완의 한자를 통합하였다. 한자의 비중이 높은만큼 동양권에서는 서체의 통합과 입출력 방식에 대해 계속 논의를 해오고 있다.

유니코드에서 한글은 십진수로 44032부터 55203까지입니다.
이 유니코드 한글의 패턴을 분석해보면,

1. 초성은 19자로
ㄱ, ㄲ, ㄴ, ㄷ, ㄸ, ㄹ, ㅁ, ㅂ, ㅃ, ㅅ,
ㅆ, ㅇ, ㅈ, ㅉ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ
의 순서이고,

2. 중성은 21자로
ㅏ, ㅐ, ㅑ, ㅒ, ㅓ, ㅔ, ㅕ, ㅖ, ㅗ, ㅘ,
ㅙ, ㅚ, ㅛ, ㅜ, ㅝ, ㅞ, ㅟ, ㅠ, ㅡ, ㅢ,

의 순서,

3. 종성은 28자로
(없음), ㄱ, ㄲ, ㄳ, ㄴ, ㄵ, ㄶ, ㄷ, ㄹ, ㄺ,
ㄻ, ㄼ, ㄽ, ㄾ, ㄿ, ㅀ, ㅁ, ㅂ, ㅄ, ㅅ,
ㅆ, ㅇ, ㅈ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ
의 순서입니다.

따라서 19*21*28 = 11172 = 55203-44032+1 자의 한글이 구성됩니다.

이들 한글 초/중/종성으로 코드를 구성할 때,
초성, 중성, 종성의 순서로 코드를 구성합니다.
즉, 코드 44032는 '가' 이고, 코드 44033은 '각', 코드 44034는 '갂', ...
'가'에 종성 붙이는 것이 끝나면 '개', '객', ...
이런 식입니다.

따라서, 어떤 글자의 코드에서 44032를 빼고, 21*28로 나누면,
0 ~ 18의 숫자를 얻게 되고, 이는 위 초성의 순서와 같습니다.
마찬가지로, 44032를 빼고, 21*28로 나눈 나머지를 다시 28로 나눈 몫은
0 ~ 20이고, 이는 위 중성의 순서와 같습니다.
마지막으로, 44032를 빼고, 21*28로 나눈 나머지를 다시 28로 나눈 나머지는
0 ~ 27이고, 이는 위 종성의 순서와 같습니다.

따라서 char ch 의 종성을 알고자 한다면,
int third = ( ( ch - 44032 ) % ( 21 * 28 ) ) % 28;
로 얻은 int third 값을 이용하면 됩니다.

참고로 초성은
int first = ( ch - 44032 ) / ( 21 * 28 );
중성은
int second = ( ch - 44032 ) % ( 21 * 28 ) / 28;
입니다.

'Programing' 카테고리의 다른 글

Drag & Drop로 구현한 쇼핑몰 샘플  (0) 2006.11.09
JAVA(JSP)가 좋은점이 무엇이냐...  (1) 2006.10.28
Sort Algorithm  (0) 2006.10.24
웹해킹 - SQL 인젝션  (0) 2006.10.14
스파게티 소스  (0) 2006.05.16
posted by 초딩입맛제주아재
2006. 8. 21. 21:02 Programing/아뜰리에

이번에 국제전화카드 쇼핑몰 프로젝트를 끝내고
로그파일을 하루 종일 지켜보고 있어야 하는 상황에서
출근하자마자 그날의 로그파일을 새로 tail -f 로 모니터링 해야했다.
그일이 불편하여 자동으로 새로운 로그파일을 모니터링 할 스크립트를 만들어봤다.

사용법 :
우선 해당 소스를 실행 모드로 설정 : chmod 777 LogViewer
LogViewer 실행
끝~

코드를 보자.
tail -f 를 직접 구현하고자 하니 여간 어려운게 아니었다.
쉬워보여서 뛰어든건데 -_-
하지만 막상 구현이 끝나니...
이게 뭐야 -_-;;;

아무튼 소스는 주석의 내용만으로 충분히 이해가 갈듯하다.

#!/usr/local/php5/bin/php -q
<?php
/**
* FILE_NAME : LogViewer
* AUTHOR    : asrada2001@hotmail.com
* DATE      : 2006-08-21
* DESC      : 로그파일 자동 뷰어
               - 일별 또는 월별 등 날짜로 명명되어진 로그파일을 보여준다.
               - 날짜 변경시 자동으로 다음 로그파일을 보여준다.
* LICENSE   :
**/

$filehead = 'logfile head';     //로그파일구성 = 헤드(유무) + 날짜(항목은 상황에 맞게 설정)
$basic_readline = 5;
$current_file = Null;
$firstopen = true;

while(true){

  $filetail   = date('Ymd');
  $filename   = $filehead.$filetail;

  if($current_file != $filename){
       echo "===============================================================\n";
       echo "##  New LogFile Watching -- ".$filetail."\n";
       echo "===============================================================\n";

       $current_file = $filename;
       $last_pos = Null;
       $firstopen = true;
  }

  if(file_exists($filename) == true){

       if(filesize($filename) == 0){
           continue;
       }

       $readline   = 0;
       $first_pos  = Null;
       $result     = Null;

       $fp = fopen($filename,'r');

       $pos       = -2;                //파일의 마지막 문자가 \n 이므로 -2 부터 시작
       $char      = Null;

       while(fseek($fp,$pos,SEEK_END) == 0){
           $char = fgetc($fp);

           if($char == "\n" || $char == "\r"){
               $str = fgets($fp);
               $this_pos = ftell($fp);

               if($this_pos == false){
                   break;
               }

               $readline++;

               //열려진 파일의 최초 검색된 new line(\n) 의 포인터 위치를 저장하여
               //다음번 로딩시에 이 포인터 이전의 내용은 검색을 제한한다.
               if($first_pos == Null){
                   $first_pos = $this_pos;
               }

               //이전 파일의 마지막 포인터의 위치
               //현재 포인터가 이전 파일의 마지막 포인터와 같은 값을 갖을때 검색을 중지한다.
               if($last_pos == $this_pos){
                   break;
               }

               //출력되어질 로그파일의 내용
               $result = $str.$result;

               //최초 파일 오픈할때는 마지막 5라인 출력
               if($firstopen == true){
                   //echo "============= first_line cashing....".$readline." ========================\n";

                   if($readline == $basic_readline){
                       $firstopen = false;
                       $readline = 0;
                       break;
                   }
               }else{
                   if($last_pos == Null){
                       break;
                   }else{
                       if($this_pos <= $last_pos){
                           break;
                       }
                   }
               }
           }

           $pos--;
       }

       $last_pos = $first_pos;

       if($result != Null){
           echo $result;
       }

       fclose($fp);
       unset($fp);

       sleep(1);

  }
  else{
       echo $filename." << file not found...\n";
       sleep(10);
  }
}
?>

필요에 의한 개발...
필요라는 동기가 무척이나 중요한것 같다.
만약 이번 프로젝트가 없었다면
언제 이런걸 만들어볼까~~~

posted by 초딩입맛제주아재
2006. 7. 24. 01:00 Programing/PHP

PHP를 단순 동적 웹사이트 제작에만 사용하는것은
PHP의 반만 쓰는 것이다.
PHP는 펄,본쉘 같은 쉘스크립트로서의 역할도 훌륭히 수행해 낼 수 있다.
물론 태생이 쉘스크립트인 것들에 비하면 기능이 다소 빈약하지만
웬만한 작업은 해낼수 있다.

요즈음 PHP로 어떤 것들을 할 수 있을까 하는 고민을 하면서
가장 쉽고 빠르게 할 수 있는것을 찾다가
간단한 대화식 프로그램을 짜보기로 했다.

#!/usr/local/php5/bin/php
<?php
set_time_limit(0);

$fd = STDIN;

if (!$fd)
  exit;

$question = "How old are you? ";

echo $question;

while (!feof ($fd)){
  $s = trim(fgets($fd,128));

  if ($s==false){
       continue;
  }

  if($s == 'exit'){
       break;
  }

  if(is_numeric($s) == false){
       echo "\nAre you kidding??\n\n\nTry again...\n\n".$question;
  }

  if($s > 30){
       echo "\nHum...You are not fresh...\n\n";
       break;
  }

  if($s == 28){
       echo "\nOh~~ Very Fantastic age~!!! You are good!!\n";
       break;
  }

  if($s < 30){
       echo "\nWe are the youngman!!!\n";
       break;
  }
}

echo "\n\n======= Bye~ ============!!\n";

?>



위 소스는 간단한... 대화형은 아니고 한번 묻고 한번 답하면 종료한다.
1번 라인에 php의 경로를 지정해 줌으로써 cli 모드로 실행이 되도록 하였으니
chmod로 실행권한을 주면 쉘에서 실행이 가능하다.

위 소스를 응용하면 간단한 머드게임도 가능할것 같다.

GTK와 결합하면 머그게임으로???

'Programing > PHP' 카테고리의 다른 글

[OSX] Eclipse에서 PHP 로컬 개발/테스트 환경 구축하기  (4) 2008.08.30
Enabling PHP and Apache in Leopard  (2) 2008.08.19
WAV파일 분석  (0) 2006.11.10
간단한 소켓통신  (0) 2006.10.13
시스템 모니터링 툴  (0) 2006.07.17
posted by 초딩입맛제주아재
2006. 7. 18. 14:08 Programing/HTML/JavaScript/CSS

xmlhttprequest 객체를 사용하면 참 편리하다.
이른바 AJAX의 핵심이 바로 이 객체가 아닌가 한다.

xmlhttprequest 객체를 사용하여 데이터를 PHP파일로 송신하면
데이터는 utf-8로 인코딩되어 전송된다.

var url = some url;
var param = 'id=myid&password=mypass&name=유시형';


xmlHttpPost(url,param,result function);

위 처럼 인자값에 한글이 들어갈 경우에는
제대로 전송이 되지 않는다.

var param = 'id=myid&password=mypass&name=' + escape(encodeURIComponent('이름'));

encodeURIComponent,escape 두 함수를 이용해서 가공을 한번 해줘야 제대로 전달이 된다.


다음으로 PHP에서 위 인자값을 받을때는

$id = $_POST['myid'];
$pass = $_POST['password'];
$name = iconv('UTF-8','EUC-KR',urldecode($_POST['name']));

자바스크립트에서 함수를 적용한 역순으로 decoding을 먼저 해주고
문자셋을 euc-kr로 변경해주면 된다.

물론,
서버셋팅이 UTF-8로 되어 있다면 위처럼 귀찮은 짓은 하지 않아도 된다.
역시 대세는 UTF-8이다....


posted by 초딩입맛제주아재
2006. 7. 17. 00:58 Programing/PHP
출저 : 네이버 검색(작성자가 불분명하여 정확한 출처를 기재하지 못함을 양해바랍니다)



시스템 모니터링이라고 하면 뭔가 복잡하고 어려운 것부터 생각되고 실제로 귀찮은 작업이기도 합니다. 이유있는 문제도 있지만 그렇지 않은 경우도 많습니다. 시스템 에러의 원인을 알 수 없을 때는 참 답답합니다. 그렇다면 그 해결책을 무엇일까요? 스스로 간단한 시스템 모니터링 툴을 만들어 보는 것입니다. SNMP에 대한 기본 지식이 있다면 PHP를 이용해 간단하게 만들 수 있습니다. 리눅스에서 PHP와 SNMP를 이용한 간단한 시스템 모니터링에 대해 알아보겠습니다.

프로그래밍하다 보면 이유 없이 시스템이 다운되는 경우가 있습니다. 물론 이유가 없다는 말은 이유를 알 수 없는 경우입니다. 그렇다고 24시간 서버만 붙잡고 있을 수도 없을 것입니다. 그래서 나온 것이 시스템 모니터링 툴일 것입니다. 시스템 모니터링의 영역은 넓고 방대하지만 여기서 만들어보고자 하는 것은 SNMP를 가지고 직접 시스템의 필요한 부분을 모니터링해 보는 것입니다. 직접 만들어보면 필요한 부분을 원하는 방식으로 모니터링할 수 있습니다. 물론 직접 만드는 것이 복잡하다는 생각이 들 수도 있습니다. 그런 것을 어떻게 만들까 하는 생각이 들 수도 있습니다. 그러나 의외로 쉬운 방법이 있는데, 바로 SNMP의 값을 활용하는 것입니다. SNMP를 이용하면 매우 간단하게 시스템 모니터링에 필요한 값을 가져올 수 있습니다.

그렇다면 어떤 값을 가져올 수 있는지 먼저 알아야 합니다. SNMP로 쉽게 값을 가져올 수 있지만 그렇다고 해서 SNMP가 그리 만만한 것은 아닙니다. 왜냐하면 SNMP로 가져올 수 있는 시스템 모니터링 값 또한 매우 방대하기 때문입니다. 그러나 간단한 모니터링 프로그램을 만들어 보기 위해서 SNMP의 모든 부분을 알 필요는 없을 것입니다. 지금 필요한 부분만 익혀서 사용하면 되고 나중에 필요한 부분이 있으면 차근차근 알아가면 됩니다.

그렇다면 시스템 모니터링에 있어서 가장 필요한 값에는 어떤 것들이 있을까요? 그것은 바로 cpu와 메모리일 것입니다. 그리고 현재 시스템에 어떤 프로세스들이 있는지도 알아야할 것입니다. 이 정도 값만 알 수 있어도 간단하게 원격으로 서버를 모니터링할 수 있습니다. 그렇다면 이런 값들을 가져오기 위해서는 기본적인 프로그램이 설치되어 있어야 합니다. 여기서는 프로그램의 설치되어 있다는 가정 아래 실제로 SNMP 값을 가져오는 부분과 SNMP의 간단한 명령어를 익히게 됩니다. 그리고 PHP에서 SNMP 값을 가져오는 방법과 PHP에서 가져오는 값을 활용해서 모니터링 프로그램을 어떻게 만들어야 하는지도 알아보겠습니다.

SNMP 모니터링을 하기 위해서 필요한 것들
실제로 모니터링 프로그램을 만들기 위해서 필요한 것에는 어떤 것이 있는지 알아보겠습니다. 먼저 모니터링할 서버에 SNMP가 설치되어 있어야 합니다. 윈도우 서버와 리눅스 서버의 SNMP 값은 각각 다릅니다. 여기서 사용하는 것은 리눅스입니다. 리눅스에 일단 net-snmp(http://net-snmp.sourceforge.net/)가 설치되어 있어야 합니다. 설치되어 있지 않다면 이 사이트에서 받아서 설치할 수 있습니다. 그리고 PHP에서 SNMP 함수를 사용할 수 있어야 합니다. 일반적인 웹사이트 서비스를 위해서 PHP가 설치되어 있다면 SNMP 관련 함수를 사용할 수 있도록 설치해 주면 됩니다.

이 두 가지만 갖추어져 있다면 이제 필요한 것은 SNMP 값을 가져오는 것입니다. 그리고 어떤 값을 가져올 지를 미리 정하는 것입니다. 그리고 가장 중요한 것은 SNMP에 대한 기본 지식이 있어야 합니다. 무조건 SNMP 값을 가져다 쓰는 것보다는 SNMP에 대한 기본 지식을 알고 있으면 더 좋을 것입니다. 여기서는 한 대의 서버를 모니터링하는 것이 아니라 여러 대의 서버를 모니터링할 수 있기 때문에 모니터링하는 서버 또한 SNMP가 설치되어 있어야 합니다. SNMP 또한 환경 설정을 따로 할 수 있기 때문에 값을 가져오는 방식이 조금 다를 수 있겠지만 여기서는 기본적인 SNMP 방식으로 설명하겠습니다.

SNMP 기본 명령어 익히기
서버에서 SNMP 값을 가져오는 방법은 SNMP를 설치할 때 같이 설치되는 SNMP 관련 프로그램을 이용해서 가능합니다. 먼저 해당 서버에 SNMP가 설치되어 있는지 확인해야 합니다. 리눅스에서는 해당 서버에 SNMP 데몬이 실행되는 ps 명령으로 먼저 확인해 봅니다.


◆ 리눅스에서 SNMP 데몬이 있는지 확인하기  
- ps -aux | grep snmpd

그리고 SNMP 값을 가져오는 데 주로 사용하는 명령어는 snmpwalk입니다. 물론 이 명령어 이외에도 몇 가지가 더 있습니다. 그러나 snmpwalk 명령어로 웬만한 값을 거의 다 가져올 수 있습니다. snmpwalk는 한 가지 값만 가져올 수도 있고 관련된 값을 모두 가져올 수도 있습니다.


◆ SNMP system 관련 값 가져오기
- snmpwalk localhost public system
- snmpwalk ****.com  public system

이 명령은 localhost의 system 관련 값을 가져오는 것입니다. 그리고 해당 서버를 지정하면 그 서버의 값도 가져올 수 있습니다. 물론 다른 서버의 값을 가져오고자 할 경우 그 서버에 SNMP가 설치되어 있어야 합니다. 그렇다면 SNMP가 설치되어 있는 서버는 무조건 모니터링할 수 있을까요? 일단 그 서버에 접근 권한이 있어야 합니다. 그리고 SNMP 환경 설정에서 지정한 값만을 모니터링할 수 있습니다. 보안상 SNMP 값을 모두 모니터링하도록 허용한 것이 아니라 일부 값만 모니터링할 수 있도록 허용했을 수도 있습니다.


◆ SNMP system 관련 값의 일부
- system.sysDescr.0 = Linux localhost ...
- system.sysObjectID.0 = OID:
- enterprises.ucdavis.ucdSnmpAgent.linux
- system.sysUpTime.0 = Timeticks: (3526547) 15:33:12.22
- system.sysContact.0 = admin@****.com
- system.sysName.0 = ****.com
- system.sysLocation.0 = Unknown
= system.sysORLastChange.0 = Timeticks: (9) 0:00:00.09

system 관련 값의 일부입니다. 시스템에 관련된 값을 구할 수 있습니다. 그렇다면 이렇게 전부가 아닌 하나의 값만 구하려면 어떻게 할까요?


◆ 하나의 값만 구하기
- snmpwalk ****.com  public system.sysDescr.0
- snmpwalk ****.com  public sysDescr.0

하나의 값만 구하고자 할 경우에는 이렇게 해당 값을 지정해 주면 됩니다. 그리고 SNMP는 값을 구하는 것만이 아니라 해당 값을 설정할 수도 있습니다. 값을 구하는 것은 단순히 해당 서버의 값을 구하는 것이기 때문에 별다른 영향을 미치지 않습니다. 그러나 값을 설정하는 것은 해당 서버에 영향을 미칠 수도 있습니다. 여기서는 간단한 모니터링을 위한 SNMP 값을 이용하기 때문에 SNMP 값을 설정하는 부분은 다루지 않습니다.

CPU, 메모리 값 가져오기
모니터링하기 위해서 주로 사용되는 값인 cpu, 메모리 그리고 Load Averages 값을 가져오는 부분을 알아보겠습니다. cpu 값을 가져오기 위해서는 해당 MIB를 입력해 주면 됩니다. 여기서 사용되는 MIB 값은 공통적으로 사용되는 것입니다. MIB는 OID라는 숫자로도 표기될 수 있으며 정확하게 ssCpuUser의 계층구조까지 다 적는다면 nterprises.ucdavis.systemStats.ssCpuUser 표현될 수도 있습니다. OID 값으로 표현하면 1.3.6.1.4.1.2021.11.9.0이 됩니다. 모두 같은 의미입니다.


◆ cpu 관련 값 가져오기
- snmpwalk ****.com  public ssCpuUser
- snmpwalk ****.com  public ssCpuSystem
- snmpwalk ****.com  public ssCpuIdle

이렇게 하면 각각 cpu 사용 퍼센트를 가져올 수 있습니다. 이 값은 리눅스에서 top 명령어를 실행했을 때 cpu user, system, idle 값과 같은 값을 가져오게 됩니다. 해당 서버에 접속하지 않고도 SNMP를 이용하면 한 줄읱 명령으로 cpu 값을 가져올 수가 있습니다. 그렇다면 메모리 관련 값들에는 어떤 것이 있을까요?


◆ 메모리 관련 값 가져오기
- snmpwalk ****.com  public memTotalReal
- snmpwalk ****.com  public memTotalFree
- snmpwalk ****.com  public memShared
- snmpwalk ****.com  public memBuffer
- snmpwalk ****.com  public memCached

이렇게 하면 메모리 관련 SNMP 값을 가져올 수 있습니다. 이 값 또한 top 명령을 실행했을 때 출력되는 메모리 관련 값들과 같습니다.


◆ Load Averages 값 가져오기
- snmpwalk ****.com  public  laLoad.1
- snmpwalk ****.com  public  laLoad.2
- snmpwalk ****.com  public  laLoad.3

각각 1, 5, 10분의 Load Averages 값을 가져옵니다. 이렇듯 SNMP를 이용하면 쉽고도 간단하게 다른 서버의 모니터링에 필요한 값을 가져올 수 있습니다. 이렇게 SNMP 값을 모니터링 값에 사용하려면 MIB 또는 OID를 알고 있어야 합니다. 그렇다면 MIB는 어떻게 알 수 있을까요? 여러 사이트와 문서가 있겠지만 net-snmp 사이트에서 제공하는 문서를 볼 수 있습니다. cpu, 메모리, Load Averages MIB가 포함되어 있는 문서는 http://net-snmp.sourceforge.net/mibs/UCD-SNMP-MIB.txt에서 볼 수 있습니다. 이 문서에 있는 MIB 값을 참고한다면 좀 더 다양한 값을 모니터링할 수 있을 것입니다.

MIB, OID는 같다
ssCpuUser, memTotalFree, laLoad.1 등 이런 값들을 MIB라고 부릅니다. MIB는 간단하게 설명하면 SNMP의 값들을 분류해 놓은 것입니다. 그리고 MIB에 해당하는 OID 값이 있습니다. ssCpuUser는 축약한 것으로 enterprises.ucdavis.systemStats.ssCpuUser라고 써주면 됩니다. 그리고 이것의 OID 값은 1.3.6.1.4.1.2021.11.9가 됩니다.


◆ MIB와 OID 예
- MIB : enterprises.ucdavis.systemStats.ssCpuUser
(OID : 1.3.6.1.4.1.2021.11.9)
- MIB : enterprises.ucdavis.memory.memTotalFree
(OID : 1.3.6.1.4.1.2021.4.11)
- MIB : enterprises.ucdavis.laTable.laEntry.laLoad
(OID : 1.3.6.1.4.1.2021.10.1.3)

◆ MIB, OID로 값 얻기
- snmpwalk ****.com  public  ssCpuUser
- snmpwalk ****.com  public  enterprises.ucdavis.systemStats.ssCpuUser
- snmpwalk ****.com  public  .1.3.6.1.4.1.2021.11.9

각각 MIB, OID로 값을 가져오는 것입니다. MIB와 OID 값은 같습니다. MIB는 계층구조로 이뤄져 있습니다. 최상위 계층을 보면 System, Interface, IP, ICMP, TCP, UDP, EGP, Transmission, SNMP 그룹들로 이뤄져 있고 하위 그룹은 트리구조를 이루고 있습니다.

SNMP는 어떻게 PHP 안에 들어갔을까?
일반적으로 PHP는 웹 사이트를 제작하는데 주로 사용되고 있습니다. 물론 다른 곳에도 사용되고 있습니다. 웹 사이트를 만드는 데만 주로 사용하다 보면 다른 곳에 사용할 생각을 잊어버리는 경우가 있습니다. ‘시스템 모니터링하는 프로그램을 만들어야 하는데 어떤 언어를 이용해서 만들 수 있을까?’는 질문에서 PHP를 떠올리기는 쉽지 않을 것입니다. 그러나 데이터베이스를 연동한 프로그램 이외에도 PHP로 할 수 있는 것은 많이 있습니다.

좀 더 자세히 살펴보면 의외로 PHP에는 많은 함수들이 존재하고 있다는 것을 알 게 될 것입니다. 꼭 필요하지 않는 함수라면 굳이 사용할 필요가 없겠지요. PHP를 오래 사용한 사람이라도 ‘이런 함수가 있었나?’고 할 정도로 다양한 함수군이 존재합니다. 그 중에서도 SNMP 관련 함수들이 있습니다.

그렇다면 어떻게 PHP에서 SNMP 함수를 사용할 수 있는 것일까요? 그것은 바로 PHP의 확장성입니다. 확장성이란 필요한 함수를 추가해서 사용할 수 있는 것입니다. SNMP 함수는 이미 누군가 개발을 해서 PHP에서 사용할 수 있도록 추가해 놓았기 때문에 우리는 함수를 호출해서 사용하기만 하면 되는 것입니다. 그렇다면 직접 SNMP 함수를 PHP에서 사용할 수 있도록 만들려면 어떻게 하면 될까요? PHP는 C언어로 만들어졌기 때문에 확장을 하려면 C언어로 확장할 수 있습니다. 물론 C, C++를 알아야 합니다. 그리고 C, C++로 PHP Extensions을 만들어서 추가할 수가 있습니다. SNMP 또한 PHP Extensions으로 만들어서 추가된 것입니다.

PHP에서 사용할 수 있는 SNMP 함수들
PHP에서는 SNMP 값을 어떻게 가져올 수 있을까요? 그것은 간단하게 SNMP 함수를 호출함으로서 가능합니다. 몇 가지의 함수들이 있는데 다음은 PHP에서 사용할 수 있는 SNMP 함수 입니다.


◆ PHP의 SNMP 함수
- snmp_get_quick_print
- snmp_get_valueretrieval
- snmp_read_mib
- snmp_set_enum_print
- snmp_set_oid_numeric_print
- snmp_set_quick_print
- snmp_set_valueretrieval
- snmpget
- snmpgetnext
- snmprealwalk
- snmpset
- snmpwalk
- snmpwalkoid

이 함수들 중에서 유용하게 사용할 수 있는 것은 snmpget, snmpwalkoid입니다. snmpget은 하나의 값을 가져 올 수 있으며 snmpwalkoid는 관련된 값들을 모두 가져올 수 있습니다. <리스트 1>을 보면 snmpget 함수의 사용 예가 있습니다. 먼저 snmp로 cpu 사용량을 가져오기 위해서는 값을 가져올 서버를 지정해야 합니다. 그리고 cpu idle 값에 해당하는 oid를 알고 있어야 합니다.


◆ cpu idle 에 해당하는 snmp oid
- .1.3.6.1.4.1.2021.11.11.0

그리고 snmp 설정에 따라서 다르겠지만 커뮤니티에 해당하는 값은 ‘public’으로 해주면 됩니다. 이 세 가지 값을 지정하면 snmpget 함수는 해당 서버의 cpu idle의 값을 가져오게 됩니다. 값을 가져오지 못했을 경우 false를 리턴하게 됩니다. 그리고 값을 정상적으로 가져왔을 경우에는 10, 20 등 이런 식으로 cpu idle 퍼센트 값이 들어 있게 됩니다.



















<리스트 1> CPU 사용량 가져오기






출력은 10%, 20% 등으로 해주면 됩니다. 그러나 여기서 구하고자 했던 값은 현재 cpu 사용량이었습니다. 그런데 왜 cpu 사용량을 구하지 않은 것일까요? 그 이유를 알기 위해서는 먼저 snmp에서 가져올 수 있는 cpu 관련 값을 알아야 합니다.


◆ snmp cpu 관련 값  
- ssCpuUser
cpu user 값을 가져옵니다.
- ssCpuSystem
cpu system 값을 가져옵니다.
- ssCpuIdle
cput idle 값을 가져옵니다.

ssCpuUser + ssCpuSystem + ssCpuIdle = 100(%)

◆ 현재 cpu 사용량
ssCpuUser + ssCpuSystem  

cpu 사용량 값을 퍼센트로 가져오게 됩니다. 세 가지 값을 합하면 100이 됩니다. 그래서 cpu idle 값을 가져온 후에 ‘100-idle’ 해주면 현재 cpu 사용량이 나오게 됩니다. 이 값은 cpu 사용의 퍼센트를 의미하므로 %를 붙여서 출력해 주면 됩니다.


echo $ssCpuUser . "%";

snmpget 함수를 이용해서 cpu 관련 값들을 가져오는 방법을 알아봤습니다.

메모리 관련 값 가져오기
snmpget 함수를 이용해서 메모리 관련 값을 가져오는 방법 또한 cpu의 값을 가져오는 것과 비슷합니다. 여기서 먼저 알아야할 것은 메모리 값을 가져오는데 필요한 SNMP OID 값입니다. <리스트 2>를 보면 메모리 관련 값을 가져오는 예가 있습니다. 먼저 값을 가져올 서버와 메모리 관련 OID 값을 지정합니다.


◆ 메모리 Free 구하기
// enterprises.ucdavis.memory.memTotalFree.0
$memTotalFree = @snmpget($host, "public", $oid, $timeout);

메모리 Free량을 구하는 것입니다. 값을 구했을 경우에는 12444, 45442 이렇게 남은 메모리 량을 리턴하게 됩니다. 이 값은 KB 단위이므로 출력하고자 할 경우에는 K를 붙여서 12444K 이런 식으로 출력해 주면 됩니다.


◆ 전체 메모리 크기 구하기
// enterprises.ucdavis.memory.memTotalReal.0
$memTotalReal = @snmpget($host, "public", $oid, $timeout);

SNMP에서 구하는 값은 실제 메모리 크기와는 차이가 있습니다. 메모리는 두 가지 값을 구했습니다. 그 이유는 무엇일까요? 그것은 남은 메모리의 크기를 퍼센트로 표현하기 위해서는 전체 메모리의 크기를 알아야 하기 때문입니다. cpu는 퍼센트 값을 리턴하기 때문에 상관이 없지만 메모리는 크기를 리턴하기 때문에 전체 메모리 크기와 Free 메모리 크기를 구해서 퍼센트를 계산해 주면 됩니다.



















<리스트 2> SNMP 함수로 메모리 관련 값 가져오기






SNMP는 시스템 리소스를 사용한다
SNMP 값을 가져오려면 일단 명령을 실행하는 서버와 값을 가져오려고 하는 서버에 모두 SNMP가 설치되어 있어야 합니다. 두 서버에 모두 설치가 되어 있으면 원하는 값을 가져올 수 있게 됩니다. 그렇다면 SNMP 값을 가져오는데 어느 정도의 시스템 리소스를 사용하게 될까요? 그것은 어떤 SNMP 값을 가져오는가에 따라서 다르게 됩니다. 그리고 시스템의 사양에 따라서도 조금씩 차이가 있습니다.

SNMP 값을 가져오는 데 해당 시스템의 리소스를 너무 많이 차지하게 된다면 서버에 영향을 미칠 수 있으므로 먼저 테스트해 보는 것이 좋습니다. SNMP 값을 가져오는 데는 주로 cpu system 리소스가 사용됩니다. 앞에서 테스트했던 cpu, Load Averages 값을 가져오는 것은 1% 미만의 cpu system을 사용하게 됩니다. 그러나 메모리의 값을 가져오는 것은 10% 정도까지 cpu system 리소스를 사용할 수도 있습니다. 물론 서버마다 차이가 있으니 테스트해 보면 됩니다.

그렇다면 1초에 한번씩 cpu 값을 가져오는 것은 큰 문제가 없어 보입니다. 해당 시스템을 모니터링하려면 1초에 한 번씩 값을 가져와서 출력해 주기 위해서입니다. 그러나 메모리의 값을 1초에 한 번씩 가져오는 것은 뭔가 문제가 있어 보입니다. 왜일까요? 1초에 한 번씩 해당 서버의 cpu system 리소스를 사용하게 되면 10% 정도의 cpu system 리소스를 사용하게 되기 때문에 뭔가 문제가 있어 보입니다. 그리고 프로그램을 하나만 실행시키는 것이 아니라 여러 사람이 모니터링 프로그램을 실행시켜서 사용하고 있다면 1초에 한 번씩 메모리 값을 가져오는 것은 문제가 있습니다. 그럴 경우에는 적당히 간격을 조정하면 됩니다. 한 5초 정도의 주기로 값을 가져오면 될 것입니다. 1초, 5초에 한 번씩 값을 가져오는 이유는 실시간 모니터링을 위해서입니다.

여러 서버의 Load Averages 구하기
서버가 여러 대 있을 때 현재 서버의 Load Averages를 보고자 할 경우 일반적으로는 리눅스에서는 서버에 접속해서 top 명령어를 실행해서 봐야 합니다. 물론 서버가 몇 대 없다면 충분히 가능한 일이겠지만 서버가 몇 십 대가 있을 경우 어떻게 해야 할까요? 일일이 서버에 접속해서 top 명령을 실행해서 결과를 보려고 해도 한참 걸릴 것입니다. 그러나 PHP에서 SNMP 함수를 이용한다면 간단하게 모든 서버의 Load Averages 값을 구할 수 있습니다. 그리고 더 중요한 것은 Load Averages 값이 큰 순서대로 정렬하는 것도 가능하다는 것입니다. Load Averages 값이 크다는 것은 해당 서버의 부하가 높다는 것입니다. 어떤 서버의 Load Averages 값이 높은지 쉽게 파악이 가능하다는 것입니다.



















<리스트 3> 여러 서버의 Load Averages 구하기






<리스트 3>을 보면 여러 서버의 Load Averages를 구하는 소스가 있습니다. 먼저 $hostList 배열에는 서버의 목록이 있습니다. cpu, 메모리 값을 구할 때는 한 대의 서버 값만 구했기 때문에 한 서버만 있었지만 Load Averages는 한 번에 여러 대의 서버에서 값을 구해야 하기 때문에 배열에 서버의 목록이 있습니다. 그리고 Load Averages의 OID 값도 있어야 합니다. $sort 값은 Desc 정렬을 할지 Asc 정렬을 할 지 지정해 주면 됩니다. 그리고 for 문을 이용해서 모든 서버의 값을 구합니다. 그 결과는 $valList 배열에 넣습니다.

그렇다면 for 문에서 값을 구해서 바로 출력해 버려도 되는데 굳이 배열에 값을 저장한 이유는 무엇일까요? 그것은 정렬을 하기 위해서입니다. 그냥 서버의 Load Averages 값을 순서대로 출력해도 되겠지만 Load Averages 값이 높거나 낮은 순으로 정렬해서 보여준다면 더 좋을 것입니다. 그래서 일단 값을 배열에 넣었습니다. 그리고 배열을 정렬하는 함수를 이용해서 정렬해 주면 됩니다. Asc 정렬은 asort() 함수를 Desc 정렬은 arsort() 함수를 호출해 주면 됩니다. 이렇게 정렬한 다음에 foreach 문을 이용해서 해당 값을 출력해 주면 됩니다. 그러면 Load Averages 값의 크기에 따라서 출력됩니다. 간단하게 웹 브라우저에서 여러 대의 서버의 Load Averages를 모니터링할 수 있는 것입니다. 서버의 대수가 많을수록 일일이 서버에 접속해서 top 명령을 실행해서 보는 것보다는 몇 배의 효과를 볼 수 있을 것입니다.

그렇다면 cpu, 메모리도 이와 같은 방법으로 볼 수 있지 않을까 하는 생각이 들 것입니다. 그러나 cpu 값은 수시로 변하는 것이기 때문에 한 서버의 값을 계속 모니터링하는 것은 의미가 있겠지만 이런 식으로 여러 대의 서버를 보는 것은 별 의미가 없습니다. 메모리 값은 충분히 해볼만 합니다. 다만 메모리 값을 구하는 데는 시간이 조금 걸리기 때문에 서버의 대수가 많을수록 값을 구하는 속도가 느릴 것입니다.

TCP 연결 상태 구하기
현재 서버의 어떤 포트에 tcp 연결이 있는지 확인하기 위해서는 리눅스에서는 netstat  명령으로 확인할 수가 있습니다. 그리고 이 값을 SNMP에서도 가져올 수 있습니다. SNMP는 MIB로 분류가 되어 있다고 했었는데 tcp 그룹에 해당되는 값을 가져오면 됩니다.


◆ tcp 값 가져오기
snmpwalk ****.net public tcp.tcpConnTable.tcpConnEntry.tcpConnState

이렇게 명령을 내리면 해당 서버의 tcp 연결 값들이 보일 것입니다. 한 가지 예를 들면 다음과 같이 연결된 값이 보이게 됩니다.


◆ tcp 값의 예
tcp.tcpConnTable.tcpConnEntry.tcpConnState.211.***.***.***.80.210.***.***.
***.12454 = established(5)

이것은 하나의 예를 든 것이고 연결된 실제로는 모든 값이 보이게 됩니다. 211.***.***.***은 해당 서버를 의미합니다. 그리고 80은 해당 서버의 포트를 의미합니다. 210.***.***.***은 해당 서버에 연결된 IP입니다. 12454는 해당 서버의 프로세스 아이디입니다. 결과적으로 210.***.***.*** IP가 211.***.***.*** 서버의 80포트에 12454 프로세스 아이디로 연결되어 있다는 의미입니다. 해당 서버의 tcp 연결 개수가 작다면 별 문제가 되지 않겠지만 연결이 많고 시스템 리소스가 부족한 경우에는 timeout으로 인해서 값을 가져오지 못하는 경우도 있습니다. 그리고 tcp 연결 개수에 따라서 cpu system 리소스 사용량도 달라집니다.

예를 들어서 200~300 정도 연결되어 있다면 약 10% 이내의 cpu system 리소스를 사용하겠지만 1000개 이상의 연결되어 있을 경우에는 약 40% 정도의 리소스를 사용할 수도 있으며 timeout으로 값을 가져오지 못할 수도 있습니다. 물론 이것은 서버 사양에 따라서 다를 수 있습니다. timeout 값을 크게 주면 tcp 값을 못 가져오는 경우는 없겠지만 해당 서버의 리소스를 너무 많이 사용하게 되는 문제가 발생할 수도 있습니다. 그래서 SNMP 값을 가져오는데 timeout 시간 지정 또한 중요합니다.

PHP에서 TCP 연결 상태 구하기
<리스트 4>에 PHP에서 SNMP로 tcp 연결 상태를 가져오는 소스가 있습니다. PHP에서 해당 서버의 tcp 연결 상태를 볼 수 있다는 것은 뭔가 새로울 것입니다. 물론 SNMP를 통해서 가져오는 것이지만요. 그리고 한 가지 더 연결 상태별로 카운트를 셀 수도 있습니다. 먼저 $tcpConnStateCount에는 tcp의 연결 종류가 있습니다.



















<리스트 4> tcp 연결 상태 구하기






SNMP에서 분류하는 tcp의 연결 종류입니다. 그리고 다른 값을 가져올 때와 마찬가지로 서버, OID 값이 있습니다. cpu, 메모리 값을 가져올 때는 snmpget() 함수를 사용했었지만 여기서는 snmpwalkoid() 함수를 사용합니다. snmpwalkoid() 함수를 이용해야 tcp에 연결된 모든 목록을 가져올 수 있기 때문입니다. SNMP 값을 정상적으로 가져왔다면 $tcpConn 변수에 배열로 값이 리턴돼 있을 것입니다. 그렇다면 여기서 tcp 연결 상태별로 카운트를 세기 위해서 foreach 문으로 가공하면 됩니다. 여기서 연결 상태별로 카운트를 하고 $tcpList 배열에 상태를 저장해 주면 됩니다. 그리고 그 다음 foreach 문에서 값을 출력해 주면 됩니다. 이렇게 함으로써 tcp 연결 상태별로 개수를 파악할 수 있습니다. 그리고 어떤 포트에 어느 서버가 연결되어 있는지도 파악할 수가 있게 됩니다.

이 소스코드는 간단하게 연결 개수와 상태를 출력해 주는 것입니다. 그러나 더 기능을 추가해 보자면 어떤 것이 있을까요? 연결된 서버의 IP가 출력되도록 했는데, IP보다는 hostname으로 출력한다면 보기 쉬울 것입니다. 예를 들면 210.***.***.***를 hostname 으로 출력한다면 *****.net 이런식으로 해당 서버의 hostname이 출력 되므로 좀 더 명확하게 파악할 수 있을 것입니다. 그리고 모든 포트가 같이 출력되는데 특정 포트만 검색해서 출력할 수도 있습니다. 그것은 배열을 좀 더 가공해서 출력해 주면 됩니다.


◆ PHP에서 ip로 hostname 구하기
- gethostbyaddr("210.***.***.***");

좀 더 깊이 생각해보고 배열을 가공해 본다면 더 보기 쉽게 tcp 연결 상태를 출력할 수 있을 것입니다. 여기서는 tcp 연결 값을 구했지만 udp 연결 값 또한 SNMP에서 구할 수 있습니다.

그래프로 모니터링 툴 업그레이드
지금까지 SNMP의 기본 개념과 PHP로 SNMP 값을 가져오는 부분을 설명했습니다. 그러나 간단하게 웹 브라우저 상에서 값을 출력하는 정도에 그쳤습니다. cpu, 메모리, Load Averages 값을 그래프로 출력해서 볼 수도 있습니다. PHP에서 이런 값들을 가지고 그래프를 그리고자 한다면 쉽지는 않을 것입니다. 더군다나 cpu 모니터링 결과를 실시간으로 그려야 한다면 아마도 답이 보이지 않을 수도 있습니다. 여기서 만들어 보고자 했던 것은 간단한 모니터링 툴입니다.

로그를 남기고 <화면 1>처럼 그래프로 그리고 방대한 량의 정보를 수집하고 그래야 하는 툴이라면 다른 GUI 툴을 이용해서 만드는 것이 효과적일 것입니다. 아니면 이미 만들어져 있는 툴을 사용해도 될 것입니다. 굳이 PHP에서 그래프로 그려보는 이유는 스스로 만들다보면 다른 툴에서 지원되지 않는 기능 또한 간단하고 쉽게 추가해 볼 수 있기 때문입니다. 그것이 PHP의 장점이기도 합니다.








<화면 1> 원도우의 cpu, 메모리 사용량

그렇다면 여기서 지금까지 SNMP로 가져온 cpu, 메모리,  Load Averages 값을 실시간으로 모니터링할 수 있는 방법은 무엇일까요? 제일 간단한 방법은 자바스크립트를 이용하는 것입니다. 자바스크립트로 공개된 그래프 소스들이 많으니 그걸 이용해서 그려보면 됩니다. PHP에서 SNMP로 구한 값을 iframe에서 자바스크립트 그래프로 값을 계속해서 공급해 주면 되는 것입니다. 그러면 <화면 1>과 같은 그래프가 나올 수 있을 것입니다.

새로운 것과의 만남
새로운 것과의 만남은 항상 사람의 마음을 설레게 합니다. 프로그래머 또한 새로운 것과의 만남은 즐거움일 수 있습니다. SNMP는 여러 가지 언어에서 지원하고 있습니다. 굳이 PHP가 아니더라도 다른 언어로 시스템 모니터링하는 프로그램을 만들어 봐도 좋을 것입니다. 중요한 것은 시스템 모니터링 프로그램을 만들면서 시스템을 이해하고 배워간다는 것입니다. 시스템을 이해하고 프로그램을 만든다면 그 프로그램은 이전의 것과 다를 것입니다. 그것이 바로 프로그래머의 즐거움이기도 합니다.

'Programing > PHP' 카테고리의 다른 글

[OSX] Eclipse에서 PHP 로컬 개발/테스트 환경 구축하기  (4) 2008.08.30
Enabling PHP and Apache in Leopard  (2) 2008.08.19
WAV파일 분석  (0) 2006.11.10
간단한 소켓통신  (0) 2006.10.13
간단한 대화식 프로그램  (0) 2006.07.24
posted by 초딩입맛제주아재