<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>v_rien.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 24 Apr 2025 12:23:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>v_rien.log</title>
            <url>https://velog.velcdn.com/images/v_rien/profile/358bc7c4-d10a-48ec-a23f-3a9fcd835aeb/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. v_rien.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/v_rien" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Redis 실습]]></title>
            <link>https://velog.io/@v_rien/Redis-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@v_rien/Redis-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 24 Apr 2025 12:23:23 GMT</pubDate>
            <description><![CDATA[<h1 id="redis">Redis</h1>
<p><strong>Redis</strong>는 Remote Dictionary Server의 약자로, 오픈 소스 기반의 NoSQL이다. 키-값(Key-Value) 구조를 사용하여 데이터를 저장하고, 데이터베이스, 캐시, 메시지 브로커 등 다양한 용도로 활용이 가능하다.
또한 Redis는 <strong>In-Memory</strong> 데이터 구조를 가진 저장소로 RAM에 데이터를 저장할 시 메모리 내부에서 처리가 되므로 데이터를 저장 및 조회할 때 하드디스크를 오고 가는 과정을 거치지 않아도 되서 <strong>속도가 빠르다</strong>.
하지만 서버의 메모리 용량을 초과하는 데이터를 처리 시, RAM의 특성인 <strong>휘발성</strong>에 따라 데이터가 유실될 수 있다.</p>
<h2 id="특징">특징</h2>
<p>위의 설명과 같이 In-Memory 데이터 저장방식으로 디스크 기반의 데이터베이스보다 훨씬 <strong>빠른 속도</strong>로 데이터 접근 및 처리가 가능하다.
또한 문자열, 리스트, 집합, 해시, 비트맵 등 <strong>다양한 데이터 구조를 지원</strong>해 다양한 애플리케이션 요구사항에 유연하게 대응 할 수 있다.
<strong>오픈 소스</strong>기반이므로 다양한 개발 환경에 적용이 가능하고, <strong>설치 및 운영이 간편</strong>해 소규모 서버에서도 쉽게 활용 가능하다.
다양한 데이터 구조를 지원하는 만큼 In-Memory 데이터 저장소로서 데이터베이스, 캐시 등 <strong>다양한 용도로 활용</strong>할 수 있다.</p>
<h2 id="주요-사용-사례">주요 사용 사례</h2>
<ul>
<li><strong>웹 애플리케이션 캐싱</strong><ul>
<li>웹 페이지, 사용자 데이터, 세션 데이터 등 자주 접근하는 데이터를 캐싱하여 웹 애플리케이션 성능을 향상시킬 수 있다.</li>
</ul>
</li>
<li><strong>메시지 브로커</strong><ul>
<li>시스템 간의 비동기 통신을 위해 메시지를 주고받는 역할을 수행한다.</li>
</ul>
</li>
<li><strong>세션 관리</strong><ul>
<li>웹 애플리케이션의 세션 정보를 저장하고 관리한다.</li>
</ul>
</li>
<li><strong>순위, 카운트 등 스코어 관리</strong><ul>
<li>게임, 소셜 네트워크 등에서 순위, 카운트 등을 관리한다.</li>
</ul>
</li>
<li><strong>실시간 데이터 분석</strong><ul>
<li>실시간 데이터 스트림을 처리하고 분석하는 데 활용된다. </li>
</ul>
</li>
</ul>
<h2 id="redis-시작">Redis 시작</h2>
<h3 id="설치-및-사용법">설치 및 사용법</h3>
<pre><code>npm install redis</code></pre><p>다른 라이브러리를 설치하듯 터미널에서 redis를 설치해서 사용할 수 있다.</p>
<p>라이브러리에 추가만 하고 바로 사용했다가는 redis자체를 설치하여 사용한 것이 아니기 때문에 오류가 뜰 것이다.
<a href="https://github.com/microsoftarchive/redis/releases">https://github.com/microsoftarchive/redis/releases</a>
위 사이트에 들어가서 redis를 다운 받아서 사용할 수 있다.</p>
<ul>
<li>index.js<pre><code>const redis = require(&#39;redis&#39;);
</code></pre></li>
</ul>
<p>const client = redis.createClient(); // localhost:6379 기본 연결</p>
<p>client.on(&#39;error&#39;, (err) =&gt; {
  console.error(&#39;Redis 오류:&#39;, err);
});</p>
<p>client.connect().then(async () =&gt; {
  await client.set(&#39;message&#39;, &#39;Hello, Redis!&#39;);
  const value = await client.get(&#39;message&#39;);
  console.log(&#39;저장된 값:&#39;, value);
  await client.quit();
});</p>
<pre><code>다운로드까지 모두 마치고 위의 코드를 작성 후 실행해보면
![](https://velog.velcdn.com/images/v_rien/post/b73bd173-fd9a-4b4d-a57e-3b507eaf5f76/image.PNG)
사진과 같은 결과를 얻을 수 있다.

이외에도 redis의 다양한 데이터 구조를 지원한다는 특징으로 Redis를 **게임 프로그래밍에서 활용**하는 여러 예제를 만들어 볼 수 있다.
#### 문자열(String)
플레이어 상태, 현재 스테이지 등 단일 값을 저장할 때 사용할 수 있다.

**예제 코드**</code></pre><p>module.exports = async (client) =&gt; {
    await client.set(&#39;username&#39;, &#39;gptuser&#39;);
    const username = await client.get(&#39;username&#39;);
    console.log(&#39;👤 [String] username:&#39;, username);
  };</p>
<pre><code>#### 리스트(List)
최근 채팅 로그, 아이템 획득 히스토리 등의 저장에 사용할 수 있다.

**예제 코드**</code></pre><p>module.exports = async (client) =&gt; {
    await client.rPush(&#39;messages&#39;, &#39;안녕&#39;, &#39;반가워&#39;);
    const messages = await client.lRange(&#39;messages&#39;, 0, -1);
    console.log(&#39;📩 [List] messages:&#39;, messages);
    const msg = await client.lPop(&#39;messages&#39;);
    console.log(&#39;📤 [List] pop:&#39;, msg);
  };</p>
<pre><code>#### 해시(Hash)
플레이어 정보 및 캐릭터 능력치 저장에 사용할 수 있다.

**예제 코드**</code></pre><p>module.exports = async (client) =&gt; {
    await client.hSet(&#39;player:1001&#39;, [
      [&#39;nickname&#39;, &#39;dragonSlayer&#39;],
      [&#39;level&#39;, &#39;15&#39;],
      [&#39;hp&#39;, &#39;1200&#39;],
    ]);
    const player = await client.hGetAll(&#39;player:1001&#39;);
    console.log(&#39;🧙 [Hash] player:1001:&#39;, player);
  };</p>
<pre><code>#### 셋(Set)
유저가 획득한 고유 업적 목록 등을 저장할 때 사용할 수 있다.

**예제 코드**</code></pre><p>module.exports = async (client) =&gt; {
    await client.sAdd(&#39;tags&#39;, &#39;nodejs&#39;, &#39;redis&#39;, &#39;backend&#39;);
    const tags = await client.sMembers(&#39;tags&#39;);
    console.log(&#39;🏷️ [Set] tags:&#39;, tags);
  };</p>
<pre><code>#### 정렬 셋(Sorted Set)
랭킹 시스템에 사용할 수 있다.

**예제 코드**</code></pre><p>module.exports = async (client) =&gt; {
    await client.zAdd(&#39;leaderboard&#39;, [
      { score: 5000, value: &#39;player:alpha&#39; },
      { score: 7200, value: &#39;player:beta&#39; }
    ]);
    const topPlayers = await client.zRevRange(&#39;leaderboard&#39;, 0, -1, { WITHSCORES: true });
    console.log(&#39;🏆 [SortedSet] leaderboard:&#39;, topPlayers);
  };</p>
<pre><code>#### Pub/sub
실시간 채팅이나 이벤트 방송 등에서 사용할 수 있다.

**예제 코드**</code></pre><p>module.exports = async (client) =&gt; {
    const subscriber = client.duplicate();
    await subscriber.connect();</p>
<pre><code>await subscriber.subscribe(&#39;chat:room1&#39;, (message) =&gt; {
  console.log(&#39;💬 [PubSub] 수신:&#39;, message);
});

await client.publish(&#39;chat:room1&#39;, &#39;🔥 [SYSTEM] 보스 등장!&#39;);</code></pre><p>  };</p>
<pre><code>#### TTL
버프 지속시간 및 아이템 한정 효과 등에서 사용할 수 있다.

**예제 코드**</code></pre><p>module.exports = async (client) =&gt; {
    await client.setEx(&#39;buff:shield:player:1001&#39;, 30, &#39;active&#39;);
    const ttl = await client.ttl(&#39;buff:shield:player:1001&#39;);
    console.log(&#39;⏳ [TTL] shield 버프 남은 시간:&#39;, ttl, &#39;초&#39;);
  };
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DATABASE(RDBMS, NoSQL)]]></title>
            <link>https://velog.io/@v_rien/DATABASERDBMS-NoSQL</link>
            <guid>https://velog.io/@v_rien/DATABASERDBMS-NoSQL</guid>
            <pubDate>Tue, 11 Mar 2025 11:52:22 GMT</pubDate>
            <description><![CDATA[<p>내일배움캠프에서 <strong>MongoDB, MySQL</strong>이 두가지의 데이터베이스를 배우고 사용해봤다. 그리고 <strong>Redis</strong>라는 데이터베이스라는게 있다는 것 또한 들었다.</p>
<p>여기서 <strong>MySQL</strong>은 <strong>RDBMS</strong>(Relational DataBase Management System)의 한 종류이고, <strong>MongoDB</strong>와 <strong>Redis</strong>의 경우에는 모델은 서로 다르지만 둘 다 <strong>NoSQL</strong>(Not Only SQL)의 종류 중 하나이다.</p>
<p>이번 포스팅에서는 <strong>RDBMS</strong>와 <strong>NoSQL</strong>에 대해 알아볼 예정이다.</p>
<h1 id="rdbmsrelational-database-management-system">RDBMS(Relational DataBase Management System)</h1>
<ul>
<li><p><strong>RDBMS</strong>(Relational DataBase Management System)는 단어 뜻 그대로 관계형 데이터베이스 관리 시스템으로, 데이터를 <strong>테이블 형식으로 저장</strong>한다. 각 테이블(Table)은 행(Row)과 열(Column)로 구성되어있고, <strong>SQL을 사용해서 데이터를 관리</strong>한다.
이때, 각 <strong>행</strong>은 데이터 레코드를 나타내고, <strong>열</strong>은 특정 데이터 유형을 가지고 있다.</p>
<img src="https://www.fun-coding.org/00_Images/rdbms_term1.png" width="500" height="300">
</li>
<li><p>RDBMS의 가장 중요한 특징으로는 <strong>ACID</strong> 특성이 있다. ACID는 원자성, 일관성, 격리성, 지속성으로 정의된다.</p>
<blockquote>
<ul>
<li>원자성(Atomicity) : 트랜잭션은 완전히 수행되거나 전혀 수행되지 않아야 한다.
=&gt; 트랜잭션은 <strong>부분적으로만 완료되지 않는다.</strong></li>
</ul>
</blockquote>
<ul>
<li>일관성(Consistency) : 트랜잭션 전후에 데이터베이스는 <strong>일관된 상태여야 한다.</strong></li>
<li>격리성(Isolation) : 여러 트랜잭션이 <strong>동시에 실행</strong>될 때, 각 트랜잭션은 다른 트랜잭션과 격리되어야 한다.
=&gt; 다른 트랜잭션에 의해 수정된 데이터에 대해 <strong>접근하지 않는다.</strong></li>
<li>지속성(Durability) : 트랜잭션이 성공적으로 <strong>완료</strong>된 경우, <strong>결과는 영구적으로 저장되어야 한다.</strong>
<br><br></li>
<li>트랜잭션(Transaction) : 데이터 베이스에서 수행되는 <strong>단일 논리적 작업 단위</strong>.
=&gt; 하나 이상의 데이터베이스 작업이 모두 성공하거나 실패할 때 까지 <strong>모두 적용 혹은 모두 롤백되어야 하는 작업 그룹</strong>.</li>
</ul>
</li>
<li><p>RDBMS는 위의 <strong>ACID</strong> 특성을 기본으로 구성하기 때문에 <strong>데이터의 무결성과 정합성을 보장한다</strong>.
또한 외래 키(foreign key)를 사용하여 테이블 간 Join이 가능해 <strong>복잡한 쿼리를 지원</strong>하고, <strong>데이터 간의 관계 표현을 쉽게 해준다</strong>.</p>
<img src="https://www.fun-coding.org/00_Images/rdbms_keys.png" width="700" height="500">
<br></li>
<li><p>하지만 여러 테이블 간의 복잡한 Join의 경우 <strong>대규모 데이터 세트에서 성능에 관한 문제를 일으킬 수 있다.</strong>
=&gt; 복잡한 쿼리는 처리 시간이 오래 걸리고 시스템 성능을 저하시킨다.
```
sql</p>
</li>
<li><ul>
<li>예제: 다중 테이블 조인
SELECT orders.id, customers.name, products.title
FROM orders
JOIN customers ON orders.customer_id = customers.id
JOIN products ON orders.product_id = products.id;<pre><code>&gt; 위의 예제는 주문, 고객, 제품 테이블 간의 관계를 가져오는 작업을 수행하는데,
이때 데이터세트가 클 경우, 위와 같은 Join은 성능을 저하시킬 수 있다.(쿼리 최적화 필요.)</code></pre></li>
</ul>
</li>
<li><p>성능 향상을 위해 <strong>Scale-up만 지원</strong>하므로 <strong>처리 비용이 기하급수적으로 커질 수 있다</strong>.(Scale-out 불가능)
또한, 정합성을 보장한다 = 정해진 스키마에 따라 데이터를 저장한다는 뜻이므로 <strong>데이터가 유연하지 못하다.</strong>
=&gt; 나중에 스키마가 변경될 경우 번거롭고 어려울 수 있다.</p>
<blockquote>
<ul>
<li>Scale-up</li>
</ul>
</blockquote>
<ul>
<li>기존의 서버를 <strong>높은 사양으로 업그레이드</strong> 하는 것.(CPU 업그레이드, RAM 추가 등)</li>
<li>하나의 서버의 능력을 증강하므로 <strong>수직 스케일링</strong>(vertical scaling)이라고도 함.
<img src="https://velog.velcdn.com/images/wnguswn7/post/89882eea-5c93-415b-a727-61af776683db/image.PNG" width="500" height="300"><br><ul>
<li>Scale-out</li>
</ul>
</li>
<li><strong>장비를 추가</strong>해 서버를 확장.(하나의 장비가 하던 일을 여러 장비가 나눠서 처리.)</li>
<li>서버를 추가로 확장하기 때문에 <strong>수평 스케일링</strong>(horizontal scaling)이라고도 함.</li>
<li>비슷한 사양의 서버를 추가로 연결해 처리할 수 있는 <strong>데이터의 용량 증가</strong>뿐만 아니라 기존 서버의 부하를 분담해 <strong>성능도 향상</strong>.<img src="https://velog.velcdn.com/images/wnguswn7/post/a8b12c47-5970-4d49-ac7b-e594b540a0aa/image.PNG" width="500" height="500">

</li>
</ul>
</li>
</ul>
<br>

<ul>
<li>이러한 특징과 장단점으로 인해 RDBMS는 <strong>데이터 구조가 명확하며 데이터의 변경 가능성이 낮은 경우</strong>나 <strong>데이터 변경이 자주 이루어지는 시스템</strong>에서 사용할 수 있다.</li>
</ul>
<hr>
<h2 id="rdbms-정리">RDBMS 정리</h2>
<h3 id="특징">특징</h3>
<ul>
<li><strong>ACID</strong> 특성</li>
<li>SQL을 이용해 관리 및 접근.</li>
<li><strong>행과 열을 가지는 테이블 형식</strong>으로 데이터를 저장.<h3 id="장점">장점</h3>
</li>
<li>데이터 <strong>무결성</strong> 보장 </li>
<li>명확한 스키마 정의로 <strong>정합성</strong> 보장. </li>
<li>데이터의 분류, 정렬, 탐색 속도 빠름.<h3 id="단점">단점</h3>
</li>
<li>대규모 데이터 세트에서 성능에 관한 문제 발생. </li>
<li>수평 확장이 어려워 <strong>처리 비용이 커질 수 있음</strong>. </li>
<li>데이터의 <strong>유연성 부족</strong>으로 스키마가 변경될 경우 번거롭고 어려워짐.</li>
</ul>
<hr>
<br>

<p>☞ <strong>RDBMS</strong>는 Join과 정해진 규격으로 인한 한계점이 존재했다. 이러한 한계를 극복하기 위해 만들어진 새로운 형태의 데이터베이스가 바로 <strong>NoSQL</strong>이다.</p>
<h1 id="nosqlnot-only-sql">NoSQL(Not only SQL)</h1>
<ul>
<li><p><strong>NoSQL</strong>(Not only SQL)은 SQL을 사용하지 않는다는 뜻이 아니라 SQL 뿐만 아니라 <strong>다른 여러 유형</strong>의 데이터 베이스를 사용한다는 뜻이다.(비관계형 데이터베이스)</p>
</li>
<li><p><em>대량의 분산된 비정형 데이터를 저장하고 조회하는데 특화*</em>된 데이터베이스로 스키마 없이 사용하거나 느슨한 스키마를 제공한다.</p>
</li>
<li><p>RDBMS는 데이터 간의 관계를 외래 키(foreign key)로 정의하고 Join 연산을 수행하는데 반해 NoSQL은 Key-Value의 형태로 저장되기 때문에 Join 연산이 불가능하다. 즉, <strong>데이터 간의 관계를 정의하지 않는다.</strong>
또한 <strong>분산형 구조로 설계되어</strong> 여러 곳의 서버에 데이터를 분산 저장해 특정 서버에 장애가 발생해도 데이터가 유실되거나 서비스가 중지되지 않도록 한다.</p>
</li>
<li><p>데이터가 유연하고 자유롭기 때문에 <strong>언제든 저장된 데이터 조정과 새로운 필드 추가</strong>가 가능하다.
또한 RDBMS는 성능 향상에 Scale-up만 지원했던 것에 비해 Scale-out까지 모두 가능하므로 <strong>데이터 분산이 용이</strong>하고, <strong>대용량의 데이터를 저장</strong>할 수 있다.</p>
</li>
<li><p>하지만 데이터가 유연하다는 것은 <strong>데이터의 일관성이 존재하지 않고</strong>, <strong>데이터 구조 결정이 어렵다</strong>는 말이 된다.
또, 데이터가 <strong>중복 발생이 가능</strong>하기 때문에 데이터를 변경하려면 <strong>모든 컬렉션에서 데이터를 변경</strong>해줘야 한다.</p>
</li>
<li><p>이러한 특징과 장단점으로 인해 NoSQL은 <strong>데이터의 수정이 많이 이루어지는 시스템</strong>이나 <strong>다량의 데이터를 저장해야 할 경우</strong>에 사용할 수 있다.</p>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>NoSQL 모델 종류<br>
- Key-Value DB
  - **Key-Value 방식**으로 데이터 저장.
  - Key값은 **모든 데이터 타입을 수용**할 수 있고, 중복되지 않는 **유니크한 값**.
  - 메모리 기반으로 **빠르게 데이터를 읽어올 수 있음**.
  ※ ex) **Redis**, Riak, Oracle Berkely, AWS DynamoDB 등
  --------------------------
  <br>
- Document DB
  - **비정형 대량 데이터를 저장**하기 위한 방식.
  - **Key-Document** 형태로 저장.
  - Document는 **계층적인 데이터 타입(JSON, XML)으로 저장**됨.
  - JSON타입을 사용하므로 HTTP 기반의 웹서버에서 **데이터를 편하게 주고받을 수 있음.**
  ※ ex) **MongoDB**, Azure Cosmos DB, CouchDB, OrientDB 등
  ---
  <br>
- Wide Column DB
  - Row가 아닌 **Column 위주로 데이터를 저장**하는 방식.
  - **Column-family Model**(Key, Value와 유사한 형태)
  - 데이터가 내부에서 **Key를 기준으로 오름차순 저장**됨.
  - 이전 모델들이 Key-Value 값을 이용해 필드를 결정한데 비해 해당 모델은 **키에서 필드를 결정**함.
  ※ ex) Cassandra, HBase, Google BigTable, Vertica, Druid 등
  --------------------------
  <br>
- Graph DB
  - 객체와 관계를 **그래프 형식의 데이터로 저장하기 위한** 방식.
  - 데이터를 **Node, Edge, Property와 함께 그래프 구조를 사용**해 데이터를 저장.
  - SNS, Network Diagrmas 등과 SNS에서 함께 아는 친구찾기, 추천 등 **연관 데이터를 추천해주는 엔진 및 패턴 기능에 사용**.
  ※ ex) Neo4j, Blazegraph, OrientDB, AgensGraph
  ---


</li>
</ul>
<h2 id="nosql-정리">NoSQL 정리</h2>
<h3 id="특징-1">특징</h3>
<ul>
<li>데이터 간의 <strong>관계를 정의하지 않음</strong>.</li>
<li><strong>분산형 구조</strong>로 설계.</li>
<li>RDBMS에 비해 <strong>대용량의 데이터 저장</strong> 가능.</li>
<li><strong>고정되지않은 스키마</strong>를 가짐.<h3 id="장점-1">장점</h3>
</li>
<li>RDBMS에 비해 <strong>저렴한 비용</strong>으로 분산처리와 병렬 처리 가능.</li>
<li>비정형 데이터 구조 설계로 <strong>설계 비용이 감소</strong>.</li>
<li><strong>대량의 데이터 처리</strong>에 효과적.</li>
<li><strong>가변적인 구조로 데이터 저장</strong>이 가능.</li>
<li>데이터 모델의 <strong>유연한 변화</strong>가 가능.<h3 id="단점-1">단점</h3>
</li>
<li>데이터 업데이트 중 장애가 발생하면 <strong>데이터 손실 발생 가능</strong>.</li>
<li>많은 인덱스를 사용하려면 <strong>충분한 메모리가 필요</strong>. 인덱스 구조가 메모리에 저장.</li>
<li><strong>데이터 일관성이 항상 보장되지 않음</strong>.</li>
</ul>
<hr>
<p>다음 포스팅에선 Redis를 사용하고 익혀 볼 예정이다.</p>
<hr>
<blockquote>
<h4 id="참고-블로그">참고 블로그</h4>
<p><a href='https://shuu.tistory.com/135' target='_blank'>NoSQL이란 무엇인지 간단하게 알아보자!</a>
<a href='https://www.fun-coding.org/post/mongodb_basic1.html#gsc.tab=0' target='_blank'>RDBMS와 NoSQL의 차이점과 사용 사례</a>
<a href='https://sbp-it.tistory.com/276' target='_blank'>Database 종류 및 개념 정리</a>
<a href='https://velog.io/@wnguswn7/Database-RDBMS%EC%99%80-NoSQL%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0' target='_blank'>[Database] RDBMS와 NoSQL의 차이점 알아보기 !</a>
<a href='https://f-lab.kr/insight/rdbms-vs-nosql-20240528' target='_blank'>NoSQL 이해</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 94일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-94%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-94%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 20 Dec 2024 19:54:56 GMT</pubDate>
            <description><![CDATA[<h1 id="최종프로젝트">최종프로젝트</h1>
<p>최종프로젝트 작업의 마지막 날이 되었다.
우리 팀 프로젝트의 브로셔를 작성했다.
<a href='https://www.notion.so/teamsparta/8256adc6156c4c51aa0cc151949cf16c' target='_blank'>최종 브로셔</a>
기간이 길었음에도 불구하고 생각보다 진도가 나가지 않았다.
이번에는 핑계로 보일 수 있지만 프로젝트의 진도가 늦었던 이유에 대해 트러블 슈팅하듯이 생각해보고 기록으로 남겨두고 싶어서 글을 쓴다.</p>
<h2 id="프로젝트-진도가-느렸던-이유">프로젝트 진도가 느렸던 이유</h2>
<h3 id="1-첫-주차-시간을-많이-버린-부분">1. 첫 주차 시간을 많이 버린 부분</h3>
<ul>
<li>처음 최종 프로젝트 팀원 명단을 받았을 때 까지만 해도 총 6명이었다. 그런데 정작 프로젝트가 시작되고 팀끼리 모였을 때 모인 인원의 수는 4명이었다. 일단 어떤 게임을 만들지도 정해야 하고 해서 다른 팀원들의 접속을 기다렸지만 여전히 접속하지 않았고 3일차에 담임 매니저께 다른 두분에 대해 여쭤봣고 <strong>개인 사정으로 인해 개인 프로젝트로 이동</strong>하셨다는 말을 들었다.
해당 상황을 알게 되고 4명에서 어떤 게임을 만들지, 캐릭터 컨셉 및 밸런스 등 팀 노션에 작성하는 내용을 작성중이었는데 최종 프로젝트의 경우 미리 팀장 및 부팀장을 미리 정하고 프로젝트를 진행하는 것으로 알고있었는데 4명의 팀원 중 <strong>팀장 및 부팀장을 신청한 인원이 없다는 것</strong>을 알게 되었고 팀장부터 정하게 되었다.</li>
<li>위의 상황으로 인해 작업을 시작했어야할 2주차 수요일쯤에 팀 노션 작성이 어느정도 끝나게 되었고 이후 작업이 시작되어 작업 진행도에 조금의 영향을 주게 되었다.<br></li>
<li>해당 상황을 겪게 된 후 &#39;4명에서 기간내에 완성이 가능할까&#39;라는 생각과 다른 두분에 대한 원망을 하게 되었는데 마음을 다시 잡고 인원이 적어진 만큼 빨리 작업을 진행 할 생각을 해야했었다. 이부분에 대해서는 반성하고 있고 다음에 프로젝트를 진행할 때 이런 일이 벌어진다면 그때는 바로 <strong>정신줄 잡고 작업을 진행해야겠다</strong>라는 생각을 했다.<h3 id="2-회원가입--로그인-문제">2. 회원가입 / 로그인 문제</h3>
</li>
<li>게임 시작 시 클라이언트 상으로 서버 주소 및 포트 입력만 하고 게임에 접속하는 방식으로 진행되었다. 하지만 초기 우리 프로젝트의 구조는 JWT토큰 방식을 통해 플레이어를 특정해 해당 플레이어의 정보를 저장하는 것이었기 때문에 회원가입/로그인 구조를 만들 필요가 있었고 이로 인해 해당 부분을 맡은 팀원분이 클라이언트 담당 튜터님께 찾아가 해당 구조 작업을 진행 해봣지만 진행이 어렵다는 결론이 나와 일단 잠정 보류하고 다른 작업부터 진행했다.<br></li>
<li>이 부분은 우선 닉네임을 특정해 플레이어를 찾을 수 있게 구조를 개선해서 해결했다.
하지만 회원가입/로그인에 쏟아부은 시간으로 인해 다음 작업에도 어느정도 지장을 주게 되었다고 생각한다.<h3 id="3-프로젝트-기본-구조-작성">3. 프로젝트 기본 구조 작성</h3>
</li>
<li>나의 경우 이전까지 프로젝트 기본 구조는 팀의 팀장분들이 짜주시는 구조를 이용해 깃허브를 통해 작업을 진행했었는데, 이번 팀에는 그 작업을 해줄 팀원분이 따로 없어서 내가 그 작업을 담당했다.
이전에 작업했던 작업 파일들을 찾아보며 기본 구조를 작성했지만 이후 캐릭터 생성에서 오류가 발생했다.
이 작업에서 변수를 받아오는 부분을 다른 변수로 받아와 작동하지 않는다는 것을 기본 구조를 바꿔보는 등 여러번의 삽질 후에 확인할 수 있었다.
<a href='https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-77%EC%9D%BC%EC%B0%A8-f6jt3poo#%EC%B5%9C%EC%A2%85%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8' target='_blank'>해당 부분에 대한 기록</a></li>
<li>문제는 잘 해결되었고 이후 내 작업 부분(플레이어 입장, 움직임, 애니메이션)은 잘 진행되었지만 다른 팀원분들은 그동안 감으로 &#39;이렇게 하면 작동하겠다.&#39;라는 식으로 작성을 했었고, 이 문제 해결중 기본 구조도 조금 바뀌면서 기존에 작업하셧던 팀원분들이 다시 코드를 작성하고 테스트를 해봐야하는 문제가 생기면서 진행도가 더뎌졌다.<br></li>
<li>뭔가 해결이 될 것 같다는 생각에 계속 물어보지 않고 잡고있었는데 이 부분이 안된다고 튜터님께 바로 물어봤더라면(문제 내용상 물어봤으면 반드시 혼날법할 내용이었지만), 시간을 버릴 일도 없었고 진행도 더 빠르게 매끄럽게 진행되지 않았을까 생각한다.<blockquote>
<p><strong>무언가 문제가 생긴다면, 해결하지 못하겠다면 혼날생각하고 바로 물어보는 습관을 가지자. 또 작성한 코드를 다시 한 번 더 확인 하는 습관을 가지자</strong>고 다시한번 생각하고 다짐하는 계기가 되었다.</p>
</blockquote>
<h3 id="4-프로젝트-완성-가능-여부개인적">4. 프로젝트 완성 가능 여부(개인적)</h3>
</li>
<li>이렇게 위의 문제 외에도 여러 문제가 생기면서 작업 진행도는 더뎌졌고, 중간 발표 당시 내가 진행한 부분(플레이어 입장, 움직임, 애니메이션, 플레이어 스폰 및 디스폰)을 제외하고는 던전 부분은 아예 진행조차 되지 않았고 다른 부분들 또한 코드상으로만 존재하고 실행되지 않았다.</li>
<li>중간 발표가 끝나고 2주 정도 남은 시점에서 한 달 간 진행했는데도 불구하고 완성이 되지않은 것에 대해 <strong>내 실력으로 다른 부분들을 도와준다고 해서 완성이 될지에 대한 생각</strong>이 들었고 이는 프로젝트에 대한 정과 흥미가 떨어지는 계기가 되었다고 생각한다.</li>
<li>이로 인해 다른 분들이 여쭤보는 것에 대해서만 설명해주고 작업을 건성건성 하게 되었고 결과적으로 프로젝트를 완성하지 못했다고 생각한다.<br></li>
<li>물론 이런 생각을 하게 된 것 자체가 말이 안되는 행동이고 해선 안되는 생각이었지만 그냥 처음부터 굴러온 스노우볼이 이런 결과를 가져왔다고 생각한다.</li>
<li>내가 실력이 더 좋았더라면, 더 공부를 열심히했더라면 하는 후회를 많이 하게 된 프로젝트 기간이었던 것 같다.</li>
<li>일단 최종 프로젝트는 이렇게 끝나지만 <strong>해당 프로젝트를 선택했던 이</strong>유(RPG장르 특성상 구현할 내용이 많다. / 어려워보인다 = 실력 향상에 도움이 된다)를 가지고 개인적으로 <strong>프로젝트를 완성해 볼 생각</strong>이다.(프로젝트 진행상황은 동일하게 velog에 글로 계속 남길 것 같다.)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 85일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-85%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-85%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 09 Dec 2024 12:11:47 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="행렬-테두리-회전하기">행렬 테두리 회전하기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>rows x columns 크기인 행렬이 있습니다. 행렬에는 1부터 rows x columns까지의 숫자가 한 줄씩 순서대로 적혀있습니다. 이 행렬에서 직사각형 모양의 범위를 여러 번 선택해, 테두리 부분에 있는 숫자들을 시계방향으로 회전시키려 합니다. 각 회전은 (x1, y1, x2, y2)인 정수 4개로 표현하며, 그 의미는 다음과 같습니다.</p>
<p>x1 행 y1 열부터 x2 행 y2 열까지의 영역에 해당하는 직사각형에서 테두리에 있는 숫자들을 한 칸씩 시계방향으로 회전합니다.
다음은 6 x 6 크기 행렬의 예시입니다.
<img src="https://velog.velcdn.com/images/v_rien/post/86503574-7cf2-4eb0-bd8e-915ad7c9ee01/image.png" alt="">
이 행렬에 (2, 2, 5, 4) 회전을 적용하면, 아래 그림과 같이 2행 2열부터 5행 4열까지 영역의 테두리가 시계방향으로 회전합니다. 이때, 중앙의 15와 21이 있는 영역은 회전하지 않는 것을 주의하세요.
<img src="https://velog.velcdn.com/images/v_rien/post/71605fd6-8b0b-4f23-9b50-5f06d28b10b8/image.png" alt="">
행렬의 세로 길이(행 개수) rows, 가로 길이(열 개수) columns, 그리고 회전들의 목록 queries가 주어질 때, 각 회전들을 배열에 적용한 뒤, 그 회전에 의해 위치가 바뀐 숫자들 중 가장 작은 숫자들을 순서대로 배열에 담아 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>rows는 2 이상 100 이하인 자연수입니다.
columns는 2 이상 100 이하인 자연수입니다.
처음에 행렬에는 가로 방향으로 숫자가 1부터 하나씩 증가하면서 적혀있습니다.
즉, 아무 회전도 하지 않았을 때, i 행 j 열에 있는 숫자는 ((i-1) x columns + j)입니다.
queries의 행의 개수(회전의 개수)는 1 이상 10,000 이하입니다.
queries의 각 행은 4개의 정수 [x1, y1, x2, y2]입니다.
x1 행 y1 열부터 x2 행 y2 열까지 영역의 테두리를 시계방향으로 회전한다는 뜻입니다.
1 ≤ x1 &lt; x2 ≤ rows, 1 ≤ y1 &lt; y2 ≤ columns입니다.
모든 회전은 순서대로 이루어집니다.
예를 들어, 두 번째 회전에 대한 답은 첫 번째 회전을 실행한 다음, 그 상태에서 두 번째 회전을 실행했을 때 이동한 숫자 중 최솟값을 구하면 됩니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function pushOrPop(query, arr, matrix, flag) {
    const [x1, y1, x2, y2] = query;
    for (y = y1-1, len = y2 - 1, x = x1 - 1; y &lt;= len; y++) {
        flag ? arr.push(matrix[x][y]) : matrix[x][y] = arr.pop();
    }
    for (x = x1, len = x2 - 1, y = y2 - 1; x &lt;= len; x++) {
        flag ? arr.push(matrix[x][y]) : matrix[x][y] = arr.pop();
    }
    for (y = y2-2, len = y1 - 1, x = x2 - 1; y &gt;= len; y--) {
        flag ? arr.push(matrix[x][y]) : matrix[x][y] = arr.pop();
    }
    for (x = x2-2, len = x1, y = y1 - 1; x &gt;= len; x--) {
        flag ? arr.push(matrix[x][y]) : matrix[x][y] = arr.pop();
    }
}

function solution(rows, columns, queries) {
    const answer = [];
    const matrix = [];
    let num = 1;
    for (i = 0; i &lt; rows; i++) {
        const arr = [];
        for (j = 0; j &lt; columns; j++) {
            arr.push(num++);
        }
        matrix.push(arr);
    }


    queries.forEach((query) =&gt; {
        const [x1, y1, x2, y2] = query;
        const arr = [];

        pushOrPop(query, arr, matrix, true);

        const newArr = arr.slice(arr.length-1).concat(arr.slice(0, arr.length-1));
        answer.push(Math.min(...newArr));
        newArr.reverse();

        pushOrPop(query, newArr, matrix, false);
    })

    return answer;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>rows, columns를 토대로 matrix라는 가변 배열을 만든다. 이후 query를 비구조할당해 구한 직사각형의 범위를 토대로 테두리에 있는 숫자를 배열에 모두 담고, 회전한 테두리의 숫자를 얻기 위해 배열의 마지막 요소를 추출해 맨 앞에 넣어준다.
움직인 숫자의 최솟값을 구해야 하므로 배열의 요소 중 가장 작읍 값을 구해 answer에 추가해주고 다음 회전으로 넘어가기전에 matrix 배열에 회전의 결과를 반영해준다.</p>
<h1 id="기술면접-문제-풀어보기">기술면접 문제 풀어보기</h1>
<h2 id="18-그래프graph와-트리tree를-설명하고-둘의-차이점을-설명해주세요">18. 그래프(Graph)와 트리(Tree)를 설명하고, 둘의 차이점을 설명해주세요</h2>
<h3 id="그래프graph">그래프(Graph)</h3>
<ul>
<li>노드와 노드간을 연결하는 간선으로 구성된 자료구조. =&gt; 연결된 노드 간의 관계를 표현할 수 있음.</li>
<li>순환 혹은 비순환 구조를 이룸.</li>
<li>방향이 있는 그래프와 없는 그래프가 있음.</li>
<li>루트 노드의 개념이 없음. =&gt; 부모 - 자식 관계가 없음.</li>
<li>2개 이상의 경로가 가능.(무방향, 방향, 양방향 가능.)</li>
<li>네트워크 모델.<h3 id="트리tree">트리(Tree)</h3>
</li>
<li>노드와 노드간을 연결하는 간선으로 구성된 자료구조. =&gt; 그래프와 같음.</li>
<li>그래프 중에서도 특수한 케이스에 해당하는 자료구조.</li>
<li>두 개의 노드 사이에 반드시 1개의 경로만을 가지고 사이클이 존재하지 않는 방향 그래프. =&gt; 최소 연결 트리</li>
<li>부모 - 자식 관계 성립 =&gt; 계층형 모델.</li>
<li>노드가 N개이면 간선은 N-1개, 각 레벨에 존재하는 노드는 2^k개.</li>
<li>방향성이 존재하고 사이클은 존재하지 않음.</li>
<li>전위순회, 중위순회, 후위순회 3가지 존재.<h3 id="비교">비교</h3>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">그래프</th>
<th align="center">트리</th>
</tr>
</thead>
<tbody><tr>
<td align="center">방향성</td>
<td align="center">방향, 무방향</td>
<td align="center">방향</td>
</tr>
<tr>
<td align="center">사이클</td>
<td align="center">순환, 비순환, 자기순환</td>
<td align="center">비순환</td>
</tr>
<tr>
<td align="center">루트노드</td>
<td align="center">루트 개념 x</td>
<td align="center">한 개의 루트 존재</td>
</tr>
<tr>
<td align="center">부모-자식</td>
<td align="center">부모-자식 개념없음</td>
<td align="center">1개의 부모노드(루트 제외)</td>
</tr>
<tr>
<td align="center">모델</td>
<td align="center">네트워크 모델</td>
<td align="center">계층 모델</td>
</tr>
<tr>
<td align="center">간선 수</td>
<td align="center">자유</td>
<td align="center">N-1개</td>
</tr>
<tr>
<td align="center"># 최종 프로젝트</td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">## 해결중인 부분</td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
</li>
<li>타워세션, 유저세션을 생성해 플레이어들을 타워 세션에 추가하고 클라이언트가 추가되면 해당 플레이어 정보를 spawn, despawn 핸들러를 이용해 기존 플레이어들에게 새로운 플레이어의 정보를 띄워줄 수 있게 구현을 했지만 정상작동을 하지 않았다.
세션이 제대로 작동하지 않는 것 같아서 이부분을 고쳐보는 중이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 84일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-84%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-84%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 06 Dec 2024 12:11:44 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="무인도-여행">무인도 여행</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>메리는 여름을 맞아 무인도로 여행을 가기 위해 지도를 보고 있습니다. 지도에는 바다와 무인도들에 대한 정보가 표시돼 있습니다. 지도는 1 x 1크기의 사각형들로 이루어진 직사각형 격자 형태이며, 격자의 각 칸에는 &#39;X&#39; 또는 1에서 9 사이의 자연수가 적혀있습니다. 지도의 &#39;X&#39;는 바다를 나타내며, 숫자는 무인도를 나타냅니다. 이때, 상, 하, 좌, 우로 연결되는 땅들은 하나의 무인도를 이룹니다. 지도의 각 칸에 적힌 숫자는 식량을 나타내는데, 상, 하, 좌, 우로 연결되는 칸에 적힌 숫자를 모두 합한 값은 해당 무인도에서 최대 며칠동안 머물 수 있는지를 나타냅니다. 어떤 섬으로 놀러 갈지 못 정한 메리는 우선 각 섬에서 최대 며칠씩 머물 수 있는지 알아본 후 놀러갈 섬을 결정하려 합니다.</p>
<p>지도를 나타내는 문자열 배열 maps가 매개변수로 주어질 때, 각 섬에서 최대 며칠씩 머무를 수 있는지 배열에 오름차순으로 담아 return 하는 solution 함수를 완성해주세요. 만약 지낼 수 있는 무인도가 없다면 -1을 배열에 담아 return 해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>3 ≤ maps의 길이 ≤ 100
3 ≤ maps[i]의 길이 ≤ 100
maps[i]는 &#39;X&#39; 또는 1 과 9 사이의 자연수로 이루어진 문자열입니다.
지도는 직사각형 형태입니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(maps) {
    const newMap = maps.map((n) =&gt; n.split(&quot;&quot;));

    const dx = [1, 0, -1, 0];
    const dy = [0, 1, 0, -1];

    function DFS(x, y, num) {
      var sum = Number(num);

      for (let i = 0; i &lt; 4; i++) {
        const nx = x + dx[i];
        const ny = y + dy[i];

        if (nx &gt;= 0 &amp;&amp; ny &gt;= 0 &amp;&amp; nx &lt; newMap.length &amp;&amp; ny &lt; newMap[0].length) {
          if (newMap[nx][ny] !== &quot;X&quot;) {
            const next = newMap[nx][ny];

            newMap[nx][ny] = &quot;X&quot;;

            sum += DFS(nx, ny, next);
          }
        }
      }

      return sum;
    }

    const answer = [];

    for (i = 0; i &lt; newMap.length; i++) {
      for (j = 0; j &lt; newMap[0].length; j++) {
        if (newMap[i][j] !== &quot;X&quot;) {
          const start = newMap[i][j];

          newMap[i][j] = &quot;X&quot;;

          answer.push(DFS(i, j, start));
        }
      }
    }
    return answer.length ? answer.sort((a, b) =&gt; a - b) : [-1];
  }</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>우선 배열 안의 문자열을 배열로 반환해주고 상 하 좌 우 좌표를 미리 세팅해둔다.
DFS함수를 만들어 들어온 num을 string =&gt; 숫자로 변환해준다.
설정해둔 좌표 배열을 통해 상 하 좌 우를 탐색하고 지도를 벗어나지 않는지 판단한다.
만약 지도를 벗어나지 않고 X가 아닌 곳을 찾는다면 식량 수를 저장 후 그곳을 X로 변환하고 DFS를 통해 그 좌표부터 다시 탐색한다. 이후 그 리턴 값을 sum에 더해준다. 최종적으로는 다 더해진 sum을 반환한다.
이중 반복문을 통해 하나씩 살펴보고 X가 아닌 곳을 찾으면 일단 현재 수를 저장하고 그 곳을 X로 변환한다. DFS(x좌표, y좌표, 식량 수)함수를 실행하고 이 반환 값을 answer에 push한다. 만약 answer의 길이가 0 이라면 -1을 반환하고, 아니라면 오름차순으로 정렬 후 반환한다.</p>
<h1 id="기술면접-문제-풀어보기">기술면접 문제 풀어보기</h1>
<h2 id="17-stack과-queue를-비교설명해주세요">17. Stack과 Queue를 비교설명해주세요.</h2>
<ul>
<li>둘 다 데이터 구조의 한 종류이고 데이터를 순서대로 저장하지만 동작 방식에서 차이가 있음.<h3 id="stack">Stack</h3>
</li>
<li>후입 선출(LIFO, Last In First Out) 구조.<ul>
<li>함수 호출 시 사용되는 <strong>재귀</strong>나 <strong>뒤로 가기 기능</strong> 등에 적합.</li>
</ul>
</li>
<li>주로 사용하는 연산 : push(데이터 추가), pop(데이터 삭제)<h3 id="queue">Queue</h3>
</li>
<li>선입 선출(FIFO, First In First Out) 구조.<ul>
<li><strong>프린터 대기열</strong>이나 운영체제의 <strong>작업 스케줄링</strong> 등에 활용.</li>
</ul>
</li>
<li>주로 사용하는 연산 : enqueue(데이터 추가), dequeue(데이터 삭제)</li>
</ul>
<h1 id="최종프로젝트">최종프로젝트</h1>
<p>중간 MVP발표가 있었다. 다른 팀들은 진행상황이 좋아보였고 그랬기에 발표 자료에도 많은 내용이 담겨있었지만 우리 팀의 경우는 townSession과 dungeonSession으로 나뉘어지는데 townSession도 끝나지 않은 상태라서 ppt에 진행 상황에 관한 내용보단 기획적인 면을 설명하는데 중점을 두고 제작했다.
발표가 끝나고 피드백을 받았는데 아무래도 진도가 안나갔더라도 코드나 영상을 추가해서 진행상황을 보여주면 좋겠다고 말씀하시고 같은 내용으로 핸들러 설명할 때 코드 어떻게 작성했는지 보여주는 것 좋았다고 하셧다.
기획적인 내용이 주가 되다보니 로직을 소개할 때 글이 많이 들어갔는데 해당 부분을 지적해주시면서 처음 보는 사람도 내용 이해하기 쉽게 글을 많이 쓰는건 지양하는게 좋다고 하시고 만약 쓰게 된다면 띄워쓰기 같은 서식의 일관성을 지키라고 해주셧다.
이후 트러블 슈팅 부분에서 그렇게 해결하면 안된다 하시는부분을 말씀해주셧는데, 플레이어 특정이 되지 않는다고 패킷 자체에 Id를 담아서 사용하게 되면 클라이언트가 어떤 패킷을 보내도 서버는 믿게 되고 어떤 유저가 피해를 볼 수 있다고, 어뷰징 문제가 발생할 수 있다고 하셧다. 또, 소통이 잘 안되면 잘 소통해주면 좋겠다는 말씀도 하셧다.
다음 발표는 약 2주뒤에 최종 발표인데 이때는 피드백 받은 내용들을 지키면서, 해결안된 부분을 고치고 완성되는 모습을 보여주기위해 노력해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 83일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-83%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-83%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 06 Dec 2024 11:56:32 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="두-큐-합-같게-만들기">두 큐 합 같게 만들기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>길이가 같은 두 개의 큐가 주어집니다. 하나의 큐를 골라 원소를 추출(pop)하고, 추출된 원소를 다른 큐에 집어넣는(insert) 작업을 통해 각 큐의 원소 합이 같도록 만들려고 합니다. 이때 필요한 작업의 최소 횟수를 구하고자 합니다. 한 번의 pop과 한 번의 insert를 합쳐서 작업을 1회 수행한 것으로 간주합니다.</p>
<p>큐는 먼저 집어넣은 원소가 먼저 나오는 구조입니다. 이 문제에서는 큐를 배열로 표현하며, 원소가 배열 앞쪽에 있을수록 먼저 집어넣은 원소임을 의미합니다. 즉, pop을 하면 배열의 첫 번째 원소가 추출되며, insert를 하면 배열의 끝에 원소가 추가됩니다. 예를 들어 큐 [1, 2, 3, 4]가 주어졌을 때, pop을 하면 맨 앞에 있는 원소 1이 추출되어 [2, 3, 4]가 되며, 이어서 5를 insert하면 [2, 3, 4, 5]가 됩니다.</p>
<p>다음은 두 큐를 나타내는 예시입니다.</p>
<pre><code>queue1 = [3, 2, 7, 2]
queue2 = [4, 6, 5, 1]</code></pre><p>두 큐에 담긴 모든 원소의 합은 30입니다. 따라서, 각 큐의 합을 15로 만들어야 합니다. 예를 들어, 다음과 같이 2가지 방법이 있습니다.</p>
<p>queue2의 4, 6, 5를 순서대로 추출하여 queue1에 추가한 뒤, queue1의 3, 2, 7, 2를 순서대로 추출하여 queue2에 추가합니다. 그 결과 queue1은 [4, 6, 5], queue2는 [1, 3, 2, 7, 2]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 7번 수행합니다.
queue1에서 3을 추출하여 queue2에 추가합니다. 그리고 queue2에서 4를 추출하여 queue1에 추가합니다. 그 결과 queue1은 [2, 7, 2, 4], queue2는 [6, 5, 1, 3]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 2번만 수행하며, 이보다 적은 횟수로 목표를 달성할 수 없습니다.
따라서 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수는 2입니다.</p>
<p>길이가 같은 두 개의 큐를 나타내는 정수 배열 queue1, queue2가 매개변수로 주어집니다. 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수를 return 하도록 solution 함수를 완성해주세요. 단, 어떤 방법으로도 각 큐의 원소 합을 같게 만들 수 없는 경우, -1을 return 해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>1 ≤ queue1의 길이 = queue2의 길이 ≤ 300,000
1 ≤ queue1의 원소, queue2의 원소 ≤ 109
주의: 언어에 따라 합 계산 과정 중 산술 오버플로우 발생 가능성이 있으므로 long type 고려가 필요합니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(queue1, queue2) {
    var answer = 0;
    var queueSum1 = 0;
    queue1.forEach(element=&gt;queueSum1+=element);
    var queueSum2 = 0;
    queue2.forEach(element=&gt;queueSum2+=element);
    var totalLen = queue1.length+ queue2.length;
    var queue1Index = 0;
    var queue2Index = 0;


    while(queueSum1 !== queueSum2){
        if(queueSum1&gt;queueSum2){
            queueSum1 -= queue1[queue1Index];
            queue2.push(queue1[queue1Index]);
            queueSum2 +=queue1[queue1Index++];
        }
        else{
             queueSum1 += queue2[queue2Index];
             queue1.push(queue2[queue2Index]);
             queueSum2 -=queue2[queue2Index++];
        }
         answer++;
        if(queue1Index&gt;totalLen||queue2Index&gt;totalLen){
            return -1;
        }
    }
    return answer;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>우선, 각각의 큐의 합을 구한다. 각각 지금 빼낼 것을 고려하고 있는 인덱스 값인 queue1Index와 queue2Index를 0으로 선언해주고 queue1의 합이 queue2 보다 크면 queue1Index에 +1을 해주고 반대의 경우에는 queue2Index에 +1을 해준다. queue1Inedx의 경우 +1 하기 전에 그 인덱스에 해당하는 queue1[index]를 queue1 합에 빼주고 queue2에 그 값을 더해주고 반대의 경우 반대로 한다.(queue2[index]를 queue2 합에 빼주고 queue1에 그 값을 더해줌.)
해당 과정을 queueSum1 과 queueSum2가 같을때까지 반복하고, 두 인덱스중 하나가 큐의 길이를 합친 것보다 넘어가면 -1을 리턴한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 82일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-82%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-82%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 06 Dec 2024 11:56:30 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="연속된-부분-수열의-합">연속된 부분 수열의 합</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>비내림차순으로 정렬된 수열이 주어질 때, 다음 조건을 만족하는 부분 수열을 찾으려고 합니다.</p>
<p>기존 수열에서 임의의 두 인덱스의 원소와 그 사이의 원소를 모두 포함하는 부분 수열이어야 합니다.
부분 수열의 합은 k입니다.
합이 k인 부분 수열이 여러 개인 경우 길이가 짧은 수열을 찾습니다.
길이가 짧은 수열이 여러 개인 경우 앞쪽(시작 인덱스가 작은)에 나오는 수열을 찾습니다.
수열을 나타내는 정수 배열 sequence와 부분 수열의 합을 나타내는 정수 k가 매개변수로 주어질 때, 위 조건을 만족하는 부분 수열의 시작 인덱스와 마지막 인덱스를 배열에 담아 return 하는 solution 함수를 완성해주세요. 이때 수열의 인덱스는 0부터 시작합니다.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>5 ≤ sequence의 길이 ≤ 1,000,000
1 ≤ sequence의 원소 ≤ 1,000
sequence는 비내림차순으로 정렬되어 있습니다.
5 ≤ k ≤ 1,000,000,000
k는 항상 sequence의 부분 수열로 만들 수 있는 값입니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(sequence, k) {
    const answer = [0, 1000000];

    var [left, right] = [0, 0];

    var sum = sequence[0];

    while (right &lt; sequence.length) {
      if (sum === k) {
        if (answer[1] - answer[0] &gt; right - left) {
          answer[0] = left;
          answer[1] = right;
        }
        sum -= sequence[left++];
        sum += sequence[++right];
      }

      else if (sum &gt; k) sum -= sequence[left++];
      else if (sum &lt; k) sum += sequence[++right];
    }

    return answer;
  }</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>두 인덱스 사이에 답이 있으므로 answer의 기본값을 0~1000000으로 세팅해준다.
left, right 배열을 만들어 0으로 선언하고 sum에 첫번째 값을 넣어준다.
right가 sequence의 길이보다 작으면 계속 루프를 도는 반복문을 만들어 만약 sum = k 라면 현재 답에 구해진 인덱스 사이의 길이와 새로 들어온 길이를 비교해 더 짧은 길이를 넣어준다.
이후 left와 right 값을 하나씩 올려주며 처리해준다.
만약 sum &gt; k 이면 sum에서 현재 left 인덱스에 해당하는 값을 빼고 left를 증가, sum &lt; k 이면 right값을 증가 시키고 right 인덱스에 해당하는 값을 더해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 81일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-81%EC%9D%BC%EC%B0%A8-6uwmop1k</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-81%EC%9D%BC%EC%B0%A8-6uwmop1k</guid>
            <pubDate>Fri, 06 Dec 2024 11:53:25 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="삼각-달팽이">삼각 달팽이</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>정수 n이 매개변수로 주어집니다. 다음 그림과 같이 밑변의 길이와 높이가 n인 삼각형에서 맨 위 꼭짓점부터 반시계 방향으로 달팽이 채우기를 진행한 후, 첫 행부터 마지막 행까지 모두 순서대로 합친 새로운 배열을 return 하도록 solution 함수를 완성해주세요.
<img src="https://velog.velcdn.com/images/v_rien/post/f5d8147d-c37a-4add-a726-676aa8dfe666/image.png" alt=""></p>
<h3 id="제한-사항">제한 사항</h3>
<blockquote>
<p>n은 1 이상 1,000 이하입니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(n) {
    let answer = [];
    let count = 0;
    let arr = Array.from({ length: n }, (_, index) =&gt; Array(index + 1).fill(0));
    let currentX = -1;
    let currentY = 0;
    while (n &gt; 0) {
      for (i = 0; i &lt; n; i++) {
        currentX++;
        count++;
        arr[currentX][currentY] = count;
      }
      for (i = 0; i &lt; n - 1; i++) {
        currentY++;
        count++;
        arr[currentX][currentY] = count;
      }
      for (i = 0; i &lt; n - 2; i++) {
        currentX--;
        currentY--;
        count++;
        arr[currentX][currentY] = count;
      }
      n -= 3;
    }

    for (i = 0; i &lt; arr.length; i++) {
      answer = [...answer, ...arr[i]];
    }

    return answer;
  }</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>위에서 아래로 내려가는 대각선, 왼쪽에서 오른쪽으로 이동, 아래에서 위로 내려가는 대각선의 3부분으로 나눠서 해당 위치의 값을 저장하고, 이 작업을 n의 값이 유효할 때까지 반복한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 80일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-80%EC%9D%BC%EC%B0%A8-u7kzvudp</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-80%EC%9D%BC%EC%B0%A8-u7kzvudp</guid>
            <pubDate>Fri, 06 Dec 2024 11:51:38 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="큰-수-구하기">큰 수 구하기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>어떤 숫자에서 k개의 수를 제거했을 때 얻을 수 있는 가장 큰 숫자를 구하려 합니다.</p>
<p>예를 들어, 숫자 1924에서 수 두 개를 제거하면 [19, 12, 14, 92, 94, 24] 를 만들 수 있습니다. 이 중 가장 큰 숫자는 94 입니다.</p>
<p>문자열 형식으로 숫자 number와 제거할 수의 개수 k가 solution 함수의 매개변수로 주어집니다. number에서 k 개의 수를 제거했을 때 만들 수 있는 수 중 가장 큰 숫자를 문자열 형태로 return 하도록 solution 함수를 완성하세요.</p>
<h3 id="제한-조건">제한 조건</h3>
<blockquote>
<p>number는 2자리 이상, 1,000,000자리 이하인 숫자입니다.
k는 1 이상 number의 자릿수 미만인 자연수입니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(number, k) {
    const arr = [];
    for (i = 0; i &lt; number.length; i++) {

      while (arr.length &gt; 0 &amp;&amp; arr[arr.length - 1] &lt; number[i] &amp;&amp; k &gt; 0) {
        k--;
        arr.pop();
      }
      arr.push(number[i]);
    }
    arr.splice(number.length - k, k);
    return arr.join(&quot;&quot;);
  }</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>배열의 마지막 숫자와 현재 숫자를 비교해 현재 숫자가 더 크면 해당 숫자를 pop한다. 이 과정을 현재 숫자보다 큰 수가 나올때까지 최대 k번 반복하고 모든 숫자를 비교 후 k가 0보다 크면 남은 k만큼 뒤에서 제거한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 79일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-79%EC%9D%BC%EC%B0%A8-jrgev6bl</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-79%EC%9D%BC%EC%B0%A8-jrgev6bl</guid>
            <pubDate>Fri, 29 Nov 2024 12:06:24 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="택배상자">택배상자</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>영재는 택배상자를 트럭에 싣는 일을 합니다. 영재가 실어야 하는 택배상자는 크기가 모두 같으며 1번 상자부터 n번 상자까지 번호가 증가하는 순서대로 컨테이너 벨트에 일렬로 놓여 영재에게 전달됩니다. 컨테이너 벨트는 한 방향으로만 진행이 가능해서 벨트에 놓인 순서대로(1번 상자부터) 상자를 내릴 수 있습니다. 하지만 컨테이너 벨트에 놓인 순서대로 택배상자를 내려 바로 트럭에 싣게 되면 택배 기사님이 배달하는 순서와 택배상자가 실려 있는 순서가 맞지 않아 배달에 차질이 생깁니다. 따라서 택배 기사님이 미리 알려준 순서에 맞게 영재가 택배상자를 실어야 합니다.</p>
<p>만약 컨테이너 벨트의 맨 앞에 놓인 상자가 현재 트럭에 실어야 하는 순서가 아니라면 그 상자를 트럭에 실을 순서가 될 때까지 잠시 다른 곳에 보관해야 합니다. 하지만 고객의 물건을 함부로 땅에 둘 수 없어 보조 컨테이너 벨트를 추가로 설치하였습니다. 보조 컨테이너 벨트는 앞 뒤로 이동이 가능하지만 입구 외에 다른 면이 막혀 있어서 맨 앞의 상자만 뺄 수 있습니다(즉, 가장 마지막에 보조 컨테이너 벨트에 보관한 상자부터 꺼내게 됩니다). 보조 컨테이너 벨트를 이용해도 기사님이 원하는 순서대로 상자를 싣지 못 한다면, 더 이상 상자를 싣지 않습니다.</p>
<p>예를 들어, 영재가 5개의 상자를 실어야 하며, 택배 기사님이 알려준 순서가 기존의 컨테이너 벨트에 네 번째, 세 번째, 첫 번째, 두 번째, 다섯 번째 놓인 택배상자 순서인 경우, 영재는 우선 첫 번째, 두 번째, 세 번째 상자를 보조 컨테이너 벨트에 보관합니다. 그 후 네 번째 상자를 트럭에 싣고 보조 컨테이너 벨트에서 세 번째 상자 빼서 트럭에싣습니다. 다음으로 첫 번째 상자를 실어야 하지만 보조 컨테이너 벨트에서는 두 번째 상자를, 기존의 컨테이너 벨트에는 다섯 번째 상자를 꺼낼 수 있기 때문에 더이상의 상자는 실을 수 없습니다. 따라서 트럭에는 2개의 상자만 실리게 됩니다.</p>
<p>택배 기사님이 원하는 상자 순서를 나타내는 정수 배열 order가 주어졌을 때, 영재가 몇 개의 상자를 실을 수 있는지 return 하는 solution 함수를 완성하세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>1 ≤ order의 길이 ≤ 1,000,000
order는 1이상 order의 길이 이하의 모든 정수가 한번씩 등장합니다.
order[i]는 기존의 컨테이너 벨트에 order[i]번째 상자를 i+1번째로 트럭에 실어야 함을 의미합니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(order) {
    var result = 0;
    const stack = [];

    for (i = 1; i &lt;= order.length; i++) {
      stack.push(i);

      // 스택의 상자 번호가 주어진 순서와 일치하는지 확인
      while (stack.length !== 0 &amp;&amp; stack.at(-1) === order[result]) {
        stack.pop(); // 일치하는 상자 번호는 스택에서 제거
        result++;
      }
    }

    return result;
  }</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>order의 길이만큼 stack에 i값을 넣어준다. stack의 상자 번호가 주어진 순서와 일치하는지 확인 후 일치하는 상자번호를 stack에서 제거하고 result값을 올려준다.</p>
<h1 id="기술면접-문제-풀어보기">기술면접 문제 풀어보기</h1>
<h2 id="16array과-linkedlist를-비교설명해주세요">16.Array과 LinkedList를 비교설명해주세요.</h2>
<h3 id="배열array">배열(Array)</h3>
<ul>
<li><strong>정적 자료구조</strong> =&gt; <strong>미리 크기를 정해두고</strong> 해당 크기만큼 연속된 메모리 주소를 할당 받음.</li>
<li>연속된 메모리 주소를 할당 받으므로 데이터가 <strong>인덱스(Index)를 가짐</strong>.<ul>
<li>array[0]에서 []안의 숫자가 인덱스.
=&gt; <strong>임의 접근</strong> 가능. <strong>접근과 탐색에 용이</strong>.</li>
</ul>
</li>
<li>크기를 미리 정해놓았으므로 <strong>수정이 불가능.</strong>, 해당 배열 <strong>크기 이상의 데이터를 저장할 수 없음</strong>.<h3 id="연결-리스트linkedlist">연결 리스트(LinkedList)</h3>
</li>
<li><strong>동적 자료구조</strong> =&gt; <strong>크기를 정할 필요가 없음.</strong></li>
<li><strong>노드가 존재</strong>. =&gt; 노드 안에 데이터가 있고, <strong>다음 데이터를 가리키는 주소를 가짐.</strong></li>
<li>크기의 제한이 없음. =&gt; <strong>데이터 추가 및 삭제가 자유로움.</strong></li>
<li>메모리 주소를 할당 받지 않았으므로 <strong>임의로 접근이 불가능</strong>. =&gt; <strong>데이터 탐색 시 순차적으로 접근해야함.</strong><h3 id="정리">정리</h3>
<table>
<thead>
<tr>
<th align="center">배열(Array)</th>
<th align="center">연결 리스트(LinkedList)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">미리 크기를 정해 놓음</td>
<td align="center">크기를 정할 필요 없음</td>
</tr>
<tr>
<td align="center">연속된 메모리 주소를 할당 받음</td>
<td align="center">연속된 메모리 주소를 할당 받지 않음</td>
</tr>
<tr>
<td align="center">접근 및 탐색 용이</td>
<td align="center">추가 및 삭제 용이</td>
</tr>
<tr>
<td align="center">index 존재</td>
<td align="center">Node 존재</td>
</tr>
<tr>
<td align="center"># 최종프로젝트</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">animationHandler와 despawnHandler를 작성했다.</td>
<td align="center"></td>
</tr>
<tr>
<td align="center"><img src="https://velog.velcdn.com/images/v_rien/post/fbb55ed9-0cf3-499f-aa8a-5ca1169f1489/image.PNG" alt=""></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">animationHandler의 경우 클라이언트로부터 받은 aniCode와 townSession에서 가져온 userid를 넣어주고 response해준 후 모든 유저들에게 animation을 보이게 구현했다.</td>
<td align="center"></td>
</tr>
<tr>
<td align="center"><img src="https://velog.velcdn.com/images/v_rien/post/eb251298-34af-4ec2-941b-23e72c19cd89/image.PNG" alt=""></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">테스트 결과 잘 작동한다!</td>
<td align="center"></td>
</tr>
<tr>
<td align="center"><img src="https://velog.velcdn.com/images/v_rien/post/ecba0d96-0d7a-496f-b235-f1a964c8c1a6/image.PNG" alt=""></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">despawnHandler의 경우 먼저 despawnUser에 userid를 넣어준 뒤 해당 값을 동기화해주고 타운세션에서 유저를 삭제하는 것으로 구현했다.</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">이전에 spawnHandler를 작성했는데 이게 어떤 부분이 문제인지 작동을 안해서 두개의 클라이언트가 접속해도 각각의 캐릭터밖에 보이지 않는다. 해당 문제를 해결 후 despawn부분이 문제있는지 확인해봐야 할 것 같다.</td>
<td align="center"></td>
</tr>
</tbody></table>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 78일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-78%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-78%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 28 Nov 2024 12:28:05 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="쿼드압축-후-개수-세기">쿼드압축 후 개수 세기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>0과 1로 이루어진 2n x 2n 크기의 2차원 정수 배열 arr이 있습니다. 당신은 이 arr을 쿼드 트리와 같은 방식으로 압축하고자 합니다. 구체적인 방식은 다음과 같습니다.</p>
<ol>
<li>당신이 압축하고자 하는 특정 영역을 S라고 정의합니다.</li>
<li>만약 S 내부에 있는 모든 수가 같은 값이라면, S를 해당 수 하나로 압축시킵니다.</li>
<li>그렇지 않다면, S를 정확히 4개의 균일한 정사각형 영역(입출력 예를 참고해주시기 바랍니다.)으로 쪼갠 뒤, 각 정사각형 영역에 대해 같은 방식의 압축을 시도합니다.</li>
</ol>
<p>arr이 매개변수로 주어집니다. 위와 같은 방식으로 arr을 압축했을 때, 배열에 최종적으로 남는 0의 개수와 1의 개수를 배열에 담아서 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>arr의 행의 개수는 1 이상 1024 이하이며, 2의 거듭 제곱수 형태를 하고 있습니다. 즉, arr의 행의 개수는 1, 2, 4, 8, ..., 1024 중 하나입니다.
arr의 각 행의 길이는 arr의 행의 개수와 같습니다. 즉, arr은 정사각형 배열입니다.
arr의 각 행에 있는 모든 값은 0 또는 1 입니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(arr) {
    if(arr.length === 1) return arr[0][0] ? [0,1] : [1,0];
    const answer = [0,0];
    const len = arr.length;
    const half = len / 2;

    const number = arr[0][0];
    let flag = true;

    out:for(let i=0; i&lt;len; i++){
        for(let j=0; j&lt;len; j++){
            if(number !== arr[i][j]){
                flag = false;
                break out;
            }
        }
    }
    if(flag){
        answer[number]++;
        return answer;
    }

    for(let i=0; i&lt;2; i++){
        for(let j=0; j&lt;2; j++){
            const compressArr = [];
            for(let k=0; k&lt;half; k++){
                compressArr.push(arr[(half * i) + k].slice(j * half, (j+1) * half));
            }
            const [zero, one] = solution(compressArr);
            answer[0]+=zero;
            answer[1]+=one;
        }
    }
    return answer;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>배열 크기가 1이면 값에따라 0, 1 개수를 반환해준다.
각 절차마다 반환을 위한 정답 배열 answer를 선언 후 정사각행렬 arr를 총 4개의 가장 큰 정사각형으로 나눠야 하기 때문에 절반 값을 취한다.
현재 재귀단계에서, 모든 행렬의 구성 값이 같다면 하나로 압출해줄 number을 만들어 주고 flag변수로 행렬 구성 상태를 파악해준다.(만약 행렬의 구성값이 모두 같지 않다면 flag를 통해 빠르게 반복문을 탈출한다. / 행렬의 구성값이 모두 같다면 number값에 따라 0, 1 개수를 반환한다.)</p>
<p>행렬 구성 값이 통일 되지 않았을 때 진행될 반복문을 만들어 준다.
압축을 위해 다음 재귀에 넣어줄 compressArr행렬을 선언 후 i,j값이 변할 때 arr을 구성하는 가장 큰 정사각행렬의 값을 총 i*j = 4로 4개를 구해준다.
이후 4개의 정사각행렬을 각각 다음 재귀로 넣어주고 반환 값을 [zero, one] 변수에 담아서 answer 배열에 넣어준다.</p>
<h1 id="기술면접-문제-풀어보기">기술면접 문제 풀어보기</h1>
<h2 id="15-dfs와-bfs의-차이를-말해주세요">15. DFS와 BFS의 차이를 말해주세요.</h2>
<ul>
<li><strong>두 방법 모두 그래프를 탐색하는 방법.</strong><h3 id="dfs-depth-first-search---깊이-우선-탐색">DFS (Depth-First Search) - 깊이 우선 탐색</h3>
<img src="https://velog.velcdn.com/images/v_rien/post/9aca2eb1-e129-407e-9229-3e249aa81a10/image.gif" alt=""></li>
<li>루트 노드(혹은 다른 임의의 노드)에서 시작해서 다음 분기로 넘어가기 전에 해당 분기를 <strong>완벽하게 탐색하는 방식</strong>.</li>
<li>ex) 미로찾기를 할때 최대한 한 방향으로 갈 수 있을 때까지 쭉 가다가 더 이상 <strong>갈 수 없게 되면</strong> 다시 가장 가까운 갈림길로 돌아와서 그 <strong>갈림길부터 다시 다른 방향으로 탐색을 진행</strong>하는 것.</li>
<li><strong>모든 노드를 방문하고자 하는 경우</strong> 해당 방법 선택.</li>
<li>BFS(너비 우선 탐색)보다 좀 더 간단함.</li>
<li>BFS(너비 우선 탐색)보다 검색 속도가 느림.<h3 id="bfs-breadth-first-search">BFS (Breadth-First Search)</h3>
<img src="https://velog.velcdn.com/images/v_rien/post/e93bc68e-f04a-48e3-8a03-767d762c9bb0/image.gif" alt=""></li>
<li>루트 노드(혹은 다른 임의의 노드)에서 시작해 <strong>인접한 노드를 먼저 탐색하는 방식</strong>.</li>
<li>시작 정점으로부터 가까운 정점을 먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문.</li>
<li>주로 <strong>두 노드 사이의 최단 경로를 찾고 싶을 때</strong> 사용.</li>
<li>ex) 지구 상에 존재하는 모든 친구 관계를 그래프로 표현한 후 Sam과 Eddie사이에 존재하는 경로를 찾을 때<ul>
<li>DFS : 모든 친구 관계를 다 살펴봐야 할 수 있음.</li>
<li>BFS : Sam과 가까운 관계부터 검색.</li>
</ul>
</li>
</ul>
<h3 id="차이점">차이점</h3>
<table>
<thead>
<tr>
<th align="center">DFS(깊이우선탐색)</th>
<th align="center">BFS(너비우선탐색)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">현재 정점에서 갈 수 있는 점들까지 들어가면서 탐색</td>
<td align="center">현재 정점에 연결된 가까운 점들부터 탐색</td>
</tr>
<tr>
<td align="center">스택 또는 재귀함수로 구현</td>
<td align="center">큐를 이용해서 구현</td>
</tr>
</tbody></table>
<h1 id="최종프로젝트">최종프로젝트</h1>
<p> merge후 있는 오류들을 해결 후 moveHandler를 작성했다.
 내일 작업할 것 : 우선 맡은 부분 마무리(Despown, Animation)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 77일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-77%EC%9D%BC%EC%B0%A8-f6jt3poo</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-77%EC%9D%BC%EC%B0%A8-f6jt3poo</guid>
            <pubDate>Thu, 28 Nov 2024 12:07:51 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="소수-찾기">소수 찾기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다.</p>
<p>각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>numbers는 길이 1 이상 7 이하인 문자열입니다.
numbers는 0~9까지 숫자만으로 이루어져 있습니다.
&quot;013&quot;은 0, 1, 3 숫자가 적힌 종이 조각이 흩어져있다는 의미입니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(numbers) {
    var result = new Set();

    function getPermutation (arr, fixed) {
        if(arr.length &gt;= 1) {
            for (let i=0; i&lt;arr.length; i++) {
                const newFixed = fixed + arr[i];
                const copyArr = [...arr];
                copyArr.splice(i, 1);

                if(isPrimeNumber(parseInt(newFixed))) {
                    result.add(parseInt(newFixed));
                }

                getPermutation(copyArr, newFixed);
            }
        }
    }

    function isPrimeNumber(num) {
        if (num &lt;= 1) return false;
        if (num === 2) return true;
        for( let i = 2; i &lt;= Math.sqrt(num); i++) {
           if(num % i === 0) {
               return false;
           }
        }
        return true;
    }

    getPermutation(numbers, &#39;&#39;)

    return result.size;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>순열을 찾을 함수, 소수일때 true를 반환하는 함수를 만든다.</p>
<h1 id="기술면접-문제-풀어보기">기술면접 문제 풀어보기</h1>
<h2 id="14-다음의-정렬을-설명하고-본인이-가장-편한-언어를-사용하여-로직을-구현해주세요">14. 다음의 정렬을 설명하고 본인이 가장 편한 언어를 사용하여 로직을 구현해주세요</h2>
<pre><code>- 선택 정렬(Selection Sort)
- 버블 정렬(Bubble Sort)
- 병합 정렬(Merge Sort)
- 삽입 정렬(Insertion Sort)
- 퀵 정렬(Quick Sort)
- 힙 정렬(Heap Sort)</code></pre><h3 id="선택-정렬selection-sort">선택 정렬(Selection Sort)</h3>
<ul>
<li>시간 복잡도 : O(n^2)</li>
<li>가장 작은 요소를 찾아서 맨 앞의 요소와 교환하는 방식.</li>
<li>예시 코드<pre><code>def selection_sort(arr):
  for i in range(len(arr)):
      min_index = i
      for j in range(i + 1, len(arr)):
          if arr[j] &lt; arr[min_index]:
              min_index = j
      arr[i], arr[min_index] = arr[min_index], arr[i]
  return arr
</code></pre></li>
</ul>
<h1 id="예시-사용">예시 사용</h1>
<p>print(selection_sort([64, 25, 12, 22, 11]))</p>
<pre><code>### 버블 정렬(Bubble Sort)
- 시간 복잡도 : O(n^2)
- 인접한 두 요소를 비교해서 큰 값을 뒤로 보내는 방식.
- 예시 코드</code></pre><p>def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] &gt; arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr</p>
<h1 id="예시-사용-1">예시 사용</h1>
<p>print(bubble_sort([64, 34, 25, 12, 22, 11, 90]))</p>
<pre><code>### 병합 정렬(Merge Sort)
- 시간 복잡도 : O(nlogn)
- 배열을 반으로 나눠서 재귀적으로 정렬 후, 나눠진 배열을 병합하면서 최종 정렬을 만듦 (배열의 길이가 1이 될 때까지 나눔)
- 예시 코드</code></pre><p>def merge_sort(arr):
    # 배열의 길이가 2보다 작으면 이미 정렬된 상태이므로 그대로 반환
    if len(arr) &lt; 2:
        return arr</p>
<pre><code># 중간 인덱스 계산
mid = len(arr) // 2

# 배열을 두 개의 하위 배열로 나누고 재귀적으로 정렬
left = merge_sort(arr[:mid])  # 왼쪽 하위 배열
right = merge_sort(arr[mid:])  # 오른쪽 하위 배열

# 두 개의 하위 배열을 병합하기 위한 배열 초기화
merged_arr = []
l = r = 0  # 왼쪽과 오른쪽 하위 배열의 인덱스 초기화

# 두 하위 배열을 비교하여 정렬된 배열을 만듭니다
while l &lt; len(left) and r &lt; len(right):
    if left[l] &lt; right[r]:
        merged_arr.append(left[l])  # 왼쪽 배열의 요소를 추가
        l += 1  # 왼쪽 인덱스 증가
    else:
        merged_arr.append(right[r])  # 오른쪽 배열의 요소를 추가
        r += 1  # 오른쪽 인덱스 증가

# 남은 요소가 있을 경우 추가
merged_arr += left[l:]  # 왼쪽 배열에 남은 요소 추가
merged_arr += right[r:]  # 오른쪽 배열에 남은 요소 추가

return merged_arr  # 병합된 배열 반환</code></pre><pre><code>### 삽입 정렬(Insertion Sort)
- 시간 복잡도 : O(n^2)
- 1번 인덱스부터 시작해서 앞에 있는 요소들과 비교해서 현재 인덱스의 값보다 작은 값을 만나면 그 작은 값의 뒤에 삽입하는 방식
- 예시 코드</code></pre><p>def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1
        while j &gt;= 0 and key &lt; arr[j]:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key
    return arr</p>
<h1 id="예시-사용-2">예시 사용</h1>
<p>print(insertion_sort([12, 11, 13, 5, 6]))</p>
<pre><code>### 퀵 정렬(Quick Sort)
- 시간 복잡도 : O(nlogn)
- 기준이 될 피벗을 선책하고 피벗보다 작은 값은 왼쪽에, 큰 요소는 오른쪽에 배치하는 방식
- 예시 코드</code></pre><p>def quick_sort(arr):
    # 배열의 길이가 1 이하인 경우, 이미 정렬된 상태이므로 그대로 반환
    if len(arr) &lt;= 1:
        return arr</p>
<pre><code># 피벗(pivot) 설정: 배열의 중간 요소를 피벗으로 선택
pivot = arr[len(arr) // 2]

# 피벗보다 작은 요소들로 이루어진 배열 생성
left = [x for x in arr if x &lt; pivot]

# 피벗과 같은 요소들로 이루어진 배열 생성
middle = [x for x in arr if x == pivot]

# 피벗보다 큰 요소들로 이루어진 배열 생성
right = [x for x in arr if x &gt; pivot]

# 재귀적으로 왼쪽, 중간, 오른쪽 배열을 정렬하고 병합하여 반환
return quick_sort(left) + middle + quick_sort(right)</code></pre><h1 id="예시-사용-3">예시 사용</h1>
<p>print(quick_sort([3, 6, 8, 10, 1, 2, 1]))  # 출력: [1, 1, 2, 3, 6, 8, 10]</p>
<pre><code>### 힙 정렬(Heap Sort)
- 시간 복잡도 : O(nlogn)
- 완전 이진 트리를 이용한 최대 힙이나 최소 힙 트리 구조를 활용해 정렬하는 방식
- 배열로부터 최대 힙 구성 =&gt; 루트(가장 큰 값)에 배열 마지막 요소를 넣고 다시 최대 힙 구성
- 예시 코드</code></pre><p>def heapify(arr, n, i):
    largest = i  # 현재 노드의 인덱스
    left = 2 * i + 1  # 왼쪽 자식 노드의 인덱스
    right = 2 * i + 2  # 오른쪽 자식 노드의 인덱스</p>
<pre><code># 왼쪽 자식이 현재 노드보다 큰 경우
if left &lt; n and arr[i] &lt; arr[left]:
    largest = left  # largest를 왼쪽 자식으로 업데이트

# 오른쪽 자식이 현재 largest보다 큰 경우
if right &lt; n and arr[largest] &lt; arr[right]:
    largest = right  # largest를 오른쪽 자식으로 업데이트

# largest가 현재 노드가 아닌 경우
if largest != i:
    arr[i], arr[largest] = arr[largest], arr[i]  # swap
    heapify(arr, n, largest)  # 재귀적으로 heapify 호출</code></pre><p>def heap_sort(arr):
    n = len(arr)  # 배열의 길이</p>
<pre><code># 배열을 힙으로 변환 (최대 힙 생성)
for i in range(n // 2 - 1, -1, -1):
    heapify(arr, n, i)

# 힙에서 요소를 하나씩 꺼내어 정렬
for i in range(n - 1, 0, -1):
    arr[i], arr[0] = arr[0], arr[i]  # 최대 요소를 배열의 끝으로 이동 (swap)
    heapify(arr, i, 0)  # 남은 힙을 다시 힙화

return arr  # 정렬된 배열 반환</code></pre><h1 id="예시-사용-4">예시 사용</h1>
<p>print(heap_sort([12, 11, 13, 5, 6, 7]))  # 출력: [5, 6, 7, 11, 12, 13]</p>
<pre><code>
![](https://velog.velcdn.com/images/v_rien/post/65506adb-d71f-45bc-98d9-a4649cf9753a/image.png)

# 최종프로젝트
이전부터 계속 있던 문제의 원인을 찾았다. loadAssets.js는 이전에 사용했던 것을 가져와서 사용한것이니 문제가 없을거라 생각하고 getJobById가 값을 못찾는다 생각해서 이부분만 확인했었는데 알고보니![](https://velog.velcdn.com/images/v_rien/post/2d90e85f-3949-4f27-85bb-9d758a00cbdc/image.PNG)
game.proto에서 C_Enter가 받는 payload값을 nickname과 job으로 해두고 정작 enterHandler에서는 nickname과 class를 받으려했다. 그러니 당연히 class값이 undefined가 뜨지...
![](https://velog.velcdn.com/images/v_rien/post/4cf0376d-064e-41fe-8bc5-c932c82c8863/image.PNG)
위 사진과 같이 class =&gt; job으로 바꿔주니 잘 작동했다..이거한다고 시간 많이 버렷는데 간단한 문제였고 저번 개인 과제때처럼 오타검수를 해봣더라면 문제 없었을텐데..너무 아쉽다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 76일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-76%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-76%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 26 Nov 2024 12:10:39 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="가장-큰-수">가장 큰 수</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.</p>
<p>예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰 수는 6210입니다.</p>
<p>0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<blockquote>
<p>numbers의 길이는 1 이상 100,000 이하입니다.
numbers의 원소는 0 이상 1,000 이하입니다.
정답이 너무 클 수 있으니 문자열로 바꾸어 return 합니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(numbers) {
    const answer = numbers
      .map((number) =&gt; number.toString())
      .sort((a, b) =&gt; b + a - (a + b))
      .join(&quot;&quot;);

    return answer[0] === &quot;0&quot; ? &quot;0&quot; : answer;
  }</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<ul>
<li>모든 숫자를 스트링으로 변환 후 해당 수와 다음 수를 이어 붙였을 때 더 큰 순서대로 정렬 해주고 이어 붙여주면 된다. 혹시 0만 있는 케이스가 있을수도 있으므로 0에 대한 처리를 해줫다.</li>
</ul>
<h1 id="기술-면접-문제-풀어보기">기술 면접 문제 풀어보기</h1>
<h2 id="13bigo에-대해-설명해주세요">13.BigO에 대해 설명해주세요.</h2>
<h3 id="bigo">BigO</h3>
<ul>
<li><p>대략적으로 숫자를 세는 공식적인 표현 =&gt; 입력된 내용이 늘어날수록 <strong>알고리즘에 실행 시간이 어떻게 변하는지 설명하는 공식적인 방식.</strong></p>
<h3 id="bigo의-시간-복잡도">BigO의 시간 복잡도</h3>
</li>
<li><p>입력이 커질수록 알고리즘의 실행 속도가 어떻게 바뀌는지 표현.</p>
<h4 id="1-1-o1--상수">1-1) O(1) : 상수</h4>
<blockquote>
</blockquote>
<p>```
/*
 n의 값이 늘어나도 실행 되는 시간에는 아무런 영향을 받지 않는다. 
 시간복잡도 O(1)</p>
</li>
<li><p>/
function addSec(n) {
   return n * (n + 1 ) / 2;
}</p>
<pre><code></code></pre></li>
<li><p>O(1) 상수 : n(입력값)이 커져도 실행 시간에 아무런 영향도 받지 않을 경우(항상 상수) 나타내는 표현</p>
</li>
</ul>
<h4 id="1-2-on--선형">1-2) O(n) : 선형</h4>
<blockquote>
</blockquote>
<pre><code>/* 
  실행되는 시간이 n의 값이 늘어나는것과 비례하게 1:1비율로, 선형으로 늘어난다.
  시간복잡도 O(n) 
*/
function addFirst(n) {
    var total = 0;
    for (let i=0; i&lt;=n; i++) { 
        total +=i;
    }
    return total
}</code></pre><ul>
<li>O(n) 선형 : n(입력 값)이 커질수록 실행 시간도 같이 늘어난다.</li>
</ul>
<h4 id="1-3-on2">1-3) O(n^2)</h4>
<blockquote>
</blockquote>
<pre><code>/*
  중첩 루프(for문)을 가진다. O(n) 연산 안에 O(n)을 가지고 있으면 O(n^2)이 된다.
  시간복잡도 O(n^2)
*/
function bigO(n) {
    for (let i=0; i &lt; n; i++) {
        for (let j=0; j &lt; n; j++) {
            console.log(i, j);
        }
    }
}</code></pre><ul>
<li>O(n^2) : 실행 시간이 n의 제곱일 경우</li>
</ul>
<h4 id="1-4-olog-n">1-4) O(log n)</h4>
<blockquote>
</blockquote>
<pre><code>/*
  입력의 크기에 따라 처리 시간이 증가하는 정렬 알고리즘에 사용 된다.
  시간 복잡도 O(log n)
*/
function bigO(n) {
    for (let i = 2; i &lt;= n; i*2) {
        console.log(i);
    }
}</code></pre><h3 id="bigo의-공간-복잡도">BigO의 공간 복잡도</h3>
<ul>
<li>입력이 커질수록 알고리즘이 얼마나 많은 공간을 차지하는지 표현. =&gt; 알고리즘 자체가 필요로 하는 공간을 의미.<h4 id="기본-규칙">기본 규칙</h4>
</li>
<li>Boolean, number, undefined, null은 불변 공간으로 O(1)를 갖는다.</li>
<li>reference타입, 배열, 객체 문자열은 O(n) 공간이 필요하다.<h4 id="2-1-o1--상수">2-1) O(1) : 상수</h4>
<blockquote>
</blockquote>
<pre><code>// 입려시 이미 배열의 길이가 정해져있어 변하는것이 아니기 때문에 공간 복잡도 O(1)를 갖는다.
function sum(arr) {
  let total = 0;
  for (let i=0; i &lt; arr.length; i++) {
      total += arr[i];
  }
}</code></pre></li>
</ul>
<h4 id="2-2-on--선형">2-2) O(n) : 선형</h4>
<blockquote>
</blockquote>
<pre><code>// arr길이 만큼 newArr의 공간이 생기기 때문에 공간 복잡도 O(n)을 갖는다.
function double(arr) {
    let newArr = [];
    for (let i=0; i &lt; arr.length; i++) {
        newArr.push([i]);
    }
}</code></pre><h1 id="최종-프로젝트">최종 프로젝트</h1>
<p>이전에 짯던 기본 구조에서 패킷 사이즈와 패킷 아이디를 받아 전체 패킷 크기를 구하는 부분에 문제가 있는 것 같아 구조를 뜯어고쳣다.
proto파일을 받아오는 loadProto와 비슷한 구조로 assets폴더 안에 있는 json파일을 읽을 수 있는 loadAssets.js를 만들었는데 loadAsset의 gameAssets를 받아서 사용하는 함수 getJobById가 제대로 작동하지 않는다.</p>
<ul>
<li>문제점 : enterhandler에서 loadAssets를 이용해 jobid를 받아와 그 id에 해당하는 스텟을 createUser를 통해 user에 넣으려하는데 loadAssets에서 jobid를 받아오는 getJobById()가 작동을 안함.(실행 시 undefined가 뜸.)</li>
</ul>
<p>어떤 부분이 잘못된것인지 잘 모르겠어서 내일 조금 더 건드려본 다음에 튜터님께 질문해봐야겠다.
아마 이부분만 잘 처리되면 게임 실행에서는 문제가 없을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 75일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-75%EC%9D%BC%EC%B0%A8-4p62w5ze</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-75%EC%9D%BC%EC%B0%A8-4p62w5ze</guid>
            <pubDate>Tue, 26 Nov 2024 12:09:56 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="다리를-지나는-트럭">다리를 지나는 트럭</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>트럭 여러 대가 강을 가로지르는 일차선 다리를 정해진 순으로 건너려 합니다. 모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 알아내야 합니다. 다리에는 트럭이 최대 bridge_length대 올라갈 수 있으며, 다리는 weight 이하까지의 무게를 견딜 수 있습니다. 단, 다리에 완전히 오르지 않은 트럭의 무게는 무시합니다.</p>
<p>예를 들어, 트럭 2대가 올라갈 수 있고 무게를 10kg까지 견디는 다리가 있습니다. 무게가 [7, 4, 5, 6]kg인 트럭이 순서대로 최단 시간 안에 다리를 건너려면 다음과 같이 건너야 합니다.</p>
<table>
<thead>
<tr>
<th align="center">경과 시간</th>
<th align="center">다리를 지난 트럭</th>
<th align="center">다리를 건너는 트럭</th>
<th align="center">대기 트럭</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">[]</td>
<td align="center">[]</td>
<td align="center">[7,4,5,6]</td>
</tr>
<tr>
<td align="center">1~2</td>
<td align="center">[]</td>
<td align="center">[7]</td>
<td align="center">[4,5,6]</td>
</tr>
<tr>
<td align="center">3</td>
<td align="center">[7]</td>
<td align="center">[4]</td>
<td align="center">[5,6]</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">[7]</td>
<td align="center">[4,5]</td>
<td align="center">[6]</td>
</tr>
<tr>
<td align="center">5</td>
<td align="center">[7,4]</td>
<td align="center">[5]</td>
<td align="center">[6]</td>
</tr>
<tr>
<td align="center">6~7</td>
<td align="center">[7,4,5]</td>
<td align="center">[6]</td>
<td align="center">[]</td>
</tr>
<tr>
<td align="center">8</td>
<td align="center">[7,4,5,6]</td>
<td align="center">[]</td>
<td align="center">[]</td>
</tr>
<tr>
<td align="center">따라서, 모든 트럭이 다리를 지나려면 최소 8초가 걸립니다.</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<p>solution 함수의 매개변수로 다리에 올라갈 수 있는 트럭 수 bridge_length, 다리가 견딜 수 있는 무게 weight, 트럭 별 무게 truck_weights가 주어집니다. 이때 모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 return 하도록 solution 함수를 완성하세요.</p>
<h3 id="제한-조건">제한 조건</h3>
<blockquote>
<p>bridge_length는 1 이상 10,000 이하입니다.
weight는 1 이상 10,000 이하입니다.
truck_weights의 길이는 1 이상 10,000 이하입니다.
모든 트럭의 무게는 1 이상 weight 이하입니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(bridge_length, weight, truck_weights) {

    var time = 0;
    var bridge = new Array(bridge_length).fill(0);
    var current_bridge = 0;

    while(truck_weights.length &gt; 0 || current_bridge &gt; 0) {

        time++;
        current_bridge -= bridge.shift();

        if(truck_weights.length &gt; 0 ){
            if(truck_weights[0] + current_bridge &lt;= weight){
                const temp = truck_weights.splice(0,1)[0];
                current_bridge += temp;
                bridge.push(temp);
            }else{
                bridge.push(0);
            }
        }
    }

    return time;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<ul>
<li>시간 = 반복문을 통해 작업이 진행된 횟수. =&gt; time = 0 으로 선언 후 반복문을 돌때마다 +1해준다.</li>
<li>마지막 트럭이 지난 이후 반복문이 끝나게 하기 위해 반복문의 조건인 트럭별 무게의 길이(truck_weights.length)가 0보다 크거나 현재 다리에 올라간 트럭 무게가 0보다 크게 잡는다.</li>
<li>bridge 배열을 만든 후 현재 다리에 올라간 트럭들을 넣을 곳으로 만들어 주고 반복문을 돌면서 truck_weights 0번 인덱스와 현재 다리에 올라간 트럭 개수만큼 더해서 weight랑 비교하고 적거나 같을 경우는 truct_weights 배열 첫번째 있는걸 bridge 배열에 넣고, 현재 무게에 그대로 더하면된다.</li>
<li>만약 weight보다 클 경우 push(0) 을 계속 해주면된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 74일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-74%EC%9D%BC%EC%B0%A8-3km82apx</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-74%EC%9D%BC%EC%B0%A8-3km82apx</guid>
            <pubDate>Tue, 26 Nov 2024 12:08:14 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="2개-이하로-다른-비트">2개 이하로 다른 비트</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>양의 정수 x에 대한 함수 f(x)를 다음과 같이 정의합니다.</p>
<p>x보다 크고 x와 비트가 1~2개 다른 수들 중에서 제일 작은 수
예를 들어,</p>
<p>f(2) = 3 입니다. 다음 표와 같이 2보다 큰 수들 중에서 비트가 다른 지점이 2개 이하이면서 제일 작은 수가 3이기 때문입니다.</p>
<table>
<thead>
<tr>
<th align="center">수</th>
<th align="center">비트</th>
<th align="center">다른 비트의 개수</th>
</tr>
</thead>
<tbody><tr>
<td align="center">2</td>
<td align="center">000...0010</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">3</td>
<td align="center">000...0011</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">f(7) = 11 입니다. 다음 표와 같이 7보다 큰 수들 중에서 비트가 다른 지점이 2개 이하이면서 제일 작은 수가 11이기 때문입니다.</td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center">수</th>
<th align="center">비트</th>
<th align="center">다른 비트의 개수</th>
</tr>
</thead>
<tbody><tr>
<td align="center">7</td>
<td align="center">000...0111</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">8</td>
<td align="center">000...1000</td>
<td align="center">4</td>
</tr>
<tr>
<td align="center">9</td>
<td align="center">000...1001</td>
<td align="center">3</td>
</tr>
<tr>
<td align="center">10</td>
<td align="center">000...1010</td>
<td align="center">3</td>
</tr>
<tr>
<td align="center">11</td>
<td align="center">000...1011</td>
<td align="center">2</td>
</tr>
<tr>
<td align="center">정수들이 담긴 배열 numbers가 매개변수로 주어집니다. numbers의 모든 수들에 대하여 각 수의 f 값을 배열에 차례대로 담아 return 하도록 solution 함수를 완성해주세요.</td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>1 ≤ numbers의 길이 ≤ 100,000
0 ≤ numbers의 모든 수 ≤ 1015</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(numbers) {
    var answer = [];
    for (i = 0; i &lt; numbers.length; i++) {
        var current = numbers[i];
        if (current % 2 === 0) {
          answer.push(current + 1);
        } else {
          current = &quot;0&quot; + current.toString(2);
          var totalLength = current.length;
          for (j = totalLength - 1; j &gt;= 0; j--) {
            if (+current[j] === 0) {
              answer.push(parseInt(current.substring(0, j) + &quot;10&quot; + current.substring(j + 2, totalLength), 2 )
              );
              break;
            }
          }
        }
      }
    return answer;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<ul>
<li>숫자가 짝수일 때 이진수로 변환하면 마지막이 무조건 0이기 때문에 마지막 자리만 바꿔주면 되므로 1을 더한다.</li>
<li>숫자가 홀수일 때는 이진수로 변환 후 0이 처음 나오는 자릿수를 찾고, 그 전자리수는 1이 확실하기 때문에 01을 제거하고 10을 넣어 해당 이진수를 숫자로 변환 후 answer에 더한다.</li>
</ul>
<h1 id="기술-면접-문제-풀어보기">기술 면접 문제 풀어보기</h1>
<h2 id="12-동기와-비동기의-차이-블락킹과-논블락킹의-차이-비동기와-논블락킹에-차이에-대해-설명해주세요">12. 동기와 비동기의 차이, 블락킹과 논블락킹의 차이, 비동기와 논블락킹에 차이에 대해 설명해주세요.</h2>
<ul>
<li><p><strong>동기</strong> : 한 작업이 끝나야 다음 작업이 시작되는 순차적인 처리 방식. =&gt; <strong>작업이 완료되기 전에 다른 작업을 수행할 수 없음.</strong></p>
</li>
<li><p><strong>비동기</strong> : 한 작업이 끝나기를 기다리지 않고 다음 작업을 실행할 수 있음. =&gt;  <strong>작업 완료 여부와 상관없이 다른 작업을 동시에 진행할 수 있음.</strong></p>
</li>
<li><p><strong>블락킹</strong> : I/O 작업(파일 읽기, 네트워크 요청)을 요청할 때 해당 작업의 완료 전까지 프로그램의 실행이 멈춤. =&gt; <strong>I/O작업의 완료 전까지 다른 어떤 작업도 수행할 수 없음.</strong></p>
</li>
<li><p><strong>논블락킹</strong> : I/O 작업 요청 후에도 프로그램의 실행이 멈추지 않고 즉시 다음 작업으로 넘어감. =&gt; <strong>I/O 작업의 완료 여부와 상관없이 프로그램이 계속 실행 가능.</strong></p>
</li>
<li><p>비동기와 논블락킹의 차이 : 비동기의 경우 작업을 <strong>어떠한 흐름으로 처리</strong>하는지에 대한 내용이고 논블락킹의 경우 처리하는 작업이<strong>전체의 흐름을 어떻게 처리하는가(막는다, 막지않는다)</strong>에 대한 내용이다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 73일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-73%EC%9D%BC%EC%B0%A8-ah2601ox</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-73%EC%9D%BC%EC%B0%A8-ah2601ox</guid>
            <pubDate>Thu, 21 Nov 2024 13:24:09 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="숫자-변환하기">숫자 변환하기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>자연수 x를 y로 변환하려고 합니다. 사용할 수 있는 연산은 다음과 같습니다.</p>
<p>x에 n을 더합니다
x에 2를 곱합니다.
x에 3을 곱합니다.
자연수 x, y, n이 매개변수로 주어질 때, x를 y로 변환하기 위해 필요한 최소 연산 횟수를 return하도록 solution 함수를 완성해주세요. 이때 x를 y로 만들 수 없다면 -1을 return 해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>1 ≤ x ≤ y ≤ 1,000,000
1 ≤ n &lt; y</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(x, y, n) {
    var que = [[y, 0]];

    while(que.length) {
        const [num, i] = que.shift();

        if(num === x){ 
            return i;
        }

        if(num%2 === 0 &amp;&amp; num/2 &gt;= x){
            que.push([num/2, i+1]);
        }
        if(num%3 === 0 &amp;&amp; num/3 &gt;= x){
            que.push([num/3, i+1]);
        }
        if(num-n &gt;= x){
            que.push([num-n, i+1]);
        }
    }
    return -1;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<ul>
<li>큐(queue)를 이용해 문제를 풀 수 있다.<ul>
<li>선입선출(FIFO)의 특징을 가진다.</li>
</ul>
</li>
</ul>
<p>y값을 기준으로 x값을 도달하기 위해 큐의 초기값을 y, 횟수를 0으로 세팅한다.
큐에 값이 없을 때 까지 반복하는 반복문을 만들어서 먼저 들어온 순으로 값을 가져오고(FIFO, First In, First Out) 조건문을 사용한다.</p>
<blockquote>
<p>조건문</p>
</blockquote>
<ul>
<li>첫번째 : n값을 빼도 x보다 크거나 같은 경우.</li>
<li>두번째 : 2로 나누어 떨어질 때.</li>
<li>세번째 : 3으로 나누어 떨어질 때.</li>
</ul>
<p>위의 조건에 부합한 경우에만 큐에 값을 저장하고, num값이 x와 같다면 i를 return해준다. 만약 반복문 안에서 return되지 않는 값은 x값과 같아질 수 없다는 의미이므로 -1을 return한다. </p>
<h1 id="기술면접-문제-풀어보기">기술면접 문제 풀어보기</h1>
<h2 id="10-nodejs의-이벤트-루프란-무엇이고-왜-필요하며-어떻게-작동하는지-아는-만큼-설명해주세요">10. Node.js의 이벤트 루프란 무엇이고 왜 필요하며 어떻게 작동하는지 아는 만큼 설명해주세요.</h2>
<ul>
<li><p>Node.js는 single-theaded 기반으로 동작하는데, 단일 thread가 가지는 단점을 보완하기 위해 I/O 작업의 처리에 비동기 논-블로킹 방식으로 동작하는 이벤트 루프를 사용합니다. 이벤트 루프는 멀티 쓰레드로 동작하는 시스템 커널에 I/O 작업을 넘겨주고, 작업이 백그라운드에서 처리되어 완료되면 처리 결과가 다시 Node.js로 돌아와 poll queue에 callback의 형태로 추가된 뒤 동기적으로 처리됩니다.</p>
</li>
<li><p>이벤트 루프에는 여러 페이즈가 존재합니다. 먼저 Timer 페이즈는 setTimeout과 setInterval로 스케쥴 된 callback 함수를 실행합니다. 다음 pending callbacks 페이즈는 I/O 콜백을 실행하고, idle/prepare 페이즈는 내부적으로 동작하며 이름 그대로 대기상태와 관련된 처리를 합니다. Poll 페이즈는 Timer 및 close 페이즈의 callback을 제외한 다른 모든 callback을 실행하며, setImmediate 를 호출한다면 이 구간에서 block 되고, Check 페이즈에서 실행됩니다. 마지막 close 페이즈에서는 소켓의 close나 database 연결 종료, 서버의 termination 등의 종료 작업이 처리됩니다.</p>
</li>
</ul>
<h2 id="11-nodejs의-libuv-라이브러리에-대해-설명해주세요">11. Node.js의 libUV 라이브러리에 대해 설명해주세요.</h2>
<ul>
<li>Libuv 라이브러리는 C언어로 작성된 라이브러리입니다. 핵심이 되는 두 가지 기능으로는 이벤트 루프와 uv_io가 있습니다. 이벤트 루프는 6개의 페이즈로 나뉘어있고, 라운드 로빈 방식으로 각 페이즈를 순회하며 non-blocking 작업을 담당합니다. uv_io는 OS 커널이 지원하는 비동기 함수 호출을 통한 비동기 작업의 처리와,  worker thread를 이용하여 커널이 지원하지 않는 작업이나 파일 시스템 관련 작업 및 blocking 작업 처리를 담당합니다. 매 작업 종료 시 작업의 종류에 따라 이벤트 루프의 알맞는 위치에 callback을 등록하기도 합니다.</li>
</ul>
<h1 id="최종프로젝트">최종프로젝트</h1>
<p>이전에 했던 팀프로젝트와 그 전 기수의 코드를 참고해가며 기본구조를 완성했다. Enter핸들러는 매칭시스템이랑 비슷하다고 생각했었는데 삽질 좀 하다 보니까 매칭시스템보다는 이전 개인프로젝트때 서버에 접속하던 방식과 비슷하다는걸 깨달았다. 
내일 할 것 : Enter 마무리, 나머지 맡은 부분 진행.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 72일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-72%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-72%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 20 Nov 2024 13:22:02 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="롤케이크-자르기">롤케이크 자르기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>철수는 롤케이크를 두 조각으로 잘라서 동생과 한 조각씩 나눠 먹으려고 합니다. 이 롤케이크에는 여러가지 토핑들이 일렬로 올려져 있습니다. 철수와 동생은 롤케이크를 공평하게 나눠먹으려 하는데, 그들은 롤케이크의 크기보다 롤케이크 위에 올려진 토핑들의 종류에 더 관심이 많습니다. 그래서 잘린 조각들의 크기와 올려진 토핑의 개수에 상관없이 각 조각에 동일한 가짓수의 토핑이 올라가면 공평하게 롤케이크가 나누어진 것으로 생각합니다.</p>
<p>예를 들어, 롤케이크에 4가지 종류의 토핑이 올려져 있다고 합시다. 토핑들을 1, 2, 3, 4와 같이 번호로 표시했을 때, 케이크 위에 토핑들이 [1, 2, 1, 3, 1, 4, 1, 2] 순서로 올려져 있습니다. 만약 세 번째 토핑(1)과 네 번째 토핑(3) 사이를 자르면 롤케이크의 토핑은 [1, 2, 1], [3, 1, 4, 1, 2]로 나뉘게 됩니다. 철수가 [1, 2, 1]이 놓인 조각을, 동생이 [3, 1, 4, 1, 2]가 놓인 조각을 먹게 되면 철수는 두 가지 토핑(1, 2)을 맛볼 수 있지만, 동생은 네 가지 토핑(1, 2, 3, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것이 아닙니다. 만약 롤케이크의 네 번째 토핑(3)과 다섯 번째 토핑(1) 사이를 자르면 [1, 2, 1, 3], [1, 4, 1, 2]로 나뉘게 됩니다. 이 경우 철수는 세 가지 토핑(1, 2, 3)을, 동생도 세 가지 토핑(1, 2, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것입니다. 공평하게 롤케이크를 자르는 방법은 여러가지 일 수 있습니다. 위의 롤케이크를 [1, 2, 1, 3, 1], [4, 1, 2]으로 잘라도 공평하게 나뉩니다. 어떤 경우에는 롤케이크를 공평하게 나누지 못할 수도 있습니다.</p>
<p>롤케이크에 올려진 토핑들의 번호를 저장한 정수 배열 topping이 매개변수로 주어질 때, 롤케이크를 공평하게 자르는 방법의 수를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>1 ≤ topping의 길이 ≤ 1,000,000
1 ≤ topping의 원소 ≤ 10,000</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(topping) {
    var answer = 0;
    let baseSet=new Set();
    let compareSet=new Set();
    let counter=new Array(10001).fill(0);

    if(topping.length===1){
        return answer;
    }

    topping.map(v=&gt;{
        baseSet.add(v);
        counter[v]++;
    })

    topping.map(v=&gt;{
        if(counter[v]&gt;=1){
            counter[v]--;
        }
        if(counter[v]===0){
            baseSet.delete(v);
        }    
        compareSet.add(v);    
        if(baseSet.size===compareSet.size){
            answer++;
        }
    })

    return answer;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>전체 토핑을 알 수 있는 baseSet과 counter배열을 통해 토핑이 몇개인지, 각 토핑이 몇 개씩 있는지 저장한다.
이후 다시 처음부터 돌면서 비교하는 set객체를 만들어 토핑을 넣고 counter에서는 각 토핑 갯수를 줄여주고 조건문을 통해 토핑갯수가 0인 경우를 baseSet에서 삭제해준다. 마지막으로 baseSet의 크기가 compareSet의 크기가 같다면 answer값을 더해준다.</p>
<h1 id="기술-면접-문제-풀어보기">기술 면접 문제 풀어보기</h1>
<h2 id="9-nodejs는-single-threaded-기반-js-런타임입니다-이에-대해-아는-만큼-설명해주세요">9. Node.js는 single-threaded 기반 JS 런타임입니다. 이에 대해 아는 만큼 설명해주세요.</h2>
<ul>
<li>싱글 스레드이다. = 단일 main thread 위에서 동작함. = 멀티 스레드 환경에서 발생할 수 있는 데드락이나 경쟁 상태와 같은 복잡한 <strong>동시성 문제를 피할 수 있고</strong>, 개발자가 스래드 간의 동기화를 고민할 필요가 없어 <strong>코드가 간단</strong>해짐.</li>
</ul>
<h3 id="--nodejs는-non-blocking-asynchronous-기반-js-런타임입니다-이에-대해-아는-만큼-설명해주세요">- Node.js는 non-blocking, asynchronous 기반 JS 런타임입니다. 이에 대해 아는 만큼 설명해주세요.</h3>
<ul>
<li>이벤트 루프라는 것을 통해 I/O 작업을 시스템 커널에 넘겨 async / non-blocking 방식으로 처리함.</li>
<li>현대의 대부분 커널은 multi-threaded 환경이므로 넘겨받은 작업이 <strong>백그라운드에서 처리</strong>되고, 완료시 이를 Node.js에 알려 이벤트 루프의 <strong>poll queue에 callback을 추가</strong>한 다음, queue가 비어있지 않은 동안에는 <strong>queue에 추가된 callback을 동기적으로 처리</strong>함.</li>
</ul>
<h4 id="nodejs-특징">Node.js 특징</h4>
<ul>
<li>싱글 스레드이다.<ul>
<li>멀티 스레드 환경에서 발생할 수 있는 데드락이나 경쟁 상태와 같은 복잡한 <strong>동시성 문제를 피할 수 있고</strong>, 개발자가 스래드 간의 동기화를 고민할 필요가 없어 <strong>코드가 간단</strong>해짐.</li>
</ul>
</li>
<li><strong>비동기 방식으로 작업을 수행</strong>하므로, 블로킹 없이 <strong>높은 성능을 유지</strong>가능.</li>
<li><strong>이벤트 기반 아키텍처</strong><ul>
<li>이벤트와 콜백함수를 통해 비동기처리를 수행.</li>
</ul>
</li>
<li><strong>다양한 패키지 생태계</strong>가 있고 npm을 통해 <strong>다양한 라이브러리 모듈을 사용</strong>할 수 있음.</li>
<li><strong>크로스 플랫폼</strong>으로 <strong>다양한 운영체제에서 사용 가능</strong>.</li>
</ul>
<h1 id="최종-프로젝트">최종 프로젝트</h1>
<p>enterHandler를 생성 후 C_Enter과 S_Enter부분의 코드를 작성했다.
S_Enter은 서버에서 클라이언트로 player정보를 보내줘야하는데, player에 대한 로직을 짜주면 제대로 작동할 것 같다.
내일 할 것 : S_Enter마무리, S_Spawn, S_Despawn, C_Move, S_Move, C_Animation, S_Animation 코드 구조 짜기.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 71일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-71%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-71%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 20 Nov 2024 13:21:54 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="뒤에-있는-큰-수-찾기">뒤에 있는 큰 수 찾기</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>정수로 이루어진 배열 numbers가 있습니다. 배열 의 각 원소들에 대해 자신보다 뒤에 있는 숫자 중에서 자신보다 크면서 가장 가까이 있는 수를 뒷 큰수라고 합니다.
정수 배열 numbers가 매개변수로 주어질 때, 모든 원소에 대한 뒷 큰수들을 차례로 담은 배열을 return 하도록 solution 함수를 완성해주세요. 단, 뒷 큰수가 존재하지 않는 원소는 -1을 담습니다.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>4 ≤ numbers의 길이 ≤ 1,000,000
1 ≤ numbers[i] ≤ 1,000,000</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(numbers) {
    const result = new Array(numbers.length).fill(-1);
    const stack = [];
    for (i = 0; i &lt; numbers.length; i++) {
        while (numbers[i] &gt; numbers[stack.at(-1)]){
            const index = stack.pop()
            result[index] = numbers[i];
        }

        stack.push(i);
    }
    return result;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>numbers.length에 대한 배열을 result에 넣어주고 fill을 통해 모든 값을 -1로 바꿔준다.
반복문을 통해 이전 값이 현재 값보다 크거나 같으면 stack안에 index를 전부 넣어 놓고 현재값이 이전값보다 크면 stack에 있는 모든 index를 꺼내서 현재값으로 바꿔준다.</p>
<h1 id="기술-면접-문제-풀어보기">기술 면접 문제 풀어보기</h1>
<h2 id="8-jwt에-대해-설명해주세요-구체적으로-jwt를-어디서-처리하는지-어떠한-방식으로-검증하는지-재발급-방식과-주기는-어떻게-처리하는지-다른-api-서비스-호출-시-어떻게-잡아서-인증-처리하는지-말씀해주세요">8. JWT에 대해 설명해주세요. 구체적으로 JWT를 어디서 처리하는지, 어떠한 방식으로 검증하는지, 재발급 방식과 주기는 어떻게 처리하는지, 다른 API 서비스 호출 시 어떻게 잡아서 인증 처리하는지 말씀해주세요.</h2>
<h3 id="jwtjson-web-token">JWT(Json Web Token)</h3>
<ul>
<li>유저를 인증하고 식별하기 위한 토큰(Token)기반 인증.</li>
<li>토큰 자체에 사용자의 권한 정보나 서비스를 사용하기 위한 정보가 포함됨.</li>
<li>RSETful과 같은 무상태(Stateless)인 환경에서 사용자 데이터를 주고받을 수 있게 됨.</li>
<li>세션(Session)을 사용할 경우 쿠키 등을 통해 사용자를 식별하고 서버에 세션을 저장했지만, 토큰을 클라이언트에 저장하고 요청시 HTTP 헤더에 토큰을 첨부하는 것만으로도 단순하게 데이터를 요청하고 응답 받을 수 있음.</li>
<li>서버에 세션 정보를 저장하지 않고 <strong>클라이언트 측에서 토큰을 통해 인증을 처리</strong>.<ul>
<li>서버 부하 ↓</li>
<li>확장성 ↑<h3 id="구조">구조</h3>
<h4 id="헤더header">헤더(Header)</h4>
</li>
</ul>
</li>
<li>토큰의 타입과 어떤 암호화를 사용해 생성된 데이터인지 정의되어있음.<h4 id="페이로드payload">페이로드(Payload)</h4>
</li>
<li>실제 전달하려는 데이터를 담음.
ex) 개발자가 원하는 데이터를 저장.<h4 id="서명signature">서명(Signature)</h4>
</li>
<li>헤더와 페이로드, 그리고 비밀 키(Secret Key)를 이용해 생성됨.</li>
<li>토큰이 변조되지 않은 정상적인 토큰인지 확인할 수 있게 해줌.<h3 id="검증-방식">검증 방식</h3>
<h4 id="1-사용자-인증-요청">1. 사용자 인증 요청</h4>
</li>
<li><ol>
<li><strong>사용자</strong>는 id와 pw를 입력해 <strong>로그인 요청을 시도</strong>함.</li>
</ol>
</li>
<li><ol start="2">
<li>사용자의 id와 pw를 받은 <strong>서버</strong>는 시큐리티 등을 통해 <strong>사용자 인증 로직을 수행</strong>함.<h4 id="2-사용자-인증-완료토큰-발급">2. 사용자 인증 완료(토큰 발급)</h4>
</li>
</ol>
</li>
<li><ol>
<li><strong>서버</strong>는 사용자 인증이 정상적으로 <strong>완료되면 JWT를 생성</strong>함.</li>
</ol>
</li>
<li><ol start="2">
<li>JWT를 <strong>사용자</strong>에게 <strong>반환</strong>.<h3 id="재발급-방식-및-주기">재발급 방식 및 주기</h3>
<h4 id="재발급-방식">재발급 방식</h4>
</li>
</ol>
</li>
<li>JWT의 <strong>만료 기간이 지나면</strong>, <strong>클라이언트</strong>는 <strong>새로운 토큰을 요청</strong>하거나 <strong>서버</strong>에서 <strong>자동으로 재발급</strong>.<h4 id="주기">주기</h4>
</li>
<li><strong>서버</strong>는 토큰의 <strong>만료 기간을 확인</strong>하고, 만료시 <strong>새 토큰을 발급</strong>해 사용자 세션을 유지.<h3 id="다른-api-서비스-호출-시-인증-처리">다른 API 서비스 호출 시 인증 처리</h3>
</li>
<li><strong>사용자 인증을 위해 사용</strong>. 사용자가 로그인 후 JWT를 받으면, 해당 토큰은 API 서비스에 대한 <strong>인증 수단</strong>으로 사용.<h1 id="최종-프로젝트">최종 프로젝트</h1>
DB 개인 설정 후 migration을 통해 table에 필요한 내용을 넣고 클라이언트로 실행해봤다.
해당 정보를 받아 클라이언트로 보내주는 C_Enter부분이 없어서 그런가 캐릭터 선택 이후 진행되지 않았다.
내일 할 것 : C_Enter구조 짜기.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 70일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-70%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-70%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 18 Nov 2024 12:09:34 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="모음-사전">모음 사전</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>사전에 알파벳 모음 &#39;A&#39;, &#39;E&#39;, &#39;I&#39;, &#39;O&#39;, &#39;U&#39;만을 사용하여 만들 수 있는, 길이 5 이하의 모든 단어가 수록되어 있습니다. 사전에서 첫 번째 단어는 &quot;A&quot;이고, 그다음은 &quot;AA&quot;이며, 마지막 단어는 &quot;UUUUU&quot;입니다.</p>
<p>단어 하나 word가 매개변수로 주어질 때, 이 단어가 사전에서 몇 번째 단어인지 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<blockquote>
<p>word의 길이는 1 이상 5 이하입니다.
word는 알파벳 대문자 &#39;A&#39;, &#39;E&#39;, &#39;I&#39;, &#39;O&#39;, &#39;U&#39;로만 이루어져 있습니다.</p>
</blockquote>
<h3 id="풀이-코드">풀이 코드</h3>
<h3 id="풀이-과정">풀이 과정</h3>
<p><img src="https://velog.velcdn.com/images/v_rien/post/bec207d5-1b89-4414-8e64-13a9b608f052/image.PNG" alt="">
문제의 사전은 해당 규칙을 통해 다음 문자가 바뀌는 것을 알 수 있다.</p>
<ul>
<li>AAAAA가 AAAAE로 바뀌는데 필요한 수는 1이다.</li>
<li>AAAA가 AAAE로 바뀌는데 필요한 수는 6이다.(AAAA = 4번째, AAAE = 10번째, AAAI = 16번째)</li>
<li>AAA가 AAE로 바뀌는데 필요한 수는 31이다.(AAA = 3번째, AAE = 34번째)
=&gt; 이 규칙을 배열로 나타내면.<pre><code>const plus = [?, ?, ?, 1*5+1, 1];
const plus = [?, ?, 6*5+1, 6, 1];
const plus = [?, 31*5+1, 31, 6, 1];
const plus = [156*5+1, 156, 31, 6, 1];
const plus = [781, 156, 31, 6, 1];</code></pre>위와 같은 값이 나오게 된다.</li>
<li>따라서 바뀌기전 자리값+배열값을 해주면 바뀐 자리의 값이 나오게 된다.
ex) AAE의 자리값 = 3(AAA의 자리값) + 31(배열값) = 34</li>
</ul>
<p>해당 배열을 이용해 문제를 풀어보면 word의 길이만큼 반복하는 반복문을 만들어주고 indexOf를 이용해 각 자리의 몇번째 요소인지 탐색해준다.
n번째 요소는 그 앞 요소(n-1)값에 maxNums값을 곱해준 만큼 경우의 수를 가지므로 해당 값을 answer에 더해준다.</p>
<h1 id="기술-면접-공부">기술 면접 공부</h1>
<h2 id="7-깊은-복사와-얕은-복사의-차이는-무엇이고-js에서-각각을-구현하는-방법은-어떻게-되는지-설명해주세요">7. 깊은 복사와 얕은 복사의 차이는 무엇이고 JS에서 각각을 구현하는 방법은 어떻게 되는지 설명해주세요.</h2>
<ul>
<li>우선 JS는 <strong>원시 값</strong>과 <strong>참조 값</strong>이라는 두가지 데이터 타입의 값이 존재한다.<ul>
<li>원시 값 : 기본 자료형(단순한 데이터).<ul>
<li>변수에 저장하면 <strong>변수의 메모리 공간</strong>에 <strong>실제 데이터 값이 저장</strong>.
=&gt; 할당된 변수를 조작하려고 하면 <strong>저장된 실제값이 조작</strong>됨.</li>
<li>Number, String, Boolean, Null, Undefined 등.</li>
</ul>
</li>
<li>참조 값 : 여러 자료형으로 구성되는 메모리에 저장된 객체.<ul>
<li>변수에 저장하면 <strong>독립적인 메모리 공간</strong>에 값을 저장하고 변수에 저장된 메모리 공간의 <strong>참조(위치 값)을 저장</strong>하게 됨.
=&gt; 할당된 변수를 조작하는 것은 객체 자체를 조작하는 것이 아닌 <strong>해당 객체의 참조를 조작하는 것.</strong></li>
<li>Object, Symbol 등.<h3 id="얕은-복사">얕은 복사</h3>
</li>
</ul>
</li>
</ul>
</li>
<li>객체를 복사할 때 원래 값과 복사된 값이 <strong>같은 참조를 가리키고 있는 것</strong>.
=&gt; 객체의 참조 값 복사</li>
<li>객체안에 객체가 있을 경우 <strong>한개의 객체라도 원본 객체를 참조</strong>하고 있으면 얕은 복사라고 함.<h4 id="방법">방법</h4>
<h5 id="arrayprototypeslice">Array.prototype.slice()</h5>
<ul>
<li>대표적인 예.</li>
<li>start와 end를 설정하지 않으면 기존 배열을 전체 얕은 복사.<pre><code>const original = [&#39;a&#39;,2,true,4,&quot;hi&quot;];
const copy = original.slice(); 
</code></pre></li>
</ul>
</li>
</ul>
<p>console.log(JSON.stringify(original) === JSON.stringify(copy)); // true </p>
<p>copy.push(10); </p>
<p>console.log(JSON.stringify(original) === JSON.stringify(copy));// false</p>
<p>console.log(original); // [ &#39;a&#39;, 2, true, 4, &#39;hi&#39; ]
console.log(copy); // [ &#39;a&#39;, 2, true, 4, &#39;hi&#39;, 10 ]</p>
<pre><code>##### Object.assign(생성할 객체, 복사할 객체)
  - 첫 번째 인자로 빈 객체를 넣어주고 두 번째 인자로 복사할 객체를 넣어주면 됨.</code></pre><p>const object = {<br>  a: &quot;a&quot;,<br>  number: {<br>    one: 1,<br>    two: 2,<br>  },
}; </p>
<p>const copy = Object.assign({}, object); </p>
<p>copy.number.one = 3; </p>
<p>console.log(object === copy); // false
console.log(object.number.one  === copy.number.one); // true</p>
<pre><code>
##### Spread 연산자(전개 연산자)</code></pre><p>const object = {
  a: &quot;a&quot;,
  number: {
    one: 1,
    two: 2,<br>  },
}; </p>
<p>const copy = {...object} </p>
<p>copy.number.one = 3; </p>
<p>console.log(object === copy); // false
console.log(object.number.one  === copy.number.one); // true</p>
<pre><code>
### 깊은 복사
- 객체 안에 객체가 있을 경우에도 **원본과의 참조가 완전히 끊어진 객체**.
=&gt; 객체의 실제 값 복사.
#### 방법
##### JSON.parse &amp;&amp; JSON.stringify
  - **JSON.stringify()**는 객체를 json 문자열로 변환하는데 이 과정에서 원본 객체와의 **참조가 모두 끊어짐**.
  이후  **JSON.parse()**를 이용해 다시 **원래 객체**(자바스크립트 객체)로 만들어줌.
  - 가장 간단하고 쉬운 방법이지만 다른 방법에 비해 **느리고** 객체가 function일 경우, **undefined로 처리**한다는 것이 단점임.</code></pre><p>const object = {<br>  a: &quot;a&quot;,<br>  number: {<br>    one: 1,<br>    two: 2,<br>  },<br>  arr: [1, 2, [3, 4]],
}; </p>
<p>const copy = JSON.parse(JSON.stringify(object)); </p>
<p>copy.number.one = 3;
copy.arr[2].push(5); </p>
<p>console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false</p>
<p>console.log(object); // { a: &#39;a&#39;, number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: &#39;a&#39;, number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }</p>
<pre><code>
##### 재귀 함수를 구현한 복사
  - 복잡함.</code></pre><p>const object = {<br>  a: &quot;a&quot;,<br>  number: {<br>    one: 1,<br>    two: 2,<br>  },<br>  arr: [1, 2, [3, 4]],
}; </p>
<p>function deepCopy(object) {<br>  if (object === null || typeof object !== &quot;object&quot;) { 
    return object; 
  }<br>  // 객체인지 배열인지 판단<br>  const copy = Array.isArray(object) ? [] : {};</p>
<p>  for (let key of Object.keys(object)) { 
    copy[key] = deepCopy(object[key]);<br>  }   </p>
<p>  return copy;</p>
<p>} </p>
<p>const copy = deepCopy(object); </p>
<p>copy.number.one = 3;
copy.arr[2].push(5);</p>
<p>console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false </p>
<p>console.log(object); // { a: &#39;a&#39;, number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: &#39;a&#39;, number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }</p>
<pre><code>##### Lodash 라이브러리 사용
- 설치를 해서 일반적인 개발에는 효율적이고 더 쉽고 안전하게 깊은 복사를 할 수 있음.
  - 코딩테스트에서는 사용할 수 없음.</code></pre><p>const deepCopy = require(&quot;lodash.clonedeep&quot;) </p>
<p>const object = {<br>  a: &quot;a&quot;,<br>  number: {<br>    one: 1,<br>    two: 2,<br>  },<br>  arr: [1, 2, [3, 4]],
}; </p>
<p>const copy = deepCopy(object); </p>
<p>copy.number.one = 3;
copy.arr[2].push(5); </p>
<p>console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false </p>
<p>console.log(object); // { a: &#39;a&#39;, number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: &#39;a&#39;, number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }</p>
<p>```</p>
<h1 id="최종프로젝트">최종프로젝트</h1>
<p>본격적으로 코드를 구현하기에 앞서 베이스를 제작했다.
migration과 쿼리 부분 한명, 로그인/회원가입 한명, 서버 구동파트 한명, 패킷부분 한명으로 나눠서 했고, 내가 서버 구동 파트여서 해당 부분 코드를 작성했다.
아마 내일 서로 맡은 부분을 합쳐보고 잘 되면 바로 기능구현으로 넘어갈 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내일배움캠프 Node.js 본캠프 69일차]]></title>
            <link>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-69%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@v_rien/%EB%82%B4%EC%9D%BC%EB%B0%B0%EC%9B%80%EC%BA%A0%ED%94%84-Node.js-%EB%B3%B8%EC%BA%A0%ED%94%84-69%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 15 Nov 2024 12:01:46 GMT</pubDate>
            <description><![CDATA[<h1 id="알고리즘-문제-풀어보기">알고리즘 문제 풀어보기</h1>
<h2 id="주차-요금-계산">주차 요금 계산</h2>
<h3 id="문제-설명">문제 설명</h3>
<p>주차장의 요금표와 차량이 들어오고(입차) 나간(출차) 기록이 주어졌을 때, 차량별로 주차 요금을 계산하려고 합니다. 아래는 하나의 예시를 나타냅니다.</p>
<ul>
<li>요금표</li>
</ul>
<table>
<thead>
<tr>
<th align="center">기본 시간(분)</th>
<th align="center">기본 요금(원)</th>
<th align="center">단위 시간(분)</th>
<th align="center">단위 요금(원)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">180</td>
<td align="center">5000</td>
<td align="center">10</td>
<td align="center">600</td>
</tr>
</tbody></table>
<ul>
<li>입/출차 기록</li>
</ul>
<table>
<thead>
<tr>
<th align="center">시각(시:분)</th>
<th align="center">차량 번호</th>
<th align="center">내역</th>
</tr>
</thead>
<tbody><tr>
<td align="center">05:34</td>
<td align="center">5961</td>
<td align="center">입차</td>
</tr>
<tr>
<td align="center">06:00</td>
<td align="center">0000</td>
<td align="center">입차</td>
</tr>
<tr>
<td align="center">06:34</td>
<td align="center">0000</td>
<td align="center">출차</td>
</tr>
<tr>
<td align="center">07:59</td>
<td align="center">5961</td>
<td align="center">출차</td>
</tr>
<tr>
<td align="center">07:59</td>
<td align="center">0148</td>
<td align="center">입차</td>
</tr>
<tr>
<td align="center">18:59</td>
<td align="center">0000</td>
<td align="center">입차</td>
</tr>
<tr>
<td align="center">19:09</td>
<td align="center">0148</td>
<td align="center">출차</td>
</tr>
<tr>
<td align="center">22:59</td>
<td align="center">5961</td>
<td align="center">입차</td>
</tr>
<tr>
<td align="center">23:00</td>
<td align="center">5961</td>
<td align="center">출차</td>
</tr>
</tbody></table>
<ul>
<li>자동차별 주차 요금</li>
</ul>
<table>
<thead>
<tr>
<th align="center">차량 번호</th>
<th align="center">누적 주차 시간(분)</th>
<th align="center">주차 요금(원)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0000</td>
<td align="center">34 + 300 = 334</td>
<td align="center">5000 + ⌈(334 - 180) / 10⌉ x 600 = 14600</td>
</tr>
<tr>
<td align="center">0148</td>
<td align="center">670</td>
<td align="center">5000 +⌈(670 - 180) / 10⌉x 600 = 34400</td>
</tr>
<tr>
<td align="center">5961</td>
<td align="center">145 + 1 = 146</td>
<td align="center">5000</td>
</tr>
</tbody></table>
<ul>
<li>어떤 차량이 입차된 후에 출차된 내역이 없다면, 23:59에 출차된 것으로 간주합니다.<ul>
<li>0000번 차량은 18:59에 입차된 이후, 출차된 내역이 없습니다. 따라서, 23:59에 출차된 것으로 간주합니다.</li>
</ul>
</li>
<li>00:00부터 23:59까지의 입/출차 내역을 바탕으로 차량별 누적 주차 시간을 계산하여 요금을 일괄로 정산합니다.</li>
<li>누적 주차 시간이 기본 시간이하라면, 기본 요금을 청구합니다.</li>
<li>누적 주차 시간이 기본 시간을 초과하면, 기본 요금에 더해서, 초과한 시간에 대해서 단위 시간 마다 단위 요금을 청구합니다.<ul>
<li>초과한 시간이 단위 시간으로 나누어 떨어지지 않으면, 올림합니다.</li>
<li>⌈a⌉ : a보다 작지 않은 최소의 정수를 의미합니다. 즉, 올림을 의미합니다.</li>
</ul>
</li>
</ul>
<p>주차 요금을 나타내는 정수 배열 fees, 자동차의 입/출차 내역을 나타내는 문자열 배열 records가 매개변수로 주어집니다. 차량 번호가 작은 자동차부터 청구할 주차 요금을 차례대로 정수 배열에 담아서 return 하도록 solution 함수를 완성해주세요.</p>
<blockquote>
<h3 id="제한사항">제한사항</h3>
</blockquote>
<ul>
<li>fees의 길이 = 4<ul>
<li>fees[0] = 기본 시간(분)</li>
<li>1 ≤ fees[0] ≤ 1,439</li>
<li>fees[1] = 기본 요금(원)</li>
<li>0 ≤ fees[1] ≤ 100,000</li>
<li>fees[2] = 단위 시간(분)</li>
<li>1 ≤ fees[2] ≤ 1,439</li>
<li>fees[3] = 단위 요금(원)</li>
<li>1 ≤ fees[3] ≤ 10,000<blockquote>
</blockquote>
</li>
</ul>
</li>
<li>1 ≤ records의 길이 ≤ 1,000<ul>
<li>records의 각 원소는 &quot;시각 차량번호 내역&quot; 형식의 문자열입니다.</li>
<li>시각, 차량번호, 내역은 하나의 공백으로 구분되어 있습니다.</li>
<li>시각은 차량이 입차되거나 출차된 시각을 나타내며, HH:MM 형식의 길이 5인 문자열입니다.<ul>
<li>HH:MM은 00:00부터 23:59까지 주어집니다.</li>
<li>잘못된 시각(&quot;25:22&quot;, &quot;09:65&quot; 등)은 입력으로 주어지지 않습니다.</li>
</ul>
</li>
<li>차량번호는 자동차를 구분하기 위한, `0&#39;~&#39;9&#39;로 구성된 길이 4인 문자열입니다.</li>
<li>내역은 길이 2 또는 3인 문자열로, IN 또는 OUT입니다. IN은 입차를, OUT은 출차를 의미합니다.</li>
<li>records의 원소들은 시각을 기준으로 오름차순으로 정렬되어 주어집니다.</li>
<li>records는 하루 동안의 입/출차된 기록만 담고 있으며, 입차된 차량이 다음날 출차되는 경우는 입력으로 주어지지 않습니다.</li>
<li>같은 시각에, 같은 차량번호의 내역이 2번 이상 나타내지 않습니다.</li>
<li>마지막 시각(23:59)에 입차되는 경우는 입력으로 주어지지 않습니다.</li>
<li>아래의 예를 포함하여, 잘못된 입력은 주어지지 않습니다.<ul>
<li>주차장에 없는 차량이 출차되는 경우</li>
<li>주차장에 이미 있는 차량(차량번호가 같은 차량)이 다시 입차되는 경우</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code>function solution(fees, records) {
    const [basic_time, basic_fee, unit_time, unit_fee] = fees;
    var parking_minutes = {};
    var parking_in = {};
    var answer = [];

    records.forEach((ele, idx) =&gt; {
        const[time, car_number, io] = ele.split(&quot; &quot;);

        if(io === &quot;IN&quot;){
            parking_in[car_number] = time;
        }

        if(io === &quot;OUT&quot;){
            var in_time = parking_in[car_number].split(&quot;:&quot;).map(Number);
            var out_time = time.split(&quot;:&quot;).map(Number);
            var minutes = 0;

            if(out_time[1] &gt; in_time[1]){
                minutes += out_time[1] - in_time[1];
            }
            else if(out_time[1]&lt;in_time[1]){
                minutes += 60 + out_time[1] - in_time[1];
                out_time[0] -= 1;
            }
            minutes += (out_time[0] - in_time[0]) * 60;
            if (car_number in parking_minutes) parking_minutes[car_number] += minutes;
            else parking_minutes[car_number] = minutes;

            delete parking_in[car_number];
        }
    });
    while (Object.keys(parking_in).length &gt; 0) {
        var keys = Object.keys(parking_in);
        var in_time = parking_in[keys[0]].split(&quot;:&quot;).map(Number);
        var minutes = (23 - in_time[0]) * 60 + (59 - in_time[1]);
        if (keys[0] in parking_minutes) parking_minutes[keys[0]] += minutes;
        else parking_minutes[keys[0]] = minutes;
        delete parking_in[keys[0]];
      }

      var keys = Object.keys(parking_minutes);
      const keys_len = keys.length;
      for (let i = 0; i &lt; keys_len; i++) {
        let min_idx = 0;
        if (keys.length &gt; 1)
          min_idx = keys.map(Number).indexOf(Math.min(...keys.map(Number)));
        let fee = basic_fee;

        if (parking_minutes[keys[min_idx]] &gt; basic_time) {
          fee +=
            Math.ceil((parking_minutes[keys[min_idx]] - basic_time) / unit_time) *
            unit_fee;
        }
        answer.push(fee);
        keys.splice(min_idx, 1);
      }



    return answer;
}</code></pre><h3 id="풀이-과정">풀이 과정</h3>
<p>기본시간, 기본요금, 단위시간, 단위요금을 배열화해서 fees에 담아준다.
차량별 누적 주차시간을 넣어줄 parking_minutes, 차량별 임차 기록을 넣어줄 parking_in을 만들어 주고, records를 forEach문을 통해 입차인 경우 parking_in에 넣어주고, 출차인 경우 시간을 계산해 parking_minutes에 넣는 작업을 해준다.
이후 반복문을 통해 입차된 후 출차된 내역이 없는 경우를 따로 처리하고 이후 주차 요금을 계산해줬다.</p>
<h1 id="기술-면접-공부">기술 면접 공부</h1>
<h2 id="language---javascript">[Language - Javascript]</h2>
<h3 id="5-arrow-function-이란-무엇인지-설명해주세요">5. Arrow Function 이란 무엇인지 설명해주세요.</h3>
<ul>
<li>화살표 함수 - ES6문법으로 함수 표현식보다 단순하고 간결한 문법으로 함수를 만드는 방법.</li>
<li>화살표 함수에서는 this를 정의하지 않는다.
ex)<pre><code>let func = (arg1, arg2, ...argN) =&gt; expression</code></pre>===<pre><code>let func = function(arg1, arg2, ...argN) {
return expression;
};</code></pre></li>
<li>위의 예시 코드처럼 긴 함수를 한줄로 축약해서 같은 의미를 가진 함수로 만들 수 있다.</li>
</ul>
<pre><code>let sum = (a, b) =&gt; a + b;

/* 위 화살표 함수는 아래 함수의 축약 버전입니다.

let sum = function(a, b) {
  return a + b;
};
*/

alert( sum(1, 2) ); // 3</code></pre><h4 id="예시">예시</h4>
<ul>
<li>인수가 하나밖에 없다면 인수를 감싸는 괄호를 생략할 수 있다. =&gt; 코드 길이를 더 줄일 수 있음.<pre><code>let double = n =&gt; n * 2;
// let double = function(n) { return n * 2 }과 거의 동일합니다.
</code></pre></li>
</ul>
<p>alert( double(3) ); // 6</p>
<pre><code>- 인수가 하나도 없을 땐 괄호를 비워둘 수 있다. =&gt; 이때 괄호는 생략할 수 없다.</code></pre><p>let sayHi = () =&gt; alert(&quot;안녕하세요!&quot;);</p>
<p>sayHi();</p>
<p>```</p>
<h4 id="정리">정리</h4>
<ul>
<li>화살표 함수는 <strong>본문이 한 줄인 함수를 작성</strong>할 때 유용.
=&gt; 한줄이 아니라면 다른 방법으로 화살표 함수를 작성해야 함.<ul>
<li>중괄호 없이 작성: (...args) =&gt; expression – 화살표 오른쪽에 표현식을 둡니다. 함수는 이 표현식을 평가하고, 평가 결과를 반환합니다.</li>
<li>중괄호와 함께 작성: (...args) =&gt; { body } – 본문이 여러 줄로 구성되었다면 중괄호를 사용해야 합니다. 다만, 이 경우는 반드시 return 지시자를 사용해 반환 값을 명기해 주어야 합니다.</li>
</ul>
</li>
</ul>
<h3 id="6-express란-무엇이고-왜-필요하며-대안은-무엇이-있는지-설명해주세요">6. Express란 무엇이고 왜 필요하며 대안은 무엇이 있는지 설명해주세요.</h3>
<ul>
<li>Express - 웹 및 모바일 애플리케이션을 위한 일련의 강력한 기능을 제공하는 <strong>간결하고 유연한 Node.js 웹 애플리케이션 프레임워크</strong>. Node.js는 <strong>표준 웹서버 프레임워크</strong>로 불려질 만큼 많은 곳에서 사용하고 있다.<h4 id="nodejs와의-관계">Node.js와의 관계</h4>
</li>
<li>Node.js는 Chrome의 V8엔진을 이용해 JS로 브라우저가 아니라 서버를 구축하고, 서버에서 JS가 작동되도록 해주는 <strong>런타임 환경(플랫폼)</strong>이다. Express는 이런 Node.js의 원칙과 방법을 이용해 <strong>웹 애플리케이션을 만들기 위한 프레임워크</strong>이다.
=&gt; Express = Node.js를 사용해 쉽게 서버를 구성할 수 있게 만든 <strong>클래스와 라이브러리의 집합체</strong>.<h4 id="필요-이유">필요 이유</h4>
</li>
<li>Express는 프레임워크이므로 웹 애플리케이션을 만들기 위한 각종 라이브러리와 미들웨어 등이 내장되어있어 <strong>개발이 편하고</strong>, 수많은 개발자들에게 <strong>개발 규칙을 강제해 코드 및 구조의 통일성을 향상시킬 수 있다.</strong></li>
<li>또한, 다른 Adonis.js, Koa, Sails.js같은 프레임워크를 사용해도 되지만 <strong>가장 유명하고 널리 사용되기 때문에</strong> Express를 사용하는게 <strong>프레임워크를 배우는 면</strong>에서 좋을 것이다.</li>
</ul>
<h1 id="최종-프로젝트">최종 프로젝트</h1>
<p>redis, mysql을 어떻게 사용할지를 정해주고 캐릭터 컨셉정도를 짰다.
이후 주말동안</p>
<ul>
<li>상점에서 판매할 아이템, 아이템의 등급, 아이템이 떨어지는 장소.</li>
<li>던전에서 나올 보상이 어떤건지.(구체적인 사항X, 떨어지는 아이템의 등급, 조합 전용 아이템 재료 등)</li>
</ul>
<p>해당 부분을 생각해보고 월요일에 말해보기로 했다.</p>
]]></description>
        </item>
    </channel>
</rss>