본문으둜 κ±΄λ„ˆλ›°κΈ°

🌈 Chapter 11: 첫 ν…ŒμŠ€νŠΈ ν†΅κ³Όν•˜κΈ°

πŸ“š ν…ŒμŠ€νŠΈ 도ꡬ ꡬ좕​

  • ν…ŒμŠ€νŠΈ 기반 ꡬ쑰에 μ‚¬μš©λ  두 개의 μ»΄ν¬λ„ŒνŠΈ, 즉 ApplicationRunner와 FakeAuctionServerλ₯Ό μž‘μ„±ν•  일이 λ‚¨λŠ”λ‹€.

🎈 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λŸ¬λ„ˆβ€‹

  • ApplicationRunnerλŠ” ν˜„μž¬ λ§Œλ“€κ³  μžˆλŠ” μŠ€μœ™ μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό 관리 및 톡신을 μ΄κ΄„ν•˜λŠ” 객체닀. ApplicationRunnerλŠ” 마치 λͺ…λ Ήμ€„μ—μ„œ μ‹€ν–‰λœ κ²ƒμ²˜λŸΌ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰ν•˜κ³  GUI μƒνƒœλ₯Ό μ‘°νšŒν•˜κ³  ν…ŒμŠ€νŠΈκ°€ 끝날 λ•Œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ’…λ£Œν•˜κΈ° μœ„ν•΄ 메인 창에 λŒ€ν•œ μ°Έμ‘°λ₯Ό νšλ“ν•΄μ„œ λ³΄κ΄€ν•œλ‹€.
  • μ•„λž˜ ν…ŒμŠ€νŠΈλŠ” νŠΉμ • λ¬Έμžμ—΄μ„ λ³΄μ—¬μ£ΌλŠ” λ ˆμ΄λΈ” μ»΄ν¬λ„ŒνŠΈλ₯Ό μ°ΎλŠ”λ° μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ 이 λ ˆμ΄λΈ”μ„ λ§Œλ“€μ–΄λ‚΄μ§€ μ•ŠλŠ”λ‹€λ©΄ μ˜ˆμ™Έκ°€ λ°œμƒν•  것이닀.
public class ApplicationRunner {
public static final String SNIPER_ID = "sniper";
public static final String SNIPER_PASSWORD = "sniper";
private AuctionSniperDriver driver;

public void startBiddingIn(final FakeAuctionServer auction) {
Thread thread = new Thread("Test Application") {
// μ½”λ“œμ˜ 각 뢀뢄을 μ˜¬λ°”λ₯΄κ²Œ μ‘°ν•©ν–ˆλŠ”μ§€ ν™•μΈν•˜κ³ μž μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ main ν•¨μˆ˜λ₯Ό 톡해 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 호좜
@Override public void run() {
try {
// ν•œ ν’ˆλͺ©μ— λŒ€ν•΄μ„œλ§Œ μž…μ°°ν•˜κ³  μ‹λ³„μžλ₯Ό main()으둜 μ „λ‹¬ν•œλ‹€κ³  κ°€μ •ν•œλ‹€.
Main.main(XMPP_HOSTNAME, SNIPER_ID, SNIPER_PASSWORD, auction.getItemId());
} catch (Exception e) {
// main()μ—μ„œ μ˜ˆμ™Έλ₯Ό λ˜μ§€λ©΄ μ—¬κΈ°μ„œλŠ” μ˜ˆμ™Έλ₯Ό 좜λ ₯ν•˜κΈ°λ§Œ ν•œλ‹€.
e.printStackTrace();
}
}
};

thread.setDaemon(true);
thread.start();
// ν”„λ ˆμž„κ³Ό μ»΄ν¬λ„ŒνŠΈλ₯Ό μ°ΎκΈ° μœ„ν•΄ μ œν•œ μ‹œκ°„ μ£ΌκΈ°λ₯Ό μ€„μ˜€λ‹€.
driver = new AuctionSniperDriver(1000);
// μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 접속을 μ‹œλ„ν–ˆλŠ”μ§€ νŒŒμ•…ν•˜κ³ μž μƒνƒœκ°€ Joining으둜 λ°”λ€”κΈΈ κΈ°λ‹€λ¦°λ‹€.
// 이 단정은 μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€κ°€ μ–΄λ”˜κ°€μ— μŠ€λ‚˜μ΄νΌ μƒνƒœλ₯Ό ν‘œμ‹œν•˜λŠ” λ ˆμ΄λΈ”μ΄ μžˆμŒμ„ 말해쀀닀.
driver.showsSniperStatus(STATUS_JOINING);
}

public void showsSniperHasLostAuction() {
// μŠ€λ‚˜μ΄νΌκ°€ κ²½λ§€μ—μ„œ λ‚™μ°°ν•˜μ§€ λͺ»ν•˜λ©΄ Lost μƒνƒœλ₯Ό 보여쀄 κ²ƒμœΌλ‘œ μ˜ˆμƒν•œλ‹€.
driver.showsSniperStatus(STATUS_LOST);
}

public void stop() {
if (driver != null) {
// ν…ŒμŠ€νŠΈκ°€ λλ‚˜λ©΄ λ“œλΌμ΄λ²„κ°€ 창을 μ—†μ• κ²Œ ν•΄μ„œ 가비지 μ»¬λ ‰μ…˜μ΄ μ™„λ£Œλ˜κΈ° 전에 λ‹€λ₯Έ ν…ŒμŠ€νŠΈμ—μ„œ 창을 μ‚¬μš©ν•˜λŠ” 것을 λ°©μ§€ν•œλ‹€.
driver.dispose();
}
}
}

