개발일지/TIL(Today I Learned)

2025-01-13 <네트워크 WRAP-UP과 중앙처리장치(CPU)>

프린스 알리 2025. 1. 13.

내일배움캠프 Node.js 트랙 53일차

네트워크 WRAP-UP

전송계층 프로토콜

전송 계층은 네트워크 계층과 응용 계층 사이에 존재하는 통신 계층입니다. 기존에 네트워크 계층의 프로토콜이 가지고 있던 약점을 보완해 주기도 하고, 또 응용계층의 하위 계층으로서 그 기반 역할을 수행합니다.

 

여기서 네트워크 계층이 가진 한계가 무엇이냐 하면, 최선형 전달 방식에 비연결 지향 통신이라고 말할 수 있겠습니다. 말 그대로 최선을 다해서 호스트에서 호스트까지 패킷을 전송은 하지만 어플리케이션 레벨에서 정말로 패킷이 전송 되었는지는 보장하지 않습니다. 중간에 패킷이 손실되거나 잘못 전송되어도 이를 확인하거나 복구할 기능이 포함되어 있지 않습니다. 물론 이런 방식이 마냥 나쁜 것만은 아닙니다. 동영상 스트리밍처럼 때론 신뢰성보다도 속도가 더 중요할 때도 있기 때문입니다.

 

이런 약점을 보완하기 위해 상위 계층인 전송 계층에서는 두 가지 종류의 통신을 지원하는데요. 하나는 TCP 통신이고, 또 하나는 UDP 통신입니다. TCP는 신뢰할 수 있는 통신과 연결형 통신을 지원하고, UDP는 신뢰할 수 없고 비연결형 통신이지만, 비교적 빠른 전송을 가능케 합니다.

 

TCP와 UDP는 소켓이라는 인터페이스, 쉽게 말해 논리적 연결부를 통해 메시지를 송수신합니다. 이 소켓의 종류가 소켓 스트림이냐 소켓 데이터그램이냐에 따라서 통신 방식이 결정됩니다. 소켓 주소는 IP주소와 포트 주소로 이루어져 있는데요. 여기서 이 포트 주소가 전송계층에서 특정한 애플리케이션을 식별하는 세부 주소입니다. 이를 테면 HTTP의 포트 번호는 80이고 HTTPS의 포트 번호는 443입니다. 이런 포트들을 잘 알려진 포트들이라고 부릅니다. 전송 계층에서는 데이터의 헤더에 송신 측과 수신 측의 소켓 주소를 작성하기 때문에 정확한 어플리케이션으로 메시지가 전달될 수 있게끔 지원합니다.

 

만약 소켓이 스트림 타입이라면 TCP 통신이 이루어질 테고 우리가 흔히 알고 있는 TCP 연결 수립 과정이 이루어집니다. 3 웨이 핸드셰이크 방식이죠. 클라이언트가 SYN 플래그를 1로 설정하고 패킷을 보내면 서버가 ACK와 SYN 패킷을 보내 연결을 허락합니다. 클라이언트가 서버의 응답을 받고 ACK 패킷을 보내면 두 소켓 간에 논리적인 연결이 수립됩니다.

TCP 세그먼트 구조

TCP가 가진 기능 중에 애플리케이션을 식별하는 것 외에도 중요한 것이 크게 세 가지가 있습니다. 오류 제어, 흐름 제어, 혼잡 제어입니다. 이 세 역할에 대해 설명드리기 앞서 TCP 프로토콜의 데이터 전송 단위인 세그먼트의 구조에 대해 간략하게 말씀을 드리고 싶은데요. TCP 세그먼트에는 다양한 제어비트 필드와 순서 번호 필드, 그리고 확인 응답 번호 필드가 존재합니다.

 

제어 비트는 앞서 쓰리 웨이 핸드셰이크에서 말씀드렸던 것처럼 SYN, ACK, FIN 등등 TCP 연결과 세그먼트 승인을 위한 비트 필드, 쉽게 말해 플래그입니다. 순서 번호는 세그먼트의 올바른 송수신 순서를 보장하기 위한 번호로, 세그먼트 데이터의 첫 바이트에 부여되는 번호입니다. 맨 처음에, 그러니까 TCP 연결을 수립하기 위해 SYN 플래그를 1로 변경해서 보낸다고 했는데요. 이 경우 초기 순서 번호로 랜덤한 값으로 가지게 됩니다. 그리고 그 다음 세그먼트의 순서 번호는 초기 순서 번호에다가 송신한 바이트를 더해 가면서 누적값을 가지게 됩니다.

 

