원저작자 : https://www.linkedin.com/in/josephmatei
2022년 4월 14일 게시
서버가 최대 65K 연결만 수용할 수 있다거나 서버가 수락된 각 연결마다 포트를 소비한다는 오해를 자주 듣습니다. 몇 가지 예시를 살펴보겠습니다:
“TCP/IP 주소는 65,000개의 연결만 지원하므로, 약 30,000개의 IP 주소를 해당 서버에 할당해야 합니다.”
“TCP 포트 번호는 65535개이므로, TCP 서버에 65535개의 클라이언트만 연결할 수 있나요? 이것이 단일 컴퓨터/애플리케이션이 유지할 수 있는 클라이언트 수에 제한을 둔다고 생각할 수 있습니다.”
“한 기계가 가질 수 있는 포트 수에 제한이 있고 소켓은 사용되지 않은 포트 번호에만 바인딩할 수 있다면, 매우 많은 양(최대 포트 번호 이상)의 요청을 처리하는 서버는 어떻게 이를 처리하나요? 시스템을 분산시켜서, 즉 여러 기계에 여러 서버를 두는 방식으로 처리하나요?”
이 글에서는 세 가지 방향에서 이 오해를 불식시키고자 합니다:
- 아마도 사용해 보셨을 채팅 앱인 WhatsApp과 Elixir 기반의 웹 프레임워크인 Phoenix는 이미 단일 포트에서 수백만 개의 연결을 처리하는 것을 증명했습니다.
- TCP/IP 프로토콜을 기반으로 이론적으로 가능한 것
- 아직 납득하지 못하는 분들을 위한 간단한 Java 실험
자세한 내용을 건너뛰고 요약으로 바로 가고 싶다면 문서 끝으로 이동하세요.
실험
Phoenix 프레임워크는 2,000,000개의 동시 웹소켓 연결을 달성했습니다. 그들의 글에서는 200만 명의 사용자를 시뮬레이션하여 모든 사용자에게 1초 내에 브로드캐스트하는 채팅 애플리케이션을 시연합니다. 또한 그 벤치마크를 달성하기 위해 프레임워크에서 마주친 기술적 과제에 대한 세부 정보를 제공합니다. 클라이언트 연결에 대한 65k 제한을 극복하기 위해 여러 IP를 할당하는 것과 같은 아이디어를 이 글에 활용했습니다.
WhatsApp도 2,000,000개의 연결을 달성했습니다. 안타깝게도 세부 정보는 많지 않습니다. 그들은 사용한 하드웨어와 OS 구성만 공개했습니다.
이론적 최대치
TCP 스펙에서 사용 가능한 포트가 2^16=65,536개이기 때문에 이것이 한계라고 생각하는 사람들이 있습니다. 이 제한은 단일 클라이언트가 단일 IP, 포트 쌍으로 나가는 연결을 만들 때 맞습니다. 예를 들어, 제 노트북은 172.217.13.174:443(google.com:443)에 65,536개의 연결만 만들 수 있습니다. Google은 제가 65k 연결에 도달하기 전에 아마도 차단할 것입니다. 따라서 두 머신 사이에 65K 이상의 동시 연결이 필요한 사용 사례가 있다면, 클라이언트는 두 번째 IP 주소에서 연결하거나 서버가 두 번째 포트를 사용 가능하게 해야 합니다.
포트에서 수신 대기 중인 서버의 경우, 각 들어오는 연결은 서버에서 포트를 소비하지 않습니다. 서버는 수신 대기 중인 포트만 소비합니다. 두 번째로, 연결은 여러 IP 주소에서 오게 됩니다. 가장 좋은 경우 서버는 모든 IP 주소, 모든 포트에서 오는 연결을 수신할 수 있습니다.
각 TCP 연결은 다음과 같이 고유하게 정의됩니다:
- 32비트 소스 IP(연결이 오는 IP 주소)
- 16비트 소스 포트(연결이 오는 소스 IP 주소의 포트)
- 32비트 대상 IP(연결이 가는 IP 주소)
- 16비트 대상 포트(연결이 가는 대상 IP 주소의 포트)
그러면 단일 포트에서 서버가 지원할 수 있는 이론적 한계는 2^48 입니다. 이는 약 1 쿼드릴리언(10^15)입니다:
- 서버는 클라이언트의 소스 IP와 소스 포트로 연결을 구분합니다
- [소스 IP 주소 수] x [소스 포트 수]
- 주소에 32비트, 포트에 16비트
- 이를 합치면: 2^32 x 2^16 = 2^48
- 이는 약 1 쿼드릴리언(log(2^48)/log(10)=14.449)입니다!
실질적 한계
낙관적인 실질적 한계를 이해하기 위해, 가능한 많은 TCP 연결을 열고 서버가 각 연결에서 메시지를 주고받을 수 있도록 실험을 구성했습니다. 이 워크로드는 Phoenix나 WhatsApp의 실험만큼 실용적이지는 않지만, 직접 시도해 보기에는 더 간단합니다. 실험을 실행하려면 OS, JVM, TCP/IP 프로토콜이라는 세 가지 과제를 극복해야 합니다.
실험
https://github.com/josephmate/java-by-experiments/blob/main/max_connections/src/Main.java
의사 코드는 다음과 같습니다:
스레드 1:
서버 소켓 열기
i=1부터 1,000,000까지:
들어오는 연결 수락
i=1부터 1,000,000까지:
소켓 i에 숫자 i 전송
i=1부터 1,000,000까지:
소켓 i에서 숫자 j 수신
i == j 확인
스레드 2:
i=1부터 1,000,000까지:
서버에 클라이언트 소켓 열기
i=1부터 1,000,000까지:
소켓 i에서 숫자 j 수신
i == j 확인
i=1부터 1,000,000까지:
소켓 i에 숫자 i 전송
기기
Mac에서 시도했습니다:
- 2.5 GHz 쿼드 코어 Intel Core i7
- 16 GB 1600 MHz DDR3
그리고 Linux 데스크톱에서:
- AMD FX(tm)-6300 Six-Core Processor
- 8GiB 1600 MHz
파일 디스크립터
가장 먼저 마주치는 문제는 운영 체제와의 싸움입니다. 기본 설정은 파일 디스크립터를 심하게 제한합니다. 다음과 같은 오류가 표시됩니다:
Exception in thread "main" java.lang.ExceptionInInitializerError
at java.base/sun.nio.ch.SocketDispatcher.close(SocketDispatcher.java:70)
...
Caused by: java.io.IOException: Too many open files
at java.base/sun.nio.ch.FileDispatcherImpl.init(Native Method)
...
각 서버 소켓에는 두 개의 파일 디스크립터가 필요합니다:
- 송신용 버퍼
- 수신용 버퍼
클라이언트 연결도 마찬가지입니다. 결과적으로 단일 기계에서 이 실험을 실행하려면:
- 클라이언트용 1,000,000 연결
- 서버용 1,000,000 연결
- 연결당 2개의 파일 디스크립터
- = 4,000,000 파일 디스크립터
BigSur 11.4 Mac에서는 다음과 같이 파일 디스크립터 제한을 늘릴 수 있습니다:
sudo sysctl kern.maxfiles=2000000 kern.maxfilesperproc=2000000
kern.maxfiles: 49152 -> 2000000
kern.maxfilesperproc: 24576 -> 2000000
sysctl -a | grep maxfiles
kern.maxfiles: 2000000
kern.maxfilesperproc: 1000000
ulimit -Hn 2000000
ulimit -Sn 2000000
[스택오버플로우 답변]
Ubuntu 20.04에서는 가장 빠른 방법은 다음과 같습니다:
sudo su
# 2^25는 충분히 많아야 합니다
sysctl -w fs.nr_open=33554432
fs.nr_open = 33554432
ulimit -Hn 33554432
ulimit -Sn 33554432
Java 파일 디스크립터 제한
이제 운영 체제는 협조적이지만, JVM도 이 실험을 좋아하지 않습니다. 실험을 실행하면 여전히 동일하거나 유사한 스택 트레이스가 발생합니다.
스택오버플로우는 JVM 플래그를 해결책으로 제시합니다:
-XX:-MaxFDLimit
: 열린 파일 디스크립터 수에 대한 소프트 제한을 하드 제한으로 설정하려는 시도를 비활성화합니다. 기본적으로 이 옵션은 모든 플랫폼에서 활성화되어 있지만 Windows에서는 무시됩니다. 이를 비활성화해야 할 유일한 경우는 Mac OS에서인데, 여기서는 사용하면 실제 시스템 최대값보다 낮은 10240의 최대값이 적용됩니다.
java -XX:-MaxFDLimit Main 6000
위의 자바 문서 인용문에서 언급했듯이, 이는 Mac에서만 필요합니다. Ubuntu에서는 이 플래그 없이도 실험을 실행할 수 있었습니다.
소스 포트
그래도 작동하지 않을 것입니다. 다음과 같은 스택 트레이스가 발생합니다:
Exception in thread "main" java.net.BindException: Can't assign requested address
at java.base/sun.nio.ch.Net.bind0(Native Method)
...
마지막 과제는 TCP/IP 사양입니다. 현재 서버 주소, 서버 포트 및 클라이언트 IP 주소를 고정했습니다. 이것은 우리에게 16비트의 자유만 남깁니다. 결과적으로 65k 연결만 열 수 있습니다.
우리의 실험은 그 이상으로 진행됩니다. 서버 IP나 서버 포트는 이 실험에서 탐색하는 문제이기 때문에 변경할 수 없습니다. 이는 클라이언트 IP를 변경하는 것만 남게 되어 추가적으로 32비트의 자유를 제공합니다. 결과적으로, 우리는 보수적으로 5,000개의 클라이언트 연결마다 클라이언트 IP 주소를 할당하여 이 문제를 해결합니다. 이는 Phoenix의 실험에서 사용한 것과 동일한 기술입니다.
BigSur 11.4에서는 다음과 같이 많은 가짜 루프백 주소를 추가할 수 있습니다:
for i in `seq 0 200`; do sudo ifconfig lo0 alias 10.0.0.$i/8 up ; done
IP 주소가 작동하는지 확인하려면 다음과 같이 핑을 보낼 수 있습니다:
for i in `seq 0 200`; do ping -c 1 10.0.0.$i ; done
제거하려면:
for i in `seq 0 200`; do sudo ifconfig lo0 alias 10.0.0.$i ; done
Ubuntu 20.04에서는 대신 ip 도구를 사용해야 합니다:
for i in `seq 0 200`; do sudo ip addr add 10.0.0.$i/8 dev lo; done
제거하려면:
for i in `seq 0 200`; do sudo ip addr del 10.0.0.$i/8 dev lo; done
결과
Mac에서는 80,000개까지 도달할 수 있었습니다. 그러나 신비롭게도 실험을 완료한 후 몇 분 뒤에 내 불쌍한 Mac은 /Library/Logs/DiagnosticReports에 충돌 보고서 없이 매번 충돌했습니다. 그래서 무슨 일이 일어났는지 진단할 수 없습니다.
내 Mac의 tcp 송수신 버퍼는 131072 바이트입니다:
sysctl net | grep tcp | grep -E '(recv)|(send)'
net.inet.tcp.sendspace: 131072
net.inet.tcp.recvspace: 131072
그래서 80000 연결 * 131072 바이트 버퍼당 * 2 입력 및 출력 버퍼 * 2 클라이언트 및 서버 연결 바이트를 사용했기 때문일 수 있으며, 이는 약 39GB 가상 메모리입니다. 또는, Mac OS가 80,00022=320,000개의 파일 디스크립터를 사용하는 것을 좋아하지 않았을 수 있습니다. 안타깝게도 충돌 보고서 없이 Mac에서 디버깅하는 데 익숙하지 않아서, 자료가 있다면 알려주세요.
Linux에서는 840,000개에 도달할 수 있었습니다! 그러나 실험이 진행되는 동안 마우스 움직임이 화면에 등록되는 데 몇 초가 걸렸습니다. 그 이상은 Linux가 멈추고 응답하지 않게 되었습니다.
sysstat를 사용하여 어떤 리소스가 경합 상태인지 조사했습니다. 여기에서 sysstat가 생성한 그래프를 볼 수 있습니다.
다음 명령을 사용하여 sysstat가 모든 것에 대한 하드웨어 통계를 기록한 다음 그래프를 생성하게 했습니다:
sar -o out.840000.sar -A 1 3600 2>&1 > /dev/null &
sadf -g out.840000.sar -- -w -r -u -n SOCK -n TCP -B -S -W > out.840000.svg
몇 가지 흥미로운 사실:
- MBmemfree는 96 MB까지 떨어졌습니다
- MBavail은 여전히 1587MB였습니다
- MBmemused는 1602MB(총 8GB의 19.6%)에 불과했습니다
- MBswpused는 1086MB까지 올라갔습니다(메모리가 여전히 사용 가능함에도)
- 1,680,483 소켓(840k 서버 소켓과 840k 클라이언트 연결 및 데스크톱에서 실행 중인 기타)
- OS는 더 많은 메모리가 있음에도 실험 시작 몇 초 후에 스왑을 사용하기 시작했습니다
Linux에서 송수신 버퍼의 기본 크기를 찾으려면 다음을 사용할 수 있습니다:
# 최소, 기본 및 최대 메모리 크기 값(바이트)
cat /proc/sys/net/ipv4/tcp_rmem
4096 131072 6291456
cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4194304
sysctl net.ipv4.tcp_rmem
net.ipv4.tcp_rmem = 4096 131072 6291456
sysctl net.ipv4.tcp_wmem
net.ipv4.tcp_wmem = 4096 16384 4194304
모든 연결을 지원하려면 247GB의 가상 메모리가 필요합니다!
수신용 131072 바이트
쓰기용 16384
(131072+16384)*2*840000
=247 GB 가상 메모리
버퍼가 요청되었지만, 각각에서 4 바이트만 필요했기 때문에 버퍼의 일부만 사용되었다고 생각합니다. 버퍼에 정수를 쓰기 위해 4 바이트만 쓰면 되므로 메모리의 1 페이지만 로드하더라도:
getconf PAGESIZE
4096
4096 바이트 페이지 크기
(4096+4096)*2*840000
=13 GB
2*840000 페이지의 메모리를 터치함으로써 13GB를 사용합니다. 이것이 어떻게 충돌하지 않았는지 모르겠습니다! 하지만 840,000개의 동시 연결로도 만족합니다.
더 많은 메모리가 있거나 tcp 버퍼 크기를 줄이는 등 OS 설정을 더 최적화하면 내 결과를 향상시킬 수 있습니다. 여러분은 얼마나 실행할 수 있나요? 알려주세요!
요약
- Phoenix Framework는 2,000,000개의 연결을 달성했습니다
- WhatsApp은 2,000,000개의 연결을 달성했습니다
- 이론적 한계는 약 1 쿼드릴리언(1,000,000,000,000,000)입니다
- 소스 포트가 부족해집니다(2^16만 있음)
- 루프백 클라이언트 IP 주소를 추가하여 이 문제를 해결할 수 있습니다
- 파일 디스크립터가 부족해집니다
- OS의 파일 디스크립터 제한을 재정의하여 이 문제를 해결할 수 있습니다
- Java도 파일 디스크립터를 제한합니다
- -XX:MaxFDLimit JVM 인자를 추가하여 이를 재정의할 수 있습니다
- 16GB Mac에서의 실질적 한계는 80,000입니다
- 8GB Linux에서의 실질적 한계는 840,000입니다
질문하신 내용은 TCP/IP 연결과 서버의 동시 연결 처리 능력에 대한 오해를 다룬 글인데, 전체적으로 기술적으로 정확하며 논리적으로 잘 구성되어 있습니다. 하지만 몇 가지 세부 사항에서 오류나 모호함이 있을 수 있는지 검토해보겠습니다. 아래에서 글의 주요 주장과 설명을 분석하며 틀린 부분이 있는지 확인하겠습니다.
1. 주요 주장 검토
- 주장: 서버가 최대 65,536개의 연결만 처리할 수 있다는 오해가 있으며, 이는 포트 번호 제한(2^16)에서 비롯됨.
- 설명: 클라이언트 측에서 단일 IP와 포트 쌍으로 서버에 연결할 때 65,536개의 연결이 한계라는 점은 맞습니다. 하지만 서버 측에서는 단일 포트로 수많은 연결을 처리할 수 있으며, 이는 소스 IP(32비트)와 소스 포트(16비트)의 조합으로 고유하게 식별되기 때문입니다.
- 평가: 이 부분은 정확합니다. TCP 연결은 4-tuple(소스 IP, 소스 포트, 대상 IP, 대상 포트)로 정의되며, 서버가 단일 포트에서 수신 대기할 경우 이론적으로 2^48(약 280조) 개의 연결을 구분할 수 있습니다.
- 주장: WhatsApp과 Phoenix 프레임워크가 단일 포트에서 200만 개의 동시 연결을 처리함.
- 설명: Phoenix는 Elixir 기반으로 200만 웹소켓 연결을 시연했고, WhatsApp도 유사한 성과를 달성했다는 사례를 듭니다.
- 평가: 이 사례는 실제로 존재하며 잘 알려져 있습니다. Phoenix의 벤치마크는 공개적으로 문서화되어 있고, WhatsApp의 경우도 Erlang 기반으로 유사한 성능을 달성했다는 보고가 있습니다. 오류 없음.
2. 이론적 최대치 분석
- 주장: 단일 포트에서 서버가 지원할 수 있는 이론적 최대 연결 수는 2^48(약 1 쿼드릴리언, 10^15)이다.
- 설명: 서버는 클라이언트의 소스 IP(2^32)와 소스 포트(2^16)를 조합하여 연결을 식별하므로, 2^32 × 2^16 = 2^48이 된다.
- 오류 여부: 계산은 맞지만, “약 1 쿼드릴리언”이라는 표현에서 오류가 있습니다. 2^48은 정확히 281,474,976,710,656(약 281조)입니다. 1 쿼드릴리언은 10^15(1,000,000,000,000,000)으로, 2^48보다 약간 큽니다. 로그 계산(
log(2^48)/log(10) ≈ 14.449
)도 대략 맞지만, “약 1 쿼드릴리언”은 수치적으로 정확하지 않습니다. 더 정확히는 “약 280조”로 표현해야 합니다.
3. 실험 분석
- 주장: 실험에서 Mac에서 80,000개, Linux에서 840,000개의 동시 연결을 달성했다.
- 설명: OS 설정(파일 디스크립터 제한), JVM 설정, 소스 포트 부족 문제를 해결하며 실험을 진행했다는 내용입니다.
- 평가:
- 파일 디스크립터: 각 소켓에 송신/수신 버퍼로 2개의 파일 디스크립터가 필요하다는 설명은 정확합니다. 100만 연결 × 2(클라이언트+서버) × 2 = 400만 파일 디스크립터라는 계산도 맞습니다.
- OS 설정: Mac과 Ubuntu에서 파일 디스크립터 제한을 늘리는 명령어는 올바르게 제시되었으며, 실제로 이런 방식으로 제한을 완화할 수 있습니다.
- 소스 포트 부족: 클라이언트 측에서 65,536개 연결 제한을 초과하려면 추가 IP(예: 루프백 주소)를 사용하는 해결책은 타당합니다. Phoenix에서도 유사한 접근을 사용했다는 점도 일치합니다.
- 결과: Mac에서 80,000개, Linux에서 840,000개라는 결과는 하드웨어와 설정에 따라 달라질 수 있지만, 기술적으로 불가능하지 않습니다. 메모리 사용량 계산(버퍼 크기 기반)도 대체로 타당합니다.
- 잠재적 오류:
- Mac에서 충돌 원인을 “39GB 가상 메모리” 또는 “32만 파일 디스크립터”라고 추측했는데, 이는 정확한 디버깅 없이 가정에 불과합니다. 실제로는 커널 메모리 부족, 스레드 제한, 기타 자원 경합일 가능성도 있습니다.
- Linux에서 840,000개 연결 시 “247GB 가상 메모리”가 필요하다는 계산은 TCP 버퍼 크기(
tcp_rmem
,tcp_wmem
)를 기반으로 한 것이지만, 실제 사용량은 페이지 단위로 할당되므로 과대 추정일 수 있습니다. 글에서 스스로 “4바이트만 사용했으므로 13GB”라고 수정한 점은 타당합니다.
4. 요약 검토
- 요약은 글의 내용을 잘 반영하며, 주요 수치(이론적 한계 2^48, Phoenix/WhatsApp 200만, 실험 결과 80,000/840,000)와 해결책(파일 디스크립터 증가, 추가 IP 할당 등)이 정확히 제시되었습니다.
- 단, “이론적 한계 약 1 쿼드릴리언”은 앞서 지적한 대로 “약 280조”로 수정해야 더 정확합니다.
결론: 틀린 내용이 있는가?
- 큰 오류는 없음: 글 전체적으로 TCP/IP 동작 방식, 실험 설계, 결과 해석이 기술적으로 타당합니다. WhatsApp, Phoenix 사례와 이론적 계산도 잘 맞습니다.
- 작은 오류/개선점:
- “약 1 쿼드릴리언”: 2^48은 약 281조(2.81 × 10^14)이며, 1 쿼드릴리언(10^15)과는 약간 차이가 있으므로 “약 280조”로 표현하는 것이 더 정확합니다.
- 실험 결과 해석: Mac 충돌 원인이나 Linux의 메모리 사용량 계산에서 추측이 포함되어 있어, 더 구체적인 디버깅 데이터가 있다면 보완될 수 있습니다. 하지만 이는 오류라기보다는 정보 부족에 가깝습니다.
최종 답변: 내용에 큰 틀린 부분은 없으며, 기술적으로 정확합니다. 다만, 이론적 한계를 “약 1 쿼드릴리언” 대신 “약 280조”로 수정하면 더 정확해질 것입니다. 실험 결과도 제시된 하드웨어와 설정을 고려할 때 충분히 타당합니다.
안녕하세요! 요청하신 대로 블로그 게시물이나 기사 내용을 검토해보았습니다. 신문기사로 사용될 예정이라면 정확성이 특히 중요하니, 주요 주장과 세부 사항을 꼼꼼히 확인했습니다. 아래는 검토 결과입니다.
주요 내용 검토
- WhatsApp과 Phoenix 프레임워크의 동시 연결 처리 능력
게시물에서 WhatsApp과 Phoenix 프레임워크가 단일 포트에서 200만 개의 동시 연결을 처리할 수 있었다고 주장하고 있습니다.
- 확인 결과: 이는 사실입니다. Phoenix 프레임워크는 Elixir와 Erlang 기반으로 잘 알려진 벤치마크에서 이 수준의 성능을 입증한 바 있으며, WhatsApp 역시 Erlang을 사용해 유사한 규모의 연결을 처리했다는 보고가 있습니다. 이 부분은 기술적으로 정확합니다.
- TCP/IP 프로토콜의 이론적 한계
저자는 단일 포트에서 서버가 지원할 수 있는 이론적 최대 연결 수를 계산했는데, 소스 IP(32비트)와 소스 포트(16비트)의 조합으로 2^48, 즉 약 280조(2.81 × 10^14)라고 설명합니다.
- 확인 결과: 이 계산은 정확합니다. TCP/IP에서 연결은 소스 IP, 소스 포트, 목적 IP, 목적 포트로 식별되며, 이론적으로 단일 포트 기준 최대 연결 수는 2^48입니다.
- 수정 제안: 다만, 게시물에서 이 값을 “약 1 쿼드릴리언(10^15)”이라고 표현했다면 이는 부정확합니다. 실제 값은 약 280조(2.81 × 10^14)이므로, “약 280조”로 수정하는 것이 맞습니다.
- 저자의 실험 결과
저자는 실험을 통해 Mac에서 80,000개, Linux에서 840,000개의 동시 연결을 달성했다고 합니다. 이는 파일 디스크립터 제한, JVM 설정, 소스 포트 부족 문제를 해결한 결과라고 설명합니다.
- 확인 결과: 이 결과는 기술적으로 타당합니다. 운영체제와 하드웨어 설정에 따라 파일 디스크립터 수나 메모리 제한이 동시 연결에 영향을 미치며, 저자가 언급한 문제를 해결했다면 해당 수치는 충분히 달성 가능합니다.
- 보완 제안: Mac에서 충돌이 발생한 원인을 저자가 메모리 사용량이나 파일 디스크립터 수로 추측했는데, 구체적인 디버깅 데이터가 없으면 이는 가정에 불과합니다. 신문기사로 사용하려면 이 부분을 “추측”임을 명확히 하거나, 더 구체적인 데이터를 추가하면 신뢰도가 높아질 것입니다.
종합 평가
게시물의 내용은 전반적으로 사실에 기반하며, 기술적으로 타당합니다. WhatsApp과 Phoenix의 사례, TCP/IP 이론적 한계 계산, 실험 결과 모두 신뢰할 만합니다. 다만, 아래 두 가지 수정 및 보완을 제안드립니다:
- 수정: 이론적 최대 연결 수를 “약 1 쿼드릴리언(10^15)” 대신 “약 280조(2.81 × 10^14)”로 수정하세요.
- 보완: 실험 결과(특히 Mac 충돌 원인)에 대한 해석을 구체적인 데이터로 뒷받침하거나, 데이터가 없다면 “추정”임을 명확히 밝히세요.
이 조정을 반영하면 신문기사로 사용하기에 충분히 정확하고 신뢰할 만한 내용이 될 것입니다. 추가로 확인하고 싶은 부분이 있다면 말씀해주세요!