무엇인가
요청하는 쪽과 제공하는 쪽을 갈라 두는 구조다. 서버는 자원을 쥐고 기다리고, 클라이언트가 필요할 때 요청한다.
계층화가 “한 프로그램 안의 방향”이라면 이쪽은 주체가 둘로 나뉜다는 점이 다르다. 그래서 둘 사이에 늘 경계가 있다 — 프로세스든 네트워크든.
구조
graph LR C1[클라이언트 A] -->|요청| S[서버] C2[클라이언트 B] -->|요청| S C3[클라이언트 C] -->|요청| S S -->|응답| C1 S -->|응답| C2 S -->|응답| C3 S --- R[(공유 자원)]
C++로 보기
소켓을 걷어내고 구조만 남기면 이렇다. 요점은 클라이언트가 서버의 내부 상태를 직접 만지지 못하고 오직 요청·응답으로만 닿는다는 것이다.
#include <iostream>
#include <string>
#include <unordered_map>
struct Request {
std::string verb; // "GET" 또는 "SET"
std::string key;
std::string value;
};
struct Response {
int code; // 200 / 404
std::string body;
};
class KeyValueServer {
std::unordered_map<std::string, std::string> store_;
public:
// 클라이언트가 닿을 수 있는 유일한 통로
Response handle(const Request& req) {
if (req.verb == "SET") {
store_[req.key] = req.value;
return {200, "ok"};
}
if (req.verb == "GET") {
auto it = store_.find(req.key);
if (it == store_.end()) return {404, "not found"};
return {200, it->second};
}
return {400, "bad verb"};
}
};
class Client {
KeyValueServer& server_; // 실제로는 소켓 핸들이 들어갈 자리
std::string name_;
public:
Client(KeyValueServer& s, std::string name) : server_(s), name_(std::move(name)) {}
void set(const std::string& k, const std::string& v) {
auto res = server_.handle({"SET", k, v});
std::cout << name_ << " SET " << k << " -> " << res.code << '\n';
}
void get(const std::string& k) {
auto res = server_.handle({"GET", k, ""});
std::cout << name_ << " GET " << k << " -> " << res.code << ' ' << res.body << '\n';
}
};
int main() {
KeyValueServer server;
Client a{server, "A"};
Client b{server, "B"};
a.set("lang", "C++");
b.get("lang"); // 다른 클라이언트가 쓴 값을 본다 — 상태는 서버에 있다
b.get("missing");
}Request/Response를 값 타입으로 둔 것이 중요하다. 경계를 넘는 것은 데이터뿐이고
객체 참조가 넘어가지 않으므로, 이 자리에 나중에 소켓이나 HTTP를 끼워도 구조가 안 바뀐다.
언제 쓰나
- 자원을 한곳에서 관리해야 할 때(데이터 일관성, 접근 제어).
- 클라이언트가 여럿이고 서로 같은 상태를 봐야 할 때.
- 클라이언트 쪽 장비가 약하고 무거운 일을 넘기고 싶을 때.
대가
- 서버가 단일 장애점이다. 죽으면 전부 멈춘다.
- 부하가 서버로 몰린다. 늘리려면 복제·분산이 필요하고, 그 순간 상태 공유 문제가 새로 생긴다.
- 네트워크가 끼면 지연과 실패를 항상 다뤄야 한다. 위 예제가 감추고 있는 부분이 정확히 이것이다.
실제로 만나는 곳
HTTP, 데이터베이스, 게임 서버, X11 같은 디스플레이 서버.
사실상 오늘날 대부분의 네트워크 프로그램이 여기 속한다.