확인 응답 번호는 순서 번호에 대한 응답입니다. “다음에는 이걸 보내 주세요"를 나타내는 값입니다. 일반적으로 순서 번호 + 1로 설정합니다.

TCP 오류 제어

그런데 만약 전송 과정에 문제가 생긴다면, 순서 번호가 n번째인 패킷을 보냈는데 n+1번째 확인 응답이 안 오는 경우가 있을 수 있습니다. 혹은 n번째 패킷을 이미 보냈음에도 수신 측에서 계속 n+1번째 패킷을 달라고 확인 응답을 중복해서 보낼 수도 있습니다. 이런 경우 TCP는 재전송 기반으로 오류 제어를 하게 됩니다. 이런 기법을 ARQ라고 부르는데요. ARQ에도 몇 가지 종류가 있습니다.

 

Stop and Wait ARQ : 제대로 전달되었음을 확인 응답을 받을 때까지 그 다음 메시지를 아예 보내지 않습니다. 높은 신뢰성을 가지지만 그만큼 효율이 낮아서 성능이 저하됩니다. 그래서 현실적으로 자주 사용되는 방식은 이 Stop and Wait ARQ가 아니라 연속적으로 메시지를 보내는 파이프라이닝 방식을 취하게 됩니다.


Go Back N ARQ : 파이프라이닝을 기반으로 합니다. n부터 n+10까지 세그먼트를 보낼 때 오류가 발생하면 해당 세그먼트부터 전부 재전송을 하는 방식입니다. 즉 순서 번호 n에 대한 확인 응답이 아닌 n까지 누적된 확인 응답을 보낸다고 볼 수 있습니다.


Selective Repeat ARQ : 역시 파이프라이닝을 기반으로 합니다.  반대로 Selective Repeat ARQ는 개별적인 확인 응답 번호를 보내주기 때문에 n+2번째 세그먼트가 누락되었다면 해당하는 세그먼트만 골라서 재전송을 하게 됩니다.

TCP 흐름제어 & 혼잡제어

하지만 파이프 라이닝을 통해 메시지를 연속적으로 보내기 위해선 고려해야 하는 점이 있습니다. 바로 수신 호스트가 한 번에 얼마만큼의 데이터를 처리할 수 있는지를 파악하는 것입니다. 물리적으로 그 양에는 한계가 있을 수밖에 없기 때문입니다. TCP의 흐름 제어 기법에서는 송신 호스트가 파이프라이닝할 수 있는 처리량을 윈도우라고 부릅니다. 수신 측 헤더에 적혀있는 이 윈도우 사이즈를 바탕으로, 송신측은 윈도우를 슬라이딩 형식으로 한 세그먼트씩 이동시키면서 메시지를 전송하게 됩니다.

 

혼잡 제어에서도 마찬가지로 혼잡 윈도우라는 걸 사용하는데요. 다만 이 경우엔 호스트와 호스트 간의 데이터 처리량을 고려하는 게 아니라 호스트와 네트워크 간에 주고 받을 수 있는 세그먼트의 양을 고려하게 됩니다. 수신 윈도우의 경우는 수신 호스트가 헤더를 통해 알려주지만 혼잡 윈도우는 송신 측에서 알아서 계산해야 합니다.

 

이 계산을 위해 가장 기본적으로 쓰이는 알고리즘이 AIMD인데요. 보내는 패킷의 양을 일정한 방식으로 증가시키다가 혼잡이 감지되면 혼잡 윈도우의 크기를 적절하게 줄이는 방식입니다. 느린 시작 알고리즘의 경우 혼잡 윈도우를 1부터 시작해서 2배씩 증가시킵니다. 그러다가 임계치에 다다르면 혼잡 회피 알고리즘을 수행합니다. 타임아웃이 발생하면 느린 시작을 아예 새로 시작합니다. 중복된 확인 응답을 수신하면 빠른 회복 알고리즘을 수행하게 됩니다.

 

혼잡 회피 알고리즘은 임계치에 다다라 혼잡의 여지가 있기에 2배씩 증가시키던 혼잡 윈도우의 양을 다시 선형적으로 증가시키는 방식입니다. 빠른 회복 알고리즘도 마찬가지로 혼잡 회피 알고리즘을 수행하는데 만약 중복 응답을 또 다시 받게 되면 타임아웃과 마찬가지로 다시 느린 시작을 수행합니다.

대칭키와 비대칭키

