클라이언트-서버 구조
고친 사람 github-actions[bot]
클라이언트-서버 구조는 프로그램의 역할을 일을 맡기는 쪽과 맡아 처리하는 쪽으로 가릅니다. 맡기는 쪽을 클라이언트, 처리하는 쪽을 서버라고 부릅니다. 함께 쓰는 데이터와 규칙은 서버 한곳에 모여 있습니다. 그래서 클라이언트가 아무리 많아도 모두가 같은 값을 봅니다.
쉽고 빠른 이해
일을 맡기는 프로그램과 맡아 처리하는 프로그램을 나눠 짓는 방식입니다. 메신저가 이렇게 돕니다. 내 휴대폰의 앱이 메시지를 보내면 메신저 회사의 컴퓨터가 받아 저장합니다. 상대에게 전해 주는 것도 그 컴퓨터입니다.
이렇게 나누면 데이터가 한곳에만 있습니다. 누가 고쳐도 그곳 하나만 고치면 됩니다. 그러니 모두가 같은 값을 봅니다. 나누지 않으면 사람마다 데이터를 따로 들고 있어서 서로 어긋납니다.
- 맡기는 쪽이 먼저 요청을 보냅니다
- 처리하는 쪽이 데이터를 꺼내 계산합니다. 규칙에 맞는지도 따집니다
- 결과를 응답으로 돌려줍니다
대가도 있습니다. 처리하는 쪽이 멈추면 모두가 함께 멈춥니다. 무엇을 하든 그곳까지 다녀와야 결과를 알 수 있어서 기다리는 시간이 생깁니다. 남과 함께 쓸 데이터가 없는 메모장 같은 프로그램은 이렇게 나눌 필요가 없습니다.
상세
클라이언트-서버 구조는 시스템을 두 역할로 나눠 짓는 방식입니다. 일을 맡기는 프로그램이 클라이언트입니다. 맡은 일을 처리해 돌려주는 프로그램이 서버입니다. 웹 브라우저가 쇼핑몰 주소를 열면 브라우저는 클라이언트입니다. 상품 목록을 꺼내 보내 주는 쇼핑몰의 프로그램은 서버입니다.
클라이언트가 보내는 부탁 한 통을 요청이라고 합니다. 서버가 그 부탁에 답해 돌려보내는 것을 응답이라고 합니다. 이 구조에서 오가는 것은 기본적으로 이 둘뿐입니다.
이렇게 나누는 까닭은 함께 쓰는 데이터를 한곳에 모으려는 것입니다. 쇼핑몰의 재고가 서버 하나에만 있으면 손님 천 명이 같은 재고를 봅니다. 재고를 손님마다 따로 들고 있으면 한 사람이 물건을 사도 다른 사람의 화면에서는 줄지 않습니다.
역할을 가르는 두 규칙
무엇이 클라이언트이고 무엇이 서버인지는 규칙 둘로 가립니다. 프로그램의 크기나 컴퓨터의 성능은 기준이 아닙니다.
첫째 규칙은 말을 먼저 거는 쪽이 정해져 있다는 것입니다. 요청은 언제나 클라이언트가 먼저 보냅니다. 서버는 누가 부를 때까지 기다리다가 요청이 오면 그때 일을 합니다.
둘째 규칙은 함께 쓰는 데이터와 규칙을 서버가 쥔다는 것입니다. 잔액이 얼마인지, 이 사람이 이 글을 지울 권한이 있는지는 서버가 정합니다. 클라이언트는 서버가 정한 결과를 받아 화면에 보여 줍니다.
여러 클라이언트가 서버 하나를 바라보는 모양
이 구조를 그리면 가운데 서버가 하나 있습니다. 클라이언트들은 그 둘레에서 서버만 바라봅니다. 클라이언트끼리는 직접 말을 주고받지 않습니다.
메신저에서 친구에게 보낸 메시지도 친구의 휴대폰으로 바로 가지 않습니다. 내 앱이 서버에 보내면 서버가 저장해 두었다가 친구의 앱에 넘겨줍니다.
이와 맞세워지는 구성이 피어 투 피어입니다. 피어 투 피어 구성에는 답하는 일만 맡은 프로그램이 따로 없습니다. 참여하는 컴퓨터는 서로 대등합니다. 저마다 요청을 보내기도 하고 답을 돌려주기도 합니다. 이런 참여자를 피어라고 부릅니다.
flowchart TD
subgraph CS["클라이언트-서버 구조"]
C1["클라이언트 1"] --> S["서버"]
C2["클라이언트 2"] --> S
C3["클라이언트 3"] --> S
S --> D[("함께 쓰는 데이터")]
end
subgraph PP["피어 투 피어"]
P1["피어 1"] <--> P2["피어 2"]
P2 <--> P3["피어 3"]
P3 <--> P1
end
CS ~~~ PP
위 그림에서는 화살표가 모두 서버로 모입니다. 데이터도 서버 뒤에 하나만 있습니다. 아래 그림에서는 피어끼리 서로 화살표를 주고받습니다. 가운데에 모이는 곳이 없습니다.
서버가 최종 판정을 내리는 까닭
클라이언트는 사용자의 기기에서 돕니다. 사용자가 그 프로그램을 고치거나 프로그램을 거치지 않고 요청을 지어내 보낼 수 있습니다. 그래서 서버는 클라이언트가 보낸 값을 믿지 않고 다시 따집니다.
화면에서 수량 칸에 음수를 못 넣게 막아 두었어도 서버는 수량이 음수인지 다시 봅니다. 화면의 검사는 사용자를 돕는 장치입니다. 막는 일은 서버의 검사가 맡습니다.
게임에서는 이 생각을 끝까지 밀어붙인 구성을 권위 서버라고 부릅니다. 권위 서버 구성에서는 캐릭터가 어디에 있는지, 총알이 맞았는지를 서버가 계산합니다. 클라이언트는 키 입력만 보냅니다. 서버가 계산한 결과를 받아 화면에 그립니다. 그래서 클라이언트를 고쳐도 벽을 뚫고 지나갈 수 없습니다.
일을 어느 쪽에 얼마나 두나
이 구조를 쓰기로 해도 계산을 어디서 할지는 따로 정해야 합니다. 그 선택에는 이름 붙은 양 끝이 있습니다.
화면만 그리고 거의 모든 계산을 서버에 맡기는 클라이언트를 씬 클라이언트라고 합니다. 클라이언트가 가벼워집니다. 대신 버튼 하나를 눌러도 서버까지 다녀와야 결과가 나옵니다.
계산과 데이터의 일부를 클라이언트가 들고 있는 쪽을 팻 클라이언트라고 합니다. 서버를 덜 부르니 화면이 빨리 반응합니다. 대신 클라이언트가 가진 사본이 서버의 값과 어긋날 수 있습니다. 판정을 내리는 규칙은 이 경우에도 서버에 남깁니다.
세 층으로 쌓은 구성
클라이언트와 서버라는 이름은 프로그램에 붙는 꼬리표가 아닙니다. 연결 하나마다 정해지는 역할입니다. 그래서 한 프로그램이 두 역할을 함께 맡기도 합니다. 프로그램 셋을 세 층으로 쌓은 구성이 흔한 예입니다.
화면을 그리는 브라우저와 데이터를 담는 데이터베이스 사이에 프로그램을 하나 더 둘 수 있습니다. 요청을 받아 업무 규칙대로 처리하는 이 프로그램을 애플리케이션 서버라고 합니다. 브라우저는 화면만 그리고, 데이터베이스는 데이터만 담고, 규칙은 가운데에 모입니다.
브라우저 · 애플리케이션 서버 · 데이터베이스, 이렇게 세 층으로 나눈 구성을 3계층 구조라고 부릅니다. 층마다 맡은 일이 하나라서 한 층을 고쳐도 다른 층은 그대로 둘 수 있습니다.
애플리케이션 서버는 브라우저의 요청을 받을 때는 서버입니다. 그 요청을 처리하려고 데이터베이스에 데이터를 달라고 부탁할 때는 클라이언트입니다. 데이터베이스에 보내는 이런 요청을 질의라고 합니다.
flowchart TD
B["브라우저"] -->|"요청"| A["애플리케이션 서버"]
A -->|"질의"| DB[("데이터베이스")]
그림의 가운데 상자는 위쪽 화살표에서는 받는 쪽이고, 아래쪽 화살표에서는 보내는 쪽입니다.
서버로 몰리는 대가
모든 요청이 서버로 모이는 것이 이 구조의 장점입니다. 그 모임이 그대로 약점이 되기도 합니다.
멈추면 시스템 전체가 함께 멈추는 하나의 부품을 단일 장애점이라고 합니다. 서버를 한 대만 두면 그 서버가 단일 장애점입니다. 아래 표는 이것을 포함한 대표적인 대가 넷과 흔히 쓰는 대응입니다.
| 대가 | 무슨 일이 생기나 | 흔한 대응 |
|---|---|---|
| 부하가 몰림 | 클라이언트가 늘수록 서버가 받는 요청도 늘어납니다 | 같은 서버를 여러 대 둡니다. 로드 밸런서가 요청을 나눠 줍니다 |
| 단일 장애점 | 서버가 멈추면 모든 클라이언트가 함께 멈춥니다 | 여러 대 가운데 한 대가 멈추면 남은 대가 이어받습니다 |
| 왕복 지연 | 무엇을 하든 서버까지 다녀와야 결과를 압니다 | 자주 쓰는 값을 클라이언트에 캐싱해 둡니다 |
| 연결에 기댐 | 네트워크가 끊기면 클라이언트는 거의 아무것도 못 합니다 | 기기에 먼저 저장합니다. 연결이 돌아오면 서버와 맞춥니다 |
표의 대응 가운데 앞의 둘은 서버를 여러 대로 늘리는 것입니다. 서버 한 대가 앞 요청을 혼자 기억하고 있으면, 다음 요청이 다른 대로 갔을 때 이어서 처리할 수 없습니다. 그래서 서버가 요청 사이에 아무것도 기억해 두지 않게 짭니다. 이런 성질을 무상태라고 합니다.
서버를 두지 않는 구성
서버를 따로 세우지 않는 편이 맞는 경우도 있습니다. 데이터를 한곳에 모아 둘 주인이 없거나, 한 서버로 트래픽이 몰리는 것을 피하고 싶을 때입니다. 큰 파일을 여러 사람이 조각조각 나눠 주고받는 프로그램이 피어 투 피어로 도는 까닭이 그것입니다.
게임에는 판정하는 서버 없이 플레이어의 컴퓨터마다 같은 계산을 돌리는 길도 있습니다. 컴퓨터끼리 주고받는 것은 플레이어의 입력뿐입니다. 같은 입력을 넣으면 언제나 같은 결과가 나오도록 짠 계산을 결정론적 시뮬레이션이라고 합니다.
이 방식은 입력만 오가니 주고받는 양이 적습니다. 하지만 컴퓨터마다 계산 결과가 한 비트라도 어긋나면 화면이 서로 달라집니다. 어긋남을 바로잡아 줄 서버가 없기 때문입니다.
남과 데이터를 나눠 쓸 일이 없는 프로그램에는 이 구조가 필요 없습니다. 메모장이나 계산기는 혼자서 일을 끝냅니다.
관련 항목
이 구조를 이루는 역할과 오가는 메시지
클라이언트 · 서버 · 요청 · 응답 · 요청-응답 · HTTP 요청 · HTTP 응답
클라이언트-서버 구조와 맞세워지는 구성
피어 투 피어 · 피어 · 락스텝 · 결정론적 시뮬레이션 · 분산 시스템 · 메시 네트워크
클라이언트에 일을 얼마나 두느냐로 갈리는 종류
씬 클라이언트 · 팻 클라이언트 · 단일 페이지 애플리케이션 · 클라이언트 사이드 렌더링 · 서버 사이드 렌더링 · 오프라인 우선
이 구조를 층으로 더 쌓은 구성
2계층 구조 · 3계층 구조 · N계층 구조 · 애플리케이션 서버 · 웹 서버 · 데이터베이스 서버 · 레이어드 아키텍처
서버가 판정을 쥐는 게임 네트워크 기법
권위 서버 · 상태 동기화 · 리플리케이션 · 클라이언트 측 예측 · 입력 예측 · 게임 개발
이 구조 위에서 메시지를 나르는 통신 수단
네트워크 · 소켓 · 포트 · TCP · HTTP · RPC · gRPC · 웹소켓 · REST
서버로 몰리는 부담을 나누는 수단
단일 장애점 · 로드 밸런서 · 수평 확장 · 장애 조치 · 무상태 · 캐싱 · CDN · 가용성
이 구조가 속하는 상위 분류
아키텍처 양식 · 소프트웨어 아키텍처 · 관심사 분리 · 마이크로서비스 · 모놀리스 · 이벤트 주도 아키텍처
다른 이름: client-server architecture · client-server model · 클라이언트-서버 · 클라이언트-서버 모델 · 클라이언트 서버 구조 · 클라이언트/서버 구조