[포스코x코딩온] 웹 풀스택 과정 9주차 회고 | Socket

TCP/IP
인터넷과 이와 유사한 컴퓨터 네트워크 사이에서 정보를 주고받는 데 이용되는 통신 프로토콜의 모음.
TCP
TCP (Transmission Control Protocol): 전송 제어 프로토콜
하나의 기기에서 다른 기기로 데이터를 전송하는 것을 담당한다.
데이터를 '신뢰성 있게 전송'하기 위한 프로토콜!
- 신뢰성: 데이터의 손실이나 손상을 최소화 & 데이터의 순서를 보장
- 연결 지향: 데이터를 주고받기 전에 송신자와 수신자 같에 연결
- 흐름 제어: 데이터의 흐름을 제어하여, 수신자가 처리할 수 있는 속도에 맞춰 데이터를 전송
- 혼잡 제어: 네트워크의 혼잡 상태를 감지하고 조절하여 네트워크 성능을 유지
IP
IP (Internet Protocol): 인터넷 프로토콜
인터넷 상에서 데이터를 주고받기 위한 통신 규약(약속)이다.
- 패킷 기반: 데이터를 작은 패킷 단위로 나누어 전송 (각 패킷에는 목적지, 출발지 주소 정보가 포함됨)
- 비연결성: 패킷은 독립적으로 처리되며, 수신자와 직접적인 연결 필요없음
- 라우팅: 각 라우터가 패킷의 경로를 결정하여 목적지까지 전달
- IP 주소: IP는 각 컴퓨터를 식별하기 위하여 IP주소를 사용함
TCP/IP 4계층
효율적인 데이터 전송을 위해 프로토콜을 설계한 것. (더 세분화되면 7계층)

네트워크 인터페이스 계층 (Network Interface Layer)
- 물리적인 네트워크와 상호작용하는 계층
- 데이터를 프레임으로 나누어 전송하고, 프레임을 수신하여 물리적인 신호로 변환하는 역할을 함
- MAC (Media Access Control) 주소를 관리함
- Ethernet, Wi-fi 등의 기술이 이 계층에 해당됨
인터넷 계층 (Internet Layer)
- IP가 속하는 계층
- IP 프로토콜이 이 계층에서 작동하며, 패킷의 출발지와 목적지 IP 주소를 이용하여 라우팅을 수행함
- IP 주소와 관련된 서비스인 ARP(Address Resoultion Protocol) 등의 프로토콜도 이 계층에서 동작함.
전송 계층 (Transport Layer)
- 데이터의 신뢰성과 흐름 제어를 관리하는 계층
- TCP(Transmission Control Protocol), UDP(User Datagram Protocol)가 작동
- UDP: 비신뢰적인 데이터 전송 (순서에 상관없이 빠르게 데이터를 전송. 처리하는 작업이 적음)
응용 계층 (Application Layer)
- 최종 사용자에게 서비스를 제공하기 위한 응용프로그램, 사용자 인터페이스가 위치하는 계층
- HTTP, FTP, SMTP, POP3, IMAP, DNS 등의 다양한 프로토콜이 동작 (각 프로토콜은 응용 서비스를 제공하는 목적으로 사용됨)
소켓
네트워크 상에서 데이터 교환이 가능하도록 하며, 응용 계층과 전송 계층 사이에서 작용한다.
- TCP와 UDP 프로토콜을 사용하여 데이터를 전송한다.
- 소켓은 프로토콜, IP주소, 포트넘버로 정의된다.
소켓 프로그래밍
- 서버 소켓: 클라이언트의 연결 요청을 받아들여, 실제 통신을 위한 소켓을 생성한다.
- 클라이언트 소켓: 서버에 연결을 요청하고, 연결이 수락되면 서버와 데이터를 주고받을 수 있는 소켓이다.
- 포트: 컴퓨터 내에서 소프트웨어 간 통신을 할 때 사용되는 식별자로, 포트를 이용하여 특정 소켓을 찾고 연결한다.
소켓 프로그래밍의 흐름

