Chapter 2. DB연결 및 클라이언트/서버

개발하는 운동인·2025년 10월 23일

구현 전 설계

    1. 솔루션 안에 Client.cs , Server.cs 분리

Client 설계

    1. Server와 연결
    1. 연결 후 질문을 던진다
    1. 숫자를 입력하여 숫자에 맞는 질문 내용을 UTP로 변환하여 서버에게 전달

Server 설계

    1. Client와 연결
    1. DB와 연결
    1. 클라이언트로 부터 받은 패킷을 받을 때 string으로 받아올 확률이 크다. 해당 string을 파싱
    1. 파싱한 후 switch - case문으로 분리 하여 해당 패킷에 맞는 메서드 호출
    1. 각 메서드에 질문에 맞는 SQL API 이용하여 쿼리문 작성

구현

클라이언트 & 서버 연결

    1. 서버 -> 클라이언트 연결 부분은 서버 프로그래밍의 첫번째 블로그에 기록되어 있음. 참고
  • Server.cs을 작성한다.
class Server
{
   
    static void Main(string[] args)
    {
        ConnectionClient();
    }

    static void ConnectionClient()
    {
        Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        server.Bind(new IPEndPoint(IPAddress.Any, 9000));
        server.Listen(1000);

        var client = server.Accept();
        //여기 아래 부터 코드가 실행된다 = 서버 클라이언트 연결됨

        byte[] buffer = new byte[1024]; //접속 후 클라이언트로부터 받을 데이터 저장 공간 만듦.
        while (true)
        {
            int received = client.Receive(buffer);

            if (received == 0) break;

            string msg = Encoding.UTF8.GetString(buffer, 0, received);

            Console.WriteLine($"클라이언트: {msg}");

            string response = $"서버 응답: {msg}";
            client.Send(Encoding.UTF8.GetBytes(response));
        }
    }

}
    1. 클라이언트 -> 서버 연결
  • 마찬가지로 이 내용도, 서버 프로그래밍의 첫번째 블로그에서 기록하였다. 참고
class Client
{

    static void Main(string[] args)
    {
        ConnectionServer();
    }

    private static void ConnectionServer()
    {
        Socket soket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        soket.Connect(new IPEndPoint(IPAddress.Loopback, 9000));
        Console.WriteLine("서버 연결 성공");

        while(true)
        {
            string msg = Console.ReadLine();

            soket.Send(Encoding.UTF8.GetBytes(msg));

            //아래 코드는 서버에서 받는 패킷 내용인데, 현 과제에서는 필요 없어보임.
            //byte[] buffer = new byte[1024];
            //int received = socket.Receive(buffer);
            //Console.WriteLine($"서버에서 온 메시지 : {Encoding.UTF8.GetString(buffer, 0, received)}");
        }
    
    }
}

현재 결과

  • 좌: 클라이언트
  • 우: 서버
  • 정상적으로 클라이언트가 보낸 패킷을 서버가 전달 받아서 출력하고 있는 상태임.

클라이언트에서 질문 내용을 던지고, 숫자 선택 후 해당 내용들을 서버에게 전달

    1. Client.cs에 아래 내용을 추가한다.
  • 서버로 넘길 번호와 내용 변수, 그리고 번호에 따른 질문 내용에 대한 딕셔너리 자료구조

    1. SetQuestion 메서드를 생성하고, 서버에 연결했던 soket을 인자로 넘긴다.
  • SetQuestion 메서드 안에서, 딕셔너리에 저장했던 키와 값들을 출력하여 질문을 모두 던진다.

  • 그 후에, while문 안에서 입력했던 번호가 딕셔너리에 있다면 해당 번호와 질문 내용을 합치고, 합친 string을 서버에 전달한다.

  • 합칠 때 | 기호로 분리한다. 서버에서는 |를 기준으로 파싱하여 잘 처리하게 할 것임.

현재 실행 결과

  • 좌 : 클라이언트
  • 우 : 서버
  • 정상적으로 작동하고 있다.

Client.cs에서 추가해야 될 부분이 생겼다.

  • 즉, 단순히 번호만 보내는 게 아니라 선택한 명령에 따라 다른 인자(입력값) 를 클라이언트에서 받고, 그걸 서버로 함께 보내야 한다는 뜻입니다. 근데 처리하지 않았나? 라고 생각하겠지만, 추가 인자가 필요한 질문들이 있다.

    1. 3,4,5,6번은 스페셜 질문이라서 추가적인 입력이 필요하다. 해당 string 메서드를 만들고, 인자는 입력한 숫자로 받는다.
  • case가 3,4,5,6일 때 입력한 값에 대한 것들을 message에 저장하고 return한다.

  • case가 3,4,5,6이 아닐 때는 "" 처리

    1. 기존 SetQuestion 메서드에 아래 내용을 추가한다. 공백인지 아닌지 비교하고 공백이라면 스페셜 질문이 아닌 거니까 딕셔너리 키값 저장, 공백이 아니라면 스페셜 질문이므로 return하여 전달 받은 speicalAnswer을 저장.

현재 결과

  • 잘 동작한다.

나중에는 서버에서 전달 받은 내용을 처리하고, 처리한 내용을 다시 클라이언트에게 전달해서 클라이언트 콘솔창에 처리 내용이 출력되게 해야 한다. 그러기 위해서는 서버에서 DB연결과 SQL문 작성이 필요하다.

서버에서 전달받은 질문 내용 분리

    1. Server.cs에서 아래 코드 추가. SperationPacket 메서드를 호출하고, 클라이언트에 연결했던 소켓과 전달 받은 패킷을 인자로 한다.
    1. SperationPacket 메서드 생성
  • 1번 질문일 때 로그인하는 메서드 호출.

1번 질문 : 유저가 로그인 함

    1. UserLogin 메서드 작성
  • sql문을 작성한다. 전달 받은 UserId에 맞는 User테이블의 모든 컬럼 조회
  • 데이터베이스 연결
  • DB에 SQL 쿼리를 전달하고 실행하기 위한 객체인 SqlCommand를 사용한다. 그리고, @UserID 자리에 로그인 시도한 유저의 ID를 넣어준다.
  • SELECT 결과를 가져오는 함수입니다. 결과가 있으면 reader가 행(row) 단위로 데이터를 담습니다.
  • 여러 줄 결과를 합치기 위해 사용합니다. +로 문자열을 이어 붙이는 것보다 훨씬 효율적입니다.
  • while (reader.Read()) 를 통해 데이터가 몇 개든 한줄 씩 읽어온다. 한 유저만 있어도 1번, 여러 명 있으면 여러번 실행 된다.
  • 각 컬럼이 몇 번째에 있는지 찾습니다. 이후엔 이 인덱스를 이용해서 값에 바로 접근
  • 인덱스에 저장된 값 꺼내기
  • 유저 1명당 한 줄씩 메시지를 누적합니다.
  • sb.Length > 0 → 유저 정보가 존재한다는 뜻이므로 로그인 성공 메시지 전송
  • 없으면 로그인 실패 메시지 전송

실행 결과

  • 1번 질문 선택 후 ID 3 입력 했을 때 ID 3이 없으므로 생성해야 되서 로그인 불가.
  • ID 1 입력했 을 때 해당 유저 있으므로 로그인 가능

