{

"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},

"author": "",
"license": "ISC",
"dependencies": {
"express": "4.17.1", //express 추가.
"lodash": "4.17.20"
}
}

package.json

 

 

서버에서

npm install

명령어 입력하여 package.json의 dependencies에 입력된 tool 설치.

 

 

 

네이버 클라우드에서 8080port 오픈

 

 

 

front 쪽이랑 Backend 쪽 나눠서 디렉토리 분리

 

table,
th,
td {
    border: 1px solid black;
}

#board > div {
    height: 100px;
}

.font_4em {
    font-size: 4em;
}

 

부트스트랩으로 안되는 부분 Class 만들어 css 관리

<!DOCTYPE html>
<html lang="ko">
    <!-- 스튜디오 코드 단축키
//https://demun.github.io/vscode-tutorial/shortcuts/
//행 위아래 복사 : shift+alt+down, shift+alt+up.
//행삭제 ctrl+shift+k
// 주석 : ctrl+/
//사이드바 토글 ctrl+b
//전체화면 토글 f11
-->

    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" />
        <link rel="stylesheet" href="./assets/css/main.css" />
        <title>TicTacToe</title>
    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col">
                    <div class="btn-group d-flex mt-4">
                        <button type="button" class="btn btn-primary w-100 d-flex justify-content-between">
                            Name1
                            <span id="nameO">0</span>
                        </button>
                        <button type="button" class="btn btn-danger w-100 d-flex justify-content-between">
                            <span id="nameX">0</span>
                            Name2
                        </button>
                    </div>
                </div>
            </div>

            <div class="row py-3">
                <div class="col">
                    <button type="button" class="btn btn-info" onclick="TicTacToe.rematch()">Rematch</button>
                    <button type="button" class="btn btn-info" onclick="TicTacToe.reset()">Reset</button>
                </div>
                <span id="message"></span>
            </div>

            <div class="row" id="board">
                <div class="col-4 border" onclick="TicTacToe.click(0)">
                    <div class="text-center font_4em" id="0"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(1)">
                    <div class="text-center font_4em" id="1"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(2)">
                    <div class="text-center font_4em" id="2"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(3)">
                    <div class="text-center font_4em" id="3"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(4)">
                    <div class="text-center font_4em" id="4"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(5)">
                    <div class="text-center font_4em" id="5"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(6)">
                    <div class="text-center font_4em" id="6"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(7)">
                    <div class="text-center font_4em" id="7"></div>
                </div>
                <div class="col-4 border" onclick="TicTacToe.click(8)">
                    <div class="text-center font_4em" id="8"></div>
                </div>
            </div>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>
    <script src="./assets/js/main.js"></script>
</html>

 

html 파일

var TicTacToe = (function () {
    var _init = false
    var _getStatusIntervalSecond = 250

    function _setMessage(message) {
        document.getElementById("message").innerHTML = message
    }

    function _pollingGetStatus() {
        $.ajax({
            url: "구매한 도메인:8080(포트)/getStatus",
            type: "GET",
            success: function (res) {
                //실시간 html 그리기.
                document.getElementById("nameO").innerHTML = res.user["O"].score
                document.getElementById("nameX").innerHTML = res.user["X"].score
                for (var i = 0; i < res.game.block.length; i++) {
                    document.getElementById(i).innerHTML = res.game.block[i]
                }

                if (!res.game.playable) {
                    _setMessage("게임이 종료되었습니다.")
                }
                // 0.25초마다 polling 호출
                setTimeout(_pollingGetStatus, _getStatusIntervalSecond)
            },
        })
    }

    function _rematch(reset) {
        //새롭게 게임 시작시. 재대결 이어서 대결.
        $.ajax({
            url: "구매한 도메인:8080(포트)/matchStatus",
            type: "GET",
            data: {
                reset: reset,
            },
            success: function (res) {
                //reset 버튼 여부로 인하여 true false 처리.
                if (!res.status) {
                    alert(res.message)
                } else {
                    _setMessage("새 게임이 시작되었습니다.")
                }
            },
        })
    }

    return {
        init: function () {
            if (_init) {
                return false
            }
            _init = true

            _pollingGetStatus()
        },
        //index : html 클릭한 파라미터
        click: function (index) {
            _setMessage("")
            $.ajax({
                //app.listen(8080)받았기 때문에 해당 메소드로 진입.
                url: "구매한 도메인:8080(포트)/setMarker",
                type: "GET",
                data: {
                    index: index,
                },
                success: function (res) {
                    if (!res.status) {
                        alert(res.message)
                    }
                },
            })
        },
        rematch: function () {
            _rematch()
        },
        reset: function () {
            _rematch(true)
        },
    }
})()

