Lacti's Archive

Java Swing을 한다면 1

January 03, 2009

Java Swing에 대해 어떻게 GUI를 배워나가는 지에 대한 이야기를 해보고 싶었다.
물론 어느 순서가 바른 순서인지는 나도 모르고, 단지 내가 해온 순서는 이렇다는 이야기를 해보고 싶어서 글을 쓴다.

Basic GUI

Java Basic GUI는

  • 창을 띄우고
  • Button을 띄우고,
  • Text를 입력받는 등의

간단한 UI 코딩이다.

  • Model, Editor, Renderer의 개념을 몰라도 괜찮고,
  • NetBeans 같은 도구를 이용하여 보다 쉽게 GUI를 그려나갈 수 있고,
  • 그에 대한 Event Handling으로 간단한 GUI Application을 만들어 나가는 단계이다.

with LayoutManager

Java Basic GUI with LayoutManager는 사실 단계가 어느 지점이 적합할지는 잘 모르겠는데, 이정도가 적당할 것이라고 생각해서 여기 써본다.

Java는 유동적인 Layout을 위해 LayoutManager라는 재미난 개념을 도입했다. 이는 말 그대로 Component들을 배치시켜주는 것이다.

  • GridLayout은 Grid 형식으로 JComponent를 배치시켜주고,
  • FlowLayout은 줄줄이 Component가 나오게 해주는 식이다.

ContainerComponent를 추가해놓고, LayoutManager만 바꿈으로써 Component의 배치가 달라지게 되는 것이다.

이는 종래 UI 제작의 모든 Component의 Bound[x, y, width, height]를 지정하던 방식인 NullLayout에 비해 상당히 유동적인 UI 구성이 가능하게 했으며, 무엇보다도 UI를 그리는 도구 없이도 간단한 UI를 만들 수 있을 뿐더러 합으로 복잡한 UI 구성도 가능하게 해주었다.

NetBeans의 GroupLayout의 등장으로 ui designer로 FreeDesign이 가능해졌고, 그 기능으로 많이 개발하는 것도 같다. 하지만 GroupLayout는 도구가 생성하는 코드라서 굉장히 코드가 복잡해진다는 단점이 있다.

LayoutManager를 구현하여 자신이 만든 LayoutManager로 Component의 배치를 직접 제어해보는 것도 상당한 도움이 된다. 물론, 빠르게 구현해야 할 때에는 NetBeans로 Design해서 Design 코드를 복사해서 Eclipse로 편집하기는 하지만, 간단한 UI를 설계하거나 배치에 모종의 규칙을 갖는 UI 설계의 경우 기존에 존재하는 Layout 조합만큼 좋은 것은 없다.

특히 Mustang(Java6)의 SpringLayout은 뭔가 재밌어보인다. 하지만 아직 깊에 안 봐서 잘 모르겠다.

with Advanced Event Handler

Java는 굉장히 많은 EventHandler를 지원한다.

Event Handling을 한다는 것은 해당 Component에서 발생한 Event에 대해 어떤 일을 수행할 것인지에 대해 코드를 작성하는 것이다.

  • Handler는 Event를 처리하기 위해 기술된 코드 집합, 즉 함수이고,
  • 보통 {EventName}Listener라는 이름 구조를 갖는다.
  • 어떤 Event에 대해서 그 Event를 기다리는 자(Listener)라는 의미로 대부분 Event를 처리하기 위한 함수 원형이 기술된 interface이다.

즉, 이걸 구현하면 함수 객체가 되는 것이고, 이것을 해당 Component의 EventHandler로 등록해주는 것이다.

보통 쉽게 접하는 것은 ActionListener, KeyListener, MouseListener, FocusEvent 정도 인데, 이것들 이외에도 ComponentEventHierarchyEvent, AncestorEvent, ContainerEvent, PropertyChangeEvent 등이 있다.

  • ComponentEvent는 해당 Component가 나타났나, 사라졌나에 대한 Event 등을 포함하고 있고,
  • ContainerEvent는 자신에게 어떤 다른 JComponent가 추가됬나, 제거됬나 등에 대한 Event가 포함되어있고,
  • AncestorEventHierarchyEvent는 부모 쪽에서 모종의 변경이 일어났을 때에 대한 Event가 포함되어있다.

위의 것들은 그냥 그러려니 하면서, 실제로 쓸 일이 없어서 잘 안 쓰는 그런 종류일 수 있다. 하지만 PropertyChangeEvent는 쓸만한, 그리고 재미있는 event이다.

Property는 Component의 상태를 표현하기 위해 필요한 정보들로, Visible, Enable 등이 있다. NetBeans에서는 아예 Property라고 해서 Table로 이를 편집할 수 있게 해준다.

