CodeGym /행동 /JAVA 25 SELF /리스너 (Listeners)와 이벤트 인터페이스

리스너 (Listeners)와 이벤트 인터페이스

JAVA 25 SELF
레벨 50, 레슨 0
사용 가능

1. 이벤트와의 첫 만남

프로그래밍에서 이벤트는 무언가가 발생했음을 알리는 신호입니다. 버튼 클릭, 텍스트 입력, 데이터 로드 완료, 변수 값 변경 등 애플리케이션에서 일어날 수 있고 개발자인 여러분이 반응하고 싶은 모든 것이 이벤트가 될 수 있습니다.

비유하자면 이벤트는 초인종과 같습니다. 누군가가 찾아왔고, 이제 무엇을 할지 여러분이 결정합니다. 문을 열 수도 있고, 무시할 수도 있고, 집에 아무도 없는 척할 수도 있으며, 가족 모두를 불러서 누가 왔는지 함께 볼 수도 있습니다. 프로그래밍에서 이벤트는 그 “초인종”이고, 여러분의 반응은 이벤트 핸들러입니다.

Java에서 이벤트는 반응형 및 그래픽 프로그래밍(GUI)의 근간입니다. 이벤트가 없다면 버튼, 드롭다운, 데스크톱 애플리케이션은 물론 많은 서버 시스템조차 존재하기 어렵습니다.

리스너(Listener): 무엇이며 왜 필요한가

리스너(Listener)는 특정 이벤트를 구독하고 발생을 기다리는 객체입니다. 이벤트가 발생하면 리스너는 신호를 받고 지정된 반응 코드를 실행합니다.

Java에서 리스너는 보통 인터페이스로 구현합니다. 가장 널리 쓰이는 예 중 하나가 ActionListener 인터페이스입니다. 이는 사용자 동작(예: 버튼 클릭)에 반응해야 하는 클래스가 구현합니다.

실무에서는 다음과 같이 상호작용이 이루어집니다. 이벤트 소스(예: 버튼)가 있고, 필요한 인터페이스를 구현한 리스너 객체가 있습니다. 소스는 자신에게 구독된 모든 리스너의 목록을 보관합니다. 사용자가 버튼을 누르면 이벤트 소스가 각 리스너의 특정 메서드를 호출합니다.

이 패턴은 뉴스 구독과 비슷합니다. 구독되어 있는 한 알림이 도착합니다. Java에서는 그 알림이 리스너 메서드 호출이며, 거기서 미리 정의한 동작이 수행됩니다.

2. 이벤트 인터페이스: ActionListener, MouseListener 등

Java에는 이벤트 유형마다 대응하는 리스너 인터페이스가 있습니다:

이벤트 유형 리스너 인터페이스 사용 위치
액션
ActionListener
버튼, 메뉴, 타이머
마우스
MouseListener
마우스에 반응하는 컴포넌트
키보드
KeyListener
텍스트 필드, 임의의 컴포넌트
변경
ChangeListener
슬라이더, 체크박스, 데이터 모델
문서
DocumentListener
텍스트 변경(예: JTextField)

각 인터페이스는 구현해야 하는 하나 이상의 메서드를 정의합니다. 예를 들어 ActionListener는 메서드 actionPerformed(ActionEvent e)의 구현을 요구합니다.

예: ActionListener

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class MyActionListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("버튼이 눌렸습니다!");
    }
}

3. 이벤트 모델의 동작 원리

Java의 이벤트 모델은 간단하지만 강력한 메커니즘에 기반합니다:

  1. 이벤트 소스(예: 버튼)가 리스너 목록을 유지합니다.
  2. 무언가 동작이 발생하면(예: 사용자가 버튼을 클릭) 소스는 이벤트 객체(예: ActionEvent)를 생성합니다.
  3. 소스는 등록된 모든 리스너를 순회하며 해당 메서드(예: actionPerformed)를 호출하고 이벤트 객체를 전달합니다.
  4. 각 리스너는 이 이벤트를 어떻게 처리할지 스스로 결정합니다.

동작 다이어그램:

flowchart LR
    A[사용자가 버튼을 눌렀다] --> B[버튼이 이벤트를 생성했다]
    B --> C[버튼이 모든 리스너의 actionPerformed를 호출한다]
    C --> D[리스너가 반응한다: 자신의 코드를 실행한다]