대칭키와 비대칭키는 키 암호화 방식에서 사용되는 두 가지 종류의 알고리즘입니다. 키 암호화는 평문 텍스트를 암호화할 때 키라고 불리는 문자열을 이용하는데요. 만약 암호화와 복호화에 같은 키가 사용된다면 그것을 대칭키, 서로 다른 키가 사용된다면 비대칭키라고 부르게 됩니다. 또 비대칭키는 개인만 가지고 있는 개인키와 외부에 자유롭게 공개하는 공개키로 나누어집니다. 개인키로 암호화를 했다면 공개키로 복호화가 가능하고 공개키로 암호화를 했다면 개인키로 복호화가 가능한 방식입니다.

 

대칭키는 서로 같은 키를 사용하기 때문에 암호화 및 복호화를 빠르게 수행할 수 있다는 장점이 있습니다. 그러나 대칭키를 전달하는 과정에서 해킹에 노출되었을 때 보안의 위험성이 커집니다.

 

반면 비대칭키는 서로 다른 키를 사용하기 때문에 암호화 및 복호화에 시간과 부하가 상대적으로 많이 들지만, 키를 안전하게 공유할 수 있습니다.

이 둘의 장점을 합치기 위해 대칭 키 암호화 방식과 공개 키 암호화 방식을 함께 사용하는 경우가 많습니다.
예를 들어 A와 B가 암호화 통신을 한다고 가정해보면, 이 둘은 빠르고 효율적인 대칭키를 이용해서 통신을 하고 싶을 것입니다. 하지만 대칭키를 주고 받는 과정을 안전하게 수행하기 위해 비대칭키를 이용합니다.

1. A가 앞으로 사용할 대칭키를 B의 공개키로 암호화하고 B에게 보냄
2. B는 암호문을 받고, 자신의 개인키로 복호화함
3. B는 이렇게 얻은 대칭키로 A에게 보낼 평문을 암호화하여 A에게 보냄
4. A는 자신이 보냈던 대칭키로 암호문을 복호화함
5. 앞으로 이 대칭키로 암호화를 통신함

인증서

다만 클라이언트 A가 서버 B의 공개키를 서버로부터 제공받을 때, 전달 받은 공개 키가 정말 신뢰할 수 있는 것인지, 전송 도중에 조작되지는 않았는지 확신하기 어렵습니다.

 

그래서 서버는 공개 키 뿐만 아니라 누가 생성했는지, 조작되지는 않았는지, 유효 기간은 언제까지인지 등의 내용을 포함한 인증서를 전송합니다. 이러한 인증서는 인증 기관(Certification Authority, CA)이라는 제3의 기관에서 발급합니다.

 

CA가 발급한 인증서에는 서명 값(Signature)이 있습니다. 서명 값은 ‘인증서 내용에 대한 해시 알고리즘을 통해 처리한 해시 값을 CA의 개인 키로 암호화’하여 만들어 집니다. 암호화에서 해시 알고리즘은 같은 입력값에 대해서 같은 출력값을 보장하데요. 인증서의 내용을 해싱한 걸 암호화해서 클라이언트에게 인증서와 함께 전송하는 것입니다.

 

그리고 클라이언트는 CA의 공개 키로 복호화해서 암호화가 되기 이전의 해시 값을 얻게 됩니다. 앞서 말씀드렸다시피 해시 값에 들어있는 건 인증서의 내용 그 자체입니다. 따라서 클라이언트가 인증서의 내용을 동일한 해시 알고리즘으로 처리한다면 두 값이 동일해야합니다. 그래서 둘이 같다면 클라이언트는 서버로부터 전달 받은 공개 키가 조작되지 않은 키라고 확신할 수 있게 됩니다. 이런 과정을 거쳐서 대칭 키 암호화 방식과 공개 키 암호화 방식을 사용하게 되고, 이 기법을 TLS 혹은 SSL이라고 부릅니다.

 

우리가 흔히 알고 있는 HTTPS가 이러한 암호화 방식을 사용하는 대표적인 예시입니다. 기존 HTTP는 평문 텍스트를 주고받지만 HTTPS는 인증 기관으로부터 사이트의 공개키를 암호화하고 인증서를 발급받게 됩니다. 그리고 이 암호화된 공개키와 인증서를 웹 브라우저에게 제공하는 것입니다. 웹브라우저는 기본적으로 공인 인증 기관의 공개키를 알고 있기 때문에 일련의 공개키 암호화/복호화 과정들이 가능해집니다.

 

