본문 바로가기
깃허브 링크!
node.js

socket사용(node.js로 영화관 자리 예약하기)

 서버 측 코드(node.js , express)

// 영화관을 만드는데 관을 1번비행기 2번비행기 3번비행기 나누어서 3개로
// 좌석을 예약 할수있게

// 사용할 모듈 
// socket.io express ejs 
// 서버 대기
// view 세팅
// 소켓 연결 까지

const express = require("express");
const app = express();
const path =require("path");

// 선택된 자리들을 보여줄 배열
let seats = [];

let temp = [
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1]
];

let temp2 = [
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1]
];

let temp3= [
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1]
];
let seatsArr = [temp, temp2, temp3];
// 선택한 비행기의 인덱스
let index = 0;

app.set("views",path.join(__dirname,"page"));
app.set("view engine","ejs");
app.unsubscribe(express.urlencoded({extended: false}));
app.get('/',(req,res)=>{
    res.render("main");
})

const socketio = require("socket.io");

app.get("/seats/:id",(req,res)=>{
    index = req.params.id;
    seats = seatsArr[index];
    // 요청에 대한 응답으로 seatsArr배열에서 id로 전달한 인덱스로 호출한 배열을 
    // 응답해준다. 
    res.send(seats);
})
const server=app.listen(8000,()=>{
    console.log("server on");
})


const io = socketio(server);

io.sockets.on("connection",(socket)=>{
    console.log("접속했습니다.");
    socket.on("reserve",(data)=>{
        console.log("예약");
        let seatTemp = seatsArr[data.selectCount];
        seatTemp[data.y][data.x] = 2;
        io.sockets.emit('reserve',data);
        
    })
})​


• 서버 설정:

• app.set("views" ,path.join(__dirname,"page"))   //view 파일의 경로 설정
• app.set("view engine","ejs")  // view 파일로 ejs 템플릿 엔진을 사용
• app.unsubscribe(express.urlencoded({extended:false}))  // 폼 데이터를 파싱하기 위한 미들웨어 설정

• 클라이언트 요청 처리:

