정글 8주차.. pintOS라는 거대한 관문에 들어서기 바로 전 주에 진행한 학습과제는 소켓을 이용하여 간단한 서버를 만드는 것이다. 기존에 백엔드 연습을 했더라면, REST api를 설계하여 uri와 각 GET/POST 메서드를 매핑하여 호출하는 로직을 작성한 것이 기억날 것이다. 여기서는 좀 더 아랫단계인 소켓 통신 단위로 로직을 작성하는데, 좀 더 깊게 이해할 필요성을 느꼈다.
소켓은 프로세스가 통신할 수 있는 출입구이다

프로세스는 프로그램의 실행 단위이다. 이들은 보통 논리적으로 격리가 되어있어 서로가 침범하지 않아 보안과 안전성에서 장점을 발휘하지만, 외부... 즉, 네트워크와 소통을 하기 위해선 소켓이라는 출입구를 만들어야 한다. 따라서, 네트워크 통신을 위해 소켓을 해당 프로세스에 열어줘야 하는데, 이를 위해서는 재료가 되어줄 addrinfo(소켓 주소 구조체)가 필요하다.
typedef struct sockaddr_in {
short sin_family; // AF_INET
u_short sin_port; // 포트 번호
struct **in_addr** sin_addr; // IPv4 주소
char sin_zero[8]; // IPv6를 수용하기 위해 들어간 옵션
} SOCKADDR_IN;
IPv4를 기준으로 한 주소 구조체는 다음 위와 같다. sin_family는 사용할 주소 체계를 명시하며, 포트와 IPv4주소를 명시해야 한다. 이외에도 IPv6를 사용하기 위한 설정도 있다. 이렇게 구조체라는 재료가 준비되면 소켓을 생성할 수 있는데... 생성하는 과정을 자세히 배우기 보단, 생성 함수에 구조체를 넣어 생성하는 방법에서 끝이 났다. 아쉬울 수 있으나, 소켓은 실제 커널 영역이라는 좀 더 로우 한 레벨에서 생성된다는 사실을 알고, 추후에 배우기를 기대하며 다음 내용으로 넘어갔다.
커널에 생성된 소켓, 그리고 식별자
우리가 코딩하여 다루는 공간은 사용자 공간(user space)라고 한다. 소켓은 커널 영역(kernel memory)에서 시스템 콜을 호출하여 만들어지지만, 이 객체가 사용자 공간에서 소통하기 위해서 식별자.. 즉, File Descripter(fd)를 사용자 공간에 생성하여 연결을 한다. 이해에 보템하기 위해 아래 예시 코드를 작성하였다. 생성 함수 중 하나인 my_open_clientfd()를 통해 생성한 다음 식별자를 반환하고 이를 clientfd라는 변수에 할당하는 과정을 볼 수 있다.
int clientfd = my_open_clientfd(host, port);
서버는 최소 두 개의 소켓을 운영해야 한다

위는 클라이언트와 서버가 통신하는 과정을 단순화 한 이미지이다. 맨 위의 각각 클라이언트와 서버가 소켓을 열기 위해선, 주소 구조체가 필요함을 잊지 말자. 클라이언트 측에서 생성된 소켓은 connect라는 함수를 통해 서버에게 연결 요청을 할 수 있다. 반면, 서버 측에서는 수동적으로 클라언트의 연결 요청을 기다리는데... 이를 수행하는 소켓이 listen 소켓이다.
listen 소켓에서 클라이언트의 요청을 감지하면, 해당 클라이언트와 소통하는 전용 소켓(connfd 식별자)를 생성한다. 이러한 과정은 accept 함수를 통해 수행된다. 전용 소켓은 클라이언트와 통신이 종료되면 close된다. 그러나, 서버는 언제나 클라이언트 요청에 대기하고 있어야 하므로 listen은 서버 종료 전 까지 상시 작동한다.
정적 컨텐츠와 동적 컨텐츠
Tiny 서버 과제를 수행하면서 다루는 주요 내용은 동적 및 정적 컨텐츠 처리 방식이다. 서버는 DB에서 클라이언트가 원하는 데이터를 식별하여 전달하는 역할을 하는데, 이 데이터가 동적이냐 혹은 정적이냐에 따라 처리 방식이 다르다.
정적 컨텐츠는 DB에 있는 정보를 그대로 클라이언트에 전달하기가 가능한 데이터를 말한다. 예를 들어, 이미지 데이터의 경우, 주소의 시작 점 부터 해서, 끝 점 까지 읽고 전송을 한다. 동적 컨텐츠는 서버가 DB에서 데이터를 가져오지만, 이를 그대로 전달 할 수 없어 연산을 한 다음 클라이언트에 보낸다.
동적 컨텐츠를 실행하기 위한 CGI 프로그램
오늘날에는 동적 컨텐츠를 유연하게 처리하도록 다양한 프레임워크가 존재하지만, Tiny 서버는 초창기 웹의 동작방식을 구현하는 것을 목표로 한다. 서버는 본업인 새 클라이언트를 accept하는 업무를 진행해야 하기 때문에, 복잡한 연산을 해야하는 동적 컨텐츠 처리는, 새로운 자식 프로그램을 생성하여 임무를 부여하는 방식이다.
/*자식 프로세서 만들기: fork*/
if(Fork() == 0){
/*자식 프로세서 환경변수 설정*/
setenv("QUERY_STRING", cgiargs, 1);
Dup2(fd, STDOUT_FILENO);
/*이제 자식 프로세서 실행*/
Execve(filename, emptylist, environ);
}
고전적인 CGI는 이렇게 요청 당 새 프로세스를 매번 만들지만, 이러한 과정은 OS입장에서 상당한 비용이 든다. 오늘날에는 Spring, Django와 같이 멀티 스레드 모델을 운영하는 방식과, Node.js같이 이벤트 루프를 이용하여 싱글 스레드 환경에서 일을 관리 및 배분하는 방식이 있다.
Proxy Server는 서버한테 클라이언트처럼 행동한다