코드 예:

import javax.swing.*;

public class EventDemo {
    public static void main(String[] args) {
        JButton button = new JButton("눌러 보세요!");

        button.addActionListener(new MyActionListener());

        JFrame frame = new JFrame("이벤트 예제");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(button);
        frame.setSize(200, 100);
        frame.setVisible(true);
    }
}

사용자가 버튼을 누르면 모든 리스너의 actionPerformed가 실행됩니다.

4. 예: 버튼에 리스너 추가하기

고전적인 패턴을 간단한 예로 살펴봅시다.

1단계. 버튼 생성

JButton button = new JButton("인사하기");

2단계. 리스너 생성

class HelloListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("안녕, 세상!");
    }
}

3단계. 리스너 등록

button.addActionListener(new HelloListener());

요약: 코드에서는 이렇게 보입니다

JButton button = new JButton("인사하기");
button.addActionListener(new HelloListener());

5. 익명 클래스와 람다 표현식: 짧고 편리하게

익명 클래스

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("익명 리스너: 안녕!");
    }
});

람다 표현식 (Java 8+)

button.addActionListener(e -> System.out.println("람다! 안녕!"));

람다는 반응 코드가 한두 줄일 때 리스너를 추가하는 가장 간결한 방법입니다.

6. 여러분의 애플리케이션과는 어떻게 연결될까요?

이 메커니즘을 학습용 애플리케이션에 통합해 봅시다. 예를 들어, “작업 추가” 버튼이 있는 간단한 창이 있다고 합시다. 버튼을 누르면 목록에 새 작업을 추가하고 메시지를 출력하고자 합니다.

코드 예:

import javax.swing.*;
import java.awt.event.ActionListener;

public class TaskManagerGUI {
    public static void main(String[] args) {
        JFrame frame = new JFrame("작업 관리자");
        JButton addButton = new JButton("작업 추가");

        // 리스너로 람다 사용
        addButton.addActionListener(e -> {
            System.out.println("작업이 추가되었습니다!");
            // 여기에서 목록에 작업을 추가하는 로직이 들어갈 수 있습니다
        });

        frame.add(addButton);
        frame.setSize(300, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

이제 여러분의 애플리케이션은 단순히 명령을 수행하는 것이 아니라 사용자 동작에 “반응”합니다!

7. 리스너와 이벤트를 다룰 때 흔한 실수

실수 1: 리스너 등록을 잊음. 리스너를 만들어 놓고 이벤트 소스에 추가하지 않으면 해당 코드는 결코 호출되지 않습니다. 잡지 구독을 신청하지 않은 것과 같습니다 — 잡지는 오지 않습니다.

실수 2: 같은 리스너를 여러 번 등록. 실수로 동일한 리스너를 여러 번 추가하면, 핸들러는 추가한 횟수만큼 호출됩니다. 때로는 유용할 수 있지만, 대부분은 이상한 버그의 원인입니다(“내 함수가 왜 세 번 실행되지?”).

실수 3: 리스너 제거를 잊음. 더 이상 필요 없는 리스너를 제거하지 않으면 메모리에 계속 남습니다. 수명이 긴 애플리케이션에서는 메모리 누수로 이어질 수 있습니다.

실수 4: 이벤트 핸들러에서 오래 걸리는 작업을 수행. 핸들러에서 무거운 작업(예: 인터넷에서 데이터 로드)을 직접 수행하면 UI가 “멈추고” 사용자가 불편해합니다. 무거운 작업은 별도의 스레드에서 실행하는 것이 좋습니다.

실수 5: 리스너에서 예외를 처리하지 않음. 핸들러에서 예외가 발생하면 이벤트 흐름 전체가 중단될 수 있습니다. 오류를 로깅하고 예외를 처리하여 애플리케이션이 중단되지 않도록 하세요.

1
과제
JAVA 25 SELF, 레벨 50, 레슨 0
잠금
Starship 커맨드 모듈의 핵심 🚀
Starship 커맨드 모듈의 핵심 🚀
1
과제
JAVA 25 SELF, 레벨 50, 레슨 0
잠금
로켓 발사 지휘 센터 ☄️
로켓 발사 지휘 센터 ☄️
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION