인터페이스 활용

콘솔 화면에서 하단만 냅두고 상단만 올라가야 됨

자바스크립트는 콘솔 짜려고 만든 언어가 아님

nano

windows nano editor

콘솔용 프로그램을 짜려고 만든 게 아님

abstract window toolkit

AWT (Abstract Window Toolkit)

06-네트워킹_스레딩 / 23 페이지

Abstract Window Toolkit

운영체제에 윈도우 관련된 API를 call 하는 일을 한다.
즉 여러분의 애플리케이션에서 awt를 call 하면 내부적으로 윈도우 API를 콜한다.

windows GUI API 검색

Windows :
Win32 API (GDI, ...) ← C
.NET API ← C#
.MFC ← C++

Linux :
X11 API
Qt API
GTK API
→ 다 C++로 되어 있다

macOS :
Cocoa API ← objective-C
Metal ← swift

에서 제공해주는 걸로 짤 거냐
X11으로 짤 거냐
Qt 사용해서 짤 거냐
하나 선택해서 프로그램 짜는 거

가상의 API를 만든다
Abstract Window Toolkit : 추상적인 윈도우 프로그래밍 도구

         ↓ call           ↓ call
JVM     AWT              AWT
         ↓ call           ↓ call
      Cocoa API         X11 API
        macOS            Linux

자바 개발자는 단일한 방법으로 프로그램을 짤 수 있다.
자바에서는 단지 실제 운영체제에서 만들기 위한 function을 호출하는 일만 한다

Frame 클래스

06-네트워킹_스레딩 / 24 페이지

new Frame()
‐ 생성된 윈도우 정보를 담은 객체
‐ Frame 클래스에서 제공하는 메서드를 통해 OS가 만든 윈도우를 제어
  → OS의 API를 호출
운영체제에서 제공하는 API 호출

"윈도우의 peer(동료)"라 부른다.
→ 프로그래밍을 하는 입장에서는 Frame 객체를 윈도우로 취급하는 게 이해하기 편하다.
그래서 Frame을 윈도우라고 하는 거

Frame(String title)

애플리케이션 버튼
‐ 미니마이즈 버튼
‐ 맥시마이즈 버튼
‐ 클로즈 버튼

인스턴스 : 변수들의 묶음
윈도우에 어떻게 변수가 들어가
윈도우의 정보가 변수에 들어가지
윈도우 정보를 담을 인스턴스 필드가 쫙 만들어지고

윈도우의 너비 높이 설정해야 됨
굳이 디멘션 객체를 만들어서 넘길 필요 없음

f.setVisible(true);

WindowListener 인터페이스

06-네트워킹_스레딩 / 25 페이지

인터페이스 : 사용 규칙, 규약, 규격

USB 인터페이스 (사용 규칙)

USB 인터페이스에 따라서 장비를 꽂는 게 있고 꼽히는 게 있음

그 규칙에 따라서 사용하는 게 있고
사용되는 장비쪽이 있음

D-SUB 인터페이스
이 규칙에 따라서 신호를 보내는 쪽 : 컴퓨터
15핀을 통해서 보낸다
9핀

한쪽은 그래픽 데이터를 보내고
한쪽은 규칙에 따라서 들어온 값을 화면에 뿌린다

D-SUB 규칙에 따라서 신호를 보낸다
컴퓨터를 만드는 입장인지 모니터를 만드는 입장인지에 따라서 갈라진다

쓰는 제품을 만드는지 쓰이는 제품을 만드는지
둘 다 짜는 게 내 일인지 고민하면 됨

컴퓨터 : 규칙에 따라서 그래픽 신호를 보내야 되고
모니터 : 입력을 받도록 제품을 만들어야 됨

그 인터페이스가 뭐하는 용도인지 알아야 됨
D-SUB, HDMI

핸드폰 -----Bluetooth----- 이어폰
둘 사이의 인터페이스가 있는데 그게 바로 Bluetooth
핸드폰 ---> 이어폰
음악 데이터를 압축해서 보내야 됨
압축하는 규칙이 있음
AAC (Advanced Audio Coding)

LDAC
소니에서 하이 레졸루션 오디오 음원을 블루투스 이어폰/헤드폰에서 재생하기 위해 개발한 손실 압축 음원 코딩 기술이다.

핸드폰이 지원을 안 하면 SBC로 통신. 저음질로 듣는다.

FLAC
FLAC은 오디오 데이터 압축을 위한 파일 형식이다. 무손실 압축 포맷이다.
블루투스로 불가능. 유선으로 들어야 됨.

apt-X

규칙에 따라서 한쪽은 사용할 거고 한쪽은 사용될 거

인터페이스는 규칙이다

WindowListener 인터페이스 (사용 규칙)

06-네트워킹_스레딩 / 25 페이지

<<interface>> WindowListener
윈도우 관련 이벤트가 발생했을 때 호출될 메서드를 정의
예) 윈도우 관련 이벤트 : 닫기 버튼을 클릭했을 때

Frame ------------- null

기본이 null
일을 할 객체가 등록이 되지 않았기 때문에 닫기 버튼을 눌렀을 때 아무런 일이 일어나지 않는다

null 대신 설정되어 있지 않으면 윈도우 이벤트에 아무런 반응을 하지 않는다
설정되어 있으면 윈도우 이벤트가 발생했을 때 메서드를 호출할 것이다

<<concrete>> MyWindowListener
MyWindowListener 라는 클래스를 만든다
모니터를 컴퓨터에 꽂으려면 먼저 모니터를 만들어야 됨

Add unimplemented methods 클릭

windowIconified
윈도우에 _ 버튼을 눌러 아이콘으로 전환되었을 때

System.exit(0);
정상적으로 종료할 때 0
상태 코드를 리턴한다
a nonzero status code indicates abnormal termination
0이 아닌 상태 코드는 비정상적인 종료를 나타냅니다.
JVM 종료 → 윈도우들 다 닫힘

모니터를 만들었으면 컴퓨터에 연결해야 됨
f.addWindowListener(new MyWindowListener());
인스턴스 주소를 주면 이 클래스의 메서드를 호출한다

WindowListener 메서드 다 구현 못 하겠음
WindowAdapter

WindowAdapter ← default 라는 문법이 등장하기 전에 만든 인터페이스
안 그랬으면 다 default로 구현했을 거임

자기가 관심 있는 메서드만 오버라이딩하면 됨

public class MyWindowListener extends WindowAdapter {
  @Override
  public void windowClosing(WindowEvent e) {
    // 윈도우의 x 버튼을 눌렀을 때 
    System.exit(0);
  }
}

이 클래스는 ChatClient의 main()에서만 사용할 수 있습니다.

그래서 중첩 클래스라는 문법이 등장함

패키지 멤버 클래스로 만들면

가위를 601호에서만 쓰게 하고 싶음
가위에 '601호용'이라고 써놓고 복도에 둘 것이냐
가위를 601호에 둘 것인가의 차이

바깥에 두면 다른 데서도 쓸 거 아님
쓰지 못하게 막아야 되는 경우도 있음
그런 경우 중첩 클래스

어떤 클래스 안에서만 사용되는 클래스라면
아예 그 클래스를 그 메서드 안에다 선언하자는 거
분리시키면서 표시

중첩 클래스 문법의 의미

06-네트워킹_스레딩 / 27 페이지

중첩 클래스의 존재 이유

class A {
  main() {
    class B {
  
    }
  }
}

✓ 컴파일러는 B 클래스가 오직 A의 main()에서만 사용될 수 있다고 표시한다.
✓ 개발자가 클래스를 정의할 때 표시하는 게 아니라 컴파일러가 표시한다.

컴파일 할 때는 B 클래스가 쏙 빠짐

main() 메서드가 호출되는 시점에 클래스 B가 만들어지는 게 아니라

컴파일 할 때 쏙 빠져서 클래스 파일이 만들어지고

