[c++]채팅 프로그램 만들기 - 개발편

정제철·2023년 5월 19일
1

[포스코x코딩온]

목록 보기
10/16
post-thumbnail

c++, Socket, TCP, SQL을 활용하여 채팅프로그램 만들기

데이터베이스와 소켓 그리고 그간 배운 C++을 활용하여 채팅프로그램을 개발하라는 프로젝트를 수행하게 되었다.
팀프로젝트이고 팀원은 2명이다. 코딩을 조금이나마 잘하는 내가 팀장을 맡아서 기획하고 개발을 주도하기로 하였다.

개발 주안점

개발 하기 전 어떤점에 주안을 두고 더 고심하며 개발할지에 대해 고민했을 때 우리는 개인정보를 보호하고 암호를 숨기는 것이었다. 그랬기 때문에 class를 사용하여 암호화 및 캡슐화를 통해 정보은닉을 하도록 개발하였다. 그리고 기능적으로 많은 것 뿐만아니라 채팅방에 DM기능을 추가하여 완벽한 채팅프로그램으로서의 기능을 다하도록 개발하였다.
그리고 cryptoPP 라이브러리를 활용하여 비밀번호를 암호화하였다. 해시값을 저장하고 단방향 암호화를 하여 복호화하지 못하도록 하였다.

기능 코딩

SERVER

#define MAX_SIZE 1024 //상수 선언
#define MAX_CLIENT 3 //최대 인원 3

struct SOCKET_INFO { //// 연결된 소켓 정보에 대한 틀 생성
    SOCKET sck=0; //ctrl + 클릭, unsigned int pointer형
    string user =""; //user  : 사람의 이름
};

std::vector<SOCKET_INFO> sck_list; // 연결된 클라이언트 소켓들을 저장할 배열 선언.
SOCKET_INFO server_sock; // 서버 소켓에 대한 정보를 저장할 변수 선언.
int client_count = 0; // 현재 접속해 있는 클라이언트를 count 할 변수 선언.
  • 상수는 메시지의 최대길이이고, 최대인원은 3명까지 접속가능하다. 3이 아닌 10으로 해도 무방하지만 편의상 3명으로 하였다.

server_init()

void server_init() //서버측 소켓 활성화 ,
{
    server_sock.sck = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    SOCKADDR_IN server_addr = {};
    // 소켓 주소 설정 변수
    server_addr.sin_family = AF_INET;
    // 소켓은 Internet 타입 
    server_addr.sin_port = htons(7777); //123.0.0.1:7777 중에 ------:7777을 정의
    // 서버 포트 설정
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //123.0.0.1:----을 정의한다.
    // Any인 경우는 호스트를 127.0.0.1로 잡아도 되고 localhost로 잡아도 되고 양쪽 다 허용하게 할 수 있다. 그것이 INADDR_ANY이다.

    bind(server_sock.sck, (sockaddr*)&server_addr, sizeof(server_addr));
    // 설정된 소켓 정보를 소켓에 바인딩한다.

    listen(server_sock.sck, SOMAXCONN);
     // 소켓을 대기 상태로 기다린다.

    server_sock.user = "server";
}
  • 서버용 소켓을 만드는 함수이다.
    socket 초기화 함수. socket(), bind(), listen() 함수 실행됨. 자세한 내용은 함수 구현부에서 확인.

add_client()

void add_client() {
    SOCKADDR_IN addr = {};
    int addrsize = sizeof(addr);
    char buf[MAX_SIZE] = { };

    ZeroMemory(&addr, addrsize);
    // addr의 메모리 영역을 0으로 초기화

    SOCKET_INFO new_client = {};

    new_client.sck = accept(server_sock.sck, (sockaddr*)&addr, &addrsize);
    recv(new_client.sck, buf, MAX_SIZE, 0);
    // Winsock2의 recv 함수. client가 보낸 닉네임을 받음.
    new_client.user = string(buf);

    string msg = "▶" + new_client.user + " 님이 입장했습니다.";
    cout << msg << endl;
    sck_list.push_back(new_client);
    // client 정보를 담는 sck_list 배열에 새로운 client 추가
    print_clients();

    std::thread th(recv_msg, client_count);
    // 다른 사람들로부터 오는 메시지를 계속해서 받을 수 있는 상태로 만들어 두기
    th.detach();
    client_count++;

    cout << "▷현재 접속자 수 : " << client_count << "명" << endl;
    send_msg(msg.c_str());
     // c_str : string 타입을 const chqr* 타입으로 바꿔줌.
}

  • 소켓에 연결을 시도하는 client를 추가(accept)하는 함수. client accept() 함수 실행됨.
    accept 함수 실행되고 있을 예정으로 accept가 실행될 경우 소켓을 만들고 주소를 할당하게 된다.
    접속했을 때 입장메시지가 출력되고, 현재 몇명이 입장해있는지 접속자수가 출력된다.

send_msg()

void send_msg(const char* msg)
{
    for (int i = 0; i < client_count; i++)
    {
        send(sck_list[i].sck, msg, MAX_SIZE, 0);
        // 접속해 있는 모든 client에게 메시지 전송
    }
}
  • send() 함수 실행됨.
    서버에 접속해있는 client 모두에게 입력받은 메시지를 출력하게된다.
void send_msg_notMe(const char* msg, int sender_idx)
{
    for (int i = 0; i < client_count; i++) {
        if (i != sender_idx) {
            send(sck_list[i].sck, msg, MAX_SIZE, 0);
        }
    }
}
  • 내가 보낸 메시지는 나에게는 출력되지 않고 상대에게만 출력되도록 하였다.
    서버에서 보내는 공지메시지는 모두에게 출력되도록 send에서 사용하지만 클라이언트끼리 대화에서 내가 보낸 메시지에는 notME로 출력되도록 했다.

sendWhisper()

void sendWhisper(int position, string sbuf, int idx)
{
    int cur_position = position + 1;
    position = sbuf.find(" ", cur_position);
    int len = position - cur_position;
    string receiver = sbuf.substr(cur_position, len);
    cur_position = position + 1;
    string dm = sbuf.substr(cur_position);
    string msg = "※귓속말 도착 [" + sck_list[idx].user + "] : " + dm;
    for (int i = 0; i < client_count; i++)
    {
        if (receiver.compare(sck_list[i].user) == 0)
            send(sck_list[i].sck, msg.c_str(), MAX_SIZE, 0);
    }
}

  • 귓속말모드이다.
    클라이언트가 "/귓말 사용자이름 내용"을 입력했을때 공백을 기준으로 잘라서 사용자이름, 내용을 구분 할 수 있도록 만들었다.
    함수의 인자로 현재 위치(position), 전체 메시지(sbuf), 그리고 클라이언트의 인덱스(idx)를 받는다.
    cur_position는 position에서 1을 더한 값으로 다음단어의 시작위치를 나타내고 sbuf에서 공백을 찾아 각 위치에 저장한다.
    수신자 이름은 cur_position부터 len까지의 부분 문자열이다.
    귓속말 메시지는 sbuf 문자열에서 cur_position부터 끝까지의 부분 문자열이다.
    귓속말 메시지를 보낼 메시지 형식으로 변환하여 msg 변수에 저장하고 이를 해당 클라이언트에게 전송한다.