오류 발생

  • 기존 코드

  • 문제

  • 아래 처럼 해결한다

기존 Ado.net에서 ORM으로 변경

패키지 다운로드

개발 시작

    1. AppDBContext 클래스 생성
    1. 현재 MyDB라는 데이터베이스 안에, 테이블 4개를 생성했었다. 각 테이블을 클래스로 만든다.

    1. 그리고 sql에서 각 테이블을 생성할 때 컬럼의 타입과 이름을 매핑하여 각 클래스에 프로퍼티로 작성한다.
  • User.cs

  • Character.cs

  • Equiment.cs

  • 기존 캐릭터 클래스에 아래 코드 추가. 의미는 캐릭터가 “장비 리스트를 들고 있게 한다”


  • Mail.cs

  • 기존 유저 클래스에 아래 코드 추가. 양방향 관계를 위함

    1. AppDBContext.cs 작성한다. 이 클래스는 DB 연결 관리, 트랜잭션 처리, 엔티티 상태 추적, 실제 SQL 쿼리 실행 및 결과 반환하는 역할을 한다.
  • 기존에 서버 클래스에서 string으로 DB주소를 저장했었는데, 그게 아니라 AppDBContext가 SQL Server에 연결할 때 “이 연결 문자열로 연결해라” 라고 EF Core에 알려주는 메서드를 작성한다.

  • 각 엔티티 클래스의 규칙을 작성한다.

  • 컬럼의 기본값 설정은 굳이 안해도 된다.

    1. 이제 Network쪽인 Server.cs를 작성해야 한다.
  • 기존 코드는 아래와 같다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
//using System.Data.SqlClient;
using Microsoft.Data.SqlClient;

namespace Network
{
    class Server
    {
        private const string connectString = @"Data Source=DESKTOP-2AIAT5E;Initial Catalog=MyDB;Integrated Security=True;TrustServerCertificate=True;"; //내가 만든 DB에 연결 

        static void Main(string[] args)
        {
            ConnectionClient();
        }

        static void ConnectionClient()
        {
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Any, 9000));
            server.Listen(1000);

            Console.WriteLine("클라이언트 접속 대기 중..");
            var client = server.Accept();
            Console.WriteLine("서버 - 클라이언트 접속 완료");
            //여기 아래 부터 코드가 실행된다 = 서버 클라이언트 연결됨

            byte[] buffer = new byte[1024]; //접속 후 클라이언트로부터 받을 데이터 저장 공간 만듦.
            while (true)
            {
                int received = client.Receive(buffer);

                if (received == 0) break;

                string msg = Encoding.UTF8.GetString(buffer, 0, received);

                Console.WriteLine($"클라이언트: {msg}");

                SeparationPacket(client, msg);



                //string response = $"서버 응답: {msg}";

                //string response = Console.ReadLine();

                //client.Send(Encoding.UTF8.GetBytes(response));
            }
        }

        static void SeparationPacket(Socket clientSocket, string clientMessage)
        {
            string[] parts = clientMessage.Split('|');

            int questionNumber = int.Parse(parts[0]);

            if (questionNumber == 1)
            {
                int ID = int.Parse(parts[1]);
                UserLogin(clientSocket, ID);
            }
            else if (questionNumber == 2)
            {
                int ID = int.Parse(parts[1]);
                UserDisConnected(clientSocket, ID);
            }
            else if (questionNumber == 3)
            {
                string nickName = parts[1];
                int level = int.Parse(parts[2]);
                string represntImage = parts[3];

                UserInsert(clientSocket, nickName, level, represntImage);
            }
            else if (questionNumber == 4)
            {
                int ID = int.Parse(parts[1]);
                string email = parts[2];

                UserSendEmail(clientSocket, ID, email);
            }
            else if (questionNumber == 99)
            {
                CheckAllUserID(clientSocket);
            }
        }
        private static void CheckAllUserID(Socket clientSocket)
        {
            string sql = @"SELECT ID, Nickname FROM [User]";


            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand(sql, connection))
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    StringBuilder sb = new StringBuilder();
                    Console.WriteLine("현재 DB에 등록된 유저 목록:");
                    while (reader.Read())
                    {
                        int id = reader.GetInt32(0);
                        string nickname = reader.GetString(1);

                        Console.WriteLine($"ID: {id}, Nickname: {nickname}");
                        sb.AppendLine($"ID: {id}, Nickname: {nickname}");
                    }

                    string result = sb.Length > 0 ? sb.ToString() : "유저가 아무도 없습니다!";
                    clientSocket.Send(Encoding.UTF8.GetBytes(result));
                }
            }
        }
        private static void UserSendEmail(Socket clientSocket, int userId, string email)
        {
            string checkUserSql = @"SELECT COUNT(*) FROM [User] WHERE ID = @UserID";

            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();

                using (SqlCommand checkCmd = new SqlCommand(checkUserSql, connection))
                {
                    checkCmd.Parameters.AddWithValue("@UserID", userId);
                    int exists = (int)checkCmd.ExecuteScalar();

                    if (exists == 0)
                    {
                        clientSocket.Send(Encoding.UTF8.GetBytes($"해당 유저 {userId} 를 찾을 수 없습니닷"));
                        return;
                    }
                }

                string insertSql = @"INSERT INTO Mail (UserID, Detail, ReceiveDate, ReadDate)
                             VALUES (@UserID, @Detail, GETDATE(), NULL)";

                using (SqlCommand insertCmd = new SqlCommand(insertSql, connection))
                {
                    insertCmd.Parameters.AddWithValue("@UserID", userId);
                    insertCmd.Parameters.AddWithValue("@Detail", email);

                    int rows = insertCmd.ExecuteNonQuery();

                    if (rows > 0)
                        clientSocket.Send(Encoding.UTF8.GetBytes($"해당 유저 {userId} 에게 정상적으로 메일을 보냈습니다"));
                    else
                        clientSocket.Send(Encoding.UTF8.GetBytes("메일 보내기 실패!"));
                }
            }
        }
        private static void UserInsert(Socket clientSocket, string nickName, int level, string represntImage)
        {
            string sql = @"INSERT INTO [User] ([Level], Nickname, CreateDate, RepresntImage) VALUES ( @Level, @Nickname, GetDate(), @RepresntImage)";

            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand(sql, connection))
                {
                    command.Parameters.AddWithValue("@Level", level);
                    command.Parameters.AddWithValue("@Nickname", nickName);
                    command.Parameters.AddWithValue("@RepresntImage", represntImage);

                    int rows = command.ExecuteNonQuery();

                    if (rows > 0)
                    {
                        string sendMsg = $"유저 데이터 삽입 완 |{nickName}|{level}";
                        clientSocket.Send(Encoding.UTF8.GetBytes(sendMsg));
                    }
                    else
                    {
                        string sendMsg = $"유저 데이터 삽입 실패";
                        clientSocket.Send(Encoding.UTF8.GetBytes(sendMsg));
                    }
                }
            }

        }
        private static void UserDisConnected(Socket clientSocket, int userDisconnectID)
        {
            string sql = @"DELETE FROM [User] WHERE ID=@UserID";

            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand(sql, connection))
                {
                    command.Parameters.AddWithValue("@UserID", userDisconnectID);

                    int rows = command.ExecuteNonQuery();
                    Console.WriteLine($"삭제된 행 수: {rows}");

                    if (rows > 0)
                        clientSocket.Send(Encoding.UTF8.GetBytes("USER_DELETE_SUCCESS"));
                    else
                        clientSocket.Send(Encoding.UTF8.GetBytes("USER_DELETE_FAIL"));
                }
            }


        }

        private static void UserLogin(Socket clientSocket, int userLoginID)
        {
            //DB에서 로그인한 유저의 기본 정보 + 관련 데이터 꺼내와야 함

            //using SqlConnection conn = new SqlConnection(ConnStr); //현재 C# 7.3 버전에서는 안됩니다...

            string sql = @"SELECT ID, Nickname, [Level], CreateDate, RepresntImage
                   FROM [User]
                   WHERE ID = @UserID";

            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand(sql, connection))
                {
                    command.Parameters.AddWithValue("@UserID", userLoginID);

                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        StringBuilder sb = new StringBuilder();

                        while (reader.Read())
                        {
                            int idxId = reader.GetOrdinal("ID");
                            int idxNickname = reader.GetOrdinal("Nickname");
                            int idxLevel = reader.GetOrdinal("Level");
                            int idxCreateDate = reader.GetOrdinal("CreateDate");
                            int idxRepresntImage = reader.GetOrdinal("RepresntImage");

                            int id = reader.GetInt32(idxId);
                            string nickname = reader.GetString(idxNickname);
                            int level = reader.GetInt32(idxLevel);
                            DateTime createDate = reader.GetDateTime(idxCreateDate);

                            string represntImage = reader.IsDBNull(idxRepresntImage)
                                ? "NULL"
                                : reader.GetString(idxRepresntImage);

                            sb.AppendLine($"로그인 성공! |{id}| {nickname} | {level} | {createDate:yyyy-MM-dd HH:mm:ss} | {represntImage}");
                        }

                        if (sb.Length > 0)
                        {
                            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
                        }
                        else
                        {
                            clientSocket.Send(Encoding.UTF8.GetBytes($"로그인 실패 ! 해당 {userLoginID}는 캐릭터를 생성하셔야 합니닷 "));
                        }
                    }
                }


            }
        }
    }

}
  • 새로운 코드는 아래처럼 작성한다. 작성하기 전, DBSet 객체의 이름은 DB 실제 테이블 이름과 동일 해야 한다.

