Skip to content

6. Code Arena

JIHUN edited this page Sep 19, 2023 · 1 revision

📌Code Arena

✅ 방 생성 시 방장 권한 부여 및 사용자 목록 갱신

  • 사용자가 방을 생성할 때, 방장인지 식별할 수 있는 값 생성(room_host)
  • 방을 입장할 때 room_host와 입장하는 사용자 이름이 동일하다면 방장으로 인식합니다.
  • 사용자가 방에 입장 시 사용자 목록 탭에 추가되고 방장 여부에 따라 '방장' 또는 '일반'으로 나뉩니다.
  • 방장은 게임을 시작할 수있는 Start 버튼이 활성화되고, '일반'은 준비를 할 수있는 Ready 버튼이 활성화 됩니다.

💻CodeDetail

방 생성시 실행되는 방장 권한 부여 코드 (클라이언트)


codeArenaList.js (line 65)

arenaSocket.emit("check_admin", { nickname: res.data });

// 방 생성시 방장 권한 부여
arenaSocket.on("admin_status", ({ isAdmin }) => {
  if (isAdmin) {
    arenaSocket["isAdmin"] = isAdmin;
    buttonDiv.style.display = "block";
    $startBtn.style.display = "block";
  }
});
방 생성시 실행되는 방장 권한 부여 코드 (서버)


server.js (line 127)

 socket.on("check_admin", (nickname) => {
        let isAdmin = false;

        if (nickname.nickname == roomInfo.createdBy) {
          isAdmin = true;
          socket["isAdmin"] = true;
        } else {
          isAdmin = false;
        }
        socket.emit("admin_status", { isAdmin });
      });
사용자 입장 시 사용자 목록 탭 업데이트 함수


codeArenaList.js (line 606)

const updateArenaNickname = (conn_user, room_host, room_number) => {
  const $c_a_p_user = document.querySelector(".c_a_p_user");
  conn_user.forEach((userInfo) => {
    const newUser = document.createElement("div");
    if (userInfo.ROOM_NUMBER == room_number) {
      if (room_host == userInfo.CONN_USER) {
        // 들어오는 사람이 방을 만든 사람의 닉네임과 같다면? = 방장일 때
        newUser.className = `c_a_p_u1`;
        newUser.innerHTML += `
        <div class="u_info">
        <div class="u_i_img">방장</div>
        <div class="u_i_nick" data-user="${userInfo.CONN_USER}">${userInfo.CONN_USER}</div>
        </div>
        <div class="u_remain">
        <div div class="u_r_ques">
        <div class="u_r_circle ${userInfo.CONN_USER}" style="display:none;">ok</div>
        </div>
        </div>
        `;
        $c_a_p_user.append(newUser);
      } else {
        if (userInfo.USER_READY == "N") {
          // 준비 안했을 때
          // 들어오는 사람이 방을 만든 사람의 닉네임과 같다면? = 일반일 때
          newUser.className = `c_a_p_u2`;
          newUser.innerHTML += `
          <div class="u_info">
          <div class="u_i_img">일반</div>
          <div class="u_i_nick normal_user" data-user="${userInfo.CONN_USER}">${userInfo.CONN_USER}</div>
          </div>
          <div class="u_remain">
          <div div class="u_r_ques">
          <div class="u_r_circle ${userInfo.CONN_USER}" style="display:none;">READY</div>
          </div>
          </div>
          `;
          $c_a_p_user.append(newUser);
        } else {
          // 기존에 준비했던 사람
          // 들어오는 사람이 방을 만든 사람의 닉네임과 같다면? = 일반일 때
          newUser.className = `c_a_p_u2`;
          newUser.innerHTML += `
          <div class="u_info">
          <div class="u_i_img">일반</div>
          <div class="u_i_nick normal_user" data-user="${userInfo.CONN_USER}">${userInfo.CONN_USER}</div>
          </div>
          <div class="u_remain">
          <div div class="u_r_ques">
          <div class="u_r_circle ${userInfo.CONN_USER}" style="display:block;">READY</div>
          </div>
          </div>
          `;
          $c_a_p_user.append(newUser);
        }
      }
    }
  });
};
입장한 사용자 방장, 일반 구분하여 이벤트 실행(서버)


