[LMS7 14/28주차] 0812 Qt 개인 프로젝트, 라이어 게임 완료 보고서

2025. 9. 4. 20:40·C++/Project

00. 개발완료보고서

제출일 : 2025.08.22(금)

​

개발 완료 보고서
프로젝트
라이어 게임
개발인원
이명진
활동일시
25.08.14 ~ 25.08.22
장소
공학 1관 드론융합실
주요주제
상대방을 속여 최후의 승자가 되거나 협력하여 공동의 승리를 할 수 있는 멀티플레이어 심리 게임 구현
개발 환경
- OS : Ubuntu 24.04.2 LTS
- Language : C++
- IDE : Qt Creator, MySQL
구현기능
- 회원가입 : 사용자로부터 아이디, 비밀번호, 닉네임 등의 정보를 받아 MySQL 데이터베이스에 안전하게 저장하고, 비밀번호는 암호화하여 저장함.
​
- 로그인: 사용자가 입력한 아이디와 비밀번호를 데이터베이스의 정보와 비교하여 인증을 처리.
​
- 회원정보 찾기 (아이디/비밀번호 찾기): 사용자가 등록한 이메일 주소 등을 통해 아이디를 찾거나, 비밀번호를 재설정할 수 있는 기능을 제공.
​
- 닉네임 변경 : 로그인한 사용자가 닉네임을 수정할 수 있도록 함.
​
- 비밀번호 변경 : 로그인한 사용자가 비밀번호를 수정할 수 있도록 함.
​
- 방 생성 : 사용자가 직접 게임방을 만들 수 있도록 함.
​
- 방 입장 : 사용자가 생성된 방에 참여할 수 있도록 함.
​
- 방 삭제 : 자신이 생성한 방을 삭제할 수 있도록
​
- 일반 채팅: 게임 시작 전, 대기실이나 로비에서 플레이어들이 자유롭게 대화할 수 있는 채팅 기능을 제공.
​
- 카테고리 : 게임 시작 전, 방장이 카테고리를 선택.
​
- 라이어 선정 : 랜덤하게 라이어를 선정하여 출력함.
​
- 단어 분배 : 서버는 선택된 카테고리에서 무작위로 정답 단어를 선정.
'라이어'를 제외한 모든 플레이어에게는 정답 단어를 알려줌.
​
- 게임 채팅: 게임 시작 후, 라운드당 한 번씩만 채팅할 수 있도록 제한하여 무분별한 힌트 노출을 방지하고 심리전을 강화.
​
- 라이어 정답 : 모든 라운드 완료 후 라이어가 정답을 말할 수 있도록 함.
​
- 투표 : 라이어가 정답을 맞추지 못하면 모든 사람들이 투표할 수 있도록 함.
​
- 게임종료 : 라이어의 투표수가 많으면 시민의 승리로, 그렇지 않으면 라이어의 승리를 출력함.
그 외 투표가 동률일 때에는 무승부로 라운드를 한번 더 진행함.
예상문제점
- 네트워 및 동기화 문제 : 멀티플레이어 게임 특성상 네트워크 지연이 발생하면 게임 진행에 문제가 생길 수 있음.
​
- 공정성 문제 : 승패에 영향을 주는 중요한 데이터가 클라이언트에서 조작될 경우, 게임의 공정성이 무너질 수 있음.
​
- 게임 밸런스 : 게임 규칙이 너무 복잡하거나, 특정 전략이 너무 강력하면 게임의 재미가 반감될 수 있음.
해결방법
- 클라이언트에서 중복 입력으로 서버의 부담을 주어 네트워크 지연 발생을 염려하여 버튼 한번 클릭 후에는 버튼을 비활성화하고, 서버에서 응답을 받을 때만 버튼을 활성화시키는 방식으로 최대한 방지함.
​
- 승패에 영향을 줄 수 있는 처리는 모두 서버에서 해결하고, 단순 행동에 대한 요청만 클라이언트에서 담당하였음.
​
- 라이어게임의 기존 규칙을 최대한 바꾸지 않도록 하였고, 3라운드라는 비교적 짧은 시간으로 게임을 끝낼 수 있도록 하여 게임 규칙을 간단히 함.
요구사항 분석서 체크리스트
1. 참조
수정문서
2. 참조
구현 스크린샷 및 기능 설명
3. 참조
실행 영상
4. 참조
소스코드
5. 참조
개인 개발 후기
6. 참조