1. 유저 로그인

2. 유저 탈퇴

3. 유저 정보 생성



4. 유저에게 메일 보내기

5. 유저의 캐릭터 생성

6. 유저의 장비 생성

  • 먼저 UserID로 캐릭터를 찾아서 그 중 하나의 CharacterID를 사용

7. 유저의 캐릭터들 조회


8. 유저의 메일들 조회

  • 7번과 구조 비슷

9. 유저의 특정 캐릭터 장비 리스트 가져오기

  • 7,8번과 구조 비슷

10. 유저 정보 변경

11. 유저의 특정 캐릭터 정보 변경

12. 유저의 특정 메일 내용 변경

13. 유저의 캐릭터 장비 정보 변경

문제 해결

  • 유저의 특정 캐릭터에 장비를 생성하는 것을 하지 않고 있었다. 아래 코드 추가
  • 위 변수명은 잘못 적음.

14. ID의 유저의 캐릭터의 장비 삭제

  • 유저 ID와 캐릭터 ID 장비 ID를 입력 받고 해당 유저 캐릭터의 특정 장비 삭제

15. ID의 유저의 우편 삭제

현재 Server.cs 의 문제점: 코드 줄이 500줄 이상이 넘어간다. 클래스를 분리해야 될 필요성이 있다.