recv_msg()

void recv_msg(int idx) 
{
    char buf[MAX_SIZE] = { };
    string msg = "";

    while (1) {
        ZeroMemory(&buf, MAX_SIZE);

        if (recv(sck_list[idx].sck, buf, MAX_SIZE, 0) > 0) {
         // 오류가 발생하지 않으면 recv는 수신된 바이트 수를 반환. 0보다 크다는 것은 메시지가 왔다는 것.
            string whisper(buf);
            int position = whisper.find(" ", 0);
            int message = position - 0;
            string flag = whisper.substr(0, message);
            if(string(buf) == "/종료")
            {
                msg = "▶" + sck_list[idx].user + " 님이 퇴장했습니다.";
                cout << msg << endl;
                send_msg(msg.c_str());
                del_client(idx);
                return;
            }
            else if (flag.compare("/귓말") == 0)
            {
                sendWhisper(position, whisper, idx);
            }
            else {

                pstmt = con->prepareStatement("INSERT INTO chatting(chatname, time, recv) VALUE(?, NOW(),  ?)");
                pstmt->setString(1, sck_list[idx].user);
                pstmt->setString(2, whisper);
                pstmt->execute();

                pstmt = con->prepareStatement("SELECT chatname, time, recv FROM chatting ORDER BY time DESC LIMIT 1");
                result = pstmt->executeQuery();
                if (result->next())
                {
                    string chatname = result->getString(1);
                    string time = result->getString(2);
                    string recv = result->getString(3);
                    msg += "--------------------------------------------------";
                    msg += "\n▷보낸사람: " + chatname + "  " + "▷보낸시간: " + time + "\n";
                    msg += "▷내용 : " + recv + "\n";
                    msg += "--------------------------------------------------\n";
                    cout << msg << endl;
                    send_msg_notMe(msg.c_str(), idx);
                }
            }
        }
        else { //그렇지 않을 경우 퇴장에 대한 신호로 생각하여 퇴장 메시지 전송
            msg = "[공지] " + sck_list[idx].user + " 님이 퇴장했습니다.";
            cout << msg << endl;
            send_msg(msg.c_str());
            del_client(idx); // 클라이언트 삭제
            return;
        }
    }
} 
  • recv() 함수 실행됨.
    코드가 복잡하지만 짧게 설명하자면 클라이언트가 "/종료"라고 입력할 경우 채팅방을 빠져나오게된다. 그럼 소켓은 종료되고 할당받은 주소는 삭제되도록한다. 그리고 채팅방 접속자에게 해당 사용자가 퇴장했다고 공지하게 된다.
  • "/귓말 사용자이름 내용"을 입력할 경우 해당사용자에게 귓속말로 내용을 전송하고, 서버 출력부에는 출력되지않고 해당사용자에게만 출력된다. 그리고 내용은 데이터베이스에 저장되지 않는다.
  • 일반적인 대화내용은 서버측 출력부와 클라이언트측 출력부에 모두 출력되고, 해당 채팅 내용은 모두 데이터베이스에 등록된다.

del_client()

void del_client(int idx) {
    std::thread th(add_client);
    closesocket(sck_list[idx].sck);
    client_count--;
    cout << "▷현재 접속자 수 : " << client_count << "명" << endl;
    sck_list.erase(sck_list.begin() + idx);
    th.join();
}

  • "/종료" 입력시 소켓을 삭제하고 할당했던 주소를 해제하게된다. 그리고 공지로 남은 접속인원을 알리게된다.

main()

int main()
{
    system("color 06");
    startSql();
    mainMenu();
    WSADATA wsa; 

    // Winsock를 초기화하는 함수. MAKEWORD(2, 2)는 Winsock의 2.2 버전을 사용하겠다는 의미.
    // 실행에 성공하면 0을, 실패하면 그 이외의 값을 반환.
    // 0을 반환했다는 것은 Winsock을 사용할 준비가 되었다는 의미.
    
    int code = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (!code)
    {
        server_init(); //서버 연결

        std::thread th1[MAX_CLIENT];

        for(int i = 0; i < MAX_CLIENT; i++)
        {
        // 인원 수 만큼 thread 생성해서 각각의 클라이언트가 동시에 소통할 수 있도록 함.
            th1[i] = std::thread(add_client);
        }

        while (1)
        { // 무한 반복문을 사용하여 서버가 계속해서 채팅 보낼 수 있는 상태를 만들어 줌. 반복문을 사용하지 않으면 한 번만 보낼 수 있음.
            string text, msg = "";

            std::getline(cin, text);
            const char* buf = text.c_str();
            msg = server_sock.user + " : " + buf;
            send_msg(msg.c_str());
        }

        for (int i = 0; i < MAX_CLIENT; i++)
        {
            th1[i].join();
            //thread 작업이 끝날 때까지 main 함수가 끝나지 않도록 해줌.
        }

        closesocket(server_sock.sck);
    }
    else cout << "프로그램 종료. (Error code : " << code << ")";

    WSACleanup();

    delete result;
    delete pstmt;
    delete con;
    delete stmt;

    return 0;
}

CLIENT

Class를 이용한 캡슐화 & 정보은닉


먼저 SQL의 데이터베이스의 정보들은 개개인의 아이디와 비밀번호, 생년월일, 전화번호, 이름 등의 개인정보를 담고 있기에 class를 사용하여 캡슐화 및 정보은닉을 사용하였다.

비밀번호 "*"표시 (_getch)