다만 쏙 빠져서 만들어진 클래스에는
이 클래스는 어느 클래스의 어느 메서드에서만 사용할 수 있다고 적혀있다

바깥에 따로 클래스를 만들고 표시하는 것보다
직관적으로 알 수 있다
표시는 어차피 컴파일러가 함
이게 바로 중첩 클래스가 등장한 이유

✓ 클래스의 사용 범위를 조정하고 싶다면 그 클래스가 사용되는 위치에 클래스를 정의하라
⟹ 클래스를 정의하는 위치에 따라 사용범위를 설정하는 문법이 중첩 클래스(nested class)이다.

컴파일 할 때 다 분리된다

익명 클래스(anonymous class)
‐ 클래스 이름이 없는 중첩 클래스이다.

중첩 클래스의 종류 중에서 익명 클래스
WindowAdapter를 상속 받은 이름이 없는 클래스를 만들고
코드를 정의한다

바로 인스턴스 생성해야 됨. 나중에 생성할 수 없음.
항상 슈퍼 클래스의 생성자를 호출
WindowAdapter는 생성자가 기본 생성자밖에 없음

클래스를 넘긴다는 게 아니라 클래스를 만든 다음에 객체를 생성한 후 그 객체 주소를 넣겠다는 거

익명 클래스를 모르면 안드로이드 소스 이해 못 함

  1. 스태틱 중첩 클래스 (static nested class)

  2. 논-스태틱 중첩 클래스 (non-static nested class = inner class)

  3. 익명 클래스(anonymous class)

  4. 로컬 클래스 : 메서드 안에 선언된 클래스
    중첩 클래스 중에서 메서드 안에 선언된 클래스를 "로컬 클래스"라고 한다.

private static final long serialVersionUID = 1L;

this 생략해도 되는데 우리가 오해할까봐
인스턴스 주소

TextField로 입력상자 만들자

Container와 LayoutMessage

06-네트워킹_스레딩 / 28 페이지

Frame 객체 -------통제-------> 윈도우
Frame 클래스에서 제공하는 메서드를 통해 OS가 만든 윈도우를 제어

<<LayoutMessage>> BorderLayout
‐ 윈도우 객체 배치 관리자
‐ 레이아웃 관리자
: 윈도우 안에 버튼이나 입력상자 등 UI 컴포넌트를 배치하는 일을 수행한다.

안드로이드에서도 원리가 똑같이 등장
다른 language에서도 똑같이 등장

윈도우를 5개의 영역으로 나눠서 관리

          North
--------------------------
West  |  Center  |  East
--------------------------
          South

TextField로 입력상자 만든 걸 add 하면 Center에 배치된다.

BorderLayout은 Center 영역에 UI 컴포넌트를 배치한다.
단, 한 영역에 오직 한 개의 컴포넌트만 배치할 수 있다.

채팅창 만들기

06-네트워킹_스레딩 / 29 페이지

Panel ← 사각형으로 된 판

Panel, TextField, TextField, Button
TextArea
Panel, TextField, Button

Run Configurations
-Dfile.encoding=MS949

FlowLayout : Panel의 기본 레이아웃 관리자
옆으로 물 흐르듯이
다만 가운데 정렬, 왼쪽 정렬하고 싶으면 FlowLayout 다시 설정

Panel topPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
    Panel topPanel = new Panel();
    topPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); // 기본 레이아웃 관리자를 교체
    Panel bottomPanel = new Panel();
    bottomPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); // 기본 레이아웃 관리자를 교체

AWT와 Swing

06-네트워킹_스레딩 / 30 페이지

🔹 AWT
AWT의 Button을 사용하면
Window 11
Linux
macOS
각 OS의 버튼을 생성
각 OS의 UI로 출력
윈도우 모양, 리눅스 모양, 맥 모양으로