//using System.Data.SqlClient;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Server.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Network
{
    class Server
    {
        private const string connectString = @"Data Source=DESKTOP-2AIAT5E;Initial Catalog=MyDB;Integrated Security=True;TrustServerCertificate=True;"; //내가 만든 DB에 연결 

        static void Main(string[] args)
        {
            ConnectionClient();
        }

        static void ConnectionClient()
        {
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Any, 9000));
            server.Listen(1000);

            Console.WriteLine("클라이언트 접속 대기 중..");
            var client = server.Accept();
            Console.WriteLine("서버 - 클라이언트 접속 완료");
            //여기 아래 부터 코드가 실행된다 = 서버 클라이언트 연결됨

            byte[] buffer = new byte[1024]; //접속 후 클라이언트로부터 받을 데이터 저장 공간 만듦.
            while (true)
            {
                int received = client.Receive(buffer);

                if (received == 0) break;

                string msg = Encoding.UTF8.GetString(buffer, 0, received);

                Console.WriteLine($"클라이언트: {msg}");

                SeparationPacket(client, msg);



                //string response = $"서버 응답: {msg}";

                //string response = Console.ReadLine();

                //client.Send(Encoding.UTF8.GetBytes(response));
            }
        }

        static void SeparationPacket(Socket clientSocket, string clientMessage)
        {
            string[] parts = clientMessage.Split('|');

            int questionNumber = int.Parse(parts[0]);

            if (questionNumber == 1)
            {
                int ID = int.Parse(parts[1]);
                UserLogin(clientSocket, ID);
            }
            else if (questionNumber == 2)
            {
                int ID = int.Parse(parts[1]);
                UserDisConnected(clientSocket, ID);
            }
            else if (questionNumber == 3)
            {
                string nickName = parts[1];
                string represntImage = parts[2];

                UserInsert(clientSocket, nickName, represntImage);
            }
            else if (questionNumber == 4)
            {
                int ID = int.Parse(parts[1]);
                string email = parts[2];

                UserSendEmail(clientSocket, ID, email);
            }
            else if(questionNumber == 5)
            {
                int ID = int.Parse(parts[1]);
                string Type = parts[2];

                UserCharacterInsert(clientSocket, ID, Type);
            }
            else if(questionNumber == 6)
            {
                int uesrID = int.Parse(parts[1]);
                int characterID = int.Parse(parts[2]);
                string type = parts[2];
                string region = parts[3];

                UserCharacterEquipmentInsert(clientSocket, uesrID, characterID,type, region);
            }
            else if(questionNumber == 7)
            {
                int ID = int.Parse(parts[1]);
                UserCharatersSelect(clientSocket, ID);
            }
            else if(questionNumber == 8)
            {
                int ID = int.Parse(parts[1]);
                UserMailSelect(clientSocket, ID);
            }
            else if (questionNumber == 9)
            {
                int ID = int.Parse(parts[1]);
                UserCharaterEquirmentSelect(clientSocket, ID);
            }
            else if (questionNumber == 10)
            {
                int ID = int.Parse(parts[1]);
                string nickName = parts[2];
                string represntImage = parts[3];
                UserUpdate(clientSocket, ID, nickName, represntImage);
            }
            else if(questionNumber == 11)
            {
                int userID = int.Parse(parts[1]);
                int characterID = int.Parse(parts[2]);
                string Type = parts[3];

                UserCharacterUpdate(clientSocket, userID, characterID, Type);
            }
            else if(questionNumber == 12)
            {
                int ID = int.Parse(parts[1]);
                int mailID = int.Parse(parts[2]);
                string email = parts[3];

                UserEmailUpdate(clientSocket, ID, mailID,email);
            }
            else if (questionNumber == 13)
            {
                int ID = int.Parse(parts[1]);
                int characterID = int.Parse(parts[2]);
                int equimentID = int.Parse(parts[3]);
                string equimentType = parts[4];
                string equimentRegion = parts[5];

                UserCharacterEquimentUpdate(clientSocket, ID, characterID, equimentID, equimentType , equimentRegion);
            }

            else if (questionNumber == 14)
            {
                int ID = int.Parse(parts[1]);
                int characterID = int.Parse(parts[2]);
                int equimentID = int.Parse(parts[3]);

                UserCharacterEquimentDelete(clientSocket, ID,characterID,equimentID);
            }
            else if (questionNumber == 15)
            {
                int ID = int.Parse(parts[1]);
                int emialID= int.Parse(parts[2]);

                UserMailDelete(clientSocket, ID, emialID);
            }
            else if (questionNumber == 98)
            {
                ResetPrimaryKeys(clientSocket);
            }
            else if (questionNumber == 99)
            {
                CheckAllUserID(clientSocket);
            }
        }

        private static void UserMailDelete(Socket clientSocket, int userID, int emialID)
        {
            var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);
            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            var mail = context.Mail.FirstOrDefault(m => m.MailID == emialID && m.UserID == userID);

            if (mail == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"MailID {emialID}의 메일을 찾을 수 없습니다."));
                return;
            }

            context.Mail.Remove(mail);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 메일 ID {emialID} 삭제 완료"));
        }
        private static void UserCharacterEquimentDelete(Socket clientSocket, int userID, int characterID, int equimentID)
        {
            var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);
            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID && c.UserID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"CharacterID {characterID}의 캐릭터를 찾을 수 없습니다."));
                return;
            }

            var equiment = context.Equipment.FirstOrDefault(e => e.EquipmentID == equimentID && e.CharacterID == characterID);

            if (equiment == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"EquipmentID {equimentID}의 장비를 찾을 수 없습니다."));
                return;
            }

            context.Equipment.Remove(equiment);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"유저 {userID}의 캐릭터 {characterID}의 장비 {equimentID} 삭제 완료"));
        }

        private static void UserCharacterEquimentUpdate(Socket clientSocket,int userID, int characterID, int enquimentID , string equimentType, string equimentRegion) //유저의 캐릭터의 장비 정보 변경
        {
            var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);
            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID && c.UserID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"CharacterID {characterID}의 캐릭터를 찾을 수 없습니다."));
                return;
            }

            var equiment = context.Equipment.FirstOrDefault(e => e.EquipmentID == enquimentID && e.CharacterID == characterID);

            if (equiment == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"EquipmentID {enquimentID}의 장비를 찾을 수 없습니다."));
                return;
            }

            equiment.Type = equimentType;
            equiment.Region = equimentRegion;
            context.SaveChanges();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("장비 정보 변경 완료!");
            sb.AppendLine($"유저 ID: {user.ID}");
            sb.AppendLine($"캐릭터 ID: {equiment.CharacterID}");
            sb.AppendLine($"장비 ID: {equiment.EquipmentID}");
            sb.AppendLine($"장비 타입: {equiment.Type}");
            sb.AppendLine($"장비 부위: {equiment.Region}");

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }
        private static void UserEmailUpdate(Socket clientSocket,int userID, int mailID, string detail) //유저의 메일 정보 변경
        {
            var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            var mail = context.Mail.FirstOrDefault(m => m.UserID == userID && m.MailID == mailID);

            if (mail == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 {mailID}가 존재하지 않으므로, 변경이 불가능 합니다."));
                return;
            }

            mail.Detail = detail;
            context.SaveChanges();

            StringBuilder sb = new StringBuilder();

            foreach (var m in user.Mails)
            {
                sb.AppendLine($"메일 ID: {m.MailID}");
                sb.AppendLine($"유저 ID: {m.UserID}");
                sb.AppendLine($"내용: {m.Detail}");
                sb.AppendLine($"받은 날짜: {m.ReceiveDate}");
                sb.AppendLine($"읽은 날짜: {m.ReadDate}");
                sb.AppendLine("------------------------------------");
            }
            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }
        private static void UserCharacterUpdate(Socket clientSocket,int userID, int characterID, string Type) //유저의 캐릭터 정보 변경
        {
            var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);

            if(user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID );

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"CharacterID {characterID}의 캐릭터를 찾을 수 없습니다."));
                return;
            }

            character.Type = Type;

            context.SaveChanges();

            StringBuilder sb = new StringBuilder();
            foreach (var m in user.Characters)
            {
                sb.AppendLine($"캐릭터 ID  : {character.CharacterID} ");
                sb.AppendLine($"캐릭터 타입  : {character.Type} ");
                sb.AppendLine($"캐릭터 레벨  : {character.Level} ");
                sb.AppendLine($"유저 ID  : {character.UserID} ");
            }
            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));

        }
        private static void UserUpdate(Socket clientSocket, int userID, string nickName, string representImage) //유저의 정보 변경
        {
            var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);

            if(user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            user.Nickname = nickName;
            user.RepresntImage = representImage;

            context.SaveChangesAsync();

            clientSocket.Send(Encoding.UTF8.GetBytes($"유저 정보 변경!\n" +
                $"변경한 닉네임: {user.Nickname}\n" +
                $"변경한 대표 이미지: {user.RepresntImage}"));
        }
        private static void UserCharaterEquirmentSelect(Socket clientSocket, int userID) //유저의 특정 캐릭터 장비 조회
        {
            var context = new AppDBContext();

            var character = context.Character.Include(u => u.Equipments).FirstOrDefault(c => c.CharacterID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            if (character.Equipments.Count == 0)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 캐릭터ID {character.CharacterID} 장비가 없습니다."));
                return;
            }

            StringBuilder sb = new StringBuilder();

            foreach (var equipment in character.Equipments)
            {
                sb.AppendLine($"장비 ID: {equipment.EquipmentID}");
                sb.AppendLine($"장비 타입: {equipment.Type}");
                sb.AppendLine($"장비 레벨: {equipment.Level}");
                sb.AppendLine($"장비 강화도: {equipment.Enforce}");
                sb.AppendLine($"장비 부위: {equipment.Region}");
                sb.AppendLine("------------------------------------");
            }

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }
        private static void UserMailSelect(Socket clientSocket, int userID) //유저의 메일 목록 조회
        {
            var context = new AppDBContext();

            var user = context.User.Include(u => u.Mails).FirstOrDefault(u => u.ID == userID);
            //Include -> 수업 때 배웠던 N+1 문제 방지. 유저의 메일 모두 갖고옴

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            if (user.Mails.Count == 0)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 메일이 없습니다."));
                return;
            }

            StringBuilder sb = new StringBuilder(); //행 마다 조회될 수 있으므로, SB로 누적해서 저장한 후 클라이언트에게 보냄

            foreach (var mail in user.Mails)
            {
                sb.AppendLine($"유저 ID : {userID}");
                sb.AppendLine($"메일 ID : {mail.MailID}");
                sb.AppendLine($"내용 : {mail.Detail}");
                sb.AppendLine($"받은 날짜 : {mail.ReceiveDate}");
                sb.AppendLine($"읽은 날짜 : {mail.ReadDate}");
            }

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }
        private static void UserCharatersSelect(Socket clientSocket, int userID) //유저의 캐릭터 목록 조회
        {
            var context = new AppDBContext();

            var user = context.User.Include(u => u.Characters).FirstOrDefault(u => u.ID == userID);
            //Include -> 수업 때 배웠던 N+1 문제 방지. 유저의 캐릭터들 모두 갖고옴

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            if (user.Characters.Count == 0)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 캐릭터가 없습니다."));
                return;
            }

            StringBuilder sb = new StringBuilder(); //행 마다 조회될 수 있으므로, SB로 누적해서 저장한 후 클라이언트에게 보냄

            foreach (var character in user.Characters)
            {
                sb.AppendLine($"유저 ID : {userID}");
                sb.AppendLine($"캐릭터 ID : {character.CharacterID}");
                sb.AppendLine($"캐릭터 타입 : {character.Type}");
            }

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }
        private static void UserCharacterEquipmentInsert(Socket clientSocket,int userID, int characterID, string type, string region)
        {
            var context = new AppDBContext();

            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID && c.UserID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userID}인 캐릭터를 못 찾겠습니다. (해당 유저{userID}의 캐릭터를 생성하시오.)"));
                return;
            }


            var equipment = new Equipment
            {
                CharacterID = character.CharacterID,
                Type = type,
                Level = 1, //레벨은 서버에서 고정값으로 사용할 것임
                Enforce = 1, //강화도도 서버에서 고정값 사용
                Region = region,
                Owner = character,
            };

            context.Equipment.Add(equipment);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"{userID}의 유저 장비 생성 완료! \n" +
                $"UserID {userID}의 유저, CharacterID {characterID}에 장비 생성 완료!\n" +
                $"장비 ID : {equipment.EquipmentID}\n" +
                $"장비 타입 : {equipment.Type}\n" +
                $"장비 레벨 : {equipment.Level}\n" +
                $"장비 강화도 :{equipment.Enforce}\n" +
                $"장비 부위 : {equipment.Region}\n" +
                $"소유 유저 ID : {character.UserID}\n" +
                $"소유 캐릭터 ID : {character.CharacterID}"));
        }

        private static void UserCharacterInsert(Socket clientSocket, int userID, string type)
        {
            var context = new AppDBContext();

            var createUser = context.User.Where(u => u.ID == userID).FirstOrDefault(); //캐릭터를 생성할 유저

            if (createUser == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userID}인 유저를 못 찾겠습니다 "));
                return;
            }

            var character = new Character
            {             
                UserID = userID,
                Type = type,
                Level = 1,
                Owner = createUser,
            };

            context.Character.Add(character);

            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"{userID}의 유저에 대한 캐릭터 생성 완료! \n" +
                $"소유 캐릭터 ID  : {character.CharacterID}\n" +
                $"Type : {character.Type}\n" +
                $"Level : {character.Level}\n" +
                $"소유 유저 ID : {character.UserID}"));
        }
        private static void UserSendEmail(Socket clientSocket, int userId, string email)
        {
            var context = new AppDBContext();

            var sendUser = context.User.Where(u => u.ID == userId).FirstOrDefault();
            
            if(sendUser == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userId}인 유저를 못 찾겠습니다 "));
                return;
            }

            var mail = new Mail
            {
                UserID = userId,
                Detail = email,
                ReceiveDate = DateTime.UtcNow,
                ReadDate = null,
                Owner = sendUser,  //이 메일의 주인이 sendUser 유저임을 설정함.
            };

            context.Mail.Add(mail);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"{userId}의 유저에게 메일 전송 완료! (MailID={mail.MailID})"));
        }
        private static void UserInsert(Socket clientSocket, string nickName, string represntImage)
        {
            using var context = new AppDBContext();

            var user = new User
            {
                Nickname = nickName,
                Level = 1, //유저 생성은 레벨은 보통1로 하긴함
                RepresntImage = represntImage,
                CreateDate = DateTime.Now, //현재 지금 시간
            };

            context.User.Add(user); //삽입 DB 예약
            context.SaveChanges(); //삽입 DB 실제 실행

            clientSocket.Send(Encoding.UTF8.GetBytes($"유저 정보 생성!\n" +
                $"로그인 가능한 ID : {user.ID}\n" +
                $"유저 닉네임 : {user.Nickname}\n"));

        }
     
        private static void UserDisConnected(Socket clientSocket, int userDisconnectID)
        {
            using var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userDisconnectID);

            if(user != null)
            {
                context.User.Remove(user); //실제 Delete 예약

                context.SaveChanges(); //실제 DB에 Delete 실행하고, 상태 반영하는 api임

                clientSocket.Send(Encoding.UTF8.GetBytes($"{userDisconnectID}에 해당 하는 유저 탈퇴 완료 "));
            }
            else
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"{userDisconnectID}의 유저가 존재하지 않으므로, 탈퇴 불가"));
            }
        }

        private static void UserLogin(Socket clientSocket, int userLoginID)
        {
            using var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userLoginID); //유저의 ID가 로그인한 ID와 일치 하다면, 첫번째 행 반환함.

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userLoginID}인 유저를 못 찾겠습니다"));
                return;
            }

            string msg = $"로그인 성공! \n" +
                $"유저 ID : {user.ID}\n" +
                $"유저 닉네임 : {user.Nickname}\n" +
                $"유저 레벨 : {user.Level}\n" +
                $"유저 로그인한 시간 : {user.CreateDate:yyyy-MM-dd HH:mm:ss}\n" +
                $"유저 대표 이미지 이름 : {user.RepresntImage}";
            clientSocket.Send(Encoding.UTF8.GetBytes(msg));
        }
        private static void CheckAllUserID(Socket clientSocket)
        {
            string sql = @"SELECT ID, Nickname FROM [User]";

            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand(sql, connection))
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    StringBuilder sb = new StringBuilder();
                    Console.WriteLine("현재 DB에 등록된 유저 목록:");
                    while (reader.Read())
                    {
                        int id = reader.GetInt32(0);
                        string nickname = reader.GetString(1);

                        Console.WriteLine($"ID: {id}, Nickname: {nickname}");
                        sb.AppendLine($"ID: {id}, Nickname: {nickname}");
                    }

                    string result = sb.Length > 0 ? sb.ToString() : "유저가 아무도 없습니다!";
                    clientSocket.Send(Encoding.UTF8.GetBytes(result));
                }
            }
        }

        private static void ResetPrimaryKeys(Socket clientSocket)
        {
            using var context = new AppDBContext();

            // 1. 데이터 삭제 (FK 순서 고려)
            var sqlDelete = @"
        DELETE FROM Equipment;
        DELETE FROM Mail;
        DELETE FROM Character;
        DELETE FROM [User];
    ";

            // 2. IDENTITY (PK) 초기화
            var sqlReset = @"
        DBCC CHECKIDENT ('Equipment', RESEED, 0);
        DBCC CHECKIDENT ('Mail', RESEED, 0);
        DBCC CHECKIDENT ('Character', RESEED, 0);
        DBCC CHECKIDENT ('[User]', RESEED, 0);
    ";

            context.Database.ExecuteSqlRaw(sqlDelete);
            context.Database.ExecuteSqlRaw(sqlReset);

            clientSocket.Send(Encoding.UTF8.GetBytes("기본키 초기화 완료"));
        }
    }
}