• app.get("/seats/:id" , (req,res) =>{ ... }  //seats/:id 경로에 대한 get 요청을 처리하며, 해당 id에 해당하는 비행기의 좌석정보를 응답한다.  😂😂😂(어렵다)

• 소켓 통신 :
• io.sockets.on("connection", (socket) => { ... }  // 클라이언트와의 소켓 연결을 처리하는 함수
 클라이언트로부터 "reserve" 이벤트를 수신하고 다른 클라이언트에게 예약 정보를 전달  😂😂😂(어렵다)

•  좌석 예약 처리:
•  socket.on("reserve", (data) =>{...}  // 클라이언트로부터 예약 정보를 받아서 처리하는 함수
좌석 예약 상태를 업데이트하고 "reserve" 이벤트를 다른 클라이언트에게 전송 😂😂😂(어렵다)

 

어려운 부분 다시
• app.get("/seats/:id" , (req,res) =>{ ... } 
 //  이 코드는 클라이언트가 /seats/:id 경로로 get 요청을 보낼때 서버에서 처리하는 부분
     예를 들어 /seats/1 경로로 get 요청이 오면 id가 1인 비행기의 좌석 정보를 응답으로 전송한다.

• io.sockets.on("connection", (socket) => { ... }  
// 

•  socket.on("reserve", (data) =>{...}
// 클라이언트가 좌석을 예약할려고 할때 해당 이벤트가 발생한다.
   서버는 받은 예약 정보(data)를 처리하고 좌석 예약 상태를 업데이트 한다. 
   이후 reserve 이벤트를 다른 클라이언트에게 전송하여 실시간으로 좌석 예약 상태를 업데이트한다.

 

새로알게된 내용
• params??
express.js에서 요청 객체의 하위 객체중 하나
req.params 객체는 url의 경로 매개변수를 포함하고 있다.
예를 들어 "/users/:id와 같은 경로에 대한 요청이 있을 때. "id" 는 동적으로 변하는 사용자 Id값을 나타낸다.

 

코드 분석
app.get("/seats/:id",(req,res)=>
{ index = req.params.id;
seats = seatsArr[index];

1. 클라이언트가 "/seats/:id 경로로 get 요청을 보낸다.
2. 서버는 해당 값을 받고 req.params.id를 통해 동적인 경로 값에 접근한다. 이 값을 인덱스에 저장
3. seatsArr 배열에서 index를 사용하여 해당 비행기의 좌석 정보를 가져온다. 좌석 정보는 seats 변수에 할당
4. res.send(seats)를 사용하여 클라이언트에게 좌석 정보를 응답한다.

클라이언트 측 코드 (HTML,javaScript)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .line {
            overflow: hidden;
        }
        .seat{
            margin: 2px;
            float: left;
            width: 30px;
            height: 30px;
            border-radius: 3px;
        }
        .enable{
            background-color: gray;
        }
        .disable{
            background-color: blueviolet;
        }
    </style>
    <script src="/socket.io/socket.io.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <div>비행기</div>
    <!--  select 선택 박스 태그 -->
    <select name="" id="selectBtn">
        <option value="0">1번</option>
        <option value="1">2번</option>
        <option value="2">3번</option>
    </select>
    <div id="content"></div>
</body>
<script>
    window.onload =() =>{
        const socket = io.connect();
        socket.on('reserve',(data)=>{
            if(data.selectCount == selectBtn.selectedIndex){
                let target = document.querySelector(`div[data-x="${data.x}"][data-y="${data.y}"]`);
                target.classList.remove("enable");
                target.classList.add("disable");
            }
        })
        let selectCount = 0;
        selectBtn.onchange = function(){
            content.innerHTML = "";
            // select 태그의 선택한 옵션의 value를 호출한다.
            selectCount = this.selectedIndex;
            console.log(selectCount);
            // 시트 생성 함수 여기에
            getseats(selectCount); 
        }
        // 시트를 클릭해서 예약하는 함수
        const onClickSeat = function(){
            // 클래스가 있는지 확인 메서드 contains
            if(this.classList.contains("disable")){
                // 이미 예약이 되어있으면 여기서 끝
                return;
            }
            // 어트리뷰트 데이터 속성을 호출 getAttribute 메서드로 매개변수로 가져올 속성 이름
            let x = this.getAttribute("data-x");
            let y = this.getAttribute("data-y");
            if(confirm("이 좌석을 예약할거임?")){
                // socket 이벤트를 푸쉬할 예정
                socket.emit("reserve",{
                    x,
                    y,
                    selectCount
                })
            }else{
                alert("ㅇㅇ 하지마셈");
            }
        }

        // 시트 생성 함수
        function getseats(selectIndex){
            // 요청 응답으로 시트를 가져올 예정
            // 변수로 받을 예정
            // axios 무조건 이것만 쓸거임
            // CDN으로 사용 
            // 요청은 get방식이고 매개변수는 아이디 값으로 요청
            axios.get("/seats/" + selectIndex).then((e)=>{
                // 요청 이후 응답받은 값이 e
                // 시트 배열이 넘어온다.
                console.log(e);
                let {data} = e;
                data.forEach((line, indexY) =>{
                    let lineElem = document.createElement("div");
                    // 시트들의 열
                    lineElem.classList.add("line");

                    line.forEach((seat, indexX)=>{
                        let seatElem = document.createElement("div");
                        // 시트들
                        seatElem.classList.add("seat");
                        // 데이터 속성 이라는 어트리뷰트속성을 사용
                        // setAttribute : 어트리뷰트 속성을 추가 메서드
                        // 첫번째 매개변수는 속성의 이름
                        // 두번째 매개변수는 속성의 값
                        seatElem.setAttribute("data-x",indexX);
                        seatElem.setAttribute("data-y",indexY);
                        // 빈공간, 예약 가능한 시트, 이미 예약된 시트
                        if(seat == 1){
                            seatElem.classList.add("enable");
                            // 시트를 클릭하면 예약
                            seatElem.addEventListener("click",onClickSeat);
                        }else if(seat == 2){
                            seatElem.classList.add("disable");
                        }
                        lineElem.appendChild(seatElem);
                    });
                    content.appendChild(lineElem);
                })
            })
        }
    }
</script>
</html>​

• 소켓 통신 :

• const socket = io.connect()  // 클라이언트가 서버와 소켓 연결을 수행한다.
• socket.on(`reserve`, (data) => {...}   //서버로부터 reserve 이벤트를 수신하면 실행되는 콜백함수


• 비행기 선택 :

•selectBtn.onchange = function() {...}
// 선택한 비행기에 해당하는 좌석을 서버로부터 가져와서 표시한다.

• 좌석 예약

•const onClickseat = function() {...}
// 좌석을 클릭했을때 실행되는 함수 예약 가능한 좌석인 경우에만 동작가능하다.

 

 

코드 분석
     const socket = io.connect();
        socket.on('reserve',(data)=>{
            if(data.selectCount == selectBtn.selectedIndex){
                let target = document.querySelector(`div[data-x="${data.x}"][data-y="${data.y}"]`);
                target.classList.remove("enable");
                target.classList.add("disable");
            }
        })​


1. 클라이언트에서 소켓을 생성하고 서버에 연결한다. 
2. 서버로부터 reserve 이벤트를 수신할 때의 동작을 정의 
    즉 다른 클라이언트에서 좌석 예약 정보를 전송했을 때 이를 수신하여 처리한다.

3.if (data.selectCount == selectBtn.selectedIndex) { ... }
   // 클라이언트에서 수신한 예약 정보와 현재 클라이언트에서 선택한 비행기가 일치하는지 확인하는 조건문

  •data.selectCount는 클라이언트에서 선택한 좌석정보
  • selectBtn.selectedIndex는 현재 클라이언트에서 선택된 비행기의 인덱스
   // 이 값은 선택한 비행기의 좌석 번호를 나타냄

4. let target = document.querySelector('div[data- ....) 
    // html 문서에서 특정 선택자에 해당하는 첫 번째 요소를 선택한다.
    이 부분에서는 div 요소중에서 data-x 속성값이 data.x와 일치하고 data-y 속성값이 data.y와 일치하는 요소를 선택함