<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>client Archives - 어제와 내일의 나 그 사이의 이야기</title>
	<atom:link href="https://lycos7560.com/tag/client/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>생각의 흐름을 타고 다니며 만드는 블로그</description>
	<lastBuildDate>Wed, 15 Oct 2025 05:36:10 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>

<image>
	<url>https://lycos7560.com/wp-content/uploads/2022/11/cropped-cropped-cropped-log-1-150x150-1-80x80.png</url>
	<title>client Archives - 어제와 내일의 나 그 사이의 이야기</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>리버스 프록시(Reverse Proxy) 개념</title>
		<link>https://lycos7560.com/etc/%eb%a6%ac%eb%b2%84%ec%8a%a4-%ed%94%84%eb%a1%9d%ec%8b%9creverse-proxy-%ea%b0%9c%eb%85%90/40122/</link>
					<comments>https://lycos7560.com/etc/%eb%a6%ac%eb%b2%84%ec%8a%a4-%ed%94%84%eb%a1%9d%ec%8b%9creverse-proxy-%ea%b0%9c%eb%85%90/40122/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sun, 27 Jul 2025 17:54:12 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[기타]]></category>
		<category><![CDATA[A/B Testing]]></category>
		<category><![CDATA[A/B 테스트]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[API Gateway]]></category>
		<category><![CDATA[API 게이트웨이]]></category>
		<category><![CDATA[Application Proxy]]></category>
		<category><![CDATA[Asynchronous Processing]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Backend Server]]></category>
		<category><![CDATA[Caching]]></category>
		<category><![CDATA[CDN]]></category>
		<category><![CDATA[Certificate Management]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Compression]]></category>
		<category><![CDATA[Container]]></category>
		<category><![CDATA[Content Delivery]]></category>
		<category><![CDATA[Content Delivery Network]]></category>
		<category><![CDATA[Cookie Management]]></category>
		<category><![CDATA[DDoS Protection]]></category>
		<category><![CDATA[DDoS 방어]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Distributed Processing]]></category>
		<category><![CDATA[Edge Computing]]></category>
		<category><![CDATA[Envoy]]></category>
		<category><![CDATA[Error Page]]></category>
		<category><![CDATA[firewall]]></category>
		<category><![CDATA[Gateway]]></category>
		<category><![CDATA[Gateway Pattern]]></category>
		<category><![CDATA[GCP]]></category>
		<category><![CDATA[HAProxy]]></category>
		<category><![CDATA[Header Manipulation]]></category>
		<category><![CDATA[High Availability]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[HTTP/2]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[Inbound Traffic]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Intermediary]]></category>
		<category><![CDATA[Kestrel]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[L7 스위치]]></category>
		<category><![CDATA[Layer 7 Switch]]></category>
		<category><![CDATA[Load Balancing]]></category>
		<category><![CDATA[Log Analysis]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[MSA]]></category>
		<category><![CDATA[Multi-tenancy]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[Offloading]]></category>
		<category><![CDATA[On-premise]]></category>
		<category><![CDATA[Operations]]></category>
		<category><![CDATA[Outbound Proxy]]></category>
		<category><![CDATA[Performance Optimization]]></category>
		<category><![CDATA[Protocol Conversion]]></category>
		<category><![CDATA[Proxy]]></category>
		<category><![CDATA[QUIC]]></category>
		<category><![CDATA[Request Forwarding]]></category>
		<category><![CDATA[Request Routing]]></category>
		<category><![CDATA[Response]]></category>
		<category><![CDATA[Reverse Proxy]]></category>
		<category><![CDATA[Scalability]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Security Layer]]></category>
		<category><![CDATA[Server Protection]]></category>
		<category><![CDATA[Service Discovery]]></category>
		<category><![CDATA[Service Mesh]]></category>
		<category><![CDATA[Service Proxy]]></category>
		<category><![CDATA[Session Persistence]]></category>
		<category><![CDATA[SSL Termination]]></category>
		<category><![CDATA[SSL 종료]]></category>
		<category><![CDATA[State Management]]></category>
		<category><![CDATA[Sticky Session]]></category>
		<category><![CDATA[System Architecture]]></category>
		<category><![CDATA[TLS Handshake]]></category>
		<category><![CDATA[TLS 핸드셰이크]]></category>
		<category><![CDATA[Traffic Management]]></category>
		<category><![CDATA[Traffic Splitting]]></category>
		<category><![CDATA[URL Rewriting]]></category>
		<category><![CDATA[URL 재작성]]></category>
		<category><![CDATA[User Experience]]></category>
		<category><![CDATA[UX]]></category>
		<category><![CDATA[Virtual Hosting]]></category>
		<category><![CDATA[WAF]]></category>
		<category><![CDATA[Web Acceleration]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[Web Application Firewall]]></category>
		<category><![CDATA[Web Cache]]></category>
		<category><![CDATA[Web Proxy]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Web Service]]></category>
		<category><![CDATA[가상 호스팅]]></category>
		<category><![CDATA[개발자]]></category>
		<category><![CDATA[게이트웨이]]></category>
		<category><![CDATA[게이트웨이 패턴]]></category>
		<category><![CDATA[고가용성]]></category>
		<category><![CDATA[네트워크]]></category>
		<category><![CDATA[데브옵스]]></category>
		<category><![CDATA[로그 분석]]></category>
		<category><![CDATA[로드 밸런싱]]></category>
		<category><![CDATA[리버스 프록시]]></category>
		<category><![CDATA[마이크로서비스]]></category>
		<category><![CDATA[멀티테넌시]]></category>
		<category><![CDATA[모니터링]]></category>
		<category><![CDATA[방화벽]]></category>
		<category><![CDATA[배포]]></category>
		<category><![CDATA[백엔드 서버]]></category>
		<category><![CDATA[보안]]></category>
		<category><![CDATA[보안 계층]]></category>
		<category><![CDATA[부하 분산]]></category>
		<category><![CDATA[부하 분산 장치]]></category>
		<category><![CDATA[분산 처리]]></category>
		<category><![CDATA[비동기 처리]]></category>
		<category><![CDATA[사용자 경험]]></category>
		<category><![CDATA[상태 관리]]></category>
		<category><![CDATA[서버 보호]]></category>
		<category><![CDATA[서비스 디스커버리]]></category>
		<category><![CDATA[서비스 메쉬]]></category>
		<category><![CDATA[서비스 프록시]]></category>
		<category><![CDATA[성능 최적화]]></category>
		<category><![CDATA[세션 유지]]></category>
		<category><![CDATA[시스템 아키텍처]]></category>
		<category><![CDATA[아웃바운드 프록시]]></category>
		<category><![CDATA[압축]]></category>
		<category><![CDATA[애플리케이션 프록시]]></category>
		<category><![CDATA[에러 페이지]]></category>
		<category><![CDATA[엣지 컴퓨팅]]></category>
		<category><![CDATA[오프로딩]]></category>
		<category><![CDATA[온프레미스]]></category>
		<category><![CDATA[요청 라우팅]]></category>
		<category><![CDATA[요청 전달]]></category>
		<category><![CDATA[운영]]></category>
		<category><![CDATA[웹 가속]]></category>
		<category><![CDATA[웹 방화벽]]></category>
		<category><![CDATA[웹 서버]]></category>
		<category><![CDATA[웹 서비스]]></category>
		<category><![CDATA[웹 애플리케이션]]></category>
		<category><![CDATA[웹 캐시]]></category>
		<category><![CDATA[웹 프록시]]></category>
		<category><![CDATA[응답]]></category>
		<category><![CDATA[인가]]></category>
		<category><![CDATA[인바운드 트래픽]]></category>
		<category><![CDATA[인증]]></category>
		<category><![CDATA[인증서 관리]]></category>
		<category><![CDATA[인프라]]></category>
		<category><![CDATA[중간자]]></category>
		<category><![CDATA[캐싱]]></category>
		<category><![CDATA[컨테이너]]></category>
		<category><![CDATA[콘텐츠 전송]]></category>
		<category><![CDATA[쿠버네티스]]></category>
		<category><![CDATA[쿠키 관리]]></category>
		<category><![CDATA[클라우드]]></category>
		<category><![CDATA[클라이언트]]></category>
		<category><![CDATA[트래픽 관리]]></category>
		<category><![CDATA[트래픽 분할]]></category>
		<category><![CDATA[프로토콜 변환]]></category>
		<category><![CDATA[프록시]]></category>
		<category><![CDATA[헤더 조작]]></category>
		<category><![CDATA[확장성]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40122</guid>

					<description><![CDATA[<p>❓ 리버스 프록시(Reverse Proxy) 리버스 프록시(Reverse Proxy) 서버는 인터넷으로부터의 클라이언트 요청을 받아 백엔드 서버(실제 콘텐츠나 애플리케이션을 호스팅하는 서버)로 전달하고, 백엔드 서버로부터 받은 응답을 다시 클라이언트에게 전달하는 서버입니다. 정리하자면 웹 서비스의 최전선에 위치하여 클라이언트와 실제 서버 사이의 중개자 역할 일반적인 포워드 프록시(Forward Proxy)가 클라이언트가 인터넷에 접속하기 위해 사용하는 것(예: 회사 네트워크에서 외부 웹사이트 접속)과는 반대로, 리버스 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/etc/%eb%a6%ac%eb%b2%84%ec%8a%a4-%ed%94%84%eb%a1%9d%ec%8b%9creverse-proxy-%ea%b0%9c%eb%85%90/40122/">리버스 프록시(Reverse Proxy) 개념</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-32d5faea      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#리버스-프록시reverse-proxy" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 리버스 프록시(Reverse Proxy)</a><li class="uagb-toc__list"><a href="#리버스-프록시는-왜-사용하는가" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f300.png" alt="🌀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 리버스 프록시는 왜 사용하는가?</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-보안-강화-security" class="uagb-toc-link__trigger">1&#x20e3; 보안 강화 (Security)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-로드-밸런싱-load-balancing" class="uagb-toc-link__trigger">2&#x20e3; 로드 밸런싱 (Load Balancing)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-성능-최적화-performance-optimization" class="uagb-toc-link__trigger">3&#x20e3; 성능 최적화 (Performance Optimization)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-쉬운-유지보수-및-확장-maintainability-scalability" class="uagb-toc-link__trigger">4&#x20e3; 쉬운 유지보수 및 확장 (Maintainability &amp; Scalability)</a></li></ul></li><li class="uagb-toc__list"><a href="#동작-방식-요약" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d9.png" alt="📙" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 동작 방식 요약</a><li class="uagb-toc__list"><a href="#대표적인-리버스-프록시-소프트웨어" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60a.png" alt="😊" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 대표적인 리버스 프록시 소프트웨어</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 리버스 프록시(Reverse Proxy)</h2>



<p class="wp-block-paragraph"><strong>리버스 프록시(Reverse Proxy)</strong> 서버는 인터넷으로부터의 클라이언트 요청을 받아 백엔드 서버(실제 콘텐츠나 애플리케이션을 호스팅하는 서버)로 전달하고, </p>



<p class="wp-block-paragraph">백엔드 서버로부터 받은 응답을 다시 클라이언트에게 전달하는 서버입니다. </p>



<p class="wp-block-paragraph">정리하자면 <strong>웹 서비스의 최전선에 위치하여 클라이언트와 실제 서버 사이의 중개자 역할</strong></p>



<p class="wp-block-paragraph">일반적인 <strong>포워드 프록시(Forward Proxy)가 클라이언트가 인터넷에 접속하기 위해 사용하는 것(예: 회사 네트워크에서 외부 웹사이트 접속)</strong>과는 반대로, </p>



<p class="wp-block-paragraph"><strong>리버스 프록시(Reverse Proxy)는 서버 앞단에서 서버로 들어오는 요청을 처리</strong>합니다.</p>



<div class="wp-block-uagb-container uagb-block-fbf8b3c1 alignfull uagb-is-root-container"><div class="uagb-container-inner-blocks-wrap">
<div class="wp-block-uagb-container uagb-block-3a8748f6">
<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1920" height="720" src="https://lycos7560.com/wp-content/uploads/2025/07/Reverse_proxy_h2g2bob.svg_.png" alt="" class="wp-image-40123" srcset="https://lycos7560.com/wp-content/uploads/2025/07/Reverse_proxy_h2g2bob.svg_.png 1920w, https://lycos7560.com/wp-content/uploads/2025/07/Reverse_proxy_h2g2bob.svg_-300x113.png 300w, https://lycos7560.com/wp-content/uploads/2025/07/Reverse_proxy_h2g2bob.svg_-768x288.png 768w, https://lycos7560.com/wp-content/uploads/2025/07/Reverse_proxy_h2g2bob.svg_-1536x576.png 1536w" sizes="(max-width: 1920px) 100vw, 1920px" /><figcaption class="wp-element-caption">Reverse Proxy<br><a href="https://ko.wikipedia.org/wiki/%EB%A6%AC%EB%B2%84%EC%8A%A4_%ED%94%84%EB%A1%9D%EC%8B%9C#/media/%ED%8C%8C%EC%9D%BC:Reverse_proxy_h2g2bob.svg" target="_blank" rel="noreferrer noopener">https://ko.wikipedia.org/wiki/%EB%A6%AC%EB%B2%84%EC%8A%A4_%ED%94%84%EB%A1%9D%EC%8B%9C#/media/%ED%8C%8C%EC%9D%BC:Reverse_proxy_h2g2bob.svg</a></figcaption></figure>
</div>



<div class="wp-block-uagb-container uagb-block-68dc1c5c">
<figure class="wp-block-image size-full"><img decoding="async" width="1920" height="960" src="https://lycos7560.com/wp-content/uploads/2025/07/forward_proxy_flow.png" alt="" class="wp-image-40124" srcset="https://lycos7560.com/wp-content/uploads/2025/07/forward_proxy_flow.png 1920w, https://lycos7560.com/wp-content/uploads/2025/07/forward_proxy_flow-300x150.png 300w, https://lycos7560.com/wp-content/uploads/2025/07/forward_proxy_flow-768x384.png 768w, https://lycos7560.com/wp-content/uploads/2025/07/forward_proxy_flow-1536x768.png 1536w" sizes="(max-width: 1920px) 100vw, 1920px" /><figcaption class="wp-element-caption"><strong>Forward Proxy</strong><br><a href="https://www.cloudflare.com/ko-kr/learning/cdn/glossary/reverse-proxy/" target="_blank" rel="noreferrer noopener">https://www.cloudflare.com/ko-kr/learning/cdn/glossary/reverse-proxy/</a></figcaption></figure>
</div>
</div></div>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f300.png" alt="🌀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 리버스 프록시는 왜 사용하는가?</h2>



<p class="wp-block-paragraph">리버스 프록시는 아래과 같은 주요 이점 때문에 웹 아키텍처에서 필수적인 요소로 자리를 잡았습니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1&#x20e3; 보안 강화 (Security)</h3>



<ul class="wp-block-list">
<li><strong>직접적인 서버 노출 방지:</strong> <br>실제 백엔드 서버의 IP 주소나 내부 구조를 외부 클라이언트에게 숨겨 보안 위험을 줄입니다. 공격자는 리버스 프록시만 볼 수 있습니다.</li>



<li><strong>공격 방어:</strong> <br>DDoS 공격, SQL Injection, XSS 공격 등 다양한 웹 공격에 대한 1차 방어선 역할을 수행합니다. 수상한 트래픽을 필터링하거나 차단할 수 있습니다.</li>



<li><strong>SSL/TLS 암호화 종료 (SSL/TLS Termination):</strong> <br>클라이언트와의 암호화된 통신(HTTPS)을 리버스 프록시가 대신 처리하고, 백엔드 서버와의 통신은 암호화되지 않은 HTTP로 전환할 수 있습니다.<br>이는 백엔드 서버의 부하를 줄여주고, SSL 인증서 관리의 중앙화를 가능하게 합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2&#x20e3; 로드 밸런싱 (Load Balancing)</h3>



<ul class="wp-block-list">
<li>리버스 프록시는 여러 개의 백엔드 서버로 트래픽을 효율적으로 분산하여 <strong>특정 서버에 부하가 집중되는 것을 방지</strong>합니다. <br>이를 통해 서비스의 <strong>안정성과 가용성을 높이고, 대규모 트래픽을 처리할 수 있도록 확장성을 제공</strong>합니다.</li>



<li>서버 중 하나에 문제가 발생하면, 해당 서버로의 트래픽을 중단하고 정상 작동하는 다른 서버로 요청을 보낼 수 있습니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3&#x20e3; 성능 최적화 (Performance Optimization)</h3>



<ul class="wp-block-list">
<li><strong>캐싱 (Caching):</strong> <br>자주 요청되는 정적 콘텐츠(이미지, CSS, JavaScript 파일 등)를 리버스 프록시 서버에 캐싱하여, <br>백엔드 서버로의 요청을 줄이고 클라이언트에게 더 빠르게 응답할 수 있습니다.</li>



<li><strong>압축 (Compression):</strong> <br>클라이언트에게 보내는 응답 데이터를 압축하여 전송량을 줄이고 로딩 속도를 향상시킵니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">4&#x20e3; 쉬운 유지보수 및 확장 (Maintainability &amp; Scalability)</h3>



<ul class="wp-block-list">
<li>백엔드 서버를 추가하거나 제거할 때, 클라이언트는 리버스 프록시의 주소만 알기 때문에 변경 사항을 알 필요가 없습니다. <br>이는 서버 아키텍처를 유연하게 관리할 수 있도록 해줍니다.</li>



<li>A/B 테스트, URL 재작성, 특정 요청 라우팅 등 복잡한 트래픽 관리 정책을 구현하기 용이합니다.</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d9.png" alt="📙" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 동작 방식 요약</h2>



<ol class="wp-block-list">
<li><strong>클라이언트 요청:</strong> <br>웹 브라우저와 같은 클라이언트가 특정 웹 서비스(예: <code>www.example.com</code>)에 요청을 보냅니다.</li>



<li><strong>리버스 프록시 수신:</strong> <br>이 요청은 DNS 설정을 통해 <strong>리버스 프록시 서버</strong>로 전달됩니다.</li>



<li><strong>요청 처리 및 전달:</strong> <br>리버스 프록시 서버는 요청을 검사하고, 설정된 규칙(로드 밸런싱 알고리즘, URL 경로 등)에 따라 적절한 <strong>백엔드 서버</strong>를 선택하여 요청을 전달합니다. <br>(이때, SSL/TLS 종료가 이루어질 수 있습니다.)</li>



<li><strong>응답 수신 및 전달:</strong> <br>백엔드 서버는 요청을 처리하고 응답을 리버스 프록시 서버로 보냅니다. <br>리버스 프록시는 이 응답을 받아 (캐싱, 압축 등의 처리를 거쳐) 최종적으로 클라이언트에게 전달합니다.</li>
</ol>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60a.png" alt="😊" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 대표적인 리버스 프록시 소프트웨어</h2>



<ul class="wp-block-list">
<li><strong>Nginx (엔진엑스):</strong> <br>가장 널리 사용되는 웹 서버이자 리버스 프록시 서버입니다. 고성능과 경량성이 특징입니다.</li>



<li><strong>Apache HTTP Server (아파치):</strong> <br><code>mod_proxy</code> 모듈을 통해 리버스 프록시 기능을 제공합니다.</li>



<li><strong>HAProxy:</strong> <br>고가용성 로드 밸런싱 및 프록싱에 특화된 솔루션입니다.</li>



<li><strong>Envoy Proxy:</strong> <br>마이크로서비스 아키텍처에서 많이 사용되는 최신 프록시입니다.</li>
</ul>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://lycos7560.com/etc/%eb%a6%ac%eb%b2%84%ec%8a%a4-%ed%94%84%eb%a1%9d%ec%8b%9creverse-proxy-%ea%b0%9c%eb%85%90/40122/">리버스 프록시(Reverse Proxy) 개념</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/etc/%eb%a6%ac%eb%b2%84%ec%8a%a4-%ed%94%84%eb%a1%9d%ec%8b%9creverse-proxy-%ea%b0%9c%eb%85%90/40122/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>.NET의 파이프 작업 (Pipe Operations in .NET)</title>
		<link>https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/</link>
					<comments>https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Tue, 15 Jul 2025 05:08:17 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[.NETCore]]></category>
		<category><![CDATA[.NETFramework]]></category>
		<category><![CDATA[AnonymousPipe]]></category>
		<category><![CDATA[Asynchronous]]></category>
		<category><![CDATA[BackgroundService]]></category>
		<category><![CDATA[CancellationToken]]></category>
		<category><![CDATA[ChildProcess]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[CrossProcess]]></category>
		<category><![CDATA[DataExchange]]></category>
		<category><![CDATA[DataTransfer]]></category>
		<category><![CDATA[DependencyInjection]]></category>
		<category><![CDATA[Duplex]]></category>
		<category><![CDATA[ErrorHandling]]></category>
		<category><![CDATA[Flush]]></category>
		<category><![CDATA[GenericHost]]></category>
		<category><![CDATA[HandleInheritability]]></category>
		<category><![CDATA[HostBuilder]]></category>
		<category><![CDATA[IHostedService]]></category>
		<category><![CDATA[Impersonation]]></category>
		<category><![CDATA[InterprocessCommunication]]></category>
		<category><![CDATA[IOException]]></category>
		<category><![CDATA[IPC]]></category>
		<category><![CDATA[IPCMechanism]]></category>
		<category><![CDATA[IPC메커니즘]]></category>
		<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Lifecycle]]></category>
		<category><![CDATA[LocalCommunication]]></category>
		<category><![CDATA[MemoryBuffer]]></category>
		<category><![CDATA[MessageBased]]></category>
		<category><![CDATA[NamedPipe]]></category>
		<category><![CDATA[NetworkCommunication]]></category>
		<category><![CDATA[OneWay]]></category>
		<category><![CDATA[Operations]]></category>
		<category><![CDATA[OS]]></category>
		<category><![CDATA[ParentProcess]]></category>
		<category><![CDATA[pipe]]></category>
		<category><![CDATA[Process]]></category>
		<category><![CDATA[Read]]></category>
		<category><![CDATA[Reconnect]]></category>
		<category><![CDATA[Resilience]]></category>
		<category><![CDATA[SecurityContext]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[Stream]]></category>
		<category><![CDATA[StreamReader]]></category>
		<category><![CDATA[StreamWriter]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Sync]]></category>
		<category><![CDATA[Threading]]></category>
		<category><![CDATA[Timeout]]></category>
		<category><![CDATA[UDS]]></category>
		<category><![CDATA[UnixDomainSockets]]></category>
		<category><![CDATA[VirtualFile]]></category>
		<category><![CDATA[WindowsPipes]]></category>
		<category><![CDATA[Write]]></category>
		<category><![CDATA[가상파일]]></category>
		<category><![CDATA[가장]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[네트워크통신]]></category>
		<category><![CDATA[단방향]]></category>
		<category><![CDATA[데이터교환]]></category>
		<category><![CDATA[데이터전송]]></category>
		<category><![CDATA[동기]]></category>
		<category><![CDATA[로컬통신]]></category>
		<category><![CDATA[메모리버퍼]]></category>
		<category><![CDATA[메시지기반]]></category>
		<category><![CDATA[명명된파이프]]></category>
		<category><![CDATA[백그라운드서비스]]></category>
		<category><![CDATA[보안컨텍스트]]></category>
		<category><![CDATA[복원력]]></category>
		<category><![CDATA[부모프로세스]]></category>
		<category><![CDATA[비동기]]></category>
		<category><![CDATA[생명주기]]></category>
		<category><![CDATA[서버]]></category>
		<category><![CDATA[스레딩]]></category>
		<category><![CDATA[스트림]]></category>
		<category><![CDATA[스트림라이터]]></category>
		<category><![CDATA[스트림리더]]></category>
		<category><![CDATA[쓰기]]></category>
		<category><![CDATA[양방향]]></category>
		<category><![CDATA[오류처리]]></category>
		<category><![CDATA[운영체제]]></category>
		<category><![CDATA[의존성주입]]></category>
		<category><![CDATA[익명파이프]]></category>
		<category><![CDATA[일반호스트]]></category>
		<category><![CDATA[읽기]]></category>
		<category><![CDATA[자식프로세스]]></category>
		<category><![CDATA[재연결]]></category>
		<category><![CDATA[취소토큰]]></category>
		<category><![CDATA[커널]]></category>
		<category><![CDATA[클라이언트]]></category>
		<category><![CDATA[타임아웃]]></category>
		<category><![CDATA[통신]]></category>
		<category><![CDATA[파이프오류]]></category>
		<category><![CDATA[프로세스]]></category>
		<category><![CDATA[프로세스간]]></category>
		<category><![CDATA[프로세스간통신]]></category>
		<category><![CDATA[플러시]]></category>
		<category><![CDATA[핸들상속]]></category>
		<category><![CDATA[호스트빌더]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40115</guid>

					<description><![CDATA[<p>네임드 파이프(Named Pipe)는 프로세스 간 통신(IPC) 방법 중 하나로, 파일 시스템에 이름이 지정된 파이프를 생성하여 프로세스들이 해당 파이프를 통해 데이터를 주고받을 수 있도록 합니다. 일반적인 파이프(익명 파이프)는 부모-자식 관계와 같이 명확히 연관된 프로세스 간에만 사용 가능하지만, 네임드 파이프는 서로 관련 없는 프로세스들도 이름을 통해 통신할 수 있다는 특징을 가집니다. </p>
<p>The post <a href="https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/">.NET의 파이프 작업 (Pipe Operations in .NET)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-65798460      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#net의-파이프-작업" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET의 파이프 작업</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#익명-파이프-anonymous-pipes" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 익명 파이프 (Anonymous Pipes)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#익명-파이프-anonymous-pipes-사용-방법" class="uagb-toc-link__trigger">익명 파이프 (Anonymous Pipes) 사용 방법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-1-서버-프로세스-부모-프로세스" class="uagb-toc-link__trigger">예제 1: 서버 프로세스 (부모 프로세스)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-2-클라이언트-프로세스-자식-프로세스" class="uagb-toc-link__trigger">예제 2: 클라이언트 프로세스 (자식 프로세스)</a></li></ul></li></ul><li class="uagb-toc__list"><a href="#명명된-파이프-named-pipes" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 명명된 파이프 (Named Pipes)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#명명된-파이프named-pipes-사용-방법" class="uagb-toc-link__trigger">명명된 파이프(Named Pipes) 사용 방법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-1-namedpipeserverstream-클래스를-사용하여-명명된-파이프-생성하기" class="uagb-toc-link__trigger">예제 1: NamedPipeServerStream 클래스를 사용하여 명명된 파이프 생성하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-2-namedpipeclientstream-클래스를-사용하는-클라이언트-프로세스" class="uagb-toc-link__trigger">예제 2: NamedPipeClientStream 클래스를 사용하는 클라이언트 프로세스</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#강력한-프로그래밍-named-pipe-클라이언트서버-설정" class="uagb-toc-link__trigger">강력한 프로그래밍: Named Pipe 클라이언트/서버 설정</a></li></ul></li></ul><li class="uagb-toc__list"><a href="#추가-정리" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" />추가 정리</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET의 파이프 작업</h2>



<p class="wp-block-paragraph"><a href="https://learn.microsoft.com/en-us/dotnet/standard/io/pipe-operations" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/dotnet/standard/io/pipe-operations</a></p>



<p class="wp-block-paragraph">파이프는 <strong>프로세스 간 통신(Interprocess Communication, IPC)을 위한 수단을 제공</strong>합니다. </p>



<p class="wp-block-paragraph">파이프에는 두 가지 유형이 있습니다:</p>



<ul class="wp-block-list">
<li>익명 파이프 (Anonymous Pipes)</li>



<li>명명된 파이프 (Named Pipes)</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 익명 파이프 (Anonymous Pipes)</h3>



<p class="wp-block-paragraph">익명 파이프는 로컬 컴퓨터에서 프로세스 간 통신을 제공합니다. </p>



<p class="wp-block-paragraph">익명 파이프는 명명된 파이프보다 오버헤드가 적지만 제공하는 서비스가 제한적입니다. </p>



<p class="wp-block-paragraph">익명 파이프는 <strong>단방향</strong>이며 네트워크를 통해 사용할 수 없습니다. 또한 <strong>단일 서버 인스턴스만 지원</strong>합니다. </p>



<p class="wp-block-paragraph">익명 파이프는 스레드 간 통신, 또는 파이프 핸들을 자식 프로세스가 생성될 때 쉽게 전달할 수 있는 부모-자식 프로세스 간 통신에 유용합니다.</p>



<p class="wp-block-paragraph">.NET에서는 <strong><code>AnonymousPipeServerStream</code> </strong>및 <strong><code>AnonymousPipeClientStream</code> </strong>클래스를 사용하여 익명 파이프를 구현합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">익명 파이프 (Anonymous Pipes) 사용 방법</h4>



<p class="wp-block-paragraph"><a href="https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-anonymous-pipes-for-local-interprocess-communication" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-anonymous-pipes-for-local-interprocess-communication</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 1: 서버 프로세스 (부모 프로세스)</h5>



<p class="wp-block-paragraph">다음 예제는 익명 파이프를 사용하여 부모 프로세스에서 자식 프로세스로 문자열을 보내는 방법을 보여줍니다.</p>



<p class="wp-block-paragraph">이 예제에서는 부모 프로세스에 <code>PipeDirection.Out</code> 값을 가진 <code>AnonymousPipeServerStream</code> 객체를 생성합니다. </p>



<p class="wp-block-paragraph">그런 다음 부모 프로세스는 클라이언트 핸들을 사용하여 <code>AnonymousPipeClientStream</code> 객체를 생성하는 자식 프로세스를 시작합니다. </p>



<p class="wp-block-paragraph">자식 프로세스는 <code>PipeDirection.In</code> 값을 가집니다.</p>



<p class="wp-block-paragraph">이후 부모 프로세스는 사용자가 입력한 문자열을 자식 프로세스로 보냅니다. </p>



<p class="wp-block-paragraph">해당 문자열은 자식 프로세스의 콘솔에 표시됩니다. 다음은 서버 프로세스(부모 프로세스) 코드입니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.IO;
using System.IO.Pipes;
using System.Diagnostics; // Process 클래스를 사용하기 위해 추가

class PipeServer
{
    static void Main()
    {
        Process pipeClient = new Process(); // 자식 프로세스를 제어할 Process 객체 생성

        pipeClient.StartInfo.FileName = "pipeClient.exe"; // 실행할 자식 프로세스의 파일 이름 설정

        // AnonymousPipeServerStream 객체 생성
        // PipeDirection.Out: 서버 -> 클라이언트로 데이터를 보냄 (단방향)
        // HandleInheritability.Inheritable: 자식 프로세스가 이 파이프 핸들을 상속받을 수 있도록 설정
        using (AnonymousPipeServerStream pipeServer =
            new AnonymousPipeServerStream(PipeDirection.Out,
            HandleInheritability.Inheritable))
        {
            Console.WriteLine($"[SERVER] 현재 전송 모드: {pipeServer.TransmissionMode}.");

            // 자식 프로세스에게 서버 파이프의 클라이언트 핸들을 문자열로 전달합니다.
            pipeClient.StartInfo.Arguments =
                pipeServer.GetClientHandleAsString();
            pipeClient.StartInfo.UseShellExecute = false; // 셸 실행 대신 직접 프로세스 시작
            pipeClient.Start(); // 자식 프로세스 시작

            // 자식 프로세스가 핸들을 상속받은 후, 부모 프로세스는 자신의 클라이언트 핸들 복사본을 해제합니다.
            // 이는 불필요한 리소스 유지를 방지합니다.
            pipeServer.DisposeLocalCopyOfClientHandle();

            try
            {
                // 사용자 입력을 읽고 그 내용을 클라이언트 프로세스로 보냅니다.
                using (StreamWriter sw = new StreamWriter(pipeServer))
                {
                    sw.AutoFlush = true; // 매 쓰기 작업 후 스트림 자동 플러시 설정

                    // '동기화 메시지'를 클라이언트로 보내고 클라이언트가 이를 받을 때까지 기다립니다.
                    sw.WriteLine("SYNC");
                    pipeServer.WaitForPipeDrain(); // 파이프에 기록된 모든 데이터가 전송될 때까지 대기

                    // 콘솔 입력을 클라이언트 프로세스로 보냅니다.
                    Console.Write("[SERVER] 텍스트를 입력하세요: ");
                    sw.WriteLine(Console.ReadLine());
                }
            }
            // 파이프가 끊어지거나 연결이 해제될 때 발생하는 IOException을 잡습니다.
            catch (IOException e)
            {
                Console.WriteLine($"[SERVER] 오류: {e.Message}");
            }
        } // using 블록을 벗어나면 pipeServer는 자동으로 Dispose됩니다.

        // 자식 프로세스가 종료될 때까지 기다립니다.
        pipeClient.WaitForExit();
        // 자식 프로세스 객체를 닫습니다.
        pipeClient.Close();
        Console.WriteLine("[SERVER] 클라이언트 종료. 서버도 종료합니다.");
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 2: 클라이언트 프로세스 (자식 프로세스)</h5>



<p class="wp-block-paragraph">다음 예제는 클라이언트 프로세스를 보여줍니다. </p>



<p class="wp-block-paragraph">서버 프로세스는 클라이언트 프로세스를 시작하고 해당 프로세스에 클라이언트 핸들을 전달합니다. </p>



<p class="wp-block-paragraph">클라이언트 코드로부터 생성된 실행 파일은 <code>pipeClient.exe</code>라는 이름으로 서버 실행 파일과 동일한 디렉토리에 복사되어야 서버 프로세스를 실행하기 전에 올바르게 동작합니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.IO;
using System.IO.Pipes;

class PipeClient
{
    static void Main(string[] args)
    {
        // 프로그램 인수가 있는지 확인합니다.
        // 서버가 클라이언트 핸들을 인수로 전달하기 때문에 필요합니다.
        if (args.Length > 0)
        {
            // AnonymousPipeClientStream 객체를 생성합니다.
            // PipeDirection.In: 클라이언트는 서버로부터 데이터를 받음 (단방향)
            // args[0]: 서버가 전달한 파이프 핸들 문자열
            using (PipeStream pipeClient =
                new AnonymousPipeClientStream(PipeDirection.In, args[0]))
            {
                Console.WriteLine($"[CLIENT] 현재 전송 모드: {pipeClient.TransmissionMode}.");

                // 파이프 스트림에서 텍스트를 읽기 위한 StreamReader 생성
                using (StreamReader sr = new StreamReader(pipeClient))
                {
                    string temp;

                    // 서버로부터 '동기화 메시지'가 올 때까지 기다립니다.
                    do
                    {
                        Console.WriteLine("[CLIENT] 동기화 대기 중...");
                        temp = sr.ReadLine();
                    }
                    while (!temp.StartsWith("SYNC")); // 'SYNC'로 시작하는 메시지를 받을 때까지 반복

                    // 서버 데이터를 읽고 콘솔에 출력합니다.
                    while ((temp = sr.ReadLine()) != null) // 스트림의 끝(null)이 아닐 때까지 읽기
                    {
                        Console.WriteLine("[CLIENT] 에코: " + temp);
                    }
                }
            } // using 블록을 벗어나면 pipeClient는 자동으로 Dispose됩니다.
        }
        Console.Write("[CLIENT] 계속하려면 Enter 키를 누르세요...");
        Console.ReadLine(); // 사용자가 Enter를 누를 때까지 대기
    }
}</pre>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 명명된 파이프 (Named Pipes)</h3>



<p class="wp-block-paragraph">명명된 파이프는 파이프 서버와 하나 이상의 파이프 클라이언트 간 프로세스 간 통신을 제공합니다. </p>



<p class="wp-block-paragraph">명명된 파이프는 <strong>단방향 또는 양방향</strong>이 될 수 있습니다. </p>



<p class="wp-block-paragraph">이는 <strong>메시지 기반 통신을 지원</strong>하며, <strong>여러 클라이언트가 동일한 파이프 이름을 사용하여 서버 프로세스에 동시에 연결</strong>할 수 있도록 합니다. </p>



<p class="wp-block-paragraph">명명된 파이프는 또한 가장(impersonation)을 지원하는데, 이를 통해 연결 프로세스가 원격 서버에서 자신의 권한을 사용할 수 있습니다.</p>



<p class="wp-block-paragraph">.NET에서는 <strong><code>NamedPipeServerStream</code> </strong>및 <strong><code>NamedPipeClientStream</code> </strong>클래스를 사용하여 명명된 파이프를 구현합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가장(Impersonation)이란?</p>



<p class="wp-block-paragraph">컴퓨터 시스템에서 어떤 리소스(예: 파일, 네트워크 공유, 데이터베이스)에 접근하려면 해당 작업을 수행하는 <strong>사용자 계정의 권한</strong>이 필요</p>



<p class="wp-block-paragraph">일반적으로 서버 애플리케이션은 특정 계정(예: 시스템 계정, 서비스 계정)의 권한으로 실행됩니다.</p>



<p class="wp-block-paragraph">만약 서버가 <strong>클라이언트가 요청한 작업을 클라이언트 자신의 권한으로 수행</strong>해야 할 때가 있습니다. 예를 들어:</p>



<ul class="wp-block-list">
<li><strong>클라이언트가 접근 권한을 가진 특정 파일 읽기/쓰기:</strong> <br>서버는 클라이언트가 보낸 파일 경로에 대해 읽기/쓰기 요청을 받았습니다. <br>하지만 서버는 해당 파일에 직접 접근할 권한이 없고, <strong>오직 클라이언트 계정만 그 파일에 접근할 권한</strong>을 가지고 있을 수 있습니다.</li>



<li><strong>클라이언트의 네트워크 자격 증명 사용:</strong> <br>클라이언트가 원격 네트워크 공유에 있는 리소스에 접근하고 싶을 때, <br>서버가 클라이언트를 &#8216;가장&#8217;하여 클라이언트의 네트워크 자격 증명(사용자 이름과 비밀번호)을 사용하여 해당 공유에 접근할 수 있습니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">명명된 파이프(Named Pipes) 사용 방법</h4>



<p class="wp-block-paragraph"><a href="https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 1: <code>NamedPipeServerStream</code> 클래스를 사용하여 명명된 파이프 생성하기</h5>



<p class="wp-block-paragraph">다음 예제는 <code>NamedPipeServerStream</code> 클래스를 사용하여 <strong>명명된 파이프를 생성하는 방법</strong>을 보여줍니다. </p>



<p class="wp-block-paragraph">이 예제에서 서버 프로세스는 <strong>4개의 스레드를 생성</strong>합니다. 각 스레드는 <strong>하나의 클라이언트 연결을 수락</strong>할 수 있습니다. </p>



<p class="wp-block-paragraph">연결된 클라이언트 프로세스는 서버에 파일 이름을 제공합니다. </p>



<p class="wp-block-paragraph">만약 클라이언트가 충분한 권한을 가지고 있다면, 서버 프로세스는 해당 파일을 열고 그 내용을 다시 클라이언트에게 보냅니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;

public class PipeServer
{
    // 동시에 처리할 서버 스레드의 개수 (클라이언트 연결을 수락할 인스턴스 수)
    private static int numThreads = 4;

    public static void Main()
    {
        int i;
        // 서버 스레드를 담을 배열 선언
        Thread?[] servers = new Thread[numThreads];

        Console.WriteLine("\n*** 명명된 파이프 서버 스트림과 가장(impersonation) 예제 ***\n");
        Console.WriteLine("클라이언트 연결 대기 중...\n");

        // numThreads 개수만큼 서버 스레드를 생성하고 시작합니다.
        for (i = 0; i &lt; numThreads; i++)
        {
            servers[i] = new Thread(ServerThread); // ServerThread 메서드를 실행할 새 스레드 생성
            servers[i]?.Start(); // 스레드 시작
        }

        // 서버 스레드가 시작될 시간을 잠시 기다립니다.
        Thread.Sleep(250);

        // 모든 서버 스레드가 작업을 완료할 때까지 대기하는 루프
        // 'i'는 아직 완료되지 않은 스레드의 개수를 추적합니다.
        while (i > 0)
        {
            for (int j = 0; j &lt; numThreads; j++)
            {
                if (servers[j] != null) // 스레드가 아직 활성 상태인 경우
                {
                    // 스레드가 250ms 내에 완료되는지 확인합니다.
                    // 완료되면 해당 스레드를 null로 설정하고 카운트를 줄입니다.
                    if (servers[j]!.Join(250))
                    {
                        Console.WriteLine($"서버 스레드[{servers[j]!.ManagedThreadId}] 작업 완료.");
                        servers[j] = null; // 완료된 스레드 참조 제거
                        i--;               // 완료되지 않은 스레드 개수 감소
                    }
                }
            }
        }
        Console.WriteLine("\n서버 스레드 모두 소진, 종료합니다.");
    }

    // 각 클라이언트 연결을 처리할 서버 스레드의 진입점 메서드
    private static void ServerThread(object? data)
    {
        // NamedPipeServerStream 인스턴스 생성
        // "testpipe": 파이프 이름 (클라이언트가 이 이름으로 연결 시도)
        // PipeDirection.InOut: 양방향 통신
        // numThreads: 이 파이프 이름으로 동시에 허용할 서버 인스턴스(클라이언트 연결)의 최대 수
        NamedPipeServerStream pipeServer =
            new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);

        int threadId = Thread.CurrentThread.ManagedThreadId; // 현재 스레드의 ID

        // 클라이언트가 연결될 때까지 대기합니다.
        // 이 메서드는 블로킹(blocking) 호출입니다.
        pipeServer.WaitForConnection();

        Console.WriteLine($"클라이언트가 스레드[{threadId}]에 연결되었습니다.");
        try
        {
            // 클라이언트로부터 요청을 읽습니다.
            // 클라이언트가 파이프에 데이터를 쓰면, 클라이언트의 보안 토큰을 사용할 수 있게 됩니다.
            StreamString ss = new StreamString(pipeServer);

            // 연결된 클라이언트에게 서버임을 증명하는 문자열을 보냅니다.
            // 클라이언트는 이 문자열을 예상하고 있습니다.
            ss.WriteString("나는 유일한 참된 서버입니다!");
            // 클라이언트로부터 파일 이름을 읽습니다.
            string filename = ss.ReadString();

            // 클라이언트를 가장(impersonate)하여 파일 내용을 읽어 스트림으로 보냅니다.
            // ReadFileToStream 클래스는 파일 읽기 로직을 캡슐화합니다.
            ReadFileToStream fileReader = new ReadFileToStream(ss, filename);

            // 현재 가장하고 있는 사용자의 이름을 출력합니다.
            Console.WriteLine($"파일 읽기: {filename} (스레드[{threadId}]) 사용자: {pipeServer.GetImpersonationUserName()}로.");
            // 클라이언트를 가장한 보안 컨텍스트 내에서 fileReader.Start 메서드를 실행합니다.
            // 이 시점부터 Start() 메서드 내의 파일 접근은 클라이언트의 권한으로 이루어집니다.
            pipeServer.RunAsClient(fileReader.Start);
        }
        // 파이프가 끊어지거나 연결이 해제될 때 발생하는 IOException을 잡습니다.
        catch (IOException e)
        {
            Console.WriteLine($"오류: {e.Message}");
        }
        finally
        {
            // 파이프 서버 스트림을 닫고 리소스를 해제합니다.
            pipeServer.Close();
            Console.WriteLine($"스레드[{threadId}] 파이프 서버 닫힘.");
        }
    }
}

// 스트림에서 문자열을 읽고 쓰기 위한 데이터 프로토콜을 정의하는 클래스
public class StreamString
{
    private Stream ioStream; // 기본 스트림 (NamedPipeServerStream 또는 NamedPipeClientStream)
    private UnicodeEncoding streamEncoding; // 문자열 인코딩 방식

    public StreamString(Stream ioStream)
    {
        this.ioStream = ioStream;
        streamEncoding = new UnicodeEncoding(); // UTF-16 인코딩 사용
    }

    // 스트림에서 문자열을 읽습니다.
    public string ReadString()
    {
        // 먼저 문자열의 길이를 2바이트로 읽습니다. (Big-endian 방식으로)
        int len = 0;
        len = ioStream.ReadByte() * 256; // 첫 번째 바이트 (상위 8비트)
        len += ioStream.ReadByte();      // 두 번째 바이트 (하위 8비트)

        // 읽을 길이만큼의 바이트 배열을 생성합니다.
        byte[] inBuffer = new byte[len];
        // 스트림에서 해당 길이만큼의 바이트를 읽어 버퍼에 채웁니다.
        ioStream.Read(inBuffer, 0, len);

        // 바이트 배열을 문자열로 디코딩하여 반환합니다.
        return streamEncoding.GetString(inBuffer);
    }

    // 스트림에 문자열을 씁니다.
    public int WriteString(string outString)
    {
        // 문자열을 바이트 배열로 인코딩합니다.
        byte[] outBuffer = streamEncoding.GetBytes(outString);
        int len = outBuffer.Length;

        // 문자열 길이가 UInt16.MaxValue (65535)를 초과하면 잘라냅니다.
        // (프로토콜이 2바이트 길이 필드를 사용하므로 최대 65535 바이트까지 표현 가능)
        if (len > UInt16.MaxValue)
        {
            len = (int)UInt16.MaxValue;
        }

        // 문자열의 길이를 2바이트로 스트림에 씁니다. (Big-endian 방식으로)
        ioStream.WriteByte((byte)(len / 256)); // 상위 바이트
        ioStream.WriteByte((byte)(len &amp; 255)); // 하위 바이트
        // 문자열 바이트 배열을 스트림에 씁니다.
        ioStream.Write(outBuffer, 0, len);
        // 버퍼에 남아있는 데이터를 즉시 스트림으로 밀어 넣습니다.
        ioStream.Flush();

        // 전송된 총 바이트 수 (문자열 바이트 + 길이 2바이트)를 반환합니다.
        return outBuffer.Length + 2;
    }
}

// 가장된 사용자(클라이언트)의 컨텍스트에서 실행될 메서드를 포함하는 클래스
public class ReadFileToStream
{
    private string fn; // 읽을 파일 이름
    private StreamString ss; // 파일 내용을 쓸 스트림 (StreamString 헬퍼 사용)

    public ReadFileToStream(StreamString str, string filename)
    {
        fn = filename;
        ss = str;
    }

    // 이 메서드는 pipeServer.RunAsClient()에 의해 호출되며,
    // 호출되는 동안 현재 스레드의 보안 컨텍스트가 클라이언트로 변경됩니다.
    public void Start()
    {
        // 클라이언트의 권한으로 파일을 읽습니다.
        string contents = File.ReadAllText(fn);
        // 읽은 파일 내용을 스트림을 통해 클라이언트에게 다시 보냅니다.
        ss.WriteString(contents);
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 2: <code>NamedPipeClientStream</code> 클래스를 사용하는 클라이언트 프로세스</h5>



<p class="wp-block-paragraph">다음 예제는 <code>NamedPipeClientStream</code> 클래스를 사용하는 클라이언트 프로세스를 보여줍니다. </p>



<p class="wp-block-paragraph">이 클라이언트는 서버 프로세스에 연결하여 서버로 파일 이름을 전송합니다. </p>



<p class="wp-block-paragraph">이 예제에서는 <strong>가장(impersonation)</strong> 기능을 사용하므로, 클라이언트 애플리케이션을 실행하는 <strong>사용자 계정에 해당 파일에 접근할 수 있는 권한이 있어야</strong> 합니다. </p>



<p class="wp-block-paragraph">그러면 서버는 파일 내용을 다시 클라이언트에게 전송하고, 클라이언트는 그 내용을 콘솔에 표시합니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.Diagnostics; // 프로세스 관련 기능을 위해 추가
using System.IO;          // 파일 및 스트림 관련 기능을 위해 추가
using System.IO.Pipes;    // Named Pipe 관련 기능을 위해 추가
using System.Security.Principal; // TokenImpersonationLevel을 위해 추가
using System.Text;        // 인코딩을 위해 추가
using System.Threading;   // 스레드 및 Sleep을 위해 추가

public class PipeClient
{
    // 생성할 클라이언트 프로세스의 개수
    private static int numClients = 4;

    public static void Main(string[] args)
    {
        // 프로그램이 'spawnclient' 인수를 가지고 실행되었는지 확인합니다.
        // 이 인수는 메인 프로세스가 다른 클라이언트 프로세스를 시작할 때 사용됩니다.
        if (args.Length > 0)
        {
            if (args[0] == "spawnclient")
            {
                // 'spawnclient' 인수가 있는 경우, 명명된 파이프 클라이언트를 생성하고 서버에 연결합니다.
                var pipeClient =
                    new NamedPipeClientStream(
                        ".",                 // 파이프가 로컬 컴퓨터에 있음을 나타냅니다. (서버 주소)
                        "testpipe",          // 연결할 파이프 이름 (서버의 파이프 이름과 일치해야 합니다)
                        PipeDirection.InOut, // 양방향 통신
                        PipeOptions.None,    // 추가 파이프 옵션 없음
                        TokenImpersonationLevel.Impersonation); // 클라이언트의 보안 토큰을 서버로 전송하여 가장을 허용합니다.

                Console.WriteLine("서버에 연결 중...\n");
                // 서버에 연결될 때까지 대기합니다.
                pipeClient.Connect();

                var ss = new StreamString(pipeClient);
                // 서버의 서명 문자열("I am the one true server!")을 읽어 유효성을 검사합니다.
                if (ss.ReadString() == "I am the one true server!")
                {
                    // 첫 번째 쓰기 작업과 함께 클라이언트의 보안 토큰이 서버로 전송됩니다.
                    // 서버가 파일 내용을 반환할 파일 이름을 서버로 보냅니다.
                    ss.WriteString("c:\\textfile.txt"); // 서버가 읽을 파일 경로를 지정합니다.

                    // 서버로부터 파일 내용을 읽어 콘솔에 출력합니다.
                    Console.Write(ss.ReadString());
                }
                else
                {
                    Console.WriteLine("서버를 확인할 수 없습니다.");
                }
                // 파이프 클라이언트 스트림을 닫고 리소스를 해제합니다.
                pipeClient.Close();
                // 클라이언트 프로세스가 결과를 표시할 시간을 주기 위해 4초 동안 대기합니다.
                Thread.Sleep(4000);
            }
        }
        else // 'spawnclient' 인수가 없는 경우, 여러 클라이언트 프로세스를 시작합니다.
        {
            Console.WriteLine("\n*** 가장(impersonation) 예제를 포함한 명명된 파이프 클라이언트 스트림 ***\n");
            // 클라이언트 프로세스를 시작하는 헬퍼 함수를 호출합니다.
            StartClients();
        }
    }

    // 파이프 클라이언트 프로세스를 생성하는 헬퍼 함수
    private static void StartClients()
    {
        // 현재 실행 중인 프로세스의 전체 명령줄을 가져옵니다. (예: "C:\path\to\YourApp.exe")
        string currentProcessName = Environment.CommandLine;

        // Visual Studio에서 실행될 때 추가되는 따옴표나 공백을 제거합니다.
        currentProcessName = currentProcessName.Trim('"', ' ');

        // 확장자를 .exe로 변경하여 실행 파일 경로를 확보합니다.
        currentProcessName = Path.ChangeExtension(currentProcessName, ".exe");
        Process?[] plist = new Process?[numClients]; // 생성될 클라이언트 프로세스들을 저장할 배열

        Console.WriteLine("클라이언트 프로세스들을 생성 중...\n");

        // 현재 디렉토리 경로가 명령줄에 포함되어 있다면 제거합니다.
        // 이는 Process.Start가 올바른 실행 파일을 찾도록 돕기 위함입니다.
        if (currentProcessName.Contains(Environment.CurrentDirectory))
        {
            currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty);
        }

        // Visual Studio 실행 시 발생할 수 있는 추가적인 문자(백슬래시, 따옴표)를 제거합니다.
        currentProcessName = currentProcessName.Replace("\\", String.Empty);
        currentProcessName = currentProcessName.Replace("\"", String.Empty);

        int i;
        for (i = 0; i &lt; numClients; i++)
        {
            // '이' 프로그램(PipeClient) 자체를 새 프로세스로 시작하되,
            // 'spawnclient' 인수를 전달하여 명명된 파이프 클라이언트 모드로 실행되도록 합니다.
            plist[i] = Process.Start(currentProcessName, "spawnclient");
        }

        // 모든 클라이언트 프로세스가 종료될 때까지 대기하는 루프
        // 'i'는 아직 종료되지 않은 프로세스의 개수를 추적합니다.
        while (i > 0)
        {
            for (int j = 0; j &lt; numClients; j++)
            {
                if (plist[j] != null) // 프로세스가 아직 활성 상태인 경우
                {
                    // 프로세스가 종료되었는지 확인합니다.
                    if (plist[j]!.HasExited)
                    {
                        Console.WriteLine($"클라이언트 프로세스[{plist[j]?.Id}]가 종료되었습니다.");
                        plist[j] = null; // 종료된 프로세스 참조 제거
                        i--;             // 종료되지 않은 프로세스 개수 감소
                    }
                    else
                    {
                        // 아직 종료되지 않았다면 잠시 대기합니다.
                        Thread.Sleep(250);
                    }
                }
            }
        }
        Console.WriteLine("\n클라이언트 프로세스들이 모두 완료되어 종료합니다.");
    }
}

// 스트림에서 문자열을 읽고 쓰기 위한 데이터 프로토콜을 정의하는 클래스
// (이 클래스는 서버 예제와 동일합니다)
public class StreamString
{
    private Stream ioStream;           // 기본 스트림 (NamedPipeClientStream)
    private UnicodeEncoding streamEncoding; // 문자열 인코딩 방식

    public StreamString(Stream ioStream)
    {
        this.ioStream = ioStream;
        streamEncoding = new UnicodeEncoding(); // UTF-16 인코딩 사용
    }

    // 스트림에서 문자열을 읽습니다.
    public string ReadString()
    {
        // 먼저 문자열의 길이를 2바이트로 읽습니다. (Big-endian 방식으로)
        int len;
        len = ioStream.ReadByte() * 256; // 첫 번째 바이트 (상위 8비트)
        len += ioStream.ReadByte();      // 두 번째 바이트 (하위 8비트)

        // 읽을 길이만큼의 바이트 배열을 생성합니다.
        var inBuffer = new byte[len];
        // 스트림에서 해당 길이만큼의 바이트를 읽어 버퍼에 채웁니다.
        ioStream.Read(inBuffer, 0, len);

        // 바이트 배열을 문자열로 디코딩하여 반환합니다.
        return streamEncoding.GetString(inBuffer);
    }

    // 스트림에 문자열을 씁니다.
    public int WriteString(string outString)
    {
        // 문자열을 바이트 배열로 인코딩합니다.
        byte[] outBuffer = streamEncoding.GetBytes(outString);
        int len = outBuffer.Length;

        // 문자열 길이가 UInt16.MaxValue (65535)를 초과하면 잘라냅니다.
        // (프로토콜이 2바이트 길이 필드를 사용하므로 최대 65535 바이트까지 표현 가능)
        if (len > UInt16.MaxValue)
        {
            len = (int)UInt16.MaxValue;
        }

        // 문자열의 길이를 2바이트로 스트림에 씁니다. (Big-endian 방식으로)
        ioStream.WriteByte((byte)(len / 256)); // 상위 바이트
        ioStream.WriteByte((byte)(len &amp; 255)); // 하위 바이트
        // 문자열 바이트 배열을 스트림에 씁니다.
        ioStream.Write(outBuffer, 0, len);
        // 버퍼에 남아있는 데이터를 즉시 스트림으로 밀어 넣습니다.
        ioStream.Flush();

        // 전송된 총 바이트 수 (문자열 바이트 + 길이 2바이트)를 반환합니다.
        return outBuffer.Length + 2;
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">강력한 프로그래밍: Named Pipe 클라이언트/서버 설정</h5>



<p class="wp-block-paragraph">이 예제에서 클라이언트와 서버 프로세스는 <strong>동일한 컴퓨터에서 실행되도록</strong> 설계되었습니다. </p>



<p class="wp-block-paragraph">그래서 <strong><code>NamedPipeClientStream</code> </strong>객체에 제공된 서버 이름이 <strong>&#8220;.&#8221;</strong> 입니다.</p>



<p class="wp-block-paragraph">만약 클라이언트와 서버 프로세스가 <strong>다른 컴퓨터에 있다면</strong>, <strong>&#8220;.&#8221;</strong> 은 서버 프로세스를 실행하는 컴퓨터의 <strong>네트워크 이름</strong>으로 대체되어야 합니다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" />추가 정리</h3>



<p class="wp-block-paragraph"><strong>명명된 파이프에 대한 동작 개념</strong></p>



<p class="wp-block-paragraph">명명된 파이프는 <strong>파일 시스템의 &#8216;이름&#8217;이라는 개념을 빌려와</strong> 프로세스들이 서로를 찾아 연결할 수 있게 해주지만, 실제 데이터 교환은 <strong>메모리를 통해 이루어지는 IPC(프로세스 간 통신) 메커니즘</strong>입니다.</p>



<p class="wp-block-paragraph">이러한 특성 덕분에 명명된 파이프는 로컬 컴퓨터 내의 여러 프로세스 간 통신에 매우 효율적이며, Windows에서는 네트워크를 통해 다른 컴퓨터의 파이프에도 연결할 수 있어 더욱 유연하게 사용할 수 있습니다.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/">.NET의 파이프 작업 (Pipe Operations in .NET)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Unity Netcode for Entities – Connecting server and clients</title>
		<link>https://lycos7560.com/c/unity-netcode-for-entities-creating-multiplayer-gameplay/39997/</link>
					<comments>https://lycos7560.com/c/unity-netcode-for-entities-creating-multiplayer-gameplay/39997/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Thu, 01 May 2025 06:21:00 +0000</pubDate>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Unity]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[Client / Server worlds]]></category>
		<category><![CDATA[dtls]]></category>
		<category><![CDATA[IP]]></category>
		<category><![CDATA[multiplayer]]></category>
		<category><![CDATA[Netcode]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[UDP]]></category>
		<category><![CDATA[WSS]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[유니티]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39997</guid>

					<description><![CDATA[<p>🔥 Connecting server and clients Netcode for Entities 1.5.0v https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/creating-multiplayer-gameplay.html 주제 설명 서버와 클라이언트 연결Connecting server and clients Netcode for Entities는 Unity Transport 패키지를 사용하여 연결을 관리합니다. 각 연결은 하나의 엔티티로 저장되며, 해당 엔티티는 NetworkStreamConnection 컴포넌트를 포함하고 있어, 연결에 사용되는 Transport 핸들을 참조합니다. 이 엔티티의 이름은 일반적으로 'NetworkConnection [nid]' 형식입니다. 상태 및 입력 동기화Synchronizing states [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/unity-netcode-for-entities-creating-multiplayer-gameplay/39997/">Unity Netcode for Entities – Connecting server and clients</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-cbe7176c      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#connecting-server-and-clients" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Connecting server and clients</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#서버와-클라이언트-연결" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 서버와 클라이언트 연결</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#명령-수신-대상-설정" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 명령 수신 대상 설정</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#게임-상태-진입" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 게임 상태 진입</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#연결-해제-요청" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 연결 해제 요청</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#수신-버퍼-incoming-buffers" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 수신 버퍼 (Incoming Buffers)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#송신-버퍼-outgoing-buffers" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 송신 버퍼 (Outgoing Buffers)</a></li></ul><li class="uagb-toc__list"><a href="#연결-흐름-connection-flow" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 연결 흐름 (Connection flow)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-수동-연결-리스닝-manually-listen-or-connect" class="uagb-toc-link__trigger">1&#x20e3; 수동 연결 / 리스닝 (Manually listen or connect )</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-autoconnectport-사용-using-theautoconnectport" class="uagb-toc-link__trigger">2&#x20e3; AutoConnectPort 사용 (Using the AutoConnectPort)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-networkstreamrequest로-연결-흐름-제어" class="uagb-toc-link__trigger">3&#x20e3; NetworkStreamRequest로 연결 흐름 제어</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-network-simulator" class="uagb-toc-link__trigger">4&#x20e3; Network simulator</a></li></ul><li class="uagb-toc__list"><a href="#클라이언트-연결-이벤트-수신" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 클라이언트 연결 이벤트 수신</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-netcodeconnectionevents-on-the-client" class="uagb-toc-link__trigger">1&#x20e3; NetCodeConnectionEvents on the client</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-netcodeconnectionevents-on-the-server" class="uagb-toc-link__trigger">2&#x20e3; NetCodeConnectionEvents on the server</a></li></ul><li class="uagb-toc__list"><a href="#connection-approval" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Connection approval</a></li></ul></li><li class="uagb-toc__list"><a href="#연결-확인-sample-코드" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 연결 확인 Sample 코드</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Connecting server and clients</h2>



<p class="wp-block-paragraph"><strong>Netcode for Entities 1.5.0v</strong></p>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/creating-multiplayer-gameplay.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/creating-multiplayer-gameplay.html</a></p>



<figure class="wp-block-table"><table><thead><tr><th>주제</th><th>설명</th></tr></thead><tbody><tr><td><strong>서버와 클라이언트 연결</strong><br><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/network-connection.html" target="_blank" rel="noreferrer noopener">Connecting server and clients</a></strong></td><td>Netcode for Entities는 Unity Transport 패키지를 사용하여 연결을 관리합니다. <br>각 연결은 하나의 엔티티로 저장되며, 해당 엔티티는 <code><strong>NetworkStreamConnection</strong></code> 컴포넌트를 포함하고 있어, 연결에 사용되는 Transport 핸들을 참조합니다. <br>이 엔티티의 이름은 일반적으로 <code><strong>'NetworkConnection [nid]'</strong></code> 형식입니다.</td></tr><tr><td><strong>상태 및 입력 동기화</strong><br><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/synchronization.html" target="_blank" rel="noreferrer noopener">Synchronizing states and inputs</a></strong></td><td>Netcode는 고스트(ghost) 상태와 입력/명령을 동기화합니다. <br>이 항목에서는 지원되는 타입 목록과, 어떤 필드와 컴포넌트를 <strong>eventual consistency 모델</strong>을 통해 복제할지 표시하는 방법(마크업)을 설명합니다.</td></tr><tr><td><strong>시간 동기화</strong><br><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/time-synchronization.html" target="_blank" rel="noreferrer noopener">Time synchronization</a></strong></td><td>Netcode는 <strong>서버 권한 모델(server authoritative model)</strong> 을 사용합니다. <br>서버는 고정된 시간 간격으로 시뮬레이션을 실행하며, 이 간격은 마지막 업데이트 이후 경과한 시간에 따라 결정됩니다. <br>따라서 <strong>클라이언트는 항상 서버 시간과 동기화되어야 이 모델이 제대로 작동</strong>합니다.</td></tr><tr><td><strong>보간 및 예측보간</strong><br><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/interpolation.html" target="_blank" rel="noreferrer noopener">Interpolation and extrapolation</a></strong></td><td>게임에서 <strong>보간(interpolation)</strong> 및 <strong>예측보간(extrapolation)</strong> 을 사용하여, 불안정한 네트워크 환경에서도 부드러운 게임플레이 경험을 제공할 수 있습니다.</td></tr><tr><td><strong>예측</strong><br><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/prediction.html" target="_blank" rel="noreferrer noopener">Prediction</a></strong></td><td>게임의 지연(latency)을 보완하기 위해 <strong>예측 로직</strong>을 사용할 수 있습니다. <br>예측은 클라이언트가 로컬에서 먼저 행동을 시뮬레이션하고, 이후 서버로부터 확정된 결과를 받는 방식입니다.</td></tr><tr><td><strong>물리</strong><br><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/physics.html" target="_blank" rel="noreferrer noopener">Physics</a></strong></td><td><strong>Netcode 패키지는 Unity Physics와의 일부 통합 기능을 제공</strong>합니다. <br>이를 통해 네트워크 환경에서도 물리 기반 오브젝트를 보다 쉽게 사용할 수 있습니다. <br>이 통합 기능은 <strong>predicted ghosts</strong>와 <strong>interpolated ghosts</strong> 양쪽 모두에 대한 물리 처리를 지원합니다.</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 서버와 클라이언트 연결</h3>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/network-connection.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/network-connection.html</a></p>



<p class="wp-block-paragraph"><strong>Netcode for Entities</strong>는 연결을 관리하기 위해 <strong>Unity Transport 패키지</strong>를 사용하며, 각 연결을 하나의 <strong>엔티티</strong>로 저장합니다.</p>



<p class="wp-block-paragraph">이 엔티티는 <code>NetworkStreamConnection</code> 컴포넌트를 가지며, 연결에 사용된 <strong>Transport 핸들</strong>을 포함합니다. </p>



<p class="wp-block-paragraph">연결이 종료되면(서버가 유저 연결을 끊거나 클라이언트가 요청한 경우) 이 엔티티는 삭제됩니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 명령 수신 대상 설정</h4>



<p class="wp-block-paragraph"><code>[AutoCommandTarget feature]</code>를 사용하지 않거나 더 세밀한 제어가 필요한 경우, <strong>CommandTarget</strong>을 설정해야 합니다. </p>



<p class="wp-block-paragraph">이 설정은 클라이언트에서 받은 명령을 저장할 엔티티를 지정합니다. </p>



<p class="wp-block-paragraph">이 엔티티 참조를 최신 상태로 유지하는 것은 게임 개발자의 책임입니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 게임 상태 진입</h4>



<p class="wp-block-paragraph">게임은 <code>NetworkStreamInGame</code> 컴포넌트를 연결 엔티티에 수동으로 추가해야 합니다. 이 작업은 자동으로 이루어지지 않습니다. </p>



<p class="wp-block-paragraph">이 컴포넌트가 추가되기 전에는 클라이언트가 명령을 전송하지 않으며, 서버도 스냅샷을 전송하지 않습니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 연결 해제 요청</h4>



<p class="wp-block-paragraph">연결을 해제하려면 해당 엔티티에 <code>NetworkStreamRequestDisconnect</code> 컴포넌트를 추가하세요. </p>



<p class="wp-block-paragraph">Transport 드라이버를 통한 <strong>직접적인 연결 해제는 지원되지 않습니다.</strong></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 수신 버퍼 (Incoming Buffers)</h4>



<p class="wp-block-paragraph">각 연결은 최대 3개의 수신 버퍼를 가질 수 있습니다:</p>



<ul class="wp-block-list">
<li>명령: <code><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.IncomingRpcDataStreamBuffer.html" target="_blank" rel="noreferrer noopener">IncomingRpcDataStreamBuffer</a></strong></code></li>



<li>RPC: <code><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.IncomingCommandDataStreamBuffer.html" target="_blank" rel="noreferrer noopener">IncomingCommandDataStreamBuffer</a></strong></code></li>



<li>스냅샷 (클라이언트 전용): <code><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.IncomingSnapshotDataStreamBuffer.html" target="_blank" rel="noreferrer noopener">IncomingSnapshotDataStreamBuffer</a></strong></code></li>
</ul>



<p class="wp-block-paragraph">서버에서 클라이언트로 스냅샷이 전송되면 버퍼에 저장되고 나중에 <code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.IncomingSnapshotDataStreamBuffer.html" target="_blank" rel="noreferrer noopener"><strong>GhostReceiveSystem</strong></a></code>에 의해 처리됩니다.</p>



<p class="wp-block-paragraph">RPC 및 명령도 동일한 방식으로 <code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NetworkStreamReceiveSystem.html"><strong>NetworkStreamReceiveSystem</strong></a></code>이 수집하고, 이후 각각의 시스템이 처리합니다.</p>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 서버는 <code>IncomingSnapshotDataStreamBuffer</code>를 가지지 않습니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 송신 버퍼 (Outgoing Buffers)</h4>



<p class="wp-block-paragraph">각 연결은 최대 2개의 송신 버퍼를 가질 수 있습니다:</p>



<ul class="wp-block-list">
<li>명령 (클라이언트 전용): <code><strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.OutgoingRpcDataStreamBuffer.html" target="_blank" rel="noreferrer noopener">OutgoingRpcDataStreamBuffer</a></strong></code></li>



<li>RPC:<strong> <code>OutgoingRpcDataStreamBuffer</code></strong></li>
</ul>



<p class="wp-block-paragraph">생성된 명령은 먼저 송신 버퍼에 저장되고, 클라이언트가 매 틱마다 이 버퍼를 전송합니다. </p>



<p class="wp-block-paragraph">RPC도 마찬가지로 해당 송신 시스템이 버퍼에 인코딩하고, <code>RpcSystem</code>이 이를 묶어 MTU 단위로 전송합니다.</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 연결 흐름 (Connection flow)</h3>



<p class="wp-block-paragraph">Netcode는 게임 시작 시 <strong>자동으로 서버 또는 클라이언트 연결을 설정하지 않습니다</strong>. </p>



<p class="wp-block-paragraph">기본적으로 <code>ClientServerBootstrap</code>는 클라이언트와 서버 월드만 생성합니다. </p>



<p class="wp-block-paragraph">연결 채널을 여는 방식은 개발자가 결정합니다.</p>



<p class="wp-block-paragraph"><strong>선택 가능한 방법:</strong></p>



<ul class="wp-block-list">
<li>1&#x20e3; <code><strong>NetworkStreamDriver</strong></code>를 사용하여 직접 <strong><code>Connect</code> </strong>또는 <strong><code>Listen</code> </strong>호출</li>



<li>2&#x20e3; <strong><code>AutoConnectPort</code> </strong>및 <strong><code>DefaultConnectAddress</code> </strong>설정</li>



<li><strong><code>NetworkStreamRequestConnect</code> </strong>및 <strong><code>NetworkStreamRequestListen</code> </strong>요청 엔티티 생성</li>
</ul>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 주의 </p>



<p class="wp-block-paragraph">연결 중에는 반드시 <code>Application.runInBackground = true</code>로 설정해야 하며, 그렇지 않으면 <strong>포커스를 잃었을 때 연결이 끊길 수 있습니다.</strong></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">1&#x20e3; 수동 연결 / 리스닝 (Manually listen or connect )</h4>



<p class="wp-block-paragraph"><code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NetworkStreamDriver.html" target="_blank" rel="noreferrer noopener">NetworkStreamDriver</a></code> 싱글톤을 사용해 <code>Connect</code> 또는 <code>Listen</code> 메서드를 호출합니다. </p>



<p class="wp-block-paragraph">코드 예시는 <a href="https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/NetcodeSamples/Assets/Samples/HelloNetcode/1_Basics/01_BootstrapAndFrontend/Frontend/Frontend.cs#L80" target="_blank" rel="noreferrer noopener">DOTS samples repository</a> 참고</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">        public void StartClientServer(string sceneName)
        {
            Debug.Log($"[StartClientServer] Called with '{sceneName}'.");
            if (ClientServerBootstrap.RequestedPlayType != ClientServerBootstrap.PlayType.ClientAndServer)
            {
                Debug.LogError($"Creating client/server worlds is not allowed if playmode is set to {ClientServerBootstrap.RequestedPlayType}");
                return;
            }

            var server = ClientServerBootstrap.CreateServerWorld("ServerWorld");
            var client = ClientServerBootstrap.CreateClientWorld("ClientWorld");

            SceneManager.LoadScene("FrontendHUD");

            //Destroy the local simulation world to avoid the game scene to be loaded into it
            //This prevent rendering (rendering from multiple world with presentation is not greatly supported)
            //and other issues.
            DestroyLocalSimulationWorld();
            if (World.DefaultGameObjectInjectionWorld == null)
                World.DefaultGameObjectInjectionWorld = server;
            SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);

            var port = ParsePortOrDefault(Port.text);

            NetworkEndpoint ep = NetworkEndpoint.AnyIpv4.WithPort(port);
            {
                using var drvQuery = server.EntityManager.CreateEntityQuery(ComponentType.ReadWrite&lt;NetworkStreamDriver>());
                drvQuery.GetSingletonRW&lt;NetworkStreamDriver>().ValueRW.RequireConnectionApproval = sceneName.Contains("ConnectionApproval", StringComparison.OrdinalIgnoreCase);
                drvQuery.GetSingletonRW&lt;NetworkStreamDriver>().ValueRW.Listen(ep);
            }

            ep = NetworkEndpoint.LoopbackIpv4.WithPort(port);
            {
                using var drvQuery = client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite&lt;NetworkStreamDriver>());
                drvQuery.GetSingletonRW&lt;NetworkStreamDriver>().ValueRW.Connect(client.EntityManager, ep);
            }
        }</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">2&#x20e3; <strong>AutoConnectPort 사용 (Using the&nbsp;AutoConnectPort)</strong></h4>



<p class="wp-block-paragraph"><code>ClientServerBootstrap</code>  <a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientServerBootstrap.html#Unity_NetCode_ClientServerBootstrap_AutoConnectPort" target="_blank" rel="noreferrer noopener"><code>AutoConnectPort</code></a>필드에는 서버와 클라이언트가 처음 설정될 때 각각 자동으로 수신하고 연결하도록 지시하는 데 사용할 수 있는 두 가지 특수 속성이 포함되어 있습니다.</p>



<ul class="wp-block-list">
<li><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientServerBootstrap.html#Unity_NetCode_ClientServerBootstrap_DefaultConnectAddress" target="_blank" rel="noreferrer noopener">DefaultConnectAddress</a></li>



<li><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientServerBootstrap.html#Unity_NetCode_ClientServerBootstrap_DefaultListenAddress" target="_blank" rel="noreferrer noopener">DefaultListenAddress</a></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public class AutoConnectBootstrap : ClientServerBootstrap
{
    public override bool Initialize(string defaultWorldName)
    {
        AutoConnectPort = 7979;
        CreateDefaultClientServerWorlds();
        return true;
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">서버는 와일드카드 주소(<code>DefaultListenAddress:AutoConnectPort</code>)에서 리슨을 시작합니다. </p>



<p class="wp-block-paragraph"><code>DefaultConnectAddress</code>는 기본적으로 <code>NetworkEndpoint.AnyIpv4</code>로 설정되어 있습니다. </p>



<p class="wp-block-paragraph">클라이언트는 서버 주소(<code>DefaultConnectAddress:AutoConnectPort</code>)로 연결을 시작하며, 이 <code>DefaultConnectAddress</code>는 기본적으로 <code>NetworkEndpoint.Loopback</code>으로 설정되어 있습니다.</p>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>참고:</strong></p>



<p class="wp-block-paragraph">에디터에서는 <strong>PlayMode 도구</strong>를 사용해 <code>AutoConnectAddress</code>와 <code>AutoConnectPort</code> 값을 오버라이드할 수 있습니다. </p>



<p class="wp-block-paragraph">그러나 <strong><code>AutoConnectPort</code>를 0으로 설정한 경우</strong>에는 PlayMode 도구의 오버라이드 기능이 작동하지 않으며, 이 경우 <strong>수동으로 연결을 트리거해야 합니다.</strong></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">3&#x20e3; <strong>NetworkStreamRequest로 연결 흐름 제어</strong></h4>



<p class="wp-block-paragraph"><code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NetworkStreamDriver.html" target="_blank" rel="noreferrer noopener">NetworkStreamDriver</a></code>에서 직접 메서드를 호출하는 대신, 다음과 같은 싱글톤을 생성하여 연결 흐름을 제어할 수 있습니다:</p>



<ul class="wp-block-list">
<li><strong><code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NetworkStreamRequestConnect.html" target="_blank" rel="noreferrer noopener">NetworkStreamRequestConnect</a></code> singleton&nbsp;</strong>: 원하는 서버 주소/포트로 연결을 요청할 때 사용합니다.</li>



<li><strong><code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NetworkStreamRequestListen.html" target="_blank" rel="noreferrer noopener">NetworkStreamRequestListen</a></code> singleton&nbsp;</strong>: 서버가 원하는 주소/포트에서 리슨을 시작하도록 요청할 때 사용합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 클라이언트 월드에서, NetworkStreamRequestConnect가 있는 새 엔티티 생성
// 이 엔티티는 이후 NetworkStreamReceiveSystem에 의해 처리됩니다.
var connectRequest = clientWorld.EntityManager.CreateEntity(typeof(NetworkStreamRequestConnect));
EntityManager.SetComponentData(connectRequest, new NetworkStreamRequestConnect { Endpoint = serverEndPoint });

// 서버 월드에서, NetworkStreamRequestListen이 있는 새 엔티티 생성
// 이 엔티티도 마찬가지로 NetworkStreamReceiveSystem에 의해 처리됩니다.
var listenRequest = serverWorld.EntityManager.CreateEntity(typeof(NetworkStreamRequestListen));
EntityManager.SetComponentData(listenRequest, new NetworkStreamRequestListen { Endpoint = serverEndPoint });
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">이러한 요청은 런타임에 <code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.NetworkStreamReceiveSystem.html" target="_blank" rel="noreferrer noopener">NetworkStreamReceiveSystem</a></code>에 의해 소비되어 실제 연결 동작을 수행합니다.</p>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>참고 :</strong></p>



<p class="wp-block-paragraph">실행 중 오류가 발생하면 <strong>PlayMode Tools</strong> 창을 열고 다시 <strong>Play Mode</strong>로 진입해 보세요.</p>



<p class="wp-block-paragraph">만약 월드가 이미 존재한다면, 부트스트랩 코드(위 설명 참조)에 의해 자동으로 월드가 생성되고 있을 가능성이 큽니다.</p>



<p class="wp-block-paragraph">서버가 이미 리슨 중이거나 클라이언트가 이미 연결 중이라면, 자동 연결 기능이 이미 활성화되어 있는 것입니다.</p>



<p class="wp-block-paragraph">이 경우 수동 연결 방식을 사용하려면 <strong>부트스트랩 코드에서 자동 연결을 비활성화</strong>해야 합니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">4&#x20e3; <strong>Network simulator</strong></h4>



<p class="wp-block-paragraph">Unity Transport는 <code><strong>SimulatorUtility</strong></code>를 제공합니다. </p>



<p class="wp-block-paragraph">이 도구는 <strong>Netcode for Entities 패키지</strong>에 포함되어 있으며, 에디터 내에서 다음 경로를 통해 접근하고 설정할 수 있습니다:</p>



<p class="wp-block-paragraph">Multiplayer &gt; PlayMode Tools</p>



<p class="wp-block-paragraph">실제 네트워크 상황을 보다 현실적으로 반영하기 위해, 시뮬레이터를 활성화한 상태로 자주 게임플레이를 테스트해보는 것이 좋습니다.</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="1261" height="990" src="https://lycos7560.com/wp-content/uploads/2025/05/image-6.png" alt="" class="wp-image-39999" style="width:838px;height:auto" srcset="https://lycos7560.com/wp-content/uploads/2025/05/image-6.png 1261w, https://lycos7560.com/wp-content/uploads/2025/05/image-6-300x236.png 300w, https://lycos7560.com/wp-content/uploads/2025/05/image-6-768x603.png 768w" sizes="(max-width: 1261px) 100vw, 1261px" /><figcaption class="wp-element-caption">PlayMode Tools</figcaption></figure>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 클라이언트 연결 이벤트 수신</h3>



<p class="wp-block-paragraph"><code>NetworkStreamDriver</code> 싱글톤을 통해 제공되는 <code>ConnectionEventsForTick</code> (형식: <code>NativeArray&lt;NetCodeConnectionEvent&gt;.ReadOnly</code>) 컬렉션을 사용하여, </p>



<p class="wp-block-paragraph">클라이언트 및 서버에서 클라이언트 연결 이벤트를 반복(iterate)하면서 반응할 수 있습니다.</p>



<p class="wp-block-paragraph">이 이벤트는 오직 <strong>단일 SimulationSystemGroup 틱 동안만 존재</strong>하며, 각각 <code>NetworkStreamConnectSystem</code>과 <code>NetworkStreamListenSystem</code>에서 초기화됩니다.</p>



<p class="wp-block-paragraph">시스템이 위 시스템들의 job 이후에 실행되면, 같은 틱에서 발생한 이벤트를 수신하게 됩니다.</p>



<p class="wp-block-paragraph">반면, 그 이전에 컬렉션을 조회하면 <strong>이전 틱의 값들을 반복</strong>하게 됩니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 예제 시스템
[UpdateAfter(typeof(NetworkReceiveSystemGroup))]
[BurstCompile]
public partial struct NetCodeConnectionEventListener : ISystem
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        var connectionEventsForClient = SystemAPI.GetSingleton&lt;NetworkStreamDriver>().ConnectionEventsForTick;
        foreach (var evt in connectionEventsForClient)
        {
            UnityEngine.Debug.Log($"[{state.WorldUnmanaged.Name}] {evt.ToFixedString()}!");
        }
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>주의 :</strong></p>



<p class="wp-block-paragraph">서버는 고정된 델타 타임으로 실행되기 때문에, <code>SimulationSystemGroup</code>은 각 렌더 프레임마다 <strong>0번 이상 실행</strong>될 수 있습니다.</p>



<p class="wp-block-paragraph">따라서 <code>ConnectionEventsForTick</code>은 반드시 <code>SimulationSystemGroup</code> 내에서 실행되는 시스템 안에서만 읽어야 합니다.</p>



<p class="wp-block-paragraph">예를 들어 <code>InitializationSystemGroup</code>, <code>PresentationSystemGroup</code>, 또는 어떤 MonoBehaviour 메서드 내에서 접근하면 다음과 같은 문제가 발생할 수 있습니다:</p>



<p class="wp-block-paragraph">b) 시뮬레이션이 <strong>이 프레임에서 실행되지 않을 경우, 이벤트를 중복 수신</strong></p>



<p class="wp-block-paragraph">a) 이전 틱 이벤트를 <strong>놓치고 현재 틱만 받음</strong></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">1&#x20e3; NetCodeConnectionEvents on the client</h4>



<figure class="wp-block-table"><table><thead><tr><th>상태</th><th>호출 규칙</th></tr></thead><tbody><tr><td>Unknown</td><td>절대 발생하지 않음</td></tr><tr><td>Connecting</td><td>자신의 클라이언트에서 한 번 발생. <code>NetworkStreamReceiveSystem</code>이 <code>Connect</code> 호출을 인식한 시점 (호출 프레임 다음일 수 있음)</td></tr><tr><td>Handshake</td><td>내부 transport 드라이버가 <code>Connected</code> 상태에 진입하면 한 번 발생</td></tr><tr><td>Approval</td><td>연결 승인 기능이 활성화된 경우에만 발생. Handshake 이후</td></tr><tr><td>Connected</td><td>서버가 <code>NetworkId</code>를 보내면 발생</td></tr><tr><td>Disconnected</td><td>연결 종료 또는 타임아웃 또는 서버에 의해 연결 해제 시 발생. <code>DisconnectReason</code>이 설정됨</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>주의 :</strong></p>



<p class="wp-block-paragraph">클라이언트는 다른 클라이언트의 이벤트를 수신하지 않습니다. 클라이언트 월드에서 발생한 이벤트는 <strong>자신의 클라이언트</strong>에 대한 이벤트뿐입니다.</p>



<p class="wp-block-paragraph">Handshake 및 Approval 단계는 실패할 수 있으며, <code>ClientServerTickRate.HandshakeApprovalTimeoutMS</code> (기본값: 5000ms)의 타임아웃을 가집니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">2&#x20e3; NetCodeConnectionEvents on the server</h4>



<figure class="wp-block-table"><table><thead><tr><th>상태</th><th>호출 규칙</th></tr></thead><tbody><tr><td>Unknown</td><td>절대 발생하지 않음</td></tr><tr><td>Connecting</td><td>클라이언트가 연결을 시작하는 순간을 서버는 알 수 없으므로 발생하지 않음</td></tr><tr><td>Handshake</td><td>모든 클라이언트에 대해 발생. transport 연결이 수락되면 진입</td></tr><tr><td>Approval</td><td>승인 기능이 활성화된 경우에만 발생. Handshake 성공 후</td></tr><tr><td>Connected</td><td>클라이언트가 <code>NetworkId</code>를 부여받는 순간 발생</td></tr><tr><td>Disconnected</td><td>클라이언트가 연결 해제되거나 타임아웃되면 발생. <code>DisconnectReason</code> 설정됨</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>주의 :</strong></p>



<p class="wp-block-paragraph">서버는 바인딩 성공이나 리스닝 시작에 대한 이벤트는 발생시키지 않습니다. <strong>기존 API를 통해 상태를 확인해야 합니다.</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Connection approval</h3>



<p class="wp-block-paragraph">서버에서 각 클라이언트 연결에 대해 <strong>승인을 요구하도록 설정할 수 있습니다</strong>.</p>



<p class="wp-block-paragraph">연결 승인은 다음과 같은 목적으로 사용됩니다:</p>



<ul class="wp-block-list">
<li>플레이어 편의성: 화이트리스트 / 블랙리스트, 비밀번호 보호된 서버 등</li>



<li>보안 검증: 매치메이킹 응답으로 받은 <strong>시크릿 토큰</strong>을 사용하여, 매치메이킹된 플레이어만 서버에 입장하도록 제한</li>
</ul>



<p class="wp-block-paragraph">연결 승인이 활성화되면 다음과 같은 변경이 발생 :</p>



<ol class="wp-block-list">
<li>클라이언트는 <strong>Handshake</strong> 및 <strong>Approval</strong> 단계에서만 <code>IApprovalRpcCommand</code> 형식의 RPC를 서버에 전송할 수 있습니다.</li>



<li>모든 클라이언트는 <strong>Handshake</strong> 상태에서 곧바로 <strong>Connected</strong>로 넘어가는 것이 아니라 <strong>Approval</strong> 상태로 이동합니다.</li>



<li>서버는 연결된 각 클라이언트의 엔티티에 <strong><code>ConnectionApproved</code> 컴포넌트</strong>를 <strong>직접 추가</strong>해야만 승인됩니다.</li>



<li><code>NetworkId</code>는 <strong>승인 성공 이후</strong>에만 할당됩니다. 승인에 실패하면 클라이언트는 연결이 끊어집니다.</li>



<li>승인 과정에는 <strong>타임아웃</strong>이 존재합니다 (<code>ClientServerTickRate.HandshakeApprovalTimeoutMS</code> 참조, 기본값: 5000ms)</li>
</ol>



<p class="wp-block-paragraph">다시 강조하면:</p>



<ul class="wp-block-list">
<li>Handshake 및 Approval 단계 동안, 클라이언트는 <strong>여러 개의 RPC 메시지를 보낼 수 있습니다.</strong></li>



<li>단, 이들 모두 <code>IApprovalRpcCommand</code> 타입이어야 합니다.</li>



<li>이 RPC의 payload에는 인증 토큰, 플레이어 정보 등 클라이언트의 유효성을 검증하기 위한 모든 정보를 담을 수 있습니다.</li>



<li>서버는 <code>ConnectionApproved</code> 컴포넌트를 클라이언트의 연결 엔티티에 추가함으로써 연결을 승인합니다. 이후 연결 흐름이 계속 진행됩니다.</li>
</ul>



<p class="wp-block-paragraph"><code>NetworkStreamDriver</code>에는 <code>RequireConnectionApproval</code> 필드가 있으며, <strong>클라이언트와 서버 모두에서 true로 설정</strong>해야 연결 승인 흐름이 올바르게 작동합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:2px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">if (isServer)
{
    using var drvQuery = server.EntityManager.CreateEntityQuery(ComponentType.ReadWrite&lt;NetworkStreamDriver>());
    drvQuery.GetSingletonRW&lt;NetworkStreamDriver>().ValueRW.RequireConnectionApproval = true;
    drvQuery.GetSingletonRW&lt;NetworkStreamDriver>().ValueRW.Listen(ep);
}
else
{
    using var drvQuery = client.EntityManager.CreateEntityQuery(ComponentType.ReadWrite&lt;NetworkStreamDriver>());
    drvQuery.GetSingletonRW&lt;NetworkStreamDriver>().ValueRW.RequireConnectionApproval = true;
    drvQuery.GetSingletonRW&lt;NetworkStreamDriver>().ValueRW.Connect(client.EntityManager, ep);
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">연결 승인 처리도 다음과 같이 설정할 수 있습니다 : </p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// The approval RPC, here it contains a hypothetical payload the server will validate
public struct ApprovalFlow : IApprovalRpcCommand
{
    public FixedString512Bytes Payload;
}

// This is used to indicate we've already sent an approval RPC and don't need to do so again
public struct ApprovalStarted : IComponentData
{
}

[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ThinClientSimulation)]
public partial struct ClientConnectionApprovalSystem : ISystem
{
    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate&lt;RpcCollection>();
    }

    public void OnUpdate(ref SystemState state)
    {
        var ecb = new EntityCommandBuffer(Allocator.Temp);
        // Check connections which have not yet fully connected and send connection approval message
        foreach (var (connection, entity) in SystemAPI.Query&lt;RefRW&lt;NetworkStreamConnection>>().WithNone&lt;NetworkId>().WithNone&lt;ApprovalStarted>().WithEntityAccess())
        {
            var sendApprovalMsg = ecb.CreateEntity();
            ecb.AddComponent(sendApprovalMsg, new ApprovalFlow { Payload = "ABC" });
            ecb.AddComponent&lt;SendRpcCommandRequest>(sendApprovalMsg);

            ecb.AddComponent&lt;ApprovalStarted>(entity);
        }
        ecb.Playback(state.EntityManager);
    }
}

[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
public partial struct ServerConnectionApprovalSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        var ecb = new EntityCommandBuffer(Allocator.Temp);
        // Check connections which have not yet fully connected and send connection approval message
        foreach (var (receiveRpc, approvalMsg, entity) in SystemAPI.Query&lt;RefRO&lt;ReceiveRpcCommandRequest>,RefRW&lt;ApprovalFlow>>().WithEntityAccess())
        {
            var connectionEntity = receiveRpc.ValueRO.SourceConnection;
            if (approvalMsg.ValueRO.Payload.Equals("ABC"))
            {
                ecb.AddComponent&lt;ConnectionApproved>(connectionEntity);

                // Destroy RPC message
                ecb.DestroyEntity(entity);
            }
            else
            {
                // Failed approval messages should be disconnected
                ecb.AddComponent&lt;NetworkStreamRequestDisconnect>(connectionEntity);
            }
        }
        ecb.Playback(state.EntityManager);
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 연결 확인 Sample 코드</h2>



<p class="wp-block-paragraph"><a href="https://discussions.unity.com/t/lobby-relay-and-netcode-for-entities/1538817/8" target="_blank" rel="noreferrer noopener">https://discussions.unity.com/t/lobby-relay-and-netcode-for-entities/1538817/8</a></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><strong>WorldManager.cs</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System.Diagnostics.CodeAnalysis;
using Unity.Entities;

namespace Managers 
{
    public static class WorldManager 
    {
        private static World _clientWorld, _serverWorld;
		
        [SuppressMessage("ReSharper", "ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator")]
        public static void DestroyLocalSimulationWorld() {
            foreach (var world in World.All) {
                if (world.Flags != WorldFlags.Game) continue;
                world.Dispose();
                break;
            }
        }
		
        public static void RegisterServerWorld(World world) => _serverWorld = world;
        public static void RegisterClientWorld(World world) => _clientWorld = world;

        public static World GetServerWorld() => _serverWorld;
        public static World GetClientWorld() => _clientWorld;
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><strong>RelayServerDataHelper.cs</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Networking.Transport;
using Unity.Networking.Transport.Relay;
using Unity.Services.Relay.Models;

namespace Extension 
{
    public static class RelayServerDataHelper 
    {
        private static RelayServerData GetRelayData(List&lt;RelayServerEndpoint> endpoints, byte[] allocationIdBytes, byte[] connectionDataBytes, byte[] hostConnectionDataBytes, byte[] keyBytes) 
        {
            
            var endpoint = endpoints.FirstOrDefault(e => e.ConnectionType == "dtls") ?? throw new InvalidOperationException($"Endpoint for connectionType dtls not found");
            
            var server = NetworkEndpoint.Parse(endpoint.Host, (ushort)endpoint.Port);
    
            var allocationId = RelayAllocationId.FromByteArray(allocationIdBytes);
            var connData = RelayConnectionData.FromByteArray(connectionDataBytes);
            var hostData = RelayConnectionData.FromByteArray(hostConnectionDataBytes);
            var key = RelayHMACKey.FromByteArray(keyBytes);

            return new RelayServerData(ref server, 0, ref allocationId, ref connData, ref hostData, ref key, true); 
        }

        public static RelayServerData RelayData(JoinAllocation a) => GetRelayData(a.ServerEndpoints, a.AllocationIdBytes, a.ConnectionData, a.HostConnectionData, a.Key);

        public static RelayServerData RelayData(Allocation a) => GetRelayData(a.ServerEndpoints, a.AllocationIdBytes, a.ConnectionData, a.ConnectionData, a.Key);
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><strong>RelayInitializer.cs</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.Collections;
using System.Threading.Tasks;
using Unity.Entities;
using Unity.NetCode;
using Unity.Networking.Transport;
using Unity.Networking.Transport.Relay;
using Unity.Services.Authentication;
using Unity.Services.Core;
using Unity.Services.Relay;
using UnityEngine;
using Extension;

namespace Managers 
{
    public class RelayInitializer : MonoBehaviour 
    {
        private static RelayServerData? _relayServerData, _relayClientData;
        private static Action OnConnectionComplete;
        public static string _joinCode;
    
        // Singleton (without duplicate destroy logic) for access to the class instance from within the static methods
        private static RelayInitializer Instance { get; set; }
        protected void Awake() => Instance = this;
        
        // HOSTING

        public static void StartHost() => Instance.StartCoroutine(InitializeHost());

        private static IEnumerator InitializeHost() 
        {
            var initializeTask = UnityServices.InitializeAsync();
            while (!initializeTask.IsCompleted)
                yield return null;
            if (ProcessTaskFail(initializeTask, nameof(initializeTask)))
                yield break;

            var signInTask = Task.CompletedTask;
            if (!AuthenticationService.Instance.IsSignedIn) {
                signInTask = AuthenticationService.Instance.SignInAnonymouslyAsync();
                while (!signInTask.IsCompleted)
                    yield return null; 
            }
            
            if (ProcessTaskFail(signInTask, nameof(signInTask)))
                yield break;

            var allocationTask = RelayService.Instance.CreateAllocationAsync(5);
            
            while (!allocationTask.IsCompleted)
                yield return null;
            
            if (ProcessTaskFail(allocationTask, nameof(allocationTask)))
                yield break;

            var joinCodeTask = RelayService.Instance.GetJoinCodeAsync(allocationTask.Result.AllocationId);
            
            while (!joinCodeTask.IsCompleted)
                yield return null;
            
            if (ProcessTaskFail(joinCodeTask, nameof(joinCodeTask)))
                yield break;

            _joinCode = joinCodeTask.Result;

            try 
            {
                Debug.Log("Hosting relay data");
                _relayServerData = RelayServerDataHelper.RelayData(allocationTask.Result);
            } catch (Exception e) {
                Debug.LogException(e);
                _relayServerData = null;
                yield break;
            }

            Debug.Log("Success, players may now connect");
            while (_relayServerData == null || (!_relayServerData?.Endpoint.IsValid ?? false))
                yield return null;
                
            yield return JoinUsingCode(_joinCode);
            yield return WaitRelayConnection();
            SetupRelayHostedServerAndConnect();
        }

        private static void SetupRelayHostedServerAndConnect() 
        {
            if (ClientServerBootstrap.RequestedPlayType != ClientServerBootstrap.PlayType.ClientAndServer) 
            {
                UnityEngine.Debug.LogError($"Creating client/server worlds is not allowed if playmode is set to {ClientServerBootstrap.RequestedPlayType}");
                return;
            }

            var relayServerData = _relayServerData.GetValueOrDefault();
            var relayClientData = _relayClientData.GetValueOrDefault();

            var oldConstructor = NetworkStreamReceiveSystem.DriverConstructor;
            NetworkStreamReceiveSystem.DriverConstructor = new RelayDriverConstructor(relayServerData, relayClientData);
            var server = ClientServerBootstrap.CreateServerWorld("ServerWorld");
            WorldManager.RegisterServerWorld(server);
            var client = ClientServerBootstrap.CreateClientWorld("ClientWorld");
            WorldManager.RegisterClientWorld(client);
            NetworkStreamReceiveSystem.DriverConstructor = oldConstructor;

            

            WorldManager.DestroyLocalSimulationWorld();
            World.DefaultGameObjectInjectionWorld ??= server;

            // Load scene here if you want to.
            
            Debug.Log(_joinCode);

            var networkStreamEntity = server.EntityManager.CreateEntity(ComponentType.ReadWrite&lt;NetworkStreamRequestListen>());
            server.EntityManager.SetName(networkStreamEntity, "NetworkStreamRequestListen");
            server.EntityManager.SetComponentData(networkStreamEntity,
                new NetworkStreamRequestListen { Endpoint = NetworkEndpoint.AnyIpv4 });

            networkStreamEntity =
                client.EntityManager.CreateEntity(ComponentType.ReadWrite&lt;NetworkStreamRequestConnect>());
            client.EntityManager.SetName(networkStreamEntity, "NetworkStreamRequestConnect");
            client.EntityManager.SetComponentData(networkStreamEntity,
                new NetworkStreamRequestConnect { Endpoint = relayClientData.Endpoint });
            
            ProcessConnectionComplete();
        }

        // CONNECTING
    
        public static void ConnectByCode(string joinCode) => Instance.StartCoroutine(ProcessCodeConnection(joinCode));
    
        private static IEnumerator ProcessCodeConnection(string joinCode) 
        {
            Instance.StartCoroutine(JoinExternalServer(joinCode));
            yield return WaitRelayConnection();
            ConnectToRelayServer();
        }

        private static IEnumerator WaitRelayConnection() {
            while (_relayClientData == null || (!_relayClientData?.Endpoint.IsValid ?? false))
                yield return null;
        }
        
        private static IEnumerator JoinExternalServer(string joinCode) {
            Debug.Log("Waiting for relay response");
            var setupTask = UnityServices.InitializeAsync();

            while (!setupTask.IsCompleted)
                yield return null;
            
            var signInTask = Task.CompletedTask;
            if (!AuthenticationService.Instance.IsSignedIn) {
                signInTask = AuthenticationService.Instance.SignInAnonymouslyAsync();
                while (!signInTask.IsCompleted)
                    yield return null;
            }
            if (ProcessTaskFail(signInTask, nameof(signInTask)))
                yield break;

            yield return JoinUsingCode(joinCode);
        }

        private static IEnumerator JoinUsingCode(string joinCode) 
        {
            // Send the join request to the Relay service
            var joinTask = RelayService.Instance.JoinAllocationAsync(joinCode);
            while (!joinTask.IsCompleted)
                yield return null;
            
            if (ProcessTaskFail(joinTask, nameof(joinTask)))
                yield break;
            
            // Format the server data, based on desired connectionType
            try {
                _relayClientData = RelayServerDataHelper.RelayData(joinTask.Result);
            } catch (Exception e) 
            {
                Debug.LogException(e);
                _relayClientData = null;
            }

            _joinCode = joinCode;
        }
    
        private static void ConnectToRelayServer() 
        {
            var relayClientData = _relayClientData.GetValueOrDefault();
            
            var oldConstructor = NetworkStreamReceiveSystem.DriverConstructor;
            NetworkStreamReceiveSystem.DriverConstructor = new RelayDriverConstructor(new RelayServerData(), relayClientData);
            var client = ClientServerBootstrap.CreateClientWorld("ClientWorld");
            WorldManager.RegisterClientWorld(client);
            NetworkStreamReceiveSystem.DriverConstructor = oldConstructor;

            WorldManager.DestroyLocalSimulationWorld();
            World.DefaultGameObjectInjectionWorld ??= client;
        
            var networkStreamEntity =
                client.EntityManager.CreateEntity(ComponentType.ReadWrite&lt;NetworkStreamRequestConnect>());
            client.EntityManager.SetName(networkStreamEntity, "NetworkStreamRequestConnect");
            client.EntityManager.SetComponentData(networkStreamEntity,
                new NetworkStreamRequestConnect { Endpoint = relayClientData.Endpoint });
            ProcessConnectionComplete();
        }

        // COMMON
    
        private static bool ProcessTaskFail(Task task, string taskName) {
            if (!task.IsFaulted) return false;
            Debug.LogError($"Task {taskName} failed.");
            Debug.LogException(task.Exception);
            return true;
        }

        public static void SubscribeToConnectionComplete(Action handler) => OnConnectionComplete += handler;

        public static void ProcessConnectionComplete() {
            if (OnConnectionComplete == null)
                return;
            OnConnectionComplete?.Invoke();
            foreach (var handler in OnConnectionComplete?.GetInvocationList()!)
                OnConnectionComplete -= (Action)handler;
        }
    }

    public class RelayDriverConstructor : INetworkStreamDriverConstructor 
    {
        private RelayServerData _relayServerData, _relayClientData;
    
        public RelayDriverConstructor(RelayServerData serverData, RelayServerData clientData) {
            _relayServerData = serverData;
            _relayClientData = clientData;
        }

        public void CreateClientDriver(World world, ref NetworkDriverStore driverStore, NetDebug netDebug) {
            var settings = DefaultDriverBuilder.GetNetworkSettings();
            settings.WithRelayParameters(ref _relayClientData);
            DefaultDriverBuilder.RegisterClientDriver(world, ref driverStore, netDebug, settings);
        }

        public void CreateServerDriver(World world, ref NetworkDriverStore driverStore, NetDebug netDebug) =>
            DefaultDriverBuilder.RegisterServerDriver(world, ref driverStore, netDebug, ref _relayServerData);
    }
}</pre>
<p>The post <a href="https://lycos7560.com/c/unity-netcode-for-entities-creating-multiplayer-gameplay/39997/">Unity Netcode for Entities – Connecting server and clients</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/unity-netcode-for-entities-creating-multiplayer-gameplay/39997/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Unity Netcode for Entities &#8211; Client / Server worlds</title>
		<link>https://lycos7560.com/unity/unity-netcode-for-entities-client-server-worlds/39993/</link>
					<comments>https://lycos7560.com/unity/unity-netcode-for-entities-client-server-worlds/39993/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Thu, 01 May 2025 00:50:19 +0000</pubDate>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Unity]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[Client / Server worlds]]></category>
		<category><![CDATA[dtls]]></category>
		<category><![CDATA[IP]]></category>
		<category><![CDATA[multiplayer]]></category>
		<category><![CDATA[Netcode]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[UDP]]></category>
		<category><![CDATA[WSS]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[유니티]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39993</guid>

					<description><![CDATA[<p>🔥 Client / Server worlds Setting Netcode for Entities 1.5.0v https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/set-up-client-server-worlds.html Netcode for Entities의 네트워킹 모델을 사용하여client 와 server를 설정합니다. ✅ 클라이언트 / 서버 World 네트워크 모델 https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/client-server-worlds.html Netcode for Entities는 Client와 Server의 로직을 각각 Client world와 Server world로 분리하여 처리합니다. 월드(World)는 Unity의 ECS(Entity Component System) 개념으로, 엔티티와 시스템을 시스템 그룹(SystemGroup)으로 구성한 단위입니다. 기본적인 Client [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/unity-netcode-for-entities-client-server-worlds/39993/">Unity Netcode for Entities &#8211; Client / Server worlds</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-cbe7176c      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#client-server-worlds-setting" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Client / Server worlds Setting</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#클라이언트-서버-world-네트워크-모델" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 클라이언트 / 서버 World 네트워크 모델</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#시스템-생성-및-업데이트-구성" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 시스템 생성 및 업데이트 구성</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-특정-시스템-그룹에-할당하기" class="uagb-toc-link__trigger">1&#x20e3; 특정 시스템 그룹에 할당하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-worldsystemfilter-사용" class="uagb-toc-link__trigger">2&#x20e3; WorldSystemFilter 사용</a></li></ul><li class="uagb-toc__list"><a href="#client-server-worlds-with-bootstrapping" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Client / Server worlds with bootstrapping</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#0-bootstrapping-커스터-마이징" class="uagb-toc-link__trigger">0&#x20e3; Bootstrapping 커스터 마이징</a></li></ul><li class="uagb-toc__list"><a href="#client-server-업데이트-주기" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Client / Server 업데이트 주기</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#성능-문제-방지" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 성능 문제 방지</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#프레임-속도-유지-방식-설정" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 프레임 속도 유지 방식 설정</a></li></ul><li class="uagb-toc__list"><a href="#월드-마이그레이션-world-migration" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 월드 마이그레이션 (World Migration)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#additional-resources" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2795.png" alt="➕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Additional resources</a></li></ul><li class="uagb-toc__list"><a href="#네트워크-프로토콜-검사" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 네트워크 프로토콜 검사</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#개발-중-발생할-수-있는-문제" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 개발 중 발생할 수 있는 문제</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#프로토콜-검사-해제" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 프로토콜 검사 해제</a></li></ul><li class="uagb-toc__list"><a href="#additional-resources" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2795.png" alt="➕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Additional resources</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Client / Server worlds Setting</h2>



<p class="wp-block-paragraph"><strong>Netcode for Entities 1.5.0v</strong></p>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/set-up-client-server-worlds.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/set-up-client-server-worlds.html</a></p>



<p class="wp-block-paragraph">Netcode for Entities의 네트워킹 모델을 사용하여client 와 server를 설정합니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 클라이언트 / 서버 World 네트워크 모델</h3>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/client-server-worlds.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/client-server-worlds.html</a></p>



<p class="wp-block-paragraph"><strong>Netcode for Entities</strong>는 <strong>Client</strong>와 Server의 로직을 각각 <strong>Client world</strong>와 <strong>Server world</strong>로 분리하여 처리합니다.</p>



<p class="wp-block-paragraph">월드(World)는 Unity의 <strong>ECS(Entity Component System)</strong> 개념으로, <strong>엔티티와 시스템을 시스템 그룹(SystemGroup)으로 구성한 단위</strong>입니다.</p>



<p class="wp-block-paragraph">기본적인 Client / Server 월드 외에도, 개발 중에 게임을 테스트할 수 있도록 <strong>씬 클라이언트(Thin Client)</strong> 기능도 지원합니다.</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 시스템 생성 및 업데이트 구성</h4>



<p class="wp-block-paragraph">기본적으로, 모든 시스템은 클라이언트/서버 월드의 <code><a href="https://docs.unity3d.com/Packages/com.unity.entities@latest?subfolder=/api/Unity.Entities.SimulationSystemGroup.html" target="_blank" rel="noreferrer noopener"><code>SimulationSystemGroup</code></a></code>에서 생성되고 업데이트됩니다. </p>



<p class="wp-block-paragraph">하지만 특정 시스템을 <strong>클라이언트에만</strong>, 또는 <strong>서버에만</strong> 작동하게 만들고 싶을 때는 다음 두 가지 방법을 사용할 수 있습니다</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">1&#x20e3; 특정 시스템 그룹에 할당하기</h5>



<p class="wp-block-paragraph">시스템이 속할 시스템 그룹을 지정하면, <strong>그 시스템 그룹이 존재하지 않는 월드에서는 자동으로 필터링</strong>됩니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[UpdateInGroup(typeof(GhostInputSystemGroup))]
public class MyInputSystem : SystemBase
{
  ...
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 참고: </p>



<p class="wp-block-paragraph"><code><a href="https://docs.unity3d.com/Packages/com.unity.entities@latest?subfolder=/api/Unity.Entities.PresentationSystemGroup.html" target="_blank" rel="noreferrer noopener"><code>PresentationSystemGroup</code></a></code>에 속한 시스템은 클라이언트 월드에서만 작동합니다. </p>



<p class="wp-block-paragraph">Server / Thin client worlds에는 이 시스템 그룹이 생성되지 않기 때문입니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">2&#x20e3; WorldSystemFilter 사용</h5>



<p class="wp-block-paragraph">시스템이 어떤 월드에서 작동할지 더 세부적으로 설정하고 싶다면 <strong><code>WorldSystemFilter</code> </strong>속성을 사용하세요.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// WorldSystemFilterFlags 옵션 
// - LocalSimulation: 오프라인 로컬 시뮬레이션 (Netcode 시스템 없음)
// - ServerSimulation: 서버 시뮬레이션
// - ClientSimulation: 클라이언트 시뮬레이션
// - ThinClientSimulation: 씬 클라이언트
// 속성을 지정하지 않으면, 기본적으로 시스템은 상위 그룹의 필터링 규칙을 따릅니다 (WorldSystemFilterFlags.Default)

[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public class MySystem : SystemBase
{
  ...
}</pre>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Client / Server worlds with bootstrapping</h4>



<p class="wp-block-paragraph"><strong>Netcode for Entities</strong>를 프로젝트에 추가하면, 기본 <code><a href="https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientServerBootstrap.html" target="_blank" rel="noreferrer noopener"><code>ClientServerBootstrap</code></a></code> 클래스가 제공됩니다. </p>



<p class="wp-block-paragraph">이 클래스는 <strong>게임 시작 시 클라이언트 및 서버 월드를 자동 생성</strong>합니다</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public virtual bool Initialize(string defaultWorldName)
{
    CreateDefaultClientServerWorlds();
    return true;
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">자동 부트스트래핑은 <strong>에디터에서 Play 모드 진입 시 매우 유용</strong>합니다. </p>



<p class="wp-block-paragraph">하지만, <strong>실제 게임에서는 메인 메뉴와 같은 전처리 과정이 필요</strong>하므로 <strong>월드 생성을 지연</strong>시키거나 수동 제어할 필요가 있습니다.</p>



<p class="wp-block-paragraph">예를 들어</p>



<ul class="wp-block-list">
<li>&#8220;클라이언트-호스트 서버 실행&#8221; → <strong>서버</strong>와 <strong>클라이언트</strong> 모두 생성</li>



<li>&#8220;전용 서버에 접속&#8221; → <strong>클라이언트</strong>만 생성</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">0&#x20e3; Bootstrapping 커스터 마이징</h5>



<p class="wp-block-paragraph">기본 <code>ClientServerBootstrap</code>을 상속하여 원하는 방식으로 월드 생성을 제어할 수 있습니다</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public class MyGameSpecificBootstrap : ClientServerBootstrap
{
    public override bool Initialize(string defaultWorldName)
    {
        // Netcode 없이 로컬 시뮬레이션 월드만 생성
        CreateLocalWorld(defaultWorldName);
        return true;
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">버튼 클릭 등의 타이밍에 다음을 호출하여 월드 생성:</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var clientWorld = ClientServerBootstrap.CreateClientWorld();
var serverWorld = ClientServerBootstrap.CreateServerWorld();

AutomaticThinClientWorldsUtility.NumThinClientsRequested = 10;
AutomaticThinClientWorldsUtility.BootstrapThinClientWorlds();

ClientServerBootstrap.CreateDefaultClientServerWorlds();
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Netcode Sample  <a href="https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/NetcodeSamples/README.md" target="_blank" rel="noreferrer noopener">https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/NetcodeSamples/README.md</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Client / Server 업데이트 주기</h4>



<p class="wp-block-paragraph">Netcode for Entities를 사용할 때, 서버는 항상 <strong>Fixed Timestep</strong>으로 업데이트됩니다.</p>



<p class="wp-block-paragraph">이는 클라이언트 예측을 위한 기본적인 결정론 수준을 보장하고(엄격한 결정론은 아니지만), 물리 시스템의 안정성과 프레임 레이트 독립성을 보장하기 위함입니다.</p>



<p class="wp-block-paragraph">또한 이 패키지는 프레임당 최대 Fixed Step 반복 횟수를 제한하여 서버가 단일 프레임을 시뮬레이션하는 데 몇 초가 걸리는 상태에 빠지지 않도록 합니다.</p>



<p class="wp-block-paragraph">중요한 점은 서버의 고정 업데이트가 <a href="https://docs.unity3d.com/Manual/class-TimeManager.html"><strong>표준 Unity 업데이트 빈도</strong></a>&nbsp;또 <strong>Unity 물리 시스템의 Fixed Timestep</strong>를 사용하지 않는다는 것입니다. </p>



<p class="wp-block-paragraph"><strong>대신 자체적인 <code>ClientServerTickRate.SimulationTickRate</code> 를 사용합니다. </strong>&#8211; 고정된 틱(tick) 간격</p>



<p class="wp-block-paragraph">(이것은 <code>Unity.Physics</code>가 사용 중이라면 정수 배수여야 합니다. <code>ClientServerTickRate.PredictedFixedStepSimulationTickRatio</code>를 참조하세요)</p>



<p class="wp-block-paragraph">그러나 클라이언트는 동적 타임스텝으로 업데이트됩니다. </p>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/intro-to-prediction.html" target="_blank" rel="noreferrer noopener">예측 코드만 예외</a>로, 이는 항상 서버와 동일한 고정 타임스텝으로 실행되어 두 시뮬레이션 간의 결정론적 관계를 유지하려고 합니다.</p>



<p class="wp-block-paragraph">전체 틱과 동기화되지 않은 리프레시 레이트에 대한 예측 처리 방법을 이해하려면 <a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/intro-to-prediction.html#partial-ticks" target="_blank" rel="noreferrer noopener">부분 틱</a>을 참조하세요.</p>



<p class="wp-block-paragraph">설정 예시:</p>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/api/Unity.NetCode.ClientServerTickRate.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.netcode@1.5/api/Unity.NetCode.ClientServerTickRate.html</a></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class MyCustomClientServerBootstrap : ClientServerBootstrap
{
   override public void Initialize(string defaultWorld)
   {
       base.Initialise(defaultWorld);
       var customTickRate = new ClientServerTickRate();
       //run at 30hz
       customTickRate.simulationTickRate = 30;
       customTickRate.ResolveDefault();
       foreach(var world in World.All)
       {
           if(world.IsServer())
           {
              // 이 경우 서버에서만 생성되지만 클라이언트 세계에서도 동일한 작업을 수행할 수 있습니다
              var tickRateEntity = world.EntityManager.CreateSingleton(new ClientServerTickRate
              {
                  SimulationTickRate = 30;
              });
           }
       }
   }
}


// SimulationTickRate = 초당 몇 번의 시뮬레이션 틱을 실행할지 설정합니다. 기본값은 초당 60 틱입니다.
// NetworkTickRate = 서버가 클라이언트에게 스냅샷(snapshot)을 전송하는 빈도를 설정합니다. 기본값은 SimulationTickRate와 동일합니다.</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 성능 문제 방지</h5>



<p class="wp-block-paragraph">서버가 설정된 시뮬레이션 틱 레이트보다 <strong>느린 속도로 업데이트될 경우</strong>, 한 프레임에서 여러 개의 시뮬레이션 틱을 처리하려고 시도합니다.</p>



<p class="wp-block-paragraph">예를 들어, 서버의 마지막 업데이트가 16ms가 아닌 50ms가 걸렸다면, 서버는 다음 프레임에서 이를 따라잡기 위해 약 <strong>3번의 시뮬레이션 스텝</strong>을 실행합니다 (16ms × 3 ≈ 50ms).</p>



<p class="wp-block-paragraph">이러한 동작은 <strong>성능 저하를 악화시키는 악순환</strong>을 유발할 수 있습니다:</p>



<p class="wp-block-paragraph">서버는 더 많은 시뮬레이션 스텝을 처리해야 하므로 점점 더 느려지고, 그 결과 틱 간격을 더 많이 놓치게 됩니다.</p>



<p class="wp-block-paragraph">위와 같은 상황을 방지하기 위해 <code>ClientServerTickRate</code>에서는 다음과 같은 설정을 제공합니다</p>



<ul class="wp-block-list">
<li><code>MaxSimulationStepsPerFrame</code><br>서버가 한 프레임 내에 수행할 수 있는 <strong>최대 시뮬레이션 스텝 수</strong>를 제한합니다.</li>



<li><code>MaxSimulationStepBatchSize</code><br>여러 틱을 <strong>하나의 배치(batch)</strong> 로 묶어 처리하도록 지시합니다. <br>이때는 <strong>델타 타임(delta time)을 곱한 값</strong>으로 단일 스텝을 실행합니다.<br>예: 2개의 스텝을 실행하는 대신, <strong>1개의 스텝</strong>만 실행하되 <strong>델타 타임을 2배</strong>로 설정하여 처리합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6ab.png" alt="🚫" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>주의</strong>: <br><code>MaxSimulationStepBatchSize</code> 에 의한 배칭 처리는 특정 조건에서만 작동하며, 자체적인 제한 사항이 있습니다.<br>게임 로직에서 &#8220;<strong>한 시뮬레이션 스텝 = 하나의 틱</strong>&#8220;이라고 가정하지 않도록 하며, <code>TimeData.DeltaTime</code> 값을 <strong>하드코딩하지 마십시오</strong>.<br>배칭이 발생하면 <strong>서버와 클라이언트 간의 시뮬레이션 정밀도가 달라져 예측 오류</strong>가 발생할 수 있습니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270f.png" alt="✏" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 프레임 속도 유지 방식 설정</h5>



<p class="wp-block-paragraph">서버가 유휴 상태일 때 <strong>프레임 속도를 어떻게 유지할지</strong>도 설정할 수 있습니다. </p>



<p class="wp-block-paragraph"><code>TargetFrameRateMode</code>는 서버가 틱 레이트를 유지하기 위한 <strong>유휴 시간 소비 방식</strong>을 제어합니다.</p>



<ul class="wp-block-list">
<li><strong><code>BusyWait</code></strong><br>가능한 한 빠른 속도로 계속 실행 (CPU 사용량 높음)</li>



<li><strong><code>Sleep</code></strong><br><code>Application.targetFrameRate</code>에 맞춰 대기하여 CPU 부하를 줄임</li>



<li><strong><code>Auto</code></strong><br>헤드리스(콘솔 없는) 서버에서는 <code>Sleep</code>을 사용하고, 그 외에는 <code>BusyWait</code>을 사용</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 월드 마이그레이션 (World Migration)</h4>



<p class="wp-block-paragraph">현재 사용 중인 월드를 <strong>파괴(destroy)</strong> 하고, <strong>연결 상태를 유지한 채로 새로운 월드로 전환</strong>하고자 할 경우, <code>DriverMigrationSystem</code>을 사용할 수 있습니다.</p>



<p class="wp-block-paragraph">이 시스템은 네트워크 전송(transport) 관련 정보를 저장 및 불러올 수 있게 해주며, <strong>매끄러운 월드 전환</strong>을 가능하게 합니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public World MigrateWorld(World sourceWorld)
{
    DriverMigrationSystem migrationSystem = default;
    foreach (var world in World.All)
    {
        if ((migrationSystem = world.GetExistingSystem&lt;DriverMigrationSystem>()) != null)
            break;
    }

    var ticket = migrationSystem.StoreWorld(sourceWorld);
    sourceWorld.Dispose();

    var newWorld = migrationSystem.LoadWorld(ticket);

    // NOTE: LoadWorld는 반드시 새 월드에 필요한 시스템을 추가하기 전에 호출해야 합니다!
    // 이유: LoadWorld는 NetworkStreamReceiveSystem이 올바른 드라이버를 불러오기 위해 필요한 
    // `MigrationTicket` 컴포넌트를 생성하기 때문입니다.

    return ClientServerBootstrap.CreateServerWorld(DefaultWorld, newWorld.Name, newWorld);
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">이 기능은 예를 들어 <strong>로비에서 인게임으로 넘어가거나</strong>, <strong>씬을 바꾸면서도 연결을 유지해야 하는 경우</strong>에 매우 유용합니다.</p>



<h4 class="wp-block-heading" id="additional-resources"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2795.png" alt="➕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Additional resources</h4>



<ul class="wp-block-list">
<li><a href="https://docs.unity3d.com/Packages/com.unity.entities@latest?subfolder=/manual/index.html" target="_blank" rel="noreferrer noopener">Entities overview</a></li>



<li><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/thin-clients.html" target="_blank" rel="noreferrer noopener">Thin clients</a></li>



<li><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/intro-to-prediction.html" target="_blank" rel="noreferrer noopener">Introduction to prediction</a></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 네트워크 프로토콜 검사</h3>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/network-protocol-checks.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/network-protocol-checks.html</a></p>



<p class="wp-block-paragraph">클라이언트가 서버에 연결할 때, <strong>Netcode 프로토콜 버전(NetworkProtocolVersion)</strong> 을 주고받습니다. </p>



<p class="wp-block-paragraph">이 프로토콜에는 다음과 같은 정보가 포함되어 있습니다:</p>



<ul class="wp-block-list">
<li>Netcode 버전 (netcode version)</li>



<li>게임 버전 (game version)</li>



<li>RPC 컬렉션 (RPC collection)</li>



<li>직렬화된 컴포넌트 컬렉션 (erialized component collections)</li>
</ul>



<p class="wp-block-paragraph">서로 호환되지 않는 게임 버전 간의 연결을 <strong>사전에 차단</strong>하여 예측할 수 없는 동작을 방지합니다.</p>



<p class="wp-block-paragraph"> <strong>RPC 컬렉션</strong>은 모든 로드된 어셈블리에서 컴파일된 RPC들의 <strong>타입과 멤버</strong>를 기반으로 해시를 계산합니다.</p>



<p class="wp-block-paragraph"><strong>Serialized Component 컬렉션</strong> 역시 Netcode for Entities가 수집한 고스트(Ghost) 컴포넌트를 기반으로 해시를 계산합니다.</p>



<p class="wp-block-paragraph">이러한 타입 정보와 멤버들을 기반으로 계산된 <strong>해시값</strong>이 프로토콜의 일부로 사용됩니다.</p>



<p class="wp-block-paragraph">Netcode for Entities는 기본적으로 이 해시값이 <strong>완전히 동일해야</strong> 통신이 가능하도록 설정되어 있습니다.</p>



<p class="wp-block-paragraph">이렇게 하면 중간에 <strong>예외가 발생하는 것을 방지</strong>하고, <strong>대역폭 최적화</strong>가 가능합니다.</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)"/>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 개발 중 발생할 수 있는 문제</h4>



<p class="wp-block-paragraph">개발 중에는 <strong>호환 가능한 빌드조차도</strong> 이 검사 때문에 <strong>부적합한 버전으로 잘못 판단되는</strong> 경우가 있습니다.</p>



<p class="wp-block-paragraph">예를 들어:</p>



<ul class="wp-block-list">
<li>독립 실행형 빌드와 Unity Editor에서 실행 중인 월드를 연결하려는 경우</li>



<li>Editor는 테스트용 어셈블리를 포함하고 있는데, 빌드에는 포함되어 있지 않을 경우</li>



<li>이로 인해 해시가 일치하지 않아 연결이 끊어짐</li>
</ul>



<p class="wp-block-paragraph">이러한 <strong>엄격한 프로토콜 검사</strong>는 개발 중 테스트에 방해가 될 수 있으므로, <strong><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/manual/network-protocol-checks.html#disabling-the-check" target="_blank" rel="noreferrer noopener">비활성화</a>할 수 있습니다.</strong></p>



<p class="wp-block-paragraph">이 프로토콜 버전 오류가 발생하면 각 피어는 사용자 코드가 읽을 수 있는 를 통해 원격 피어와의 연결을 끊고&nbsp;</p>



<p class="wp-block-paragraph"><code>NetworkStreamDisconnectReason.BadProtocolVersion</code>, 이를 사용하여 플레이어에게 해당 빌드가 대상 원격 피어와 호환되지 않음을 알립니다. </p>



<p class="wp-block-paragraph">개발 빌드에서는 패키지가 로컬 피어에 로드된 RPC 및 고스트 유형의 전체 목록과 정렬된 목록을 나타내는 오류 로그도 출력합니다. </p>



<p class="wp-block-paragraph">유형 불일치 문제를 해결하려면 이러한 로그를 원격 피어에서 발생한 로그와 상호 참조하세요.</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>프로토콜 검사</strong> 해제</h5>



<p class="wp-block-paragraph">검사를 비활성화하려면, 다음과 같이 <code>RpcCollection.DynamicAssemblyList</code>를 <code>true</code>로 설정합니다:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[BurstCompile] // 선택 사항
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ServerSimulation | WorldSystemFilterFlags.ThinClientSimulation)]
[UpdateInGroup(typeof(InitializationSystemGroup))]
[CreateAfter(typeof(RpcSystem))]
public partial struct SetRpcSystemDynamicAssemblyListSystem : ISystem
{
    public void OnCreate(ref SystemState state)
    {
        SystemAPI.GetSingletonRW&lt;RpcCollection>().ValueRW.DynamicAssemblyList = true;
        state.Enabled = false;
    }
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6ab.png" alt="🚫" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>주의</strong>: </p>



<p class="wp-block-paragraph">이 설정은 <code>RpcSystem.OnUpdate</code>가 실행되기 전에 적용되어야 하며,</p>



<p class="wp-block-paragraph"><code>RpcSystem.OnCreate</code> 이후에 설정되어야 합니다.</p>



<p class="wp-block-paragraph"><strong>클라이언트와 서버 모두 이 플래그 값을 동일하게 설정해야</strong> 연결이 가능합니다. (값이 다르면 프로토콜 해석 방식이 달라지기 때문)</p>



<p class="wp-block-paragraph">이 기능을 활성화하면 다음과 같은 단점이 있습니다:</p>



<ul class="wp-block-list">
<li>각 RPC마다 <strong>6바이트의 오버헤드</strong>가 발생합니다 (ushort 인덱스 대신 해시값 전체를 전송)</li>



<li>게임 도중, 클라이언트가 <strong>알 수 없는 RPC나 고스트 타입을 수신</strong>하게 되면<br>→ 런타임 오류 발생 후 <strong>즉시 연결이 끊어짐</strong><br>→ 즉, 연결 시점이 아닌 게임 중간에 강제 종료될 수 있습니다</li>
</ul>



<p class="wp-block-paragraph">따라서 이 설정은 <strong>개발 중 테스트 목적</strong>으로 사용하는 것이 좋으며, <strong>출시용 빌드에서는 권장되지 않습니다.</strong></p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2795.png" alt="➕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Additional resources</h4>



<ul class="wp-block-list">
<li><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/api/Unity.NetCode.NetworkProtocolVersion.html" target="_blank" rel="noreferrer noopener">NetworkProtocolVersion API</a></li>
</ul>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://lycos7560.com/unity/unity-netcode-for-entities-client-server-worlds/39993/">Unity Netcode for Entities &#8211; Client / Server worlds</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/unity-netcode-for-entities-client-server-worlds/39993/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>IInputComponentData [Netcode for Entities]</title>
		<link>https://lycos7560.com/unity/iinputcomponentdata-netcode-for-entities/39970/</link>
					<comments>https://lycos7560.com/unity/iinputcomponentdata-netcode-for-entities/39970/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sun, 27 Apr 2025 06:33:37 +0000</pubDate>
				<category><![CDATA[Unity]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[Client / Server worlds]]></category>
		<category><![CDATA[compress]]></category>
		<category><![CDATA[connect]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[dtls]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Entities]]></category>
		<category><![CDATA[Entity]]></category>
		<category><![CDATA[IComponentData]]></category>
		<category><![CDATA[IInputComponentData]]></category>
		<category><![CDATA[IP]]></category>
		<category><![CDATA[multiplayer]]></category>
		<category><![CDATA[Netcode]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[NFE]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[Quantization]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[UDP]]></category>
		<category><![CDATA[WSS]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[양자]]></category>
		<category><![CDATA[양자화]]></category>
		<category><![CDATA[유니티]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=39970</guid>

					<description><![CDATA[<p>🔍 IInputComponentData https://docs.unity3d.com/Packages/com.unity.netcode@1.5/api/Unity.NetCode.IInputComponentData.html Netcode for Entities (NFE)에서 클라이언트의 입력을 서버로 전송하기 위해 특별히 설계된 인터페이스 IInputComponentData의 핵심 역할 1️⃣ 입력 데이터의 네트워크 동기화 2️⃣ 예측(Prediction) 시스템 지원 3️⃣ 입력 버퍼링 ✍🏻예시 코드 ❓[GhostField(Quantization = 100)] 양자화를 사용하는 이유 Netcode for Entities (NFE)에서&#160;네트워크 대역폭을 최적화하기 위해 사용되는 핵심 기능 1️⃣ 데이터 압축으로 대역폭 절감 2️⃣ 네트워크 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/unity/iinputcomponentdata-netcode-for-entities/39970/">IInputComponentData [Netcode for Entities]</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-ab9c0ba8      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#iinputcomponentdata" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50d.png" alt="🔍" class="wp-smiley" style="height: 1em; max-height: 1em;" /> IInputComponentData</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#iinputcomponentdata의-핵심-역할" class="uagb-toc-link__trigger">IInputComponentData의 핵심 역할</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-입력-데이터의-네트워크-동기화" class="uagb-toc-link__trigger">1&#x20e3; 입력 데이터의 네트워크 동기화</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-예측prediction-시스템-지원" class="uagb-toc-link__trigger">2&#x20e3; 예측(Prediction) 시스템 지원</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-입력-버퍼링" class="uagb-toc-link__trigger">3&#x20e3; 입력 버퍼링</a></li></ul><li class="uagb-toc__list"><a href="#예시-코드" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270d-1f3fb.png" alt="✍🏻" class="wp-smiley" style="height: 1em; max-height: 1em;" />예시 코드</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#ghostfieldquantization-100-양자화를-사용하는-이유" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" />[GhostField(Quantization = 100)] 양자화를 사용하는 이유</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-데이터-압축으로-대역폭-절감" class="uagb-toc-link__trigger">1&#x20e3; 데이터 압축으로 대역폭 절감</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-네트워크-지연-감소" class="uagb-toc-link__trigger">2&#x20e3; 네트워크 지연 감소</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-작동-원리" class="uagb-toc-link__trigger">3&#x20e3; 작동 원리</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-간단-적용-예시" class="uagb-toc-link__trigger">4&#x20e3; 간단 적용 예시</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-최적화-가이드" class="uagb-toc-link__trigger">5&#x20e3; 최적화 가이드</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-주의사항" class="uagb-toc-link__trigger">6&#x20e3; 주의사항</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#7-성능비교" class="uagb-toc-link__trigger">7&#x20e3; 성능비교</a></li></ul></li></ul><li class="uagb-toc__list"><a href="#iinputcomponentdata-와-icomponentdata의-차이점" class="uagb-toc-link__trigger">IInputComponentData 와 IComponentData의 차이점</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50d.png" alt="🔍" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <code>IInputComponentData</code></h2>



<p class="wp-block-paragraph"><a href="https://docs.unity3d.com/Packages/com.unity.netcode@1.5/api/Unity.NetCode.IInputComponentData.html" target="_blank" rel="noreferrer noopener">https://docs.unity3d.com/Packages/com.unity.netcode@1.5/api/Unity.NetCode.IInputComponentData.html</a></p>



<p class="wp-block-paragraph"><strong>Netcode for Entities (NFE)</strong>에서 클라이언트의 입력을 서버로 전송하기 위해 특별히 설계된 인터페이스</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong><code>IInputComponentData</code>의 핵심 역할</strong></h3>



<h4 class="wp-block-heading"><strong>1&#x20e3; 입력 데이터의 네트워크 동기화</strong></h4>



<ul class="wp-block-list">
<li><strong>클라이언트 → 서버</strong>로의 입력 전송을 최적화합니다.</li>



<li>NFE는 이 인터페이스가 붙은 컴포넌트를 자동으로 직렬화/역직렬화합니다.</li>
</ul>



<h4 class="wp-block-heading"><strong>2&#x20e3; 예측(Prediction) 시스템 지원</strong></h4>



<ul class="wp-block-list">
<li><strong>클라이언트 측 예측</strong>과&nbsp;<strong>서버 측 보정</strong>을 가능하게 합니다.</li>



<li><code>GhostPredictionSystemGroup</code>에서 특별 처리됩니다.</li>
</ul>



<h4 class="wp-block-heading"><strong>3&#x20e3; 입력 버퍼링</strong></h4>



<ul class="wp-block-list">
<li>틱(tick) 기반으로 입력을 저장해&nbsp;<strong>네트워크 지연 보정</strong>에 사용됩니다.</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270d-1f3fb.png" alt="✍🏻" class="wp-smiley" style="height: 1em; max-height: 1em;" /><strong>예시 코드</strong></h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Unity.Entities;
using Unity.NetCode;

// 반드시 IInputComponentData를 상속해야 함
public struct NetcodePlayerInput : IInputComponentData
{
    // 필드에 [GhostField] 속성을 추가해 직렬화 옵션 지정
    [GhostField(Quantization = 100)] // 값 범위: -1.0 ~ 1.0 → -100 ~ 100으로 압축
    public float MoveX; // 좌우 이동 입력 (-1: 왼쪽, 1: 오른쪽)

    [GhostField]
    public float MoveY; // 전후 이동 입력

    [GhostField]
    public InputEvent Jump; // 점프 입력 (버튼 상태)

    // 현재 틱 정보 (NFE가 자동으로 주입)
    public uint Tick { get; set; }
}</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" />[GhostField(Quantization = 100)] 양자화를 사용하는 이유</h4>



<p class="wp-block-paragraph">Netcode for Entities (NFE)에서&nbsp;<strong>네트워크 대역폭을 최적화</strong>하기 위해 사용되는 핵심 기능</p>



<h5 class="wp-block-heading"><strong><strong>1&#x20e3;</strong> 데이터 압축으로 대역폭 절감</strong></h5>



<ul class="wp-block-list">
<li><strong>원본 데이터</strong>:&nbsp;<code>float</code>&nbsp;(32비트)</li>



<li><strong>양자화 후</strong>:&nbsp;<code>short</code>&nbsp;(16비트) 등 작은 타입으로 변환</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[GhostField(Quantization = 100)] 
public float MoveX; // -1.0 ~ 1.0 → -100 ~ 100 정수로 압축
// 1.75f → 175 (전송) : 수신 측에서 1.75f로 복원</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong><strong>2&#x20e3;</strong> 네트워크 지연 감소</strong></h5>



<p class="wp-block-paragraph">작은 데이터 패킷으로 통신하여&nbsp;<strong>전송 시간 단축</strong>하여 <strong>실시간 반응성 향상</strong> (지연 시간 감소)</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong><strong>3&#x20e3;</strong> <strong><strong>작동 원리</strong></strong></strong></h5>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>원본 값</th><th>양자화 계수</th><th>송신 값</th><th>복원 값</th><th>오차</th></tr></thead><tbody><tr><td>0.73f</td><td>100</td><td>73</td><td>0.73f</td><td>0%</td></tr><tr><td>1.28f</td><td>100</td><td>128</td><td>1.28f</td><td>0%</td></tr><tr><td>0.123f</td><td>100</td><td>12</td><td>0.12f</td><td>2.4%</td></tr></tbody></table></figure>



<ul class="wp-block-list">
<li><strong>적정 범위</strong>:&nbsp;<code>-1.0 ~ 1.0</code>&nbsp;(조이스틱 입력 등)
<ul class="wp-block-list">
<li><code>Quantization = 100</code>&nbsp;→&nbsp;<code>-100 ~ +100</code>&nbsp;(총 201단계)</li>



<li><strong>필요 비트</strong>:&nbsp;<code>⌈log₂(201)⌉ = 8비트</code>&nbsp;(1바이트)</li>
</ul>
</li>



<li><strong>정밀도 vs 효율 트레이드오프</strong>:
<ul class="wp-block-list">
<li><strong>100</strong>: 0.01 단위 정밀도 (대부분의 게임 입력에 충분)</li>



<li><strong>1000</strong>: 0.001 단위 (고정밀 필요 시) → 대역폭 2배 소모</li>
</ul>
</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>4&#x20e3; <strong><strong>간단 적용 예시</strong></strong></strong></h5>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 캐릭터 이동
[GhostField(Quantization = 100)]
public float Horizontal; // 좌우 이동 (-1.0 ~ 1.0)

[GhostField(Quantization = 100)]
public float Vertical;   // 전후 이동

// 캐릭터 회전
[GhostField(Quantization = 32767)] // short.MaxValue
public float RotationY; // 0 ~ 360도 → -32767 ~ 32767
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong> </strong>5&#x20e3; 최적화 가이드</h5>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>값 범위</strong></th><th><strong>권장 양자화 계수</strong></th><th><strong>비트 사용량</strong></th></tr></thead><tbody><tr><td>-1.0 ~ 1.0</td><td>100</td><td>8비트</td></tr><tr><td>-10.0 ~ 10.0</td><td>10</td><td>8비트</td></tr><tr><td>0 ~ 360 (각도)</td><td>32767</td><td>16비트</td></tr><tr><td>고정밀 물리 파라미터</td><td>10000+</td><td>16~32비트</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>6&#x20e3; 주의사항</strong></h5>



<ul class="wp-block-list">
<li><strong>오버플로우 방지</strong><br><code>Quantization</code>&nbsp;값을 너무 크게 설정하면 정수 변환 시 오버플로우 발생 가능:</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 위험 예시 (float.MaxValue → 양자화 시 오류)
[GhostField(Quantization = 1000000)] 
public float DangerValue;</pre>



<ul class="wp-block-list">
<li><strong><strong>정밀도 한계</strong></strong><br><code>0.005f</code>&nbsp;같은 미세한 값은&nbsp;<code>Quantization=100</code>에서&nbsp;<strong>반올림 오차</strong>&nbsp;발생:</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">0.005f * 100 = 0.5f → 반올림 → 1 → 복원 시 0.01f (100% 오차)</pre>



<ul class="wp-block-list">
<li><strong><strong>에디터 디버깅</strong></strong><br><code>NetworkProtocol</code>&nbsp;로그에서 실제 전송 크기 확인:</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// Unity 콘솔에 패킷 크기 출력
Debug.Log($"패킷 크기: {System.Runtime.InteropServices.Marshal.SizeOf(packet)}바이트");</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><strong>7&#x20e3; </strong>성능비교</h5>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 양자화 미적용 (32비트 float)
[GhostField] 
public float Uncompressed; // 4바이트

// 양자화 적용 (8비트 압축)
[GhostField(Quantization = 100)] 
public float Compressed; // 1바이트

절감 효과: 75% 대역폭 감소 (4배 효율)
초당 60프레임 기준:
미적용: 60fps * 4B = 240B/s
적용: 60fps * 1B = 60B/s</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong><code>IInputComponentData</code></strong> 와 <code>IComponentData</code>의 차이점</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>특징</th><th><code>IInputComponentData</code></th><th><code>IComponentData</code></th></tr></thead><tbody><tr><td><strong>자동 직렬화</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b55.png" alt="⭕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 네트워크 전송 최적화</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 직접 구현 필요</td></tr><tr><td><strong>예측 지원</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b55.png" alt="⭕" class="wp-smiley" style="height: 1em; max-height: 1em;" />&nbsp;<code>PredictedGhost</code>와 연동</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 별도 시스템 필요</td></tr><tr><td><strong>틱 동기화</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b55.png" alt="⭕" class="wp-smiley" style="height: 1em; max-height: 1em;" />&nbsp;<code>Tick</code>&nbsp;프로퍼티 자동 관리</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 수동 처리</td></tr><tr><td><strong>버퍼링</strong></td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b55.png" alt="⭕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 입력 히스토리 유지</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 직접 구현</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://lycos7560.com/unity/iinputcomponentdata-netcode-for-entities/39970/">IInputComponentData [Netcode for Entities]</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/unity/iinputcomponentdata-netcode-for-entities/39970/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>TortoiseSVN 설치 및 접속 + 간단한 용어 설명 + Unity ignore 설정</title>
		<link>https://lycos7560.com/etc/tortoise-svn-install-unity-ignore/3199/</link>
					<comments>https://lycos7560.com/etc/tortoise-svn-install-unity-ignore/3199/#comments</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sat, 10 Dec 2022 05:25:38 +0000</pubDate>
				<category><![CDATA[기타]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[Ignore]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[Tortoise]]></category>
		<category><![CDATA[TortoiseSVN]]></category>
		<category><![CDATA[Unity]]></category>
		<category><![CDATA[version]]></category>
		<category><![CDATA[버전]]></category>
		<category><![CDATA[버전관리]]></category>
		<category><![CDATA[유니티]]></category>
		<category><![CDATA[이그노어]]></category>
		<category><![CDATA[저장소]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=3199</guid>

					<description><![CDATA[<p>Tortoise SVN 를 설치하는 과정 및 URL 접속 및 unity 협업 시 필요한 ignore 설정에 대한 글입니다. 추가적으로  SVN의 간단한 용어 설명이 있습니다.  (This article describes the process of installing Tortoise SVN and the ignore setting required for URL access and unity collaboration. Additionally, there is a brief description of SVN terminology.)</p>
<p>The post <a href="https://lycos7560.com/etc/tortoise-svn-install-unity-ignore/3199/">TortoiseSVN 설치 및 접속 + 간단한 용어 설명 + Unity ignore 설정</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5698326622209671"
     crossorigin="anonymous"></script>
<!-- HorizontalAD -->
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-5698326622209671"
     data-ad-slot="6908948342"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Tortoise SVN 설치 및 접속 + 간단한 용어 설명 + Unity</h2>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-medium-font-size wp-block-paragraph"><a href="https://tortoisesvn.net/downloads.html">https://tortoisesvn.net</a><a href="https://tortoisesvn.net/downloads.html" target="_blank" rel="noreferrer noopener">/downloads.html</a></p>



<figure class="wp-block-image size-full"><a href="https://tortoisesvn.net/downloads.html" target="_blank" rel="noreferrer noopener"><img decoding="async" width="1311" height="321" src="https://lycos7560.com/wp-content/uploads/image-593.png" alt="" class="wp-image-3200" srcset="https://lycos7560.com/wp-content/uploads/image-593.png 1311w, https://lycos7560.com/wp-content/uploads/image-593-300x73.png 300w, https://lycos7560.com/wp-content/uploads/image-593-768x188.png 768w" sizes="(max-width: 1311px) 100vw, 1311px" /></a></figure>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-medium-font-size wp-block-paragraph">사이트에서 SVN을&nbsp; 다운로드 합니다.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1343" height="552" src="https://lycos7560.com/wp-content/uploads/image-594.png" alt="" class="wp-image-3201" srcset="https://lycos7560.com/wp-content/uploads/image-594.png 1343w, https://lycos7560.com/wp-content/uploads/image-594-300x123.png 300w, https://lycos7560.com/wp-content/uploads/image-594-768x316.png 768w" sizes="(max-width: 1343px) 100vw, 1343px" /></figure>



<div style="height:37px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1296" height="997" src="https://lycos7560.com/wp-content/uploads/image-595.png" alt="" class="wp-image-3202" srcset="https://lycos7560.com/wp-content/uploads/image-595.png 1296w, https://lycos7560.com/wp-content/uploads/image-595-300x231.png 300w, https://lycos7560.com/wp-content/uploads/image-595-768x591.png 768w" sizes="(max-width: 1296px) 100vw, 1296px" /></figure>



<div style="height:57px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1436" height="731" src="https://lycos7560.com/wp-content/uploads/image-596.png" alt="" class="wp-image-3203" srcset="https://lycos7560.com/wp-content/uploads/image-596.png 1436w, https://lycos7560.com/wp-content/uploads/image-596-300x153.png 300w, https://lycos7560.com/wp-content/uploads/image-596-768x391.png 768w" sizes="(max-width: 1436px) 100vw, 1436px" /></figure>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-medium-font-size wp-block-paragraph">리포지토리(Repository) 관리자가 주는 URL로 접속</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1350" height="559" src="https://lycos7560.com/wp-content/uploads/image-597.png" alt="" class="wp-image-3204" srcset="https://lycos7560.com/wp-content/uploads/image-597.png 1350w, https://lycos7560.com/wp-content/uploads/image-597-300x124.png 300w, https://lycos7560.com/wp-content/uploads/image-597-768x318.png 768w" sizes="(max-width: 1350px) 100vw, 1350px" /></figure>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-medium-font-size wp-block-paragraph">리포지토리(Repository) 관리자가 설정한 개인 아이디와 비번으로 접속 후 업데이트를 해준다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1408" height="635" src="https://lycos7560.com/wp-content/uploads/image-598.png" alt="" class="wp-image-3205" srcset="https://lycos7560.com/wp-content/uploads/image-598.png 1408w, https://lycos7560.com/wp-content/uploads/image-598-300x135.png 300w, https://lycos7560.com/wp-content/uploads/image-598-768x346.png 768w" sizes="(max-width: 1408px) 100vw, 1408px" /></figure>



<div style="height:92px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-medium-font-size wp-block-paragraph">Unity 프로그램으로 협업을 할 경우 ignore 에서 항목을 추가하자</p>



<p class="has-medium-font-size wp-block-paragraph">&nbsp;ignore는 커밋 목록에서 제외하는 것을 의미</p>



<div style="height:48px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1400" height="499" src="https://lycos7560.com/wp-content/uploads/image-599.png" alt="" class="wp-image-3206" srcset="https://lycos7560.com/wp-content/uploads/image-599.png 1400w, https://lycos7560.com/wp-content/uploads/image-599-300x107.png 300w, https://lycos7560.com/wp-content/uploads/image-599-768x274.png 768w" sizes="(max-width: 1400px) 100vw, 1400px" /></figure>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-medium-font-size wp-block-paragraph">입력 후 저장</p>



<div style="height:37px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">*.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo __pycache__ *.rej *~ #*# .#* .*.swp .DS_Store [Tt]humbs.db obj Temp *.suo *.user *.pidb *.userprefs *.csproj *.pidb *.sln *.csproj *.unityproj Library Temp Logs UserSettings .vsconfig .vs obj *.log </pre>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5698326622209671"
     crossorigin="anonymous"></script>
<ins class="adsbygoogle"
     style="display:block"
     data-ad-format="autorelaxed"
     data-ad-client="ca-pub-5698326622209671"
     data-ad-slot="4245812909"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<div style="height:47px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><strong>간단한 용어정리</strong></h2>



<p class="has-medium-font-size wp-block-paragraph"><a href="https://fall-in-it.tistory.com/10" target="_blank" rel="noreferrer noopener">https://fall-in-it.tistory.com/10</a>  &lt;- 참고</p>



<div style="height:77px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>SVN&nbsp;(SubVersion)&nbsp;</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">형상관리/소스관리 툴</p>



<p class="has-medium-font-size wp-block-paragraph">프로젝트의 버전관리를 위해 Repository를 만들고 안에 소스를 저장하여 운용하는 소프트웨어입니다.</p>



<p class="has-medium-font-size wp-block-paragraph">버전 관리 기능을 제공하여 프로젝트를 여러 사람이 함께 참여할때 프로젝트 소스코드의 공유,&nbsp;<br>옛날 버전의 소스 코드 조회 소스 코드의 백업의 용도로 사용할수있다</p>



<p class="has-medium-font-size wp-block-paragraph">&#8211; 체크아웃 : 저장소에 접근해 소스 코드와 버전관리를 위한 파일들을 받아 오는 행위</p>



<p class="has-medium-font-size wp-block-paragraph">&#8211; 추가 : 말 그대로 파일을 서버에 올린다</p>



<p class="has-medium-font-size wp-block-paragraph">&#8211; 커밋 : 내가 수정한 코드를 올린다</p>



<p class="has-medium-font-size wp-block-paragraph">&#8211; 업데이트 : 최신 리버전된 버전의 코드를 받는다</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Repository : 저장소</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">SVN 에서 저장소는 보통 Remote Repository(원격 저장소)를 의미한다.</p>



<p class="has-medium-font-size wp-block-paragraph">즉, 서버에 올라가 있는 저장소로 소스뿐만이 아니라 소스의 변경 사항도 모두 저장되며</p>



<p class="has-medium-font-size wp-block-paragraph">URL 이 있는 모든 사용자가 네트워크를 통해서 접근 할 수 있습니다.&nbsp;</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>클라이언트, client</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">네트워크로 연결된 서버로부터 정보를 제공받는 컴퓨터.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Revision : 리비전</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">저장소에 올라가 있는 파일들의 버전이라고 표현 할 수 있다.</p>



<p class="has-medium-font-size wp-block-paragraph">Revision 의 숫자는 소스 및 파일&#8230;등 을 수정하여 commit 하면 일정한 규칙에 의해 숫자가 증가합니다.</p>



<p class="has-medium-font-size wp-block-paragraph">이 Revision&nbsp;을 통해 롤백을 하거나 이전에 소스코드를 확인할 수 있다.</p>



<p class="has-medium-font-size wp-block-paragraph">위와 같은 이유로 commit 시 어떤 변경사항들이 발생했는지 Commit Message 를 잘 작성해야 한다.</p>



<p class="has-medium-font-size wp-block-paragraph">Revision 을 보고 프로젝트 진행 상황을 알 수 있습니다.</p>



<p class="has-medium-font-size wp-block-paragraph">Subversion의 경우 파일별로&nbsp;Revision 이 매겨지지 않고 한번&nbsp;commit 한 것으로 전체&nbsp;Revision&nbsp;이 매겨집니다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Trunk : 트렁크</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">해당 저장소의 중심이 된다는 의미이다.</p>



<p class="has-medium-font-size wp-block-paragraph">아래의 그림을 참고하자.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Branch : 브랜치</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">브랜치는 트렁크와 마찬가지로 영어 단어의 뜻적으로 접근하면 나뭇가지를 뜻한다.</p>



<p class="has-medium-font-size wp-block-paragraph">나뭇가지는 나무의 몸통에서부터 파생되어 나온다.</p>



<p class="has-medium-font-size wp-block-paragraph">* 그렇다면 트렁크는 중심에서 전체적으로 에러가 없이 잘 돌아가는 소스코드이고</p>



<p class="has-medium-font-size wp-block-paragraph">&nbsp; &nbsp;브랜치는 그 트렁크에서 기능 추가, 수정을 하기 위해 가지로 뻗어 나와 있는 기둥에 영향을 안 주는 소스코드이다.</p>



<p class="has-medium-font-size wp-block-paragraph">&nbsp; &nbsp;나뭇가지를 자른다고(실제로는 그런 행동을 하면 안 됩니다!) 기둥이 죽지는 않는다.</p>



<div style="height:42px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1145" height="626" src="https://lycos7560.com/wp-content/uploads/image-600.png" alt="" class="wp-image-3207" srcset="https://lycos7560.com/wp-content/uploads/image-600.png 1145w, https://lycos7560.com/wp-content/uploads/image-600-300x164.png 300w, https://lycos7560.com/wp-content/uploads/image-600-768x420.png 768w" sizes="(max-width: 1145px) 100vw, 1145px" /></figure>



<div style="height:58px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-medium-font-size wp-block-paragraph">그림으로 표현하면 위와 같다.</p>



<p class="has-medium-font-size wp-block-paragraph">그래서 보통 개발할 때는 기반이 되는 Trunk 소스코드에서 기능 별로 혹은 메뉴 별로 혹은 카테고리 별로</p>



<p class="has-medium-font-size wp-block-paragraph">(이건 프로젝트 내부 룰에 따라)</p>



<p class="has-medium-font-size wp-block-paragraph">Branch 만들어서 개발하고&nbsp;<strong>나중에 Trunk 에 합치는 식으로 개발을 진행</strong>한다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Tag : 태그</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">태그는&nbsp;<strong><u>여러 브랜치들이 모여서 만들어진 트렁크에 대해 어느 특정 지점을 기록하기 위한 꼬리표</u></strong>라고 생각하면 된다.</p>



<p class="has-medium-font-size wp-block-paragraph">주로 상용 서비스에서는 Release 단위에 따라 태그를 붙이는 편이다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Checkout : 체크아웃</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">체크아웃은 초기에 원격 저장소에서 작업을 하기 위해 소스코드를<strong>&nbsp;내 PC 로 내려받는 것을 의미</strong>한다.</p>



<p class="has-medium-font-size wp-block-paragraph">(Git 의 Clone 과 동일함)</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Import : 임포트</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">임포트는 초기에 원격 저장소에&nbsp;<strong>버전 관리할 소스 코드를 넣는 것을 의미</strong>한다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Export : 익스포트</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">익스포트는 임포트와 반대로 원격 저장소에서 소스 코드를 가져오는 것을 의미한다.</p>



<p class="has-medium-font-size wp-block-paragraph"><strong>체크아웃과 다른 점은 .svn 숨김 폴더는 제외한다는 것</strong>이다.</p>



<p class="has-medium-font-size wp-block-paragraph">즉, 버전 관리 파일을 제외한 순수 소스 코드만 가져오는 것이다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Commit : 커밋</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">커밋은&nbsp;<strong><u>원격 저장소에 변경된 변경 사항들을 저장</u></strong>하는 것을 의미한다.</p>



<p class="has-medium-font-size wp-block-paragraph">내 PC에서 아무리 열심히 해도 커밋을 안 한다면 아무 소용이 없다.</p>



<p class="has-medium-font-size wp-block-paragraph"><strong>커밋을 하게 되면 저자(Author)와 커밋 메시지, 일자와 일시가 찍히고 리비전이 갱신</strong>된다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Update : 업데이트</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">업데이트는&nbsp;<strong>원</strong><strong>격 저장소에서 다른 사람에 의해 변경된 소스 코드를 내 PC 에 반영하는 작업</strong>이다.</p>



<p class="has-medium-font-size wp-block-paragraph">업데이트는 수시로 해주는 게 좋다.&nbsp;</p>



<p class="has-medium-font-size wp-block-paragraph"><strong>작업을 시작하기 전에, 커밋을 하기 전에는 필수</strong>로 해준다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Revert : 리버트, 롤백</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">리버트는&nbsp;<strong>내 PC 에서 수정했던 작업 내역을 되돌리는 작업</strong>이다.</p>



<p class="has-medium-font-size wp-block-paragraph">파일 단위로 할 수도 있고 폴더 단위로 할 수도 있다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Merge : 머지, 병합</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">머지는 내 브랜치와 다른 사람이 작업해 둔 브랜치를 합치는 작업이다.</p>



<p class="has-medium-font-size wp-block-paragraph">업데이트 시에는 자동적으로 진행되는 작업이다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Conflict : 컨플릭트, 충돌</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">가장 까다롭고 귀찮은 녀석이다..</p>



<p class="has-medium-font-size wp-block-paragraph">일단 충돌이 나는 이유는 동일한 파일을 서로 다른 프로그래머가 개발할 경우 자주 발생한다.</p>



<p class="has-medium-font-size wp-block-paragraph">충돌이 발생하면 하나하나 비교해가면서 병합을 해야 한다.. (손머지..)</p>



<p class="has-medium-font-size wp-block-paragraph">사람이 하는 것이기에 휴먼에러가 발생할 가능성이 있고 그렇게 되면 개발됐던 소스 코드들을 날려버릴 수가 있다&#8230;</p>



<p class="has-medium-font-size wp-block-paragraph">미연에 방지하기 위해서는 원격 저장소와 동일한 파일 상태에서 업데이틀 무조건 해주는 것이 정신건강에 이롭다..</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Lock : 락</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">그래서 있는 것이 이 Lock(락) 이라는 것이다.</p>



<p class="has-medium-font-size wp-block-paragraph"><strong>이 명령어를 파일에 걸어두면 락을 건 사용자만 해당 파일을 수정할 수 있다.</strong></p>



<p class="has-medium-font-size wp-block-paragraph">사용한 후에는 반드시 unlock 을 해주는 것을 잊지 말자.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Diff : 디프</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">Difference 의 약자이다.</p>



<p class="has-medium-font-size wp-block-paragraph"><strong>현재 소스 코드 대비 특정 리비전의 소스 코드와 차이점</strong>을 볼 수 있다.</p>



<p class="has-medium-font-size wp-block-paragraph">특정 리비전 2개의 차이점을 볼 수도 있다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Add : 추가</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">원격 저장소에 커밋하기 위해 VCS 목록에 추가하는 것을 의미한다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Ignore : 이그노어</strong></h3>



<p class="has-medium-font-size wp-block-paragraph">Add 와는 반대로 커밋 목록에서 제외하는 것을 의미한다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5698326622209671"
     crossorigin="anonymous"></script>
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-5698326622209671"
     data-ad-slot="5078714126"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>
<p>The post <a href="https://lycos7560.com/etc/tortoise-svn-install-unity-ignore/3199/">TortoiseSVN 설치 및 접속 + 간단한 용어 설명 + Unity ignore 설정</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/etc/tortoise-svn-install-unity-ignore/3199/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