🎈 κ°€μ§œ 경맀​

  • FakeAuctionServerλŠ” λŒ€μ²΄ μ„œλ²„λ‘œμ„œ, ν…ŒμŠ€νŠΈμ—μ„œλŠ” FakeAuctionServerλ₯Ό μ΄μš©ν•΄ 경맀 μŠ€λ‚˜μ΄νΌκ°€ μ–΄λ–»κ²Œ XMPP λ©”μ‹œμ§€λ₯Ό μ‚¬μš©ν•΄ 경맀와 μƒν˜Έ μž‘μš©ν•˜λŠ”μ§€ 검사할 수 μžˆλ‹€.
  • FakeAuctionServerμ—λŠ” μ„Έ 가지 μ±…μž„μ΄ μžˆλ‹€.
    1. XMPP λΈŒλ‘œμ»€μ— 접속해 μŠ€λ‚˜μ΄νΌμ™€μ˜ μ±„νŒ…μ— μ°Έμ—¬ν•˜λΌλŠ” μš”μ²­μ„ μˆ˜λ½ν•΄μ•Ό ν•œλ‹€.
    2. μŠ€λ‚˜μ΄νΌλ‘œλΆ€ν„° μ±„νŒ… λ©”μ‹œμ§€λ₯Ό λ°›κ±°λ‚˜ νŠΉμ • μ œν•œ μ‹œκ°„ 내에 μ•„λ¬΄λŸ° λ©”μ‹œμ§€λ„ 받지 λͺ»ν•˜λ©΄ μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€.
    3. μ‚¬μš°μŠ€λΉ„ μ˜¨λΌμΈμ—μ„œ λͺ…μ‹œν•œ λŒ€λ‘œ ν…ŒμŠ€νŠΈμ—μ„œ μŠ€λ‚˜μ΄νΌλ‘œ λ©”μ‹œμ§€λ₯Ό 되돌렀 보낼 수 있게 ν•΄μ•Ό ν•œλ‹€.
public class FakeAuctionServer {
// μƒμˆ˜ μƒλž΅..
private final String itemId;
private final XMPPConnection connection;
private Chat currentChat;

public FakeAuctionServer(String itemId) {
this.itemId = itemId;
this.connection = new XMPPConnection(XMPP_HOSTNAME);
}

public void startSellingItem() throws XMPPException {
// XMPP 브둜컀둜 접속
connection.connect();
// ν’ˆλͺ© μ‹λ³„μžλ‘œ 둜그인 이름을 생성
connection.login(format(ITEM_ID_AS_LOGIN, itemId), AUCTION_PASSWORD,AUCTION_RESOURCE);
// 둜그인 이름을 ChatManagerListener에 등둝
connection.getChatManager().addChatListener(
new ChatManagerListener() {
public void chatCreated(Chat chat, boolean createdLocally) {
currentChat = chat;
}
}
);
}

public String getItemId() {
return itemId;
}
}
  • λ©”μ‹œμ§€λ₯Ό λ°›μœΌλ‘œλ©΄ chat에 MessageListenerλ₯Ό μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.
public class FakeAuctionServer {
// ...
private final SingleMessageListener messageListener = new SingleMessageListener();

// ...
public void startSellingItem() throws XMPPException {
// ...
connection.getChatManager().addChatListener(
new ChatManagerListener() {
public void chatCreated(Chat chat, boolean createdLocally) {
currentChat = chat;
chat.addMessageListener(messageListener);
}
}
);
}
// ...

public void hasReceivedJoinRequestFromSniper() throws InterruptedException {
// ν…ŒμŠ€νŠΈμ—μ„œ Join λ©”μ‹œμ§€κ°€ μ–Έμ œ λ„μ°©ν•˜λŠ”μ§€ μ•Œ ν•„μš”κ°€ μžˆλ‹€. (이 κ΅¬ν˜„μ—μ„œ 5초 이내)
messageListener.receivesAMessage();
}

public void announceClosed() throws XMPPException {
// ν…ŒμŠ€νŠΈμ—μ„œλŠ” 경맀가 μ’…λ£Œλ  λ•Œ 경맀 μ’…λ£Œ 선언을 흉내 λ‚Ό 수 μžˆμ–΄μ•Ό ν•œλ‹€.
currentChat.sendMessage(new Message());
}

public void stop() {
// μ—°κ²° λ‹«κΈ°
connection.disconnect();
}
}

