새소식

Welcome to the tech blog of Junior Backend Developer Seulbin Kang!

Study

[CS Study] 네트워크 통신은 왜 비싸다고 하는 것일까? 이게 개발과 무슨 관련일까?

  • -

👀 들어가기 전에

네트워크 통신은 비싼 작업이다.

 

라는 말 한 번즘은 들어보셨을 것이라 생각됩니다.

이에 저는

이게 개발하는 것과 어떻게 관련되어있는지
왜 이 네트워크 통신 비용을 생각해야하는지

 

라는 의문이 들었습니다. 

처음에는 이 네트워크 통신이 비싼 작업이라는 말이 굉장히 추상적으로 들렸습니다.

서버에서 API를 호출하는 것도 결국 코드 한 줄이고,

DB에 데이터를 저장하는 것도 Repository 메서드 하나를 호출하는 것처럼 보이기 때문입니다.

 

하지만 실제 성능 문제를 마주하게 되면서 깨닫게 되었습니다.

 

반복문 안에서 외부 API를 계속 호출하거나, DB에 데이터를 한 건씩 Insert하거나,

이미지와 JSON 응답 크기가 커질 때 순간 응답 시간이 눈에 띄게 느려지는 경우가 발생합니다.

네트워크 통신에는 물리적인 거리, TCP 연결 비용, 대역폭의 한계점, 패킷 왕복 시간, 커넥션 관리 비용이 모두 포함되어있기 때문에

위와 같은 현상이 발생할 수 있는 것임을 최근에 깨닫게 되었습니다.

 

이번 글에서는 그래서 네트워크 통신이 왜 성능을 저하시키는 작업인지 알아보고,

이 이론을 바탕으로 쇼핑몰 모아보기 서비스의 크롤링 속도를 어떻게 튜닝할 수 있는지 정리해보겠습니다.


👀 본론

1. 빛도 지구를 무한히 빠르게 돌지는 못합니다. (물리 지식이 다량(?) 함유되어 있기에 Skip하셔도 좋습니다 ㅎㅎ)

네트워크 지연을 이해하려면 먼저 물리적인 한계를 계산해봐야 합니다.

 

진공 상태에서 빛의 속도는 정확히 초속 299,792,458m, 즉 약 299,792km/s 입니다.

지구의 적도 지름은 약 12,756km이고(NASA에서 설명하는 지구 적도 지름), 

이를 기준으로 적도 둘레를 계산하면 약 40,075 km 정도가 됩니다.

 

그러면 빛은 1초에 지구를 몇 바퀴나 돌 수 있을까요?

빛의 속도 ≒ 299,792km/s
지구 둘레 ≒ 40,075km

299,792 / 40,075 ≒ 7.48

 

즉 빛은 1초에 지구를 약 7.5바퀴 돌 수 있습니다.

 

빛조차도 지구 한 바퀴를 도는데 약 133ms가 걸리게 됩니다.

40,075km / 299,792km/s ≒ 0.133초

 

개발자가 보는 100ms, 200ms, 500ms는 코드 레벨에서는 짧아 보일 수 있습니다.

하지만 네트워크 관점에서는 이미 지구 규모의 거리와 물리적인 전파 시간이 포함된 수치입니다.

 

물론 실제 인터넷 통신은 진공 속 빛의 속도로 직선 이동하지 않습니다.

광섬유 안에서의 전파 속도는 진공보다 느리고,

라우터 / 스위치 / ISP / 해저 케이블 / 중간 경로를 거치면서 더 많은 지연이 붙습니다.

 

그래서 한국에서 미국 서버까지 TCP 통신을 하면 단순히 코드가 느릴 수도 있지만,

물리적으로 왕복하는 데 필요한 시간이 응답 시간에 포함되어 느려질 수도 있는 것입니다.


2. TCP 통신이 어떻게 돌아가더라?

여기서 우리가 서버와 통신할 때 자주 사용하는 TCP 프로토콜도 살펴볼 필요가 있습니다.

TCP는 신뢰성을 제공하는 프로토콜입니다.

 

TCP는 데이터를 그냥 던지고 끝내지 않습니다.

