05 완성도를 높여라 - 직원 급여 관리 애플리케이션 (3)
October 3rd, 2007
지난 글 - 04 기능 두큰술 - 직원 급여 관리 애플리케이션 (2) / 루비 코코아 프로그래밍
맥용 애플리케이션들을 사용하다보면 친절하다는 느낌을 받고, 작은 배려들이 쌓여서 높은 완성도를 이루었다는 생각이 들었다. 이런 느낌은 애플리케이션의 근간이 되는 코코아 프레임워크에도 그대로 적용되는 것 같다. 기존에는 개발자들이 일일이 개발해야하만 했던 것들도 모두 사용하기 편한 형태로 프레임워크에 포함되어 있어고 또 너무 쉽게 만들수 있어서 다들 자연스럽게 해당 기능을 지원하다보니 평균적인 완성도가 높은 것은 어찌보면 당연해 보인다.
이번에는 11장 ~ 13장에 설명된 내용으로 필수는 아니지만, 있으면 사용자에게 도움이 되는 그런 기능을 RaiseMan에 덧붙일 차례다.
OSX::NSNotificationCenter
지난 글에서 환경 설정에 NSTableView의 배경색을 넣었다. 그런데 문제는 이 설정에 다음에 뜨는 창에만 반영되고, 현재 떠 있는 윈도의 색은 변경하지 않는다는 것이다. 방법 중 하나는 현재 떠있는 모든 윈도우 핸들을 얻어서 직접 바꿔주는 것이다. 더 똑똑한 방법이 없을까? 코코아에서 제공하는 노티피케이션을 사용하면 좀 더 우아하게 구현할 수 있다.
먼저, 설정이 바뀌면 BNRColorChanged 노티피케이션을 보낸다.
- class PreferenceController < OSX::NSWindowController
def change_background_color(sender)
OSX::NSNotificationCenter.defaultCenter.postNotificationName_object('BNRColorChanged', self)
end
end
그리고 이 노티피케이션을 받아야하는 MyDocument에서는 자신을 BNRColorChanged의 옵저버로 등록한다. 그러면 이 노티피케이션이 발생되었을 때, 지정한 메서드 handle_color_change가 실행된다.
- class MyDocument < OSX::NSDocument
def init
OSX::NSNotificationCenter.defaultCenter.addObserver_selector_name_object(self, 'handle_color_change:', 'BNRColorChanged', nil)
end - def handle_color_change(sender)
@table_view.backgroundColor = Preference.background_color
@table_view.needsDisplay = true
end
end
단순한 구현이지만, 많은 부분에서 활용될 수 있을 것 같다. 한가지 아쉬운 점이 있다면 루비 코코아의 구현이다. addObserver 메서드가 블럭을 받을 수 있다면 좀 더 간결한 코드를 만들 수 있을 것 같은데 말이다. 나중에 구현해봐야겠다. (자꾸 글에 코코아에 대한 감탄과 루비 코코아에 대한 아쉬움이 반복되는 것 같다. 하하 이런 내용은 이제 그만 해야지 ^^)
한가지 주의할 점은 removeObserver를 호출하는 시점이다. 책의 예제에서는 dealloc 메시지를 받았을 때 removeObserver를 수행한다. 하지만 가비지 컬렉터가 있는 루비의 특성상 dealloc이 불려지는 시점은 장담할 수 없다. 그 경우 postNotificationName 메서드를 호출할 때 에러가 발생할 수 있다. 따라서 적절한 시점(도큐멘트가 사라지는)을 잡아서 removeObjserver를 실행해야만 한다. 윈도우가 닫힐때가 좋겠다 싶어, 그렇게 해보았다. 윈도우가 닫히는 시점을 잡을려해도 NotificationCenter에 등록을 해야한다.
- def init
OSX::NSNotificationCenter.defaultCenter.addObserver_selector_name_object(self, 'windowClosed:', OSX::NSWindowWillCloseNotification, nil)
end
- def windowClosed(sender)
OSX::NSNotificationCenter.defaultCenter.removeObserver self
end
이제 메모리 문제도 해결되었다.
OSX::NSAlertPanel
경고패널은 GUI 클라이언트에서 정말 자주 쓰이는 기능이다. 이 글 지울까요? 이런 메시지는 필수적이다.
구현은 간단하다.
- def remove(sender)
objs = @people_controller.selectedObjects
choice = OSX::NSRunAlertPanel('delete', "Do you really want to delete #{objs.count} records?", 'Delete', 'Cancel', nil)
@people_controller.remove(sender) if choice == OSX::NSAlertDefaultReturn
end
L10N
요즘 스프링노트팀이 계속 하고 있는 일의 바로 로컬라이제이션(L10N)이다. 귀찮은 일이지만, 더 많은 사용자를 위해서는 반드시 거쳐야하는 일이기도 하다. 코코아에서는 nibtool을 이용해 좀 더 편하게 할 수 있었다. 하지만 버전이 늘어나면 관리의 부담이 높아지는 문제는 아직 어쩔 수 없는 것 같다.
nibtool을 이용하면 영문 nib 파일에서 문자열만 추출해서 번역하고 이 파일을 적용한 새로운 nib 파일을 만들 수 있다. 메시지 매핑만 잘 유지하면, 어렵지 않게 GUI 여러벌을 관리할 수 있다는 점에서 편리해 보인다.
- nibtool -L MyDocument.nib > Doc.string
- nibtool -d Doc.string -w ../Korean.lproj/MyDocument.nib MyDocument.nib
그리고 로컬라이즈된 문자열을 얻어오기 위한 간단한 wrapper를 만들었다.
- class String
def l
OSX::NSLocalizedString(self)
end
end
이 메서드를 이용해 NSAlertPanel을 사용하는 부분을 다시 작성했다.
- choice = OSX::NSRunAlertPanel('Delete'.l, "SureDelete".l, 'Delete'.l, 'Cancel'.l, nil, objs.count)
이제 NSAlertPanel이 사용자의 언어 설정에 따라 다르게 출력됨을 확인할 수 있다.
후기
드디어 RaiseMan 완성이다. 짝짝짝. 지금까지 구현한 소스 파일은 다음 주소에서 내려받을 수 있다.
RaiseMan을 따라 구현하면서 든 생각은 부족한게 없는 코코아라는 것이다. 그래서 찾아보면 다 있을 것 같은 그런 편안한 느낌이다. 각 구성 요소들의 구현도 어느 GUI 프레임워크보다 완성도가 높아 보였다. 루비 코코아는 브리지의 역할에 충실한 것 같다. Objective-C 코드를 보면서 루비로 옮기는데 전혀 불편함이 없었다. 다음에 시간이 된다면 루비 코코아 위에서 간단한 확장 라이브러리를 구축하고 싶다. 좀 더 루비스러운 문법으로 말이다(예를 들면 위의 'Delete'.l 처럼). 여름프로젝트 할 때 함께 진행해야겠다.
다음 글은 이제 새로운 애플리케이션이다. 그리고 GUI 관련 프로그래밍 예제로 언제나 빠지지 않는 단골손님 스케치북이다. 식상하지만, 아마 설명할 수 있는 개념이 많이 때문에 장수하는 것이라 생각한다. 그럼 06 커스텀 뷰와 이벤트 활용 - 간단 패인트 애플케이션에서 이어서 쓰겠다(전체보기: 루비 코코아 프로그래밍).




Leave a Reply