<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ong_hh.log</title>
        <link>https://velog.io/</link>
        <description>ᕕ( ᐛ )ᕗ</description>
        <lastBuildDate>Sat, 11 Jun 2022 09:42:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ong_hh.log</title>
            <url>https://images.velog.io/images/ong_hh/profile/2ec69d7f-0e9b-48db-9d37-5df0e042cb71/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ong_hh.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ong_hh" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[멋쟁이사자처럼 X 넥슨 MOD Suppoters Hackathon 5주차 회고]]></title>
            <link>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-5%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-5%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 11 Jun 2022 09:42:13 GMT</pubDate>
            <description><![CDATA[<h1 id="컴포넌트의-활용-i">컴포넌트의 활용 I</h1>
<h2 id="movementcomponent">MovementComponent</h2>
<p>InputSpeed : 좌우로 움직임 (이동력)
JumpForce : 점프력</p>
<h2 id="rigidbodycomponent">RigidbodyComponent</h2>
<p>가감속에 따른 움직임, 중력의 영향을 받는다, 기본 물리</p>
<p>DownJumpSpeed : 하단점프 할 때 위로 좀 뛰었다가 내려감
Gravity : 밑으로 당겨지는 힘(높을 수록 빨리 떨어짐)
IsBlockVerticalLi : 떨어져있는 기둥에 막힘
IsolatedMove : 낭떠러지에서 안 떨어짐
Mass : 물체의 질량, 값이 높을수록 가감속이 느려짐
IsQuaterView : 중력을 따르지 않고 움직임</p>
<h2 id="triggercomponent">TriggerComponent</h2>
<p>충돌시에 많이 쓰임</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/2f8f9867-0735-4d3a-83f3-37e5249c0d6b/image.png" alt=""></p>
<p>아이템 먹은 것 처럼 쓰기</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/68b132e4-38e5-4f9f-a032-3813a4a9c2cc/image.png" alt=""></p>
<h2 id="webspritecomponent">WebSpriteComponent</h2>
<p>display하는 컴포넌트
웹에 있는 이미지를 가져와서 표시해줌
애니메이션 설정 가능</p>
<h2 id="youtubeplayerworldcomponent">YoutubePlayerWorldComponent</h2>
<p>사용 방법은 WebSprite Component와 유사</p>
<h2 id="avatarenderercomponent">AvataRendererComponent</h2>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/eee86730-a737-493e-92be-77912bd0387d/image.png" alt=""></p>
<p><strong>활용 방안</strong></p>
<ul>
<li>랭킹 시스템</li>
<li>마네킹 디스플레이</li>
<li>나만의 Entity</li>
</ul>
<h1 id="mod-api">MOD API</h1>
<p>입력관련은 대부분 script들어감</p>
<h2 id="key-downup-event">Key down/up/... Event</h2>
<p>키를 받음</p>
<pre><code>local key = event.key
if key == KeyBoardKey.P then
    log(&quot;ppppp&quot;)
end</code></pre><p>P키를 입력받았을 때 로그가 찍히는 코드</p>
<h2 id="touchevent">TouchEvent</h2>
<p>서비스에서 날라옴, 특정한 컴포넌트가 터치를 받아서 엔티티에 쏴줌</p>
<h2 id="screentouchevent">ScreenTouchEvent</h2>
<p>스크론 터치시에 이벤트 발생</p>
<h1 id="effect">Effect!</h1>
<p>Panles -&gt; Resource Storage -&gt; MOD리소스 -&gt; animationclip -&gt; skill
RUID복사
<code>_EffectService : playEffect()</code>
<code>_EffectService:P;ayEffectAttached()</code> 
어디에 붙일 것 인지 부모 엔티티가 필요함. 부모 엔티티 기준으로 로컬포지션</p>
<p>바라보고 있는 방향 정보</p>
<pre><code>local direction = self.Entity.PlayerControllerComponent.LookDirectionX</code></pre><p>거리에 따른 소리 조절</p>
<pre><code class="language-Lua">void OnBeginPlay()
{
    local treeEntity = _EntityService:GetEntity(&quot;RUID&quot;)
    local treeSound = treeEntity.SoundComponent

    treeSound:SetListenerEntity(self.Entity)
    treeSound:Play()
 }</code></pre>
<p> 맵별로 사운드 설정 -&gt; Map Entity</p>
<p> 카메라 화면전환 (goal Entity)</p>
<pre><code> HandleTouchEvent(TouchEvent event) :
 {
     local goalEntity = _EntityService:GetEntity(&quot;RUID&quot;)
    local goalCamera = goalEntity.CameraComponent

    _CameraService:SwitchCameraTo(goalCamera)
}</code></pre><p><code>포털 이동</code></p>
<ol>
<li>컴포넌트 활용 - 직관적</li>
<li>스크립트 활용</li>
</ol>
<p>충돌이 일어난 사용자를 왼쪽 풀로 이동</p>
<pre><code>local TriggerBodyEntity = event.TriggerBodyEntity
--------------------------------------------------------
local left = _EntityService:GetEntity(&#39;RUID&#39;)

if TriggerBodyEntity ~= _UserService.LocalPlayer then
    return
end

_TeleportService:TeleportToEntity(TriggerBodyEntity, left)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[멋쟁이사자처럼 X 넥슨 MOD Suppoters Hackathon 4주차 회고]]></title>
            <link>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 04 Jun 2022 09:32:16 GMT</pubDate>
            <description><![CDATA[<h1 id="event와-컴포넌트-확장">Event와 컴포넌트 확장</h1>
<blockquote>
<p><strong>목차</strong></p>
</blockquote>
<ul>
<li>Event System</li>
<li>Entity Event System </li>
<li>Componet 종류 (Native, Script) 의 차이점</li>
<li>왜 나뉘어져 있는지, 서로를 오가는 기법에 대한 소개</li>
</ul>
<h2 id="event">Event?</h2>
<p>어떤 객체와 객체간의 주고 받는 형식</p>
<blockquote>
<p><strong>MOD에서 제공하는 Event들</strong>
<img src="https://velog.velcdn.com/images/ong_hh/post/848a5b59-443e-473d-a512-a362594c2683/image.png" alt=""></p>
</blockquote>
<blockquote>
<p><strong>Event 직접 만들기</strong>
<img src="https://velog.velcdn.com/images/ong_hh/post/4c5edaeb-d61a-4f21-9a37-b3627adb8f90/image.png" alt="">
이벤트 수신을 위해, Component도 생성</p>
</blockquote>
<h2 id="entity-event-system">Entity Event System</h2>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/07886b07-099d-4604-9e70-2e78576296a0/image.png" alt=""></p>
<p>Component는 Entity에 오는 Event를 수신 하겠다!</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/461593ac-1120-44cb-adf4-a54e726c36ff/image.png" alt=""></p>
<p>(같은 Entitiy내에서 Event를 주고 받는다는 가정)</p>
<p>Component(1)에서 어떠한 일이 일어나면 Component(3)에서 수행을 하는 로직을 짜고 싶음
그런데 둘 간의 연관이 없기 때문에 바로 호출하지 않고 Entity를 통해 수행함 </p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/80a6de4a-1dd4-4175-b05e-6d2208c40b5b/image.png" alt=""></p>
<p>다른 Entity로 발송도 가능</p>
<p>이벤트를 임의로 발생시켜 보자!
(외부의 다른 Entity에게 이벤트를 발생시키는 경우)</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/4e13f2ff-de7e-4ffc-9cf3-842f09d1ef29/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/b665f40f-8038-4a23-a6c0-8ce994db423b/image.png" alt=""></p>
<p>Entity ID값 찾는 방법
<img src="https://velog.velcdn.com/images/ong_hh/post/9ad54b6e-d77c-4827-b619-a1e42dd6d14d/image.png" alt=""></p>
<p>등록도 다른 Entity에 할 수 있음</p>
<p>기본적으로는 self로 자기 자신임
<img src="https://velog.velcdn.com/images/ong_hh/post/cff41f5f-a6ae-4fa7-961c-519d23890e9b/image.png" alt=""></p>
<p>다른 Entity로 처리하고 싶으면 이렇게 수정 가능
<img src="https://velog.velcdn.com/images/ong_hh/post/e606102b-ede8-4122-8df2-bf80b5a5cdae/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/91eabca6-343d-45fe-9b18-ded65a9424ba/image.png" alt="">
함수로 만들어서 호출할 수도 있음</p>
<p>하지만 <strong>확장성</strong> 때문에 Entity와 Component로 처리하는게 좋음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[멋쟁이사자처럼 X 넥슨 MOD Suppoters Hackathon 3주차 회고]]></title>
            <link>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 28 May 2022 09:17:23 GMT</pubDate>
            <description><![CDATA[<h1 id="스크립트의-이해">스크립트의 이해</h1>
<p>MOD에서의 스크립팅 : LuaScript기반 + a</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/a4d8c048-0a01-444f-9784-072a8046b301/image.png" alt=""></p>
<h2 id="반복문-만들어보기">반복문 만들어보기</h2>
<h4 id="짝수만-더해보기">짝수만 더해보기</h4>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/7fdeae02-3c7d-40fc-9626-8a2dffbb9093/image.png" alt=""></p>
<h2 id="logic">Logic</h2>
<p>월드에서 딱 하나밖에 없음, 어디 위치에 두는게 아니고 지속적으로 돌아가는 것</p>
<h2 id="component">Component</h2>
<p>컴포넌트를 실행할려면 엔티티에 추가시켜줘야함</p>
<ul>
<li>OnBeginPlay()</li>
<li>OnEndPlay()</li>
<li>OnUPdatePlay()</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/9529c91d-fb42-428c-a00b-16c2926682cc/image.png" alt=""></p>
<pre><code>Property:
    [Sync] -&gt; 서버와의 동기화
    number sum=0

Function:
    void OnBeginPlay(){
        log(&quot;begin&quot;) // 가장 기본적으로 월드가 실행할때 실행되는 함수이다.
        self.sum=5 -&gt; 컴포넌트의 프로퍼티 접근을 위해 self.붙임
        self:myFunction() -&gt; 컴포넌트의 함수 실행 코드

        local myEntityPosition=self.Entity.TransformComponent.Position
        -&gt; 컴포넌트의 엔티티에 다른 컴포넌트에 접근가능하게 해준다.
    }

    void myFuntion(){
        log(&quot;my Function&quot;)
    }</code></pre><p><img src="https://velog.velcdn.com/images/ong_hh/post/76f1796c-104c-4159-9e4a-c3491e4ed7ee/image.png" alt=""></p>
<h4 id="응용">응용</h4>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/5f894ec3-e582-4de2-88ec-f712b3b3b85a/image.png" alt=""></p>
<p>Entity의 position이 1초마다 0.5씩 움직이게 해주는 코드</p>
<h1 id="네트워크의-이해">네트워크의 이해</h1>
<p>통신모델
서버 클라이언트 기본 구조
서버 1개 여러개의 클라이언트에서 접속해서 통신하는 형태
클라이언트는 각각의 유저! 어딘가에 있는 서버!</p>
<h2 id="컴포넌트">컴포넌트</h2>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/32604c34-eca5-4e6b-a32b-d514d567c723/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/ca685121-c38e-4cf3-8061-4524cdcf7b4f/image.png" alt=""></p>
<p>서버 와 클라이언트가 있을 때,
Entity를 생성하면 서버에도 존재, 플레이를 하면 클라이언트에도 존재
한 곳에 작업을 한 것 처럼 보이지만 서버와 클라 양쪽에 추가된 것
내부적으로 ID를 가지고 식별을 유니크하게 함 개념상 연결되어 있다고 생각</p>
<blockquote>
<p>스코어 값을 1로 바꿈</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/dea425f4-aae0-4140-ae40-ed3ed89df7c3/image.png" alt=""></p>
<p>서버에서 score값 처음엔 0 클라이언트도 0임
여기서 값을 바꾸면 바꾼 위치에 따라 변경되는 값이 다름
서버에서 바꾸면 서버는 1 클라는 0
클라에서 바꾸면 클라는 1 서버는 0
두 개의 Entitiy가 다른 영역에 있어서 &lt;&lt; 다른 Entity임
문제점 : 컨텐츠 상에선 같은 객체인데 서버와 클라의 값이 달라짐
따라서 같이 바꿔줘야 한다. &gt;&gt; <strong>동기화</strong></p>
<p>MOD에서는 기본적으로 Property에 동기화를 설정할 수 있게 해놓음</p>
<pre><code>Property: +
    [Sync] // 이부분!!!
    number score = 0 :</code></pre><p>몇몇의 타입은 동기화가 안됨</p>
<p>동기화 방향은 기본적으로 단방향</p>
<pre><code>Server
    └--Client 1
    └--Clinet 2
    └--Client 3</code></pre><p>의 구조로 되어 있을 때, Client 1이 값을 바꾼다 해서 다른 애들한테 영향이 가지 않음
모든 값을 바꿀라면 Server의 값을 바꿔야함
Server가 score를 1의 값으로 바꿨는데 Client1에서 score값을 2로 바꾼다면, 모두가 1을 가지고 있지만 Client1만 2값을 가지고 있게됨
==&gt; 일반적으로 이렇게 작업하진 않지만 가끔 이렇게 코딩해야 할 때가 있음</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/d1cbc9e7-8af9-484a-b666-53a03d51cbbb/image.png" alt=""></p>
<p>Sever에서 score가 바뀜
Client에서 이게 바뀐걸 알고 싶음 (OnSyncProperty)</p>
<p>Function =&gt; 실행제어</p>
<p>메소드는 부르는 위치에 따라 동작하는 구간이 다름 👇</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/b4663dff-bf6e-462a-8235-f821438dc4aa/image.png" alt=""></p>
<p>Method Setting = 메소드의 속성을 정의할 수 있음 = 공간 활성화
(현재는 Function Setting으로 바뀐 듯)</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/d908216b-d1d4-4d23-b7ca-f5ea40b3b915/image.png" alt=""></p>
<p><strong>Server Only와 Client Only</strong>
Client에서만 돌아가고, Server에서만 돌아가게
대부분의 로직은 Server Only로 짬</p>
<p><strong>Client와 Server</strong>
이 함수가 Clinet에서 돌아가야해! =&gt; Client Only와 무슨 차이?
혹시 이 함수가 Server에서 불리게 되면 자동으로 Client쪽에러 패킷을 보내서 통신을 해서 Client가 호출이 되게 함</p>
<p>Server는 혹시 이 함수가 Client에서 불렸다 하더라도 Server에 보내서 실행을 하라는 뜻 (Client가 Function을 실행하라고 Server에 보내고 Client는 자기 갈 길 마저 감 Function은 Server가 실행시킴)</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/5bf0ec6d-f64b-4026-8203-77e49680de0b/image.png" alt=""></p>
<p>Server에서 Client용 Function을 호출하면 각각의 Client에게 모두 실행시키라 명령함
<img src="https://velog.velcdn.com/images/ong_hh/post/69172deb-5e2a-41e0-9126-98156ca649cc/image.png" alt=""></p>
<p><strong>Multicast</strong>
Function이 양쪽에서 실행</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/495a01de-74fc-4d80-8852-8db5363319c7/image.png" alt=""></p>
<p>self.ClinetOnly() 는 OnBeginPlay()가 ServerOnly함수라 호출이 안됨</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/84985200-1c77-424e-abab-4bca3f18984c/image.png" alt=""></p>
<p>Server가 Client에 함수를 호출하라 메시지를 보내고 계속 Server실행을 해서 순서가 Server -&gt; End -&gt; Client 순으로 찍히고 있음
타이밍 이슈는 있음 (정확히 알 수 없음)</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/e29d8709-2d66-4a1b-832c-9dbcb24dc6b5/image.png" alt=""></p>
<p>Multicast순서또한 위와 같은 맥락</p>
<p>일반적으로, Server에서 Entity를 생성하면 그 밑에 Client들도 해당 Entity가 생성된다.
가끔씩 Client 1만의 Entity를 갖고 싶어하는 경우가 있음 (대표적으로 UI)</p>
<p><strong>UI, 입력단</strong> : <em>Local Entity</em>, Play1이 스탯창을 키는데 다른 유저의 스탯창이 열리면 안되니까, 이런건 나만의 Client에서 돌아가야 함, 이런건 Server 에도 없음!</p>
<p><strong>최적화</strong>를 할 때도 Client쪽 로직을 사용</p>
<blockquote>
<p><strong>Example</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/6831dc71-92b3-4796-9e43-0b6ddd99760a/image.png" alt=""></p>
<p>KeyDown을 했을 때 Log가 찍히도록 해봄</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/9b3759d6-2c13-492e-ab90-396225fa687d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/1e0463b5-41f5-48ae-b307-2f7fb4be6a22/image.png" alt=""></p>
<p>Client1에서 power값을 증가 시켜줬는데 해당 함수가 Client라면 다른 Client2에서 Power값이 계속 0임, 해당 함수를 Server로 바꾸면 Server에서 동기화 시켜줘서 증가된 Power값을 모두가 볼 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[멋쟁이사자처럼 X 넥슨 MOD Suppoters Hackathon 2주차 회고]]></title>
            <link>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 28 May 2022 08:43:02 GMT</pubDate>
            <description><![CDATA[<h1 id="기본-컴포넌트의-이해">기본 컴포넌트의 이해</h1>
<p>mod는 다양한 native component를 기본적으로 제공
(각각에 대해서는 api reference에서 자세히 살펴보기)</p>
<p>특수하고 자주 쓰는 component
<strong>transformComponent, SpriteRendererComponent</strong></p>
<h2 id="transformcomponent">TransformComponent</h2>
<p>월드 상에서 entity들에 어디에 어떻게 위치하는가 나타내는 정보
특수한 형태를 제외하곤 대부분 있음(거의 무조건 있음)</p>
<p>위치 정보를 가지고 있다</p>
<p><strong>position</strong> (x,y)
<strong>scale</strong> (1이 기본 값)
<strong>ZRotation</strong> 대부분의 수직 게임에서는 z축으로 회전함, degree값</p>
<p>Entity - 트리구조로 이루어져 있음 -부모와 자식의 형태가 있음
배치를 할 때도 마찬가지, 자식값으로 넣으면 부모랑 같이 프로퍼티 조절할 수 있음</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/28eac607-ee0b-4574-8c6b-85b72e8fd3b4/image.png" alt=""></p>
<p>좌표는 부모 좌표 기준으로 상대 좌표
<strong>world position</strong> 최상위(월드) 기준으로는 자신의 좌표 더하기 부모 좌표가 됨 == 절대 좌표
<strong>rotation</strong>도 마찬가지</p>
<h2 id="spriterenderercomponent">SpriteRendererComponent</h2>
<p>어떤걸 표시할지, 어떻게 표시할지
없을 수도 있음</p>
<p>어떠한 이미지를 표시할 것인가</p>
<p><strong>SpriteRUID</strong>
위 이미지에서 나무를 뜻한다 하면 됨, RUID = 이미지를 ID형태로 관리하는 것
외부의 이미지도 import하면 id가 발금됨 이걸 쓰면됨
<strong>PlayRate</strong> 
애니메이션이 되는 경우 속도 조절 가능
<strong>StartFrameIndex, EndFrameIndex</strong> 
이미지가 여러개 인덱스로 되어져 있고 그걸 루프 돌리는거
<strong>FlipX, FlipY</strong>
이미지 반전
<strong>Color</strong>
색깔입히기(원래 있던 색과 혼합되어서 나옴)
<strong>DrawMode</strong></p>
<ul>
<li>simple 기본</li>
<li>tiled <ul>
<li>Tiled Size 를 조정해서 타일링함
<img src="https://velog.velcdn.com/images/ong_hh/post/e5703ebc-6db2-44f8-a03c-cd3f84823139/image.png" alt=""></li>
</ul>
</li>
</ul>
<h1 id="지형과-레이어의-이해">지형과 레이어의 이해</h1>
<h2 id="tilemapcomponent">TileMapComponent</h2>
<p>TileMapRUID : 타일 이미지 ID값
<strong>FootHold</strong> : 캐릭터, 몬스터들이 밟고 다니는 발판
<img src="https://velog.velcdn.com/images/ong_hh/post/0a0cec35-2552-41c1-bdbe-d969790d7941/image.png" alt=""></p>
<p>빨간색 선이 foothold 지형 편집 가능(Edit Foothold)
property안에서 edge를 통해서도 수정 가능</p>
<h2 id="maplayer">MapLayer</h2>
<p>하나의 맵에 최대 10개 레이어 추가 가능
sorting layer로 내가 1 레이어에 만든거를 2레이어에 넣을 수도 있음</p>
<h1 id="자주-사용하는-컴포넌트">자주 사용하는 컴포넌트</h1>
<h2 id="tweencomponent">TweenComponent</h2>
<ul>
<li>TweenCircularComponent</li>
<li>TweenFlatingComponent</li>
<li>TweenLineComponent
<img src="https://velog.velcdn.com/images/ong_hh/post/0e5201be-9e3f-40f3-8ab2-3abc0dd5fa71/image.png" alt=""></li>
</ul>
<p>캐릭터가 좌우로 3씩 직선으로 움직이고 원점으로 돌아감(OneRoundTrip)
왔다 갔다 하는 방식을 등속인지 직선인지 여러가지 결정하는 것 : <strong>TweenType</strong>
<strong>SyncType</strong> : 동기화 방식, 여러 클라이언트에서 같은 방식으로(애니메이션 속도가 같게!)</p>
<h2 id="rigidbodycomponent">RigidBodyComponent</h2>
<p>강체를 의미(형태가 바뀌지 않음), 물리적인 속성을 컨트롤할 수 있음
gravity값이 있어서 객체(몬스터,npc 등)가 떨어짐(육체는 떨어지지 않음)
기본적으로 연결이 되어있는 곳들은 벽에 막힐 수 있는데, 연결이 안 되어져 있는 곳은 뚫고 다님</p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/627350ef-00bc-499b-8a14-fa2ad51b2b56/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/68838f84-46e9-40a0-9e07-f5c6d61d0482/image.png" alt=""></p>
<p>레이어를 추가해서 벽을 두면 그 뒤로 돌아다닐 수 있음!!</p>
<p><strong>IsQuateViewMode</strong> : 이동 규칙을 쿼터뷰로, 지형에 구애 없이(?) 움직일 수 있음</p>
<h2 id="triggercomponent">TriggerComponent</h2>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/6dbeef09-b919-4725-8d6a-04426311d03c/image.png" alt=""></p>
<p>충돌이 발생했을 때 뒤로 튕겨져 나가는 코드!</p>
<ul>
<li>TweenLineComponent랑 같이 사용하면 움직이는 트랩이 된다</li>
</ul>
<h2 id="playercontrolcomponent">PlayerControlComponent</h2>
<p>캐릭터가 바라보는 방향도 설정 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[멋쟁이사자처럼 X 넥슨 MOD Suppoters Hackathon 1주차 회고]]></title>
            <link>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ong_hh/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-X-%EB%84%A5%EC%8A%A8-MOD-Suppoters-Hackathon-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 22 May 2022 07:26:50 GMT</pubDate>
            <description><![CDATA[<h1 id="entity-component-property">Entity, Component, Property</h1>
<h2 id="entity">Entity</h2>
<p>Entity는 MOD world를 구성하는 요소
MOD 내에 존재하는 객체라고 볼 수 있다.
World 밑에 속하는 모든 것을 뜻한다.
Tree형태의 계층형 구조를 띈다.(자식이 부모의 속성을 따라간다.)</p>
<h2 id="component">Component</h2>
<p>특정 기능에 특화된 어떤 것을 component라 한다.
재사용성이 높게 활용하기 위해서 Component를 개별로 분리한다.</p>
<h2 id="proerty">Proerty</h2>
<p>Component의 세부적인 정보를 담고 있는 것이 property</p>
<h1 id="model">Model</h1>
<p>Entity의 component와 property 등의 정보를 가지고 있는 판본</p>
<p>MOD에서는 기본적으로 메이플에 있는 요소를 model로 만들어서 제공한다. 
있는 콘텐츠를 잘 활용하여 이것저것 만들 수 있다. </p>
<p>Entity를 만든 다음에 Model로 설정해서 언제든지 다시 재활용할 수 있게끔 &#39;모델화&#39;할 것.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[REACT+NODE.js Web Deploying on AWS EC2 (2)]]></title>
            <link>https://velog.io/@ong_hh/REACTNODE.js-Web-Deploying-on-AWS-EC2-2</link>
            <guid>https://velog.io/@ong_hh/REACTNODE.js-Web-Deploying-on-AWS-EC2-2</guid>
            <pubDate>Mon, 16 May 2022 06:16:32 GMT</pubDate>
            <description><![CDATA[<h2 id="git">Git</h2>
<pre><code>git clone [내프로젝트주소]</code></pre><h2 id="각-폴더로-들어가서-npm-install-해주기">각 폴더로 들어가서 npm install 해주기</h2>
<ul>
<li><strong>Clinet에서 npm install 오류</strong>
  해결방안 : rm -rf package-lock.json 하고 다시 npm install 해주기
  혹시 안된다면 npm install --force로 다시 해본다.</li>
<li><strong>Server에서 npm install</strong>
  여기서는 오류가 나지 않았다!</li>
</ul>
<h2 id="server에서-npm-start-오류">Server에서 npm start 오류</h2>
<h4 id="error-message1--module-not-found-body-parser">Error Message1 : module not found &#39;body-parser&#39;</h4>
<p>해당 에러가 뜬 후 시간이 지나면 Server starting이 떠서 서버 동작이 잘 되는 줄 알았는데 아니었다. 서버 start에서 오류가 뜨니 해당 과정을 스킵하고 프론트만 돌려진 것. 서버가 잘 돌아간다면 내가 index.js에서 지정해둔 Server starting on 3001이라는 문구와 sequlize가 동작하는 문구들이 떠야 하는데 이게 안뜬다. body-parser라는 모듈을 찾을 수 없다는 에러인데 보통은 npm install body-parser를 따로 해주면 해결되는 에러인데 해결이 되지 않는다. </p>
<p><strong>해결방안</strong> : 서버쪽 코드에 들어간 모든 body-parser를 삭제해줬다. 찾아보니 express 4.xx버전 이상 부터는 express안에 body-parser가 내장되어 있다고 한다. 그래서 따로 body-parser모듈을 설치하지 않아도 해당 기능을 동작할 수 있다. 그렇다면 지금 오류가 나는 body-parser를 다 삭제 하고 express에서 body-parser기능을 사용하게 코드를 수정하자.</p>
<p><strong>수정된 코드</strong> :</p>
<pre><code class="language-javascript">        app.use(express.json());
        app.use(express.urlencoded({ extended: true}));</code></pre>
<p>사실 제일 애먹은 오류 중 하나,, 결국에는 server starting이 잘 뜨길래 코드에 문제가 없는 줄 알았지😂😂 지나가는 로그조차 우습게 보지 말자...!</p>
<h4 id="error-message2--npm-run-all-permission-denied">Error Message2 : npm-run-all permission denied</h4>
<p>body-parser를 다 지우고 npm run start를 했는데 permission denied 에러가 뜬다. 갑자기 권한이 거부되었다니.. 그래서 <code>chmod</code>명령어를 통해서 강제로 권한을 주는 방법을 시도해 보았지만 실패</p>
<p><strong>해결방안</strong> : 의외로 해결 방안은 간단했다. <code>node_modules</code>폴더를 삭제하고 다시 npm install을 해준 뒤 npm start를 하니 잘 실행이 된다. 폴더를 삭제하는 명령어는 <code>rm -rf node_modules</code> 분명 rm 명령어는 함부로 쓰면 안된다고 배웠는데..?</p>
<p>다시 npm start하면 sequelize문구와 index.js에 지정한 Server starting on 3001이라는 문구가 뜬 후 <code>Server starting</code>으로 넘어간다.</p>
<h2 id="nginx">Nginx</h2>
<p>이제 http:/[내 퍼블릭 IP]:3000 으로 들어가면 배포된 내 웹사이트를 볼 수 있다.
뒤에 있는 저 3000포트도 지우고 어디서나 우리 웹사이트에 들어갈 수 있도록 Nginx설정을 해준다.</p>
<pre><code>$ sudo apt-get install nginx</code></pre><h3 id="npm-run-build">npm run build</h3>
<p>react 경로로 들어가서 npm run build를 해주면 build폴더가 하나 생성된다.
build 내부에는 index.html이라는 파일이 있을 것이다. 이 파일은 이후 Nginx 설정에서 사용되며 React로 만들어진 웹에서의 시작점이라고 할 수 있다.</p>
<h3 id="nginx설정">Nginx설정</h3>
<h4 id="default파일-수정">default파일 수정</h4>
<pre><code>$ sudo vi /etc/nginx/sites-available/default</code></pre><p>명령어를 입력하고 들어가면 기본으로 설정된 코드들이 있을 것이다.</p>
<pre><code>server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # SSL configuration
    .
    .
    .

    root /home/ubuntu/[react 파일 경로]/build;
    index index.html index.htm;
    server_name [퍼블릭IP];
    location / {
        try_files $uri $uri/ /index.html;
    }</code></pre><p>이처럼 수정해준다.
root에는 build된 파일의 경로를 넣어준다.
index 에서 build파일에 있는 index.html파일을 불러올 것이다.
server_name 은 배포된 내 웹사이트 주소를,
location의 try_Files에 404을 없애고 index.html을 넣어주었다.첫 화면을 무조건 홈 화면으로 띄우기 위해서.</p>
<p>여기까지 하면 프론트 배포는 잘 마쳤다.
<em><strong>그런데 서버 동작을 안 한다.</strong></em>
무엇을 더 해야하는 것인지 생각 하다가 DB연결이 local로만 되어 있어서 그런 것인가?</p>
<p>DB를 AWS RDS로 연결해보자</p>
<h2 id="aws-rds">AWS RDS</h2>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/837a6482-6ea4-41a8-8ad2-1c2b9c816d55/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/bce99778-fd3b-42fd-955a-c3561b057cd3/image.png" alt=""></p>
<p>서브넷 및 VPC 모두 default값으로 해주었다.</p>
<p>여기서 중요한건 RDS생성후 나옹는 엔드포인트값! 이게 곧 우리의 DB HOST가 된다.</p>
<h3 id="ec2-인바운드-그룹-수정">EC2 인바운드 그룹 수정</h3>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/e26997ee-3856-4e5f-8068-18f5c3d8ad09/image.png" alt="">
디비 연결 포트인 3306 포트를 추가해준다.</p>
<h3 id="server-코드-수정">Server 코드 수정</h3>
<h4 id="config파일">config파일</h4>
<p><code>ServerFolder/config/config.json</code></p>
<pre><code class="language-javascript">  &quot;production&quot;: {
    &quot;username&quot;: &quot;마스터 사용자 이름&quot;,
    &quot;password&quot;: &quot;마스터 암호&quot;,
    &quot;database&quot;: &quot;내 database&quot;,
    &quot;host&quot;: &quot;엔드포인트 주소&quot;,
    &quot;dialect&quot;: &quot;mysql&quot;,
    &quot;port&quot;:&quot;3306&quot;
  }</code></pre>
<p><code>ServerFolder/model/index.js</code></p>
<pre><code class="language-javascript">...
const env = process.env.NODE_ENV || &#39;production&#39;;
const config = require(__dirname + &#39;/../config/config.json&#39;)[env];
const db = {};

let sequelize;
...</code></pre>
<h3 id="-mysql-workbench-연결">+ MySQL Workbench 연결</h3>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/8d9889a6-1ceb-419e-8ac1-9cc2a68d5db4/image.png" alt=""></p>
<p>마스터 암호 치고 연결 누르면 OK👌</p>
<p>DB연결까지 해주었지만 서버 동작은 하지 않는다.
문제는 Nginx Proxy서버를 안해줬던 것 ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[REACT+NODE.js Web Deploying on AWS EC2 (1)]]></title>
            <link>https://velog.io/@ong_hh/REACTNODE.js-Web-Deploying-on-AWS-EC2-1</link>
            <guid>https://velog.io/@ong_hh/REACTNODE.js-Web-Deploying-on-AWS-EC2-1</guid>
            <pubDate>Mon, 16 May 2022 01:30:47 GMT</pubDate>
            <description><![CDATA[<h2 id="aws-ec2-인스턴스-생성">AWS EC2 인스턴스 생성</h2>
<p>AWS 가 리뉴얼을 해서 당황했었다. 몇 번 클릭 해 보니 바뀐 후 버전이 더 쉽게 인스턴스 시작하기 좋다.
<img src="https://velog.velcdn.com/images/ong_hh/post/778c2999-f393-4602-a20d-1ac38c302747/image.png" alt="">
우분투 선택
<img src="https://velog.velcdn.com/images/ong_hh/post/70d4d32a-868d-4405-a61b-9593d52f20e7/image.png" alt="">
작은규모의 프로젝트이면 프리티어 수준의 micro를 하면 되지만
우리 프로젝트는 프론트쪽 용량도 무시 못하고 사용자도 예상이 안되므로 medium으로 했다. 
추후 사용량을 보고 수정 예정.
<img src="https://velog.velcdn.com/images/ong_hh/post/107974f3-ef12-4770-97e9-0fe9dbfce94e/image.png" alt="">
키 페어 로그인 정보 입력! 기존에 있는 걸 써도 되고 새로 만들어도 된다.
<img src="https://velog.velcdn.com/images/ong_hh/post/a9b997af-d321-41d5-936e-2160c22cf09a/image.png" alt="">
네트워크와 서브넷은 완전 처음 인스턴스를 만들면 자동으로 만들어주는게 생겼다.
그래서 그때 만든 네트워크와 서브넷을 그대로 사용해주었다.
<img src="https://velog.velcdn.com/images/ong_hh/post/54522888-1235-4587-a552-412fbb386fd1/image.png" alt="">
인바운드 보안 그룹을 설정한다.
<img src="https://velog.velcdn.com/images/ong_hh/post/df5977af-ac4d-4427-bae4-bbfe7d409fa0/image.png" alt="">
::/0은 사실 필요 없는 듯 하다...
그리고 처음 인스턴스를 만들 때에는 3000포트(client)와 3001포트(server)를 열어두었었다.</p>
<p>탄력적 IP를 할당해준다.
<img src="https://velog.velcdn.com/images/ong_hh/post/f3853ddd-af1d-45ec-9a8b-ec9a4d872ea5/image.png" alt="">
running중인 내 인스턴스와 연결해주면 퍼블릭 IP가 고정이 된다.</p>
<h2 id="ssh">SSH</h2>
<p>windows os를 사용중이라 putty를 이용했다.</p>
<blockquote>
<p><strong>PuttyGen사용법</strong></p>
<blockquote>
</blockquote>
</blockquote>
<ol>
<li>puttygen 으로 새 키 페어 생성시 받은 .pem파일을 .ppk파일로 변환해준다.</li>
<li>Conversions &gt; import key</li>
<li>Save private key</li>
</ol>
<blockquote>
<p><strong>Putty사용법</strong></p>
<blockquote>
</blockquote>
<p>1.
<img src="https://velog.velcdn.com/images/ong_hh/post/3be1dd04-877e-4759-9edb-dc63c2980259/image.png" alt="">
2. <img src="https://velog.velcdn.com/images/ong_hh/post/56110d75-916b-4d1b-a170-dab3cc9a9b9a/image.png" alt="">
3. Open!</p>
</blockquote>
<blockquote>
<p><strong>Login</strong></p>
<blockquote>
</blockquote>
</blockquote>
<ol>
<li>login as : ubuntu 입력</li>
<li>sudo su 입력해서 관리자 계정으로 로그인</li>
</ol>
<h2 id="패키지-설치">패키지 설치</h2>
<h3 id="npm-설치">npm 설치</h3>
<p><a href="https://docs.aws.amazon.com/ko_kr/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html">Ubuntu npm 설치 docs</a></p>
<h3 id="yarn-설치">yarn 설치</h3>
<pre><code>npm install -g yarn</code></pre><p>Ubuntu에 내 프로젝트를 설치할 준비는 끝났다.
이후 과정이 바로 오류와의싸움!!^^</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[학교 축제 사이트 배포하기 PROJECT]]></title>
            <link>https://velog.io/@ong_hh/%ED%95%99%EA%B5%90-%EC%B6%95%EC%A0%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-PROJECT</link>
            <guid>https://velog.io/@ong_hh/%ED%95%99%EA%B5%90-%EC%B6%95%EC%A0%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-PROJECT</guid>
            <pubDate>Sat, 14 May 2022 15:52:44 GMT</pubDate>
            <description><![CDATA[<h1 id="intro">INTRO</h1>
<h4 id="프로젝트-정보">프로젝트 정보</h4>
<blockquote>
</blockquote>
<p><strong>개발환경(IDE)</strong> : Visual Studio code (VS code), MySQLWorkbench
<strong>기술스택</strong> : React, Node.js, JavaScript 
<strong>웹 호스팅</strong> : AWS EC2, AWS Route 53
<strong>데이터베이스</strong> : MySQL, AWS RDS
<strong>형상관리</strong> : Jira, GitHub, Discord</p>
<h4 id="프로젝트-경로">프로젝트 경로</h4>
<pre><code>Root Folder
    └--Client
    └--Server(My Part!✨)</code></pre><h4 id="서버-구성도">서버 구성도</h4>
<p><img src="https://velog.velcdn.com/images/ong_hh/post/ff48a4d8-0928-40f9-a0f0-83f017a239c4/image.png" alt=""></p>
<h4 id="프로젝트-기간">프로젝트 기간</h4>
<p>2022.04.15 ~ 2022.05.16
배포기간 : 2022.05.18 ~ 2022.05.20
서버 코드를 짜는데 3일, 배포에 2주가 걸렸다.😂</p>
<p>2주나 걸린 배포,, 에러와 트러블슈팅 과정을 정리하자❗</p>
<h4 id="✨url공개✨">✨URL공개✨</h4>
<p><a href="http://dswu2022f5.site/">http://dswu2022f5.site/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[졸프] WebRTC]]></title>
            <link>https://velog.io/@ong_hh/%EC%A1%B8%ED%94%84-WebRTC</link>
            <guid>https://velog.io/@ong_hh/%EC%A1%B8%ED%94%84-WebRTC</guid>
            <pubDate>Wed, 25 Aug 2021 17:14:44 GMT</pubDate>
            <description><![CDATA[<h2 id="서론">서론</h2>
<p>졸프를 시작한지 6개월이 넘었지만,, 이제야 정리하는 <strong>WebRTC개념 정리</strong>
멀티 유저의 영상을 전송하고 전송받은 영상에 Facedetection기술을 접목시키는게 가장 큰 핵심인데, 초창기 이 프로젝트를 기획했을 때와 달리 생각보다 Multi유저간의 실시간 영상을 주고 받는게 더 어려웠고 얼굴감지는 해당 API가 워낙에 잘 되어 있어서 곧바로 얼굴인식 후 이미지를 씌울 수 있었다. 하지만 <strong>&quot;WebRTC를 이용해 멀티 유저간의 실시간 영상 전달&quot;</strong> 이게 해결이 되어야 FACE API를 적용해 보든가 할텐데 😂😂
현재 이것 때문에 6개월간 <del>도르마무</del> 이지만,, 
점점 정리는 되어가는 느낌이다. 제발 얼른 끝내게 해주세요.🙏</p>
<h2 id="webrtc란">WebRTC란?</h2>
<p><strong>WebRTC(Web Real-Time Communications)</strong>란, 웹 애플리케이션과 사이트가 중간자 없이 브라우저 간에 오디오나 영상 미디어를 포착하고 마음대로 스트림 할 뿐 아니라 임의의 데이터 교환할 수 있도록 하는 기술이다.
<U><strong><span style="color:red">드라이버나 플러그인 설치 없이</span> 웹브라우저간 P2P 연결을 통해 데이터 교환을 가능하게 하는 기술‼</strong></U></p>
<p>WebRTC는 P2P통신에 최적화가 되어있다.<del>(이것때문에 n개월간 애를 먹었다.)</del></p>
<p>WebRTC의 주요 API에는 3가지가 있다.(해당 클래스에 의해서 실시간 데이터 교환이 일어난다)</p>
<ul>
<li><strong>MediaStream</strong> - 카메라/마이크 등 데이터 스트림 접근</li>
<li><strong>RTCPeerConnection</strong> - 암호화 및 대역폭 관리, 오디오 또는 비디오 연결,Peer들 간의 데이터를 안정적이고 효율적으로 통신하게 처리하는 WebRTC 컴포넌트</li>
<li><strong>RTCDataChannel</strong> - json/text 데이터들을 주고받는 채널을 추상화한 API(일반적인 데이터의 P2P통신)</li>
</ul>
<p>RTCPeerConnection으로 연결하고자 하는 peer의 정보를 주고받아 연결된다.
이 과정을 <strong>Signaling</strong>이라고 한다.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/bddd6c81-437e-4702-9892-e5d57b139e81/image.png" alt=""></p>
<p>Caller가 Signaling서버를 통해 자신의 SessionDescription을 보내면 Callee도 마찬가지로 Signaling서버를 통해 자신의 SessionDescription을 보낸다. 그 외에도 ICECandidate를 Signaling서버를 통해 주고 받으며 peer간 연결을 완료하고 Caller와 Callee간에 Media데이터를 주고 받는다.</p>
<p><strong>즉,</strong> 두 명의 유저가 스트림을 주고 받는 것이고. 연결 요청을 한 자가 Caller, 연결을 받는 자가 Callee이다. 이 두 사람이 통신을 하기 위한 중간 역할을 해 주는 서버가 SessionDescription이다. </p>
<h2 id="webrtc의-p2p사용-시의-한계점">WebRTC의 P2P사용 시의 한계점</h2>
<p>앞서 말했듯이 WebRTC는 P2P에 최적화 되어 있다.
하지만 Peer들 간의 단순 연결 또한 쉽지 않다. <strong>왜?</strong> 를 위해서 네트워크를 공부해보자..<del>(네트워크를 배운지 n년이 지나서 개념이 어려웠다)</del></p>
<p>P2P 연결은 ip:port를 open 하거나 listen 함으로서 동작한다. 따라서 사전에 상호 간 연결 설정을 교환하고 동의해야 하는데, IP주소를 알고 있고 연결할 준비가 되었다고 해서 연결에 성공하는 것이 아니다. 공유기와 같은 라우터 장비 내부에는 NAT이라는 기술이 설정되어 있다.</p>
<p>NAT/방화벽 환경의 클라이언트는 내부망에서 사설 IP를 사용하다가, 외부망과의 인터넷 통신을 위해 NAT장비(라우터 혹은 방화벽)를 통해 공인 IP를 할당받아 인터넷 연결을 하는 구조를 가지고 있다. (<a href="https://velog.io/@ong_hh/Network-NAT%EB%9E%80">NAT개념정리</a>) 이 구조는 외부에 있는 공인 IP로 구성된 호스트에 요청과 응답을 받을 수는 있지만, NAT는 일반적으로 요청하지 않은 패킷은 차단하는 특성을 가지고 있다. </p>
<h2 id="nat방화벽을-통과하는-webrtc기술">NAT/방화벽을 통과하는 WebRTC기술</h2>
<p>WebRTC는 NAT 통과 기법을 위해 다음의 네트워크 표준을 이용한다.</p>
<h3 id="iceinteractive-connnectivity-establishment">ICE(Interactive Connnectivity Establishment)</h3>
<p>브라우저가 peer를 통한 연결이 가능하도록 해주는 프레임 워크이다.</p>
<blockquote>
<ul>
<li><strong>peer간 단순 연결 시 작동하지 않는 이유들</strong></li>
</ul>
</blockquote>
<ul>
<li>연결을 시도하는 방화벽을 통과해야 함</li>
<li>단말에 Public IP가 없다면 유일한 주소값을 할당해야 한다.</li>
<li>라우터가 peer간의 직접 연결을 허용하지 않을 때 데이터를 릴레이해야 하는 경우</li>
</ul>
<p>ICE는 위의 작업들을 수행하기 위해 STUN 서버를 이용하여 외부 주소를 획득하고 그것이 실패한다면 TURN 중계 서버를 통해 트래픽을 라우팅한다.</p>
<ul>
<li>STUN 서버는 외부 네트워크 주소를 얻는데 사용</li>
<li>TURN 서버들은 직접(P2P) 연결이 실패할 경우 트래픽을 중계하는데 사용</li>
</ul>
<h3 id="stunsession-traversal-utilities-for-nat">STUN(Session Traversal Utilities for NAT)</h3>
<p>NAT들은 사설 로컬 네트워크에서 디바이스에 IP 주소를 제공하지만 이 주소는 외부에서 사용될 수 없다. 공용 주소가 없이는 WebRTC 피어(Peer)들은 통신할 수 있는 방법이 없다. 이 문제를 해결하기 위해서 WebRTC는 STUN을 사용.</p>
<p>STUN 서버들은 공용 인터넷에서 동작하며 아래와 같은 단순한 한가지 작업을 수행한다.
(NAT 뒤에서 동작한느 어플리케이션으로부터) 전달된 요청의 IP:port 주소를 확인하고 그 주소를 응답으로 되돌려 보낸다.</p>
<p>이 절차는 WebRTC 피어(Peer)가 그 자신에 대해 공용에서 액세스 가능한 주소를 획득할 수 있도록 한 뒤 직접 연결을 설정하기 위한 시그널링 메커니즘을 통해 또다른 피어(Peer)로 전송한다</p>
<p><img src="https://images.velog.io/images/ong_hh/post/ffe352f9-2da1-4dfd-9ded-01b71957dc11/image.png" alt=""></p>
<h3 id="turntraversal-using-relays-around-nat">TURN(Traversal Using Relays around NAT)</h3>
<p>TURN은 시그널링 데이터가 아니라 피어(Peer)들 사이의 오디오/비디오/데이터 스트리밍 릴레이를 위해 사용</p>
<p>TURN 서버들은 공용 주소들을 가지고 있으므로 설령 피어(Peer)들이 방화벽이나 프록시들 뒤에 존재하더라도 피어(Peer)들이 접속할 수 있다.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/fb676255-ec95-425f-8173-b5e11492ecb9/image.png" alt=""></p>
<h2 id="정리">정리</h2>
<p>봐도봐도 헷갈렸는데 <a href="https://millo-l.github.io/WebRTC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-1-1-P2P/">millo</a>님의 정리글을 보고 이해가 잘 되어서 가져왔다.</p>
<p>**</p>
<ol>
<li>우선 각각의 peer들은 STUN 서버에 자신의 Public Address와 접근 가능한지 여부를 전달 받는다.<br></li>
<li>Peer1(Caller)이 createOffer를 통해 먼저 자신의 SessionDescription을 생성하고 Signaling Server를 통해 Peer2에게 전달한다.<br></li>
<li>Peer2가 Peer1의 SessionDescription을 전달 받고 이에 대한 답으로 createAnswer을 통해 자신의 SessionDescription을 생성하고 Signaling Server를 통해 Peer1에 전달한다.<br></li>
<li>Peer1과 Peer2 모두 자신의 SessionDescription을 생성한 후부터 자신의 ICECandidate 정보를 생성하기 시작하고 이를 각각 서로에게 전달한다.<br></li>
<li>서로의 MediaStream을 peer간 통신으로 주고 받는다.<br></li>
<li>만약 Peer1과 Peer2 둘 중 Symmetric NAT을 가진 Peer가 있는 경우 TURN 서버를 사용해 data relay로 연결을 진행해야 한다.<br></li>
</ol>
<p>**</p>
<p><strong>참고</strong>
<a href="https://m.blog.naver.com/sehyunfa/221678622407">https://m.blog.naver.com/sehyunfa/221678622407</a>
<a href="https://lovejaco.github.io/posts/webrtc-connectivity-and-nat-traversal/">https://lovejaco.github.io/posts/webrtc-connectivity-and-nat-traversal/</a>
<a href="https://millo-l.github.io/WebRTC-%EC%9D%B4%EB%A1%A0-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0/">https://millo-l.github.io/WebRTC-%EC%9D%B4%EB%A1%A0-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0/</a>
<a href="https://www.html5rocks.com/ko/tutorials/webrtc/infrastructure/">https://www.html5rocks.com/ko/tutorials/webrtc/infrastructure/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] CustomDialog]]></title>
            <link>https://velog.io/@ong_hh/Android-CustomDialog</link>
            <guid>https://velog.io/@ong_hh/Android-CustomDialog</guid>
            <pubDate>Fri, 13 Aug 2021 18:27:35 GMT</pubDate>
            <description><![CDATA[<h2 id="dialogfragment-사용">DialogFragment 사용</h2>
<ul>
<li><code>Fragment</code>를 생성</li>
<li>xml 파일에서 원하는 디자인을 생성</li>
<li><code>CustomDialogFragment.kt</code> 파일에서 코드 작성</li>
</ul>
<pre><code class="language-kotlin">    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        val view = inflater.inflate(R.layout.fragment_custom_dialog, container, false)

        dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
        dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
        dialog?.setCancelable(false)
        dialog?.window?.setGravity(Gravity.CENTER)

        return view
    }

</code></pre>
<blockquote>
</blockquote>
<ul>
<li><code>requestFeature(Window.FEATURE_NO_TITLE)</code> : dialog 의 기본 title 디자인을 없애는 처리</li>
<li><code>setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))</code> : 투명한 뒷배경 처리</li>
<li><code>setCancelable()</code> : 검은 영역을 터치 시 dialog 닫기 처리 👉 false : 닫기 처리 기능 막기, true : 닫기 처리</li>
<li><code>setGravity()</code> : dialog 위치 설정 👉 Gravity.LEFT, Gravity.CENTER, Gravity.TOP , ...</li>
</ul>
<ul>
<li>Activity 에서 dialog 생성해주기<pre><code class="language-kotlin">val customDialog = CustomDialogFragment()
customDialog.show(supportFragmentManager,&quot;custom_fragment&quot;)</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Navigation 이용]]></title>
            <link>https://velog.io/@ong_hh/Android-Navigation-%EC%9D%B4%EC%9A%A9</link>
            <guid>https://velog.io/@ong_hh/Android-Navigation-%EC%9D%B4%EC%9A%A9</guid>
            <pubDate>Fri, 13 Aug 2021 18:06:58 GMT</pubDate>
            <description><![CDATA[<h2 id="navigation">Navigation</h2>
<p><strong>Navigation 구성요소</strong>는 세 가지 주요 부분으로 구성된다.</p>
<ul>
<li><code>Navigation graph</code>: 모든 탐색 관련 정보가 하나의 중심 위치에 모여 있는 XML 리소스. 
여기에는 대상이라고 부르는 앱 내의 모든 개별적 콘텐츠 영역과 사용자가 앱에서 갈 수 있는 모든 이용 가능한 경로가 포함된다.</li>
<li><code>NavHost</code>: 탐색 그래프에서 대상을 표시하는 빈 컨테이너. 
대상 구성요소에는 프래그먼트 대상을 표시하는 NavHost, NavHostFragment가 포함된다.
<img src="https://images.velog.io/images/ong_hh/post/03aeb858-57bd-4d7b-a2db-f31d86407790/image.png" alt=""></li>
<li><code>NavController</code>: NavHost에서 앱 탐색을 관리하는 객체. 
NavController는 사용자가 앱 전체에서 이동할 때 NavHost에서 대상 컨텐츠의 스와핑을 조정한다.</li>
</ul>
<p>앱을 탐색하는 동안 탐색 그래프에서 특정 경로를 따라 이동할지, 특정 대상으로 직접 이동할지 NavController에게 전달한다. 그러면 NavController가 NavHost에 적절한 대상을 표시한다.</p>
<p><strong>장점</strong>
<code>Safe Args</code> - 대상 사이에서 데이터를 탐색하고 전달할 때 유형 안정성을 제공하는 그래프 플러그인
<code>ViewModel</code> 지원 - 탐색 그래프에 대한 ViewModel을 확인해 그래프 대상 사이에 UI 관련 데이터를 공유
등등</p>
<h2 id="환경설정">환경설정</h2>
<p>앱의 <code>build.gradle</code> 파일에 다음 종속 항목을 추가</p>
<pre><code class="language-kotlin">dependencies {
  val nav_version = &quot;2.3.5&quot;

  // Kotlin
  implementation(&quot;androidx.navigation:navigation-fragment-ktx:$nav_version&quot;)
  implementation(&quot;androidx.navigation:navigation-ui-ktx:$nav_version&quot;)

  // Feature module Support
  implementation(&quot;androidx.navigation:navigation-dynamic-features-fragment:$nav_version&quot;)

  // Testing Navigation
  androidTestImplementation(&quot;androidx.navigation:navigation-testing:$nav_version&quot;)

  // Jetpack Compose Integration
  implementation(&quot;androidx.navigation:navigation-compose:2.4.0-alpha05&quot;)
}</code></pre>
<h2 id="navigation-graph-만들기">Navigation graph 만들기</h2>
<p><img src="https://images.velog.io/images/ong_hh/post/a4be9a88-d8d7-4914-853e-cbf479277045/image.png" alt=""></p>
<blockquote>
</blockquote>
<ul>
<li>Project 창에서 <code>res</code> 디렉터리를 마우스 오른쪽 버튼으로 클릭하고 New &gt; Android Resource File을 선택합니다. New Resource File 대화상자가 나타납니다.</li>
<li>File name 필드에 <strong>&#39;nav_graph&#39;</strong>와 같은 이름을 입력합니다.</li>
<li>Resource type 드롭다운 목록에서 Navigation을 선택하고 OK를 클릭합니다.
<img src="https://images.velog.io/images/ong_hh/post/ca069324-ad33-4791-b55a-3589a7772830/image.png" alt=""></li>
</ul>
<h2 id="xml을-통한-navhostfragment-추가">XML을 통한 NavHostFragment 추가</h2>
<p>아래의 XML 예에서는 앱 기본 활동의 일부로 NavHostFragment를 보여준다.</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;androidx.appcompat.widget.Toolbar
        .../&gt;

    &lt;androidx.fragment.app.FragmentContainerView
        android:id=&quot;@+id/nav_host_fragment&quot;
        android:name=&quot;androidx.navigation.fragment.NavHostFragment&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
        app:layout_constraintRight_toRightOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;

        app:defaultNavHost=&quot;true&quot;
        app:navGraph=&quot;@navigation/nav_graph&quot; /&gt;

    &lt;com.google.android.material.bottomnavigation.BottomNavigationView
        .../&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</code></pre>
<ul>
<li><code>android:name</code> 속성은 <code>NavHost</code> 구현의 클래스 이름을 포함합니다.</li>
<li><code>app:navGraph</code> 속성은 <code>NavHostFragment</code>를 탐색 그래프와 연결합니다. 탐색 그래프는 사용자가 이동할 수 있는 이 NavHostFragment의 모든 대상을 지정합니다.</li>
<li><code>app:defaultNavHost="true"</code> 속성을 사용하면 NavHostFragment가 시스템 뒤로 버튼을 가로챕니다. 하나의 NavHost만 기본값으로 지정할 수 있습니다. 동일한 레이아웃에 여러 호스트가 있다면(예: 창이 2개인 레이아웃) 한 호스트만 기본 NavHost로 지정해야 합니다<br>

</li>
</ul>
<h2 id="navigation-graph에-대상-추가">Navigation Graph에 대상 추가</h2>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;navigation xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    app:startDestination=&quot;@id/blankFragment&quot;&gt;
    &lt;fragment
        android:id=&quot;@+id/blankFragment&quot;
        android:name=&quot;com.example.cashdog.cashdog.BlankFragment&quot;
        android:label=&quot;Blank&quot;
        tools:layout=&quot;@layout/fragment_blank&quot; /&gt;
&lt;/navigation&gt;</code></pre>
<h3 id="대상-연결">대상 연결</h3>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;navigation xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    app:startDestination=&quot;@id/blankFragment&quot;&gt;
    &lt;fragment
        android:id=&quot;@+id/blankFragment&quot;
        android:name=&quot;com.example.cashdog.cashdog.BlankFragment&quot;
        android:label=&quot;fragment_blank&quot;
        tools:layout=&quot;@layout/fragment_blank&quot; &gt;
        &lt;action
            android:id=&quot;@+id/action_blankFragment_to_blankFragment2&quot;
            app:destination=&quot;@id/blankFragment2&quot; /&gt;
    &lt;/fragment&gt;
    &lt;fragment
        android:id=&quot;@+id/blankFragment2&quot;
        android:name=&quot;com.example.cashdog.cashdog.BlankFragment2&quot;
        android:label=&quot;fragment_blank_fragment2&quot;
        tools:layout=&quot;@layout/fragment_blank_fragment2&quot; /&gt;
&lt;/navigation&gt;</code></pre>
<h2 id="대상으로-이동">대상으로 이동</h2>
<p>다음 메서드 중 하나를 사용하여 NavController를 검색할 수 있다.</p>
<p><strong>Kotlin:</strong></p>
<ul>
<li><code>Fragment.findNavController()</code></li>
<li><code>View.findNavController()</code></li>
<li><code>Activity.findNavController(viewId: Int)</code></li>
</ul>
<p><strong>Example</strong></p>
<pre><code class="language-kotlin">val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController</code></pre>
<h2 id="onclicklistener로-작동">onClickListener로 작동</h2>
<pre><code class="language-kotlin">class FirstFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_first, container, false)

        view.findViewById&lt;Button&gt;(R.id.button1).setOnClickListener {
            Navigation.findNavController(view).navigate(R.id.action_firstFragment_to_secondFragment)
        }
        return view
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[1장] 핵심 개념 이해하기]]></title>
            <link>https://velog.io/@ong_hh/1%EC%9E%A5-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ong_hh/1%EC%9E%A5-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 09 Aug 2021 10:16:52 GMT</pubDate>
            <description><![CDATA[<h1 id="핵심-개념-이해하기">핵심 개념 이해하기</h1>
<h2 id="서버">서버</h2>
<p><img src="https://images.velog.io/images/ong_hh/post/c6f42fbe-7884-4d37-bb1a-d08bfb9dd422/image.png" alt="">
<strong>서버가 하는 일</strong></p>
<blockquote>
<p> 주소창에 웹 사이트 주소(<a href="http://info.cern.ch/index.html)%EB%A5%BC">http://info.cern.ch/index.html)를</a> 입력<strong>(요청)</strong> 하면 브라우저는 그 주소에 해당하는 컴퓨터의 위치를 파악함. 그리고 그 컴퓨터로부터  웹 사이트 페이지를 받아와서 요쳥자의 브라우저(클라이언트)에 띄움<strong>(응답)</strong> . </p>
</blockquote>
<p>웹이나 앱을 사용할 때 유저의 데이터와 서비스의 데이터가 생성되는데, 이 데이터를 어딘가에 저장하고, 그 어딘가에서 클라이언트로 데이터를 받아와야 함. 이곳이 바로 <strong>서버</strong></p>
<h2 id="자바스크립트-런타임">자바스크립트 런타임</h2>
<p><strong>런타임</strong> : 특정 언어로 만든 프로그램들을 실행할 수 있는 환경</p>
<p>노드는 자바스크립트 프로그램을 컴퓨터에서 실행할 수 있다.</p>
<p>즉, 노드는 자바스크립트 실행기라고 봐도 무방하다.</p>
<p>노드는 V8과 더불어 libuv라는 라이브러리를 사용한다. </p>
<p>libuv라이브러리는 노드의 특성인 이벤트 기반, 논 블로킹 I/O 모델을 구현하고 있다. </p>
<h2 id="이벤트-기반event-driven">이벤트 기반(event-driven)</h2>
<p><strong>이벤트 기반</strong> : 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식을 의미한다. 
(이벤트로는 클릭이나 네트워크 요청 등이 있을 수 있다.)</p>
<p>이벤트 기반 시스템에서는 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해 두어야 한다. 이를 <strong>이벤트 리스너(event listener)</strong>에 <strong>콜백(callback)함수</strong>를 등록한다고 표현한다.</p>
<p>ex) 버튼을 클릭할 때 경고창을 띄우도록 설정</p>
<p>클릭 이벤트 리스너에 경고창을 띄우는 콜백 함수를 등록해두면 클릭 이벤트가 발생할 때마다 콜백 함수가 실행되어 경고창이 뜨게 된다.</p>
<p>노드도 이벤트 기반 방식으로 동작한다.</p>
<p>이벤트 기반 모델에서는 <strong>이벤트 루프(event loop)</strong>라는 개념이 등장한다. </p>
<p>여러 이벤트가 동시에 발생했을 때 어떤 순서로 콜백 함수를 호출할지 이벤트 루프가 판단한다.</p>
<br>

<p>노드는 자바스크립트 코드의 맨 위부터 한 줄씩 실행한다. 
함수 호출 부분을 발견하면 호출한 함수를 호출 스택(call stack)에 넣는다.</p>
<blockquote>
<p>다음 코드가 콘솔에 어떤 로그를 남길지 예측해보자.</p>
</blockquote>
<pre><code class="language-jsx">function first(){
    second();
    console.log(&#39;첫 번재&#39;);
}
function second(){
    third();
    console.log(&#39;두 번재&#39;);
}
function third(){
    console.log(&#39;세 번재&#39;);
}
first();</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/2c6835d1-e9b5-4b22-9932-11c450401317/image.png" alt="">
<img src="https://images.velog.io/images/ong_hh/post/a6e1421e-eb62-4e9a-95b2-a3e52017ea9c/image.png" alt=""></p>
<p><strong>anonymous 함수</strong>는 처음 실행 시의 전역 컨텍스트를 의미한다.
컨텍스트는 함수가 호출되었을 때 생성되는 환경을 의미한다.
자바스크립트 코드는 실행 시 기본적으로 전역 컨텍스트 안에서 돌아간다고 생각하는게 좋다.
함수는 실행되는 동안 호출 스택에 머물러 있다가 실행이 완료되면 호출 스택에 지워진다.
third, second, first, anonymous 순서로 지워지고, anonymous 컨텍스트까지 실행이 모두 완료되었다면 호출 스택은 비어 있게 된다.
<br></p>
<blockquote>
<p>특정 밀리초 이후에 코드를 실행하는 setTimeout을 사용해보자</p>
</blockquote>
<pre><code class="language-jsx">function run(){
    console.log(&#39;3초 후 실행&#39;);
}
console.log(&#39;시작&#39;);
setTimeout(run, 3000);
console.log(&#39;끝&#39;);</code></pre>
<p>3초 뒤에 run 함수를 실행하는 코드이다. 콘솔 결과는 쉽게 예측할 수 있지만, 호출 스택으로 설명하기는 힘들다. setTimeout 함수의 콜백인 run이 호출 스택에 언제 들어가는지 쉽게 파악할 수 없다. </p>
<p>이를 위해 이벤트 루프, 태스트 큐(task queue), 백그라운드(background)를 알아야 한다.</p>
<ul>
<li><strong>이벤트 루프</strong> : 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정하는 역할을 담당한다. 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복하므로 루프(loop)라고 부른다.</li>
<li><strong>백그라운드</strong> : setTimeout 같은 타이머나 이벤트 리스너들이 대기하는 곳입니다. 자바스크립트가 아닌 다른 언어로 작성된 프로그램이라고 봐도 된다. 여러 작업이 동시에 실행될 수 있다.</li>
<li><strong>태스크 큐</strong> : 이벤트 발생 후, 백그라운드에서는 태스크 큐로 타이머나 이벤트 리스너의 콜백함수를 보낸다. 정해진 순서대로 콜백들이 줄을 서 있으므로 콜백 큐라고도 부른다. 콜백들은 보통 완료된 순서대로 줄을 서 있지만 특정한 경우에는 순서가 바뀌기도 한다.</li>
</ul>
<blockquote>
<p><strong>run코드가 실행되는 내부 과정1</strong>
<img src="https://images.velog.io/images/ong_hh/post/fad23f9b-a1af-4409-ad60-deab4259cf82/image.png" alt=""></p>
</blockquote>
<ul>
<li>먼저 전역 컨텍스트인 anonymous가 호출 스택에 들어간다.</li>
<li>setTimeout이 호출 스택에 들어간다.</li>
<li>호출 스택에 들어간 순서와 반대로 실행되므로, setTimeout이 실행된다.</li>
<li>setTimeout이 실행되면 타이머와 함께 run 콜백을 백그라운드로 보내고, setTimeout은 호출 스택에 빠진다.</li>
<li>anonymous가 호출 스택에서 빠진다.</li>
<li>백그라운드에서 3초를 센 후  run 함수를 택스트 큐로 보낸다.(3초를 세었다는 것은 백그라운드에 맡겨진 작업이 완료된 것으로 이해해도 된다.)</li>
</ul>
<blockquote>
<p><strong>run코드가 실행되는 내부 과정2</strong>
<img src="https://images.velog.io/images/ong_hh/post/59fcb162-3e2d-4e29-b8f3-1264f1cff782/image.png" alt=""></p>
</blockquote>
<ul>
<li>이벤트 루프는 호출 스택이 비어 있으면 테스크 큐에서 함수를 하나씩 가져와 호출 스택에 넣고 실행한다.</li>
</ul>
<blockquote>
<p><strong>run코드가 실행되는 내부 과정3</strong>
<img src="https://images.velog.io/images/ong_hh/post/0c49dcd3-f830-445f-be8f-fafe7a39c4f4/image.png" alt=""></p>
</blockquote>
<ul>
<li>이벤트 루프가 run콜백을 태스크 큐에서 꺼내 호출 스택으로 올라오면, 호출 스택으로 올려진 run은 실행되고, 실행 완료 후 호출 스택은 비워진다. 이벤트 루프는 태스크 큐에 콜백함수가 들어올 때까지 계속 대기한다.</li>
<li>이벤트 루프는 호출 스택이 비어 있을 때만 태스크 큐에 있는 run함수를 호출 스택으로 가져오기 대문에 만약 호출 스택에 함수들이 너무 많이 들어 있으면 3초가 지난 후에도 run함수가 실행되지 않을 수 있다. 이것이 setTimeout의 시간이 정확하지 않을 수도 있는 이유다.</li>
</ul>
<h2 id="논-블로킹-io">논 블로킹 I/O</h2>
<p>이벤트 루프를 잘 활용하면 오래 걸리는 작업을 효율적으로 처리할 수 있다. </p>
<p>작업에는 두가지 종류가 있다. <strong>동시에 실행될수 있는 작업</strong>과 <strong>동시에 실행될수 없는 작업</strong>이다.</p>
<p>자바스크립트 코드는 기본적으로 동시에 실행될 수 없다. </p>
<p>하지만 자바스크립트 상에서 돌아가는 것이 아닌 I/O 작업 같은 것은 동시에 처리될 수 있다.</p>
<p>I/O 작업을 할 때 노드는 논블로킹 방식으로 처리하는 방법을 제공한다.</p>
<ul>
<li>논 블로킹 : 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행함</li>
<li>블로킹 : 이전 작업이 끝나야만 다음 작업을 수행함</li>
</ul>
<p>노드는 I/O 작업을 백그라운드로 넘겨 동시에 처리하기도 한다. 따라서 동시에 처리될 수 있는 작업들은 최대한 묶어서 백그라운드로 넘겨야 시간을 절약할 수 있다.</p>
<p>위 그림을 보면 처리하는 데 1초가 걸리는 작업 다섯 개가 있는데 그 중 3개는 동시에 처리할 수 있고 2개는 도이에 처리할 수 없다. 블로킹 방식(case1)으로 처리하면 5초가 걸리지만, 논블로킹 방식(case2)으로 처리하면 3초 정도로 작업 시간이 단축되게 된다.</p>
<p>이렇게 작업 순서에 따라 성능이 크게 바뀌게 된다. 동시에 처리될 수 있는 I/O 작업이라도 논 블로킹 방식으로 코딩하지 않으면 의미가 퇴색되므로 논 블로킹 방식으로 코딩하는 습관을 들여야 한다.</p>
<pre><code class="language-jsx">function longRunningTask(){
    //오래 걸리는 작업
    console.log(&#39;작업 끝&#39;)
}

console.log(&#39;시작&#39;);
longRunningTask();
console.log(&#39;다음 작업&#39;);</code></pre>
<pre><code class="language-jsx">function longRunningTask(){
    //오래 걸리는 작업
    console.log(&#39;작업 끝&#39;);
}
console.log(&#39;시작&#39;);
setTimeout(longRunningTask, 0);
console.log(&#39;다음 작업&#39;);</code></pre>
<p>setTimeout은 코드를 논 블로킹으로 만들기 위해 사용하는 기법 중 하나이다.</p>
<p>이벤트 루프를 이해했다면, setTimeout의 콜백함수인 longRunningTask가 태스크 큐로 보내지므로 순서대로 실행되지 않는다는 것을 알 수 있다. 다음 작업이 먼저 실행 된 후, 오래 걸리는 작업이 완료된다.</p>
<p>다만, 아무리 논 블로킹 방식으로 코드를 작성하더라도 코드가 전부 우리가 작성한 것이라면 전체 소요 시간이 짧아지지는 않는다. 우리의 코드는 서로 동시에 실행되지 않기 때문이다. 단순히 실행 순서만 바뀔 뿐이다.</p>
<p>그렇다고 I/O 작업이 없다고 해서 논 블로킹이 의미가 없는 것은 아니다.</p>
<p>오래 걸리는 작업을 처리해야 하는 경우, 논 블로킹을 통해 실행 순서를 바꿔줌으로써 그 작업 때문에 간단한 작업들이 대기하는 상황을 막을 수 있다는 점에서 의의가 있다. </p>
<h2 id="싱글-스레드">싱글 스레드</h2>
<p>싱글 스레드란 스레드가 하나뿐이라는 것을 의미한다.</p>
<p>우리가 작성한 자바스크립트 코드가 동시에 실행될 수 없는 이유이기도 하다.</p>
<p>스레드를 이해하기 위해서는 프로세스부터 알아야 한다.</p>
<ul>
<li><strong>프로세스</strong> : 운영체제에서 할당하는 작업의 단위이다. 노드나 웹 브라우저 같은 프로그램은 개별적인 프로세스이다. 프로세스 간에는 메모리 등의 자원을 공유하지 않는다.</li>
<li><strong>스레드</strong> : 프로세스 내에서 실행되는 흐름의 단위이다. 프로세스는 스레드를 여러 개 생성해 여러 작업을 동시에 처리할 수 있다. 스레드들은 부모 프로세스의 자원을 공유한다. 같은 주소의 메모리에 접근 가능하므로 데이터를 공유할 수 있다.
<img src="https://images.velog.io/images/ong_hh/post/f5a7a293-7220-4495-90d1-81f586667cbb/image.png" alt=""></li>
</ul>
<p>노드가 싱글 스레드라는 말을 들어봤는가, 하지만 엄밀히 말하면 싱글 스레드로 동작하지 않는다. 노드를 실행하면 먼저 프로세스가 하나 생성된다. 그리고 그 프로세스에서 스레드들을 생성하는데, 이때 내부적으로 스레드를 여러 개 생성하게 된다. 이 중에서 <strong>우리가 직접 제어할 수 있는 스레드는 하나뿐</strong>이다. 따라서 흔히 노드가 싱글 스레드라고 여겨지는 것이다.</p>
<p>스레드를 작업을 처리하는 일손으로 표현하기도 한다.</p>
<p>하나의 스레드만 직접 조작할수 있다는 것은 일손이 하나라는 셈이다.</p>
<p>요청이 많이 들어오면 한 번에 하나식 요청을 처리한다.</p>
<p>블로킹이 심하게 일어나는 작업을 처리하지만 않는다면 스레드 하나로도 충분하며, 블로킹이 발생할 것 같은 경우에는 논 블로킹 방법으로 대기 시간을 최대한 줄인다.</p>
<p>이렇게 보면 여러 개의 일을 동시에 처리할 수 있으므로 멀티 스레드가 싱글 스레드보다 좋아 보이지만 꼭 그런 것은 아니다.</p>
<p>&lt;<strong>싱글 스레드, 블로킹&gt;</strong></p>
<p>한 음식점에 점원이 한 명 있다. 손님은 여러 명이다. </p>
<p>점원 한 명이 주문을 받아 주방에 넘기고, 주방에서 요리가 나오면 손님에게 서빙을한다. 그 후 다음 손님의 주문을 받는다. </p>
<p>이런 구조라면 다음 손님은 이전 손님의 요리가 나올 때까지 아무것도 못 하고 기다려야 한다.</p>
<p>이것이 바로 싱글 스레드(점원), 블로킹 모델이다. </p>
<p>(매우 비효율적)
<img src="https://images.velog.io/images/ong_hh/post/0e15d4fd-e7b9-49ac-bd0f-c471d3298643/image.png" alt=""></p>
<p>&lt;<strong>싱글 스레드, 논 블로킹&gt;</strong></p>
<p>점원이 한 손님의 주문을 받고, 주방에 주문 내역을 넘긴 뒤 다음 손님의 주문을 받는다.</p>
<p>요리가 끝나기까지 기다리는 대신, 주문이 들어왔다는 사실만 주방에 계속 알려주는 것이다.</p>
<p>주방에서 요리가 완료되면 완료된 순서대로 손님에게 서빙한다.</p>
<p>요리의 특성(블로킹인지 논블로킹인지)에 따라 완료되는 순서가 다를 수 있으므로, 주문이 들어온 순서와 서빙하는 순서는 일치하지 않을 수도 있다.</p>
<p>이 방법이 싱글 스레드, 논 블로킹 모델이며 노드가 채택하고 있는 방식이다.</p>
<p>점원은 한 명이지만 혼자서 많은 일을 처리할 수 있다. 하지만, 그 점원 한 명이 아파서 나오지 못한다면 문제가 생길 수 있다. 또한, 요리를 하는데 시간이 오래 걸린다면(CPU를 많이 쓰는 작업) 주문이 많이 들어왔을 때 버거울 수 있다.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/bd4115ac-40a0-40d5-9c8e-11770924a38c/image.png" alt=""></p>
<p><strong>&lt;멀티 스레드, 블로킹&gt;</strong></p>
<p>멀티 스레드 방식에서는 손님 한 명이 올 때마다 점원도 한 명씩 붙어 주문을 받고 서빙한다.</p>
<p>언뜻 보면 싱글 스레드보다 좋은 방법인 것 같지만, 장단점이 있다.</p>
<p>일단 손님 한 명당 점원도 한 명이면 서빙 자체는 걱정이 없다. </p>
<p>점원 한 명에게 문제가 생겨도 다른 점원으로 대체하면 되기 때문이다. </p>
<p>하지만 손님의 수가 늘어날 수록 점원의 수도 늘어난다. </p>
<p>손님 수가 줄어들었을 때는 일을 하지 않고 노는 점원이 생기는 것도 문제가 된다.</p>
<p>점원을 새로 고용하거나 기존 점원을 해고하는 데는 비용이 발생한다.
<img src="https://images.velog.io/images/ong_hh/post/22567f74-836d-4cd3-bfcb-25a7a1193ad8/image.png" alt=""></p>
<p><strong>&lt;멀티 스레드, 논블로킹&gt;</strong></p>
<p>점원 여러명이 모두 논 블로킹 방식으로 주문을 받으면 더 좋지 않을까?</p>
<p>실제로 그렇지만, 멀티 스레드 방식으로 프로그래밍하는 것은 상당히 어렵기 때문에 멀티 프로세싱 방식을 대신 사용한다. I/O요청에는 멀티 프로세싱이 더 효율적이기도 한다.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/39a4ebb5-aa7d-4c2b-8260-94a21f0ebd0e/image.png" alt=""></p>
<h1 id="서버로서의-노드">서버로서의 노드</h1>
<p>노드는 기본적으로 싱글 스레드, 논 블로킹 모델을 사용하므로 노드 서버 또한 동일한 모델일 수 밖에 없다. 따라서 노드 서버의 장단점은 싱글 스레드, 논 블로킹 모델의 장단점과 크게 다르지 않다.</p>
<p>서버에는 기본적으로 I/O 요청이 많이 발생하므로, I/O 처리를 잘하는 노드를 서버로 사용하면 좋다. 노드는 우리가 논 블로킹 방식으로 코드를 작성했다는 가정하에 libuv라이브러리를 사용하여 I/O 작업을 논 블로킹 방식으로 처리한다. 따라서 스레드 하나가 많은 수의 I/O를 혼자서도 감당할 수 있다. </p>
<p>하지만 노드는 CPU 부하가 큰 작업에는 적합하지 않다. 우리가 작성하는 코드는 모두 스레드 하나에서 처리된다. 코드가 CPU 연산을 많이 요구하면 스레드 하나가 혼자서 감당하기 어렵다.</p>
<p>이와 같은 특성을 활용하여 노드는 개수는 많지만 크기는 작은 데이터를 실시간으로 주고받는 데 적합하다.</p>
<p>네트워크나 데이터베이스, 디스크 작업 같은 I/O에 특화되어 있기 때문이다.</p>
<p>실시간 채팅 어플리케이션, 주식차트, JSON 데이터를 제공하는 API 서버가 노드를 많이 사용한다.</p>
<p>노드에는 웹 서버가 내장되어 있어 입문자가 쉽게 접근할 수 있다. </p>
<p>노드 외의 서버를 개발하다 보면 아파치, nginx, IIS처럼 별도의 웹 서버를 설치해야 하는 경우가 많다. 심지어 톰캣 같은 웹 애플리케이션 서버(WAS)를 추가로 설치하는 경우도 있다.</p>
<p>이 경우에는 프로그래밍 외에도 웹 서버와 WAS 사용법을 익혀야 한다.</p>
<p>하지만 노드는 내장된 웹 서버를 사용하면 되므로 편리하다.</p>
<p>하지만 나중에 서버 규모가 커지면 결국 nignx 등의 웹 서버를 노드 서버와 연결해야만 한다.</p>
<p>노드 사용자들이 말하는 가장 큰 장점은 언어로 자바스크립트를 사용한다는 것이다.</p>
<p>웹 브라우저도 자바스크립트를 사용하므로 서버까지 노드를 사용하면 하나의 언어로 웹 사이트를 개발할 수 있다. 이로써 개발 생산성을 획기적으로 높였고, 생산성이 중요한 기업이 노드를 채택하는 이유가 되었다.</p>
<p>노드는 생산성은 매우 좋지만, Go처럼 비동기에 강점을 보이는 언어나 nginx처럼 정적 파일 제공, 로드 밸런싱에 특화된 웹 서버에 비해서는 속도가 느리다. </p>
<p>그렇긴 해도 극단적인 성능이 필요하지 않다면 이러한 단점은 노드의 생산성으로 어느 정도 극복할 수 있다.</p>
<p>자바스크립트를 사용함으로써 얻을 수 있는 소소한 장점도 있다.</p>
<p>요즘은 XML대신 JSON을 사용하여 데이터를 주고 받는데, JSON이 자바스크립트 형식이므로 노드에서는 쉽게 처리할 수 있다.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/1370501b-8faa-442b-be71-ef2404de8919/image.png" alt=""></p>
<h1 id="서버-외의-노드">서버 외의 노드</h1>
<p>처음에는 노드를 대부분 서버로 사용했지만, 노드는 자바스크립트 런타임이므로 용도가 서버에만 한정되지 않는다. 사용 범위가 점점 늘어나서 노드는 웹, 모바일, 데스크톱 애플리케이션 개발에도 사용되기 시작했다.</p>
<p>노드 기반으로 돌아가는 대표적인 웹 프레임워크로는 앵큘러, 리액트, 뷰 등이 있다.</p>
<p>앵귤러는 구글진영에서 프런트엔드 앱을 만들 때 주로 사용하고,</p>
<p>리액트는 페이스북 진영에서 주로 사용한다.</p>
<p>모바일 개발 도구로는 리액트 네이티브를 많이 사용한다.</p>
<p>페이스북, 인스타그램, 핀터레스트, 월마트, 테슬라 등이 리액트 네이티브를 사용하여 모바일 앱을 운영 중이다.</p>
<p>데스크톱 개발 도구로는 일렉트론이 대표적이다. 일렉트론으로 만들어진 프로그램으로는 Atom, Slack, Discord 등이 있다. 이 책에서 사용할 에디터인 비주얼 스튜디오 코드도 일렉트론으로 만들어졌다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Node.js] 1:1 채팅 구현하기]]></title>
            <link>https://velog.io/@ong_hh/Node.js-11%EC%B1%84%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ong_hh/Node.js-11%EC%B1%84%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 04 Jul 2021 11:30:19 GMT</pubDate>
            <description><![CDATA[<h2 id="1-socketio-사용하기">1. Socket.IO 사용하기</h2>
<p>웹은 기본적으로 요청을 하고 응답을 받고 끊게 되어 있음. 
But, 실시간 메시지는 연결을 끊지 않고 유지하는 것을 기본으로 함.
웹 서버는 연결 유지가 잘 안된다는 문제가 생김.
So, 웹 서버를 이용하지 않고 Soket서버를 사용(Websocket프로토콜). Soket.IO는 웹 Soket을 편리하게 사용할 수 있게 만들어준 라이브러리. Node.js기반이고 멀티 디바이스(web,android,ios,windows)를 지원하며 websocket을 지원하지 않는 browser도 지원함.</p>
<pre><code>% npm install socket.io --save
% npm install cors --save</code></pre><p>CORS도 사용해야 함. --&gt; 다른 웹서버도 접근할 수 있게 해줌.</p>
<p>두 개다 외부 모듈이라 이걸 사용해줄 수 있게 코드 작성.</p>
<pre><code class="language-javascript">// socket.io모듈 불러들이기
var socketio = require(&#39;socket.io&#39;);
// cors사용 - 클라이언트에서 ajax로 요청하면 CORS지원
var cors = require(&#39;cors&#39;);

...

//cors를 미들웨어로 사용하도록 등록
app.use(cors());

...

//socket.io 서버 시작
var io = socketio.listen(server); 
//웹 서버 위에서 websocket으로 들어오는 요청을 받아서 처리할 수 있는 준비가 됨
console.log(&#39;socket.io 요청을 받아들일 준비가 되었습니다.&#39;);
</code></pre>
<p>[클라이언트] ---웹소켓 요청---&gt; 웹서버(3000port)</p>
<p>웹 브라우저의 클라이언트가 웹소켓을 통해서 요청을 보내면 웹 서버 위에서 똑같은 3000번 port라 하더라도 프로토콜이 다름(왔다 갔다 하는 데이터 형식). 데이터를 받아서 Socket.io모듈에서 처리해줄 준비가 됨(이게 listen()메소드, attach메소드도 있음)</p>
<p>socket.io는 이벤트기반으로 처리함
클라이언트가 서버로 보낼 때 : init
클라이언트가 보내온 이벤트를 받을 때 on이 됨</p>
<pre><code class="language-javascript">io.sockets.on(&#39;connection&#39;, function(socket){
    console.log(&#39;connection info -&gt; &#39; + socket.request.connection._peername);
    socket.remoteAddress = socket.request.connection._peername.address;
    socket.remotePort = socket.request.connection_peername.port;
});</code></pre>
<p>socket이 on이라는 메소드를 가지고 있음
connection이라는 이벤트를 가짐. 함수가 socket이라는 파라미터를 가짐
클라이언트가 어떤 IP주소와 어떤 PORT에서 접속했는지 알고 싶을 때 사용하기 위해 socket객체에 속성을 추가해둠 (socket.remoteAddress, socket.remotePort)</p>
<p>연결을 맺어줄려면 접속이 가능한 클라이언트가 필요 → html</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;채팅 클라이언트 01&lt;/title&gt;
    &lt;script src=&quot;./jquery-3.1.1.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;socket.io.js&quot;&gt;&lt;/script&gt;
    &lt;script&gt;
        $(function(){

        });
        function connect(){
            var host = $(&#39;#hostInput&#39;).val();
            var port = $(&#39;#portInput&#39;).val();

            connectToServer(host, port);
        }
        function connectToServer(host, port){
            var url = &#39;http://&#39; + host + &#39;:&#39; + port;
            var options = {
                forceNew:true
            };
            var socket = io.connect(url, options);

            socket.on(&#39;connect&#39;, function(){
                println(&#39;웹소켓 서버에 연결됨 -&gt; &#39; + url);
            });

            socket.on(&#39;disconnect&#39;, function(){
                println(&#39;웹소켓 연결 종료됨.&#39;);
            });
        }

        function println(data){
            console.log(data);
            $(&#39;#results&#39;).append(&#39;&lt;p&gt;&#39; + data + &#39;&lt;/p&gt;&#39;);
        }
    &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h3&gt;채팅클라이언트 01&lt;/h3&gt;
    &lt;br&gt;
    &lt;div&gt;
        &lt;input type=&quot;text&quot; id=&quot;hostInput&quot; value=&quot;localhost&quot;&gt;
        &lt;input type=&quot;text&quot; id=&quot;portInput&quot; value=&quot;3000&quot;&gt;
        &lt;input type=&quot;button&quot; id=&quot;connectButton&quot; value=&quot;연결하기&quot; onclick=&quot;connect()&quot;&gt;
    &lt;/div&gt;
    &lt;hr&gt;
    &lt;p&gt;결과&lt;/p&gt;
    &lt;div id=&quot;results&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/a425d16a-a512-47d7-b8eb-eb7edb474afe/image.png" alt="">
서버와 연결이 되었음을 확인.
<br><br></p>
<blockquote>
<pre><code>                    ▼▽구조▽▼</code></pre><p><img src="https://images.velog.io/images/ong_hh/post/e420cb01-b1b8-4343-8f25-d7178438631d/image.png" alt="구조"></p>
</blockquote>
<h2 id="2서버에서-보낸-메시지-받기">2.서버에서 보낸 메시지 받기</h2>
<p>emit()으로 보내고 on()으로 받음</p>
<pre><code class="language-javascript">    // &#39;message&#39; 이벤트를 받았을 때의 처리
    socket.on(&#39;message&#39;, function(message) {
        console.log(&#39;message 이벤트를 받았습니다.&#39; + JSON.stringify(message));

        if(message.recepient ==&#39;ALL&#39;) {
            // 나를 포함한 모든 클라이언트에게 메시지 전달
            console.log(&#39;나를 포함한 모든 클라이언트에게 message 이벤트를 전송합니다.&#39;)
            io.sockets.emit(&#39;message&#39;, message);
        }
    });</code></pre>
<p>io.sockets.emit() 은 나를 포함한 모든 클라이언트에게 전송하고,
socket.broadcast.emit()은 나를 제외한 모든 클라이언트에게 전송함.</p>
<h2 id="311채팅하기">3.1:1채팅하기</h2>
<p><strong>진행단계</strong>
내가 지정한 사람한테 메세지를 보내는 것 = 메시지를 보낼 때 부터 받는 사람을 지정 
→ 받는 사람의 ID가 필요 
→ 로그인 기능 추가</p>
<p><strong>로그인 기능 추가하기</strong></p>
<pre><code class="language-javascript">    // &#39;login&#39; 이벤트를 받았을 때의 처리
    socket.on(&#39;login&#39;, function(input) {
        console.log(&#39;login 이벤트를 받았습니다.&#39; + JSON.stringify(input));

        // 기존 클라이언트 ID가 없으면 클라이언트 ID를 맵에 추가
        login_ids[input.id] = socket.id;
        socket.login_id = input.id;

        // 응답 메시지 전송
        sendResponse(socket, &#39;login&#39;, &#39;200&#39;, &#39;로그인되었습니다.&#39;);
    });</code></pre>
<p><strong>일대일 채팅 대상에게 메시지 전달</strong></p>
<pre><code class="language-javascript">// &#39;message&#39; 이벤트를 받았을 때의 처리
    socket.on(&#39;message&#39;, function(message) {
        console.log(&#39;message 이벤트를 받았습니다.&#39; + JSON.stringify(message));

        if (message.recepient ==&#39;ALL&#39;) {
            // 나를 포함한 모든 클라이언트에게 메시지 전달
            console.log(&#39;나를 포함한 모든 클라이언트에게 message 이벤트를 전송합니다.&#39;)
            io.sockets.emit(&#39;message&#39;, message);
        } else {
            // 일대일 채팅 대상에게 메시지 전달
            if (login_ids[message.recepient]) {
                io.sockets.connected[login_ids[message.recepient]].emit(&#39;message&#39;, message);

                // 응답 메시지 전송
                sendResponse(socket, &#39;message&#39;, &#39;200&#39;, &#39;메시지를 전송했습니다.&#39;);
            } else {
                // 응답 메시지 전송
                sendResponse(socket, &#39;message&#39;, &#39;404&#39;, &#39;상대방의 로그인 ID를 찾을 수 없습니다.&#39;);
            }
        }
    });</code></pre>
<p><strong>결과창</strong>
<img src="https://images.velog.io/images/ong_hh/post/3cc6d8b3-c921-4195-978c-d1110f037b7d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Outer,Cross,Self Join 및 Union등]]></title>
            <link>https://velog.io/@ong_hh/OuterCrossSelf-Join-%EB%B0%8F-Union%EB%93%B1</link>
            <guid>https://velog.io/@ong_hh/OuterCrossSelf-Join-%EB%B0%8F-Union%EB%93%B1</guid>
            <pubDate>Tue, 09 Mar 2021 10:40:42 GMT</pubDate>
            <description><![CDATA[<h3 id="outer-join외부조인">OUTER JOIN(외부조인)</h3>
<p>조인의 조건에 만족되지 않는 행까지도 포함시키는 것</p>
<pre><code class="language-sql">SELECT &lt;열 목록&gt;
FROM &lt;첫 번째 테이블(LEFT 테이블)&gt;
&lt;LEFT | RIGHT | FULL&gt; OUTER JOIN &lt;두 번째 테이블(RIGHT 테이블)&gt;
    ON &lt;조인될 조건&gt;
[WHERE 검색조건] ;</code></pre>
<p>전체 회원 출력시 사용.</p>
<h3 id="cross-join상호-조인">CROSS JOIN(상호 조인)</h3>
<p>한쪽 테이블의 모든 행들과 다른 쪽 테이블의 모든 행을 조인시키는 기능
그래서 결과 개수는 두 테이블 개수를 곱한 개수가 된다.
<img src="https://images.velog.io/images/ong_hh/post/5384a923-9525-448f-926a-8f39bf6fe68c/image.png" alt=""></p>
<pre><code class="language-sql">USE sqldb;
SELECT *
    FROM buytbl
        CROSS JOIN usertbl;</code></pre>
<p>샘플데이터 만들 때 종종 사용한다.</p>
<h3 id="self-join자체-조인">SELF JOIN(자체 조인)</h3>
<p>별도의 구문이 있는 것이 아니라 자기 자신과 자기 자신이 조인한다는 의미.</p>
<pre><code class="language-sql">USE sqldb;
SELECT *
    FROM buytbl A
        INNER JOIN buytbl B
            ON A.userID = B.userName
    WHERE A.userID = &#39;JYP&#39;;</code></pre>
<p>같은 테이블인데 별칭을 다르게 하면서 씀.</p>
<h3 id="unionunion-allnot-inin">UNION/UNION ALL/NOT IN/IN</h3>
<p>두 쿼리의 결과를 합친 것</p>
<p><img src="https://images.velog.io/images/ong_hh/post/f411baa5-fe79-4595-9c31-56935ac57cd0/image.png" alt=""></p>
<p>UNION : 중복된 열은 제거
UNION ALL : 중복된 열까지 모두 출력</p>
<p>NOT IN : 뒤의 내용을 빼고 출력
IN : 첫 번째 쿼리의 결과 중에서, 두 번째 쿼리에 해당되는 것만 조회</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[조인 개념과 Inner Join]]></title>
            <link>https://velog.io/@ong_hh/%EC%A1%B0%EC%9D%B8-%EA%B0%9C%EB%85%90%EA%B3%BC-Inner-Join</link>
            <guid>https://velog.io/@ong_hh/%EC%A1%B0%EC%9D%B8-%EA%B0%9C%EB%85%90%EA%B3%BC-Inner-Join</guid>
            <pubDate>Sat, 27 Feb 2021 11:10:26 GMT</pubDate>
            <description><![CDATA[<h3 id="조인">조인</h3>
<p><img src="https://images.velog.io/images/ong_hh/post/1673c192-eda2-4225-ac58-07fb40ea0893/image.png" alt=""></p>
<p>회원테이블과 구매테이블의 pk와 fk가 1:N 관계</p>
<h3 id="inner-join내부-조인">INNER JOIN(내부 조인)</h3>
<p>물건을 배송하기 위해 구매한 회원의 주소와 이 회원의 주소 정보를 알기 위해 주소 정보가 있는 회원 테이블과 결합하는 것 :** INNER JOIN
**</p>
<pre><code class="language-sql">SELECT &lt;열 목록&gt;
FROM &lt;첫 번째 테이블&gt;
    INNER JOIN &lt;두 번째 테이블&gt;
        ON &lt;조인될 조건&gt;
[WHERE 검색조건]</code></pre>
<pre><code>        👇</code></pre><pre><code class="language-sql">USE sqldb;
SELECT *
    FROM buytbl
        INNER JOIN usertbl
            ON buytbl.userID = usertbl.userID
    WHERE buytbl.userID = &#39;JYP&#39;;</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/39063a9a-7123-4d9e-9227-ad40cf519d62/image.png" alt=""></p>
<p>구매 테이블의 userID인 &#39;JYP&#39;를 추출하고 &#39;JYP&#39;와 동일한 값을 회원 테이블의 userID열에서 검색한 후 &#39;JYP&#39;라는 아이디를 찾으면 구매 테이블과 회원 테이블의 두 행을 결합(JOIN)한다.</p>
<p><em>MySQL 8.0.16</em> 버전까지는  맨 아랫줄에 ORDER BY num 구문을 넣지 않아도 buytbl의 num열에 의해서 정렬되었으나 _MySQL 8.0.17_버전에서는 ORDER BY num 구문을 넣지 않으면 userID열로 정렬된다. </p>
<p>열을 골라와서(아이디/이름/구매물품/주소/연락처 만) 뽑고 싶어서</p>
<pre><code class="language-sql">SELECT userID, name, prodName, addr, CONCAT(mobile, mobile2) AS &#39;연락처&#39;
  FROM buytbl
          INNER JOIN usertbl
              ON buytbl.userID = usertbl.userID
      ORDER BY num;</code></pre>
<p>위처럼 코드를 작성한다면, 에러가 뜬다.</p>
<p>구매테이블열도 userID열이 있고 회원테이블에도 userID열이 있다.
테이블 두 개에 같은 열 이름이 있어서 userID가 어느쪽 열인지 알 수 없다는 오류 메시지가 뜬다.</p>
<pre><code class="language-sql">SELECT buytbl.userID, name, prodName, addr, CONCAT(mobile, mobile2) AS &#39;연락처&#39;
  FROM buytbl
          INNER JOIN usertbl
              ON buytbl.userID = usertbl.userID
      ORDER BY num;</code></pre>
<p>따라서 위처럼 buytbl.userID라 써서 어느 테이블에 있는 userID를 쓰는지 명시해줘야 한다.</p>
<p>테이블 명을 앞에다가 다 붙여주는 것이 더 안전하지만, 코드가 너무 길어질 수 있다.
이를 간편하게 하기 위해서 각 테이블에 별칭을 줄 수 있다.</p>
<pre><code class="language-sql">SELECT B.userID, U.name, B.prodName, U.addr, CONCAT(mobile, mobile2) AS &#39;연락처&#39;
  FROM buytbl B
          INNER JOIN usertbl U
              ON B.userID = U.userID
      ORDER BY num;</code></pre>
<p>table다음에 띄우고 별칭을 써주면 된다.</p>
<p><strong>전체 회원 중 물품을 구매한 회원을 선별해보자.</strong></p>
<pre><code class="language-sql">SELECT DISTINCT U.userID, U.name, U.addr
  FROM usertbl U
          INNER JOIN buytbl B
              ON U.userID = B.userID
      ORDER BY U.userID;</code></pre>
<p>👉DISTINCT를 이용해 중복을 제거한다.</p>
<h4 id="세-개의-테이블의-조인-해보자">세 개의 테이블의 조인 해보자.</h4>
<p>&#39;다대다(<em>many-to-many</em>)&#39;의 관계이다.
다대다 관계는 논리적으로는 구성이 가능하지만 이를 물리적으로 구성하기 위해서는 두 테이블의 사이에 연결 테이블을 둬서 이 연결 테이블과 두 테이블이 일대다 관계를 맺도록 구성해야 한다.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/ff3a5702-3f57-4d7b-b586-fba8809b052b/image.png" alt=""></p>
<pre><code class="language-sql">SELECT S.stdName, S.addr, SC.clubName, C.roomNo
    FROM stdtbl S
            INNER JOIN stdclubtbl SC
                ON S.stdName = SC.stdName
            INNER JOIN clubtbl C
                ON SC.clubName = SC.stdName
    ORDER BY S.stdName;</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/fe2691a8-5489-4470-8493-d4aa9701da39/image.png" alt=""></p>
<p>첫번째 INNER JOIN한 결과를 가지고 다시 두번째 INNER JOIN을 하게 되는 것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[피벗,JSON]]></title>
            <link>https://velog.io/@ong_hh/%ED%94%BC%EB%B2%97JSON</link>
            <guid>https://velog.io/@ong_hh/%ED%94%BC%EB%B2%97JSON</guid>
            <pubDate>Sat, 27 Feb 2021 10:48:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ong_hh/post/c8b38a0e-19dd-406b-9c9e-5ec9b941ad23/image.png" alt=""></p>
<h3 id="피벗의-구현">피벗의 구현</h3>
<p>피벗 : 한 열에 포함된 여러 값을 출력하고, 이를 여러 열로 변환하여 테이블 반환 식을 회전하고 필요하면 집계까지 수행하는 것.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/c7e6500d-3fc0-4d21-b81c-64a448a4e1f6/image.png" alt=""></p>
<pre><code class="language-sql"> USE sqldb;
 CREATE TABLE pivotTest
     ( uName CHAR(3),
          season CHAR(2),
          amount INT );

 INSERT INTO pivotTest VALUES
     (&#39;김범수&#39;,&#39;겨울&#39;,10), (&#39;윤종신&#39;,&#39;여름&#39;,15) ;

 SELECT * FROM pivoitTest;

 SELECT uName,
     SUM(IF(season=&#39;봄&#39;,amount,0)) AS &#39;봄&#39;,
        SUM(IF(season=&#39;여름&#39;,amount,0)) AS &#39;여름&#39;,
        SUM(IF(season=&#39;가을&#39;,amount,0)) AS &#39;가을&#39;,
        SUM(IF(season=&#39;겨울&#39;,amount,0)) AS &#39;겨울&#39;,
        SUM(amount) AS &#39;합계&#39; FROM pivotTest GROUP BY u Name;</code></pre>
<h3 id="json데이터">JSON데이터</h3>
<p>JSON데이터 : 현대의 웹가 모바일 응용 프로그램 등과 데이터를 교환하기 위한 개방형 표준 포맷. </p>
<p><img src="https://images.velog.io/images/ong_hh/post/349e4a35-6b73-422d-901a-315e04f2c35b/image.png" alt=""></p>
<pre><code class="language-sql">USE sqldb;
SELECT JSON_OBJECT(&#39;name&#39;,name,&#39;height&#39;,height) AS &#39;JSON 값&#39;
    FROM usertbl
    WHERE height &gt;= 180;</code></pre>
<h4 id="json-함수">json 함수</h4>
<pre><code class="language-sql">SET @json=&#39;{&quot;usertbl&quot; :
    [
        {&quot;name&quot;:&quot;임재범&quot;,&quot;height&quot;:182},
        {&quot;name&quot;:&quot;이승기&quot;,&quot;height&quot;:182},
        {&quot;name&quot;:&quot;성시경&quot;,&quot;height&quot;:186}
    ]
}&#39;;
SELECT JSON_VALID(@json) AS JSON_VALID;
SELECT JSON_SEARCH(@json, &#39;one&#39;,&#39;성시경&#39;) AS JSON_EXTRACT; -- 첫번째로 나오는 성시경을 찾아라, one과 all이 있음
SELECT JSON_EXTRACT(@json,&#39;$.usertbl[2].name&#39;) AS JSON_EXTRACT;
SELECT JSON_INSERT(@json, &#39;$usertbl[0].mDate&#39;,&#39;2009-09-09&#39;) AS JSON_INSERT;
SELECT JSON_REPLACE(@json,&#39;$.usertbl[0].name&#39;, &#39;홍길동&#39;) AS JSON_REPLACE; -- 0번째 name을 바꿈
SELECT JSON_REMOVE(@json,&#39;$.usertbl[0]&#39;) AS JSON_REMOVE;</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/4c361af7-9380-4f2d-addb-6b3c0b1e1bff/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[sqlDB생성]]></title>
            <link>https://velog.io/@ong_hh/sqlDB%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@ong_hh/sqlDB%EC%83%9D%EC%84%B1</guid>
            <pubDate>Mon, 22 Feb 2021 10:50:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ong_hh/post/def56ce2-2a08-442f-8805-0840edabb916/image.png" alt="">
<strong>샘플데이터</strong>
<img src="https://images.velog.io/images/ong_hh/post/876af92e-3ef6-4654-b5ef-1d9c9d36adaa/image.png" alt=""></p>
<p><strong>작성</strong></p>
<pre><code class="language-sql">DROP DATABASE IF EXISTS sqldb; -- 만약 sqldb가 존재하면 우선 삭제한다.
CREATE DATABASE sqldb;

USE sqldb;
CREATE TABLE usertbl -- 회원 테이블
( userID    CHAR(8) NOT NULL PRIMARY KEY, -- 사용자 아이디(PK)
  name        VARCHAR(10) NOT NULL, -- 이름
  birthYear    INT NOT NULL, -- 출생년도
  addr         CHAR(2) NOT NULL, -- 지역(경기,서울,경남 식으로 2글자만 입력)
  mobile1     CHAR(3), -- 휴대폰의 국번(010,011 등)
  mobile2     CHAR(8), -- 휴대폰의 나머지 전화번호(하이픈제외)
  height     SMALLINT, -- 키
  mDate     DATE -- 회원 가입일
);</code></pre>
<p>NOT NULL : 입력을 반드시 해야함
VARCHAR(10) : 최대10자지만 내부적으로는 입력한 글자수 대로</p>
<pre><code class="language-sql">CREATE TABLE buytbl -- 회원 테이블
( num    INT AUTO_INCREMENT NOT NULL PRIBARY KEY, -- 순번(PK)
  userID        CHAR(8) NOT NULL, -- 아이디(FK)
  prodName         CHAR(6) NOT NULL, -- 물품명
  groupName         CHAR(6), -- 분류
  price     INT NOT NULL, -- 단가
  amount     SMALLINT NOT NULL, -- 수량
  FOREIGN KEY (userID) REFERENCES usertbl(userID)
);</code></pre>
<p>AUTO_INCREMENT : 자동으로 입력
FOREIGN KEY (userID) REFERENCES usertbl(userID) : userID열을 FOREIGN KEY로 잡아러 얘가 참조하는 레퍼런스는 usertbl의 userID이다.</p>
<pre><code class="language-sql">INSERT INTO usertbl VALUES(&#39;LSG&#39;,&#39;이승기&#39;,1987,&#39;서울&#39;,&#39;011&#39;,&#39;11111111&#39;,182,&#39;2008-8-8&#39;); -- 데이터 입력
INSERT INTO buytbl VALUES(NULL,&#39;LSG&#39;,&#39;운동화&#39;,NULL,30,2); -- 첫 번째 인자는 자동으로 증가해야 하므로  NULL</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT FROM문]]></title>
            <link>https://velog.io/@ong_hh/SELECT-FROM%EB%AC%B8</link>
            <guid>https://velog.io/@ong_hh/SELECT-FROM%EB%AC%B8</guid>
            <pubDate>Sun, 21 Feb 2021 17:39:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ong_hh/post/99854fd2-4137-4420-82a8-d12e4b1dd89d/image.png" alt=""></p>
<h3 id="select문">SELECT문</h3>
<p>데이터의 내용을 검색한다.(데이터 내용을 추출해온다)</p>
<h4 id="selectfrom">SELECT...FROM</h4>
<pre><code class="language-sql">SELECT select_expr
    [FROM table_references]
    [WHERE where_condition]
    [GROUP BY {col_name | expr | position}]
    [HAVING where_condition]
    [ORDER BY {col_name | expr | position}]</code></pre>
<p>많이 쓰이는 구문👇</p>
<pre><code class="language-sql">SELECT 열 이름
FROM 테이블이름
WHERE 조건</code></pre>
<h3 id="use문">USE문</h3>
<pre><code class="language-sql">USE employees;</code></pre>
<p>&quot;지금부터 employees를 사용하겠으니, 모든 쿼리는 employees에서 수행하라&quot;</p>
<pre><code class="language-sql">USE mysql;
SELECT * FROM employees;</code></pre>
<p>제일 많이 하는 오류!
mysql을 사용한다 해놓고 emplyees에 있는 내용을 찾아오면 오류 발생</p>
<pre><code class="language-sql">USE mysql;
SELECT * FROM employees.titles;</code></pre>
<p>이거는 가능 employees에 있는 title을 가져오겠다고 명명한 것</p>
<h3 id="show-describe문">SHOW, DESCRIBE문</h3>
<p>데이터베이스 이름, 테이블 이름, 필드 이름이 정확히 기억나지 않거나 또는 각 일므의 철자가 확실하지 않을 때 찾아서 조회하는 방법</p>
<pre><code class="language-sql">SHOW DATABASES;
USE employess;
SHOW TABLE STATUS;
DESCRIBE first_name, gender FROM employees;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[사용자 관리]]></title>
            <link>https://velog.io/@ong_hh/%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@ong_hh/%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sun, 21 Feb 2021 16:49:30 GMT</pubDate>
            <description><![CDATA[<p>실무에서는 사장, 팀장, 사원이 각각의 권한을 따로 줌으로 사용자 권한을 설정한다.</p>
<h3 id="사용자-만들기">사용자 만들기</h3>
<p><img src="https://images.velog.io/images/ong_hh/post/7b31956b-bfcd-4985-b3a0-fe83e20d65e6/image.png" alt="">
팀장은 모든 곳의 수정 권한이 있다.</p>
<p><strong>사원만들기</strong>
사원은 shopdb만 수정할 수 있고 employees를 볼 수 있다.</p>
<p><img src="https://images.velog.io/images/ong_hh/post/c3a78f01-6d70-44a1-9825-fae023f021cf/image.png" alt=""></p>
<p><strong>각 사용자로 로그인</strong>
<img src="https://images.velog.io/images/ong_hh/post/5bf4b70d-18ec-4bba-a7c0-e4dd4ab50e65/image.png" alt="">
마우스 오른쪽 클릭 후  Edit Connection에서 해당 아이디 로그인</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백업과 복원]]></title>
            <link>https://velog.io/@ong_hh/%EB%B0%B1%EC%97%85%EA%B3%BC-%EB%B3%B5%EC%9B%90</link>
            <guid>https://velog.io/@ong_hh/%EB%B0%B1%EC%97%85%EA%B3%BC-%EB%B3%B5%EC%9B%90</guid>
            <pubDate>Mon, 15 Feb 2021 14:42:42 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ong_hh/post/2a1e38f8-95b3-4dc7-997b-0118bcb5c0ed/image.png" alt=""></p>
<p><strong>백업</strong>은 현재의 데이터베이스를 다르나 매체에 보관하는 작업을 말하며, <strong>복원</strong>은 데이터베이스에 문제가 발생했을 때 다른 매체에 백업된 데이터를 이용해서 원상태로 돌려놓는 작업을 말한다.</p>
<h3 id="백업">백업</h3>
<pre><code class="language-sql">USE shopDB;
SELECT * FROM productTBL;</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/07ef0cf1-e4a0-4465-bea1-2cefda243dae/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/ong_hh/post/f3637ef8-db30-4bb4-9640-e43cc3023afb/image.png" alt=""></p>
<h3 id="복원">복원</h3>
<pre><code class="language-sql">DELETE FROM productTBL;
USE sys; -- 복원할 DB말고 다른 곳에서 사용해야함</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/b887d0e6-1420-4caa-b6d9-c21ab281332e/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/ong_hh/post/51d18070-9622-4678-8154-7ed265f23aa9/image.png" alt=""></p>
<pre><code class="language-sql">USE shopDB;
SELECT * FROM productTBL;</code></pre>
<p><img src="https://images.velog.io/images/ong_hh/post/acadb34e-2514-4358-a952-edc650774782/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>