상대방과 연결을 맺고, 데이터를 보내고, 응답을 받고, 연결을 종료하는 절차를 거칩니다.

 

TCP 연결 수립은 컴퓨터 네트워크 시간에 배우는 것과 같이 three-way handshake 절차로 이루어지게 됩니다.

가장 단순하게 TCP 연결을 새로 열고 HTTP 요청을 보낸 뒤 연결을 닫는다고 가정하면 아래와 같은 흐름이 됩니다.

TCP 통신

  1. Client → Server : SYN
  2. Server → Client : SYN + ACK
  3. Client → Server : ACK

Server 와 Client가 연결된 이후

  1. Client → Server : HTTP Request
  2. Server → Client : HTTP Response

까지 전부 이루어졌으면 통신을 닫는 과정으로 들어가게 됩니다.

  1. Client → Server : FIN
  2. Server → Client : ACK
  3. Server → Client : FIN
  4. Client → Server : ACK

즉, 단순화 했을 때, 총 9번의 패킷 교환이 발생합니다.

(물론 실제로는 HTTP Keep-Alive, HTTP/2, HTTP/3, TLS, 커넥션 풀링 등에 따라 이 흐름은 달라지게 됩니다.)

 

어쨋든 결국 네트워크 요청은 실제로는 여러 번의 패킷 왕복과 상태 관리가 포함된 작업이라는 것입니다.\

 

빛이 지구 한바퀴를 도는 데만 약 133ms가 걸립니다.

지구 4.5바퀴에 해당하는 거리는 이론적으로 약 600ms가 걸리는 것에 가깝습니다.

 

이 이야기를 한 이유는, 

여러 번의 왕복이 누적되면 물리적 거리 비용이 커진다는 것입니다.

 

한국에서 미국 서버와 통신할 때는 네트워크 경로, 리전, ISP, CDN 여부에 따라 다르지만,

왕복 지연 시간인 RTT가 수십 ms에서 수백 ms까지 발생할 수 있습니다.

출처 : https://www.cloudflare.com/learning/performance/glossary/what-is-latency

 

Cloudflare 공식문서에서도 지연 시간은 대표적으로 물리적 거리에 의해서 발생한다는 점을 이야기하고 있는 것을 볼 수 있습니다.


3. Latency와 Bandwidth

Latency는 요청을 보내고 응답이 오기까지 걸리는 지연 시간입니다.

Bandwidth는 한 번에 얼마나 많은 데이터를 보낼 수 있는지를 나타냅니다.

 

예를 들어 인터넷 속도가 100Mbps라고 가정해보겠습니다.

100Mbps = 100 Megabit per second
100 / 8 = 12.5MB/s

 

위 식에 따라 이론적으로는 초당 12.5MB정도를 받을 수 있습니다.

하지만 서버가 지구 반대편에 있고 RTT가 높다면 첫 응답을 받기까지는 여전히 시간이 걸립니다.

반대로 Latency가 낮아도 이미지, 동영상, 대용량 JSON처럼 전송해야 할 데이터가 크면 Bandwidth 한계에 걸립니다.

 

즉, 네트워크 성능 문제는 아래 두 가지로 갈리게 됩니다.

1. 왕복이 너무 많아서 느린 경우
   → Latency 문제

2. 한 번에 보내는 데이터가 너무 커서 느린 경우
   → Bandwidth 문제

 

웹과 모바일 서비스에서는 아래의 데이터들이 계속 네트워크를 통해 이동하게 됩니다.

  • 이미지
  • 동영상
  • API Json 응답
  • 서버와 DB 사이의 쿼리 결과
  • 마이크로 서비스 간 요청/응답 데이터
  • 로그, 이벤트, 메시지 큐 payload

등등...

이 데이터들의 크기가 커지면 대역폭을 결국에는 더 많이 사용하게 됩니다.

그리고 대역폭보다 많은 데이터를 동시에 보내려고 하면 큐잉, 지연, 타임아웃 및 재전송이 발생할 수 밖에 없는 것입니다.


4. 그래서 네트워크 통신은 횟수와 크기를 모두 줄여야 합니다.

물론, 그냥 토이 프로젝트라면 이런 부분까지 세심하게 신경 쓸 이유는 없습니다.

