(AI 코딩) Cursor IDE와 AI 로 크로스 플랫폼 웹소켓 통신 구현하기

2025. 6. 16.

부제 : Node.js 웹 클라이언트 ↔ C# 윈도우 서버

오늘은 Cursor IDE의 강력한 AI 기능을 십분 활용하여, Node.js 기반의 웹 클라이언트와 C# 기반의 윈도우 애플리케이션 서버 간의 실시간 웹소켓(WebSocket) 통신을 구현하는 방법에 대해 심층적으로 알아보겠습니다.

 

두 개의 다른 언어와 환경에서 동작하는 프로젝트를 연결하는 것은 쉽지 않은 일이지만, Cursor IDE의 AI 짝꿍과 함께라면 훨씬 효율적이고 빠르게 목표를 달성할 수 있습니다!


🚀 왜 웹소켓 통신인가요?

일반적인 HTTP 통신은 클라이언트의 요청이 있을 때만 서버가 응답하는 단방향 통신입니다. 하지만 실시간 양방향 통신이 필요한 채팅, 주식 시세, 게임, 알림 서비스 등에서는 HTTP의 한계가 명확합니다.

 

**웹소켓(WebSocket)**은 이러한 문제를 해결하기 위해 등장했습니다. 한 번 연결이 수립되면 클라이언트와 서버가 언제든지 데이터를 주고받을 수 있는 전이중(Full-Duplex) 통신 채널을 제공하여, 진정한 실시간 애플리케이션 구현을 가능하게 합니다.


🛠️ 프로젝트 목표 및 아키텍처

우리의 목표는 다음과 같습니다:

  • Node.js 웹 프로그램 (클라이언트): 브라우저에서 동작하며, 사용자 입력을 받아 웹소켓 서버로 전송하고, 서버로부터 메시지를 받아 화면에 표시합니다.
  • C# 윈도우 프로그램 (서버): 백그라운드에서 웹소켓 서버로 동작하며, 클라이언트로부터 메시지를 수신하고, 특정 응답을 클라이언트로 다시 전송합니다.

통신 흐름:

  1. C# 윈도우 서버가 특정 포트에서 웹소켓 연결을 기다립니다.
  2. Node.js 웹 클라이언트(브라우저)가 해당 포트로 웹소켓 연결을 시도합니다.
  3. 연결이 성공하면, 클라이언트와 서버 간에 양방향 메시지 교환이 가능해집니다.
  4. 클라이언트가 메시지를 보내면, 서버는 이를 수신하고 다시 클라이언트로 응답을 보냅니다.

✨ 준비물