현재 계층 구조

변경된 계층 구조

  • Domain(테이블) 폴더를 만들어서, 해당 Entity 클래스들을 이동
  • Services 폴더를 만들어서 각 테이블을 어떻게 조작할지에 대한 Service정의

변경 된 클래스 내용

UserService.cs

using Server.Database;
using Server.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Server.Services
{
    public class UserService
    {
        private static AppDBContext CreateContext() => new AppDBContext();

        /// 1번문제 :  유저 로그인 
        //  ID를 통해 로그인하며, 로그인 시 로그인 성공에 대한 출력과 해당 유저의 정보들이 출력됨. (단, 유저가 정보 생성을 하여 얻은 유저 ID값으로 로그인 해야 함, ID 틀릴 시 로그인 불가)
        public static void UserLogin(Socket clientSocket, int userLoginID)
        {
            using var context = CreateContext();

            var user = context.User.FirstOrDefault(u => u.ID == userLoginID); //유저의 ID가 로그인한 ID와 일치 하다면, 첫번째 행 반환함.

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userLoginID}인 유저를 못 찾겠습니다"));
                return;
            }

            string msg = $"로그인 성공! \n" +
                $"유저 ID : {user.ID}\n" +
                $"유저 닉네임 : {user.Nickname}\n" +
                $"유저 레벨 : {user.Level}\n" +
                $"유저 로그인한 시간 : {user.CreateDate:yyyy-MM-dd HH:mm:ss}\n" +
                $"유저 대표 이미지 이름 : {user.RepresntImage}";
            clientSocket.Send(Encoding.UTF8.GetBytes(msg));
        }

        //2번 문제 : 유저 탈퇴
        // 입력 받은 ID를 통해 탈퇴 하며, ID가 존재하지 않을 경우 탈퇴 불가
        public static void UserDisConnected(Socket clientSocket, int userDisconnectID)
        {
            using var context = CreateContext();

            var user = context.User.FirstOrDefault(u => u.ID == userDisconnectID);

            if (user != null)
            {
                context.User.Remove(user); //실제 Delete 예약

                context.SaveChanges(); //실제 DB에 Delete 실행하고, 상태 반영하는 api임

                clientSocket.Send(Encoding.UTF8.GetBytes($"{userDisconnectID}에 해당 하는 유저 탈퇴 완료 "));
            }
            else
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"{userDisconnectID}의 유저가 존재하지 않으므로, 탈퇴 불가"));
            }
        }
        //3번 문제 : 유저 정보 생성 
        // ID는 입력 받지 않는다. 왜냐하면 정보 생성이기 때문에 ID를 발급 받아야 한다. EX) 홈페이지 회원가입 느낌? 
        // 또한, Level은 보통 1로 시작 하기 때문에 정보 생성 시 1로 고정 -> 게임 진행 과정에서 레벨 시스템을 구현하여 추후에 처리할 것임.
        public static void UserInsert(Socket clientSocket, string nickName, string represntImage)
        {
            using var context = CreateContext();

            var user = new User
            {
                Nickname = nickName,
                Level = 1, //유저 생성은 레벨은 보통1로 하긴함
                RepresntImage = represntImage,
                CreateDate = DateTime.Now, //현재 지금 시간
            };

            context.User.Add(user); //삽입 DB 예약
            context.SaveChanges(); //삽입 DB 실제 실행

            clientSocket.Send(Encoding.UTF8.GetBytes($"유저 정보 생성!\n" +
                $"로그인 가능한 ID : {user.ID}\n" +
                $"유저 닉네임 : {user.Nickname}\n"));

        }
        //4번 문제 : 유저에게 이메일 보내기
        //어느 유저에게 이메일을 보낼지 ID를 입력 받고, 해당 ID가 존재하지 않는다면 이메일 송신 불가.
        // 해당 ID 존재하면 이메일 정보 삽입.
        public static void UserSendEmail(Socket clientSocket, int userId, string email)
        {
            using var context = CreateContext();

            var sendUser = context.User.Where(u => u.ID == userId).FirstOrDefault();

            if (sendUser == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userId}인 유저를 못 찾겠습니다 "));
                return;
            }

            var mail = new Mail
            {
                UserID = userId,
                Detail = email,
                ReceiveDate = DateTime.UtcNow,
                ReadDate = null,
                Owner = sendUser,  //이 메일의 주인이 sendUser 유저임을 설정함.
            };

            context.Mail.Add(mail);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"{userId}의 유저에게 메일 전송 완료! (MailID={mail.MailID})"));
        }

        //10. ID로 유저 정보 변경
        //변경할 유저 ID 입력 받아오고, 해당 ID 존재하지 않는다면 정보 변경 불가
        //ID 존재 한다면, 변경할 유저 닉네임과 대표 이미지를 해당 유저 테이블의 데이터 변경(set)

        public static void UserUpdate(Socket clientSocket, int userID, string nickName, string representImage) //유저의 정보 변경
        {
            var context = new AppDBContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            user.Nickname = nickName;
            user.RepresntImage = representImage;

            context.SaveChangesAsync();

            clientSocket.Send(Encoding.UTF8.GetBytes($"유저 정보 변경!\n" +
                $"변경한 닉네임: {user.Nickname}\n" +
                $"변경한 대표 이미지: {user.RepresntImage}"));
        }

    }
}

