티스토리 뷰

직업 배정 문제 해결 과정

 

기존에는 직업 배정을 roleFactory에서 한 후, 이 직업들을 사회자가 갖고 있는 player의 닉네임과 클라이언트매니저에서 받은 닉네임과 비교해서("if(clientManager.getMyName().equals(player.nickname))") 같으면 사회자쪽에서 갖고 있는 직업을 클라이언트매니저-> lobby 클래스의 함수인 setRoleView에 전달해주려고 했었다. 그렇지만 계속 이중 for문 안을 들어가지 못하고 당연히 if문에도 들어가지 못했었다. 

이유를 몰랐었는데, 팀원 중 한 명이 싱글톤인 사회자 객체가 서버와 클라이언트 쪽에서 다르게 생성될 것 같다고 했다. 

찾아보니... 서버 Java Virtual Machine이 만든 객체, 클라이언트 여러 명의 JVM이 만든 객체 모두 다르다고 한다. 따라서 서버에서 만든 싱글톤, 클라이언트들에서 만든 싱글톤 모두 전혀 다른 객체인 것이다. 따라서 과거 구조에서

서버 사회자.lobbyList.add(클라이언트의 Lobby);

 

이건 불가능한 구조였던 것이다...!! (서버가 갖고 있는 Lobby vs 클라이언트 화면과 별개였던 것.) 고로 아무리 setRoleView()를 해도 클라이언트쪽 UI는 변하지 않았다.

 

또한 찾아보니, 네트워크로 공유된 객체가 넘어갈 순 없다고 한다. 오직 문자열만 가능하다고 한다. (그래서 컴퓨터 네트워크에서 프로토콜 배울 때 메시지 어떻게 보내는지에 대해 배운거구나 깨달았다.)


 

서버와 클라이언트가 다른 컴퓨터, 다른 JVM(Java Virtual Machine)이라고 가정해보자.

그렇다면 서버가 갖고 있는 Lobby는 진짜 클라이언트의 화면이 아닐테고, 서버가 아무리 setRoleView()를 불러도 클라이언트인 유저 입장에선 아무 일도 일어나지 않을 것이다.

즉, 서버 사회자가 클라이언트의 Lobby, View 같은 Swing 객체를 직접 들고 있으면서 UI 조작을 시도했던 것이다. 네트워크를 넘어 서버가 클라이언트쪽 UI를 직접 건드리려했던 것.


그렇기에 고친 구조는 다음과 같다.

무조건 UI 조작은 클라이언트가 하고, 서버는 클라이언트에게 메시지만 전달하는 걸로!

(클라이언트쪽 사회자 싱글톤 부르는 부분도 삭제했다.)


서버

사회자 : 게임 전체 상태, 플레이어 리스트, 역할 배정, 승패 판정

ServerThread : 각 클라이언트와의 연결 1:1 담당

Player<->ServerThread 연결(고쳐야 한다...)

서버는 UI를 모른다. 오직 Join:닉네임, Start:, Role:mafia, Message: 같은 문자열만 주고 받는다.

 

클라이언트

Lobby, View, MafiaChatView = Swing UI + 로컬 화면 로직

ClientManager : 서버와 통신해서 받은 문자열 해석 후 UI 업데이트

사회자는 안 쓴다. 쓰면 서버 사회자랑 달라지기 때문에.

 

서버<->클라이언트 사이에는 오직 텍스트 프로토콜만 존재한다. (!!!네트워크로 건널 수 있는 건 메시지뿐!!! 공유된 객체 전달 못한다.)


이제 역할을 개인별로 보내보자. 그럴려면 서버가 알아야 한다. 

 

Player<->ServerThread 매핑

 

1. JoinCommand에서 

Player newPlayer = logicBrain.createNewPlayer(payload); // payload = 닉네임
sender.setPlayer(newPlayer);
newPlayer.setServerThread(sender);

