캐치 마인드 만들기 2

백승일·2021년 7월 29일
0

회원가입과 로그인을 간단히 구현했으니 이제 소켓을 이용해서 접속하는 기능을 구현해야겠다.
처음에는 하나의 컴포넌트에서 소켓을 구현하려고 했으나, 컴포넌트의 상태가 변하게되면 리랜더링이 일어나고 그 과정에서 소켓의 연결이 끊겨서 소켓에 접속하는 컴포넌트로 뷰를 담당하는 컴포넌트들을 감싸는 방식으로 진행해야 했다.

<SocketLinkComponent>
	...화면 구성 및 기능
</SocketLinkComponent>

또한 해당 화면을 나가면 소켓 접속을 끊어야함으로 useEffect의 클리어함수에서 해당 기능을 구현한다.

  const socket = openSocket();
  useEffect(()=>{
    return ()=>{
      socket.disconnect();
    }
  })
    return (
    <div>
      <CatchmindHeader/>
      <CatchmindGameComponent socket={socket}/>
    </div>
  )

이러면 이 컴포넌트가 뜰때 소켓에 연결이 되고, 컴포넌트가 unmount될때 소켓의 연결이 끊기겠지 또한 연결된 소켓 객체를 props로 넘겨줌으로 하위 컴포넌트에서 이벤트들을 컨트롤 하게 만들자.

게임 컴포넌트는 로그인해야 들어올 수 있으니 redux에서 login상태를 가져와서 확인후 로그인 상태가 아니라면 로그인 화면으로 보내야한다.

화면 디자인은 이렇게 나오려고 한다. 때문에 컴포넌트 구조를 컨테이너 내부에 유저자리들과 그림 그릴 캔버스, 채팅과 답을 입력할 input으로 구분해준다.

      <GameContainer>
        <UserRow>
          <UserBox user={users[0]?users[0]:initUser} socket={socket} position={"left"}/>
          <UserBox user={users[1]?users[1]:initUser} socket={socket} position={"left"}/>
          <UserBox user={users[2]?users[2]:initUser} socket={socket} position={"left"}/>
        </UserRow>
        <CanvasLayer socket={socket} width={700} height={700}/>
        <UserRow>
          <UserBox user={users[3]?users[3]:initUser} socket={socket} position={"right"}/>
          <UserBox user={users[4]?users[4]:initUser} socket={socket} position={"right"}/>
          <UserBox user={users[5]?users[5]:initUser} socket={socket} position={"right"}/>
        </UserRow>
      </GameContainer>
      <InputAnswer sendAnswer={sendAnswer} />

이제 고민할 부분은 각 컴포넌트들에서 필요한 정보들이다. 예를 들어. 현제 컨테이너에서는 접속한 유저들의 리스트가 필요하다. 그래야 유저 정보를 토대로 유저 박스에 사람을 넣을 수 있을 테니까.
또한 소켓으로부터 유저들이 입력한 정보들을 받는 컴포넌트이기 때문에 정답 모달을 띄울 수도 있어야 한다. 이때 입력받은 값이 채팅 혹은 정답이기 때문에 박스의 사이드에 입력한 값을 보여준다.
의 경우에는 채팅을 처리해야한다. 현재 소켓은 입력받은 채팅을 모든 소켓에게 던지기 때문에 클라이언트에서 채팅을 한 사람을 찾아서 해당 유저 사이드에 말풍선을 띄워줘야한다.

캔버스의 경우, 캔버스에 그리는 이벤트들을의 좌표들을 소켓으로 보내야하고 받아야한다. 또한 그릴 주제들과 그리는 도구들의 정보도 받아줘야한다.

  useEffect(()=>{
    if(!user.name){
      history.push('/');
    } // 로그인 정보 없으면 로그인페이지로
    socket.emit('users',user); //접속한 유저 정보를 서버로
    socket.on('recivedUsers',data=>{
      setUsers(()=>data);
    }); // 서버로부터 접속한 유저 리스트를 받아오기
    socket.on('goldenCorrect',(data)=>{
      onCorrect(data);
      const timeOut = setTimeout(()=>{
        closeCorrect();
        socket.emit('changeSubject');
        clearTimeout(timeOut);
      },4000)
    });// 보낸 문장이 정답이면 정답입니다 모달 띄우기
  },[]);
  useEffect(()=>{
    socket.on('chatting',data=>{
      if(data.user!==(user&&user.pid))return;//채팅을 받아서 고유한 pid와 비교
      receiveMsg(data.value);// 틀리면 리턴 아니면 채팅으로 처리
    });
  },[user.pid]);