이러한 대칭키를 세션키라고도 부르는데 세션이 종료되면 이 세션키는 당연히 폐기됩니다. 한 번의 세션에서 사용하기 위한 임시적인 대칭키라고 볼 수 있습니다.

로드밸런싱

로드 밸런싱은 서버의 안정성을 위한 기술입니다.

 

여기서 로드는 단어 그대로 서버에 가해지는 부하를 의미합니다. 이러한 부하를 적절하게 분배하는 기술이 바로 로드 밸런싱입니다. 먼저 적절하게 분배한다는 표현에 대해 더 자세한 설명을 해보고 싶은데요. 작은 규모의 서비스라면 서버 한 대로도 클라이언트의 요청에 응답할 수 있습니다. 그러나 규모가 확장될 수록 트래픽이 증가할 것이고, 하나의 서버로는 감당하기 불가할 것입니다.

 

이에 대한 대응 방안으로는 서버의 성능을 올리거나, 물리적으로 서버의 개수를 늘려서 나눠서 일하도록 만드는 방식이 있는데요. 후자의 방법을 사용할 때, 즉 이중화 혹은 다중화 방식으로 서버를 구성할 때 트래픽을 적절하게 나누어주는 기술이 바로 로드 밸런싱입니다.

 

로드밸런싱은 로드밸런서에 의해 이루어집니다. 로드밸런서는 일반적으로 L4 스위치 혹은 L7 스위치입니다.(왜냐하면 애플리케이션의 포트 번호를 Layer4 즉 전송 계층부터 가지고 있기 때문에 그렇다고 합니다. L4는 하나의 서버에서 여러 개의 포트 번호를 나누어서 밸런싱을 해야할 때 사용됩니다. L7 로드 밸런서는 애플리케이션 계층에서 로드를 분산시키기 때문에 HTTPS면 HTTPS, FTP면 FTP 이런식으로 사용자의 요청을 기준으로 더 섬세하게 밸런싱이 가능합니다.)

 

혹은 해당 기능을 가진 소프트웨어를 호스트에 깔아서 로드 밸런서로서 사용하기도 합니다. 로드밸런서는 헬스 체크 불리는 검사를 통해서 서버의 상태를 주기적으로 확인하게 됩니다. (가장 기본적인 헬스 체크 방식은 서버의 서비스 포트를 확인하는 것입니다. 서버의 서비스 포트로 SYN 신호를 보내보고 ACK, SYN 신호가 되돌아온다면 ACK와 FIN을 보내서 헬스체크를 종료하는 방식입니다.)

 

이렇게 파악한 서버의 상태를 바탕으로 트래픽 분산 알고리즘이 수행되는데, 로드밸런싱 알고리즘은 매우 다양합니다. 대표적인 몇 가지만 설명을 드리도록 하겠습니다.

 

가장 간단한 방신은 바로 라운드 로빈 입니다. 서버에 요청이 들어온 순서대로 서버가 차례차례 배정을 받는 방식입니다. 서버의 스펙이 동일하고 한 세션의 길이가 오래 지속되지 않는 경우에 적합합니다.

 

여기서 서버의 스펙이 서로 다른 경우엔 서버마다 가중치가 다르게 부여되기도 하는데요. 이를 가중 라운드 로빈 방식이라고 부릅니다. 예를 들어 A라는 서버의 가중치가 5고 B라는 서버의 가중치가 3이라면 A에 5개의 요청을, B에는 3개의 요청을 분배하는 방식입니다.

 

IP 해시 방식은 해시 함수가 하나의 입력값에 대해 항상 같은 출력값을 내보낸다는 성질을 이용합니다. 클라이언트의 IP를 해싱해서 특정 서버와 매핑시키는 방식입니다. 최소 연결 방식은 요청이 들어온 시점에 가장 적은 연결 상태를 보이는 서버에 트래픽을 우선적으로 배분합니다. 최소 리스폰타임은 요청에 대한 응답 시간이 가장 빠른 서버에 트래픽을 우선 배분합니다.

컴퓨터의 구조

컴퓨터 시스템의 구조

컴퓨터 시스템은 크게 하드웨어와 소프트웨어로 나누어집니다.

 

 

여기서 하드웨어는 크게 분류해보았을 때 중앙처리장치(CPU), 기억장치(RAM, HDD), 입출력 장치(마우스, 키보드, 프린터)로 이루어져 있습니다.

 

좀더 자세히 살펴보자면 아래 그림처럼 나타낼 수 있습니다.

 