01. 요구사항 분석서 체크리스트

기능적 요구사항
ID
기능명
세부내용
UC
성공여부
설명
FR01
회원가입
- 아이디, 비밀번호, 닉네임으로 회원가입할 수 있어야 함.
- 비밀번호는 암호화되어야 함.
UC01
O
로그인 페이지에서
가능
FR02
로그인
- 아이디와 비밀번호를 입력하여 로그인할 수 있어야 함.
UC02
O
로그인 페이지에서
가능
FR03
회원정보 찾기
등록된 이메일 등을 통해 아이디를 찾거나 비밀번호를 재설정할 수 있어야 함.
UC03
O
로그인 페이지에서
가능
FR04
회원정보 변경
로그인 후 비밀번호와 닉네임을 변경할 수 있어야 함.
UC04
O
메인 페이지에서 가능
FR05
게임방
생성
게임 제목, 최대 인원 등을 설정하여 새로운 게임방을 만들 수 있어야 함.
UC05
O
메인 페이지에서 가능
FR06
게임방
입장
생성된 게임방 목록을 보고 원하는 방에 입장할 수 있어야 함.
UC06
O
메인 페이지에서 가능
FR07
일반 채팅
게임 시작 전 대기실에서 자유롭게 채팅할 수 있어야 함.
UC07
O
게임 로비 페이지에서 가능
FR08
게임 시작
방장이 게임을 시작할 수 있어야 함.
UC08
O
게임 로비 페이지에서 가능
FR09
게임 채팅
게임 시작 후에는 라운드당 한 번씩만 채팅할 수 있어야 함.
UC09
O
게임 페이지에서 가능
FR10
카테고리 선택
게임 시작 후 각 라운드마다 참여자들이 게임의 카테고리를 선택할 수 있어야 함.
UC10
O
게임 페이지에서 가능
FR11
단어 분배
선택된 카테고리에서 정답 단어와 라이어 단어를 선정하여 각 플레이어에게 다르게 분배해야 함.
UC11
O
게임 페이지에서 가능
FR12
단어 확인
'라이어'를 제외한 모든 플레이어는 정답 단어를, '라이어'는 다른 단어를 화면에서 확인할 수 있어야 함.
UC12
O
게임 페이지에서 가능
FR13
투표
라운드 종료 후, 플레이어들이 '라이어'라고 생각하는 사람에게 투표할 수 있어야 함.
UC13
O
게임 페이지에서 가능
FR14
승패결정
투표 결과 등 게임 규칙에 따라 '라이어'를 찾아내거나 '라이어'가 승리하는 등의 최종 승패를 결정해야 함.
UC14
O
게임 페이지에서 가능
FR15
게임 상태
동기화
시스템은 모든 플레이어의 게임 상태(턴, 남은 시간, 채팅 내용 등)를 실시간으로 동기화해야 함.
UC15
O
전체적 관리

​

비기능적 요구사항
ID
유형
세부내용
성공
여부
설명
NFR01
성능
C++와 MySQL을 활용하여 10명 이내의 플레이어가 참여하는 게임 환경에서 딜레이 없이 원활하게 작동해야 함.
△
총 10명의 플레이어가 게임에 참여가능. 간헐적 끊김 발생.
NFR02
보안
비밀번호는 암호화하여 저장해야 하며, 회원정보 찾기/변경 시 강력한 본인 인증 절차를 거쳐야 한다.
O
비밀번호 암호화, 각각 아이디 및 이메일을 요구함.
NFR03
안정성
서버 오류 발생 시 데이터 유실을 최소화하는 방안을 고려해야 함.
X
서버 끊김 발생 시 DB의 저장 내용을 초기화하지 못함.
NFR04
사용성
Qt Creator로 개발된 UI는 직관적이며 사용하기 편리해야 함.
O
이미지를 통해 시각적인 디자인 개선, 버튼과 Edit 같은 UI는 직관적으로 배치함.
NFR05
호환성
Ubuntu 24.04.2 LTS 개발 환경에서 안정적으로 동작해야 함.
O
안정적으로 동작함.