server.js(line 390)

if (room_host != nickname) {
          ArenaNamespace.to(room_number).emit("enter_normal_user", {
            conn_user,
            room_host,
            room_number,
          });
          socket.emit("normal_user_ready");
        } else {
          ArenaNamespace.to(room_number).emit("enter_host_user", {
            conn_user,
            room_host,
            room_number,
          });
        }
입장한 사용자 방장, 일반 구분하여 이벤트 실행(클라이언트)


codeArenaList.js(line 570)

arenaSocket.on("enter_host_user", ({ conn_user, room_host, room_number }) => {
  const $c_a_p_user = document.querySelector(".c_a_p_user");
  const $divs = $c_a_p_user.querySelectorAll("div");
  $divs.forEach(($div) => {
    $div.remove();
  });
  // userList는 전체 유저가 입장한 방번호와 닉네임을 객체로 배열에 넣은 것
  // room_number는 입장하는 방의 번호
  // room_host는 입장하는 방을 만든 이
  updateArenaNickname(conn_user, room_host, room_number);
});

arenaSocket.on("enter_normal_user", ({ conn_user, room_host, room_number }) => {
  const $c_a_p_user = document.querySelector(".c_a_p_user");
  const $divs = $c_a_p_user.querySelectorAll("div");
  $divs.forEach(($div) => {
    $div.remove();
  });
  updateArenaNickname(conn_user, room_host, room_number);
});

arenaSocket.on("normal_user_ready", () => {
  buttonDiv.style.display = "block";
  $readyBtn.style.display = "block";
  $startBtn.style.display = "none";
});

🖼️ Preview

방 생성 및 입장.gif

방생성 및 입장

✅ 게임 Start 기능

  • 방장이 게임을 시작할 수 있습니다. 하지만 방에 입장한 사용자 중 방장을 제외한 모든 사용자가 Ready를 해야 합니다.
  • 방장을 제외한 모든 사용자는 입장할 때, Ready 값이 'N'인 상태로 DB에 저장됩니다.
  • Ready를 클릭하면 axios를 통해 DB를 업데이트하여 해당 사용자의 Ready 값이 'Y'또는 'N'으로 바뀌게 되고,
    모든 인원의 Ready 값이 'Y'일 때 방장이 Start버튼을 클릭하면 게임이 시작됩니다.
  • axios를 통해 Start / Ready 버튼 클릭 시, DB에 접근하여 모든 사용자의 Ready 값을 수정 및 조회 한 후, 값에 따른 스크립트가 실행됩니다.

💻CodeDetail

일반 사용자 준비 완료/ 취소 기능


codeArenaList.js (line 330)

// 일반 사용자 준비 완료 / 취소 기능

// 레디 버튼을 눌렀을 때 실행되는 함수
const ready = () => {
  arenaSocket.emit("click_ready_btn", { nickName: currentNickname });
};
// 클릭 이벤트 리스너 등록
$readyBtn.addEventListener("click", ready);

// DB에 접근하여 Ready 값 변경
arenaSocket.on("my_ready", (data) => {
  axios.post("/codeArena/codeReady", { data }).then((res) => {
    let data = JSON.parse(res.data);
    arenaSocket.emit("update_ready", data[0]);
  });
});

// 준비 / 준비 취소 기능
arenaSocket.on("ready_on", (data) => {
  let $normal_user = document.querySelectorAll(".normal_user"); // 들어온 일반유저 닉네임 태그 전부 가져오기
  let click_nickname = data.nickName; // 클릭한 사용자의 닉네임
  let roomNum = data.roomNum; // 클릭한 사용자가 속해있는 방 번호
  let isReady = data.isReady;

  $normal_user.forEach((user_nick) => {
    // user_nick : 각 일반유저의 닉네임
    let foreach_nickname = user_nick.dataset.user;

    if (isReady == "Y") {
      // 준비X => 준비
      if (click_nickname == foreach_nickname) {
        // 클릭한 사용자만 활성화 하기위해
        let ready_on = document.querySelector(`.${foreach_nickname}`);
        ready_on.style.display = "block"; // 준비 표시 활성화
        arenaSocket.emit("ready_count_up"); // 현재 준비한 인원수+ 체크
      }
    } else {
      // 준비 => 준비X
      if (click_nickname == foreach_nickname) {
        // 클릭한 사용자만 활성화 하기위해
        let ready_on = document.querySelector(`.${foreach_nickname}`);
        ready_on.style.display = "none"; // 준비 표시 비활성화
        // 여기에다가 DB USER_READY를 N로 변경
        arenaSocket.emit("ready_count_down"); // 현재 준비한 인원수- 체크
      }
    }
  });
});
Ready를 한 인원수 카운트