Proxy(중계) 서버는 위의 이미지와 같이 클라이언트와 본 서버 간의 통신을 중계하는 역할을 한다. Proxy 구현 단계로 넘어가 코드를 작성하면서, 중점적으로 다룬 내용은 proxy 서버는 본 서버에게는 클라이언트로, 본 클라이언트에게는 서버로 인식된다는 점이었다. 서버 역할을 하기 위에 listenfd, connfd로 접속 대기 및 전용 소켓을 생성하는 동시에, serverfd를 만들어 본 서버에게 요청을 하는 아래 로직을 통해 알 수 있다.
int listenfd, connfd;
/*클라이언트로부터 연결 수락 => 전용 소켓 할당 => 소켓 식별자를 doit 함수에 전달*/
listenfd = Open_listenfd(argv[1]);
while(1){
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
}
/*doit 함수 내부*/
/*Tiny와 연결할 소켓 생성(본인은 클라이언트로 act)*/
serverfd = Open_clientfd(host, port);
proxy 서버의 주요 역할
Proxy 서버는 일상에서 빈도 높게 사용되는데, 도입함으로써 다양한 이점이 있기 때문이다. 중요한 3가지를 설명을 하자면, 먼저 보완과 익명성이 있겠다. Proxy는 진짜 클라이언트가 직접 접촉하지 않으므로, 본 서버는 실제 클라이언트 정보를 아는데 어렵다. 이를 통해 실제 접촉차의 정보를 은닉할 수 있다. 다른 역할로는 동시병렬과 부하 분산이다. 프록시 서버가 여러 개의 본 서버에 적절히 업무를 분담하는 load balancing이 대표적이다. 마지막 역할은 캐싱이 있는데, 반복된 요청의 경우 본 서버까지 가지 않아도 되므로 연산 부하에 상당한 개선이 가능하다.
단계적으로 이어지는 서버 구현
이번 주차의 학습과 과제는 echo → tiny → proxy 순으로 진행되었다. 각각 서버를 구현한 후에도, 추가과제(대용량 파일 전송, 동시병렬 처리 등)를 진행하면서 조금씩 함수를 추가 구현하여 덧붙이면서 완성도를 높여갔다. 초기에는 CSAPP 서적에 나온 함수를 그대로 구현하면서, 과연 내 지식이 될 것인가... 라는 의문을 가졌었다. 하지만 다음 서버, 추가 기능을 구현하면서, 기존 함수를 재구성 할 기회를 가질 수 있었다.
유사한 함수를 두 번 작성하는 것이지만, 후자에서는 서적과 차별화?되는 함수를 작성하면서 좀 더 나만의 것이 될 수 있었다. 당장 완벽하게 이해해야 한다는 마음 가짐보다는, 반복적인 접근과 응용을 통해 숙련도를 높이는 경험이었다. 동일한 내용을 반복해서 보더라도, 추후 학습한 지식이 바탕이 되면 시각이 확장될 수 있기 때문이 아닐까 싶다.
'크래프톤 정글 > WIL' 카테고리의 다른 글
| 정글[Week_10]: Pintos의 프로세스의 생애 주기 구현하기 (0) | 2026.05.07 |
|---|---|
| 정글[Week_09]: Pintos의 스레드 알고리즘을 설계하다 (4) | 2026.04.30 |
| 정글[Week_07]: malloc(동적 할당기) 직접 구현 후기 (0) | 2026.04.16 |
| 정글[Week_06]: 파일 기반 SQL 처리기 만들기 (0) | 2026.04.09 |
| 정글[Week_05]: React의 Component · State · Hooks 이해하기 (0) | 2026.04.02 |