02. 수정문서

없음


03. 구현 스크린샷 및 설명

1. 서버와 로그인 화면

>

로그인, 회원가입, 아이디 찾기, 비밀번호 찾기 선택

 

1.2 로그인 화면

> 아이디, 비밀번호 입력후 로그인 가능

 

1.3 회원가입 화면

> 각 입력창 입력후 회원가입 가능

 

1.4 아이디 찾기 화면

> 이메일 입력 시 아이디 찾기 가능

 

1.5 비밀번호 찾기 화면(실제기능은 비밀번호 변경)

> 각 입력창 입력 후 비밀번호 변경 가능


2. 메인화면

> 닉네임 표시, 설정, 방만들기, 방 삭제, 입장 가능

 

2.1 닉네임 변경, 비밀번호 변경, 로그아웃 가능

 

2.2 닉네임 변경

> 변경할 닉네임 입력후 변경 가능

 

2.3 비밀번호 변경

> 이전 비밀번호와 변경 비밀번호 입력 후 변경 가능

 

2.4 로그아웃

 

2.5 방 만들기

> 방 이름 입력 후 방 만들기 가능

 

2.6 방 삭제

> 해당 방의 방장만 삭제 가능

 

2.7 방 입장

> 입장 버튼으로 방 입장 가능


3. 게임 로비 화면

> 닉네임 표시, 일반채팅, 유저 입장 목록

 

3.1 카테고리 선택

> 게임시작 버튼 클릭 후 카테고리 선택 가능

 

3.2 게임시작

> 방장만이 게임을 시작할 수 있음


4. 게임화면

> 닉네임 표시, 직업 표시, 일반채팅, 게임채팅, 시스템메시지

 

4.1 게임로직1

> 해당 인원들이 1라운드마다 게임채팅으로 힌트를 제공

 

4.2 게임로직2

> 모든 라운드 진행 후 라이어는 정답을 입력

> 맞췄다면 라이어의 승리, 틀렸다면 투표를 진행

 

4.3 게임로직3

> 투표를 진행하여 라이어가 가장 많은 득표를 하면 시민의 승리, 무승부의 경우 라운드를 재시작, 그 외의 경우에는 라이어의 승리

 

4.4 게임로직4

> 최종 결과를 출력하고 게임 상태 초기화 후 해당 방에 있는 클라이언트들의 화면을 게임로비화면으로 전환


04. 실행영상

 

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.


05. 전체 소스코드 및 주요코드

LiarGameClient.zip
9.88MB
LiarGameServer.zip
5.80MB

 

1. 서버-DB 연결

// 버튼과 서버시작 연결
void MainWindow::on_serverStartButton_clicked()
{
    // 서버가 현재 실행 중인지 확인
    if (tcpServer->isListening()) {
        // 서버가 실행 중이면 종료 로직 수행
        tcpServer->close();
        ui->logTextEdit->append("서버가 종료되었습니다.");
        ui->serverStatusLabel->setText("서버 상태: <font color='red'>종료됨</font>");
        ui->serverStartButton->setText("서버 시작");
        ui->dbStatusLabel->setText("DB 연결 상태: <font color='gray'>끊김</font>");
        if (db.isOpen()) {
            db.close();
        }
    } else {
        // 서버가 종료 상태면 시작 로직 수행
        // 1. DB 연결
        if (!connectDatabase()) {
            ui->logTextEdit->append("DB 연결 실패로 인해 서버를 시작할 수 없습니다.");
            QMessageBox::critical(this, "서버 시작 오류", "DB 연결에 실패했습니다.");
            return;
        }

        // 2. DB 연결 성공 시, 서버 시작
        if (tcpServer->listen(QHostAddress::Any, 9806)) {
            ui->logTextEdit->append("서버 시작 성공! 포트 9806에서 대기 중.");
            ui->serverStatusLabel->setText("서버 상태: <font color='green'>실행 중</font>");
            ui->serverStartButton->setText("서버 중지");
        } else {
            QString errorMsg = "서버 시작 실패: " + tcpServer->errorString();
            ui->logTextEdit->append(errorMsg);
            ui->serverStatusLabel->setText("서버 상태: <font color='red'>실패</font>");
            QMessageBox::critical(this, "서버 시작 오류", errorMsg);
        }
    }
} // on_serverStartButton_clicked()
// DB 연결
bool MainWindow::connectDatabase()
{
    // DB 연결 정보를 반복적으로 설정하지 않도록 한 번만 설정
    if (db.isOpen()) {
        db.close();
    }

    db.setHostName(DB_HOST_NAME);
    db.setDatabaseName(DB_DATABASE_NAME);
    db.setUserName(DB_USER_NAME);
    db.setPassword(DB_PASSWORD);

    if (db.open()) {
        ui->logTextEdit->append("DB 연결 성공!");
        ui->dbStatusLabel->setText("DB 연결 상태: <font color='green'>성공</font>");
        return true;
    } else {
        ui->logTextEdit->append("DB 연결 실패: " + db.lastError().text());
        ui->dbStatusLabel->setText("DB 연결 상태: <font color='red'>실패</font>");
        return false;
    }
} // connectDatabase()

 

