프로그래밍/여러가지

자바스크립트 socket.io 채팅으로 초성게임 만들기 #3 중복과 턴

bonong 2021. 10. 6. 17:47
반응형

단어입력은 되지만 한 사람이 여러번 입력이 가능하고
중복단어가 되는 문제가 있었습니다

이 기능을 추가하기 위해 데이터 베이스가 있으면 좋지만 그럼 너무 커지기 때문에
오로지 서버에서만 데이터를 관리하도록 하겠습니다

계속 진행을 하다보니 클라이언트에서 socket.id가 메세지를 보낼때 마다 바뀌는 문제를 발견해서
처음 접속을 했을때만 소켓아이디를 사용하도록 하겠습니다

중복방지

중복을 방지하는 것을 쉽게 가능합니다
App.js에서 word 를 담는 list가 있었는데
onValid 함수 안에

 if (checkedWord === word) { 
 	if (list.find((w) => w === wordInput)) { 
    alert("중복단어입니다"); return; 
    } 
 }

인풋 함수에서 배열.find 함수를 써서 입력단어가 있는지 확인이 되면 alert가 나오게 설정하면 됩니다

턴 기능

턴을 줘서 한명씩 입력을 하는게 제일 중요합니다

server/index.js

let turn; 

... 

io.on("connection", (socket) => { 

...

	socket.on("join", (socketId) => { 
          if (userSocketIdMap.size === 0) { 
            userSocketIdMap.set(0, "user1"); 
            turn = "user1"; 
            io.emit("join", { 
              username: userSocketIdMap.get(0), socketId }); 
              //user1 
          } else {
              userSocketIdMap.set(1, "user2"); 
              io.emit("join", { 
                  username: userSocketIdMap.get(1), socketId 
              }); //user2 
          } 
      	});
        
    socket.on("message", (message) => { 
        if (turn === "user1") { 
        	turn = "user2"; 
            } else { 
              turn = "user1"; 
            } 
        io.emit("message", { message, turn }); 
        }); 
        
... 
   
 }

let turn을 선언하고
join과 message 이벤트를 수정을 합니다
join에서 첫번째 들어온사람을 turn으로 지정하고 username(user1)과 socketId를 클라이언트로 전달합니다
두번째도 같은 작업을 합니다
message에서 turn을 확인하고 turn을 바꾸는 역할을 합니다 여기서 turn의 역할은 메세지가오면 바꾸고 전달하는 역할만 합니다

App.js

const [who, setWho] = useState(""); 
const [cTurn, setCTurn] = useState();

클라이언트의 네임을 저장할 who와 지금 턴을 저장할 cTurn이 필요합니다
cTurn 의 c는 클라이언트를 의미하고 server에서 turn 을 관리하기 때문에 헷갈리지 않게 이름을 저장했습니다

<Button onClick={() => {
                        socket.connect();
                        socket.emit(EVENTS.JOIN, socket.id); socket.emit(EVENTS.START); 
                        setConnect(true); 
		}} >

버튼 누르면 일어나는 기능에 join부분을 보면 socket.id도 보내는 거를 #2에서 설정했었습니다
여기서 보낸 socket.id를

useEffect(()=>{ 

... 

socket.on(EVENTS.MESSAGE, ({ message, turn }) => { 
		
        setList((prev) => [...prev, message]); 
        
        setCTurn(turn); setValue("wordInput", ""); 
}); 

socket.on(EVENTS.JOIN, ({ username, socketId }) => { 
		if (socket.id === socketId) { 
        	setWho(username);
        } 
}); 

... 

},[setValue])

useEffect에 설정해놓았던 join이벤트가 다시 socket.id랑 비교해서 같으면 username을 저장합니다
그럼 첫번째 들어온 사람이 버튼을 눌러서 보냈던 socketId가 서버를 통해서 다시 들어오게되고
socket.id와 비교해서 같으면 user1이 저장이 됩니다
두번째 들어오는 사람이 문제인데 두번째 들어오는 사람이 버튼을 누르면 socketId가
다시 서버를 통해서 client들에게 다 보내지게 되는데 이거를 판별하는 게 user1 client 에서 socket.id와 socketId가 다르기 때문에 user2가 두번째 유저에게 저장이 됩니다

수식으로 보자면
첫번째유저 버튼 클릭 (client user1 socket.id) -> server (socket.id를 socketId로 받아서 바로 emit) -> client로 돌아온 socketId와 socket.id는 같다 -> username 저장

두번째 유저도 같은 방식이지만 첫번째 유저도 join 이벤트가 살아있기 때문에 socketId를 받게되는데
두번째 유저 버튼클릭(client user2 socket.id) -> server (socket.id를 socketId로 받아서 바로 emit) -> 첫번째 client에 두번째 유저의 socketId랑 socket.id랑 비교하면 틀리다 -> who 그대로

이부분은 머릿속에서 정리만 하고 만들어서 애먹었습니다

다시 onValid에서

 if (checkedWord === word) { 
 
 ... 
 
 	if (cTurn !== who) { //비교해서 같으면 보낸다 
 		alert("상대 턴입니다"); return; 
        } else { 
     		socket.emit(EVENTS.MESSAGE, wordInput);		
    } 
 }

지금 client에 저장된 who 와 비교에서 같으면 보내고 아니면 alert가 뜨게 합니다
turn인 상태의 유저가 메세지를 보내면
cTurn 이 mesaage socket.on 에서 setCTurn 으로 바꿔주기 때문에 cTurn은 메세지를 보낼때마다 변경이 되고
요 onValid에서 turn인 사람만 보낼수 있게 걸러주기 때문에 server는 그냥 턴만 변경해주는 역할만 하면 되는겁니다


 

반응형