이번 글에서 먼저 살펴볼 것은 중앙처리장치는 CPU입니다. 인간으로 따지면 두뇌에 해당하고 프로그램 명령어와 데이터를 읽어와서 처리하고 제어하는 역할을 담당합니다. 이 CPU는 비교와 연산을 담당하는 산술논리연산장치(ALU)와 명령어의 해석과 실행을 담당하는 제어장치, 속도가 빠른 데이터 기억 장소인 레지스터로 구성되어 있습니다.

산술논리연산장치 ALU

ALU는 레지스터로부터 피연산자를 받아들이고, 제어장치로부터 제어 신호를 받아들입니다.

 


만약 1+2를 수행한다면 레지스터로부터 1과 2라는 피연산자를, 제어장치로부터 더하라는 제어 신호를 받아들이게 됩니다. 그리고 그 결과값을 다시 레지스터에 담습니다. 그 결과는 숫자가 될 수도 있고 문자가 될 수도 있고 주소가 될 수도 있습니다.(피연산자와 제어장치가 건네준 신호에 따라 달라질 겁니다.)

 

그런데 이 결과값에 대한 부가 정보도 플래그 레지스터에 담게 되는데요. 여기서 판단하는 대표적인 정보로는 숫자의 양수 음수 판별이 있습니다. 혹은 결과값이 0이다라는 플래그도 저장될 수 있습니다. 혹은 연산을 한 결과값이 너무 커서 레지스터에 다 담기 어려우면 플래그 레지스터에 담기기도 합니다. 이게 그 유명한 오버플로우입니다.

제어장치

제어 장치는 CPU가 명령어를 순서대로 실행하기 위해 제어하는 장치입니다. 주기억장치에서 프로그램 명령어를 꺼내어 해독하고, 그 결과에 따라 명령어 실행에 필요한 제어 신호를 기억장치, 연산장치, 입출력장치로 보냅니다. 이들 장치가 다음에 수행할 동작을 결정하기도 합니다.

 

제어 장치에는 클럭이라는 개념이 있습니다. 클럭이란, 컴퓨터의 모든 부품을 일사불란하게 움직일 수 있게 하는 시간 단위입니다. 가령 명령어 레지스터에서 명령어를 가져오는 일련의 행위들이 바로 이 클럭 주기에 따라 수행된다고 볼 수 있습니다.

 


클럭 신호에 따라 명령어를 가져왔다면 이를 해석해서 제어 신호를 레지스터, 기억장치, 연산장치, 입출력장치로 내보냅니다. 이러한 과정에는 플래그 레지스터로부터 플래그를 받아들여서 부가 정보를 덧붙이는 작업도 포함이 되어 있습니다.

 

그런데 제어 신호를 발생시키는 건 CPU만 가능한 건 아닙니다. 외부에서도 제어 신호가 발생할 수 있기 때문에 제어 장치는 그러한 신호를 받아들이는 역할 또한 하게 됩니다.

레지스터

메모리와 정반대로 속도가 빠른 기억 장치입니다. 명령어 주소, 코드, 연산에 필요한 데이터, 연산 결과 등이 임시로 저장됩니다. 용도에 따라서 범용 레지스터와 특수 목적 레지스터로 구분됩니다.

  • 범용 레지스터 : 연산에 필요한 데이터나 연산 결과를 임시로 저장
  • 특수 목적 레지스터 : 특별한 용도로 사용하는 레지스터

특수 목적 레지스터

  • MAR(메모리 주소 레지스터) : 읽기와 쓰기 연산을 수행할 주기억장치 주소 저장
  • PC(프로그램 카운터) : 다음에 수행할 명령어 주소 저장
  • IR(명령어 레지스터) : 현재 실행 중인 명령어 저장
  • MBR(메모리 버퍼 레지스터) : 주기억장치에서 읽어온 데이터 or 저장할 데이터 임시 저장
  • AC(누산기) : 연산 결과 임시 저장

CPU의 동작 과정

  1. 주기억장치는 입력장치에서 입력받은 데이터 또는 보조기억장치에 저장된 프로그램 읽어옵니다.
  2. CPU는 프로그램을 실행하기 위해 주기억장치에 저장된 프로그램 명령어와 데이터를 읽어와 처리하고 결과를 다시 주기억장치에 저장합니다.
  3. 주기억장치는 처리 결과를 보조기억장치에 저장하거나 출력장치로 보냅니다.
  4. 제어장치는 1~3 과정에서 명령어가 순서대로 실행되도록 각 장치를 제어합니다.

댓글