1.1 클라이언트-서버 연결

<서버>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // TCP 서버 객체 생성 및 시그널 연결
    tcpServer = new QTcpServer(this);
    connect(tcpServer, &QTcpServer::newConnection, this, &MainWindow::handleNewConnection);

    // DB 객체 초기화
    db = QSqlDatabase::addDatabase("QMYSQL");
} // MainWindow()
void MainWindow::handleNewConnection()
{
    // 새로운 클라이언트 소켓을 가져와서 리스트에 추가
    QTcpSocket* newClientSocket = tcpServer->nextPendingConnection();
    if (newClientSocket) {
        clientSockets.append(newClientSocket);
        ui->logTextEdit->append("새로운 클라이언트 접속: " + newClientSocket->peerAddress().toString());

        // 소켓의 시그널들을 슬롯에 연결
        connect(newClientSocket, &QTcpSocket::readyRead, this, &MainWindow::handleSocketReadyRead);
        connect(newClientSocket, &QTcpSocket::disconnected, this, &MainWindow::handleSocketDisconnected);

        // 새로운 클라이언트의 패킷 관련 정보 초기화
        clientDataBuffers[newClientSocket] = QByteArray();
        clientNextBlockSizes[newClientSocket] = 0;

        // 로그에 연결 정보 기록
        qDebug() << "새로운 클라이언트 연결:" << newClientSocket->peerAddress().toString();

        // 새로운 클라이언트가 연결되자마자 DB에 있는 방 목록을 전송
        sendRoomListToAllClients();
    }
} // handleNewConnection()

 

<클라이언트>

// 소켓 시그널-슬롯 연결
    connect(socket, &QTcpSocket::connected, this, &MainWindow::handleConnected);
// 연결
void MainWindow::handleConnected()
{
    qDebug() << "서버에 연결되었습니다.";
}

2. 서버와 클라이언트 간 데이터 전송

<서버>