πŸ“š ν…ŒμŠ€νŠΈ μ‹€νŒ¨μ™€ 톡과​

🎈 첫 μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€β€‹

🐢 ν…ŒμŠ€νŠΈ μ‹€νŒ¨β€‹

  • ν…ŒμŠ€νŠΈμ—μ„œ Auction Sniper Mainμ΄λΌλŠ” μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€ μ»΄ν¬λ„ŒνŠΈλ₯Ό 찾지 λͺ»ν•œλ‹€.

🐢 κ΅¬ν˜„β€‹

  • 예제 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—λŠ” μ΅œμƒμœ„ μœˆλ„κ°€ ν•„μš”ν•˜λ‹€. μ—¬κΈ°μ„œλŠ” μœˆλ„λ₯Ό ν•˜λ‚˜ λ§Œλ“€κ³  μ μ ˆν•œ μ΄λ¦„λ§Œ λΆ€μ—¬ν•œλ‹€.
public class Main {
private MainWindow ui;

public Main() throws Exception {
startUserInterface();
}

public static void main(String... args) throws Exception {
Main main = new Main();
}

private void startUserInterface() throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
ui = new MainWindow();
}
});
}
}

public class MainWindow extends JFrame {
public MainWindow() {
super("Auction Sniper");
setName(MAIN_WINDOW_NAME);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}

🐢 참고​

  • ν˜„μž¬ μœ„ μ½”λ“œμ˜ μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€λŠ” μ΅œμ†Œν™”λ˜μ–΄μžˆλ‹€. μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€λŠ” 별것 μ—†μ§€λ§Œ ν™•μ‹€νžˆ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μœˆλ„λ₯Ό μ‹œμž‘ν•˜κ³  거기에 μ—°κ²°ν•  수 μžˆλ‹€. ν…ŒμŠ€νŠΈλŠ” μ—¬μ „νžˆ μ‹€νŒ¨ν•˜μ§€λ§Œ ν•œκ±ΈμŒ λ‚΄λ””λ”˜ μ…ˆμ΄λ‹€.
  • 이제 ν…ŒμŠ€νŠΈ μ„€λΉ„κ°€ λ™μž‘ν•œλ‹€λŠ” 사싀을 μ•Œ 수 있으며, 이둜써 μ’€ 더 ν₯미둜운 κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λŠ” κ²ƒμœΌλ‘œ λ‚˜μ•„κ°ˆ λ•Œ 걱정거리가 ν•˜λ‚˜ 쀄어든 μ…ˆμ΄λ‹€.

🎈 μŠ€λ‚˜μ΄νΌ μƒνƒœ ν‘œμ‹œβ€‹

🐢 ν…ŒμŠ€νŠΈ μ‹€νŒ¨β€‹

  • ν…ŒμŠ€νŠΈμ—μ„œλŠ” μ΅œμƒμœ„ μœˆλ„λ₯Ό μ°Ύμ•˜μ§€λ§Œ μŠ€λ‚˜μ΄νΌμ˜ ν˜„μž¬ μƒνƒœλŠ” λ‚˜νƒ€λ‚˜ μžˆμ§€ μ•Šλ‹€.
  • μš°μ„  μŠ€λ‚˜μ΄νΌμ—μ„œλŠ” 경맀가 μ‘λ‹΅ν•˜κΈΈ κΈ°λ‹€λ¦¬λŠ” λ™μ•ˆ Joining을 λ³΄μ—¬μ€˜μ•Ό ν•œλ‹€.

🐢 κ΅¬ν˜„β€‹

  • MainWindow에 μŠ€λ‚˜μ΄νΌ μƒνƒœλ₯Ό μΆ”κ°€ν•˜λŠ” λ ˆμ΄λΈ”μ„ μΆ”κ°€ν•œλ‹€.
public class MainWindow extends JFrame {
public static final String SNIPER_STATUS_NAME = "sniper status";
private final JLabel sniperStatus = createLabel(STATUS_JOINING);

public MainWindow() {
super("Auction Sniper");
setName(MAIN_WINDOW_NAME);
add(sniperStatus);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}

private static JLabel createLabel(String initialText) {
JLabel result = new JLabel(initialText);
result.setName(SNIPER_STATUS_NAME);
result.setBorder(new LineBorder(Color.BLACK));
return result;
}
}

🎈 경맀 연결​

🐢 ν…ŒμŠ€νŠΈ μ‹€νŒ¨β€‹

  • μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€λŠ” λ™μž‘ν•˜μ§€λ§Œ κ²½λ§€μ—μ„œλŠ” μŠ€λ‚˜μ΄νΌλ‘œλΆ€ν„° Join μš”μ²­μ„ λ°›μ§€λŠ” μ•ŠλŠ”λ‹€.

🐢 κ΅¬ν˜„β€‹

  • κ΅¬ν˜„μ—μ„œλŠ” Mainμ—μ„œ μ±„νŒ…μ— μ—°κ²°ν•΄ 빈 λ©”μ‹œμ§€λ₯Ό 보낸닀. μ•„μ§κΉŒμ§„ λ©”μ‹œμ§€λ₯Ό λ°›λŠ” 것에 μ‹ κ²½ 쓰지 μ•ŠμœΌλ―€λ‘œ 널 MessageListenerλ₯Ό μž‘μ„±ν•΄ 졜초의 빈 λ©”μ‹œμ§€λ₯Ό 보내기 μœ„ν•œ Chat을 μž‘μ„±ν•œλ‹€.
// κ΅¬ν˜„ μƒλž΅.. (μ±… 118p μ°Έκ³ )

🎈 κ²½λ§€λ‘œλΆ€ν„° 응닡받기​

🐢 ν…ŒμŠ€νŠΈ μ‹€νŒ¨β€‹

  • 경맀와 μ—°κ²°λ˜λ©΄ μŠ€λ‚˜μ΄νΌλŠ” κ²½λ§€λ‘œλΆ€ν„° Lost 응닡을 λ°›μ•„ λ³΄μ—¬μ€˜μ•Ό ν•œλ‹€.

🐢 κ΅¬ν˜„β€‹

  • 이제 μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ±„νŒ…μ— μΆ”κ°€ν•΄ κ²½λ§€μ—μ„œ 응닡을 받을 수 μžˆμ–΄μ•Ό ν•œλ‹€.

πŸ“š ν•„μš”ν•œ μ΅œμ†Œν•œμ˜ 것​

  • μ „ ꡬ간에 걸친 μ‹œμŠ€ν…œμ˜ 초기 ꡬ쑰λ₯Ό μ„€κ³„ν•˜κ³  검증해(μ—¬κΈ°μ„œ μ „ ꡬ간에 κ±Έμ³€λ‹€λŠ” 말은 λ™μž‘ν•˜λŠ” ν™˜κ²½μ— λ°°ν¬ν•˜λŠ” κ²ƒκΉŒμ§€ ν¬ν•¨ν•œλ‹€) νŒ¨ν‚€μ§€, 라이브러리, 도ꡬ 선택이 μ‹€μ œλ‘œ νš¨κ³Όκ°€ μžˆμ„μ§€ 증λͺ…ν•˜λŠ” 데 μžˆλ‹€.
  • 긴박감은 νŒ€μ΄ 가정을 ν…ŒμŠ€νŠΈν•˜λŠ” 데 μ ˆλŒ€μ μœΌλ‘œ ν•„μš”ν•œ μ΅œμ†Œν•œμ˜ κΈ°λŠ₯을 μΆ”λ €λ‚΄λŠ” 데 도움이 될 것이닀. μ΄λŸ¬ν•œ 이유둜 μŠ€λ‚˜μ΄νΌ λ©”μ‹œμ§€μ— μ•„λ¬΄λŸ° λ‚΄μš©λ„ 집어넣지 μ•Šμ€ 것이닀.
  • μƒμ„Έν•œ μ½”λ“œ μ„€κ³„λ‘œ λ„ˆλ¬΄ νž˜μ„ 빼지도 μ•Šκ²Œλ˜μ—ˆλ‹€.
  • 0 번째 반볡 μ£ΌκΈ°μ—μ„œλŠ” 보톡 νŒ€μ—μ„œ μ˜μ‚¬ 결정을 μ΄λ„λŠ” 기쀀을 νŒŒμ•…ν•˜λ©΄μ„œ ν”„λ‘œμ νŠΈ μ£Όμš” μ•ˆκ±΄μ„ μ œκΈ°ν•˜λ―€λ‘œ ν”„λ‘œμ νŠΈλ₯Ό μ£Όκ΄€ν•˜λŠ” κ³³μ—μ„œλŠ” ν”„λ‘œμ νŠΈ λͺ©μ μ— κ΄€ν•œ 심측적인 λ¬Έμ œλ“€μ„ μ²˜λ¦¬ν•  κ²ƒμœΌλ‘œ μ˜ˆμƒν•΄μ•Ό ν•œλ‹€.