AWT를 사용하여 UI를 생성
① OS UI로 표현 → UI의 일관성이 없다
② OS의 공통 UI 컴포넌트만 지원 → 특정 OS에만 존재하는 UI 컴포넌트는 제공하지 않는다.
특정 운영체제에 있는 UI 컴포넌트가 다른 운영체제 없을 수도 있음
모든 OS에 존재하는 UI만 지원

🔹 Swing
JButton
Windows 11
① 생성 요청
② plain 윈도우 생성
③ 버튼 모양이 나게 그린다 ← 버튼을 자바에서 그린다
⟹ OS에 상관 없이 동일한 UI 출력

contentPane
JFrame의 경우 내부에 contentPane가 있다

탭을 만들고 싶을 때 CardLayout

FlowLayout 옆으로 쭉

여러 계층으로 되어 있음

Frame에 바로 add 시키면 안 되고

JFrame

06-네트워킹_스레딩 / 31 페이지

Content Pane
getContentPane();
add를 안 시키면 사라진다
Center가 다 차지한다

Frame 객체에 바로 add 하지 말고
contentPane.add

Look and Feel

getCrossPlatfo

운영체제에 상관없이 동일한 UI

com.sun.java.swing.plaf.gtk.GTKLookAndFeel

UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");

UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

OS와 Window Manage

06-네트워킹_스레딩 / 32 페이지

윈도우 매니저 = desktop manager

Window 11 → Window 관리자가 한 개다 → 한 개의 모양으로 출력된다

Linux → Window 관리자가 여러 개다 → 여러 개의 모양을 가질 수 있다

Solaris → Window 관리자가 여러 개다 → 여러 개의 모양을 가질 수 있다

linux window manager screenshot 검색

리눅스는 마음대로 옷을 갈아입을 수 있다

solaris desktop manager 검색

자바는 운영체제 상관없이 바꿀 수 있다

기존의 AWT는 운영체제가 만들었다
Swing은 윈도우가 plain 윈도우를 만들어주면 그리는 걸 자바에서 그린다
위에 어떻게 그리느냐에 따라서 달라짐

운영체제가 그리는 게 더 빠름
swing은 속도가 느림
자바에서 위에 직접 그리는 거

com.sun.java.swing.plaf.gtk.GTKLookAndFeel

System.out.println(UIManager.getSystemLookAndFeelClassName());

com.apple.laf.AquaLookAndFeel

    class ConnectBtnListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
        System.out.println("연결 버튼 눌렀네!!!");
      }
    }
    connectBtn.addActionListener(new ConnectBtnListener());

익명 클래스 사용하기

connectBtn.addActionListener(new 상속 받을 클래스명 또는 구현한 인터페이스명() {클래스 코드});

슈퍼 클래스의 생성자 new ActionListener()

    connectBtn.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        System.out.println("연결 버튼 눌렀네!!!");
      }
    });

arrow function
람다 문법
단, 인터페이스인 경우만 가능
반드시 인터페이스인 경우에 가능
반드시 추상 메서드가 1개인 경우 가능

(e) → e
파라미터가 1개인 경우 괄호까지 생략 가능
단, 파라미터의 타입을 적어야 되는 경우 생략 불가능

System.out.println("연결 버튼 눌렀네!!!")
괄호 {} 안에 문장이 하나인 경우 중괄호 생략 가능
문장의 끝에 ;을 붙이지 않아야 한다.

connectBtn.addActionListener(e -> System.out.println("연결 버튼 눌렀네!!!"));

버튼을 눌렀을 때 actionPerformed()를 호출할 건데

메서드 레퍼런스
메서드 주소
레퍼런스
똑같은 기능, 형식이 같은 메서드
인터페이스를 구현해야 되는 때문에
기존에 만든 메서드를 재사용
인터페이스 객체여야 하고
메서드 이름은 달라도 됨
이 자리에 원래 인터페이스 객체를 줘야 되는데 동일한 형식으로 되어 있는 메서드를 주는 거

connectChatServer() 메서드 정의, 메서드 레퍼런스

    // class MyActionListener implements ActionListener {
      // @Override
      // public void actionPerformed(ActionEvent e) {

      // }
    // }
    connectBtn.addActionListener(this::connectionChatServer);

  ...
  
  public void connectChatServer(ActionEvent e) {
    System.out.println("서버에 연결하기");
  }