- serverThread(네트워크 통로)와 Player를 서로 연결시켰다. 이 소켓은 이 플레이어의 것! 관계 만듦.

 

2. 이후 특정 플레이어에게만 메시지 보냄

for(Player player : players){
	player.getServerThread().sendMessage("ROLE:"+player.getRole());
}

- 특정 플레이어만의 소켓에서(player.getServerThread(). 해서) sendMessage하면 그 플레이어의 소켓으로만 메시지 보내진다.

이게 있어야 역할을 개인별로 보낼 수 있다.

 

init_game : 서버는 결정만, 클라이언트가 뷰에 반영하자

예전 방식 : 

for (Lobby lobby : lobbyList) {
    if(lobby.getClientManager().getMyName().equals(player.nickname)) {
        lobby.getView().setRoleView(player.getRole());
    }
}

서버가 직업 배정도 하고, 클라이언트 UI도 건드렸다. 그러나 그 Lobby는 클라이언트 화면이 아니었으므로 에러가 났다.

 

바꾼 방식 :

1. 서버:

roleFactory.randomRole(players); // 역할 랜덤 배정

for (Player player : players) {
    player.getServerThread().sendMessage("ROLE:" + player.getRole());
}

- 직업 배정은 서버가 한다.

- 결과는 "Role:mafia" 같은 텍스트로만 전송한다.

 

2. 클라이언트:

else if (message.startsWith("ROLE:")) {
    String role = message.substring(5);
    lobby.getView().setRoleView(role);
}

- 서버가 보낸 텍스트를 보고

- "아 mafia구나!" -> UI 변경을 클라이언트 측에서 수행


프로토콜 관점으로 보자.

1. Join

- 클라이언트->서버: `Join:남규`

- 서버:

  • Player("남규") 생성
  • ServerThread랑 연결
  • 전체에게 브로드캐스트 >> 클라이언트 UI 리스트에 닉네임 추가

2. Start

- 클라이언트->서버: `Start:`

- 서버:

  • broadcastAll("Start:"); >> 모든 클라이언트 Lobby.start() >> View 띄움
  • init_game() >> 역할 랜덤 배정 + ROLE 메시지 전송 시작

3. Role

- 서버-> 각 클라이언트 : `ROLE:mafia`, `ROLE:doctor`,...

- 클라이언트:

  • setRoleView("mafia") >> 이미지 & 직업 이름 표시

정리:

서버와 클라이언트는 다른 JVM 환경에 있다. (싱글톤 사회자 객체 또한 각각 다르다.) 그렇기에 서버가 클라이언트 각각의 UI를 건드릴 수 없다. 따라서 서버는 오직 역할만 배정하고, 메시지 문자열 형태로 전달만 하고, 클라이언트는 문장을 받아와서 자기 화면에 자신의 직업에 맞는 걸 표시한다.

 

 

드디어 직업이 배정되고 setRoleView() 함수가 정상 동작되어 각각의 이미지가 뜨는 것을 볼 수 있다ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

이 문제로 거의 2주를 골머리 앓았는데 각각의 직업 이미지들이 뜨길 팀원들과 함께 계속 소망했다... 너무 기쁘다.

결국 서버와 클라이언트 각각 싱글톤 사회자가 다른 객체이고, 네트워크를 통해 공유된, 공유하고 싶은 객체는 전달을 못하고 문자열만 전달할 수 있기에, 서버와 클라이언트의 역할을 명확히 분리해서(서버는 직업 배정, 결과 문자열 날리기, 클라이언트는 서버로부터 직업 메시지 받아서 자신의 뷰에 반영하기) 코드를 짜야한다는 것을 몸소 알았다... 역할 나누기, 폴더 나누기의 중요성을 깊이 깨달았다.....


남아있는 문제:

11.25 현재의 plantUML

 

서로 참조하는, 상호참조가 너무 많이 일어난다. Player클래스와 ServerThread 사이에서도 그렇고... 이를 어떻게 해결할지 더 생각해봐야겠다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함