각 코드를 모두 설명하고 싶지만 기본적인 코드와 SQL연계이기 때문에 생략하고 주안점으로 생각했던 비밀번호 설정코드를 살펴보자

 while (1)
                {
                    string new_pw = "";
                    cout << ">>새로운 비밀번호를 입력해주세요 : ";
                    char ch = ' ';
                    while (ch != 13) { // Enter 키를 누르면 입력 종료
                        ch = _getch();
                        if (ch == 13) break; // Enter 키를 누르면 입력 종료
                        if (ch == 8) { // Backspace 키인 경우
                            if (!new_pw.empty()) { // 입력된 문자가 있으면
                                new_pw.pop_back(); // 마지막 문자를 삭제
                                cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                            }
                        }
                        else {
                            new_pw.push_back(ch);
                            cout << '*'; // 별표로 대체하여 출력
                        }
                    }
                    cout << endl;

                    string renew_pw = "";
                    cout << ">>다시 한번 입력해주세요. : ";
                    char rech = ' ';
                    while (rech != 13) { // Enter 키를 누르면 입력 종료
                        rech = _getch();
                        if (rech == 13) break; // Enter 키를 누르면 입력 종료
                        if (rech == 8) { // Backspace 키인 경우
                            if (!renew_pw.empty()) { // 입력된 문자가 있으면
                                renew_pw.pop_back(); // 마지막 문자를 삭제
                                cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                            }
                        }
                        else {
                            renew_pw.push_back(rech);
                            cout << '*'; // 별표로 대체하여 출력
                        }
                    }
                    cout << endl;

                    if (new_pw == renew_pw)
                    {
                        pstmt = con->prepareStatement("UPDATE user SET pw = ? WHERE id = ?");
                        pstmt->setString(1, new_pw);
                        pstmt->setString(2, id);
                        pstmt->executeQuery();
                        printf("▶새로운 비밀번호로 변경되었습니다.\n");
                        Sleep(500);
                        break;
                    }
                    else {
                        cout << "▶비밀번호가 다릅니다." << endl;
                        Sleep(500);
                    }
                }

  • 비밀번호를 입력할 때 개인정보 보호를 위해 비밀번호는 *로 표시되도록했다.
    _getch()함수를 사용하여 동적으로 출력되도록 하였고,
    while (ch != 13) { // Enter 키를 누르면 입력 종료되도록하고
    if (ch == 8) { // Backspace 키인 경우 가장마지막 문자를 삭제하고 커서를 하나 앞으로 이동시켜서 backspace의 역할을 수행하도록 조건을 달아주었다.

소켓 코드

#define MAX_SIZE 1024

SOCKET client_sock;
string my_nick;
int chat_recv() {
    char buf[MAX_SIZE] = { };
    string msg;

    while (1) {
        ZeroMemory(&buf, MAX_SIZE);
        if (recv(client_sock, buf, MAX_SIZE, 0) > 0) {
            textColor(7, 0);
            msg = buf;
            std::stringstream ss(msg);  // 문자열을 스트림화
            string user;
            ss >> user; 
            if (user != my_nick) cout << buf << endl;
        }
        else {
            cout << "Server Off" << endl;
            return -1;
        }
    }
}
  • 이름과 메시지를 msg에 담아 서버로 보낸다.
 WSADATA wsa;
                    code = WSAStartup(MAKEWORD(2, 2), &wsa); // 변수에 초기화 진행
                    if (!code)
                    {
                        cout << "< 채팅방에 입장합니다. >" << endl;
                        my_nick = sql.getName();
                        closesocket(client_sock);
                        client_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

                        SOCKADDR_IN client_addr = {};
                        client_addr.sin_family = AF_INET;
                        client_addr.sin_port = htons(7777);
                        InetPton(AF_INET, TEXT("127.0.0.1"), &client_addr.sin_addr);

                        while (1)
                        {
                            if (!connect(client_sock, (SOCKADDR*)&client_addr, sizeof(client_addr))) {
                                cout << "※지인을 사칭하여 금전을 요구할 수 있으니, 도용이 의심 된다면 대화를 중단해주시기 바랍니다." << endl;
                                send(client_sock, my_nick.c_str(), my_nick.length(), 0);
                                break;
                            }
                            cout << "Connecting..." << endl;
                        }
                        std::thread th2(chat_recv);
                        while (1)
                        {
                            string text;
                            std::getline(cin, text);

                            const char* buffer = text.c_str(); // string형을 char* 타입으로 변환
                            send(client_sock, buffer, strlen(buffer), 0);
                            if (text == "/종료")
                            {
                                closesocket(client_sock);
                                backButton = true;
                                break;
                            }
                        }
                        th2.join();
                    }
                    WSACleanup();

접속시 소켓주소를 할당받고 메시지를 입력하여 서버로 전송한다.

  • 각 소켓 내용별 관련 설명은 주석에 설명해놨다.

TCP 통신의 Socket흐름을 알고 본다면 조금더 이해하기 쉬울것이다.

전체 코드 - Server

#pragma comment(lib, "ws2_32.lib") 

#include <WinSock2.h>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <string>
#include <mysql/jdbc.h>
#include <sstream>

using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::vector;

#define MAX_SIZE 1024 //상수 선언
#define MAX_CLIENT 3 //최대 인원 3

const string server = "tcp://127.0.0.1:3306"; // 데이터베이스 주소
const string username = "root"; // 데이터베이스 사용자
const string password = "07wd2713"; // 데이터베이스 접속 비밀번호


struct SOCKET_INFO { //구조체 정의
    SOCKET sck=0; //ctrl + 클릭, unsigned int pointer형
    string user =""; //user  : 사람의 이름
};

vector<SOCKET_INFO> sck_list; //서버에 연결된 client를 저장할 변수.
SOCKET_INFO server_sock; //서브소켓의 정보를 저장할 정보.
int client_count = 0; //현재 접속된 클라이언트 수 카운트 용도.

//SQL
sql::mysql::MySQL_Driver* driver; // 추후 해제하지 않아도 Connector/C++가 자동으로 해제해 줌 
sql::Connection* con;
sql::PreparedStatement* pstmt;
sql::ResultSet* result;
sql::Statement* stmt;
void startSql(); //SQL실행 - creatTable;

//시스템구동
void mainMenu();

//채팅
void server_init(); //서버용 소켓을 만드는 함수
void add_client(); //accept 함수 실행되고 있을 예정
void send_msg(const char* msg); //send() 실행
void send_msg_notMe(const char* msg, int sender_idx);
void sendWhisper(int position, string sbuf, int idx);
void recv_msg(int idx); //recv() 실행
void del_client(int idx); //클라이언트와의 연결을 끊을 때
void print_clients();


int main()
{
    system("color 06");
    startSql();
    mainMenu();
    WSADATA wsa;

    int code = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (!code)
    {
        server_init(); //서버 연결

        std::thread th1[MAX_CLIENT];

        for(int i = 0; i < MAX_CLIENT; i++)
        {
            th1[i] = std::thread(add_client);
        }

        while (1)
        {
            string text, msg = "";

            std::getline(cin, text);
            const char* buf = text.c_str();
            msg = server_sock.user + " : " + buf;
            send_msg(msg.c_str());
        }

        for (int i = 0; i < MAX_CLIENT; i++)
        {
            th1[i].join();
        }

        closesocket(server_sock.sck);
    }
    else cout << "프로그램 종료. (Error code : " << code << ")";

    WSACleanup();

    delete result;
    delete pstmt;
    delete con;
    delete stmt;

    return 0;
}
void mainMenu(){
    cout << "\n";
    cout << " "; cout << "*************************************************\n";      
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                 < SERVER ON >                 *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void startSql()
{
    // MySQL Connector/C++ 초기화

    try {
        driver = sql::mysql::get_mysql_driver_instance();
        con = driver->connect(server, username, password);
    }
    catch (sql::SQLException& e) {
        cout << "Could not connect to server. Error message: " << e.what() << endl;
        exit(1);
    }

    // 데이터베이스 선택                                                                
    con->setSchema("project1");

    // DB 한글 저장을 위한 셋팅                                                             
    stmt = con->createStatement();
    stmt->execute("set names euckr");
    if (stmt) { delete stmt; stmt = nullptr; }
}
void server_init() //서버측 소켓 활성화 , //서버용 소켓을 만드는 함수
{
    server_sock.sck = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    SOCKADDR_IN server_addr = {};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(7777); //123.0.0.1:7777 중에 ------:7777을 정의
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //123.0.0.1:----을 정의한다.

    bind(server_sock.sck, (sockaddr*)&server_addr, sizeof(server_addr));

    listen(server_sock.sck, SOMAXCONN);

    server_sock.user = "server";
}
void add_client() {
    SOCKADDR_IN addr = {};
    int addrsize = sizeof(addr);
    char buf[MAX_SIZE] = { };

    ZeroMemory(&addr, addrsize);

    SOCKET_INFO new_client = {};

    new_client.sck = accept(server_sock.sck, (sockaddr*)&addr, &addrsize);
    recv(new_client.sck, buf, MAX_SIZE, 0);
    new_client.user = string(buf);

    string msg = "▶" + new_client.user + " 님이 입장했습니다.";
    cout << msg << endl;
    sck_list.push_back(new_client);
    print_clients();

    std::thread th(recv_msg, client_count);
    th.detach();
    client_count++;

    cout << "▷현재 접속자 수 : " << client_count << "명" << endl;
    send_msg(msg.c_str());
}
void send_msg(const char* msg)
{
    for (int i = 0; i < client_count; i++)
    {
        send(sck_list[i].sck, msg, MAX_SIZE, 0);
    }
}
void send_msg_notMe(const char* msg, int sender_idx)
{
    for (int i = 0; i < client_count; i++) {
        if (i != sender_idx) {
            send(sck_list[i].sck, msg, MAX_SIZE, 0);
        }
    }
}
void sendWhisper(int position, string sbuf, int idx)
{
    int cur_position = position + 1;
    position = sbuf.find(" ", cur_position);
    int len = position - cur_position;
    string receiver = sbuf.substr(cur_position, len);
    cur_position = position + 1;
    string dm = sbuf.substr(cur_position);
    string msg = "※귓속말 도착 [" + sck_list[idx].user + "] : " + dm;
    for (int i = 0; i < client_count; i++)
    {
        if (receiver.compare(sck_list[i].user) == 0)
            send(sck_list[i].sck, msg.c_str(), MAX_SIZE, 0);
    }
}
void recv_msg(int idx) {
    char buf[MAX_SIZE] = { };
    string msg = "";

    while (1) {
        ZeroMemory(&buf, MAX_SIZE);

        if (recv(sck_list[idx].sck, buf, MAX_SIZE, 0) > 0) {
            string whisper(buf);
            int position = whisper.find(" ", 0);
            int message = position - 0;
            string flag = whisper.substr(0, message);
            if(string(buf) == "/종료")
            {
                msg = "▶" + sck_list[idx].user + " 님이 퇴장했습니다.";
                cout << msg << endl;
                send_msg(msg.c_str());
                del_client(idx);
                return;
            }
            else if (flag.compare("/귓말") == 0)
            {
                sendWhisper(position, whisper, idx);
            }
            else {

                pstmt = con->prepareStatement("INSERT INTO chatting(chatname, time, recv) VALUE(?, NOW(),  ?)");
                pstmt->setString(1, sck_list[idx].user);
                pstmt->setString(2, whisper);
                pstmt->execute();

                pstmt = con->prepareStatement("SELECT chatname, time, recv FROM chatting ORDER BY time DESC LIMIT 1");
                result = pstmt->executeQuery();
                if (result->next())
                {
                    string chatname = result->getString(1);
                    string time = result->getString(2);
                    string recv = result->getString(3);
                    msg += "--------------------------------------------------";
                    msg += "\n▷보낸사람: " + chatname + "  " + "▷보낸시간: " + time + "\n";
                    msg += "▷내용 : " + recv + "\n";
                    msg += "--------------------------------------------------\n";
                    cout << msg << endl;
                    send_msg_notMe(msg.c_str(), idx);
                }
            }
        }
        else { //그렇지 않을 경우 퇴장에 대한 신호로 생각하여 퇴장 메시지 전송
            msg = "[공지] " + sck_list[idx].user + " 님이 퇴장했습니다.";
            cout << msg << endl;
            send_msg(msg.c_str());
            del_client(idx); // 클라이언트 삭제
            return;
        }
    }
} 
void del_client(int idx) {
    std::thread th(add_client);
    closesocket(sck_list[idx].sck);
    client_count--;
    cout << "▷현재 접속자 수 : " << client_count << "명" << endl;
    sck_list.erase(sck_list.begin() + idx);
    th.join();
}
void print_clients() {
    cout << "▷현재 접속 : ";
    for (auto& client : sck_list) {
        cout << client.user << " ";
    }
    cout << endl;
}

전체 코드 - client

#pragma comment(lib, "ws2_32.lib")

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string>
#include <sstream>
#include <iostream>
#include <thread>
#include <mysql/jdbc.h>
#include <conio.h> 
#include <Windows.h>

using std::cout;
using std::cin; 
using std::endl;
using std::string;

#define MAX_SIZE 1024

const string server = "tcp://127.0.0.1:3306";
const string username = "root";
const string password = "07wd2713";

sql::mysql::MySQL_Driver* driver;
sql::Connection* con;
sql::PreparedStatement* pstmt;
sql::ResultSet* result;
sql::Statement* stmt;

SOCKET client_sock;
string my_nick;

void startMenu()
{
    system("cls");
    cout << "\n";    
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 시작 화면 >                   *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               1.로그인                        *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               2. ID 찾기                      *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               3. PW 찾기                      *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               4. 회원가입                     *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               0. 종료                         *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void login()
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 로그인 화면 >                 *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              >> 아이디 입력                   *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              >> 비밀번호 입력                 *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              >> 완료시 로그인!                *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              >> 실패시 메인화면               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void searchId() 
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 아이디 찾기 >                 *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 이름 입력                    *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 번호 입력                    *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 생년월일(8자리) 입력         *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 완료시 아이디 출력           *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 실패시 메인화면              *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void searchPw()
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 비밀번호 찾기 >               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 아이디 입력                  *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 이름 입력                    *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 전화번호 입력                *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 생년월일(8자리) 입력         *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 완료시 메인화면              *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void createUser()
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               <  회원 가입  >                 *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 아이디 입력                  *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 비밀번호 입력                *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 이름, 전화번호 입력          *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 생년월일(8자리) 입력         *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 완료시 메인화면              *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void mainMenu()
{
    Sleep(500);
    system("color 06");
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              < 현재 상태 : 접속 중 >          *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                1. 내 정보                     *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                2. 친구                        *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                3. 대화방                      *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                4. 설정                        *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                0. 종료                        *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void myMenu()
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 내 정보 보기 >                *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              1. 내 프로필                     *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              2. 상메 설정/수정                *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              3. BGM 설정/수정                 *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*              0. 뒤로가기                      *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void friends()
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 내 친구 정보 >                *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               1. 친구 목록 보기               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               2. 친구 생일 검색               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> 월일 ~ 월일 검색             *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               0. 뒤로가기                     *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void chatting()
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 채팅 입장/검색 >              *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               1. 채팅방 입장                  *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> /귓말 이름 내용입력          *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               >> /종료 입력시 퇴장            *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               2. 채팅 내용 검색               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               3. 채팅 기간 검색               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               0. 뒤로가기                     *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void setting()
{
    system("cls");
    cout << "\n";
    cout << " "; cout << "*************************************************\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*       *******      *       *       *  *       *\n";
    cout << " "; cout << "*          *        * *      *       * *        *\n";
    cout << " "; cout << "*          *       *****     *       **         *\n";
    cout << " "; cout << "*          *      *     *    *       * *        *\n";
    cout << " "; cout << "*          *     *       *   *****   *  *       *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               < 내 설정 >                     *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               1. 비밀번호 변경                *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               2. 회원 탈퇴                    *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*               0. 뒤로가기                     *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*                                               *\n";
    cout << " "; cout << "*************************************************\n\n";
}
void textColor(int foreground, int background)
{
    int color = foreground + background * 16;
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
int chat_recv() {
    char buf[MAX_SIZE] = { };
    string msg;

    while (1) {
        ZeroMemory(&buf, MAX_SIZE);
        if (recv(client_sock, buf, MAX_SIZE, 0) > 0) {
            textColor(7, 0);
            msg = buf;
            //닉네임 : 메시지
            std::stringstream ss(msg);  // 문자열을 스트림화
            string user;
            ss >> user; 
            if (user != my_nick) cout << buf << endl;
        }
        else {
            cout << "Server Off" << endl;
            return -1;
        }
    }
}


class SQL
{
private:
    string id, pw, name, phone, status, birth, song;
public:
    SQL()
    {
        try {
            driver = sql::mysql::get_mysql_driver_instance();
            con = driver->connect(server, username, password);
        }
        catch (sql::SQLException& e) {
            cout << "Could not connect to server. Error message: " << e.what() << endl;
            exit(1);
        }

        con->setSchema("project1");

        // DB 한글 저장을 위한 셋팅
        stmt = con->createStatement();
        stmt->execute("set names euckr");
        if (stmt) { delete stmt; stmt = nullptr; }
    }
    int login()
    {
        cout << ">>아이디 : ";
        cin >> id;
        this->id = id;
        cout << ">>비밀번호를 입력해주세요 : ";
        char ch = ' ';
        while (ch != 13) { // Enter 키를 누르면 입력 종료
            ch = _getch();
            if (ch == 13) break; // Enter 키를 누르면 입력 종료
            else if (ch == 8) { // Backspace 키인 경우
                if (!pw.empty()) { // 입력된 문자가 있으면
                    pw.pop_back(); // 마지막 문자를 삭제
                    cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤,
                                     // 다시 커서 위치를 원래대로 이동시킴
                }
            }
            else {
                pw.push_back(ch);
                cout << '*'; // 별표로 대체하여 출력
            }
        }
        cout << endl;
        pstmt = con->prepareStatement("SELECT id, pw, name FROM user \
            WHERE id=?");
        pstmt->setString(1, id);
        result = pstmt->executeQuery();

        if (result->next())
        { // 쿼리 결과가 있다면
            string db_id = result->getString(1); // 데이터베이스에서 가져온 ID
            string db_pw = result->getString(2); // 데이터베이스에서 가져온 PW

            if (db_id == id && db_pw == pw)
            {
                name = result->getString(3);
                this->name = name;
                cout << endl << "▶로그인 중입니다. 잠시만 기다려주세요." << endl << endl;

                cout << "▶" << name << "님 환영합니다." << endl;
                return 1;
            }
            else cout << "▶해당하는 정보가 없습니다." << endl;
        }
        else cout << "▶해당하는 정보가 없습니다." << endl;
        pw.clear();
    }
    void searchId()
    {
        cout << ">>이름 : ";
        cin >> name;
        cout << ">>전화번호 : ";
        cin >> phone;
        while (true)
        {
            cout << ">>생년월일 : ";
            cin >> birth;
            if (birth.length() != 8) {
                cout << "▶생년월일은 8자리로 입력해주세요." << endl;
                continue;
            }
            break;
        }

        string year = birth.substr(0, 4);
        string month = birth.substr(4, 2);
        string day = birth.substr(6, 2);
        string DATE = year + "-" + month + "-" + day;

        pstmt = con->prepareStatement("SELECT id, name, phone, birth FROM user WHERE phone=?");
        pstmt->setString(1, phone);
        result = pstmt->executeQuery();

        if (result->next())
        {
            string db_id = result->getString(1);
            string db_name = result->getString(2);
            string db_phone = result->getString(3);
            string db_birth = result->getString(4);

            if (db_name == name && db_phone == phone && db_birth == DATE)
            {
                cout << "▶" << name << "님의 아이디는 " << db_id << "입니다." << endl;
                Sleep(3000);
            }
            else
            {
                cout << "▶해당하는 정보가 없습니다.!" << endl;
                Sleep(500);
            }
        }
        else {
            cout << "▶해당하는 정보가 없습니다.?" << endl;
            Sleep(500);
        }
    }
    void searchPw()
    {
        cout << ">>아이디 : ";
        cin >> id;
        cout << ">>이름 : ";
        cin >> name;
        cout << ">>전화번호 : ";
        cin >> phone;
        while (true)
        {
            cout << ">>생년월일 : ";
            cin >> birth;
            if (birth.length() != 8) {
                cout << "▶생년월일은 8자리로 입력해주세요." << endl;
                continue;
            }
            break;
        }

        string year = birth.substr(0, 4);
        string month = birth.substr(4, 2);
        string day = birth.substr(6, 2);
        string DATE = year + "-" + month + "-" + day;

        pstmt = con->prepareStatement("SELECT id, name, phone, birth FROM user WHERE id=?");
        pstmt->setString(1, id);
        result = pstmt->executeQuery();

        if (result->next())
        {
            string db_id = result->getString(1);
            string db_name = result->getString(2);
            string db_phone = result->getString(3);
            string db_birth = result->getString(4);

            if (db_id == id && db_name == name && db_phone == phone && db_birth == DATE)
            {
                while (1)
                {
                    string new_pw = "";
                    cout << ">>새로운 비밀번호를 입력해주세요 : ";
                    char ch = ' ';
                    while (ch != 13) { // Enter 키를 누르면 입력 종료
                        ch = _getch();
                        if (ch == 13) break; // Enter 키를 누르면 입력 종료
                        if (ch == 8) { // Backspace 키인 경우
                            if (!new_pw.empty()) { // 입력된 문자가 있으면
                                new_pw.pop_back(); // 마지막 문자를 삭제
                                cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                            }
                        }
                        else {
                            new_pw.push_back(ch);
                            cout << '*'; // 별표로 대체하여 출력
                        }
                    }
                    cout << endl;

                    string renew_pw = "";
                    cout << ">>다시 한번 입력해주세요. : ";
                    char rech = ' ';
                    while (rech != 13) { // Enter 키를 누르면 입력 종료
                        rech = _getch();
                        if (rech == 13) break; // Enter 키를 누르면 입력 종료
                        if (rech == 8) { // Backspace 키인 경우
                            if (!renew_pw.empty()) { // 입력된 문자가 있으면
                                renew_pw.pop_back(); // 마지막 문자를 삭제
                                cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                            }
                        }
                        else {
                            renew_pw.push_back(rech);
                            cout << '*'; // 별표로 대체하여 출력
                        }
                    }
                    cout << endl;

                    if (new_pw == renew_pw)
                    {
                        pstmt = con->prepareStatement("UPDATE user SET pw = ? WHERE id = ?");
                        pstmt->setString(1, new_pw);
                        pstmt->setString(2, id);
                        pstmt->executeQuery();
                        printf("▶새로운 비밀번호로 변경되었습니다.\n");
                        Sleep(500);
                        break;
                    }
                    else {
                        cout << "▶비밀번호가 다릅니다." << endl;
                        Sleep(500);
                    }
                }
            }
            else {
                cout << "▶해당하는 정보가 없습니다." << endl;
                Sleep(500);
            }
        }
        else {
            cout << "▶해당하는 정보가 없습니다." << endl;
            Sleep(500);
        }
    }
    void crateUser()
    {
        while (1)
        {
            cout << ">>아이디 : ";
            cin >> id;
            pstmt = con->prepareStatement("SELECT id FROM user WHERE id=?");
            pstmt->setString(1, id);
            result = pstmt->executeQuery();

            if (result->next())
            {
                string db_id = result->getString(1);
                if (db_id == id) {
                    cout << "▶중복된 아이디가 있습니다." << endl;
                    continue;
                }
            }
            else {
                cout << "▶중복체크 완료." << endl;
                break;
            }
        }
        while (1)
        {
            cout << ">>비밀번호를 입력해주세요 : ";
            char ch = ' ';
            while (ch != 13) { // Enter 키를 누르면 입력 종료
                ch = _getch();
                if (ch == 13) break; // Enter 키를 누르면 입력 종료
                if (ch == 8) { // Backspace 키인 경우
                    if (!pw.empty()) { // 입력된 문자가 있으면
                        pw.pop_back(); // 마지막 문자를 삭제
                        cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                    }
                }
                else {
                    pw.push_back(ch);
                    cout << '*'; // 별표로 대체하여 출력
                }
            }
            cout << endl;
            string new_pw = "";
            cout << endl << ">>다시 한번 입력해주세요. : ";
            char rech = ' ';
            while (rech != 13) { // Enter 키를 누르면 입력 종료
                rech = _getch();
                if (rech == 13) break; // Enter 키를 누르면 입력 종료
                else if (rech == 8) { // Backspace 키인 경우
                    if (!new_pw.empty()) { // 입력된 문자가 있으면
                        new_pw.pop_back(); // 마지막 문자를 삭제
                        cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                    }
                }
                else {
                    new_pw.push_back(rech);
                    cout << '*'; // 별표로 대체하여 출력
                }

            }
            cout << endl;

            if (pw == new_pw)
            {
                break;
            }
            else
            {
                cout << "▶비밀번호가 다릅니다." << endl;
                pw.clear();
            }
        }

        cout << ">>이름 : ";
        cin >> name;
        cout << ">>전화번호 : ";
        cin >> phone;
        while (true)
        {
            cout << ">>생년월일 : ";
            cin >> birth;
            if (birth.length() != 8) {
                cout << "▶생년월일은 8자리로 입력해주세요." << endl;
                continue;
            }
            break;
        }

        string year = birth.substr(0, 4);
        string month = birth.substr(4, 2);
        string day = birth.substr(6, 2);
        string DATE = year + "-" + month + "-" + day;

        pstmt = con->prepareStatement("INSERT INTO user(id,pw, name, phone, birth) VALUE(?,?, ?, ?, ?)");
        pstmt->setString(1, id);
        pstmt->setString(2, pw);
        pstmt->setString(3, name);
        pstmt->setString(4, phone);
        pstmt->setString(5, DATE);
        pstmt->execute();
        cout << "▶회원가입이 완료되었습니다." << endl;
        pw.clear();
        Sleep(500);
    }
    void myProfile()
    {
        pstmt = con->prepareStatement("SELECT name, status, song, birth, phone FROM user WHERE id = ?;");
        pstmt->setString(1, id);
        result = pstmt->executeQuery();

        while (result->next())
        {
            cout << "-----------------------------------------" << endl;
            cout << "▷이름 : " << result->getString("name") << endl;
            if (result->getString("status") == "")
            {
                cout << "▷상메 : 없음" << endl;
            }
            else {
                cout << "▷상메 : " << result->getString("status") << endl;
            }
            if (result->getString("song") == "")
            {
                cout << "▷노래 : 없음" << endl;
            }
            else {
                cout << "▷노래 : " << result->getString("song") << endl;
            }
            cout << "▷생일 : " << result->getString("birth") << endl;
            cout << "▷번호 : " << result->getString("phone") << endl;
            cout << "-----------------------------------------" << endl;
        }
    }
    void updateStatus()
    {
        cout << ">>상태메시지 입력 : ";
        cin.ignore();
        getline(cin, status);
        pstmt = con->prepareStatement("UPDATE user SET status = ? WHERE id = ?");
        pstmt->setString(1, status);
        pstmt->setString(2, id);
        pstmt->executeQuery();
        printf("▶업데이트 되었습니다.\n");
    }
    void updateSong()
    {
        cout << ">>노래 입력 : ";
        cin.ignore();
        getline(cin, song);
        pstmt = con->prepareStatement("UPDATE user SET song = ? WHERE id = ?");
        pstmt->setString(1, song);
        pstmt->setString(2, id);
        pstmt->executeQuery();
        printf("▶업데이트 되었습니다.\n");
    }
    void friends()
    {
        pstmt = con->prepareStatement("SELECT name, status, song, birth, phone FROM user WHERE id != ?;");
        pstmt->setString(1, id);
        result = pstmt->executeQuery();

        while (result->next())
        {
            cout << "-----------------------------" << endl;
            cout << "▷이름 : " << result->getString("name") << endl;
            if (result->getString("status") == "")
            {
                cout << "▷상메 : 없음" << endl;
            }
            else {
                cout << "▷상메 : " << result->getString("status") << endl;
            }
            if (result->getString("song") == "")
            {
                cout << "▷노래 : 없음" << endl;
            }
            else {
                cout << "▷노래 : " << result->getString("song") << endl;
            }
            cout << "▷생일 : " << result->getString("birth") << endl;
            cout << "▷번호 : " << result->getString("phone") << endl;

        }
    }
    void searchBirth()
    {
        string startDay, endDay;
        cout << "시작월일 4자리를 입력해주세요.(ex.0201) : ";
        cin >> startDay;// 0303
        cout << "종료월일 4자리를 입력해주세요.(ex.0405) : ";
        cin >> endDay; //0505
        pstmt = con->prepareStatement("SELECT name, birth, phone FROM user \
        WHERE DATE_FORMAT(birth, '%m%d') BETWEEN ? AND ? \
        AND id != ?;");
        pstmt->setString(1, startDay);
        pstmt->setString(2, endDay);
        pstmt->setString(3, id);
        result = pstmt->executeQuery();

        if (!result->next()) {
            cout << "검색 결과가 없습니다." << endl;
        }
        else {
            while (true) {
                cout << "-----------------------------------------------" << endl;
                cout << "▷이름 : " << result->getString("name") << endl;
                cout << "▷생일 : " << result->getString("birth") << endl;
                cout << "▷번호 : " << result->getString("phone") << endl;
                if (!result->next()) break; // 더이상 결과가 없으면 while문을 빠져나옵니다.

            }
        }
    }
    void search_content_Message()
    {
        string content;
        cout << ">>내용에 따른 메시지 검색 : ";
        cin >> content;
        pstmt = con->prepareStatement("SELECT chatname, time, recv FROM chatting\
                               WHERE recv LIKE ?");
        pstmt->setString(1, "%" + content + "%");
        result = pstmt->executeQuery();

        if (!result->next()) {
            cout << "검색 결과가 없습니다." << endl;
        }
        else {
            while (true) {
                string chatname = result->getString(1);
                string time = result->getString(2);
                string recv = result->getString(3);
                cout << "--------------------------------------------------" << endl;
                cout << "▷보낸사람 : " << chatname << " ▷보낸시간 : " << time << endl;
                cout << "▷" << recv << endl;
                if (!result->next()) break;
            }
        }
    }
    void search_day_Message()
    {
        string startDay, endDay;
        cout << "시작월일 4자리를 입력해주세요.(ex.0201) : ";
        cin >> startDay;// 0303
        cout << "종료월일 4자리를 입력해주세요.(ex.0405) : ";
        cin >> endDay; //0505

        string startMonthDay = "2023" + startDay;
        string endMonthDay = "2023" + endDay;

        pstmt = con->prepareStatement("SELECT chatname, time, recv FROM chatting \
                                   WHERE time BETWEEN ? AND ?");
        pstmt->setString(1, startMonthDay);
        pstmt->setString(2, endMonthDay);
        result = pstmt->executeQuery();

        if (!result->next()) {
            cout << "검색 결과가 없습니다." << endl;
        }
        else {
            while (true) {
                string chatname = result->getString(1);
                string time = result->getString(2);
                string recv = result->getString(3);
                cout << "--------------------------------------------------" << endl;
                cout << "▷보낸사람 : " << chatname << " ▷보낸시간 : " << time << endl;
                cout << "▷" << recv << endl;
                if (!result->next()) break;
            }
        }
    }
    void modifyPw()
    {
        cout << ">>기존 비밀번호를 입력해주세요. : ";
        string existPw = "";
        char ch = ' ';
        while (ch != 13)
        { // Enter 키를 누르면 입력 종료
            ch = _getch();
            if (ch == 13) break; // Enter 키를 누르면 입력 종료
            if (ch == 8) { // Backspace 키인 경우
                if (!existPw.empty()) { // 입력된 문자가 있으면
                    existPw.pop_back(); // 마지막 문자를 삭제
                    cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                }
            }
            else {
                existPw.push_back(ch);
                cout << '*'; // 별표로 대체하여 출력
            }
        }
        cout << endl;

        pstmt = con->prepareStatement("SELECT pw FROM user WHERE id=?");
        pstmt->setString(1, id);
        result = pstmt->executeQuery();
        if (result->next())
        {
            while (1)
            {
                string new_pw = "";
                cout << ">>새로운 비밀번호를 입력해주세요 : ";
                char ch = ' ';
                while (ch != 13) { // Enter 키를 누르면 입력 종료
                    ch = _getch();
                    if (ch == 13) break; // Enter 키를 누르면 입력 종료
                    else if (ch == 8) { // Backspace 키인 경우
                        if (!new_pw.empty()) { // 입력된 문자가 있으면
                            new_pw.pop_back(); // 마지막 문자를 삭제
                            cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                        }
                    }
                    else {
                        new_pw.push_back(ch);
                        cout << '*'; // 별표로 대체하여 출력
                    }
                }
                cout << endl;

                string renew_pw = "";
                cout << endl << ">>다시 한번 입력해주세요. : ";
                char rech = ' ';
                while (rech != 13) { // Enter 키를 누르면 입력 종료
                    rech = _getch();
                    if (rech == 13) break; // Enter 키를 누르면 입력 종료
                    else if (rech == 8) { // Backspace 키인 경우
                        if (!renew_pw.empty()) { // 입력된 문자가 있으면
                            renew_pw.pop_back(); // 마지막 문자를 삭제
                            cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                        }
                    }
                    else {
                        renew_pw.push_back(rech);
                        cout << '*'; // 별표로 대체하여 출력
                    }

                }
                cout << endl;

                if (new_pw == renew_pw)
                {
                    pstmt = con->prepareStatement("UPDATE user SET pw = ? WHERE id = ?");
                    pstmt->setString(1, new_pw);
                    pstmt->setString(2, id);
                    pstmt->executeQuery();
                    printf("▶새로운 비밀번호로 변경되었습니다.\n");
                    break;
                }
                else cout << "▶비밀번호가 다릅니다." << endl;
            }

        }
        else  cout << "▶비밀번호가 다릅니다." << endl;

    }
    int deleteUser()
    {
        cout << ">>기존 비밀번호를 입력해주세요. : ";
        string existPw = "";
        char ch = ' ';
        while (ch != 13) { // Enter 키를 누르면 입력 종료
            ch = _getch();
            if (ch == 13) break; // Enter 키를 누르면 입력 종료
            if (ch == 8) { // Backspace 키인 경우
                if (!existPw.empty()) { // 입력된 문자가 있으면
                    existPw.pop_back(); // 마지막 문자를 삭제
                    cout << "\b \b"; // 커서 위치를 왼쪽으로 이동시켜 공백을 출력한 뒤, 다시 커서 위치를 원래대로 이동시킴
                }
            }
            else {
                existPw.push_back(ch);
                cout << '*'; // 별표로 대체하여 출력
            }
        }
        cout << endl;

        pstmt = con->prepareStatement("SELECT pw FROM user \
            WHERE id=?");
        pstmt->setString(1, id);
        result = pstmt->executeQuery();

        if (result->next())
        { // 쿼리 결과가 있다면
            string db_pw = result->getString(1); // 데이터베이스에서 가져온 PW
            if (db_pw == existPw)
            {
                cout << "정말 삭제하시겠습니까? 삭제한 이후엔 되돌릴 수 없습니다. 1. 계속하기, 2. 그만두기 : ";
                char lastCheck;
                cin >> lastCheck;
                if (lastCheck == '1')
                {
                    pstmt = con->prepareStatement("DELETE FROM user WHERE id = ?");
                    pstmt->setString(1, id);
                    result = pstmt->executeQuery();
                    cout << "▶그동안 감사했습니다. 또 이용해주세요." << endl;
                    return 1;
                }
                else if (lastCheck == '2')
                {
                    cout << "잘 생각하셨습니다.." << endl;
                    return 2;
                }
                else cout << "▶잘못된 입력입니다." << endl;

            }
            else cout << "▶비밀번호가 다릅니다." << endl;
        }
        else cout << "▶비밀번호가 다릅니다." << endl;
    }
    string getName()
    {
        string getName = "";
        pstmt = con->prepareStatement("SELECT name FROM user \
            WHERE id=?");
        pstmt->setString(1, id);
        result = pstmt->executeQuery();
        if (result->next())
        {
            getName = result->getString(1);
        }
        return getName;
    }   
    void beforeChat()
    {
        pstmt = con->prepareStatement("SELECT chatname, time, recv FROM chatting ORDER BY time DESC LIMIT 5");
        result = pstmt->executeQuery();

        string chatname[5];
        string time[5];
        string recv[5];
        string msg[5];
        int num = 0;
        while (result->next())
        {
            chatname[num] = result->getString(1);
            time[num] = result->getString(2);
            recv[num] = result->getString(3);
            num++;
        }
        for (int i = 4; i >= 0; i--) {
            msg[i] += "--------------------------------------------------";
            msg[i] += "\n▷보낸사람 : " + chatname[i] + " " + "▷보낸시간 : " + time[i] + "\n";
            msg[i] += "▷내용 : " + recv[i] + "\n";
            msg[i] += "--------------------------------------------------\n";
            cout << msg[i] << endl;
        }
    }
};

int main()
{
    SQL sql;
    bool loginSuccess = false;

    //시작화면 구현부    
    while (!loginSuccess)
    {
        startMenu(); //시작 화면
        char startIn = 0;
        cout << "▶ ";
        cin >> startIn;
        switch (startIn)
        {
        case '1': //1. 로그인
            login();
            if (sql.login() == 1) {
                loginSuccess = true;
                break;
            }
            continue;
        case '2': //2. 아이디 찾기
            searchId();
            sql.searchId();
            continue;

        case '3': //3. 비밀번호찾기
            searchPw();
            sql.searchPw();
            continue;

        case '4': //4. 회원가입
            createUser();
            sql.crateUser();
            continue;
        case '0': //0. 시스템 종료
            return -1;
        default: //5 ~ 9. 재입력
            cout << "▶잘못된 입력입니다. 다시 입력해주세요." << endl;
            continue;
        }

    }

    //메인화면 구현부
    while (1)
    {
        mainMenu(); //메인 화면
        int mainIn = 0;
        cout << "▶ ";
        cin >> mainIn;

        //내정보 구현부
        if (mainIn == 1)
        {
            myMenu();
            bool backButton = false;
            while (!backButton)
            {
                char informationIn = 0;
                cout << "▶ ";
                cin >> informationIn;
                switch (informationIn)
                {
                case '1':
                    sql.myProfile();
                    break;
                case '2':
                    sql.updateStatus(); //상메 설정
                    continue;
                case '3':
                    sql.updateSong(); //노래 설정
                    continue;
                case '0':
                    backButton = true;
                    break;
                default:
                    cout << "▶잘못된 입력입니다. 다시 입력해주세요." << endl;
                    continue;
                }
            }
        }

        //친구 구현부
        else if (mainIn == 2)
        {
            friends(); //친구화면
            bool backButton = false;
            while (!backButton)
            {
                char friendsIn = 0;
                cout << "▶ ";
                cin >> friendsIn;
                switch (friendsIn)
                {
                case '1':
                    sql.friends(); //친구 목록
                    continue;
                case '2':
                    sql.searchBirth(); //생일 검색
                    continue;
                case '0':
                    backButton = true;
                    break;
                default:
                    cout << "▶잘못된 입력입니다. 다시 입력해주세요." << endl;
                    continue;
                }
            }
        }

        //채팅방 구현부
        else if (mainIn == 3)
        {
            chatting(); //친구화면
            bool backButton = false;
            int code = 0; // 초기화 진행
            while (!backButton)
            {
                char chattingIn = 0;
                cout << "▶ ";
                cin >> chattingIn;
                switch (chattingIn)
                {
                case '1':
                    sql.beforeChat();
                    WSADATA wsa;
                    code = WSAStartup(MAKEWORD(2, 2), &wsa); // 변수에 초기화 진행
                    if (!code)
                    {
                        cout << "< 채팅방에 입장합니다. >" << endl;
                        my_nick = sql.getName();
                        closesocket(client_sock);
                        client_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

                        SOCKADDR_IN client_addr = {};
                        client_addr.sin_family = AF_INET;
                        client_addr.sin_port = htons(7777);
                        InetPton(AF_INET, TEXT("127.0.0.1"), &client_addr.sin_addr);

                        while (1)
                        {
                            if (!connect(client_sock, (SOCKADDR*)&client_addr, sizeof(client_addr))) {
                                cout << "※지인을 사칭하여 금전을 요구할 수 있으니, 도용이 의심 된다면 대화를 중단해주시기 바랍니다." << endl;
                                send(client_sock, my_nick.c_str(), my_nick.length(), 0);
                                break;
                            }
                            cout << "Connecting..." << endl;
                        }
                        std::thread th2(chat_recv);
                        while (1)
                        {
                            string text;
                            std::getline(cin, text);

                            const char* buffer = text.c_str(); // string형을 char* 타입으로 변환
                            send(client_sock, buffer, strlen(buffer), 0);
                            if (text == "/종료")
                            {
                                closesocket(client_sock);
                                backButton = true;
                                break;
                            }
                        }
                        th2.join();
                    }
                    WSACleanup();
                    break;
                case '2':
                    sql.search_content_Message();
                    continue;
                case '3':
                    sql.search_day_Message();
                    continue;
                case '0':
                    backButton = true;
                    break;
                default:
                    cout << "▶잘못된 입력입니다. 다시 입력해주세요." << endl;
                    continue;
                }

            }
        }

        //설정부 구현
        else if (mainIn == 4)
        {
            setting(); //설정화면
            bool backButton = false;
            while (!backButton)
            {
                char settingIn = 0;
                cout << "▶ ";
                cin >> settingIn;
                switch (settingIn)
                {
                case '1':
                    sql.modifyPw(); //비밀번호 변경
                    continue;
                case '2':
                    if (sql.deleteUser() == 1) return -1;
                    else backButton = true;
                case '0':
                    backButton = true;
                    break;
                default:
                    cout << "▶잘못된 입력입니다. 다시 입력해주세요." << endl;
                }
            }
        }

        //종료 버튼
        else if (mainIn == 0)
        {
            cout << "프로그램을 종료합니다." << endl;
            return 0;
        }

        else cout << "▶잘못된 입력입니다. 다시 입력해주세요." << endl;
    }
    delete result;
    delete pstmt;
    delete con;
    delete stmt;
    return 0;
}
profile
성공의 반대는 실패가 아닌 도전하지 않는 것이다.

0개의 댓글