주로, 한국에서 한국 서버로 서빙하거나, 미국에서 미국 서버로 서빙하기도 하고,

주고 받는 데이터의 양 자체가 그렇게 크지 않은 경우가 많기 때문입니다.

 

그러나, 대규모의 프로젝트가 되고 글로벌 프로젝트가 된다면 이야기가 달라집니다.

대량의 데이터가 오갈 수 있고, 물리적으로 먼 거리를 오갈 수도 있습니다.

 

이때, 네트워크 최적화의 핵심은 단순합니다.

  1. 통신 횟수를 줄입니다.
  2. 통신 데이터 크기를 줄입니다.
  3. 이미 필요한 통신이라면 대역폭을 최대한 활용합니다.

예를 들어 DB에 데이터를 저장할 때 아래의 코드는 성능상 불리할 수 밖에 없습니다.

for (Product product : products) {
    productRepository.save(product);
}

 

보기에는 굉장히 단순해 보이지만, 내부적으로 DB와의 통신이 여러번 가게 될 수 있습니다.

데이터가 몇 십건 단위면 큰 문제가 없을 수 있지만 몇 십만건 단위가 되면 큰 문제가 될 수 있습니다.

 

그럼 이럴 때는 어떻게 하면 좋을까요?

가능한 Batch Insert 또는 Bulk Insert를 고려합니다.

jdbcTemplate.batchUpdate(sql, batchArgs);

 

이렇게 되면 DB에 한 건씩 왕복하여 시간복잡도가 O(n)이 되는 것이 아니라, O(1)로 수렴하도록 만들 수 있습니다.

네트워크 왕복 횟수 또한 줄어들게 됩니다.

 

HTTP 응답에서도 마찬가지입니다.

API에서 반환하는 JSON에 불필요한 필드, 공백, 개행, 중복 데이터가 많다면 그만큼 응답 크기가 커집니다.

일반적으로 프레임워크나 웹 서버는 JSON 직렬화 과정에서 불필요한 공백을 제거하거나

HTTP 압축을 적용할 수 있습니다.

 

HTTP의 Content-Encoding을 통해 gzip과 같은 압축 방식을 사용할 수도 있습니다.


5. 끽해봐야 JSON? JSON 오브젝트 하나는 필드가 많아질수록 어느 정도까지 커질까?

예를 들어 로그 분석 결과 1건을 JSON으로 내려준다고 해보겠습니다.

{
  "resultId": 1,
  "logDate": "2026-05-29T10:15:30",
  "email": "user@example.com",
  "clientIp": "123.123.123.123",
  "requestUrl": "/api/login",
  "ruleType": "BRUTE_FORCE",
  "riskLevel": "HIGH",
  "summary": "5분 내 로그인 실패 10회 발생",
  "status": "DETECTED"
}

이 정도가 들어가게 될 경우, 아래 사진에서 확인할 수 있는 바와 같이 280바이트가 나오게 됩니다.

실제 저장 JSON 크기

 

하지만, 실제 로그 필드는 여기서 끝나는게 아니라 필드가 더욱 늘어날 수 있습니다.

예를 들어 아래와 같이 늘어났다고 해보겠습니다.

{
  "resultId": 1,
  "inspectionId": 10,
  "systemName": "고객정보관리시스템",
  "logDate": "2026-05-29T10:15:30",
  "email": "user@example.com",
  "userName": "홍길동",
  "department": "개발팀",
  "clientIp": "123.123.123.123",
  "requestUrl": "/api/admin/users",
  "httpMethod": "POST",
  "userAgent": "Mozilla/5.0 ...",
  "ruleType": "UNAUTHORIZED_IP_LOGIN",
  "ruleName": "비인가 IP 로그인 성공 점검",
  "riskLevel": "CRITICAL",
  "reason": "허용되지 않은 IP에서 로그인 성공",
  "evidenceCount": 12,
  "status": "REQUESTED",
  "createdAt": "2026-05-29T11:00:00",
  "updatedAt": "2026-05-29T11:10:00"
}

 

JSON 오브젝트 안에서 줄이 몇개 늘어났을 뿐인데, 643바이트까지 늘어난 것을 확인할 수 있습니다.

 