// 클라이언트의 전송 데이터 관리
void MainWindow::handleSocketReadyRead()
{
    QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
    if (!socket) return;

    // 소켓과 연결된 QDataStream을 생성합니다.
    QDataStream in(socket);
    in.setVersion(QDataStream::Qt_6_0);

    // 루프를 사용하여 소켓에 남은 모든 데이터 패킷을 처리합니다.
    while (socket->bytesAvailable() > 0) {
        quint32 nextBlockSize = 0;

        // 패킷 크기(4바이트)가 충분히 도착했는지 확인합니다.
        if (socket->bytesAvailable() < (int)sizeof(quint32)) {
            return;
        }

        // 패킷 크기를 읽고, 스트림의 위치를 다음 데이터로 이동시킵니다.
        in >> nextBlockSize;

        // 패킷 본문이 완전히 도착했는지 확인합니다.
        if (socket->bytesAvailable() < nextBlockSize) {
            return;
        }

        // 패킷 본문 데이터를 읽습니다.
        QByteArray jsonData;
        in >> jsonData;

        // JSON 데이터 처리
        QJsonParseError parseError;
        QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError);

        if (!doc.isNull() && doc.isObject() && parseError.error == QJsonParseError::NoError) {
            HandleRequestByClient(socket, doc.object());
        } else {
            ui->logTextEdit->append("잘못된 JSON 형식의 요청 수신");
            qDebug() << "파싱 오류: " << parseError.errorString();
        }
    }
} // handleSocketReadyRead()
// 클라이언트로부터 요청
void MainWindow::HandleRequestByClient(QTcpSocket* socket, const QJsonObject& request)
{
    // JSON 객체에서 "type" 키를 읽음.
    QString requestType = request["type"].toString();

    // 로그에 요청 타입과 클라이언트 주소 기록
    ui->logTextEdit->append(QString("요청 수신 (%1): %2").arg(socket->peerAddress().toString(), requestType));

    // "type" 키와 대응된 값에 따라 처리 함수로 분기
    // 클라이언트와 일치해야 함
    if (requestType == "login") {
        handleLogin(socket, request);
    } else if (requestType == "signup") {
        handleSignUp(socket, request);
    } else if (requestType == "check_id") {
        handleCheckId(socket, request);
    } else if (requestType == "check_email") {
        handleCheckEmail(socket, request);
    } else if (requestType == "find_account") {
        handleFindAccount(socket, request);
    } else if (requestType == "password_change") {
        handlePasswordChange(socket, request);
    } else if (requestType == "logout") {
        handleLogout(socket, request);
    } else if (requestType == "change_nickname") {
        handleNicknameChange(socket, request);
    } else if (requestType == "create_room") {
        handleCreateRoom(socket, request);
    } else if (requestType == "join_room") {
        handleJoinRoom(socket, request);
    } else if (requestType == "leave_room") {
        handleExitRoom(socket, request);
    } else if (requestType == "delete_room") {
        handleDeleteRoom(socket, request);
    } else if (requestType == "chat") {
        handleChat(socket, request);
    } else if (requestType == "start_game") {
        handleStartGame(socket, request);
    } else if (requestType == "hint_message") {
        handleHintMessage(socket, request);
    } else if (requestType == "liar_answer") {
        handleLiarAnswer(socket, request);
    } else if (requestType == "vote") {
        handleVote(socket, request);
    }
}
// 클라이언트에게 전송
void MainWindow::SendResponseToClient(QTcpSocket* socket, const QJsonObject& response)
{
    // JSON 객체를 바이트 배열로 변환
    QByteArray jsonData = QJsonDocument(response).toJson(QJsonDocument::Compact);

    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_6_0);

    // 4바이트 헤더를 위한 공간을 확보합니다.
    out << (quint32)0;

    // JSON 데이터를 직렬화하여 담습니다.
    out << jsonData;

    // 헤더를 다시 쓰고, 전체 데이터 크기를 업데이트합니다.
    out.device()->seek(0);
    out << (quint32)(block.size() - sizeof(quint32));

    // 데이터 전송
    socket->write(block);
    socket->flush();
}

 

<클라이언트>