CharacterService.cs

using Microsoft.EntityFrameworkCore;
using Server.Database;
using Server.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Server.Services
{
    public class CharacterService
    {
        private static AppDBContext CreateContext() => new AppDBContext();

        //5번 문제: 유저 캐릭터 정보 생성
        // 어떤 유저에게 캐릭터를 생성할지 ID를 입력 받고, 해당 ID가 존재하지 않는다면 정보 생성 불가
        // 해당 ID존재 한다면 캐릭터 정보 삽입
        public static void UserCharacterInsert(Socket clientSocket, int userID, string type)
        {
            using var context = CreateContext();

            var createUser = context.User.Where(u => u.ID == userID).FirstOrDefault(); //캐릭터를 생성할 유저

            if (createUser == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userID}인 유저를 못 찾겠습니다 "));
                return;
            }

            var character = new Character
            {
                UserID = userID,
                Type = type,
                Level = 1,
                Owner = createUser,
            };

            context.Character.Add(character);

            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"{userID}의 유저에 대한 캐릭터 생성 완료! \n" +
                $"소유 캐릭터 ID  : {character.CharacterID}\n" +
                $"Type : {character.Type}\n" +
                $"Level : {character.Level}\n" +
                $"소유 유저 ID : {character.UserID}"));
        }
        //7번 문제: ID로 어느 유저의 어떤 캐릭터들이 존재하는지 조회.
        // 어느 유저 ID인지 입력 받고, 해당 ID가 없다면 조회 불가
        // 해당 ID가 있다면, 캐릭터 정보들 조회.  단일 조회가 아닌 다중 조회 -> Include
        public static void UserCharatersSelect(Socket clientSocket, int userID) //유저의 캐릭터 목록 조회
        {
            using var context = CreateContext();

            var user = context.User.Include(u => u.Characters).FirstOrDefault(u => u.ID == userID);
            //Include -> 수업 때 배웠던 N+1 문제 방지. 유저의 캐릭터들 모두 갖고옴

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            if (user.Characters.Count == 0)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 캐릭터가 없습니다."));
                return;
            }

            StringBuilder sb = new StringBuilder(); //행 마다 조회될 수 있으므로, SB로 누적해서 저장한 후 클라이언트에게 보냄

            foreach (var character in user.Characters)
            {
                sb.AppendLine($"유저 ID : {userID}");
                sb.AppendLine($"캐릭터 ID : {character.CharacterID}");
                sb.AppendLine($"캐릭터 타입 : {character.Type}");
            }

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }
        //11번 문제. ID로 유저의 캐릭터 정보 변경
        //1. 변경할 유저 ID 입력 받고, 해당 유저 ID 존재하지 않는다면 변경 불가
        //2. 유저 ID의 캐릭터 ID 입력 받고, 해당 캐릭터 ID가 존재하지 않는다면 변경 불가능.
        //3. 위 과정의 조건이 모두 참(변경 가능)이라면 캐릭터 정보 변경 가능
        //4. 정보 변경은 캐릭터의 타입만 변경하게끔 의도 하였음. 레벨은 클라이언트가 입력 받아서 처리하는 것은 말이 안된다 생각하였고, 추후에 따로 레벨 구현을 했을 때 생각.
        public static void UserCharacterUpdate(Socket clientSocket, int userID, int characterID, string Type) //유저의 캐릭터 정보 변경
        {
            using var context = CreateContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"CharacterID {characterID}의 캐릭터를 찾을 수 없습니다."));
                return;
            }

            character.Type = Type;

            context.SaveChanges();

            StringBuilder sb = new StringBuilder();
            foreach (var m in user.Characters)
            {
                sb.AppendLine($"캐릭터 ID  : {character.CharacterID} ");
                sb.AppendLine($"캐릭터 타입  : {character.Type} ");
                sb.AppendLine($"캐릭터 레벨  : {character.Level} ");
                sb.AppendLine($"유저 ID  : {character.UserID} ");
            }
            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));

        }
    }
}

