2024. 12. 12. 01:46ㆍProjects/SimpleChat
1. 시작하게 된 계기
개인적으로, 옛날부터 네트워크 활용 경험이 부족한 것이 약점이라고 생각해왔습니다. 개발 공부는 만들고 싶은 것을 만들면서 Top-down 형태로 공부해나가라는 말을 많이 봤죠. 저 또한 이에 동의하며, 거창한 건 아니더라도 간단한 기능부터 점차 살을 붙여가며 개발해볼까 합니다.
2. 뭐부터 시작해야하지?..
하지만 의욕과 달리, 처음부터 난관에 부딪혔습니다. 자바 언어를 활용해 응용 프로그램을 만들어 본 경험도 없고, 책도 없었습니다. 다행히 저에겐 구글이라는 방대한 지식 창고가 있고, 최근에 고용한 `Claude`라는 멋진 비서가 있죠. Claude에게 간단한 채팅 프로그램을 만들기 위해 뭐부터 시작해야 하는지 물어봤습니다.
저는 아래와 같이 Claude가 알려준대로 학습을 진행해보기로 했습니다.
3. 간단한 1:1 채팅 프로그램 만들기 | 클라이언트
자바 IDE로 생산성이 좋고 다양한 플러그인을 지원하는 인텔리제이(IntelliJ)를 많이 사용하더라구요. 저 또한 이번 기회에 인텔리제이를 써보면서 기존에 써봤던 이클립스(Eclips)와 어떤 차이점이 있는지 느껴볼까 합니다. `SimpleChat`이란 프로젝트명과 함께, 클라이언트에 해당하는 자바 코드를 작성해보겠습니다.
(1) 호스트 설정
package client;
public class ChatClient {
private static final String SERVER_HOST = "localhost";
// ...
}
우선 해당 클라이언트가 어느 컴퓨터에 접속시킬 지부터 지정해줘야 했습니다. 현재 네트워크에 연결된 컴퓨터나 장치를 `호스트(Host)`라고 부르며, 이는 `IP 주소`나 `도메인 이름`으로 식별됩니다.
`localhost`는 자기 자신의 컴퓨터를 가리키는 호스트명으로, IP 주소로는 `127.0.0.1`과 동일합니다. 만약 다른 컴퓨터와 연결하고 싶다면 해당 컴퓨터의 IP 주소 또는 도메인 이름을 지정해주면 접속할 수 있습니다. 저는 우선 같은 컴퓨터 환경에서 메시지를 잘 주고 받는지에 대한 간단한 기능만 만들 거라서 `localhost`로 지정해줬습니다.
옛날에 "카운터스트라이크 온라인"이라는 게임을 할 때 게임 방에 접속하는 과정에서 "호스트 연결 중"이라는 메시지 문구를 본 적이 있습니다. 그때는 무슨 의미인지 몰랐지만 이제는 알겠네요!
(2) 포트 번호 설정
package client;
public class ChatClient {
private static final String SERVER_HOST = "localhost";
private static final int SERVER_PORT = 12345;
// ...
}
컴퓨터의 IP 주소는 고유하게 1개로 지정되지만, 컴퓨터 안에서 돌아가는 프로그램들은 여러 개죠. 그렇기에 이들을 각각 구별해줄 수 있는 식별자가 필요한데, 그것이 바로 `포트 번호(Port Number)`입니다. 가령, 편지를 보낼 때 집 주소를 IP에 비유할 수 있고, 편지를 받을 집 구성원들(ex. 아빠, 엄마, 누나, 나) 중에 누가 받을 것인지를 포트 번호에 비유할 수 있습니다.
이와 같이, 포트 번호는 하나의 컴퓨터에서 실행되는 여러 네트워크 프로그램을 구분하는 번호이며 $0 \sim 65,535$의 숫자를 사용합니다. 이 영역 내에서도 포트 번호는 3가지 정도로 나뉘어집니다.
- `Well-known Ports(0 ~ 1023)` : 잘 알려진 서비스들이 사용하는 예약된 포트
- `Registered Ports(1024 ~ 49151)` : 기업 및 서비스가 등록해 사용하며, 일반적으로 개발자가 자유롭게 사용 가능
- `Dynamic/Private Ports(49152 ~ 65535)` : 누구나 자유롭게 사용 가능한 포트이며, 임시 할당에 주로 사용
저는 이 중에서 `Registered Ports` 영역에 속하는 숫자 아무거나를 하나 골라 설정해주었습니다.
(3) Java Socket 및 I/O 클래스들
프로그램이 시작되었을 때 클라이언트 인스턴스가 생성되고, 인스턴스가 생성되면 서버로 세션 연결 요청을 합니다.
public static void main(String[] args) {
ChatClient chatClient = new ChatClient();
chatClient.startChatSession();
}
// 게시글 폭이 좁아서 코드가 이쁘게 나오질 않아, 줄 바꿈을 많이 했습니다.
public void startChatSession() {
try (Socket socket = new Socket(SERVER_HOST, SERVER_PORT);
BufferedReader userInputReader =
new BufferedReader(new InputStreamReader(System.in));
PrintWriter messageTransmitter =
new PrintWriter(socket.getOutputStream(), true);
BufferedReader messageReceiver =
new BufferedReader(new InputStreamReader(socket.getInputStream())))
{
System.out.println("Successfully connected to chat server.");
handleChatSession(userInputReader, messageTransmitter, messageReceiver);
} catch (IOException e) {
System.err.println("Socket Connection Error: " + e.getMessage());
}
}
연결하고자 하는 서버의 호스트와 포트 번호를 넘겨주어 연결을 신청하고, 연결에 성공했다면 통신할 준비가 끝난 겁니다. 지금 만들고 있는 프로그램은 채팅 프로그램이므로 유저가 키보드를 통해 입력하는 것과 서버로부터 메시지를 입력받기 위해 `BufferedReader`를 사용합니다.
둘 다 `BufferedReader`를 사용하지만 유저의 입력은 키보드로 받으므로 `System.in` 노드 스트림을, 서버로부터 받는 메시지는 연결한 소켓으로부터 `InputStream`을 받고 있습니다.
또한, 유저가 작성한 메시지를 서버로 보내기 위해 `PrintWriter` 인스턴스를 생성했으며, 이 또한 소켓의 `OutputStream`을 사용하고 있는 모습을 볼 수 있습니다. 이제 이를 통해 서버와 메시지를 주고 받으면 됩니다.
private static final String EXIT_COMMAND = "quit";
private static final String PROMPT_MESSAGE = "Enter your message: ";
private void handleChatSession(BufferedReader userInputReader,
PrintWriter messageTransmitter,
BufferedReader messageReceiver) throws IOException {
String userMessage;
while (true) {
System.out.println(PROMPT_MESSAGE);
userMessage = userInputReader.readLine();
if (userMessage.equalsIgnoreCase(EXIT_COMMAND)) {
System.out.println("Chat session ended.");
break;
}
sendAndReceiveMessage(userMessage, messageTransmitter, messageReceiver);
}
}
private void sendAndReceiveMessage(String message,
PrintWriter messageTransmitter,
BufferedReader messageReceiver) throws IOException {
messageTransmitter.println(message);
String serverResponse = messageReceiver.readLine();
System.out.println("Seraver response: " + serverResponse);
}
4. 간단한 1:1 채팅 프로그램 만들기 | 서버
package server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ChatServer {
private static final int SERVER_PORT = 12345;
private static final String SERVER_START_MESSAGE = "Chat server is running on port: ";
private static final String CLIENT_CONNECTED_MESSAGE = "New client connection established from: ";
public void startChatServer() {
try (ServerSocket serverSocketConnection = new ServerSocket(SERVER_PORT)) {
System.out.println(SERVER_START_MESSAGE + SERVER_PORT);
while (true) {
try (Socket clientConnection = serverSocketConnection.accept();
BufferedReader messageReceiver = new BufferedReader(new InputStreamReader(clientConnection.getInputStream()));
PrintWriter messageTransmitter = new PrintWriter(clientConnection.getOutputStream(), true))
{
System.out.println(CLIENT_CONNECTED_MESSAGE + clientConnection.getInetAddress());
String receivedMessage;
while ((receivedMessage = messageReceiver.readLine()) != null) {
System.out.println("Message from client: " + receivedMessage);
String responseMessage = "Message received: " + receivedMessage;
messageTransmitter.println(responseMessage);
}
} catch (IOException e) {
System.err.println("Error handling client connection: " + e.getMessage());
}
}
} catch (IOException e) {
System.err.println("Server error: " + e.getMessage());
}
}
public static void main(String[] args) {
ChatServer chatServer = new ChatServer();
chatServer.startChatServer();
}
}
서버 쪽에서는 `ServerSocket`이라는 클래스를 사용하여 특정 포트에 대한 연결 요청을 받을 수 있는 것 같습니다. 즉, Listener 역할을 하는 친구인 것 같아요. 그렇게 계속 연결 요청이 오는지 계속 확인하다가 연결 요청이 왔다면 그에 대한 새로운 `Socket`을 만들어서 통신합니다. 서버에서는 유저의 입력을 받을 필요가 없으니 클라이언트로부터 메시지를 받아 읽을 `BufferedReader`, 그리고 클라이언트로 메시지를 전송하기 위한 `PrintWriter` 두 개만 있으면 됩니다.
현재 만들고 있는 채팅 프로그램은 진짜 간단한 것이어서 1:1로만 연결하여 채팅을 하죠. 심지어 지금은 같은 컴퓨터 내에서 채팅을 하니 사실상 채팅 프로그램이라고 하기에도 애매한 것 같습니다. 하지만 시작을 초라하더라도 끝은 창대하리라 믿으며, 추후 기능을 하나하나 추가하며 발전시켜 나가도록 하겠습니다.
5. 테스트
서버 소스 코드를 먼저 실행한 후, 클라이언트 소스 코드를 실행하면 잘 연결되는 것을 볼 수 있습니다. 제가 다른 컴퓨터가 없어서 컴퓨터 간 채팅은 아쉽게도 못해보네요. 테스트 할 수 있는 환경이 생긴다면 테스트를 해보고, 다음에는 여러 명이 채팅할 수 있는 기능을 만들어 보는 걸로 하죠.