Java Socket을 이용해 IRC 스타일 채팅 서버/클라이언트 구조를 연습하기 위한 프로젝트입니다.
하나의 서버가 여러 클라이언트 접속을 받고, 클라이언트는 닉네임 설정, 채널 입장, 채널 메시지 전송, 개인 메시지, 채널 목록 조회 등의 명령을 통해 간단한 채팅 흐름을 실습할 수 있습니다.
이 프로젝트는 완성형 IRC 서버를 만드는 것보다, Java 기반 네트워크 프로그래밍의 기본 흐름을 직접 구현해보는 데 목적이 있습니다.
주요 학습 포인트는 다음과 같습니다.
ServerSocket과Socket을 이용한 TCP 연결 처리- 클라이언트별 스레드 생성 및 요청 처리
DataInputStream,DataOutputStream기반 문자열 송수신- 서버-클라이언트 간 간단한 명령어 프로토콜 설계
- 채널 단위 메시지 브로드캐스트
- 닉네임 등록 및 중복 검사
- 개인 메시지 전송
- 연결 종료 시 리소스 정리
| 구분 | 내용 |
|---|---|
| Language | Java |
| Network | Java TCP Socket |
| I/O | DataInputStream, DataOutputStream |
| Concurrency | Thread 기반 처리 |
| Build Tool | 없음 |
| IDE | IntelliJ IDEA 프로젝트 설정 포함 |
별도의 Gradle/Maven 설정 없이
src디렉터리 아래의 Java 파일로 구성된 연습용 프로젝트입니다.
proj-irc/
├─ src/
│ ├─ MainController.java
│ ├─ ChannelManager.java
│ ├─ ChannelReceiver.java
│ ├─ Client.java
│ ├─ ClientSender.java
│ └─ ClientReceiver.java
├─ .gitignore
├─ 2hnK.iml
└─ IRC.iml
서버의 진입점입니다.
9910포트에서ServerSocket을 생성합니다.- 클라이언트 접속을
accept()로 대기합니다. - 클라이언트가 접속하면
ChannelReceiver스레드를 생성해 요청 처리를 위임합니다.
클라이언트의 진입점입니다.
127.0.0.1:9910서버에 접속합니다.- 접속 성공 시
ClientSender,ClientReceiver두 개의 스레드를 실행합니다.
사용자 콘솔 입력을 서버로 전송하는 클라이언트 송신 스레드입니다.
Scanner(System.in)으로 입력을 받습니다.- 입력한 문자열을
DataOutputStream.writeUTF()로 서버에 전송합니다. QUIT입력 시 송신 루프를 종료합니다.
서버에서 온 메시지를 수신하는 클라이언트 수신 스레드입니다.
DataInputStream.readUTF()로 서버 메시지를 읽습니다.- 일반 메시지는 콘솔에 출력합니다.
PONG응답은 클라이언트 기준 ping 시간으로 계산해 출력합니다.
서버 측에서 클라이언트 한 명의 요청을 처리하는 스레드입니다.
- 클라이언트 메시지를 계속 수신합니다.
JOIN,NICK,PRIVMSG,LIST,PING등 명령어를 파싱합니다.- 명령어가 아닌 일반 메시지는 현재 채널에 브로드캐스트합니다.
- 연결 종료 또는 예외 발생 시 닉네임/채널 정보를 정리하고 소켓을 닫습니다.
서버의 채널과 사용자 정보를 관리하는 객체입니다.
- 채널별 접속자 출력 스트림 관리
- 닉네임과 출력 스트림 매핑 관리
- 채널 참여/퇴장 처리
- 채널 메시지 브로드캐스트
- 닉네임 중복 검사
- 개인 메시지 전송
프로젝트 루트에서 실행합니다.
mkdir -p out
javac -encoding UTF-8 -d out src/*.java첫 번째 터미널에서 서버를 실행합니다.
java -cp out MainController실행되면 다음과 비슷한 메시지가 출력됩니다.
IRC Server started on port 9910
다른 터미널에서 클라이언트를 실행합니다.
java -cp out Client여러 명의 접속을 테스트하려면 클라이언트 터미널을 2개 이상 실행하면 됩니다.
java -cp out Client
java -cp out Client| 명령어 | 설명 | 예시 |
|---|---|---|
HELP |
사용 가능한 명령어 목록 출력 | HELP |
LIST |
현재 생성된 채널 목록 조회 | LIST |
NICK <nickname> |
닉네임 설정 | NICK jihun |
JOIN <channel> |
채널 입장 | JOIN study |
PART |
현재 채널 나가기 | PART |
PRIVMSG <user> <message> |
특정 사용자에게 개인 메시지 전송 | PRIVMSG minsu hello |
USER |
현재 사용자 정보 출력 | USER |
PING |
서버와의 연결 상태 확인 | PING |
QUIT |
서버 연결 종료 | QUIT |
닉네임은 영문, 숫자, 한글을 사용할 수 있으며 2~12자로 제한됩니다.
NICK jihun
JOIN study
안녕하세요
NICK minsu
JOIN study
반갑습니다
PRIVMSG jihun 개인 메시지 테스트
같은 채널에 접속한 클라이언트는 서로의 일반 채팅 메시지를 받을 수 있습니다.
서버 실행
↓
ServerSocket 생성 및 9910 포트 대기
↓
클라이언트 접속
↓
클라이언트마다 ChannelReceiver 스레드 생성
↓
클라이언트가 명령어 또는 채팅 메시지 전송
↓
ChannelReceiver가 명령어 파싱
↓
ChannelManager가 채널/닉네임/브로드캐스트 처리
↓
결과 메시지를 클라이언트에게 전송
- 서버 실행 및 클라이언트 접속 수락
- 클라이언트별 독립 스레드 처리
- 닉네임 설정 및 중복 방지
- 채널 생성 및 참여
- 채널 목록 조회
- 채널 내 메시지 브로드캐스트
- 채널 나가기
- 사용자 정보 조회
- 개인 메시지 전송
- ping/pong 기반 연결 확인
- 클라이언트 연결 종료 및 리소스 정리
연습용 프로젝트이므로 다음과 같은 부분을 개선해볼 수 있습니다.
currentChannel초기값을 문자열"null"이 아니라 실제null또는 별도 상태 객체로 관리- 서버 종료 기능 추가
- 명령어 파싱 로직을 별도 클래스로 분리
- 클라이언트 세션 정보를
ClientSession같은 객체로 추출 - 채널 사용자 목록 조회 기능 추가
- 비정상 종료된 클라이언트의 출력 스트림 제거 강화
HashSet접근에 대한 동시성 처리 개선- 예외 발생 시 서버 로그와 클라이언트 응답 분리
- 중복 닉네임 처리, 퇴장 처리, 재접속 처리 테스트 추가
- 서버 주소/포트를 설정 파일 또는 실행 인자로 분리
- 명령어 입력 프롬프트 추가
- 클라이언트 화면 출력 포맷 정리
- Gradle 또는 Maven 도입
- JUnit 기반 단위 테스트 추가
- 서버는 여러 클라이언트를 어떻게 동시에 처리하는가?
- 클라이언트 입력과 서버 응답 수신을 왜 별도 스레드로 분리해야 하는가?
- 채널 브로드캐스트는 어떤 방식으로 구현되는가?
- 닉네임 중복 검사는 서버의 어느 계층에서 처리하는 것이 적절한가?
Socket,InputStream,OutputStream은 언제 닫아야 하는가?- 현재 구조에서 동시성 문제가 발생할 수 있는 지점은 어디인가?
현재 저장소에 별도의 라이선스 파일은 포함되어 있지 않습니다.