EquimentService.cs

using Microsoft.EntityFrameworkCore;
using Server.Database;
using Server.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Server.Services
{
    public class EquimentService
    {
        private static AppDBContext CreateContext() => new AppDBContext();

        //6번 문제 : 유저의 캐릭터 장비 생성 
        //어느 유저에게 캐릭터 장비 생성할지와 어느 캐릭터에게 장비 생성할지 ID를 입력 받고, 해당 ID들이 모두 존재 해야 장비 생성 가능.
        //해당 ID들이 존재하지 않는다면 장비 생성 불가.
        //레벨과 강화도는 입력 받지 않게 의도 하였음. 보통 서버에서 고정값으로 세팅하는게 보안상 안전할 것 같아서.
        public static void UserCharacterEquipmentInsert(Socket clientSocket, int userID, int characterID, string type, string region)
        {
            var context = CreateContext();

            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID && c.UserID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"ID가 {userID}인 캐릭터를 못 찾겠습니다. (해당 유저{userID}의 캐릭터를 생성하시오.)"));
                return;
            }


            var equipment = new Equipment
            {
                CharacterID = character.CharacterID,
                Type = type,
                Level = 1, //레벨은 서버에서 고정값으로 사용할 것임
                Enforce = 1, //강화도도 서버에서 고정값 사용
                Region = region,
                Owner = character,
            };

            context.Equipment.Add(equipment);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"{userID}의 유저 장비 생성 완료! \n" +
                $"UserID {userID}의 유저, CharacterID {characterID}에 장비 생성 완료!\n" +
                $"장비 ID : {equipment.EquipmentID}\n" +
                $"장비 타입 : {equipment.Type}\n" +
                $"장비 레벨 : {equipment.Level}\n" +
                $"장비 강화도 :{equipment.Enforce}\n" +
                $"장비 부위 : {equipment.Region}\n" +
                $"소유 유저 ID : {character.UserID}\n" +
                $"소유 캐릭터 ID : {character.CharacterID}"));
        }
        //9번 문제: ID로 유저의 특정 캐릭터 장비 목록 조회
        //1. 먼저, 어느 유저를 조회할지 부터 유저 ID 입력 받아오고, ID 존재하지 않는다면 조회 불가
        //2. 유저의 캐릭터 ID 입력 받아오고, ID가 존재하지 않는다면 조회 불가 
        //3. 유저의 특정 캐릭터의 장비가 아무것도 없으면 장비 조회 출력 불가 
        //4. 위 과정들 모두 가능하다면, 장비 조회 가능
        public static void UserCharaterEquirmentSelect(Socket clientSocket, int userID) //유저의 특정 캐릭터 장비 조회
        {
            var context = CreateContext();

            var character = context.Character.Include(u => u.Equipments).FirstOrDefault(c => c.CharacterID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            if (character.Equipments.Count == 0)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 캐릭터ID {character.CharacterID} 장비가 없습니다."));
                return;
            }

            StringBuilder sb = new StringBuilder();

            foreach (var equipment in character.Equipments)
            {
                sb.AppendLine($"장비 ID: {equipment.EquipmentID}");
                sb.AppendLine($"장비 타입: {equipment.Type}");
                sb.AppendLine($"장비 레벨: {equipment.Level}");
                sb.AppendLine($"장비 강화도: {equipment.Enforce}");
                sb.AppendLine($"장비 부위: {equipment.Region}");
                sb.AppendLine("------------------------------------");
            }

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }

        //13번 문제 : ID의 유저의 캐릭터의 장비 정보 변경
        //유저 ID,캐릭터 ID, 장비 ID를 입력 받고, 각각 존재하지 않는다면 장비 정보 변경 불가
        //해당 ID들이 모두 존재한다면 입력한 장비 ID의 정보를 변경
        public static void UserCharacterEquimentUpdate(Socket clientSocket, int userID, int characterID, int enquimentID, string equimentType, string equimentRegion) //유저의 캐릭터의 장비 정보 변경
        {
            var context = CreateContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);
            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID && c.UserID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"CharacterID {characterID}의 캐릭터를 찾을 수 없습니다."));
                return;
            }

            var equiment = context.Equipment.FirstOrDefault(e => e.EquipmentID == enquimentID && e.CharacterID == characterID);

            if (equiment == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"EquipmentID {enquimentID}의 장비를 찾을 수 없습니다."));
                return;
            }

            equiment.Type = equimentType;
            equiment.Region = equimentRegion;
            context.SaveChanges();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("장비 정보 변경 완료!");
            sb.AppendLine($"유저 ID: {user.ID}");
            sb.AppendLine($"캐릭터 ID: {equiment.CharacterID}");
            sb.AppendLine($"장비 ID: {equiment.EquipmentID}");
            sb.AppendLine($"장비 타입: {equiment.Type}");
            sb.AppendLine($"장비 부위: {equiment.Region}");

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }

        //14번 문제: ID의 유저의 캐릭터의 장비 삭제
        //유저 ID,캐릭터 ID, 장비 ID를 입력 받고, 각각 존재하지 않는다면 장비 삭제 불가
        //해당 ID들이 모두 존재한다면 입력한 장비 ID의 장비 삭제
        public static void UserCharacterEquimentDelete(Socket clientSocket, int userID, int characterID, int equimentID)
        {
            var context = CreateContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);
            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            var character = context.Character.FirstOrDefault(c => c.CharacterID == characterID && c.UserID == userID);

            if (character == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"CharacterID {characterID}의 캐릭터를 찾을 수 없습니다."));
                return;
            }

            var equiment = context.Equipment.FirstOrDefault(e => e.EquipmentID == equimentID && e.CharacterID == characterID);

            if (equiment == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"EquipmentID {equimentID}의 장비를 찾을 수 없습니다."));
                return;
            }

            context.Equipment.Remove(equiment);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"유저 {userID}의 캐릭터 {characterID}의 장비 {equimentID} 삭제 완료"));
        }

    }
}

