[B-singroom]프로젝트 빌딩 4 - Lobby, User 작성

Gomi·2022년 1월 14일
0

Bsingroom 프로젝트

목록 보기
5/6
post-thumbnail

🚀 Lobby 컴포넌트 작성


적용한 것들

  • context api를 통한 전역 상태관리
  • setState의 비동기성으로 인해 유저 생성 후 소켓 프로그래밍 오류 해결
  • 이후에 채팅방에서도 사용할 수 있는 User class를 고려해 상속 사용해보기

 Lobby에서는 Intro에서 전달된 아이콘과 닉네임을 이용해 유저 인스턴스를 생성한다. 유저 인스턴스는 context api를 통해 전역적으로 관리되며, context선언은 app.js에서 한다. 이전에는 유저가 생성되면서 socket연결이 정의되기에 room목록을 불러온다던가 하는 동작에서 발생하는 동기처리 오류를 제작 속도를 맞추기 위해 그냥 setTimeout으로 해결했는데, 이번에는 setUser함수를 promise로 처리하여 해결해보았다.


context api를 통한 전역 상태관리



 소켓 연결과 관련하여 웹팩에서 다음과 같은 CSP오류가 발생한다.

Refused to connect to '' because it violates the following Content Security Policy directive: "default-src 'self' 'unsafe-inline' data:". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.


 package.json의 electron-forge의 웹팩 플러그인에 devContentSecurityPolicy를 추가하여 해결했다. 코드를 보았을 때는 모든 출처에 대해서 허용하는 형태의 코드로 보이는데 분명 보안관점에서 좋지않은 코드가 아닐까 추측하고있다. 자료와 시간이 한정적이라 일단은 이상태로 넘어간다. 코드에 대해 이해하기 위해 이것저것 만져봤지만 잘 모르겠다ㅜㅜ
  "plugins": [
        [
          "@electron-forge/plugin-webpack",
          {
            "devContentSecurityPolicy": "default-src * self connect-src self * 'unsafe-inline' blob: data: gap:; frame-src * self blob: data: gap:;",
            "mainConfig": "./webpack.main.config.js",
            "renderer": {
              "config": "./webpack.renderer.config.js",
              "entryPoints": [
                {
                  "html": "./src/index.html",
                  "js": "./src/renderer.js",
                  "name": "main_window"
                }
              ]
            },
            "port": "3030"
          }
        ]
      ]




setState의 비동기성으로 인해 유저 생성 후 소켓 프로그래밍 오류 해결



 리빌딩 하면서 음성 채팅 외에 가장 시간을 많이 쏟은 오류 중 하나는 setState함수의 비동기성에서 오는 오류였다. setUser를 통해 유저 클래스의 인스턴스를 생성하면서 소켓 연결이 정의되는데(이전 코드에서는 시간부족으로 그냥 컴포넌트 내에서 소켓을 정의했지만, 일관성이 사라져서 클래스필드에서 정의한 채로 진행해보기로했다.) setUser를 통해 소켓이 정의되어야 소켓 프로그래밍이 되는데, user가 정의되지않아 채팅방 정보를 받아오는게 불가능 했던 것. 보통 이와 관련해서는 useEffect의 의존성 배열에 변수를 추가하는 방법으로 해결하지만, 내 경우에는 잘 되지 않았다.

최종적으로 선택한 방법은 (useEffect의존성 배열) + (if문) 이었다.

lobby.js

      var {user, setUser} = useContext(UserDispatch); 	//유저 전역관리
    const [rooms, setRooms] = useState([]);         	//채팅방 state

    useEffect(() => {      				//컴포넌트 렌더링 시 유저 인스턴스 생성
        navigator.mediaDevices.getUserMedia({
            audio: { echoCancellation: false },
            video: false
        }).then((stream)=>{
            setUser(new Me(history.state.usr.icon, history.state.usr.nickname, stream))
        })
        return () => {
            if(user){
                user.socket.removeAllListeners();	 //socketOn 이벤트는 리렌더링할 때마다 수가 늘어남에 따라 재등록을 방지함.
            }
        };
    }, []);

    useEffect(() => {       				 //유저 인스턴스 생성 후 채팅방 정보 업데이트
        if(user){
        user.socket.on('showRoomList', (rooms)=>{       
            let roomList = [];
            console.log(rooms)
            for(var i=0; i<rooms.length; ++i){
                //if (rooms[i])
                if(rooms[i].item.slice(0,4)=="room")
                roomList.push({roomname:rooms[i].item, membercount:rooms[i].leng})
            }
            setRooms(roomList)
       	})
      }
    }, [user]);



이후에 채팅방에서도 사용할 수 있는 User class를 고려해 상속 사용해보기



 유저 인스턴스 생성이라는 플로우에 걸맞게 채팅창 참여 유저와 그것을 상속한 나(me)를 클래스형으로 작성했다. 기본적으로 채팅방 상대편을 정의할 user에는 아이콘 닉네임 미디어 스트림을, 나를 정의하는 me에는 getUserMedia를 통한 마이크세팅과 방 입장 등의 소켓 이벤트를 작성했다.
  
import {io} from 'socket.io-client'

const ENDPOINT = "localhost:8000";

class User{

    host = false;
    roomInfo = false;
    mediaStream = null;
    constructor(userIcon, nickname){
        this.userIcon = userIcon;
        this.nickname = nickname;
    }
    
}

class Me extends User{

    socket = io.connect(ENDPOINT);

    constructor(userIcon, nickname){
        super(userIcon, nickname);
        this.setMedia();
    }
    
    joinRoom(roomname){
        this.socket.emit('joinRoom',roomname)
        this.roomInfo = roomname;
    }

    setMedia(deviceID){
        navigator.mediaDevices.getUserMedia({
            audio: { echoCancellation: false, deviceId:{exact: deviceID} },
            video: false
        }).then((stream)=>{
            this.mediaStream = stream
        })
    }
}

export {User, Me}

완성화면

profile
터키어 배운 롤 덕후

0개의 댓글