- 서버
socket(): Socket 생성 함수
bind(): IP, PORT 번호 설정 함수
listen(): 클라이언트의 요청에 대하여 수신 대기열을 만드는 함수
accept(): 클라이언트와의 연결을 기다리는 함수
→ 통신 시도 시, 서버가 accept()함수를 이용하여 클라이언트의 socket descriptor을 반환함 (socket descriptor는 어떤 클라이언트가 접속하고 있는지 정보를 담고 있음)
→ socket descriptor을 통해 클라이언트와 서버가 서로 read(), write()를 반복하며 통신 - 클라이언트
socket(): 소켓을 여는 함수
connect(): 통신할 서버의 설정된 IP와 PORT 번호에 통신을 시도하는 함수
웹소켓
요청 후 응답을 받으면 끝나는 http와 달리, 웹소켓은 한번 요청을 받으면 closed 되기 전까지 웹소켓 연결된 상태가 유지된다.

- Handshake(핸드셰이크): 클라이언트와 서버가 연결된 것을 의미한다. (클라이언트가 서버로 웹소켓 연결을 요청하여, 서버와 클라이언트 간에 초기 핸드셰이크가 이루어지면서 웹소켓이 연결됨)
클라이언트
클라이언트 측에서는, 브라우저의 WebSocket 객체를 사용하여 웹소켓 연결을 생성하고 관리한다.
const socket = new WebSocket("ws://localhost:8000");
WebSocket 이벤트
open: 웹소켓 연결이 성공적으로 열렸을 때 발생
socket.addEventListener("open",(event) => {
console.log("서버에 연결되었습니다.");
socket.send("Hello, Server");
});
message: 웹소켓을 통해 데이터를 주고받을 때 발생
socket.addEventListner("message", (event) => {
console.log(event);
console.log(`서버로부터 받은 메시지: ${event.data}`)
});
error: 웹소켓 연결 중 오류가 발생했을 때 발생 (연결 실패, 통신 오류 등)
socket.addEventListener("error", (event) => {
console.log("오류가 발생하였습니다.", event.error
});
close: 웹소켓 연결이 종료되었을 때 발생
socket.addEventListener("close", (event) => {
console.log("서버와 연결이 종료되었습니다.");
});
서버
서버 측에서는, 별도의 라이브러리나 모듈을 설치하여 웹소켓을 관리한다. (npm install ws)
const ws = require("ws");
서버 접속
const PORT = 8000;
const server = app.listen(PORT, () => {
console.log(`http://localhost:${PORT}`)
});
const wss = new ws.Server({ server });
ws 모듈 이벤트
connection: 클라이언트가 웹소켓 서버에 연결되었을 때 발생. (이 이벤트의 콜백함수는 새로운 클라이언트 연결마다 실행됨)
message: 클라이언트로부터 메시지를 받았을 때 발생
error: 웹소켓 연결 중 오류가 발생했을 때 발생
close: 클라이언트와 연결이 종료되었을 때 발생
// 서버 실행
// socket: 접속한 브라우저를 의미함
wss.on("connection", (socket) => {
console.log("클라이언트가 연결되었습니다.");
socket.on("message", (message) => {
console.log(`클라이언트로부터 받은 메시지: ${message}`);
socket.send(`서버메시지: ${message}`);
});
socket.on("error", (err) => {
console.log("에러가 발생했습니다.", err);
});
socket.on("close", () => {
console.log("클라이언트와 연결이 종료되었습니다.");
});
});
여러 개의 브라우저에 동시 적용하려면?
배열을 만들어, 브라우저 접속할 때마다 배열에 socket을 추가한다. 메시지를 전송할 때, 해당 배열에 있는 모든 socket에 전송하면 된다.
const sockets = [];
wss.on("connection", (socket) => {
sockets.push(socket);
socket.on("message", (message) => {
sockets.forEach((elem) => {
elem.send(`서버메세지: ${message}`);
});
});
});
클라이언트로부터 여러 메시지를 받을 때
하나로 묶어서 서버로 전송해야 하는데, 모든 서버가 객체를 받을 수 있는 것이 아니기 때문에 JSON.stringify()으로 문자열로 변환 후 전달해야 한다.
서버는 이렇게 전달받은 데이터를 JSON.parse()으로 읽을 수 있는 형태로 parsing 하여 처리한다.
추가) 실습을 통해 알게된 부분 - 폼의 submit 방식
1) button type을 submit으로 설정한다.
2) form에 onsubmit="" 이벤트 추가
3) 폼을 가져와 addEventListener("submit", 콜백함수) 으로 이벤트 추가
※ submit 이벤트로 데이터 전송할 때, 페이지 새로고침되지 않도록 event.preventDefault(); 작성해줘야 함.