MailService.cs

using Microsoft.EntityFrameworkCore;
using Server.Database;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Server.Services
{
    public class MailService
    {
        private static AppDBContext CreateContext() => new AppDBContext();

        //8. ID로 유저의 우편 리스트 조회
        // 어느 유저의 우편 리스트를 조회할지 ID를 받아오고, 해당 ID가 없다면 유저가 없다고 출력 및 해당 유저가 있지만 메일이 존재하지 않는다면 메일 없다고 출력
        // 해당 유저도 있고 메일도 존재한다면 메일 정보 조회.
        public static void UserMailSelect(Socket clientSocket, int userID) 
        {
            var context = CreateContext();

            var user = context.User.Include(u => u.Mails).FirstOrDefault(u => u.ID == userID);
            //Include -> 수업 때 배웠던 N+1 문제 방지. 유저의 메일 모두 갖고옴

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            if (user.Mails.Count == 0)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 메일이 없습니다."));
                return;
            }

            StringBuilder sb = new StringBuilder(); //행 마다 조회될 수 있으므로, SB로 누적해서 저장한 후 클라이언트에게 보냄

            foreach (var mail in user.Mails)
            {
                sb.AppendLine($"유저 ID : {userID}");
                sb.AppendLine($"메일 ID : {mail.MailID}");
                sb.AppendLine($"내용 : {mail.Detail}");
                sb.AppendLine($"받은 날짜 : {mail.ReceiveDate}");
                sb.AppendLine($"읽은 날짜 : {mail.ReadDate}");
            }

            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }
        //12. ID의 유저의 우편 정보 변경
        // 우편 정보 변경을 위해 어느 유저 ID를 입력 받고, 해당 유저 ID가 존재하지 않는다면 정보 변경 불가 
        // 유저 ID가 존재하여, 어느 메일의 정보를 변경할 지 메일 ID 입력을 받고, 해당 메일 ID가 존재하지 않는다면 정보 변경 불가 
        // 해당 메일 ID가 존재한다면 정보 변경
        public static void UserEmailUpdate(Socket clientSocket, int userID, int mailID, string detail) //유저의 메일 정보 변경
        {
            var context = CreateContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);

            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }

            var mail = context.Mail.FirstOrDefault(m => m.UserID == userID && m.MailID == mailID);

            if (mail == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 {mailID}가 존재하지 않으므로, 변경이 불가능 합니다."));
                return;
            }

            mail.Detail = detail;
            context.SaveChanges();

            StringBuilder sb = new StringBuilder();

            foreach (var m in user.Mails)
            {
                sb.AppendLine($"메일 ID: {m.MailID}");
                sb.AppendLine($"유저 ID: {m.UserID}");
                sb.AppendLine($"내용: {m.Detail}");
                sb.AppendLine($"받은 날짜: {m.ReceiveDate}");
                sb.AppendLine($"읽은 날짜: {m.ReadDate}");
                sb.AppendLine("------------------------------------");
            }
            clientSocket.Send(Encoding.UTF8.GetBytes(sb.ToString()));
        }

        //15번 문제 : ID의 유저의 우편 삭제
        // 유저 ID와 메일 ID 입력 받고, 해당 ID들이 모두 존재하지 않는다면 삭제 불가
        // 모두 존재한다면 입력한 메일 ID를 삭제
        public static void UserMailDelete(Socket clientSocket, int userID, int emialID)
        {
            var context = CreateContext();

            var user = context.User.FirstOrDefault(u => u.ID == userID);
            if (user == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 유저를 찾을 수 없습니다."));
                return;
            }
            var mail = context.Mail.FirstOrDefault(m => m.MailID == emialID && m.UserID == userID);

            if (mail == null)
            {
                clientSocket.Send(Encoding.UTF8.GetBytes($"MailID {emialID}의 메일을 찾을 수 없습니다."));
                return;
            }

            context.Mail.Remove(mail);
            context.SaveChanges();

            clientSocket.Send(Encoding.UTF8.GetBytes($"UserID {userID}의 메일 ID {emialID} 삭제 완료"));
        }


    }
}

Server.cs에서 리팩토링 필요 코드

  • 패킷의 내용을 분리하는 메서드에서 if문이 10개 이상이 된다. 리팩토링 해야 함.

리팩토링 시작

    1. 질문 번호를 정의할 CommandID와 실제 패킷 분리를 처리하고, 각 테이블에 맞는 서비스의 기능을 호출하는 구문을 작성하기 위해 Excute() 작성
    1. 인터페이스를 구현한다.
  • 유저가 로그인하는 질문은 1번이였고, parts[1]로 패킷을 분리했었다. 그리고 UserService의 UserLogin메서드를 호출
  • 유저가 탈퇴하는 질문은 2번이였고, parts[1]로 패킷을 분리 했었다. 그리고 UserService의 UserDisconnect 메서드를 호출
    1. int를 KEY, ICommandHandler(인터페이스)를 Value로 하는 딕셔너리 생성
    1. 딕셔너리에 데이터를 추가하는 메서드 호출. 호출할 때 해당 인터페이스를 구현하는 클래스들을 인자로 넘김
    1. 실제 딕셔너리에 데이터를 추가하고, 인터페이스의 객체 접근을 통해 CommandID에 맞는 클래스를 매핑
    1. 실제 딕셔너리에 Key에 맞는 handler를 반환 받고 해당 handler에 Execute()를 호출

다른 클래스들도 더 추가 해보자.


예시 캐릭터 클래스

  • 캐릭터 데이터 삽입
  • 캐릭터 테이블 조회
  • 캐릭터 데이터 변경

최종 클래스 구조

0개의 댓글