// 클라이언트의 전송 데이터 관리
void MainWindow::handleSocketReadyRead()
{
    QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
    if (!socket) return;

    // 소켓과 연결된 QDataStream을 생성합니다.
    QDataStream in(socket);
    in.setVersion(QDataStream::Qt_6_0);

    // 루프를 사용하여 소켓에 남은 모든 데이터 패킷을 처리합니다.
    while (socket->bytesAvailable() > 0) {
        quint32 &nextBlockSize = clientNextBlockSizes[socket];

        // 패킷 크기 아직 모르면 읽기 시도
        if (nextBlockSize == 0) {
            if (socket->bytesAvailable() < (int)sizeof(quint32))
                break; // 아직 헤더도 안 옴 → 다음 readyRead 때 이어서
            in >> nextBlockSize;
        }

        // 패킷 크기(4바이트)가 충분히 도착했는지 확인합니다.
        if (socket->bytesAvailable() < nextBlockSize) {
            break;
        }

        // 패킷 본문 데이터를 읽습니다.
        QByteArray jsonData;
        in >> jsonData;

        // JSON 데이터 처리
        QJsonParseError parseError;
        QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError);

        if (!doc.isNull() && doc.isObject() && parseError.error == QJsonParseError::NoError) {
            HandleRequestByClient(socket, doc.object());
        } else {
            ui->logTextEdit->append("잘못된 JSON 형식의 요청 수신");
            qDebug() << "파싱 오류: " << parseError.errorString();
        }
        nextBlockSize = 0;
    }
} // handleSocketReadyRead()
// 서버의 요청 응답
void MainWindow::ResponseByServer(const QJsonObject& json)
{
    // 응답 처리가 시작되면 isRequestInProgress를 리셋하고 버튼 활성화 함수 호출
    isRequestInProgress = false;
    setAllButtonsEnabled(true);

    QString type = json["type"].toString();
    // 회원 정보 관련 응답-------------------------------------------------------------------------
    // 회원가입 응답
    if (type == "signup_response") {
        handleSignupResponse(json);
    }
    // 아이디 중복검사 응답
    else if (type == "check_id_response") {
        handleIdCheckResponse(json);
    }
    // 이메일 중복검사 응답
    else if (type == "check_email_response") {
        handleEmailCheckResponse(json);
    }
    // 로그인 응답 처리 로직
    else if (type == "login_response") {
        handleLoginResponse(json);
    }
    // 아이디 찾기 응답
    else if (type == "find_account_response") {
        handleFindAccountResponse(json);
    }
    // 비밀번호 찾기시 변경 응답
    else if (type == "password_change_response") {
        handlePasswordChangeResponse(json);
    }
    // 로그아웃
    else if (type == "logout_response") {
        handleLogoutResponse(json);
    }
    // 닉네임 변경 응답
    else if (type == "change_nickname_response") {
        handleChangeNicknameResponse(json);
    }
    // 비밀번호 변경 응답
    else if (type == "change_password_response") {
        handleChangePasswordResponse(json);
    }
    // 회원 정보 관련 응답-------------------------------------------------------------------------

    // 게임 관련 응답-----------------------------------------------------------------------------
    // 서버에서 보낸 방 목록 업데이트 응답 처리
    else if (type == "room_list_update") {
        roomArray = json["room_list"].toArray();
        qDebug() << "서버로부터 방 목록 업데이트 받음. 방 개수:" << roomArray.size();

        // 방 목록이 보이는 페이지라면 즉시 위젯을 업데이트합니다.
        handleRoomListUpdate();
    }
    else if (type == "user_list_update") {
        handleUserListUpdate(json);
    }
    // 방 생성 응답 처리 로직
    else if (type == "create_room_response") {
        handleCreateRoomResponse(json);
    }
    // 방 제거 응답 처리 로직
    else if (type == "delete_room_response") {
        handleDeleteRoomResponse(json);
    }
    // 방 입장 응답
    else if (type == "join_room_response") {
        handleJoinRoomResponse(json);
    }
    // 방 나가기 응답 처리 로직
    else if (type == "leave_room_response") {
        handleLeaveRoomResponse(json);
    }
    // 채팅 메시지 응답 처리 로직
    else if (type == "chat_response") {
        handleChatResponse(json);
    }
    // 게임 시작 응답 처리 로직
    else if (type == "start_game_response") {
        handleStartGameResponse(json);
    }
    // 게임 채팅 응답 처리 로직
    else if (type == "game_chat") {
        handleGameChat(json);
    }
    else if (type == "liar_answer_phase") {
        handleLiarAnswerPhase(json);
    }
    else if (type == "vote_phase") {
        handleVotePhaseResponse(json);
    }
    else if (type == "game_end"){
        handleGameEndResponse(json);
    }
    // 서버 메시지 출력 응답 처리 로직
    else if (type == "system_message") {
        handleSystemMessage(json);
    }
}
// 서버에게 요청 전송
void MainWindow::SendRequestToServer(const QJsonObject& request)
{
    // QByteArray에 데이터를 직렬화합니다.
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_6_0);

    // 4바이트 크기 헤더를 위한 공간을 확보합니다.
    out << (quint32)0;

    // JSON 객체를 바이트 배열로 직렬화합니다.
    QJsonDocument doc(request);
    out << doc.toJson(QJsonDocument::Compact);

    // 헤더를 다시 쓰고, 전체 데이터 크기를 업데이트합니다.
    out.device()->seek(0);
    out << (quint32)(block.size() - sizeof(quint32));

    // 최종 데이터를 소켓으로 전송합니다.
    socket->write(block);

    qDebug() << "JSON 데이터 전송 성공 (총 크기:" << block.size() << "바이트)";
}