sendMessage() 메서드 정의, 메서드 레퍼런스

    JButton sendBtn = new JButton("보내기");
    sendBtn.addActionListener(this::sendMessage);

    ...

  public void sendMessage(ActionEvent e) {
    System.out.println("메시지 보내기");
  }

리스너가 두 개 등록됐기 때문에 둘 다 실행됨

1) 로컬 클래스
2) 익명 클래스
3) 람다
4) 메서드 레퍼런스

    // 1) 로컬 클래스
    //    class MyActionListener implements ActionListener {
    //      @Override
    //      public void actionPerformed(ActionEvent e) {
    //      }
    //    }
    //    connectBtn.addActionListener(new MyActionListener());

    // 2) 익명 클래스
    //    connectBtn.addActionListener(new ActionListener() {
    //      @Override
    //      public void actionPerformed(ActionEvent e) {
    //      }
    //    });

    // 3) 람다(lambda) 문법
    //    connectBtn.addActionListener(e -> System.out.println("연결 버튼 눌렀음!"));

    // 4) 메서드 레퍼런스
    connectBtn.addActionListener(this::connectChatServer);

메서드 1개짜리 인터페이스
파라미터 형식과 리턴 타입이 같다면

인터페이스를 구현한 객체 주소를 주는데
ActionListener 인터페이스를 구현한 클래스의 객체 주소를 주면 됨
ActionListener를 구현하지 않았다고 하더라도 동일한 메서드가 있다면 가져다 쓰자
인터페이스를 구현하지 않았지만 actionPerformed랑 동일하게 동작
자바스크립트에서는 파라미터에 함수를 넘기는 게 흔한데
자바는 생소해서 그럼
클래스가 갖고 있는 메서드가 인터페이스와 같은 형식이면 과감하게
단 이 메서드의 파라미터 타입이 인터페이스여야 하고
그 인터페이스에는 추상 메서드가 오직 1개 있어야 한다

창을 close 할 때 소켓을 닫는다

swing 에서는 다음 컴포넌트를 제공한다

Dialogs

에러를 콘솔창에 출력하지 말고 dialogs
안드로이드 기술이랑 비슷하게 돌아감
유닉스 윈도우 프로그램이나 마이크로소프트에 있는 윈도우 프로그램이나 비슷함
제록스 연구소
마우스로 제어해서 놀람
거의 비슷함. 대동소이.

JOptionPane.showMessageDialog(this, e);

DataInputStream in
DataOutputStream out

serverSocket.accept()가 먼저임

// ChatServer

    @Override
    public void run() {
      try (Socket socket2 = socket;
          DataOutputStream out = new DataOutputStream(socket.getOutputStream());
          DataInputStream in = new DataInputStream(socket.getInputStream());) {

        while (true) {
          String message = in.readUTF();
          if (message.equals("\\quit")) {
            out.writeUTF("Goodbye!");
            out.flush();
            break;
          }
          out.writeUTF(message);
          out.flush();
        }
      } catch (Exception e) {
        System.out.println("클라이언트와의 통신 오류! - " + e.getMessage());
      }
    }

main에서 출발
결국에는 main에서 끝난다

멀티 채팅

같이 물려 있는 사람들한테 다 전달해야 됨

messageTf.addActionListener(this::sendMessage);

다른 클라이언트

출력스트림 목록을 따로 관리

스레드

ChatClient도 스레드를 고용해야 됨

서버의 응답을 기다리는 일을 한다
서버가 메시지를 보내면 화면에 출력한다

내가 입력하지 않아도 서버가 데이터를 보낼 수 있음
사용자가 서버에 메시지를 보내지 않아도 서버가 메시지를 보낼 수 있음

서버와 연결되는 순간 가동시킨다

Client와 스레드

main thread가 출력
MessageReceiver가 입력

0개의 댓글