codeArenaList.js (line 379)

// Ready 한 인원수 카운트
let ready_count;
let currentUsers;

arenaSocket.on("user_count", (user_count) => {
  currentUsers = user_count.user_count;
});

arenaSocket.on("ready_count", () => {
  axios
    .post("/codeArena/readyCount", { roomNum: arenaSocket.roomNum })
    .then((res) => {
      let arenaUsers = JSON.parse(res.data);
      ready_count = arenaUsers[0].COUNT;
    });
});

방장 Start 버튼 클릭 로직


codeArenaList.js (line 396)

// 방장이 start 버튼을 눌렀을 때
$startBtn.addEventListener("click", () => {
  
  if (arenaSocket.isAdmin) {
    currentUsers = parseInt(currentUsers);
    ready_count = parseInt(ready_count);
    if (currentUsers - 1 == ready_count) {
      arenaSocket.emit("click_start_btn");

      $startBtn.style.display = "none";
      buttonDiv.style.display = "none";
      TIMER();
      question_div.style.display = "block";
      question_div2.style.display = "block";
    }
    else { // 시작 조건 미충족일 때 클릭 시 start 빨간색 => 하얀색
      $startBtn.classList.add("clicked"); // clicked 클래스 추가
      setTimeout(() => {
        $startBtn.classList.remove("clicked")
      }, 500)
    }
  }
  //code editor 기본 값 입력
  js.setValue(`function codeBuddy(n){
      let result;
      result = 정답을입력하세요;
      return result;
  }`);
});

//방장이 Game 시작시 모든유저 ready Y -> N 변경
arenaSocket.on('gameStart',(roomNum)=>{
  axios.post('/codeArena/gameStart', {roomNum})
})

// 방장이 Game 시작시 모든유저 ready 표시 해제
arenaSocket.on("remove_ok", () => {
  let green_ok = document.querySelectorAll(".u_r_circle")
  green_ok.forEach((ok) => {
    ok.style.display = "none"
  })
})

🖼️ Preview

Start Game.gif

Start

✅ 알고리즘 문제 풀이 성공 시 상태 변화

  • 주어진 문제에 대해 알고리즘 로직을 작성하고, 코드 실행을 클릭하면 실행 결과가 출력 됩니다.
  • 코드 실행 클릭 시, axios로 '문제 번호'에 대한 입력값과 출력값(정답)을 DB에서 응답 받습니다.
  • 그리고 Code Editor에 작성한 알고리즘(value) 값을 가져와서 입력 값을 대입해 실행시킨 결과 값과, 출력 값을 검증해서
  • 정답 유무를 판단하여 iframe에 출력해줍니다.
  • 코드 제출 클릭 시, 코드 실행과 같이 작성한 알고리즘을 실행시켜 검증 후, 정답이라면 ok 아이콘이 활성화 됩니다.
  • 방 인원이 모두 ok 아이콘이 활성화 되었을 때, 게임이 종료됩니다.

💻CodeDetail

코드 실행 버튼 클릭이벤트