TicTacToe.init()

main.js 

프론트 부분은 html 받아온 값만 이벤트 처리를 back단으로 넘기는 형식으로 구현하였다.

var express = require("express")
var app = express()
var _ = require("lodash")

// CORS대응 코드.
app.all("/*", function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*")
    res.header("Access-Control-Allow-Headers", "X-Requested-With")
    next()
})

// DATABASE
var _database = {
    game: {
        playable: true,
        count: 0,
        block: ["", "", "", "", "", "", "", "", ""],
        winner: "", // [X, O, _, ]
    },
    user: {
        X: { name: "user1", score: 0 },
        O: { name: "user2", score: 0 },
    },
}

var _successCondition = [
    //틱택토 게임 성공의 경우의 수.
    ["0", "1", "2"],
    ["3", "4", "5"],
    ["6", "7", "8"],
    ["0", "3", "6"],
    ["1", "4", "7"],
    ["2", "5", "8"],
    ["0", "4", "8"],
    ["2", "4", "6"],
]

function _getMarker() {
    //처음 시작시 X,O 표시
    return _database.game.count == 0 || _database.game.count % 2 == 0 ? "X" : "O"
}

function _getResult(marker) {
    var markeredIndex = _.compact(
        _.map(_database.game.block, function (value, index) {
            return value == marker ? index.toString() : ""
        })
    )

    var checkNumber = Math.abs(3 - markeredIndex.length)
    _.map(_successCondition, function (condition) {
        if (_.xor(condition, markeredIndex).length == checkNumber) {
            _database.user[marker].score++
            _database.game.playable = false
            _database.game.winner = marker
        }
    })

    if (_database.game.playable && _database.game.count == 9) {
        _database.game.winner = "_"
        _database.game.playable = false
    }
}

//matchStatus
app.get("/matchStatus", function (req, res) {
    var response = {
        status: false,
        message: "Wrong Access.",
    }

    if (_database.game.playable) {
        response.message = "게임이 진행중입니다."
    } else {
        _database = {
            game: {
                playable: true,
                count: 0,
                block: ["", "", "", "", "", "", "", "", ""],
                winner: "", // [X, O, _, ]
            },
            user: {
                X: { name: "baek", score: _database.user["X"].score },
                O: { name: "oh", score: _database.user["O"].score },
            },
        }

        if (req.query.reset) {
            _database.user = {
                X: { name: "baek", score: 0 },
                O: { name: "oh", score: 0 },
            }
        }

        response = {
            status: true,
            message: "게임이 초기화되었습니다.",
        }
    }

    res.send(response)
})

// getStatus
app.get("/getStatus", function (req, res) {
    res.send(_database)
})

/*
    - 마커를 입력한다.
    {
        index: int(required)
    }
*/
app.get("/setMarker", function (req, res) {
    var response = {
        status: false,
        message: "Wrong Access.",
    }

    //잘못된 값이 들어올경우의 예외처리
    if (typeof req.query.index === "undefined") {
        response.message += "(-1)"
    } else if ([0, 1, 2, 3, 4, 5, 6, 7, 8].includes(req.query.index)) {
        response.message += "(-2)"
    } else if (!_database.game.playable) {
        response.message = "게임이 종료되었습니다."
    } else if (_database.game.block[req.query.index].length) {
        response.message = "이미 선택된 블록입니다."
    } else {
        //해당 예외처리가 아니면 O,X 찍어주고 선택횟수가 5이상 넘넘어가면 게임종료.
        _database.game.block[req.query.index] = _getMarker()
        _database.game.count++

        if (_database.game.count >= 5) {
            _getResult(_database.game.block[req.query.index])
        }

        response = {
            status: true,
            message: "Success.",
        }
    }

    res.send(response)
})
//(오픈된 방화벽 port)
app.listen(8080)

 

중요 로직 및 통신관련부분은 index.js로 구분하여 처리

해당 소스 부분 수정시에는 

node index.js

를 서버에서 입력하여 실행

 

다른 브라우저.(즉 다른 세션)에서 동기화 되는것 테스트.

 

 

+ Recent posts