728x90
✅ 구성
👤 사용자 화면
- 이름 + 전화번호 입력
- 내 번호 확인
- 실시간 대기열 확인
🧑💼 관리자 화면
- 다음 사람 호출 버튼
- 현재 호출 번호 전체 화면 강조
- 호출 시 소리 알림
- 실시간 갱신
🔒 서버
- Mutex (threading.Lock)
- 동시 접속 안전
- AJAX 기반 (새로고침 없음)
📁 파일 구조
queue_system/
├ app.py
└ templates/
├ user.html
└ admin.html
1️⃣ app.py (서버)
from flask import Flask, render_template, request, jsonify
import threading
app = Flask(__name__)
queue = []
queue_lock = threading.Lock()
next_id = 1
current_person = None
@app.route("/")
def user():
return render_template("user.html")
@app.route("/admin")
def admin():
return render_template("admin.html")
@app.route("/queue")
def get_queue():
with queue_lock:
return jsonify({
"queue": queue,
"current": current_person
})
@app.route("/enqueue", methods=["POST"])
def enqueue():
global next_id
data = request.get_json(silent=True)
if not data or "name" not in data or "phone" not in data:
return jsonify({"error": "잘못된 요청"}), 400
with queue_lock:
person = {
"id": next_id,
"name": data["name"],
"phone": data["phone"]
}
queue.append(person)
next_id += 1
return jsonify(person)
@app.route("/dequeue", methods=["POST"])
def dequeue():
global current_person
with queue_lock:
current_person = queue.pop(0) if queue else None
return jsonify(current_person)
if __name__ == "__main__":
app.run(threaded=True, debug=True)
2️⃣ templates/user.html (사용자 화면)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>줄서기</title>
<style>
body { font-family: Arial; text-align: center; margin-top: 50px; }
input, button { padding: 10px; margin: 5px; }
#myNumber { font-size: 24px; color: green; }
</style>
</head>
<body>
<h1>🎟️ 줄서기</h1>
<input id="name" placeholder="이름">
<input id="phone" placeholder="전화번호">
<button onclick="join()">줄서기</button>
<div id="myNumber"></div>
<h2>대기열</h2>
<ul id="queue"></ul>
<script>
function refresh() {
fetch('/queue').then(r=>r.json()).then(d=>{
let ul = document.getElementById('queue');
ul.innerHTML='';
d.queue.forEach(p=>{
let li=document.createElement('li');
li.textContent=`${p.id}번 - ${p.name}`;
ul.appendChild(li);
});
});
}
function join() {
const nameInput = document.getElementById('name').value;
const phoneInput = document.getElementById('phone').value;
if (!nameInput || !phoneInput) {
alert("이름과 전화번호를 입력하세요");
return;
}
fetch('/enqueue', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
name: nameInput,
phone: phoneInput
})
})
.then(r => r.json())
.then(p => {
document.getElementById('myNumber').textContent =
`✅ 접수 완료! 내 번호는 ${p.id}번입니다.`;
});
}
setInterval(refresh,1000);
refresh();
</script>
</body>
</html>
3️⃣ templates/admin.html (관리자 + 대형 화면)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>관리자</title>
<style>
body {
font-family: Arial;
background: black;
color: white;
text-align: center;
}
#current {
font-size: 120px;
color: yellow;
margin-top: 100px;
}
button {
font-size: 30px;
padding: 20px 40px;
}
</style>
</head>
<body>
<h1>📢 현재 호출 번호</h1>
<div id="current">-</div>
<button onclick="callNext()">다음 호출</button>
<audio id="beep">
<source src="https://actions.google.com/sounds/v1/alarms/beep_short.ogg">
</audio>
<script>
function refresh() {
fetch('/queue').then(r=>r.json()).then(d=>{
if (d.current) {
current.textContent = d.current.id + "번";
}
});
}
function callNext() {
fetch('/dequeue',{method:'POST'})
.then(r=>r.json())
.then(p=>{
if(p){
current.textContent = p.id + "번";
document.getElementById('beep').play();
}
});
}
setInterval(refresh,1000);
refresh();
</script>
</body>
</html>
🚀 실행 방법
pip install flask
python app.py
- 사용자 화면 👉 http://서버주소:5000/
- 관리자 👉 http://서버주소:5000/admin
- 관리자 페이지 접속시 비밀 번호 받기 및 전화번호 중복 처리 등 추가한 코드
- 실행 파일 만들때 꼭 html도 포함되도록 만들어야 함
pyinstaller.exe -F --add-data "templates:templates" app.py
728x90