시작하기 전에 필요한 것들을 확인해주세요:

  1. Cursor IDE: (필수) 이 가이드의 핵심 도구입니다. 공식 웹사이트 (https://cursor.sh/) 에서 다운로드 및 설치해주세요.
  2. Node.js: 웹 클라이언트 프로젝트를 실행하고 웹 서버를 호스팅하는 데 필요합니다.
  3. .NET SDK: C# 윈도우 서버 프로젝트를 빌드하고 실행하는 데 필요합니다.
  4. 웹 브라우저: 웹 클라이언트 프로그램 테스트를 위해 필요합니다.

🧑‍💻 Cursor IDE와 함께 프로젝트 시작하기 (단계별 심층 가이드)

Cursor IDE의 AI 기능을 적극 활용하여 두 개의 분리된 프로젝트를 효율적으로 생성하고 코딩하는 과정을 보여드리겠습니다.

1. 프로젝트 폴더 구조 설정

먼저, 두 프로젝트를 담을 부모 폴더를 생성합니다.

      your-websocket-project/
├── websocket-server-csharp/  # C# 윈도우 서버 프로젝트
└── websocket-client-nodejs/  # Node.js 웹 클라이언트 프로젝트
    

Cursor IDE를 열고 your-websocket-project 폴더를 워크스페이스로 추가합니다. (File > Open Folder)

 

2. C# 윈도우 웹소켓 서버 구현

이제 websocket-server-csharp 폴더 내부에 C# 콘솔 애플리케이션 형태의 웹소켓 서버를 구현합니다.

 

Cursor IDE 활용:

  1. 폴더 생성 및 프로젝트 스캐폴딩:
    • websocket-server-csharp 폴더를 우클릭하고 "New Folder"를 선택하여 MyWebSocketServer 폴더를 만듭니다.
    • MyWebSocketServer 폴더를 선택한 상태에서 Cmd + K (Ctrl + K) 를 눌러 AI 채팅창을 엽니다.
    • 프롬프트 입력: "Create a C# .NET 8 console application project here that will act as a WebSocket server. Use System.Net.WebSockets to listen on http://localhost:8080/ws/. It should accept client connections, receive text messages, print them to the console, and then send back an acknowledgement message to the client."
    • Cursor는 .csproj 파일과 Program.cs 파일을 포함한 기본적인 프로젝트 구조를 제안할 것입니다. Enter를 눌러 제안을 수락하여 파일을 생성합니다.
  2. Program.cs 코드 상세 작성:
    • Cursor가 생성해준 Program.cs 파일 내용을 AI의 도움을 받아 완성합니다.
    • 기본 구조 확인 및 추가 요청: Cursor가 제안한 Program.cs 파일을 열고, 웹소켓 서버 로직이 비어있거나 불완전하다면 AI에게 추가 요청합니다.
    • Program.cs 열기:
    using System;
    using System.Net;
    using System.Net.WebSockets;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    public class Program
    {
        private static async Task Main(string[] args)
        {
            Console.WriteLine("C# WebSocket Server Starting...");
            await StartWebSocketServer();
        }
    
        private static async Task StartWebSocketServer()
        {
            // Cmd + K 또는 Inline Edit (Cmd + E) 로 이 부분을 채울 수 있습니다.
            // 예시 프롬프트: "Implement a WebSocket server using HttpListener on http://localhost:8080/ws/. Accept connections, handle messages, and send acknowledgements."
            
            var httpListener = new HttpListener();
            httpListener.Prefixes.Add("http://localhost:8080/ws/"); // 웹소켓 경로
            httpListener.Start();
            Console.WriteLine("Listening for WebSocket connections on http://localhost:8080/ws/");
    
            while (true)
            {
                HttpListenerContext context = await httpListener.GetContextAsync();
                if (context.Request.IsWebSocketRequest)
                {
                    ProcessWebSocketRequest(context);
                }
                else
                {
                    // 웹소켓 요청이 아닌 경우 400 Bad Request 반환
                    context.Response.StatusCode = 400;
                    context.Response.Close();
                }
            }
        }
    
        private static async void ProcessWebSocketRequest(HttpListenerContext context)
        {
            // Cmd + K 또는 Inline Edit (Cmd + E) 로 이 부분을 채울 수 있습니다.
            // 예시 프롬프트: "Complete the ProcessWebSocketRequest method. It should accept the WebSocket, receive messages, print them, and echo back a response."
    
            WebSocketContext webSocketContext = null;
            try
            {
                webSocketContext = await context.AcceptWebSocketAsync(subProtocol: null);
                WebSocket webSocket = webSocketContext.WebSocket;
    
                Console.WriteLine($"Client connected: {context.Request.RemoteEndPoint}");
    
                byte[] buffer = new byte[1024];
                WebSocketReceiveResult receiveResult = null;
    
                while (webSocket.State == WebSocketState.Open)
                {
                    receiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
    
                    if (receiveResult.MessageType == WebSocketMessageType.Text)
                    {
                        string receivedMessage = Encoding.UTF8.GetString(buffer, 0, receiveResult.Count);
                        Console.WriteLine($"Received from client ({context.Request.RemoteEndPoint}): {receivedMessage}");
    
                        // 클라이언트로 응답 메시지 전송
                        string responseMessage = $"Server received: '{receivedMessage}' at {DateTime.Now}";
                        byte[] sendBuffer = Encoding.UTF8.GetBytes(responseMessage);
                        await webSocket.SendAsync(new ArraySegment<byte>(sendBuffer, 0, sendBuffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);
                        Console.WriteLine($"Sent to client ({context.Request.RemoteEndPoint}): {responseMessage}");
                    }
                    else if (receiveResult.MessageType == WebSocketMessageType.Close)
                    {
                        // 클라이언트가 연결을 닫았을 때
                        Console.WriteLine($"Client disconnected: {context.Request.RemoteEndPoint} (Code: {receiveResult.CloseStatus}, Reason: {receiveResult.CloseStatusDescription})");
                        await webSocket.CloseAsync(receiveResult.CloseStatus.Value, receiveResult.CloseStatusDescription, CancellationToken.None);
                        break;
                    }
                }
            }
            catch (WebSocketException ex)
            {
                // 웹소켓 통신 중 발생하는 예외 처리
                Console.WriteLine($"WebSocket error: {ex.Message}");
            }
            catch (Exception ex)
            {
                // 그 외 일반 예외 처리
                Console.WriteLine($"General error: {ex.Message}");
            }
            finally
            {
                webSocketContext?.WebSocket?.Dispose(); // 리소스 해제
                Console.WriteLine($"Connection closed for client: {context.Request.RemoteEndPoint}");
            }
        }
    }

빌드 및 실행:

  • 터미널을 열고 websocket-server-csharp/MyWebSocketServer 폴더로 이동합니다.
  • dotnet build 명령어로 프로젝트를 빌드합니다.
  • dotnet run 명령어로 서버를 실행합니다.
  • 팁: Cursor의 통합 터미널(Ctrl + ~)을 사용하면 편리합니다. AI에게 "How to build and run this C# project?"라고 물어볼 수도 있습니다.

 

 

3. Node.js 웹소켓 클라이언트 구현

다음으로 websocket-client-nodejs 폴더 내부에 Node.js를 이용한 간단한 웹 서버와 브라우저에서 동작하는 웹소켓 클라이언트를 구현합니다.

 

Cursor IDE 활용:

  1. 프로젝트 초기화 및 파일 생성:
    • websocket-client-nodejs 폴더를 선택하고 Cmd + K (Ctrl + K) 를 누릅니다.
    • 프롬프트 입력: "Initialize a Node.js project for a web client that connects to a WebSocket server. Create an index.html file with a simple chat interface (input field, send button, message display area) and a client.js file to handle the WebSocket connection and UI logic. Also, create a server.js using Express to serve index.html."
    • Cursor가 package.json, index.html, client.js, server.js 파일 생성을 제안합니다. Enter로 수락합니다.

  2. package.json 설정 (Cursor가 자동 생성):
    • 터미널에서 websocket-client-nodejs 폴더로 이동하여 npm install을 실행하여 Express를 설치합니다.
      {
  "name": "websocket-client-nodejs",
  "version": "1.0.0",
  "description": "Node.js web client for WebSocket communication",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.19.2" // Cursor가 자동으로 추가해 줄 것입니다.
  }
}
    
  1. server.js (웹 페이지 호스팅):
    • Cursor가 생성해준 server.js 파일을 엽니다.
    • Cursor에게 요청: "Complete this server.js to serve index.html from the current directory on port 3000."
          const express = require('express');
    const path = require('path');
    const app = express();
    const port = 3000;
    
    // index.html 파일을 호스팅합니다.
    app.get('/', (req, res) => {
        res.sendFile(path.join(__dirname, 'index.html'));
    });
    
    app.listen(port, () => {
        console.log(`Node.js Web Server listening at http://localhost:${port}`);
        console.log('Open your browser and navigate to this address.');
    });
        
  2. index.html (클라이언트 UI):
    • Cursor가 생성해준 index.html 파일을 엽니다.
    • Cursor에게 요청: "Add basic HTML structure for a chat interface: a div for messages, an input field for typing, and a send button. Link client.js."
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>WebSocket Client</title>
        <style>
            body { font-family: Arial, sans-serif; margin: 20px; }
            #messages { border: 1px solid #ccc; padding: 10px; height: 300px; overflow-y: scroll; margin-bottom: 10px; }
            #messageInput { width: calc(100% - 80px); padding: 8px; }
            #sendButton { width: 70px; padding: 8px; }
        </style>
    </head>
    <body>
        <h1>WebSocket Chat</h1>
        <div id="messages"></div>
        <input type="text" id="messageInput" placeholder="Type your message...">
        <button id="sendButton">Send</button>
    
        <script src="client.js"></script>
    </body>
    </html>
  3. client.js (웹소켓 클라이언트 로직):
    • Cursor가 생성해준 client.js 파일을 엽니다.
    • Cursor에게 요청: "Write JavaScript code to connect to a WebSocket server at ws://localhost:8080/ws/. Handle onopen, onmessage, onerror, onclose events. When the 'Send' button is clicked, send the input field's value to the server and clear the input. Display all received messages in the #messages div."
    document.addEventListener('DOMContentLoaded', () => {
        const messagesDiv = document.getElementById('messages');
        const messageInput = document.getElementById('messageInput');
        const sendButton = document.getElementById('sendButton');
    
        // C# 서버의 웹소켓 주소
        const socket = new WebSocket('ws://localhost:8080/ws/');
    
        // 연결이 열렸을 때
        socket.onopen = (event) => {
            console.log('WebSocket connection opened:', event);
            addMessage('System: Connected to WebSocket server.');
        };
    
        // 메시지를 받았을 때
        socket.onmessage = (event) => {
            console.log('Message from server:', event.data);
            addMessage(`Server: ${event.data}`);
        };
    
        // 에러가 발생했을 때
        socket.onerror = (event) => {
            console.error('WebSocket error:', event);
            addMessage('System: WebSocket error occurred.');
        };
    
        // 연결이 닫혔을 때
        socket.onclose = (event) => {
            console.log('WebSocket connection closed:', event);
            addMessage(`System: Disconnected from WebSocket server. Code: ${event.code}, Reason: ${event.reason}`);
        };
    
        // 메시지 전송 버튼 클릭 이벤트
        sendButton.addEventListener('click', () => {
            sendMessage();
        });
    
        // Enter 키를 눌렀을 때 메시지 전송
        messageInput.addEventListener('keypress', (event) => {
            if (event.key === 'Enter') {
                sendMessage();
            }
        });
    
        function sendMessage() {
            const message = messageInput.value.trim();
            if (message && socket.readyState === WebSocket.OPEN) {
                socket.send(message);
                addMessage(`You: ${message}`); // 내가 보낸 메시지도 화면에 표시
                messageInput.value = ''; // 입력 필드 초기화
            } else if (socket.readyState !== WebSocket.OPEN) {
                addMessage('System: Not connected to server. Please wait or refresh.');
            }
        }
    
        function addMessage(message) {
            const p = document.createElement('p');
            p.textContent = message;
            messagesDiv.appendChild(p);
            // 스크롤을 항상 최하단으로 유지
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }
    });
  4. Node.js 웹 서버 실행:
    • 터미널을 열고 websocket-client-nodejs 폴더로 이동합니다.
    • npm start 명령어로 Node.js 서버를 실행합니다.
    • 팁: Cursor의 통합 터미널에서 Cmd/Ctrl + Shift + P -> "Terminal: Create New Terminal"로 새 터미널을 열고 각 프로젝트 폴더에서 실행할 수 있습니다.

🧪 테스트 및 확인

이제 두 프로젝트가 정상적으로 통신하는지 확인해볼 차례입니다.

  1. C# 윈도우 서버 실행:
    • websocket-server-csharp/MyWebSocketServer 폴더의 터미널에서 dotnet run 실행.
    • 콘솔에 "C# WebSocket Server Starting..." 메시지가 표시되어야 합니다.
  2. Node.js 웹 클라이언트 서버 실행:
    • websocket-client-nodejs 폴더의 터미널에서 npm start 실행.
    • 콘솔에 "Node.js Web Server listening at http://localhost:3000" 메시지가 표시되어야 합니다.
  3. 웹 클라이언트 접속:
    • 웹 브라우저를 열고 http://localhost:3000으로 접속합니다.
    • "WebSocket Chat" 페이지가 나타나고, client.js가 자동으로 ws://localhost:8080/ws/ 서버에 연결을 시도할 것입니다.
    • C# 서버 콘솔에 "Client connected: [클라이언트 IP:포트]"와 같은 메시지가 표시되면 연결 성공입니다.
  4. 메시지 전송 및 확인:
  • 웹 브라우저의 메시지 입력란에 메시지를 입력하고 "Send" 버튼을 클릭합니다.
  • 클라이언트 UI에 보낸 메시지가 표시되고, 곧이어 C# 서버에서 보낸 응답 메시지가 표시될 것입니다.
  • C# 서버 콘솔에도 "Received from client: [메시지]" 및 "Sent to client: [응답 메시지]" 로그가 찍히는 것을 확인할 수 있습니다.

 

 

💡 Cursor IDE의 AI 파워 활용 팁

이 과정을 통해 Cursor IDE의 AI 기능이 어떻게 여러분의 개발 워크플로우를 가속화하는지 경험하셨을 것입니다. 몇 가지 팁을 추가합니다:

  • 다국어 프로젝트 관리: Cursor는 여러 언어를 동시에 이해하고 제안하므로, 이처럼 C#과 JavaScript/Node.js를 넘나드는 프로젝트에서 빛을 발합니다.
  • 오류 진단 및 수정: 통신 중 오류가 발생하면, C# 서버의 터미널 로그나 브라우저의 개발자 도구 콘솔에 나타난 오류 메시지를 Cmd + K로 복사-붙여넣기 하여 Cursor에게 "Fix this error" 또는 "Explain this error"라고 물어보세요. 놀라운 해결책을 제안할 수 있습니다.
  • 코드 리팩토링 및 개선: Cmd + E (인라인 편집)를 사용하여 특정 코드 블록을 선택하고 "Make this code more robust by adding error handling for network disconnections" (네트워크 연결 끊김에 대한 예외 처리를 추가하여 이 코드를 더 견고하게 만들어줘)와 같이 요청하여 코드 품질을 향상시킬 수 있습니다.
  • 컨텍스트 제공: AI에게 질문할 때, 관련 있는 Program.cs 파일과 client.js 파일을 함께 컨텍스트 윈도우(Cmd + K 하단)에 추가하면, AI가 두 파일의 내용을 동시에 이해하여 더 정확하고 통합적인 답변을 제공할 수 있습니다.

맺음말

Cursor IDE는 단순한 코드 편집기를 넘어, 마치 경험 많은 시니어 개발자가 항상 여러분 옆에서 페어 프로그래밍을 해주는 것과 같은 경험을 제공합니다. Node.js 웹 클라이언트와 C# 윈도우 서버 간의 웹소켓 통신 구현은 복잡한 작업처럼 보이지만, Cursor의 강력한 AI 지원 덕분에 훨씬 더 효율적이고 빠르게 구현할 수 있었습니다.

 

이 가이드가 여러분의 크로스 플랫폼 개발 여정에 도움이 되기를 바랍니다. Cursor IDE와 함께라면, 여러분은 더 적은 노력으로 더 많은 것을 코딩할 수 있을 것입니다.

댓글