보통 set{PropertyName}으로 설정하고, [get|is]{PropertyName}로 가져온다. 즉, 속성을 멤버 변수로 두고, getter/setter로 접근하도록 한다는 것이다. (VB나 C#에서는 Property라는 좀 더 재미난 개념을 제공하지만 별로 OO스럽지 않아서 Java는 지원 안하나 보다. 하지만 개인적 취향은 PHP의 getter가 좋다.)

그럼 이 PropertyChangeEvent를 Listening하면 해당 Component의 속성 변화를 알 수 있다는 것인데, 기껏해야 Visible, Enable 등의 변화를 알 수 있다는 것일까?

Java 진영에서는 JComponent(javax.swing.JComponent) 간의 데이터 교환을 위해서 javax.accessibility.AccessibleContext를 제공하고, 이것은 각 JComponentgetAccessibleContext() 함수를 통해 얻어올 수 있다.
Java Swing에는 수많은 JComponent가 존재하고, 각 JComponent마다 관리하는 저마다의 값(Model)이 있다.

  • JTextField이면 String type의 text를 관리할 것이고,
  • JSlider이면 int 값의 value를 관리할 것이다.

이러한 정보들이 각 JComponent마다 천차만별이고 (심지어 Tree나 Table은 어찌할 것인가!) 이쯤 되니까 각각의 reference로 접근해서 해당 데이터를 접근하는 것은 쉬워도, 이들을 공통적으로 접근하기는 애매해진 것이다. (사실 나는 그래서 모 코드에서 instanceof 로 모든 JComponent 객체의 type로 분기해서 처리해주는 굉장한 것도 봤다.)

그래서 각 JComponent마다 자신의 Context를 반환할 수 있는데, 그 중에서 Accessible한 것들, 즉 관리하는 값(Model)에 대한 접근을 class로 묶어서 객체를 반환하도록 한 것이다. 이 객체로 데이터만 접근할 수 있는 것이라면 재미가 없다. 재밌는 것은 이 Context의 값이 변화하면 여기서도 PropertyChangeEvent가 발생한다는 것이다.

즉, 이 Context 객체에 PropertyChangeListener를 걸면 해당 Component의 값(Model)의 변화를 감지할 수 있고, 이것은 두 Component의 Data Binding을 쉽게 해줄 수 있는 요소가 된다.

  • 예를 들어 JSlider 객체의 AccessibleContext를 가져와서 해당 ProperyChangeEvent를 Listening을 하여 JTextField 객체의 값으로 setText() 함수를 통해 지정해줄 수 있다는 것이다.

물론 그냥 JSliderChangeListener를 걸어서 처리할 수도 있지만, 요는 추상화를 통한 다형성, 즉 모든 JComponent에 대해 동일한 방법을 제공한다는 것이다. (어떤 것은 ActionListener, 어떤 것은 ItemListener, 어떤 것은 ChangeListener, 등등이라면 각각 JComponent에 대해서 할 경우에는 모두 다른 Listener를 걸어주어 객체간의 Data Binding을 위한 Event 구독 관계가 복잡하게 꼬일 것이다.)

정리하면 다음과 같다.

  • JComponent는 자신의 데이터를 접근할 수 있는 Context class를 모든 Component에 대해 추상화해놓은 AccessibleContext를 구현하여 반환할 수 있고,
  • 이 객체의 PropertyChangeEvent를 Listener하면 Data Binding을 수행할 수 있다.

요즘 같이 데이터가 많아진 시대에 데이터 중심의 코딩이 예전보다 중요해졌다. 고로 Data Binding 기술에 대한 이야기가 여기저기서 많이 나오고 있는데(너무 나와서 이제는 안 나오나;) Java에서는 이를 구현하기 위해 이와 같은 방법을 이용할 수도 있다는 것이다.

마무리

쓰다보니 뭔가 재밌어져서 이것저것 쓰다보니 당초에 쓰고 싶었던 D&D나 Dockable이 아래로 내려가 버렸다.
그리고 의외로 AccessibleContext에 대해서 열변을 토해버렸는데, 그만큼 저 쪽에는 재밌는게 많다는 것이다.

지난 번에 잠깐 Qt를 보면서 SIGNALSLOT, emit 등의 개념을 보면서 참 재밌게도 연결하는구나 싶었는데, AccessibleContext도 나름 그런 방식으로 각자 다른 Data에 대해 객체로 추상화하고, 동일하게 접근할 수 있는 interface를 제공해준다는 점에서 재밌다는 것이다. 물론 내공이 더 쌓이면 더 재미난 짓을 할 수 있겠지만 말이다.

혼자 예제도 만들고 하면서 하다보니 시간이 너무 걸려서, 일단 쓰는 걸 멈춘다.

추후 이어서 올릴 예정.
아래는 목차

  • Java GUI with Model, Editor, Renderer
  • Java Graphics
  • Drag and Drop, Dockable or Floatable
  • Java GUI with Reflection
  • Look and Feel
Loading script...