06. 개인개발후기

라이어 게임을 만들면서 C++도 아직 익숙하지 않는데 그보다 익숙치 않은 Qt를 다루게 되어 코드를 작성하는 동시에 공부해야할 부분이 많았기 때문에 예상보다 많은 시간이 걸렸습니다.

단순히 이미지를 삽입하는 작업조차 경로를 직접 설정해야 하는 등 사소한 부분에서 시행착오가 많았습니다.

클라이언트–서버–DB 구조를 설계할 때는, 먼저 연결 과정을 확실히 구축한 덕분에 이후에는 연결 문제에 대한 부담을 줄일 수 있었습니다.

또한 클라이언트와 서버 간의 요청·응답을 처리하는 함수를 별도로 정의해 둔 덕분에, 새로운 로직을 구현할 때 기존 함수를 재사용하기만 하면 되어 매우 편리했습니다.

C++을 사용했음에도 불구하고 여전히 C 언어식 절차지향적인 코드가 많이 나온 점은 아쉬움으로 남습니다.

앞으로는 의식적으로라도 여러 기능을 담당하는 클래스를 정의하고, 그 관계를 설계하는 데 더 많은 노력을 기울여야겠다고 생각합니다.

'C++ > Project' 카테고리의 다른 글

[LMS7 24/28주차] 1024 MFC 프로젝트, "CanSCan" 완료 보고서  (0) 2026.05.06
[LMS7 24/28주차] 1024 MFC 프로젝트, "CanSCan" 개발계획서  (0) 2026.05.06
[LMS7 16/26주차] 0826 [제60회 전국기능경기대회] 전시 작품 제작 프로젝트, "스마트홈" 완료 보고서  (0) 2025.11.11
[LMS7 16/26주차] 0826 [제60회 전국기능경기대회] 전시 작품 제작 프로젝트, "스마트홈" 개발 계획서  (0) 2025.11.11
[LMS7 14/28주차] 0812 Qt 개인 프로젝트, "라이어게임" 개발 계획서  (6) 2025.08.17
'C++/Project' 카테고리의 다른 글
  • [LMS7 24/28주차] 1024 MFC 프로젝트, "CanSCan" 개발계획서
  • [LMS7 16/26주차] 0826 [제60회 전국기능경기대회] 전시 작품 제작 프로젝트, "스마트홈" 완료 보고서
  • [LMS7 16/26주차] 0826 [제60회 전국기능경기대회] 전시 작품 제작 프로젝트, "스마트홈" 개발 계획서
  • [LMS7 14/28주차] 0812 Qt 개인 프로젝트, "라이어게임" 개발 계획서
m_Dev
m_Dev
  • m_Dev
    m_Dev
    m_Dev
  • 전체
    오늘
    어제
    • 분류 전체보기
      • MAIN STUDY
        • 정보보안
        • 빅데이터
        • 정보처리
        • 컴퓨터 구조
        • 기타
      • JOB
        • Study
        • Project
      • LMS 7
        • 개발일지
      • FRAMEWORK
        • Qt
        • MFC
        • Winform
        • WPF
        • MAUI
      • NETWORK
        • Study
        • Assignment
      • PYTHON
        • Set
        • Study
        • Assignment
        • Project
      • C
        • Set
        • Study
        • Assignment
        • Project
      • C++
        • Set
        • Study
        • Assignment
        • Project
      • C#
        • Set
        • Study
        • Assignment
        • Project
      • DATABASE
        • MySQL
        • SQLite
      • IDE
        • VisualStudioCode
        • VisualStudio
        • Pycharm
        • Colab
      • 기타
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
m_Dev
[LMS7 14/28주차] 0812 Qt 개인 프로젝트, 라이어 게임 완료 보고서
상단으로

티스토리툴바