실제 저장 JSON 크기

 

만약 여기에 requestHeaders, ResponseBody 같은 값을 같이 내려주게 되면 1건이 1키로바이트가 넘어갈 수도 있는 것입니다.


6. 그렇다면 한 번에 주고받는 JSON 크기는 어느 정도가 최대일까?

순수하게 궁금해졌습니다. 한 건당 1킬로바이트가 넘어가기 쉬운데, 1번에 주고받는 JSON 크기는 과연 어느 정도까지가 최대일까요?

 

찾아보고 공부해본 바로는

정해진 최대값은 없고, 설정에 따라 달라진다

가 답이었습니다.

 

JSON은 결국 HTTP body에 실려 가는 텍스트일 뿐이라, 크기 한계는 JSON 자체가 아니라

그 사이를 거치는 인프라 계층마다 따로 걸려 있습니다.

 

대표적으로 Nginx는 요청 body 최대 크기를 client_max_body_size로 제한합니다.
기본값은 1MB이고, 요청 크기가 이 값을 넘으면 413 Request Entity Too Large 에러가 발생합니다.

그런데 한계는 여기 하나만 있는 게 아닙니다.

  • WAS 계층: Spring Boot라면 spring.servlet.multipart.max-request-size,
    Tomcat이라면 maxPostSize 등이 따로 걸려 있습니다.
  • 애플리케이션 계층: JSON 파서(Jackson 등)가 역직렬화할 때 메모리에 통째로 올리므로, 큰 payload는 곧 힙 사용량입니다.
  • 클라이언트/프록시 계층: CDN, API Gateway, 로드밸런서마다 각자의 body 크기 제한이 있습니다.

즉 "JSON을 얼마나 크게 보낼 수 있나"는 이 경로상에서 가장 낮은 제한값에 의해 결정됩니다.

 

물론 client_max_body_size를 10MB, 50MB로 늘려도 됩니다.

하지만 설정을 늘리는 것과 그렇게 보내도 되는 것은 다른 문제입니다.

payload가 커질수록 직렬화/역직렬화 CPU 비용, 서버 메모리 점유, 그리고 무엇보다 3번에서 본 대역폭 한계에 그대로 부딪힙니다.

 

"보낼 수 있다"가 "보내야 한다"를 의미하진 않는 것이죠.

그래서 큰 데이터를 다뤄야 한다면 설정값을 키우기보다

  • 페이지네이션 / 커서 기반 조회로 한 번에 내려주는 양 자체를 쪼개거나,
  • 파일이나 이미지처럼 큰 바이너리는 body에 싣지 않고 Presigned URL로 스토리지에서 직접 받게 하거나,
  • 꼭 필요한 필드만 내려주는 부분 응답(partial response) / GraphQL 스타일 필드 선택을 고려하는 편이 낫습니다.

👀 결론

처음에 저는 "네트워크 통신은 비싼 작업이다"라는 말이 추상적이라고 느꼈습니다.

그냥 한 번의 왔다갔다인데 뭐가 비싸다는 거지, 싶었습니다.

 

하지만 정리하고 보니 그 한 줄 뒤에는

빛조차 지구 한 바퀴에 133ms가 걸리는 물리적 거리, 연결 하나를 맺고 끊는 데만 여러 번 오가는 패킷 등 수많은 작업들이 있었습니다.

 

우리가 코드 레벨에서 보는 "겨우 200ms"는, 네트워크 입장에서는 이미 지구 규모의 비용이 응축된 숫자였던 것입니다.

그래서 네트워크 최적화의 원칙은 의외로 단순하게 귀결됩니다.

  1. 통신 횟수를 줄인다 
  2. 통신 데이터 크기를 줄인다
  3. 이미 필요한 통신이라면 대역폭을 최대한 활용한다

물론 토이 프로젝트에서는 여기까지 신경 쓸 이유가 없을 수도 있습니다. 오히려 과잉 설계에 과잉 걱정이 될 수도 있습니다.

하지만 데이터가 수십만 건이 되고, 서버가 지구 반대편에 있고, 트래픽이 글로벌해지는 순간

이 작은 비용들이 누적되어 서비스의 체감 속도를 좌우하게 되는 것입니다.

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.