<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>pure_bver.log</title>
        <link>https://velog.io/</link>
        <description>개발 하고 싶은 비버</description>
        <lastBuildDate>Mon, 20 Jan 2025 07:21:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. pure_bver.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/pure_bver" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[GCC(GNU Compiler Collection) 설치]]></title>
            <link>https://velog.io/@pure_bver/GCCGNU-Compiler-Collection-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@pure_bver/GCCGNU-Compiler-Collection-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Mon, 20 Jan 2025 07:21:21 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>MSYS2사이트 진입</p>
</li>
<li><p>msys-x86_64.exe 클릭
<img src="https://velog.velcdn.com/images/pure_bver/post/4593976b-fa4d-49ef-8397-0fb3f2545ccf/image.png" alt=""></p>
</li>
<li><p>실행 후 설치
<img src="https://velog.velcdn.com/images/pure_bver/post/c1ded43e-a41b-4a26-863d-72f1dc0a8c31/image.png" alt=""></p>
</li>
<li><p>설치된 msys2실행
<img src="https://velog.velcdn.com/images/pure_bver/post/0133d2a2-69c5-4d1c-89e5-fb1a65a6ed62/image.png" alt=""></p>
</li>
<li><p>MSYS2사이트에 적혀있는대로 실행
=&gt; msys2에서 아래 명령어 실행</p>
<pre><code>pacman -S mingw-w64-ucrt-x86_64-gcc</code></pre></li>
<li><p>설치확인
=&gt; msys2에서 아래 명령어 실행</p>
<pre><code>gcc --version
gcc.exe (Rev2, Built by MSYS2 project) 13.2.0 //예시</code></pre></li>
<li><p>환경설정에 추가
=&gt; 시스템 - 고급 시스템 설정 - 고급 - 환경변수
=&gt; 또는 시스템 환경 변수 편집 검색하여 클릭 - 고급 - 환경변수
<img src="https://velog.velcdn.com/images/pure_bver/post/da32af20-4df5-411b-aad2-cb911a8e5b32/image.png" alt=""></p>
</li>
<li><p>Path에 경로 추가
8-1. 경로 복사
=&gt; msys2가 설치된 위치\ucrt64\bin
8-2. 경로 붙여넣기
=&gt; 변수 Path클릭 - 편집 - 새로 만들기 - 경로 붙여넣기</p>
</li>
<li><p>최종 확인
=&gt; 터미널을 열어서 6번 설치명령과 같은 내용이 나오는지 확인</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[MSA(Micro Services Architecture)]]></title>
            <link>https://velog.io/@pure_bver/MSAMicro-Services-Architecture</link>
            <guid>https://velog.io/@pure_bver/MSAMicro-Services-Architecture</guid>
            <pubDate>Thu, 19 Dec 2024 20:14:11 GMT</pubDate>
            <description><![CDATA[<h3 id="정의">정의:</h3>
<ul>
<li>각 기능별로 독립적인 서비스로 분리하여 개발하고 운영하는 분산 서버 아키텍처</li>
</ul>
<h3 id="특징">특징:</h3>
<ul>
<li><p>독립적인 서비스:</p>
<ul>
<li>각 서비스는 특정 기능을 담당하며 독립적으로 개발되고 배포됨</li>
<li>서비스 간에는 최소한의 의존성을 유지합니다</li>
</ul>
</li>
<li><p>서비스 단위 확장</p>
<ul>
<li>특정 서비스만 별도로 확장할 수 있음</li>
<li>한 시스템을 위해 전체 시스템을 확장할 필요가 없음</li>
</ul>
</li>
<li><p>다양한 기술 스택</p>
<ul>
<li>각 서비스 별로 서비스마다 최적화된 언어, 프레임워크를 선택하여 구현 가능</li>
</ul>
</li>
<li><p>SPOF(단일지점장애) 회피</p>
<ul>
<li>Gateway에 로드밸런서, 헬스체크 시스템을 도입</li>
<li>Distributor에 메시지 브로커 클러스터링<ul>
<li>RabbitMQ, Kafka, pub/sub등</li>
</ul>
</li>
<li>위 두개 지점만 해결해줄경우 SPOF 회피</li>
</ul>
</li>
</ul>
<h3 id="단점">단점:</h3>
<ul>
<li><p>복잡성 증가:</p>
<ul>
<li>분산 시스템 관리, 서비스 간 통신, 데이터 일관성 관리 등이 필요</li>
</ul>
</li>
<li><p>네트워크 지연:</p>
<ul>
<li>서비스 간 API 호출로 인해 네트워크 지연 문제가 발생</li>
</ul>
</li>
<li><p>운영 비용 증가:</p>
<ul>
<li>서비스 모니터링, 로깅, 배포 자동화 등</li>
<li>운영비용을 줄이는 대신 복잡성을 더욱더 증가 시킬 수 있음</li>
</ul>
</li>
</ul>
<hr>
<h2 id="프로젝트에서-진행해본-msa기반-서버-아키텍쳐">프로젝트에서 진행해본 MSA기반 서버 아키텍쳐</h2>
<p><img src="https://velog.velcdn.com/images/pure_bver/post/fbdc7bc8-7a0f-4941-9bb6-01f4c2b7a704/image.png" alt=""></p>
<h3 id="서버">서버</h3>
<ul>
<li>Dedicated: 게임기능을 담당</li>
<li>G_Master: 데디 서버를 생성 또는 참가시키는 ㅓ버</li>
<li>Session: 세션관리 서버</li>
<li>Lobby: 게임 진입 전 로비 서버</li>
<li>Account: 회원 관리를 담당하는 서버</li>
<li>Distributor: 서버간의 통신을 위한 서버</li>
<li>Gateway: 클라이언트와 서버간의 통신을 위한 서버</li>
</ul>
<h3 id="문제점">문제점</h3>
<ul>
<li>복잡한 구조로 인해 기초 다지는데 고민되는 시간이 오래 걸렸음</li>
<li>전체적인 구조가 어느정도 완성이 될 때 까지 어디서 어떤 문제가 일어난건지 확인이 힘들었음</li>
<li>기초가 부실하게 잡힌 상태에서 서비스만 확장될 경우 유지보수가 매우 힘들어짐</li>
<li>현 시점에서 SPOF 회피를 위한 로드밸런서나 헬스체크 시스템을 도입하지 못해 게이트웨이에 문제가 발생 할 경우 모든 기능이 정지됨</li>
<li>데디케이트 서버를 따로 생성시키면 모를까 데디케이트서버도 게이트웨이와 연결시켜 게이트웨이 부담 증가, 레이턴시 증가</li>
</ul>
<h3 id="좋은점">좋은점</h3>
<ul>
<li>다양한 기술에 대한 이해<ul>
<li>MSA 서버를 만들기 위해 여러 기술들(pub/sub, Turborepo, Esbuild 등)이 사용됨</li>
</ul>
</li>
<li>분산 서버에 대한 이해</li>
</ul>
<h3 id="결론">결론</h3>
<ul>
<li><p>약 6주라는 짧은 시간안에 구현하려 하다보니 장점보다는 단점이 부각되어 괜히 MSA를 한다고했다 했지만 분산서버에 대한 이해와 다양한 기술에 대해 사용 해봤다는 점에서 좋은 경험이었다고 생각</p>
</li>
<li><p>합칠때부터 반대했지만... 데디케이트 서버 만큼은 합치면 안됐다고 생각합니다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Bull Queue와 Pub/Sub]]></title>
            <link>https://velog.io/@pure_bver/Bull-Queue%EC%99%80-PubSub</link>
            <guid>https://velog.io/@pure_bver/Bull-Queue%EC%99%80-PubSub</guid>
            <pubDate>Thu, 12 Dec 2024 16:53:01 GMT</pubDate>
            <description><![CDATA[<h3 id="불큐-bull-queue">불큐 (Bull Queue)</h3>
<h4 id="어디에-쓸까">어디에 쓸까?</h4>
<ul>
<li><code>백그라운드</code> 작업 처리 및 <code>비동기</code> 작업 관리를 위해 사용</li>
</ul>
<h4 id="사용-이유">사용 이유</h4>
<ol>
<li><p>서버의 주요 작업 흐름을 차단하지 않고, 백그라운드에서 처리</p>
</li>
<li><p>실패한 작업을 자동으로 재시도하는 기능이 내장</p>
</li>
<li><p>특정 시간 이후나 정해진 시간대에 작업을 실행할 수 있는 예약 기능</p>
</li>
<li><p>작업 병렬 처리가 가능</p>
</li>
<li><p>작업 대기, 진행 중, 완료, 실패 등 작업 상태를 추적</p>
</li>
</ol>
<h4 id="문제점">문제점</h4>
<ol>
<li>Redis에 의존하므로 Redis가 다운되거나 느려지면 작업 처리가 중단될 수 있음</li>
</ol>
<hr>
<h3 id="펍섭-pubsub">펍섭 (Pub/Sub)</h3>
<h4 id="어디에-쓸까-1">어디에 쓸까?</h4>
<ul>
<li><p>실시간 이벤트 전송</p>
</li>
<li><p>실시간 알림 시스템, 채팅 애플리케이션, 로그 스트리밍 등 이벤트 중심의 애플리케이션 등</p>
<h4 id="사용-이유-1">사용 이유</h4>
<ol>
<li><p>발행자(publisher)와 구독자(subscriber)가 서로 독립적으로 동작하므로, 시스템의 모듈화가 용이</p>
</li>
<li><p>다수의 구독자가 하나의 발행자로부터 데이터를 동시에 받을 수 있어 확장성이 좋음</p>
</li>
<li><p>구독자는 발행된 이벤트를 실시간으로 수신하므로, 비동기적 이벤트 관리에 적합</p>
</li>
</ol>
</li>
</ul>
<h4 id="문제점-1">문제점</h4>
<ol>
<li><p>구독자가 비활성 상태일 경우 메시지 손실 가능성 있음</p>
</li>
<li><p>Pub/Sub은 메시지의 상태를 저장하지 않으므로 작업 추적이나 재처리에 부적합</p>
</li>
<li><p>구독자 수가 많아지면 성능이 저하</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[EC2 내에 Docker띄우기]]></title>
            <link>https://velog.io/@pure_bver/EC2-%EB%82%B4%EC%97%90-Docker%EB%9D%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@pure_bver/EC2-%EB%82%B4%EC%97%90-Docker%EB%9D%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Thu, 12 Dec 2024 15:26:48 GMT</pubDate>
            <description><![CDATA[<p>ecs를 활용하여 데디서버를 생성해보기 위해 테스트를 진행하였고
테스트 결과 ecs를 이용해 인스턴스가 생성되는 것을 확인하였고
1인스턴스 = 1게임서버 형태로 서버가 만들어지는것을 확인하였다.</p>
<p>팀 내에 회의 결과
ecs를 배운다는 입장에서 활용해 보는 것도 좋을 거 같긴 하지만 시간 관계상 그리고 <code>비용 문제</code>로 테스트를 성공한 정도로 만족하고 ec2 인스턴스 내에서 도커를 띄우는 방향으로 가게되었다.</p>
<hr>
<h3 id="ec2-docker-추가하기">EC2 Docker 추가하기</h3>
<ol>
<li>Node.js 설치(인스턴스)
curl -fsSL <a href="https://deb.nodesource.com/setup_18.x">https://deb.nodesource.com/setup_18.x</a> | sudo -E bash -
sudo apt-get install -y nodejs</li>
</ol>
<blockquote>
<p>yarn을 사용하였기에 yarn 설치</p>
</blockquote>
<ol>
<li><p>Yarn GPG 키 추가
curl -sS <a href="https://dl.yarnpkg.com/debian/pubkey.gpg">https://dl.yarnpkg.com/debian/pubkey.gpg</a> | sudo apt-key add -</p>
</li>
<li><p>Yarn 공식 리포지토리 추가
echo &quot;deb <a href="https://dl.yarnpkg.com/debian/">https://dl.yarnpkg.com/debian/</a> stable main&quot; | sudo tee /etc/apt/sources.list.d/yarn.list</p>
</li>
<li><p>패키지 리스트 업데이트
sudo apt update</p>
</li>
<li><p>Yarn 설치
sudo apt install yarn</p>
</li>
<li><p>Docker 설치(인스턴스)
sudo apt update
sudo apt install -y docker.io
sudo systemctl start docker
sudo usermod -aG docker $USER</p>
</li>
<li><p>Docker로 빌드한 이미지를 .tar파일로 저장(Docker)
docker save -o dedicated-server.tar dedicated-server</p>
</li>
<li><p>EC2에 tar파일로 저장(배쉬)
scp -i /key.pem /dedicated-server.tar ubuntu@<server>:/home/ubuntu/</p>
</li>
<li><p>tar파일 EC2내 Docker에 이미지로 저장(인스턴스)
docker load -i dedicated-server.tar</p>
</li>
<li><p>EC2내 Docker 이미지 확인
docker images</p>
</li>
</ol>
<hr>
<h3 id="코드">코드</h3>
<ol>
<li>Docker 실행하기</li>
</ol>
<pre><code class="language-js">  import { spawn } from &#39;child_process&#39;;

    const runProcess = spawn(&#39;docker&#39;, [
      &#39;run&#39;,
      &#39;-d&#39;,
      &#39;--rm&#39;,
      &#39;--name&#39;,
      containerName,
      &#39;-e&#39;,
      `INVITE_CODE=${inviteCode}`,
      &#39;dedicated-server&#39;, //띄울 Docker 이미지 명
    ]);</code></pre>
<ol start="2">
<li><p>띄운 Docker 서버에서 포트번호 송신</p>
<pre><code class="language-js">tcpServer.listen(3000, config.server.tcpHost, () =&gt; {
   console.log(`새로운 게임이 포트 ${tcpServer.address().port}에 생성`);
   console.log(tcpServer.address());

   const roomServer = net.connect({ host: &#39;172.17.0.1&#39;, port: 6666 }, () =&gt; {
     console.log(&#39;발송&#39;);
     roomServer.write(
       JSON.stringify(`${inviteCode}:${tcpServer.address().port}`),
     );
   });
 });</code></pre>
</li>
</ol>
<ul>
<li>EC2 인스턴스 내 Docker를 실행한 호스트 서버의 ip는 172.17.0.1로 기본 브릿지 설정이 되어있음</li>
<li>윈도우 에서는 host.docker.internal로 사용</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[레디스]]></title>
            <link>https://velog.io/@pure_bver/%EB%A0%88%EB%94%94%EC%8A%A4</link>
            <guid>https://velog.io/@pure_bver/%EB%A0%88%EB%94%94%EC%8A%A4</guid>
            <pubDate>Wed, 11 Dec 2024 15:12:05 GMT</pubDate>
            <description><![CDATA[<h3 id="레디스">레디스</h3>
<h4 id="어디에-쓸까">어디에 쓸까?</h4>
<ol>
<li>데이터 캐싱</li>
</ol>
<ul>
<li>속도</li>
</ul>
<blockquote>
<p>캐싱: 자주 사용되거나 요청되는 데이터를 임시로 저장하여, 데이터 접근 속도를 높이고 시스템의 전반적인 성능을 향상시키는 기술</p>
</blockquote>
<ol start="2">
<li>세션</li>
</ol>
<ul>
<li>세션 데이터를 메모리에 저장하면 빠른 액세스가 가능하며, TTL(Time-To-Live) 설정으로 자동 만료가 가능</li>
</ul>
<ol start="3">
<li>실시간 애플리케이션</li>
</ol>
<ul>
<li>낮은 레이턴시와 높은 처리량을 제공하여 실시간 처리가 필요한 애플리케이션에 적합</li>
</ul>
<ol start="4">
<li>lock</li>
</ol>
<ul>
<li>SETNX(Set if Not Exists) 명령과 만료 시간을 활용하여 분산 환경에서 락을 관리할 수 있습니다.</li>
</ul>
<ol start="5">
<li>작업 큐 및 비동기 처리</li>
</ol>
<ul>
<li>리스트 자료구조(List)를 사용하여 작업 큐를 만들고, 작업의 순차 처리를 쉽게 구현</li>
<li>bullmq라는 라이브러리도 있음</li>
</ul>
<ol start="6">
<li>그 외</li>
</ol>
<ul>
<li>Sorted Set을 이용한 데이터분석</li>
<li>GEO명령어를 통한 좌표저장 및 계산</li>
</ul>
<h4 id="사용-이유">사용 이유</h4>
<ol>
<li>속도</li>
</ol>
<ul>
<li>메모리 기반 데이터 저장소로, 데이터 접근 속도가 빠름</li>
</ul>
<ol start="2">
<li>다양한 데이터 구조</li>
</ol>
<ul>
<li>key-value 외에도 list, hset, set, sorted set, bitmap, hyperloglog 등 다양한 자료형 지원</li>
</ul>
<ol start="3">
<li>지속성</li>
</ol>
<ul>
<li>메모리 기반이지만 레디스 자체에서 스냅샷과 AOF(Append-Only File) 방식을 통해 데이터 영속성을 유지<blockquote>
<p>스냅샷: 특정 시점 전체를 저장
AOF: 데이터 변경 사항(쓰기, 삭제 등)을 명령어 단위로 순차적으로 로그에 기록</p>
</blockquote>
</li>
</ul>
<ol start="4">
<li>쉽고 간편</li>
</ol>
<ul>
<li>간단한 명령어로 데이터를 처리할 수 있어 배우고 사용하기 쉬움</li>
</ul>
<ol start="5">
<li>레디스 옵션? 기능들</li>
</ol>
<ul>
<li>pub/sub, bullMQ 등 여러 기능</li>
</ul>
<h4 id="문제점">문제점</h4>
<ol>
<li><p>속도가 빠른 메모리기반이라 용량이 부족 
=&gt; 돈으로 해결가능</p>
</li>
<li><p>복잡한 데이터 처리의 어려움</p>
</li>
</ol>
<ul>
<li>Redis는 기본적으로 키-값 데이터 모델을 따르며, 관계형 데이터베이스(RDBMS)와 같은 스키마 기반 또는 복잡한 데이터 관계를 다루기 힘듬</li>
<li>복잡한 데이터는 관계형 데이터베이스를 사용하자</li>
</ul>
<ol start="3">
<li>영속성</li>
</ol>
<ul>
<li>스냅샷은 실시간 데이터를 저장하는 방식이 아니기에 장애 발생 시 최신 데이터를 잃을 위험이 있음</li>
<li>AOF는 명령어 실행 시마다 디스크에 기록이 되므로 성능에 영향을 줌</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.12.10 AWS ECS]]></title>
            <link>https://velog.io/@pure_bver/2024.12.10-AWS-ECS</link>
            <guid>https://velog.io/@pure_bver/2024.12.10-AWS-ECS</guid>
            <pubDate>Tue, 10 Dec 2024 08:58:27 GMT</pubDate>
            <description><![CDATA[<h2 id="클러스터-생성">클러스터 생성</h2>
<p><img src="https://velog.velcdn.com/images/pure_bver/post/13610b25-691e-4a1d-82b8-8761fc914ec5/image.png" alt=""></p>
<ol>
<li>AWS Fargate(서버리스)</li>
</ol>
<ul>
<li>인프라 관리를 해주는 aws 서비스</li>
<li>대신 돈이듬</li>
</ul>
<ol start="2">
<li>Amazon EC2 인스턴스</li>
</ol>
<ul>
<li>직접 관리를 해줘야함</li>
<li>대신 조금 더 쌈</li>
</ul>
<ol start="3">
<li>Auto Scaling</li>
</ol>
<ul>
<li><p>트래픽 변화에 따라 EC2 인스턴스를 자동으로 추가하거나 제거</p>
</li>
<li><p>이하 내용들은 오토 스케일링 기능으로 생성될 인스턴스를 선택하는 작업</p>
<ul>
<li><p>프로 비저닝</p>
<ul>
<li>온디맨드: 시간당 고정 요금, 특이성 없음 &lt;= 선택</li>
<li>스팟: 시간당 온디맨드에 비해 저렴, 사용자 의지와 상관없이 <strong>중단될 가능성 있음</strong></li>
</ul>
</li>
<li><p>컨테이너 인스턴스 AMI (Amazon Machine Image)
  =&gt; 컨테이너를 실행할 EC2 인스턴스의 운영체제와 기본 설정이 포함된 이미지</p>
</li>
<li><p>EC2 인스턴스 유형
  =&gt; EC2 인스턴스의 하드웨어 사양</p>
</li>
<li><p>원하는 용량
  =&gt; ECS 클러스터에 실행할 EC2 인스턴스의 최소 및 최대 개수를 설정</p>
<ul>
<li>최소: 클러스터에서 항상 실행해야 하는 EC2 인스턴스의 개수</li>
<li>최대: 필요할 때 Auto Scaling을 통해 확장할 수 있는 최대 인스턴스 개수</li>
</ul>
</li>
<li><p>SSH 키 페어
  =&gt; EC2 인스턴스에 SSH로 접속할 때 사용하는 인증 키</p>
</li>
<li><p>루트 EBS 볼륨 크기
  =&gt; EC2 인스턴스의 루트 볼륨(운영체제 설치 공간) 크기를 설정</p>
</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/pure_bver/post/6c7be8fd-da25-4108-9ca1-6ba6ffa95e4e/image.png" alt=""></p>
<ol>
<li>항상성 관찰성을 갖춘 Container Insights</li>
</ol>
<ul>
<li>컨테이너 수준에서 CPU, 메모리, 네트워크 성능 데이터를 지속적으로 수집</li>
<li>수집되는 데이터양에 따라 추가 비용</li>
</ul>
<ol start="2">
<li>Container Insights</li>
</ol>
<ul>
<li>클러스터 수준에서 데이터를 수집</li>
<li>컨테이너 작업(Task) 수준의 세부 데이터는 제공하지 않음</li>
<li>수집되는 데이터양에 따라 추가 비용</li>
</ul>
<ol start="3">
<li>끄기</li>
</ol>
<ul>
<li>CloudWatch Container Insights를 사용하지 않음.</li>
<li>추가비용 없음</li>
</ul>
<hr>
<h2 id="작업-정의task-definition-생성">작업 정의(Task Definition) 생성</h2>
<ul>
<li>ECS에서 실행할 컨테이너를 정의
<img src="https://velog.velcdn.com/images/pure_bver/post/d5e87cf2-24db-4c4f-a852-d20b0d82af95/image.png" alt=""></li>
</ul>
<ul>
<li><p>운영 체제/아키텍처: 컨테이너를 실행할 EC2 인스턴스의 운영체제와 아키텍처를 선택</p>
<pre><code>- 리눅스 운영체제와 x86_64 아키텍처(일반적인 Intel/AMD 기반 서버)를 사용</code></pre></li>
<li><p>네트워크 모드</p>
<pre><code>- awsvpc: 각 작업(Task)이 고유한 네트워크 인터페이스를 가져서 독립된 IP 주소를 할당</code></pre><p> =&gt; 보안특화</p>
<ul>
<li>bridge: 여러 컨테이너가 같은 네트워크 인터페이스를 공유
=&gt; 멀티 컨테이너</li>
<li>host: 컨테이너가 EC2 인스턴스의 네트워크 인터페이스를 공유
=&gt; 단일 컨테이너</li>
</ul>
</li>
<li><p>CPU: 컨테이너가 사용할 CPU 용량(코어사용량)</p>
</li>
<li><p>메모리: 컨테이너가 사용할 메모리 용량</p>
<blockquote>
<p>태스크 크기는 컨테이너가 실행될 <strong>EC2 인스턴스</strong>의 자원과 <strong>일치</strong>해야함
리소스 요구사항이 너무 크거나 작으면 EC2 인스턴스에 배치되지 않을 수 있음</p>
</blockquote>
</li>
<li><p>태스크 역할</p>
<ul>
<li>작업(Task) 내 컨테이너가 AWS 서비스(API, 리소스)에 접근할 때 사용하는 IAM 역할</li>
<li>ex: S3 버킷에 접근하거나 DynamoDB 데이터를 조회할 때 사용</li>
<li>IAM 콘솔에서 생성한 역할을 선택하거나 새 역할 생성 옵션을 사용</li>
</ul>
<ul>
<li>태스크 실행 역할<ul>
<li>작업(Task)을 실행하는 AWS ECS 자체가 사용하는 IAM 역할<ul>
<li>ECS가 EC2 인스턴스에서 작업을 실행하고, 로그를 수집하며, ECR에서 이미지를 가져오는 데 사용</li>
<li>기본적으로 ECS 서비스가 실행에 필요한 권한을 AWS에서 자동으로 생성
<img src="https://velog.velcdn.com/images/pure_bver/post/2e99b5c5-2470-44cb-934b-2c078b751532/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>이름: 컨테이너의 고유한 이름을 지정</p>
</li>
<li><p>이미지 URI: 컨테이너에서 실행할 Docker 이미지의 URI</p>
<ul>
<li>ex: Docker Hub 이미지: nginx:latest</li>
<li>ex: AWS ECR 이미지: 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest</li>
</ul>
</li>
<li><p>프라이빗 레지스트리: Docker Hub 또는 AWS ECR 외의 프라이빗 레지스트리에서 이미지를 가져오는 경우, 인증 정보가 필요</p>
</li>
<li><p>포트 매핑: 컨테이너 내부의 포트를 <strong>호스트(EC2 인스턴스)</strong>나 awsvpc 네트워크 모드로 노출</p>
<ul>
<li>컨테이너 포트와 호스트 포트 설정 작업</li>
<li>포트 이름은 네트워크 구성이나 로드 밸런서와 통합 시 유용</li>
</ul>
</li>
<li><p>읽기 전용 루트 파일 시스템: 컨테이너의 루트 파일 시스템을 읽기 전용으로 설정할지 여부</p>
</li>
<li><p>리소스 할당 제한 - 조건부: 컨테이너가 사용할 CPU, GPU, 메모리의 양을 정의
=&gt; EC2 cpu용량을 초과할 수 없음 , GPU를 할당하려면 EC2 GPU 인스턴스가 필요</p>
</li>
</ul>
<hr>
<h2 id="서비스-생성">서비스 생성</h2>
<p><img src="https://velog.velcdn.com/images/pure_bver/post/cae64019-19af-400b-9535-ab10d9a8a500/image.png" alt=""></p>
<ol>
<li>컴퓨팅 옵션</li>
</ol>
<ul>
<li>용량 공급자 전략 &lt;= 선택<ul>
<li>작업(Task)을 실행할 리소스의 우선순위와 비율을 설정하는 전략</li>
</ul>
</li>
<li>시작 유형<ul>
<li>ECS 서비스가 실행되는 방식을 직접 정의</li>
</ul>
</li>
</ul>
<ol start="2">
<li>용량 공급자 전량</li>
</ol>
<ul>
<li>클러스터 기본값 사용 &lt;= 선택<ul>
<li>처음 클러스터를 만들 때 정의한 내용을 따라감</li>
</ul>
</li>
<li>사용자 지정 사용(고급)<ul>
<li>용량 공급자를 직접 지정하고 가중치도 직접 지정</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/pure_bver/post/79404136-f9cf-4377-ae36-1d7bf9ce327f/image.png" alt="">
3. 어플리케이션 유형: ECS 클러스터에서 실행할 애플리케이션의 유형을 정의</p>
<ul>
<li>서비스: 애플리케이션이 중지 없이 계속 실행되어야 할 경우</li>
<li>테스크: 한 번 실행하고 종료되는 작업(Task)에 적합, 작업이 실패하거나 완료되면 다시 실행되지 않음</li>
</ul>
<ol start="4">
<li>태스크 정의</li>
</ol>
<ul>
<li>ECS에서 실행할 작업(Task)에 대한 정의</li>
<li>개정: 작업 정의가 업데이트되면 새로운 개정 번호가 생성되며, 이 중 특정 버전을 실행 가능</li>
</ul>
<ol start="5">
<li><p>서비스 이름: 서비스 이름</p>
</li>
<li><p>서비스 유형: 작업(Task)을 ECS 클러스터에 배포하는 방식</p>
</li>
</ol>
<ul>
<li>복제본<ul>
<li>클러스터 전체에서 작업(Task)의 복제본을 배포하고 유지 관리</li>
<li>원하는 작업 수(Replica 수)를 지정하면, ECS가 해당 작업 수를 유지</li>
</ul>
</li>
<li>데몬<ul>
<li>클러스터의 각 컨테이너 인스턴스(EC2)에 하나의 작업을 배포</li>
</ul>
</li>
</ul>
<ol start="7">
<li>원하는 태스크</li>
</ol>
<ul>
<li>서비스가 실행할 작업(Task)의 수를 정의</li>
</ul>
<ol start="8">
<li>가용 영역 리밸런싱</li>
</ol>
<ul>
<li>ECS 서비스가 특정 가용 영역에서 작업(Task)이 너무 많거나 부족할 경우, 작업을 다른 가용 영역으로 이동</li>
</ul>
<p><img src="https://velog.velcdn.com/images/pure_bver/post/d9fcf41c-5395-4846-93d1-78fe6fd8f80e/image.png" alt=""></p>
<ol start="9">
<li>롤링 업데이트</li>
</ol>
<ul>
<li>기존 작업(Task)을 점진적으로 교체</li>
<li>새 작업이 정상적으로 실행되면 기존 작업을 종료</li>
</ul>
<ol start="10">
<li>블루/그린 배포</li>
</ol>
<ul>
<li>기존 작업(Task)을 유지한 채 새 작업(Task)을 병렬로</li>
<li>새 작업이 성공하면 트래픽을 기존 작업에서 새 작업으로 전환</li>
</ul>
<ol start="11">
<li>최소 실행 작업 비율 (%)</li>
</ol>
<ul>
<li>서비스 배포 중 서비스에서 실행 중인 작업(Task)의 최소 백분율을 설정</li>
<li>기본값: 100% =  작업(Task)이 실행되기 전까지 기존 작업(Task)이 중단되지 않음</li>
</ul>
<ol start="12">
<li>최대 실행 작업 비율 (%)</li>
</ol>
<ul>
<li>동시에 실행 가능한 작업(Task)의 최대 백분율</li>
<li>기본값: 200% =  기존 작업(Task)와 새 작업(Task)을 모두 실행하여 최대 2배의 작업을 실행</li>
</ul>
<hr>
<h2 id="ec2를-사용하는-ecs를-하려면">EC2를 사용하는 ECS를 하려면?</h2>
<h4 id="1-ec2-인스턴스-생성">1. EC2 인스턴스 생성</h4>
<pre><code>- 클러스터에 연결할 VPC와 서브넷 선택
- 보안 그룹에서 ECS 작업에 필요한 포트를 허용</code></pre><h4 id="2-생성된-ec2-터미널bash-ecs-에이전트-설치">2. 생성된 EC2 터미널(bash) ECS 에이전트 설치</h4>
<ul>
<li><p>ECS-Optimized AMI를 사용중이라면 상관없지만 비싸서 안쓰는걸로 예시를 들겠다.(2$/hr)</p>
<ul>
<li>ECS 에이전트 및 관련 패키지 설치<blockquote>
<p><strong>Linux</strong>
sudo yum update -y
sudo yum install -y ecs-init docker</p>
</blockquote>
</li>
</ul>
<blockquote>
<p><strong>Ubuntu</strong>
sudo apt update
sudo apt install -y docker.io
curl -o /usr/local/bin/ecs-agent <a href="https://s3.amazonaws.com/amazon-ecs-agent-us-east-1/amazon-ecs-agent-latest.tar.gz">https://s3.amazonaws.com/amazon-ecs-agent-us-east-1/amazon-ecs-agent-latest.tar.gz</a>
chmod +x /usr/local/bin/ecs-agent</p>
</blockquote>
</li>
</ul>
<h4 id="3-ecs-에이전트-구성">3. ECS 에이전트 구성</h4>
<ul>
<li><p>ECS 에이전트는 /etc/ecs/ecs.config 파일을 통해 클러스터와 통신</p>
<ul>
<li><p>구성 파일 생성 및 편집</p>
<blockquote>
<p>sudo mkdir -p /etc/ecs
sudo nano /etc/ecs/ecs.config</p>
</blockquote>
</li>
<li><p>클러스터 이름 추가</p>
<blockquote>
<p>ECS_CLUSTER=&lt;클러스터 이름&gt;</p>
</blockquote>
</li>
<li><p>에이전트 실행</p>
<blockquote>
<p><strong>Linux</strong>
sudo systemctl start ecs
sudo systemctl enable ecs</p>
</blockquote>
<blockquote>
<p><strong>Ubuntu</strong>
sudo docker run --name ecs-agent <br>--detach=true <br>--restart=always <br>--volume=/var/run/docker.sock:/var/run/docker.sock <br>--volume=/var/log/ecs/:/log <br>--volume=/var/lib/ecs/data:/data <br>--network=host <br>amazon/amazon-ecs-agent:latest</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<h4 id="4-ecs-클러스터-생성상단-참조">4. ECS 클러스터 생성(상단 참조)</h4>
<ul>
<li>EC2 인스턴스 클러스터에 연결 확인<blockquote>
<p>aws ecs list-container-instances --cluster my-cluster</p>
</blockquote>
</li>
<li>결과가 비어 있으면 /etc/ecs/ecs.config를 다시 확인하고 에이전트를 재시작</li>
</ul>
<h4 id="5-작업-정의task-definition-생성상단-참조">5. 작업 정의(Task Definition) 생성(상단 참조)</h4>
<ul>
<li>용량공급자 클러스터 기본값을 해놨으면 1,2,3 필요없음</li>
</ul>
<h4 id="6-서비스service-생성">6. 서비스(Service) 생성</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.12.02 트러블 슈팅3 (불큐)]]></title>
            <link>https://velog.io/@pure_bver/2024.12.02-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%853-%EB%B6%88%ED%81%90</link>
            <guid>https://velog.io/@pure_bver/2024.12.02-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%853-%EB%B6%88%ED%81%90</guid>
            <pubDate>Mon, 02 Dec 2024 11:34:56 GMT</pubDate>
            <description><![CDATA[<h3 id="트러블">트러블</h3>
<ul>
<li>불큐에서 다른 서버의 큐를 가지고옴</li>
</ul>
<h3 id="상황">상황</h3>
<ul>
<li>나: id/&#39;나클3&#39; 으로 인벤토리에 아이템을 집어넣는 작업</li>
<li>동료: id/&#39;나는&#39; 으로 인벤토리에 아이템을 집어넣는 작업</li>
<li>동시 실행</li>
<li>내 서버에서 동료의 큐를 실행</li>
<li>동료 서버에서 내 큐를 실행
<img src="https://velog.velcdn.com/images/pure_bver/post/ed37ac5c-765a-4252-9717-27ffbf2406b7/image.png" alt=""></li>
</ul>
<h3 id="원인-파악">원인 파악</h3>
<ul>
<li>같은 레디스 서버를 사용 중</li>
<li>싱글톤으로 작성되어 같은 이름의 불큐를 사용</li>
</ul>
<h3 id="해결">해결</h3>
<ul>
<li>각 게임 세션별로 불큐를 생성시키고 세션id를 불큐id에 추가하여 다른 불큐id와 겹치지 않게 함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.11.29 Bull Queue(JS), 트러블 슈팅2]]></title>
            <link>https://velog.io/@pure_bver/2024.11.29-Bull-QueueJS-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%852</link>
            <guid>https://velog.io/@pure_bver/2024.11.29-Bull-QueueJS-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%852</guid>
            <pubDate>Fri, 29 Nov 2024 14:53:55 GMT</pubDate>
            <description><![CDATA[<h2 id="bull-queue">Bull Queue</h2>
<ol>
<li><p><code>bull</code> 라이브러리 설치</p>
</li>
<li><p>세팅</p>
<pre><code class="language-js"> const queue = new Queue(&#39;itemQueue&#39;, {
       redis: {
         host: &#39;127.0.0.1&#39;,
         port: 6666,
         password: 1234,
       },
 });</code></pre>
</li>
<li><p>큐에 작업 추가: Queue.add</p>
</li>
</ol>
<pre><code class="language-js">    await queue
    .add(
      // 작업에 사용될 데이터 =&gt; ex: job.data.userId
      { userId: socket.userId, itemId, newInventorySlot },
      { jobId: `getItem:${itemId}`, removeOnComplete: true },// 불큐 옵션
    );</code></pre>
<blockquote>
<p><strong>옵션 종류</strong>
    - <code>jobId</code>: 작업의 고유id로 동일한 id의 작업이 큐에 이미 있다면 새 작업이 추가되지 않음
    - <code>priority</code>: 작업의 우선순위, 숫자가 낮을수록 높은 우선순위
    - <code>delay</code>: 지정된 시간(ms) 이후 작업실행
    - <code>attempts</code>: 작업 실패 시 재시도(횟수 지정)
    - <code>removeOnComplete</code>: 작업 완료 후 큐에서 제거 여부 
    =&gt; 기본값이 false라 true로 지정하지 않으면 jobId가 제거되지 않아 같은 jobId의 작업을 실행하지 않음
    - <code>removeOnFail</code>: 작업 실패 시 큐에서 제거 여부</p>
</blockquote>
<ol start="4">
<li><p>큐의 작업 지시: Queue.process(concurrency, (job) =&gt; {})</p>
<pre><code class="language-js"> await queue.process(4, async (job) =&gt; {
   //작업에 사용될 데이터 구조 분해 할당
   const { userId, inventorySlot, itemId } = job.data;

   //데이터를 활용한 작업 실행
 });</code></pre>
<blockquote>
<ul>
<li><strong>concurrency</strong>는 값을 설정하지 않으면 1
위의 코드는 4로 지정</li>
</ul>
</blockquote>
</li>
<li><p>그 외 실패할경우 실행될 이벤트 등 지정할 수 있음.</p>
</li>
</ol>
<hr>
<h2 id="동시성-제어트러블슈팅2">동시성 제어(트러블슈팅2)</h2>
<ul>
<li><p>원하는 것 A, B, C구간 에서 B구간을 큐에서 작업하고 C구간을실행</p>
</li>
<li><p>동시성 제어로 동일한 작업이 B구간 진입 시 C구간 작업을 못하게 함
=&gt; 동시에 아이템을 잡을 경우 한명한테는 인벤토리에 아이템이 들어가게 다른 한명은 아무 일도 없게</p>
</li>
<li><p>코드 원본</p>
<pre><code class="language-js">export const itemGetRequestHandler = async ({ socket, payload }) =&gt; {
//--A구간
const { itemId, inventorySlot } = payload;

const user = getUserById(socket.userId);
if (!user) {
  throw new CustomError(ErrorCodesMaps.USER_NOT_FOUND);
}

const gameSession = getGameSessionById(user.gameId);
if (!gameSession) {
  throw new CustomError(ErrorCodesMaps.GAME_NOT_FOUND);
}

const item = gameSession.getItem(itemId);

if (!item.mapOn) {
  return;
}
const [bool, newInventorySlot] = await checkSetInventorySlotRedis(
  socket.userId,
  inventorySlot,
);

// 모든 슬롯에 아이템이 있을경우 처리 중지
if (!bool) {
  return;
}
//--A구간
</code></pre>
</li>
</ul>
<p>//--B구간
  const time = 640;</p>
<p>  const key = <code>${config.redis.user_set}:${userId}:${newInventorySlot}</code>;</p>
<p>  await redisManager.getClient().set(key, itemId, &#39;EX&#39;, time);</p>
<p>  user.character.itemCount++;
//--B구간</p>
<p>//--C구간</p>
<p>  // 응답 보내주기
  itemGetResponse(user.socket, itemId, newInventorySlot);</p>
<p>  // 손에 들어주기
  itemChangeNotification(gameSession, socket.userId, itemId);
//--C구간
};</p>
<pre><code>---
### 1차 시기
- 처음 A구간 B구간 C구간이 나누어져 있을 때 B구간만 불큐에 넣어 작업하는 형태로 만들었다.
```js
//--A구간
~~~
//--A구간
//--B구간
await itemQueueManager
.getQueue()
.add(
  { userId: socket.userId, itemId, newInventorySlot },
  { jobId: `getItem:${itemId}`, removeOnComplete: true },
);
//--B구간
//--C구간
~~~
//--C구간</code></pre><h4 id="문제점">문제점</h4>
<ul>
<li>거의 동시에 B구간 진입 시 한명은 B구간에서 제외가 되는 것은 확인</li>
<li>하지만 C구간은 B구간과 상관없이 그대로 진행됨</li>
</ul>
<hr>
<h3 id="2차-시기">2차 시기</h3>
<ul>
<li>B구간에서 getState()로 제어를 시도<blockquote>
<p><strong>getState</strong>는 현재 진행중이 작업의 진행도를 나타냄</p>
<ul>
<li><code>wait</code>: 작업이 대기열에 추가되었지만 아직 처리되지 않은 상태</li>
<li><code>active</code>: 작업이 현재 처리 중</li>
<li><code>completed</code>: 작업이 성공적으로 완료</li>
<li><code>failed</code>: 작업 실행 중 오류가 발생하여 실패</li>
<li><code>delayed</code>: 작업이 특정 시간만큼 지연되도록 설정된 상태</li>
<li><code>paused</code>: 큐 자체가 일시 중지되어 작업이 대기 상태</li>
<li><code>stuck</code>: 예상치 못한 이유로 진행 중이거나 처리되지 않은 상태로 멈춘 경우</li>
</ul>
</blockquote>
</li>
</ul>
<pre><code class="language-js">//--B구간
    job = await queueManager.getItemQueue().add(
        { userId: socket.userId, itemId, newInventorySlot },
        { jobId: `getItem:${itemId}` },
     )
    const state = await job.getState();
    if (state !== &#39;wait&#39;) {
        console.log(`Duplicate job detected for item ${itemId}.`);
        return; // 중복 작업인 경우 중단
    }
//--B구간</code></pre>
<h4 id="문제점-1">문제점</h4>
<ul>
<li>state가 같은작업의 첫 작업에서 wait이 나올 가능성이 높지만 100퍼는 아님</li>
<li>좀더 확실한 처리를 원하기에 폐기</li>
</ul>
<hr>
<h3 id="3차-시기">3차 시기</h3>
<ul>
<li>B구간이 완료된 이후에 C구간을 처리하는 로직을 찾아보고 싶었음</li>
<li>결론만 따지면 불가능, 아시는분 있으면 알려주셨으면...</li>
</ul>
<h4 id="문제점-2">문제점</h4>
<ul>
<li>job.finished()를 이용해 보려 했으나 전혀 관련없던 기능<blockquote>
<p>await job.finished(): job이 종료될 때 까지 기다림
=&gt; 만약 동시에 두 작업이 들어오면 한 작업은 add로 큐에 추가되고 완료를 기다리고 두번째 작업은 add로 큐에 추가되지는 않지만 첫번째 작업이 완료될때 까지 기다리는 기현상이 일어난다.</p>
</blockquote>
</li>
</ul>
<hr>
<h3 id="4차-시기">4차 시기</h3>
<ul>
<li>ABC구간을 섞어서 C구간 일부를 최상위에 두고 전부 queue.process에 때려밖음</li>
</ul>
<h4 id="결과">결과</h4>
<ul>
<li><p>동시성 제어는 성공 했으나 3차구간에서 원하던 바를 얻지못해 아쉬움.</p>
<pre><code class="language-js">export const itemGetRequestHandler = async ({ socket, payload }) =&gt; {
const { itemId, inventorySlot } = payload;

const [bool, newInventorySlot] = await checkSetInventorySlotRedis(
  socket.userId,
  inventorySlot,
);

// 모든 슬롯에 아이템이 있을경우 처리 중지
if (!bool) {
  return;
}

// 동시성 제어1
// 불큐
// 실질적인 아이템 저장 메서드
await itemQueueManager
  .getQueue()
  .add(
    { userId: socket.userId, itemId, newInventorySlot },
    { jobId: `getItem:${itemId}`, removeOnComplete: true },
  );
};</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.11.23 트러블 슈팅1]]></title>
            <link>https://velog.io/@pure_bver/2024.11.23-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%851</link>
            <guid>https://velog.io/@pure_bver/2024.11.23-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%851</guid>
            <pubDate>Wed, 27 Nov 2024 14:45:35 GMT</pubDate>
            <description><![CDATA[<h2 id="발생한-문제">발생한 문제</h2>
<h4 id="1-기본-레이턴시가-200이-넘어감">1. 기본 레이턴시가 200이 넘어감</h4>
<h4 id="2-클라이언트와-이동-동기화-시-레이턴시가-20초가-넘어감">2. 클라이언트와 이동 동기화 시 레이턴시가 20초가 넘어감</h4>
<hr>
<h3 id="문제-원인">문제 원인</h3>
<h4 id="1-클라이언트-코드에-임의로-레이턴시를-강제로-늘리는-코드-발경">1. 클라이언트 코드에 임의로 레이턴시를 강제로 늘리는 코드 발경</h4>
<h4 id="2-클라이언트-서버-양쪽에서-단기간에-너무-많은량의-패킷을-전송">2. 클라이언트 서버 양쪽에서 단기간에 너무 많은량의 패킷을 전송</h4>
<h4 id="3-aws에서-ec2-서버를-연-위치가-미국-버지니아주-북부">3. AWS에서 ec2 서버를 연 위치가 미국 버지니아주 북부</h4>
<h4 id="4-빌드를-했을-경우와-안했을-경우의-차이드를-했을-경우와-안했을-경우의-차이">4. 빌드를 했을 경우와 안했을 경우의 차이드를 했을 경우와 안했을 경우의 차이</h4>
<hr>
<h3 id="해결방안">해결방안</h3>
<h4 id="1-불필요한-코드-제거">1. 불필요한 코드 제거</h4>
<h4 id="2-이동-동기화-패킷-교환-방법을-01초-주기로-클라이언트-위주로-변경">2. 이동 동기화 패킷 교환 방법을 0.1초 주기로 클라이언트 위주로 변경</h4>
<h4 id="3-ec2-서버-위치를-가까운-곳으로-변경">3. ec2 서버 위치를 가까운 곳으로 변경</h4>
<hr>
<h3 id="결론">결론</h3>
<h4 id="1-최적화-완료">1. 최적화 완료</h4>
<h4 id="2-물리적인-거리가-생각보다-중요함">2. 물리적인 거리가 생각보다 중요함</h4>
<h4 id="3-빌드를-하면-클라이언트-처리-속도가-5배는-차이남-유니티6-기준">3. 빌드를 하면 클라이언트 처리 속도가 5배는 차이남 (유니티6 기준)</h4>
<hr>
<hr>
<hr>
<h3 id="원인-분석-story">원인 분석 story</h3>
<h3 id="1차">1차</h3>
<h4 id="원인-분석">원인 분석</h4>
<ul>
<li><p>처리 속도 문제인가 싶어서 서버에서 체크 
=&gt; 문제없음</p>
</li>
<li><p>클라이언트 체크
=&gt; 임의로 레이턴시를 강제로 늘리는 코드를 발견 (waitsecond)</p>
</li>
</ul>
<h4 id="처리">처리</h4>
<ul>
<li>예전에 사용했던 코드를 긁어오다 잊어버린 것으로 판단하고 전달 후 삭제</li>
</ul>
<h4 id="결과">결과</h4>
<ul>
<li>조금 빨라지긴 했으나 레이턴시가 계속 늘어나는건 여전함</li>
</ul>
<hr>
<h3 id="2차">2차</h3>
<h4 id="원인분석">원인분석</h4>
<ul>
<li>클라이언트 코드를 분석해 sendQueue와 receiveQueue의 스택 쌓이는 양을 체크
=&gt; sendQueue와 receiveQueue 둘다 심각하게 많은량이 쏟아져 내림
=&gt; 양쪽에서 인터벌로 약0.02초 간격으로 보내줌(너무 많음)
=&gt; 클라이언트 처리 속도로는 쌓이는 속도를 못따라감</li>
</ul>
<h4 id="처리-1">처리</h4>
<ul>
<li>서버에서는 인터벌로 주지 않고 클라이언트에서 받은 이동에대한 notification을 해주는 것으로 변경</li>
</ul>
<h4 id="결과-1">결과</h4>
<ul>
<li><p>receiveQueue는 상당히 줄었으나 쌓이는 속도를 못따라감</p>
</li>
<li><p>sendQueue는 여전히 엄청나게 쌓임</p>
</li>
</ul>
<hr>
<h3 id="3차">3차</h3>
<h4 id="원인-분석-1">원인 분석</h4>
<ul>
<li>0.02초마다 이동동기화를 해주는 것이 너무 빠르다고 판단
=&gt; 일반적으로 이동 동기화가 중요한 FPS나 MOBA같은 게임이 0.05 ~ 0.1초 마다 동기화를 해준다고함
=&gt; 현재 우리가 진행중인 프로젝트는 리썰컴퍼니류라 이동 동기화에 그리 힘쓸 필요가 없음</li>
</ul>
<h4 id="처리-2">처리</h4>
<ul>
<li>이동 동기화를 0.1초마다 해주는 것으로 변경</li>
</ul>
<h4 id="결과-2">결과</h4>
<ul>
<li>레이턴시가 250~350 사이로 안정적으로 변경됨</li>
</ul>
<hr>
<h3 id="4차">4차</h3>
<ul>
<li><p>레이턴시가 안정적이긴 하나 기본 레이턴시가 너무 큼</p>
<h4 id="원인-분석-2">원인 분석</h4>
</li>
<li><p>AWS에서 ec2 서버를 연 위치가 미국 버지니아주 북부로 되어있는것을 발견</p>
</li>
</ul>
<h4 id="처리-3">처리</h4>
<ul>
<li>서울로 위치변경</li>
</ul>
<h4 id="결과-3">결과</h4>
<ul>
<li>레이턴시가 50~100 사이로 생각 이상으로 빨라짐</li>
</ul>
<hr>
<h3 id="5차">5차</h3>
<h4 id="추가-문제">추가 문제</h4>
<ul>
<li><p>4차 결과로 만족을 하였으나 0.05초로 이동 동기화를 해도 레이턴시가 늘어나는 게 이상하다고 생각</p>
<h4 id="원인분석-1">원인분석</h4>
</li>
<li><p>빌드를 했을 경우와 안했을 경우에 속도 차이가 있을 것이라고 판단하여 빌드를 한 뒤 진행</p>
</li>
</ul>
<h4 id="결과-4">결과</h4>
<ul>
<li><p>동기화 0.05초
=&gt; 정상작동 오히려 레이턴시가 더 짧아짐 30~60 사이</p>
</li>
<li><p>동기화 0.02초
=&gt; 정상작동했으나 50~120 사이 값을 벋어나지는 않지만 4차 결과보다 늘어난 레이턴시에 이 이상은 문제가 있을 거라고 판단
=&gt; 이후 에너미 동기화나 추가 유저 동기화를 생각하면 더 많은량의 패킷이 오고갈 것을 생각해 0.1초로 동기화 하기로 결정</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.11.15 BFS]]></title>
            <link>https://velog.io/@pure_bver/2024.11.15-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@pure_bver/2024.11.15-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Fri, 15 Nov 2024 14:42:09 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-bfs-지도-탐색">문제: BFS 지도 탐색</h3>
<h4 id="문제설명">문제설명</h4>
<ul>
<li>2차원 배열 형태로 주어진 지도에서 숫자로 표시된 영역의 합을 구하는 문제</li>
<li>X는 벽으로 간주하며 탐색할 수 없음</li>
<li>연결된 숫자 영역의 합을 계산하여 오름차순으로 정렬된 결과를 반환</li>
<li>연결된 영역이 없으면 [-1]을 반환</li>
</ul>
<h4 id="문제접근방법">문제접근방법</h4>
<ol>
<li>그래프 탐색</li>
<li>BFS or DFS</li>
<li>방문 기록</li>
</ol>
<h4 id="코드">코드</h4>
<pre><code class="language-js">function solution(maps) {
    const visited = Array.from({length: maps.length}, () =&gt; Array(maps[0].length).fill(false));
    const direction = [[1,0], [-1,0], [0,1], [0,-1]];

    const bfs = (startX, startY) =&gt; {
        let queue = [[startX, startY]];
        let sum = 0;

        while(queue.length &gt; 0){
            const [x, y] = queue.shift();
            if(visited[x][y]) continue;

            visited[x][y] = true;

            sum += Number(maps[x][y]);

            for(let [dx, dy] of direction){
                const nextX = x + dx;
                const nextY = y + dy;

                if(nextX &gt;= 0 &amp;&amp; nextX &lt; maps.length &amp;&amp; nextY &gt;= 0 &amp;&amp; nextY &lt; maps[0].length){
                    if(maps[nextX][nextY] !== &#39;X&#39; &amp;&amp; !visited[nextX][nextY]){
                        queue.push([nextX, nextY])
                    }
                }
            }
        }
        return sum;
    }

    const result = [];
    for(let i = 0; i &lt; maps.length; i++){
        for(let j = 0; j &lt; maps[i].length; j++){
            if(maps[i][j] !== &#39;X&#39; &amp;&amp; !visited[i][j]){
                result.push(bfs(i,j));
            }
        }
    }

    return result.length &gt; 0 ? result.sort((a, b) =&gt; a - b) : [-1];
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.11.13 트러블 슈팅]]></title>
            <link>https://velog.io/@pure_bver/2024.11.13-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@pure_bver/2024.11.13-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</guid>
            <pubDate>Wed, 13 Nov 2024 12:17:53 GMT</pubDate>
            <description><![CDATA[<h3 id="1-패킷-송수신">1. 패킷 송수신</h3>
<h4 id="문제점--원인-분석">문제점 &amp; 원인 분석</h4>
<ul>
<li>프로토버프의 데이터형 제한<pre><code>  - ushort, ubytes 형식 사용 불가</code></pre></li>
<li>oneof 형태의 프로토버프 인코딩 / 디코딩 문제<pre><code>  - oneof 형태의 프로토버프는 다양한 형태의 데이터를 하나의 필드로 처리할 수 있지만, 클라이언트와 서버가 동일하게 해석하지 못해 통신 오류 발생</code></pre></li>
</ul>
<h4 id="해결-방법">해결 방법</h4>
<ul>
<li>수신 시 직접 버퍼를 읽도록 수정 및 페이로드는 약속한 프로토버프로 디코딩</li>
<li>클라이언트와 약속한 객체의 이름으로 매핑하여 응답</li>
</ul>
<h3 id="2-매칭-시스템">2. 매칭 시스템</h3>
<h4 id="문제점">문제점</h4>
<ul>
<li>매칭 시작 시 루프에 의해 블로킹되어 다음 패킷 처리 불가<h4 id="원인-분석">원인 분석</h4>
</li>
<li>유저 검색 루프가 콜 스택에 쌓이게 되고 처리가 되지 않으면서 블로킹 발생</li>
<li>따라서 매칭 시스템을 비동기적으로 처리해야 함<h4 id="해결-방법-1">해결 방법</h4>
</li>
<li>싱글톤 형식으로 매칭 매니저 구현매칭 인스턴스 내의 큐에 유저를 추가하여 블로킹 없이 유저 탐색 진행</li>
<li>콜백 함수로 매칭 처리유저가 매칭 될 시, 콜백 함수가 실행되어 게임세션에 접속</li>
</ul>
<h3 id="3-후속-처리-시스템">3. 후속 처리 시스템</h3>
<h4 id="문제점-1">문제점</h4>
<ul>
<li>공통적인 후속 처리를 각 핸들러마다 작성</li>
<li>이로 인해 코드의 복잡성 증가 및 가독성 저하 문제 발생<h4 id="해결-방법-2">해결 방법</h4>
</li>
<li>후속 처리 매니저 구현</li>
<li>후속 패킷을 한 곳에서 일괄적으로 관리하고, 동일 기능의 후속 처리 패킷의 중복 코드를 방지</li>
<li>비동기 후속 처리 방식 도입</li>
<li>await handler()가 작동된 이후에 후속 처리를 확인하고 실행하도록 진행</li>
</ul>
<h3 id="4-타워의-초-장거리-공격">4. 타워의 초 장거리 공격</h3>
<h4 id="문제점-2">문제점</h4>
<ul>
<li>타워의 초 장거리 공격</li>
</ul>
<h4 id="원인분석">원인분석</h4>
<ul>
<li>타워 소환 시 타워ID 없이 생성된 후 서버로부터 타워ID 패킷을 전달받음</li>
<li>타워ID 패킷을 서버로부터 할당 받기 전에 몬스터를 공격하면 발생</li>
<li>기존 생성된 타워ID와 새로 생성된 타워ID 중복으로 인해 발생 </li>
</ul>
<h4 id="해결-방법-3">해결 방법</h4>
<ul>
<li>서버: 타워ID 중복 회피<pre><code>  - 임시로 타워ID를 1부터 시작</code></pre></li>
<li>클라: 타워ID가 유효한 값일 때만 공격을 허용<pre><code>  - 클라이언트에서 타워ID가 0 또는 null인 경우, 공격에 제한</code></pre></li>
</ul>
<h3 id="5-강제-종료-후-처리">5. 강제 종료 후 처리</h3>
<h4 id="문제점-3">문제점</h4>
<ul>
<li>강제로 연결을 끊으면 승패 결과가 뒤바뀌는 문제 발생<h4 id="원인-분석-1">원인 분석</h4>
</li>
<li>게임이 정상적으로 끝나고 로비로 이동하기 전, 세션 연결을 종료하면 승패 결과</li>
<li>Session과 연결을 끊지만, 이를 OnEnd()에서 확인하지 않아 발생한 문제<h4 id="해결-방법-4">해결 방법</h4>
</li>
<li>OnEnd()에서 Session과 연결이 끊어졌는지 확인</li>
<li>수정 후, 연결이 강제로 끊어져도 승패 결과가 뒤바뀌지 않는 것을 확인</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.11.05 레이스 컨디션]]></title>
            <link>https://velog.io/@pure_bver/2024.11.05-%EB%A0%88%EC%9D%B4%EC%8A%A4-%EC%BB%A8%EB%94%94%EC%85%98</link>
            <guid>https://velog.io/@pure_bver/2024.11.05-%EB%A0%88%EC%9D%B4%EC%8A%A4-%EC%BB%A8%EB%94%94%EC%85%98</guid>
            <pubDate>Mon, 04 Nov 2024 15:19:37 GMT</pubDate>
            <description><![CDATA[<h2 id="레이스-컨디션">레이스 컨디션</h2>
<ul>
<li>여러 프로세스나 스레드가 공유 자원에 동시에 접근할 때 발생하는 문제</li>
<li>접근 순서에 따라 결과가 달라질 수 있는 상황</li>
</ul>
<h3 id="세마포어">세마포어</h3>
<ul>
<li>동시에 여러 스레드가 접근할 수 있는 자원의 수를 제한</li>
</ul>
<h4 id="동작방식">동작방식</h4>
<ul>
<li>세마포어는 자원에 접근할 수 있는 허가된 &quot;카운트&quot;를 가지며, 이 카운트가 0이 되면 더 이상 접근이 불가능</li>
<li>자원에 접근하기 위해 P() 또는 wait() 연산을 수행하여 카운트를 1 감소시키고, 작업을 마친 후 V() 또는 signal() 연산을 통해 카운트를 1 증가</li>
</ul>
<h4 id="종류">종류</h4>
<ul>
<li>이진 세마포어(Binary Semaphore): 카운트가 0 또는 1만 가지며, 자원에 한 번에 하나의 스레드만 접근(뮤텍스처럼 동작)</li>
<li>계수기 세마포어(Counting Semaphore): 특정 자원에 여러 개의 접근을 허용할 수 있는 세마포어로, 자원의 허용 가능한 최대 접근 수를 나타내는 초기값을 가짐</li>
</ul>
<hr>
<h3 id="뮤텍스">뮤텍스</h3>
<ul>
<li>한 번에 하나의 스레드만 자원에 접근할 수 있도록 <strong>상호 배제</strong>를 보장하는 도구</li>
</ul>
<h4 id="동작방식-1">동작방식</h4>
<ul>
<li>잠금(Lock) 개념을 사용하여 자원 접근을 관리</li>
<li>스레드가 자원에 접근할 때 뮤텍스를 잠그고, 작업이 끝나면 뮤텍스를 해제</li>
<li>뮤텍스가 잠겨 있으면 대기</li>
</ul>
<hr>
<h3 id="데드락">데드락</h3>
<ul>
<li>여러 스레드나 프로세스가 서로의 자원을 기다리며 무한히 대기하는 상태</li>
<li>ex: 스레드 A는 자원 X를 잠그고 자원 Y를 기다리는 동안, 스레드 B는 자원 Y를 잠그고 자원 X를 기다린다면 두 스레드는 영원히 대기</li>
</ul>
<blockquote>
<p>데드락을 발생시키는 접근 제한 조건</p>
</blockquote>
<ul>
<li>상호 배제(Mutual Exclusion): 자원은 한 번에 한 프로세스만 접근</li>
<li>점유 대기(Hold and Wait): 자원을 점유한 프로세스가 추가로 다른 자원을 기다림</li>
<li>비선점(No Preemption): 다른 프로세스가 자원을 강제로 빼앗을 수 없음</li>
<li>순환 대기(Circular Wait): 프로세스들이 자원을 기다리는 관계가 순환 구조</li>
</ul>
<h4 id="데드락-해결방법">데드락 해결방법</h4>
<ol>
<li>데드락 예방</li>
</ol>
<ul>
<li>접근 제한 조건중 하나라도 사용하지 않으면 됨</li>
</ul>
<ol start="2">
<li>데드락 회피</li>
</ol>
<ul>
<li>은행가 알고리즘(Banker&#39;s Algorithm): 자원 요청 시 현재 시스템 상태를 확인하고, 데드락 상태가 발생하지 않는 경우에만 자원을 할당</li>
</ul>
<ol start="3">
<li>데드락 검출과 회복</li>
</ol>
<ul>
<li>검출: 주기적으로 자원 할당 그래프를 검사하여 데드락 상태를 감지</li>
<li>회복: 데드락 상태가 감지되면 일부 프로세스를 종료하거나 자원을 회수하여 데드락을 해소</li>
</ul>
<ol start="4">
<li>타임아웃 설정</li>
</ol>
<ul>
<li>특정 자원을 일정 시간 동안 얻지 못하면 요청을 취소하고 자원을 재요청</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.11.04 컨텍스트 스위칭]]></title>
            <link>https://velog.io/@pure_bver/2024.11.04-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%8A%A4%EC%9C%84%EC%B9%AD</link>
            <guid>https://velog.io/@pure_bver/2024.11.04-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%8A%A4%EC%9C%84%EC%B9%AD</guid>
            <pubDate>Mon, 04 Nov 2024 14:42:54 GMT</pubDate>
            <description><![CDATA[<h3 id="컨텍스트-스위칭">컨텍스트 스위칭</h3>
<ul>
<li><p>운영체제(OS)에서 여러 프로세스나 스레드가 CPU 자원을 공유할 때, 현재 실행 중인 프로세스 또는 스레드의 상태를 저장하고 새로운 프로세스나 스레드로 전환하는 과정</p>
</li>
<li><p>각 작업을 순차적으로 실행하면서도 동시에 여러 작업이 진행되는 것처럼 보이게 하는 핵심 메커니즘</p>
</li>
<li><p>다음 실행할 프로세스의 상태를 로드하여 이어서 실행</p>
</li>
<li><p>컨텍스트 스위칭은 멀티태스킹 환경에서 필수적인 작업이지만, &#39;전환 과정에서 시간이 소요&#39;되기 때문에 잦은 스위칭은 시스템 성능에 영향</p>
</li>
</ul>
<h3 id="컨텍스트-스위칭-과정">컨텍스트 스위칭 과정</h3>
<ol>
<li>현재 프로세스/스레드의 상태 저장<blockquote>
<p>레지스터 저장</p>
</blockquote>
</li>
</ol>
<ul>
<li>현재 프로세스나 스레드가 CPU에서 실행되고 있는 동안 CPU는 프로그램 카운터(Program Counter), 스택 포인터(Stack Pointer), 상태 레지스터(Status Register) 등 다양한 레지스터에 상태 정보를 저장</li>
<li>컨텍스트 스위칭이 일어나면 이 정보를 메모리의 PCB(Process Control Block) 또는 TCB(Thread Control Block)에 저장하여, 이후에 이 작업을 재개할 때 동일한 상태에서 시작</li>
</ul>
<blockquote>
<p>메모리 관리 정보 저장</p>
</blockquote>
<ul>
<li>프로세스 간 스위칭 시에는 레지스터 외에도 프로세스의 메모리 매핑 정보나 페이지 테이블 등의 메모리 관련 정보도 저장</li>
<li>프로세스가 사용하는 메모리 주소 공간을 복구하기 위해 필요하며, 이를 통해 프로세스는 전환 후에도 동일한 메모리 위치를 참조</li>
</ul>
<ol start="2">
<li>상태 변경 및 스케줄링</li>
</ol>
<blockquote>
<p>상태변경</p>
</blockquote>
<ul>
<li>현재 실행 중인 프로세스나 스레드는 &#39;실행 중(Running)&#39; 상태에서 &#39;준비(Ready)&#39; 또는 &#39;대기(Waiting)&#39; 상태로 변경</li>
<li>프로세스가 입출력 작업을 기다려야 하는 경우 &#39;대기&#39; 상태가 되고, 단순히 CPU 점유 시간이 끝났다면 &#39;준비&#39; 상태로 변경</li>
</ul>
<blockquote>
<p>스케줄러</p>
</blockquote>
<ul>
<li>운영체제의 스케줄러(Scheduler)는 현재 프로세스/스레드의 상태를 평가하고, 우선순위에 따라 다음 실행할 프로세스를 선택</li>
<li>스케줄링 방식은 우선순위 기반, 라운드 로빈(Round Robin), 선점형 등 다양한 알고리즘을 사용할 수 있으며, 이를 통해 CPU 자원을 최적화</li>
</ul>
<ol start="3">
<li>새로운 프로세스/스레드 상태 로드</li>
</ol>
<blockquote>
<p>PCB/TCB 로드</p>
</blockquote>
<ul>
<li>스케줄러가 다음에 실행할 프로세스나 스레드를 결정하면, 선택된 프로세스의 PCB 또는 TCB에 저장된 레지스터 값, 프로그램 카운터, 스택 포인터 등의 정보를 CPU에 로드</li>
<li>이를 통해 새롭게 선택된 작업은 중단된 시점부터 다시 실행</li>
</ul>
<blockquote>
<p>메모리 매핑 복구</p>
</blockquote>
<ul>
<li>프로세스 간 스위칭에서는 페이지 테이블과 같은 메모리 매핑 정보도 복구</li>
<li>이를 통해 새로운 프로세스는 자신의 고유한 메모리 공간을 사용할 수 있으며, 다른 프로세스의 메모리에 접근하지 않도록 격리</li>
</ul>
<ol start="4">
<li>캐시 및 CPU 파이프라인 플러싱</li>
</ol>
<blockquote>
<p>캐시 플러싱</p>
</blockquote>
<ul>
<li>프로세스 간 스위칭이 일어날 때는 이전 프로세스의 데이터가 CPU 캐시(Cache)에 남아 있을 수 있음</li>
<li>이 데이터를 제거하고 새로운 프로세스의 데이터를 캐시에 적재해야 하며, 이러한 과정은 캐시 미스(Cache Miss)를 유발, 오버헤드를 발생시키는 주요 요인</li>
</ul>
<blockquote>
<p>파이프라인 플러싱</p>
</blockquote>
<ul>
<li>CPU는 파이프라인 구조를 갖추고 있어 명령어를 단계별로 실행하는데, 스레드나 프로세스가 바뀌면 기존 명령어 파이프라인을 비우고 재구성되어 성능 저하의 원인이 됨</li>
</ul>
<ol start="5">
<li>작업 복귀</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.11.01 프로세스와 스레드]]></title>
            <link>https://velog.io/@pure_bver/2024.11.01-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</link>
            <guid>https://velog.io/@pure_bver/2024.11.01-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</guid>
            <pubDate>Fri, 01 Nov 2024 11:33:23 GMT</pubDate>
            <description><![CDATA[<h3 id="1-프로세스">1. 프로세스</h3>
<ul>
<li><p>프로세스는 컴퓨터에서 실행 중인 하나의 프로그램을 의미</p>
</li>
<li><p>프로그램이 메모리에서 독립적으로 실행되기 위해 할당된 하나의 작업 단위</p>
</li>
<li><p>각 프로세스에 고유한 메모리 공간과 자원(CPU, 메모리, 파일 등)을 할당하며, 서로 간의 메모리를 공유하지 않으므로 독립적</p>
</li>
</ul>
<h4 id="메모리-영역-프로세스의-메모리-구조">메모리 영역 (프로세스의 메모리 구조)</h4>
<ul>
<li><p>코드(Code) 영역: 프로그램의 코드(명령어)가 저장되는 메모리 공간, 일반적으로 읽기 전용</p>
</li>
<li><p>데이터(Data) 영역: 전역 변수나 정적 변수가 저장되는 영역, 프로그램이 시작할 때 할당되며 종료될 때 해제</p>
</li>
<li><p>힙(Heap) 영역: 프로세스가 동적으로 할당한 메모리 공간이 위치하는 곳, 힙은 런타임 중에 메모리를 할당하고 해제할 수 있으며, 프로그래머가 직접 관리</p>
</li>
<li><p>스택(Stack) 영역: 함수 호출 시 할당되는 메모리 공간, 지역 변수와 함수 호출 정보를 저장, 스택은 함수 호출이 끝나면 자동으로 해제되는 구조</p>
</li>
</ul>
<h3 id="2-스레드">2. 스레드</h3>
<ul>
<li><p>프로세스 내에서 실행되는 작업의 하위 단위</p>
</li>
<li><p>하나의 프로세스가 여러 개의 스레드를 가질 수 있음</p>
</li>
<li><p>프로세스 내의 메모리와 자원을 공유하기 때문에 스레드 간 통신이 빠름</p>
</li>
<li><p>같은 프로세스 내에서 공유 메모리에 접근할 수 있지만, 이를 동기화하지 않으면 경쟁 상태</p>
</li>
</ul>
<blockquote>
<p>공유 메모리: 같은 프로세스 내의 스레드는 코드, 데이터, 힙 영역을 공유, 동일한 전역 변수나 동적 메모리에 접근</p>
</blockquote>
<blockquote>
<p>독립 메모리: 각 스레드는 고유한 스택(Stack) 영역을 가짐, 스택에는 함수 호출 시 생성되는 지역 변수와 호출 정보가 저장</p>
</blockquote>
<blockquote>
<p>경쟁상태: 여러 스레드가 동시에 같은 자원(특히 메모리)에 접근하고 수정하려고 할 때 발생하는 문제 작업의 결과가 스레드의 실행 순서에 따라 달라질 수 있어, 예기치 못한 오류나 데이터 손상이 발생</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.10.31 트러블슈팅]]></title>
            <link>https://velog.io/@pure_bver/2024.10.30-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@pure_bver/2024.10.30-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</guid>
            <pubDate>Fri, 01 Nov 2024 01:00:24 GMT</pubDate>
            <description><![CDATA[<h3 id="트러블1">트러블1</h3>
<ul>
<li>Unity 클라이언트 (강제)종료 시점에 오류 발생</li>
</ul>
<h4 id="문제-발생-조건">문제 발생 조건</h4>
<ul>
<li>Unity 클라이언트와 JavaScript TCP 서버 간 연결에서, ping-pong 메시징 활성화 시 클라이언트 (강제)종료 시점에 오류 발생</li>
</ul>
<h4 id="원인-분석">원인 분석</h4>
<ul>
<li>mac에서 실행 시 에러가 안나는걸 확인</li>
</ul>
<h4 id="결과">결과</h4>
<ul>
<li>윈도우 에서만 나오는 오류로 판정</li>
<li>진행상 문제가 없어보였으나 추가적인 오류가 발생 =&gt; 트러블2</li>
</ul>
<hr>
<h3 id="트러블2">트러블2</h3>
<ul>
<li>에러 처리 부분에서 removeUser를 해서 유저 데이터를 빼내는 메소드가 작동을 안함</li>
</ul>
<h4 id="문제-발생-조건-1">문제 발생 조건</h4>
<ul>
<li>트러블1에서 파생되었다 시피 ping-pong 메시징 활성화 강제종료 시 에만 발생</li>
</ul>
<h4 id="원인-분석-1">원인 분석</h4>
<ol>
<li><p>소켓이 끊기며 발생한 오류라고 생각해 socket데이터가 바뀐건가? 싶어 비교분석
 =&gt; socket데이터에 변화는 없음</p>
</li>
<li><p>socket데이터에 변화가 없으므로 socket관련 메서드는 정상작동 해야하지만 작동 하지 않음</p>
</li>
<li><p>log를 찍어보며 확인한 결과 에러 처리 마지막에 클라이언트에 socket.write해서 보내는게 문제가됨</p>
</li>
<li><p>이미 연결이 끊겼는데 socket.write를 작동해서 오류가 발생된것</p>
</li>
</ol>
<h4 id="해결방안">해결방안</h4>
<ul>
<li>연결이 끊겼을 경우 발생하는 에러코드가 ECONNRESET인 것을 확인</li>
<li>에러에 대해서 따로 처리를 해 socket.write 작동시키지 않음으로 해결</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.10.30 레이턴시와 핑퐁, 전송 주기]]></title>
            <link>https://velog.io/@pure_bver/2024.10.30-%EB%A0%88%EC%9D%B4%ED%84%B4%EC%8B%9C%EC%99%80-%ED%95%91%ED%90%81-%EC%A0%84%EC%86%A1-%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@pure_bver/2024.10.30-%EB%A0%88%EC%9D%B4%ED%84%B4%EC%8B%9C%EC%99%80-%ED%95%91%ED%90%81-%EC%A0%84%EC%86%A1-%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Wed, 30 Oct 2024 12:20:15 GMT</pubDate>
            <description><![CDATA[<h1 id="레이턴시latency와-핑퐁ping-pong">레이턴시(Latency)와 핑퐁(Ping-Pong)</h1>
<h2 id="레이턴시latency">레이턴시(Latency)</h2>
<ul>
<li><p><strong>레이턴시</strong>는 네트워크 지연 시간</p>
</li>
<li><p>데이터가 <strong>출발지</strong>에서 <strong>목적지</strong>까지 도달하는 데 걸리는 시간</p>
</li>
<li><p>주로 <strong>밀리초(ms)</strong> 단위로 측정</p>
</li>
<li><p>네트워크 응답 속도를 평가하는 지표</p>
</li>
<li><p><strong>레이턴시가 높을수록 사용자 경험이 나빠지며</strong>, 특히 게임, 스트리밍, 화상 회의 등 실시간 서비스에 큰 영향</p>
</li>
</ul>
<hr>
<h2 id="핑퐁ping-pong이란">핑퐁(Ping-Pong)이란?</h2>
<ul>
<li><p><strong>Ping</strong>은 네트워크 연결 상태를 확인하기 위해 패킷을 보내는 도구</p>
</li>
<li><p><strong>핑퐁(Ping-Pong)</strong>은 서버와 클라이언트가 <strong>반복적으로 왕복 통신을 수행하는 것</strong>을 의미합니다.</p>
</li>
<li><p>즉 핑퐁은 서버와 클라이언트간 <strong>연결이 정상적으로 유지되고 있는지</strong> 확인하기 위해 사용.</p>
</li>
<li><p>일정 주기마다 핑을 보내 응답이 있는지 확인하는 작업</p>
</li>
<li><p>일반적으로 5~30초 인터벌로 사용</p>
<blockquote>
<p>FPS(1인칭 슈팅 게임): 짧은 핑 주기 (3<del>5초)
MOBA(예: 롤, 도타2): 중간 정도의 주기 (10</del>15초)
RPG/MMORPG: 긴 핑 주기 (15~30초)</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="전송-주기">전송 주기</h2>
<ul>
<li>데이터 전송을 임의로 지연시켜 발송하는 것으로 추측항법을 시험할 때 전송 주기를 지연시켜 시험해 본다던지 할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.10.29 삼각함수, 역삼각함수]]></title>
            <link>https://velog.io/@pure_bver/2024.10.29-%EC%82%BC%EA%B0%81%ED%95%A8%EC%88%98-%EC%97%AD%EC%82%BC%EA%B0%81%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@pure_bver/2024.10.29-%EC%82%BC%EA%B0%81%ED%95%A8%EC%88%98-%EC%97%AD%EC%82%BC%EA%B0%81%ED%95%A8%EC%88%98</guid>
            <pubDate>Mon, 28 Oct 2024 17:33:16 GMT</pubDate>
            <description><![CDATA[<h3 id="삼각함수">삼각함수</h3>
<p>정의: (직각)삼각형의 각도로 양변의 길이를 구하는것</p>
<h4 id="삼각비">삼각비</h4>
<ol>
<li>$sinθ = \frac{높이} {빗변{}}$</li>
</ol>
<ul>
<li>주기가 2π이며, -1과 1 사이에서 반복
<img src="https://velog.velcdn.com/images/pure_bver/post/e6df7d80-237c-4cde-8280-8148eb50d365/image.png" alt=""></li>
</ul>
<ol start="2">
<li>$cosθ = \frac{밑변} {빗변{}}$</li>
</ol>
<ul>
<li>주기가 2π이며, 사인 함수와 비슷하게 -1과 1 사이에서 반복되지만, 위상이 π/2만큼 차이
<img src="https://velog.velcdn.com/images/pure_bver/post/691b3dba-c32c-494c-9d73-952d4504a530/image.png" alt=""></li>
</ul>
<ol start="3">
<li>$tanθ = \frac{높이} {밑변{}}$</li>
</ol>
<ul>
<li>주기가 π이며, 특정 각도(예: π/2, 3π/2)에서 비연속적인 극점이 발생
<img src="https://velog.velcdn.com/images/pure_bver/post/d9a8fb6f-8df5-4114-9617-b44622d1e95b/image.png" alt=""></li>
</ul>
<table>
<thead>
<tr>
<th align="center">각도</th>
<th align="center">sinθ</th>
<th align="center">cosθ</th>
<th align="center">tanθ</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0°</td>
<td align="center">0</td>
<td align="center">1</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">30°</td>
<td align="center">$\frac12$</td>
<td align="center">$\frac{\sqrt3}2$</td>
<td align="center">$\frac1{\sqrt3}$</td>
</tr>
<tr>
<td align="center">45°</td>
<td align="center">$\frac{\sqrt2}2$</td>
<td align="center">$\frac{\sqrt2}2$</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">60°</td>
<td align="center">$\frac{\sqrt3}2$</td>
<td align="center">$\frac12$</td>
<td align="center">$\sqrt3$</td>
</tr>
<tr>
<td align="center">90°</td>
<td align="center">1</td>
<td align="center">0</td>
<td align="center">∞</td>
</tr>
</tbody></table>
<blockquote>
<p>사진1
<img src="https://velog.velcdn.com/images/pure_bver/post/fdd0df8e-0877-4f61-ba68-95cc40407182/image.png" alt=""></p>
</blockquote>
<h4 id="사인법칙사진1-참조">사인법칙(사진1 참조)</h4>
<p>$\frac {a} {sinA} = \frac {b} {sinB} = \frac {c} {sinC}$</p>
<h4 id="코사인법칙사진1-참조">코사인법칙(사진1 참조)</h4>
<p>$c^2 = a^2 + b^2 - 2ab cos C$</p>
<hr>
<h3 id="역삼각함수">역삼각함수</h3>
<p>정의: (직각)삼각형의 변의 길이로 각도를 구하는 것</p>
<ol>
<li>$θ = arcsin\frac{높이} {빗변{}}$</li>
</ol>
<ul>
<li>정의역은 [−1,1]이며, 출력값(각도)은 −π/2에서 π/2 사이
<img src="https://velog.velcdn.com/images/pure_bver/post/2517738e-60b5-4193-ab8f-41b37515f6a3/image.png" alt=""></li>
</ul>
<ol start="2">
<li>$θ = arccos\frac{밑변} {빗변{}}$</li>
</ol>
<ul>
<li>정의역은 [−1,1]이고, 출력값은 0에서 π 사이
<img src="https://velog.velcdn.com/images/pure_bver/post/0a7ff258-d1b7-4dd2-8d78-b371692c7cef/image.png" alt=""></li>
</ul>
<ol start="3">
<li>$θ = arctan\frac{높이} {밑변{}}$</li>
</ol>
<ul>
<li>정의역은 모든 실수이며, 출력값은 −π/2에서 π/2 사이로 제한
<img src="https://velog.velcdn.com/images/pure_bver/post/9cf8af7f-9f53-4d21-853b-2d9f558e02bb/image.png" alt=""></li>
</ul>
<hr>
<h3 id="어디서-사용될까">어디서 사용될까?</h3>
<h4 id="1-2d-캐릭터-회전과-방향-계산">1. 2D 캐릭터 회전과 방향 계산</h4>
<ul>
<li>2D 슈팅 게임에서 플레이어가 마우스로 클릭한 방향을 계산
$θ = arctan2(Δy,Δx)$</li>
<li>Δy와 Δx는 캐릭터와 마우스 위치의 y, x 차이</li>
<li>(0, 0)에서 (Δx,Δy)까지 선을 그어서 나온 각도가 θ
$라디안=θ×\frac{π}{180}$
ex: 90도 = $\frac{π}{2}$</li>
</ul>
<h4 id="2-투사체의-궤적-계산">2. 투사체의 궤적 계산</h4>
<ul>
<li>포탄이 발사되는 포물선(앵그리버드)
$x(t) = v * cos(θ) * t$
$y(t) = v * sin(θ) * t - \frac{1}{2}gt^2$
v = 초기속도, g = 중력, t = 시간
ps: 공기저항은 계산 안했음</li>
</ul>
<h4 id="3-3d-회전">3. 3D 회전</h4>
<ul>
<li>3D에서 특정 점의 좌표값을 계산
$x = r * cos(θ)<em>cos(ϕ)$
$y = r * sin(ϕ)$
$z = r * sin(θ)</em>cos(ϕ)$
r: 반지름, θ: 수평 회전 각도, ϕ: 수직 회전 각도</li>
</ul>
<h4 id="4-거리계산">4. 거리계산</h4>
<ul>
<li>피타고라스!
$a^2 + b^2 = c^2$
$Δx^2 + Δy^2 = 거리$</li>
</ul>
<p>등이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.10.25 삼각형 달팽이]]></title>
            <link>https://velog.io/@pure_bver/2024.10.25-%EC%82%BC%EA%B0%81%ED%98%95-%EB%8B%AC%ED%8C%BD%EC%9D%B4</link>
            <guid>https://velog.io/@pure_bver/2024.10.25-%EC%82%BC%EA%B0%81%ED%98%95-%EB%8B%AC%ED%8C%BD%EC%9D%B4</guid>
            <pubDate>Fri, 25 Oct 2024 14:36:28 GMT</pubDate>
            <description><![CDATA[<p>주어진 자연수 n에 따라 삼각형 형태로 숫자를 채워 넣는 문제</p>
<p>답안:</p>
<pre><code class="language-js">function solution(n) {
    //예외
    if(n === 1){
        return [1]
    }
    if(n === 2){
        return [1, 2, 3]
    }

    //결과 저장공간
    const coordinate = new Array(n).fill(0).map((a, b) =&gt; new Array(b + 1));

    //현재 위치
    const location = [0, 0];

    //다음 위치 더해줄 값
    const addLocation = [[1, 0], [0, 1], [-1, -1]];

    //다음 위치 인덱스값(방향)
    let direction = 0;

    //위치의 값
    let num = 1;

    while(true){
        const [y, x] = location;

          //현재 위치에 값이 있을경우 === 완전히 채워진 경우
        if(coordinate[y][x]){
            break;
        }

          //현재 위치에 값 저장
        coordinate[y][x] = num;

          //다음값
        num++;

          //다음위치
        const [yPlus, xPlus] = addLocation[direction];
        location[0] = y + yPlus;
        location[1] = x + xPlus;

          //다음 위치가 경계거나 값이 있을 경우 방향 변경
        if(location[0] + yPlus === n || location[1] + xPlus === n || coordinate[location[0] + yPlus][location[1] + xPlus]){
            direction = (direction + 1) % 3;
        }
    }

      //2차원 배열을 1차월 배열로 변환
    return coordinate.flat()
}</code></pre>
<ul>
<li>한 방향으로 쭉 이동하다가 막히면 다음 방향으로 전환하는 알고리즘</li>
<li>위 알고리즘을 생각하는게 90%</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.10.24 Protocol Buffers]]></title>
            <link>https://velog.io/@pure_bver/2024.10.24-Protocol-Buffers</link>
            <guid>https://velog.io/@pure_bver/2024.10.24-Protocol-Buffers</guid>
            <pubDate>Thu, 24 Oct 2024 14:49:47 GMT</pubDate>
            <description><![CDATA[<h3 id="protocol-buffers">Protocol Buffers</h3>
<ul>
<li>Protocol Buffers(프로토콜 버퍼)는 데이터 직렬화 포맷으로, XML이나 JSON보다 더 빠르고 가벼운 직렬화 방식</li>
<li>proto 파일은 데이터 구조를 정의하는 템플릿</li>
<li>서버와 클라이언트가 통신할 때 교환하는 메시지나 API의 인터페이스를 정의<blockquote>
<p>기본구조</p>
<pre><code>syntax = &quot;proto3&quot;;
</code></pre></blockquote>
package mypackage;<blockquote>
</blockquote>
message User {
int32 id = 1;         // 버퍼 순서 1: 사용자 ID
string name = 2;       // 버퍼 순서 2: 사용자 이름
bool is_active = 3;    // 버퍼 순서 3: 활성 상태
}<blockquote>
<pre><code></code></pre></blockquote>
</li>
</ul>
<h4 id="protocol-buffers의-주요-개념">Protocol Buffers의 주요 개념</h4>
<ul>
<li><p>Syntax 버전:
proto2와 proto3 두 가지 버전이 있으며, 일반적으로 최신 버전인 proto3를 사용</p>
</li>
<li><p>메시지(Message):
데이터의 구조를 정의하며, 여러 필드가 포함
각 필드에는 타입, 이름, 필드 번호가 필요</p>
</li>
<li><p>서비스(Service):
gRPC와 함께 사용하여 원격 프로시저 호출(RPC)을 정의
rpc 키워드로 요청(Request)과 응답(Response)을 정의</p>
<blockquote>
<p>=&gt; 이부분은 아직 사용을 안해봄.</p>
</blockquote>
</li>
<li><p>패키지(Package):
네임스페이스와 유사하게 동일한 패키지 이름을 사용하여 이름 충돌을 방지</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024.10.23 new.target, super(), bind()]]></title>
            <link>https://velog.io/@pure_bver/2024.10.23-new.target-super-bind</link>
            <guid>https://velog.io/@pure_bver/2024.10.23-new.target-super-bind</guid>
            <pubDate>Wed, 23 Oct 2024 12:39:28 GMT</pubDate>
            <description><![CDATA[<h4 id="newtarget">new.target</h4>
<ul>
<li><p>JavaScript에서 생성자 함수나 클래스를 new 키워드로 호출할 때 사용</p>
</li>
<li><p>주로 생성자 함수가 올바르게 new를 통해 호출되었는지 확인</p>
</li>
</ul>
<p>ex1: new 없이 생성자 함수 호출 방지</p>
<pre><code class="language-js">function Person(name) {
  if (!new.target) {
    throw new Error(&#39;Person은 new 키워드로 호출해야 합니다.&#39;);
  }
  this.name = name;
}

const p1 = new Person(&#39;Alice&#39;); // 정상 작동
const p2 = Person(&#39;Bob&#39;);       // Error: Person은 new 키워드로 호출해야 합니다.</code></pre>
<p>ex2: 직접 인스턴스화 금지</p>
<pre><code class="language-js">class Animal {
  constructor() {
    if (new.target === Animal) {
      throw new Error(&#39;Animal 클래스는 직접 인스턴스화할 수 없습니다.&#39;);
    }
  }
}

class Dog extends Animal {
  constructor() {
    super(); // 반드시 부모 클래스 생성자 호출
  }
}

const a = new Animal(); // Error: Animal 클래스는 직접 인스턴스화할 수 없습니다.
const d = new Dog();    // 정상 작동</code></pre>
<hr>
<h4 id="super">super()</h4>
<ul>
<li>자식 클래스에서 부모클래스의 생성자를 호출함</li>
<li>자식 클래스의 생성자에서 첫 줄에 호출</li>
<li>만약 부모 클래스가 생성자를 가지고 있을 때, super()를 호출하지 않으면 오류가 발생</li>
</ul>
<p>ex1: 부모 클래스 호출</p>
<pre><code class="language-js">class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name}이(가) 소리를 냅니다.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 부모 클래스의 생성자를 호출하여 name을 초기화
    this.breed = breed;
  }

  speak() {
    console.log(`${this.breed}인 ${this.name}이(가) 멍멍 짖습니다.`);
  }
}

const myDog = new Dog(&#39;바둑이&#39;, &#39;진돗개&#39;);
myDog.speak();  // &quot;진돗개인 바둑이이(가) 멍멍 짖습니다.&quot;</code></pre>
<p>ex2: 부모 클래스 메서드 호출</p>
<pre><code class="language-js">class Animal {
  speak() {
    console.log(&#39;동물이 소리를 냅니다.&#39;);
  }
}

class Cat extends Animal {
  speak() {
    super.speak(); // 부모 클래스의 speak() 호출
    console.log(&#39;고양이가 야옹합니다.&#39;);
  }
}

const myCat = new Cat();
myCat.speak();
// 출력:
// 동물이 소리를 냅니다.
// 고양이가 야옹합니다.</code></pre>
<hr>
<h4 id="bind">bind()</h4>
<ul>
<li>this를 지정해준다고 보면 된다.</li>
<li>자바스크립트에서는 클래스로 생성된 객체의 내장 메서드를 콜백함수로 전달하면 객체의 연결이 끊어져 this가 유실된다.</li>
<li>그렇다고 user.ping()처럼 즉시실행하면 반환값만 가져온다</li>
<li>this를 지정해주는 bind를 사용해 연결을 유지한다 &#39;user.ping.bind(user)&#39;</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>