이제 서버에서 접속을 받아보자.

io = require('socket.io')(server,{
  cors:{
    origin:"http://localhost:3000"
  },
  maxHttpBufferSize:1e8
}); // 소켓 열기
let userArray = []; // 접속자 명단
let subTitles = []; // 그리기 주제 리스트
let subTitle = ""; // 주제
let artist = ""; // 그리는 사람
io.on('connection',socket=>{
  if(subTitles.length===0){
    mongoose.connection.db.collection("subtitles",(err,collection)=>{
      collection.find({}).sort({_id:-1}).limit(1).toArray()
        .then(res=>{
          subTitles = res[0].subTitles
          subTitle = res[0].subTitles[Math.floor(Math.random()*3)]
        })
        .catch(err=>console.log(err));
    });
  }; // 유저가 접속하면 db에서 주제들을 가져왔는지 체크 주제리스트가 있으면 스킵, 없으면 채우기
  
  socket.on('users',(data)=>{ //클라이언트로부터 유저정보를 받으면
    socket.nickName = data.pid; // 소켓에 닉네임 붙여주기
    userArray.push(data); // 접속유저 리스트에 유저 넣기
    if(!artist){//그리는 사람이 안정해져있으면 유저리스트에서 랜덤으로 정하기
      artist = userArray[Math.floor(Math.random()*(userArray.length))].pid;
    }
    io.sockets.emit('recivedUsers',userArray);//저장된 유저리스트를 클라들에게 
    io.to(socket.id).emit('artist',data.pid===artist?true:false);//현재 그리는 중인 사람은 방금 접속한 사람만 모르니까 1대1로만 보내기
  });
  socket.on('chatting',(data)=>{//채팅을 받으면
    const who = userArray.find(data=>data.pid===socket.nickName);
    //누구인지 유저 리스트에서 찾는다. 이때 맨 처음에 저장해둔 소켓의 닉넴을 이용한다.
    if(data.value===subTitle){ //보낸 문장이 정답이면
      io.sockets.emit('goldenCorrect',who.name);  //정답자이름을 보내고
    }
    io.sockets.emit('chatting',data); //아니면 채팅 데이터를 보내고
  });
  
  socket.on('disconnect',()=>{// 유저의 연결이 끊겼을때
    userArray = userArray.filter(data=>data.pid !== socket.nickName);
    //유저리스트에서 나간애 빼주고
    socket.broadcast.emit('recivedUsers',userArray);
    // 접속한 애들한테 다시 유저리스트를 보내주고
    if(socket.nickName === artist){
      artist = userArray.length!==0?userArray[Math.floor(Math.random()*(userArray.length))].pid:"";
    };//나간애가 그리는 사람이면 다시 정하고
    const newArtist = userArray.length!==0?userArray.filter(data=>data.pid===artist)[0].name:"";
    //모두 다 나가면 그리는 사람 빈 문자열로 바꾸고
    socket.emit("artistClose",newArtist);//바뀐 아티스트 보내준다.
    console.log('유저 나감')
  });
})

캐치마인드니까 그릴 주제를 가져와야한다. 이를 mongodb에서 가져온다. node에서 mongodb접속은 mongoose로 한다.

const mongoose = require('mongoose');
mongoose.connect("mongodb://localhost:27017/catchmind",{
  useNewUrlParser:true,
  useUnifiedTopology:true
}).then((e)=>{
  console.log('on mongo');
}).catch(e=>console.log(e));

module.exports = mongoose

여기 까지 소켓을 이용한 접속과 주제 가져오기가 구현되었다. 이제 웹뷰로 그려야지..

profile
뉴비 개발자

0개의 댓글