codeArena.js (line 751)

    // 코드실행 버튼 클릭 이벤트
    codeStart.addEventListener('click', async(e)=>{
      e.preventDefault()
       // codeStart 요청을 보내서 문제번호와 일치하는 입력값과 출력값 database에서 반환
       const res = await axios.post('/codeArena/codeStart', {jsValue})
       .then(res=>{
         let data = JSON.parse(res.data)
         //입력값
         let resultInput = data[0].RESULT_INPUT
         //출력값
         let resultOutput = data[0].RESULT_OUTPUT   

         // script 태그 생성
         const scriptElement = document.createElement("script");
         // 생성한 script 태그안에 사용자가 입력한 값 추가
         scriptElement.innerHTML = `var n = ${resultInput};
                                    var outPut = ${resultOutput};
                                    ${jsValue};
                                    try{
                                     console.log('코드실행중');
                                       if(typeof codeBuddy !== 'undefined'){
                                          if(codeBuddy(n) == outPut){
                                            let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
                                            document.body.innerHTML = '정답입니다.<br>'+data                                           
                                            
                                          }
                                          else{                                            
                                            let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
                                            document.body.innerHTML = '틀렸습니다.<br>'+data 
                                          
                                          }
                                        }
                                       else {
                                         throw new Error('codeBuddy 함수가 정의되지 않았습니다.');
                                       }
                                     }
                                     catch(err){
                                       console.error('에러발생',err);
                                       var body = document.body                      
                                       body.innerHTML = err
                                       body.style.color = 'red'
                                     }
                                     `
                                     
         
         // script를 body 태그 안에 추가(script가 정상작동하게 하기위함)
         outPut.contentWindow.document.body.appendChild(scriptElement);
         
         
       })

    })
  

코드 제출 버튼 클릭이벤트


codeArenaList.js (line 805)

    // 코드제출 버튼 클릭 이벤트
    codeSubmit.addEventListener('click', async(e)=>{
      e.preventDefault()
      // codeStart 요청을 보내서 문제번호와 일치하는 입력값과 출력값 database에서 반환
      const res = await axios.post('/codeArena/codeStart', {jsValue})
        .then(res=>{
          let data = JSON.parse(res.data)
          //입력값
          let resultInput = data[0].RESULT_INPUT
          //출력값
          let resultOutput = data[0].RESULT_OUTPUT   

          // script 태그 생성
          const scriptElement = document.createElement("script");
          // 생성한 script 태그안에 사용자가 입력한 값 추가
          scriptElement.innerHTML = `
                                     var n = ${resultInput};
                                     var outPut1 = ${resultOutput};
                                     var name = '${currentNickname}';
                                     var startResult;
                                     ${jsValue};
                                     try{
                                      if(typeof codeBuddy !== 'undefined'){
                                        if(codeBuddy(n) == outPut1){
                                          let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
                                          let userS = document.querySelectorAll('.u_i_nick')
                                          userS.forEach((user)=>{
                                            if(name == user.dataset.user){                                              
                                              arenaSocket.emit('please', name)
                                              arenaSocket.emit('pleaesRoomNum')
                                            }
                                          })
                                          
                                        }
                                        else{                                            
                                          let data = startResult = '작성한 함수에' + n + '를 대입한 결과는 ' + codeBuddy(n) + '입니다.'
                                        }
                                      }
                                      else {
                                        throw new Error('codeBuddy 함수가 정의되지 않았습니다.');
                                      }
                                    }              
                                      catch(err){
                                        console.error('에러발생',err);
                                    
                                      }
                                      `
                                      
          
          // script를 body 태그 안에 추가(script가 정상작동하게 하기위함)
          document.body.appendChild(scriptElement);
          
          
        })
    })
문제 정답 시 실행로직

codeArenaList.js (line 862)

// 정답 제출한 사용자 ok 아이콘 활성화
arenaSocket.on('testSucess',(data)=>{
  let name = data
  let check = document.querySelector(`.${data}`)
  check.style.display = 'block'
  check.innerText = 'ok'

})

// 전체 사용자 모두 정답시 게임 종료
arenaSocket.on('okRoomNum',(data)=>{
  let data1 = data.roomNum
  axios.post('/codeArena/testSucess',{roomNum:data1, name:currentNickname})
    .then(res=>{
      let data = JSON.parse(res.data)
      data = data.COUNT
      if(data == currentUsers){
        arenaSocket.emit('gameSet')
      }
    })
})

🖼️ Preview

알고리즘 풀이.gif

문제풀이

알고리즘 풀이 상세.gif

알고리즘풀이 상세