<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ru_bryunak.log</title>
        <link>https://velog.io/</link>
        <description>프론트엔드 개발자 입니다. </description>
        <lastBuildDate>Thu, 05 Dec 2024 02:00:52 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ru_bryunak.log</title>
            <url>https://velog.velcdn.com/images/ru_bryunak/profile/10609ddb-c577-4755-a374-3f1d3d8dd1ec/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ru_bryunak.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ru_bryunak" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[앱스플라이어 작업 하면서 겪었던 이슈]]></title>
            <link>https://velog.io/@ru_bryunak/%EC%95%B1%EC%8A%A4%ED%94%8C%EB%9D%BC%EC%9D%B4%EC%96%B4-%EC%9E%91%EC%97%85-%ED%95%98%EB%A9%B4%EC%84%9C-%EA%B2%AA%EC%97%88%EB%8D%98-%EC%9D%B4%EC%8A%88</link>
            <guid>https://velog.io/@ru_bryunak/%EC%95%B1%EC%8A%A4%ED%94%8C%EB%9D%BC%EC%9D%B4%EC%96%B4-%EC%9E%91%EC%97%85-%ED%95%98%EB%A9%B4%EC%84%9C-%EA%B2%AA%EC%97%88%EB%8D%98-%EC%9D%B4%EC%8A%88</guid>
            <pubDate>Thu, 05 Dec 2024 02:00:52 GMT</pubDate>
            <description><![CDATA[<p>현재 운영중인 서비스는 처음에 Flutter로 개발되었었다.
점주용 서비스를 담당하셨던 개발자분이 Flutter로 만드셨었고 시간 단축을 위해 고객용 앱 껍데기도 Flutter로 만들었었다.</p>
<p>그런데 앱에 기능을 넣을 일이 점점 늘어나면서 웹을 React로 개발하면서 앱을 Flutter로 개발하기에는 그만큼 새로 공부를 해야하기 때문에 효율이 안나온다고 생각했다. 이후 새로 들어온 프론트 개발자와 이야기하면서도 언젠가 React native로 바꾸자고 이야기를 했는데, 결국 그분이 퇴사하실 때까지도 일이 바빠 전환하지 못했고, 최근에야 총대를 메고 전환을 해버렸다.</p>
<p>전환하면서 어차피 네이티브 설정들은 똑같을 텐데..하며 AndroidManifest 파일을 그대로 복사를 해서 원링크를 작동시켜봤는데, 앱이 설치돼있어도 스토어로만 이동을 하고 앱이 열리지 않는 문제가 발생했다.</p>
<p>나는 위 AndroidManifest 파일이 문제일거라 생각은 못하고 자꾸 앱스플라이어 원링크 설정을 만지고 있었다. 그래도 해결하지 못했고</p>
<p>결국 안되면 문서를 처음부터 다시 볼 수 밖에 없다.</p>
<p>그래서 찾아낸 부분은 이 부분이었다.</p>
<pre><code>&lt;data android:scheme=&quot;app-name&quot; /&gt;</code></pre><p>위 부분을 아래와 같이 바꾸니까 제대로 작동을 했다.</p>
<pre><code>&lt;data android:scheme=&quot;app-name&quot; android:host=&quot;*&quot; /&gt;</code></pre><p>운영팀에 혹시 기존에 사용했던 링크들이 정상 작동했었나 물어봤는데, 아무도 모르고 있었다.
당시에 나도 관심을 가졌어야 했는데 내 일이 바빠서 관심을 못가졌었다...이 부분을 반성하고.
그래서 노션에 히스토리를 남겼고, 여기로도 옮겨본다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Intent 처리를 했음에도 ERR_UNKNOWN_URL_SCHEME 발생할 때 처리]]></title>
            <link>https://velog.io/@ru_bryunak/Intent-%EC%B2%98%EB%A6%AC%EB%A5%BC-%ED%96%88%EC%9D%8C%EC%97%90%EB%8F%84-ERRUNKNOWNURLSCHEME-%EB%B0%9C%EC%83%9D%ED%95%A0-%EB%95%8C-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@ru_bryunak/Intent-%EC%B2%98%EB%A6%AC%EB%A5%BC-%ED%96%88%EC%9D%8C%EC%97%90%EB%8F%84-ERRUNKNOWNURLSCHEME-%EB%B0%9C%EC%83%9D%ED%95%A0-%EB%95%8C-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Tue, 23 May 2023 00:27:00 GMT</pubDate>
            <description><![CDATA[<p>귀찮아서 웹에서 애플로그인 처리한 것도 안남겼는데..
이번에 좀 고생을 해서 기록을 남긴다.</p>
<p>우리 서비스는 웹으로 개발 후에 Flutter 웹뷰로 띄워서 서비스 하고 있다.
(웹뷰 띄울 땐 inappwebview를 사용하고 있다.)
이번에 결제 기능을 붙여야해서 토스 결제위젯을 붙였다.</p>
<p>커스텀 할 게 없기 때문에, 토스의 가이드를 보고 그대로 따라 했는데,
KB은행 서비스로 결제하려고만 하면 ERR_UNKNOWN_URL_SCHEME 에러가 발생을 하는 것이었다.
문제는 에러화면이 뜨고 그 다음 작업은 이어 나간다는 것..(앱이 없으면 스토어로 간다던가)</p>
<p>토스 개발자분들에게 질문했으나 재현이 안돼서 더 할 수 있는게 없었다.</p>
<p>이틀간 꼬박 삽질을 하다가,
문득 이건 아닐거야..(왜냐면 이 문제 해결기에선 Inappwebview의 버전이 5.4.3 이전일 때 발생한다고 하셔서)라고 참고를 안했던 글이 떠올랐다.</p>
<p><a href="https://blog.joongna.com/%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-flutter-inappwebview-intent-err-unknown-url-scheme-%EC%B2%98%EB%A6%AC%EB%AC%B8%EC%A0%9C-9d887cc77f29">해당링크</a></p>
<pre><code class="language-dart">if (Platform.isAndroid) {
    await _convertIntentToAppUrl(currentUrl).then((value) async {
        currentUrl = value;
    });
        try {
          // 추가된 부분
          if (!navigationAction.isForMainFrame) {
              await controller.stopLoading();
          }
          //
              await launchUrlString(currentUrl);
        } catch (e) {
          currentUrl = await _convertIntentToMarketUrl(uri.toString());
          await launchUrlString(currentUrl);
        }
    } else if (Platform.isIOS) {
        await launchUrlString(currentUrl);
}
</code></pre>
<p>위 코드 처럼 수정하고
InAppWebViewGroupOptions에 resourceCustomSchemes: [&#39;intent&#39;] 추가,
shouldOverrideUrlLoading 에 아래 코드 추가했더니 해결됐다.</p>
<pre><code class="language-dart">onLoadResourceCustomScheme: (controller, url) async {
     await controller.stopLoading();
     return null;
},</code></pre>
<p>셋 중 하나라도 빠지면 안되더라.</p>
<p>테스트를 더 하다가 페이북/ISP 앱이 없을 때 앱 다운로드 하러가기 버튼을 클릭했더니 또 ERR_UNKNOWN_URL_SCHEME 에러가 발생했다.
Intent를 보니 fallback url이 있길래, fallback url이 있으면 그걸 리턴하고 아니면 intent.dataString을 넘기는 식으로 처리했다.</p>
<p>휴...어려워라</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ios 15 버전대에서 웹뷰 사용할 때 키보드 입력이 안되는 문제]]></title>
            <link>https://velog.io/@ru_bryunak/ios-15-%EB%B2%84%EC%A0%84%EB%8C%80%EC%97%90%EC%84%9C-%EC%9B%B9%EB%B7%B0-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%9E%85%EB%A0%A5%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@ru_bryunak/ios-15-%EB%B2%84%EC%A0%84%EB%8C%80%EC%97%90%EC%84%9C-%EC%9B%B9%EB%B7%B0-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%9E%85%EB%A0%A5%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Thu, 13 Apr 2023 01:20:20 GMT</pubDate>
            <description><![CDATA[<p>인풋 스타일에 -webkit-user-select: text; 를 추가해주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드에서 shouldOverrideUrlLoading 메서드 호출되지 않는 경우]]></title>
            <link>https://velog.io/@ru_bryunak/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90%EC%84%9C-shouldOverrideUrlLoading-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%98%B8%EC%B6%9C%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B2%BD%EC%9A%B0</link>
            <guid>https://velog.io/@ru_bryunak/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90%EC%84%9C-shouldOverrideUrlLoading-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%98%B8%EC%B6%9C%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EA%B2%BD%EC%9A%B0</guid>
            <pubDate>Thu, 09 Mar 2023 04:43:40 GMT</pubDate>
            <description><![CDATA[<p>내 경우에는 웹뷰 옵션으로 안드로이드 옵션에서 supportMultipleWindows 를 추가해 줬었다.</p>
<p>이걸 지우니 shouldOverrideUrlLoading 가 호출됐다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[사소한 이슈 해결기]]></title>
            <link>https://velog.io/@ru_bryunak/%EC%82%AC%EC%86%8C%ED%95%9C-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%EA%B8%B0</link>
            <guid>https://velog.io/@ru_bryunak/%EC%82%AC%EC%86%8C%ED%95%9C-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0%EA%B8%B0</guid>
            <pubDate>Tue, 07 Feb 2023 23:29:06 GMT</pubDate>
            <description><![CDATA[<p>회사의 서비스를 이용하고 있는 업체 중 최근에 노트북을 구매하고 윈도우 패치를 한 고객들에게만, 고객 검색창에서 고객을 선택했을 때 정보가 제대로 바인딩 되지 않는 이슈가 발생했다.</p>
<p>나름 급한 이슈였기 때문에 프론트엔드 두 명 모두 붙어야 했다.
특정 OS 패치에서만 발생하는 이슈였고, 좀 막막했던 것은 사실이다. OS문제를 어떻게 하지??</p>
<p>문제가 됐던 부분은 셀렉트 박스에서 고객을 선택하면 클릭 이벤트와 블러 이벤트가 발생하는 상황에서 특정 패치에서만 블러 이벤트가 발생했을 때 고객 정보가 바인딩 되지 않는 부분이었다.</p>
<p>로그를 찍어보다가 왜 해당 패치에서만 그러는걸까? 라는 의문은 제쳐두고, 왜 블러 이벤트가 발생하는 거지? 라는 생각을 했다. 블러 이벤트가 필요한게 아니라면? 증상이 아니라 병을 고치면 해결될 일이었다.</p>
<p>그래서 혹시 이전에 고객을 다수 선택한 적이 있냐고 물었고 아니라는 답변을 들었다. 다수 선택을 하고 블러 이벤트가 발생했을 때 데이터가 바인딩 돼야 했다면 어쩌면 지금의 동작도 가져갈 수 있는데 아니었던 것이다. 그럼 문제가 심플해졌다.</p>
<p>애초에 고객을 선택했을 때 두 이벤트가 발생하면  안되는 것이었다.
레거시에서 사용하던 UI컴포넌트에 블러가 되면 셀렉트 이벤트가 발생하는 속성이 들어가 있는 걸 발견하고 그 한 줄을 제거함으로써 문제를 해결했다.</p>
<p>초기 작업자 말고 이후 작업자가 블러 이벤트 발생하는걸 필연적으로 보고 추가 작업해놓은 코드가 있긴한데...나중에....윽...해결하기로 하고...</p>
<p>세팅을 하는데 한세월 걸리고 문제를 파악하고 해결하는데 1시간 남짓 걸렸다.
...밥값은 한 것 같은데 영 가성비가 안나오는 것 같기도 하고.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] 키보드 올라왔을 때 몇가지 이슈 해결 방법]]></title>
            <link>https://velog.io/@ru_bryunak/Flutter-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%98%AC%EB%9D%BC%EC%99%94%EC%9D%84-%EB%95%8C-%EB%AA%87%EA%B0%80%EC%A7%80-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@ru_bryunak/Flutter-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%98%AC%EB%9D%BC%EC%99%94%EC%9D%84-%EB%95%8C-%EB%AA%87%EA%B0%80%EC%A7%80-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 26 Jan 2023 23:34:41 GMT</pubDate>
            <description><![CDATA[<p>개발하면서 이슈가 많긴 한데 일단 간단한 것부터 남긴다.
현재 내가 맡은 서비스는 Nextjs로 개발했고, 얼추 1단계 개발은 완료했다. 앱으로 서비스할 예정이라 서비스를 Flutter로 래핑하는 작업을 하고 있다.
그런 작업 중에 발견된 이슈들.</p>
<ol>
<li><p>다른데에선 그렇지 않은데 특정 페이지에서 키보드가 올라오면 화면이 확대되는 이슈가 있었다. (IOS)
이 문제는 쉽게 해결됐는데 회사 동료가 겪었던 이슈였다. Input의 font size가 16보다 작을 때 이런 이슈가 발생을 했고, 화면 확대를 막음으로써 해결했다.</p>
<pre><code class="language-html">&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, user-scalable=no&quot; /&gt;</code></pre>
</li>
<li><p>앱으로 사용하는 특성 상 많은 모달, 오버레이 등이 사용될 수 밖에 없었다. 그래서 z-index, fixed 관련한 이슈가 발생을 했다. 크롬에서는 문제가 없었는데 fixed 처리해놓은 Navigation이 Overlay 위로 올라 온다던지, 이미지 뷰어를 만들어서 띄웠는데 이미지 뷰어 뒤쪽의 스크롤이 노출된다던지..
가장 쉽게 해결하는 방법은 Portal을 사용해서 가장 상위에 엘리먼트를 띄우는 것이다.
모달을 모달답게, 토스트를 토스트답게 사용하려면 최상단에 띄워야 한다고 하는 글을 얼핏 본 것 같지만, 자잘한 관련 이슈를 해결하려면 무조건 최상단으로 올리는게 맞는 것 같다.
그게 어렵더라도 최소한 해당 컴포넌트 레벨에서 최상단으로 올려야 문제가 안생긴다. 나같은 경우는 크게 공지사항 내용과 댓글 영역, fixed 처리 된 댓글 작성 영역이 있었고 원래는 댓글 작성 영역이 댓글 영역에 포함되어 있었다. 그런데 fixed로 넣은 이미지가 노출되지 않는 문제가 발생했다. 그래서 이들을 분리해서 프래그먼트 안에 헤더, 공지사항 내용, 댓글, 댓글 작성 이런 식으로 배치했다. 댓글 작성 영역을 제일 상위로 올림으로써 문제를 해결했다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Query 맛보기]]></title>
            <link>https://velog.io/@ru_bryunak/React-Query-%EB%A7%9B%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@ru_bryunak/React-Query-%EB%A7%9B%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 10 Nov 2022 08:05:20 GMT</pubDate>
            <description><![CDATA[<p>채용글에도 제법 보이고, 얼마전 면접을 볼 때도 react-query에 대해서 아는지 질문이 들어왔다.
어...react-swr은 예전에 본적이 있는 것 같긴 한데 뭐지?</p>
<p>결론을 위로 당겨써보자면, react query 를 사용하면 아래와 같은 장점이 있다.</p>
<ol>
<li>나는 이 때 새로 패치하겠어! 라고 선언적으로 프로그래밍 할 수 있기 때문에 코드가 간결해진다. 
<code>리덕스를...써보진 않았지만 기본적으로도 작성해야할 코드가 많은데 비동기 작업을 하려면 리덕스 사가나 썽크와 같은 라이브러리도 사용해야 하기 때문에 코드가 더 길어진다.</code></li>
<li>원하는 시점에 서버의 상태를 업데이트 할 수 있다. (인터벌, 포커싱 될 때 등등)</li>
</ol>
<p>그렇다고 react-query 만으로도 상태관리가 가능하냐? 하면 그건 아니다.
이 라이브러리는 서버에서 받아오는 데이터만을 다루기 때문에 전역 상태 관리는 따로 해야 한다.
대신 그 양이 줄어들기 때문에 가벼운 상태 관리 라이브러리를 사용해 볼 수는 있겠다.</p>
<h1 id="react-query">React Query</h1>
<p><img src="https://velog.velcdn.com/images/ru_bryunak/post/7c382eac-f3ad-4916-9789-d7d347012a9e/image.png" alt=""></p>
<blockquote>
<p>React Query는 서버로부터 데이터를 가져오고, 그 데이터를 캐싱, 에러 핸들링 등 비동기 작업을 수월하게 할 수 있도록 도와주는 라이브러리이다.</p>
</blockquote>
<p>먼저 React Query는 여느 라이브러리와 마찬가지로 최상단에서 프로바이더를 통해 무언가를 주입을 하고 시작한다.</p>
<pre><code class="language-typescript">import { QueryClientProvider, QueryClient } from &quot;react-query&quot;;

const queryClinet = new QueryClient();

&lt;QueryClientProvider client={queryClinet}&gt;
  &lt;App /&gt;
  &lt;/QueryClientProvider&gt;</code></pre>
<p>그 다음 사용법을 보도록 하자.
먼저 <code>useQuery</code>다.</p>
<pre><code class="language-typescript">const result = useQuery(키, 함수, 옵션);</code></pre>
<p>첫번째 인자로 들어가는 키는 어플리케이션 전체에서 쿼리를 다시 가져오고, 캐싱하고, 공유하는데 내부적으로 사용된다.
두번째 인자로 들어가는 통신을 위한 함수로 프로미스를 반환한다.
옵션들에는 이런 것들이 있다.</p>
<blockquote>
</blockquote>
<p>refetchInterval: 지정한 시간 간격에 따라 데이터를 패칭
refetchOnWindowFocus: 브라우저에 포커싱 될 때마다 새로운 데이터 패칭
onSuccess: 패칭 성공
onError: 패칭 실패</p>
<p>result 에는 아주 다양한 상태들이 포함되어 있고 기본적인 상태는 아래와 같다.</p>
<blockquote>
<p>isLoading: 아직 데이터를 받아오지 못함.
isError: 에러가 발생함.
isSuccess: 데이터를 받아옴.</p>
</blockquote>
<p>아래는 간단하게 테스트해본 코드이다.
path만 나와있는건 무시하자. axios baseurl 설정을 해놔서 그렇다.</p>
<pre><code class="language-typescript">  const { data, isLoading, isError } = useQuery(&quot;getPosts&quot;, async () =&gt; {
    const { data } = await axios.get(&quot;/post&quot;);
    return data;
  });

  if(isLoading) {
    return &lt;h2&gt;Loading...&lt;/h2&gt;
  }

  if(isError) {
    return &lt;h2&gt;{error.message}&lt;/h2&gt;
  }

  return (
    &lt;&gt;
      { data?.map(({ id, title }) =&gt; (
        &lt;div key={id}&gt;{title}&lt;/div&gt;
      )) }
    &lt;/&gt;
  );</code></pre>
<p>그 다음은 <code>useMutation</code>이다. 이 훅은 POST, PUT, DELETE 요청을 할 때 사용을 한다.</p>
<pre><code class="language-typescript">const { mutate } = useMutation(함수, 옵션);</code></pre>
<p>함수의 역할은 useQuery와 같고, mutate함수를 통해 api 호출 함수를 실행할 수 있다.
옵션에서는 뮤테이션의 진행상황에 따른 콜백함수를 작성할 수 있다. 보통 뮤테이션 성공 후 캐시를 초기화할 때 사용할 수 있다.
<del>어...? 이거 완전 아폴로랑 비슷...</del></p>
<pre><code class="language-typescript">  const { mutate } = useMutation(value =&gt; {
    return axios.post(&quot;/post&quot;, value)
  }, {
      onSuccess: (data, variables) =&gt; {
      // 작업에 성공하면 getPosts에 매핑된 useQuery api 함수를 실행한다.
      queryClient.invalidateQueries(&quot;getPosts&quot;);
      // 만약 muation 에서 리턴된 값을 이용해 get 을 해야 하는 경우에는 setQueryData를 사용한다.
      queryClient.setQueryData([&quot;getPosts&quot;, { id: 5 }], data);
    }
  })

  return (
    &lt;&gt;
      &lt;button onClick={handleClick}&gt;버튼&lt;/button&gt;
    &lt;/&gt;
  );</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 기초 - 3 (실행 컨텍스트, 호이스팅, this)]]></title>
            <link>https://velog.io/@ru_bryunak/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-3-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@ru_bryunak/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-3-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Tue, 02 Jun 2020 01:16:07 GMT</pubDate>
            <description><![CDATA[<h1 id="실행-컨텍스트execution-context란">실행 컨텍스트(execution context)란?</h1>
<p>실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이며, 아래와 같이 생겼다.</p>
<pre><code>ExecutionContext = {
  Lexical Environment: [Lexical Environment],
  Variable Environment: [Lexical Environment],
  ThisBinding: [object]
}</code></pre><ul>
<li><code>Variable Environment</code>: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언 시점의 <code>Lexical Environment</code>의 스냅샷</li>
<li><code>Lexical Environment</code>: <code>Variable Environment</code> 를 복사한 객체</li>
<li><code>This Binding</code>: <code>this</code>가 바인딩 되야할 객체</li>
</ul>
<p><code>Lexical Environment</code> 타입은 아래와 같이 생겼다.</p>
<pre><code>Lexical Environment = {
  enviroment record: {},
  outer enviroment reference: {}
}</code></pre><p><code>enviroment record</code> 에는 현재 <code>Context</code>에서 선언된 함수, 변수, 매개변수들이 저장되는 공간이다.
<code>outer enviroment reference</code> 는 현재 <code>Context</code> 를 기준으로 외부 <code>Context</code> 를 참조하는 공간이다.</p>
<p><strong>아래와 같은 코드가 있다고 해보자.</strong></p>
<pre><code>function sum(x, y) {
  var result = x + y;
  var etc = function() {
    console.log(&#39;good&#39;)
  }
  function msg() {
    return result;
  }
}
sum(10, 20)</code></pre><p>우선 자바스크립트 엔진이 함수를 만났으니 함수 컨텍스트를 생성할 것이다.
그리고 <code>this</code>를 바인딩 해줘야 하는데, 기본적으로 함수의 this는 window이다.
그 다음, <code>Lexical Environment</code> 를 넣어준다.</p>
<pre><code>{
  Decleartion EnvironmentRecord: {},
  Outer Environment Reference: null
}</code></pre><p>먼저, <code>DecleartionEnvironmentRecord</code> 에 매개변수 x와 y가 들어간다.
그 다음, 함수 선언문을 가져오고, 함수 내부에서 사용할 수 있는 <code>arguments</code> 를 세팅한다.
그 후, 선언된 변수를 가져온다.
중간 정리를 해보자.</p>
<pre><code>{
  Decleartion Environment Record: {
    x: 10,
    y: 20,
    msg: Function Reference,
    arguments: Arguments Object,
    result: undefined,
    etc: undefined,
  },
  Outer Environment Reference: null,
}</code></pre><p>코드를 보면 알겠지만, msg 는 함수를 참조하고 있고, etc 는 undefined 이다.
컨텍스트가 생성이 되면 변수 선언이 먼저 되고, 할당이 함수 실행 후에 되기 때문이다.</p>
<p>자, 이제 함수를 실행하게 되면</p>
<ul>
<li><code>Outer Environment Reference</code> 는 가장 가까운 상위 컨텍스트를 참조한다. 기본적으로는 <code>window</code> 일 것이다.</li>
<li><code>var result = x + y;</code> 가 실행되며 <code>result</code> 에 <code>30</code> 이 할당된다.</li>
<li><code>var etc</code> 는 우항의 함수를 참조한다.</li>
</ul>
<h1 id="호이스팅">호이스팅</h1>
<p>위에서 보았듯이, 자바스크립트가 실행이 되면 먼저 컨텍스트 내부의 모든 변수를 수집한다.
즉, 함수를 실행하기 전에 자바스크립트 엔진은 해당 환경에 속한 변수들을 모두 알고 있게 되는 것이다.
이를 자바스크립트 실제 동작 방식과 다르지만, 변수들을 모두 최상단으로 끌어올린 것과 다를바 없다고 하여, 끌어올리다의 의미의 <code>hoist</code> 에 <code>ing</code> 를 붙여 호이스팅(<code>hoisting</code>) 이라고 한다.</p>
<h1 id="this">this</h1>
<p>기본적으로 자바스크립트 엔진이 <code>this</code> 를 바인딩 하는 메커니즘은 함수 실행 시의 좌항을 보는 것이다.
아래에서 this 바인딩에 대해 더 알아보자.</p>
<p><strong>1. 기본바인딩</strong></p>
<pre><code>var name = &#39;foo&#39;;

function foo() { console.log(this.name) };

foo(); //  &quot;foo&quot;</code></pre><p>기본적으로 함수는 window에 바인딩 된다.</p>
<p><strong>2. 객체 또는 new 통한 바인딩</strong></p>
<pre><code>function foo() { console.log(this.name) };
var name = &#39;baz&#39;;
var obj = { name: &#39;foo&#39;, bar: foo };

obj.bar(); // &quot;foo&quot;</code></pre><blockquote>
<p><strong>메소드와 함수의 차이</strong>
함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다.
어떤 함수를 객체의 프로퍼티에 할당한다고 해서 그 자체로 메서드가 되는 것이 아니라 객체의 메서드로서 호출한 경우에 메서드로 동작하고 그렇지 않으면 함수로 동작한다.</p>
</blockquote>
<p>간단히 말해서, 함수 앞에 점이 있으면 메서드, 아니면 함수이다. 
메서드의 <code>this</code> 는 점 앞의 마지막 객체를 <code>this</code> 로 한다. 즉, 위 코드에서  <code>this</code> 는 <code>obj</code> 이다.</p>
<pre><code>function foo() { console.log(this.name) };
var name = &#39;baz&#39;;
var obj = { name: &#39;foo&#39;, bar: foo };
var obj2 = obj.bar;

obj2(); // &quot;baz&quot;</code></pre><p>당연히도, 전역변수를 선언 후 함수를 참조하게 한다면, <code>this</code> 는 전역을 가리키게 된다.</p>
<pre><code>function Person(name) { this.name = name; };
Person.prototype.hello = function() { console.log(this.name) };
var obj = new Person(&#39;foo&#39;);
obj.hello(); // &quot;foo&quot;</code></pre><p>new를 사용하여 객체를 생성하면, this는 생성된 객체에 바인딩된다.</p>
<p><strong>3. 명시적 바인딩</strong>
명시적으로 this를 바인딩 해줄 수 있다. 자바스크립트의 call, bind, apply 함수가 그런 역할을 한다.
위 세 함수의 차이점은 아래와 같다.</p>
<ul>
<li>bind 함수는 선언할 때에, apply 와 call 함수는 호출할 때에 this를 바인딩하고 인자들을 제공한다.</li>
<li>apply 함수는 인자들을 배열로, bind와 call 함수는 인자들을 리스트로 제공한다.</li>
</ul>
<h1 id="참조">참조</h1>
<p><a href="https://medium.com/@kkak10/lexical-environment-4e0cffcad98d">https://medium.com/@kkak10/lexical-environment-4e0cffcad98d</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 기초 - 2 (데이터 타입, 변수, 불변성)]]></title>
            <link>https://velog.io/@ru_bryunak/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-2-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85-%EB%B3%80%EC%88%98-%EB%B6%88%EB%B3%80%EC%84%B1</link>
            <guid>https://velog.io/@ru_bryunak/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-2-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85-%EB%B3%80%EC%88%98-%EB%B6%88%EB%B3%80%EC%84%B1</guid>
            <pubDate>Wed, 27 May 2020 08:42:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 글은 공부하면서 작성한 글로, 너무 깊게는 들어가지 않으려 합니다.
그리고 많은 부분을 리액트와 연관지어 작성할 예정입니다.</p>
</blockquote>
<h1 id="데이터-타입의-종류">데이터 타입의 종류</h1>
<p>최신 ECMAScript 표준은 다음과 같은 자료형을 정리한다.</p>
<ul>
<li>기본 자료형인 여섯가지 데이터타입<ul>
<li>Boolean</li>
<li>Null</li>
<li>Undefined</li>
<li>Number</li>
<li>String</li>
<li>Symbol</li>
</ul>
</li>
<li>객체 (Objects)</li>
</ul>
<p>보통 기본형과 참조형이라 나누어 부르기도 하며, 객체는 <code>object</code> <code>Array</code> <code>Function</code> <code>Date</code> <code>WeakMaps</code> <code>Maps</code> <code>Sets</code> 등을 일컫는다.</p>
<h1 id="변수-선언과-데이터-할당">변수 선언과 데이터 할당</h1>
<p>변수(variable). 여기서 변수란 데이터 할당을 통해 변할 수 있는 어떤 것 이라 할 수 있다.</p>
<pre><code>let foo;
foo = 1;
let bar = 1;</code></pre><p>첫번째 줄에서는 변수 <code>foo</code> 를 <strong>선언</strong>하고 있으며, 두번째 줄에서는 변수 <code>foo</code> 에 데이터를 <strong>할당</strong>하고 있다.
세번째 줄은 <strong>선언과 할당</strong>을 동시에 하고 있다.</p>
<p>변수를 선언하면, 컴퓨터는 메모리에서 비어있는 공간을 확보하고, 이 공간의 이름을 <code>foo</code> 라고 지정한다.
그 다음, 변수에 데이터를 할당하면 데이터를 저장하기 위한 메모리 공간을 확보한 후 1을 저장하고, <code>foo</code> 는 해당 메모리의 주소를 참조한다.</p>
<p>만약, 10 이라는 데이터로 재할당 한다면, 10이라는 데이터가 있는지 체크한 후 없으면 메모리 공간을 확보해 10을 저장할 것이다. 그리고 <code>foo</code> 는 새로 만들어진 메모리의 주소를 참조하게 될 것이다.</p>
<p><strong>그렇다면 객체를 할당할 때는 어떨까?</strong></p>
<pre><code>let foo = { name: &#39;John&#39;, age: 1 }</code></pre><p>변수는 총 3가지다. <code>foo</code>, <code>name</code>, <code>age</code>
각 변수명을 가진 메모리 공간이 확보될 것이고,
<code>foo</code> 는 <code>name</code> 과 <code>age</code>를  프로퍼티로 가진 데이터 그룹의 주소를 참조하게 될 것이다.
그리고 위 데이터 그룹은 <code>name</code> 과 <code>age</code> 라는 공간의 주소를 참조하게 될 것이다.</p>
<p>이와 같이 참조-참조를 하기 때문에 <code>name</code> 의 값을 바꾼다고 해도 <code>foo</code> 의 값에는 영향을 주지 못한다.
<code>foo</code> 가 참조하는 주소값을 변경시키려면, let foo = { name: &#39;James&#39;, age: 1&#39; } 과 같이 새로운 객체를 할당해야 한다.</p>
<h1 id="불변성">불변성</h1>
<p>리액트는 함수형 프로그래밍을 지향하고, 함수형 프로그래밍에서는 데이터가 변할 수가 없는데, 이를 불변성이라 한다. 불변성은 리액트 뿐만 아니라 Vue.js 나 Angular 등의 라이브러리나 프레임워크에서도 사용되는 개념이다.</p>
<p>객체는 참조를 통해 공유되고 있고, 그 상태는 언제든지 변경될 수 있기 때문에 문제가 발생할 여지가 있다. 객체를 참조하는 모든 장소에서 영향을 받기 때문이다.
이런 현상을 막기 위해 객체를 복사하여 새로운 객체를 생성한 후 변경해야 한다.</p>
<p>자바스크립트에서는 원본 객체의 불변성을 유지하게 도와줄 메서드로 Object.assign 을 제공하고 있다.
불변성을 유지하면 복제나 비교를 위한 조작을 단순화 할 수 있다.
하지만, Object.assign은 객체를 얕은 복사(shallow copy)를 한다. 
즉, 바로 아래단계의 객체의 주소값만 복사하기 때문에, 중첩된 객체들의 주소값들은 복사하지 못한다.</p>
<p>깊은 복사(deep copy)를 하려면 재귀적으로 객체를 복사하는 메서드를 만들거나, 라이브러리를 이용하거나, JSON.parse(JSON.stringify(foo)) 과 같이 JSON 형식으로 바꾸고 다시 파싱을 해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SPA 그리고 SSR과 CSR]]></title>
            <link>https://velog.io/@ru_bryunak/SPA-%EC%82%AC%EC%9A%A9%EC%97%90%EC%84%9C%EC%9D%98-SSR%EA%B3%BC-CSR</link>
            <guid>https://velog.io/@ru_bryunak/SPA-%EC%82%AC%EC%9A%A9%EC%97%90%EC%84%9C%EC%9D%98-SSR%EA%B3%BC-CSR</guid>
            <pubDate>Wed, 27 May 2020 02:27:27 GMT</pubDate>
            <description><![CDATA[<h1 id="spasingle-page-application">SPA(Single Page Application)</h1>
<p><code>SPA</code> 란 말 그대로 한개의 페이지를 가진 어플리케이션이다.
왜 <code>SPA</code> 로 개발하느냐고 묻는다면,</p>
<ul>
<li>사용자 친화적</li>
<li>초기 렌더링 후 데이터만 받아오기 때문에, 상대적으로 서버 요청이 적음</li>
<li>Virtual Dom</li>
<li>프론트 엔드와 백엔드 분리로 개발업무 분업화 및 협업이 용이</li>
<li>개발이 상대적으로 효율적</li>
</ul>
<p>정도를 말할 수 있겠다.</p>
<p>기본적으로 <code>SPA</code> 는 클라이언트 사이드 렌더링이지만 그렇다고 <code>SPA === CSR</code> 은 아니다. 이제 아래에서 <code>SPA</code> 에서의 <code>SSR</code> 과 <code>CSR</code> 에 대해 알아보자</p>
<h1 id="ssrserver-side-rendering">SSR(Server Side Rendering)</h1>
<p><img src="https://images.velog.io/images/ru_bryunak/post/772ce7cf-f920-4373-8202-ed862e77c22e/546df7c4b04ea9ca5f72d822ca1d23b4.png" alt="">
과거 그리고 현재도 그렇지만, 많은 웹사이트들은 페이지를 이동할 때마다 서버에 새로운 페이지에 대한 요청을 하는 방식을 택하고 있다.</p>
<p>이 방식이 <code>SSR</code>이다. 서버에서 렌더링을 마치고, Data가 결합된 HTML파일을 내려주는 방식이다. 새로운 페이지로 이동할 때마다 서버에 요청하여 페이지를 받아야 하기 때문에, 받아오는 시간동안 깜빡거리는 현상을 마주할 수 있다.</p>
<p>한편, SPA(Single Page Application) 기법이 대두 되면서 <code>CSR</code> 방식이 각광받기 시작했다.</p>
<h1 id="csrclient-side-rendering">CSR(Client Side Rendering)</h1>
<p><img src="https://images.velog.io/images/ru_bryunak/post/cd76c9fd-36c1-45dc-9015-ac99b27202f3/acca0a2e60d91ba7eef6a7967b6b7d2f.png" alt="">
<code>CSR</code> 방식은 최초 요청시에 HTML을 비롯해 CSS, Javascript 등 각종 리소스를 받아온다. 이후에는 서버에 데이터만 요청하고, 자바스크립트로 뷰를 컨트롤 한다.
당연히 초기 요청 때 <code>SSR</code> 보다 많은 리소스를 요청하기 때문에, 렌더링은 속도는 <code>SSR</code>이 더 빠르다.
하지만 이후 다른 페이지로의 이동시에는 <code>SSR</code> 보다 빠른 페이지 전환 속도와 더 나은 사용자 경험을 제공한다.</p>
<p>만약 인터넷 속도가 어어엄청 느리다면, 유저는 제대로된 화면을 한참 후에나 만나볼 수 있을 것이다. 일단 처음 받게될 HTML은 빈페이지일 테니까.</p>
<h1 id="seosearch-engine-optimization-문제">SEO(Search Engine Optimization) 문제</h1>
<p>아무래도 리액트나 뷰를 사용하면서, <code>CSR</code>을 할 지 <code>SSR</code>을 할 지 고민하게 되는 이유는 SEO 때문이다. 물론 회사내에서만 사용하는 CMS라면 신경쓸 필요가 없지만 공식 홈페이지와 같이 일반 사용자에게 검색되어야 하는 사이트라면 SEO 때문에 <code>SSR</code>에 대해 생각하게 된다.</p>
<p>사실 <code>갓구글</code>은 크롤러 안에 자바스크립트 엔진이 들어있어서 크게 문제될게 없지만, 네이버나 다음은 다르다. 왜냐면...그들은 자바스크립트를 해석할 수 있는 엔진이 없어서 빈 페이지로 인식할테니까!</p>
<blockquote>
<p>렌더링 퍼포먼스 외적인 측면도 다루었다. 흔히 많이들 하는 오해가 CSR은 SEO가 잘 되지 않는다라는 것인데, 많은 크롤러들이 JavaScript를 지원하지 않기 때문에 발생한 오해다. Google Bot(크롤러)은 JavaScript를 지원하기 때문에 CSR 사이트도 SEO가 잘 된다. 특히, 최신 버전의 Google Bot은 ES2015 이상의 최신 JavaScript도 지원한다. 또한 Full SSR 없이도 메타 태그들을 잘 활용하면 SEO를 잘 지원할 수 있다.
<a href="https://hyunseob.github.io/2019/05/26/google-io-2019-day-3/">https://hyunseob.github.io/2019/05/26/google-io-2019-day-3/</a></p>
</blockquote>
<p>이를 해결하기 위해 SSR with Hydration 기법이 나왔는데, 대표적으로 React 진영의 Next.js와 Vue 진영의 Nuxt.js가 위 기법을 구현한 프레임 워크다.
즉, 처음엔 서버사이드 렌더링을 하고, 그 후 다른 페이지들에선 클라이언트 사이드 렌더링을 이용하는 방식이다.</p>
<h1 id="직접-ssr-구현-nextjs-사용">직접 SSR 구현? Next.js 사용?</h1>
<p>현재 사이드 프로젝트로 진행중인 서비스의 CMS 구현을 어느정도 마치고, 공식 홈페이지를 개발해야하는 상황이다. 유명한 개발 블로거들은 직접 SSR을 구현하는게 낫다고 하는데, 세부설정이 불편하기 때문이라고... 그 세부설정이 뭔지 모르겠는데, 오늘 아침 개발자 카톡방에서 SSR 이야기가 나와서 살짝 운을 띄웠더니 버전 업 되면서 세부 설정이 없어도 된다고 하더라.
도대체 세부 설정이 뭐지...?
여튼, 회사 공식홈페이지도 Next.js 로 했었는데 잘 모른채로 사용했던지라 이번에는 꼼꼼히 세팅해볼 생각이다.</p>
<p>간단하게 Next.js가 제공해주는 기능을 보고 넘어가자면</p>
<ul>
<li>페이지 기반 라우팅 시스템 (동적 라우팅 지원)</li>
<li>pre-rendering , 페이지별 정적파일 생성과 서버사이드 렌더링 지원</li>
<li>코드스플리팅</li>
<li>prefetch(필요한 데이터를 미리 요청) 기능을 갖춘 클라이언트 사이드 라우팅</li>
<li>CSS, Sass 기본 지원 및 다른 CSS-in-JS 라이브러리 지원</li>
<li>HMR(Hot Module Replacement) 지원</li>
<li>API routes to build API endpoints with Serverless Functions</li>
<li>Fully extendable</li>
</ul>
<p>음..마지막 두개는 어떻게 이해하기 쉽게 해석 해야할 지 모르겠어서, 원문 그대로 남겨둔다.
여튼, 위와 같이 편한 기능을 제공하기에 특별한 이유가 없다면 사용하는게 나을 것 같다.
한가지 불편한 점이 있다면, 선택적 핫로딩이 안된다는 점...</p>
<blockquote>
</blockquote>
<p>Next.js 9 패치노트 번역본
<a href="https://velog.io/@jakeseo_me/%EB%B2%88%EC%97%AD-Next.js-9-%ED%8C%A8%EC%B9%98%EB%85%B8%ED%8A%B8">https://velog.io/@jakeseo_me/%EB%B2%88%EC%97%AD-Next.js-9-%ED%8C%A8%EC%B9%98%EB%85%B8%ED%8A%B8</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 기초 - 1 (자바스크립트 엔진)]]></title>
            <link>https://velog.io/@ru_bryunak/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-1</link>
            <guid>https://velog.io/@ru_bryunak/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-1</guid>
            <pubDate>Tue, 26 May 2020 12:18:54 GMT</pubDate>
            <description><![CDATA[<h1 id="자바스크립트-엔진이란">자바스크립트 엔진이란?</h1>
<p>자바스크립트 엔진은 자바스크립트 코드를 해석하고 실행하는 인터프리터이다.
대중적으로 알려진 엔진은 구글의 V8엔진인데, 아무래도 가장 많이 사용되는 크롬에서 사용되고, <code>Node.js</code>에서 사용되는 엔진이기도 하기 때문인 듯 하다.</p>
<p><strong>텍스트</strong>하지만 렌더링 엔진과 마찬가지로 브라우저마다 다른 엔진을 사용한다.</p>
<ul>
<li>SpiderMonkey : 파이어폭스</li>
<li>V8 : 크롬</li>
<li>Webkit : 사파리</li>
<li>Chakra : 익스플로러, 엣지</li>
</ul>
<blockquote>
<p><strong>자바스크립트 엔진은 메모리 힙과 호출 스택으로 이루어져 있다.</strong></p>
</blockquote>
<ul>
<li>메모리 힙 : 변수와 객체의 메모리 할당이 발생하는 곳</li>
<li>콜 스택 : 코드가 실행될 때마다 호출 스택이 쌓이는 곳
<img src="https://images.velog.io/images/ru_bryunak/post/79a826fb-e84c-4e28-b049-609af759ff63/js-engine-structure.png" alt=""></li>
</ul>
<h1 id="런타임">런타임</h1>
<p>자바스크립트 엔진에서 제공해주지 않지만 자바스크립트 개발자가 사용하는 <code>setTimeout</code> 과 같은 API들이 있다. 이러한 API들은 브라우저에서 제공하는 Web API이다. 그리고 이런 Web API 의 호출을 제어하기 위한 콜백큐와 이벤트 루프가 있다.
<img src="https://images.velog.io/images/ru_bryunak/post/24b708a4-9e6f-45d5-8d56-a40df3e2443a/javascript-engine.png" alt=""></p>
<h1 id="콜스택">콜스택</h1>
<p>자바스크립트는 싱글 쓰레드 언어이기 때문에, 한번에 한가지 작업만 실행하게 된다.
콜 스택이란 프로그램에서 우리가 어디 있는지를 기록하는 자료 구조이다. 함수를 실행하면, 그 함수가 콜 스택의 가장 상단에 위치하게 된다. 그리고 함수의 실행이 종료 되면 그 함수는 스택에서 제거(Pop) 된다.
그리고 호출 스택의 각 항목을 스택 프레임이라고 한다.
<img src="https://images.velog.io/images/ru_bryunak/post/589fba80-e85d-4553-b0d6-93fee0733ece/call-stack.png" alt=""></p>
<h1 id="스택-오버플로우">스택 오버플로우</h1>
<pre><code>function foo() {
  foo();
}
foo();</code></pre><p>위와 같은 함수를 호출하게 되면 콜스택 위에 계속 함수가 쌓일 것이다. 그리고 호출 스택의 실제 크기를 초과하게 되면 브라우저는 <code>Maximum call stack size exceeded</code> 라는 에러를 발생시키고 함수를 종료시킨다.</p>
<h1 id="이벤트-루프">이벤트 루프</h1>
<p>호출 스택에 처리 시간이 굉장히 오래 걸리는 함수가 있으면 어떻게 될까? 자바스크립트는 싱글 쓰레드 이기 때문에 함수가 종료될 때까지 다른 작업들이 모두 대기 상태가 될 것이다.
그리고 결국 브라우저는 웹 페이지를 종료할지 여부를 무든 오류메시지를 표시할 것이다.</p>
<p>이런 상황을 제어하기 위해 사용하는 것이 비동기 콜백이다.
즉, 코드 일부를 실행하고 나중에 실행될 콜백 함수를 제공하는 것이다. 비동기 콜백은 즉시 콜스택에 올라가는 것이 아닌, 특수한 시점에 실행이 되므로 스택안에 바로 올라가지 않는다.</p>
<p>그리고 이 콜백 함수들의 스케쥴을 관리하는 것이 이벤트 큐이다.</p>
<h1 id="콜백-큐">콜백 큐</h1>
<p>자바스크립트 런타임은 콜백 큐를 가지고 있다. 이는 처리할 메시지 목록과 실행할 콜백 함수들의 리스트이다.
<img src="https://images.velog.io/images/ru_bryunak/post/f4a12bf1-3fcf-4ac7-8ff6-e07c1b31ae0e/event-queue.png" alt=""></p>
<p><code>setTimeout</code> 를 포함하고 있는 함수가 실행된다고 하면, 함수는 콜스택에 올라갈 것이고, <code>setTimeout</code> 은 콜백 큐에 밀어 넣어진다. 
이벤트 루프는 콜스택과 콜백 큐를 감시하면서, 콜스택이 비워지게 되면 콜백 큐에 대기 중인 항목이 있는지 확인하고, 있으면 스택에 올린다.</p>
<h1 id="자바스크립트-삽입-위치">자바스크립트 삽입 위치</h1>
<p>브라우저는 렌더링 도중 자바스크립트를 만나게 되면 이에 대한 해석과 실행이 완료될 때까지 렌더링을 멈춘다. 그렇기 때문에, 렌더링이 정상적으로 끝난 후 실행하기 위해 바디 하단에 스크립트를 두는게 안정적이라는 말을 한다.
<img src="https://images.velog.io/images/ru_bryunak/post/f39f7606-a0b3-4488-b391-c73caf8bd249/script-async-defer-1.png" alt=""></p>
<ol>
<li>head에 삽입되는 경우</li>
</ol>
<ul>
<li>무거운 스크립트가 실행되는 경우 렌더링에 방해가 될 수 있다.</li>
<li>제이쿼리와 같은 설정을 위한 스크립트들이 사용된다.</li>
<li>DOM이 필요할 경우 document.onload 를 사용하여, load 이벤트 발생 시, 실행되도록 할 수 있다.</li>
</ul>
<ol start="2">
<li>바디 하단에 삽입되는 경우</li>
</ol>
<ul>
<li>브라우저의 렌더링이 완료 된 후 실행되기 때문에, DOM을 조작할 수 있다.</li>
</ul>
<p><strong>하지만! 헤드 태그에서 발생할 수 있는 문제를 해결하기 위해 추가된 속성이 있다.</strong>
바로, <code>async</code>와 <code>defer</code>이다.</p>
<pre><code>&lt;script async src=&quot;myAsyncScript.js&quot; onload=&quot;myInit()&quot;&gt;&lt;/script&gt;
&lt;script defer src=&quot;myDeferScript.js&quot; onload=&quot;myInit()&quot;&gt;&lt;/script&gt;</code></pre><p>위와 같이 <code>async</code> 또는 <code>defer</code> 속성이 추가된 스크립트는 렌더링을 방해하지 않는다.
<code>async</code> 속성은 외부 스크립트에만 사용할 수 있으며, 스크립트를 내려받는 즉시 실행된다.
<img src="https://images.velog.io/images/ru_bryunak/post/f84e6c50-bd67-4f2e-b103-075fdae378a0/script-async-defer-2.png" alt="">
<code>defer</code> 속성은 비동기적으로 스크립트를 다운로드 하며, 렌더링이 완료된 후 스크립트를 실행한다.
<img src="https://images.velog.io/images/ru_bryunak/post/20897823-25e1-4a57-8828-d35f9aa43a80/script-async-defer-3.png" alt=""></p>
<h1 id="참조">참조</h1>
<p><a href="https://blog.asamaru.net/2017/05/04/script-async-defer/">https://blog.asamaru.net/2017/05/04/script-async-defer/</a>
<a href="https://junhobaik.github.io/js-script-position/">https://junhobaik.github.io/js-script-position/</a>
<a href="https://new93helloworld.tistory.com/358">https://new93helloworld.tistory.com/358</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML 기초 - 3 (렌더링이란?)]]></title>
            <link>https://velog.io/@ru_bryunak/%EB%A0%8C%EB%8D%94%EB%A7%81%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@ru_bryunak/%EB%A0%8C%EB%8D%94%EB%A7%81%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Tue, 26 May 2020 08:57:22 GMT</pubDate>
            <description><![CDATA[<h1 id="렌더링">렌더링</h1>
<p>렌더링이란 서버로부터 HTML 파일을 받아 브라우저에 뿌려주는 과정이다.</p>
<p>브라우저는 서버로부터 HTML 문서를 다운 받는다.
렌더링 엔진은 HTML 문서를 파싱해서 DOM 트리를 만든다.</p>
<p>그 다음, 외부 css 파일과 함께 포함된 스타일 요소를 파싱하여 CSSOM 트리를 만든다.
스타일은 브라우저의 자체 스타일, 사용자 정의 스타일, html 태그에 걸려있는 스타일 순서로 처리되며, 나중에 처리되는 스타일을 따르게 된다.</p>
<blockquote>
<p>따라서, 인라인으로 들어가있는 스타일 속성이 우선순위를 가지는 것이다.</p>
</blockquote>
<p>다음으로, DOM 트리와 CSSOM을 결합하여 렌더링 트리를 형성한다.</p>
<p>이때, 화면에 드러나지 않는 <code>head</code> 태그나 <code>display</code> 속성에 <code>none</code> 값이 할당된 요소는 렌더 트리에 포함되지 않는다.</p>
<p>다음은 기기의 뷰포트 내에서 노드들의 정확한 위치와 크기를 계산하는 과정이 진행된다. 이 단계가 <code>레이아웃</code> 단계이며, <code>리플로우</code>라고도 한다.</p>
<p>페이지에서 각 객체의 정확한 크기와 위치를 파악하기 위해 브라우저는 렌더링 트리의 루트부터 시작하여 렌더링 트리를 순회한다.</p>
<p>이 작업이 끝나면, 이제 렌더링 엔진은 각 요소가 어디에 어떤 크기로 표현될지 알게 된다. 렌더링 엔진은 페인트 이벤트를 발생시켜 렌더링 트리를 화면에 그리고, 이 과정을 <code>페인팅</code> 또는 <code>래스터화</code> 라고 한다.</p>
<blockquote>
<p>모든 HTML 을 파싱할 때까지 기다리지 않고 배치와 그리기 과정을 진행한다. 
웹페이지에 접속했을 때, 페이지가 한꺼번에 뜨지 않고 점점 화면에 그려지는것이 이 때문이다.</p>
</blockquote>
<h1 id="렌더링-정리">렌더링 정리</h1>
<p>이 과정을 정리해보자.</p>
<ul>
<li>HTML을 파싱하여 DOM 트리를 만든다.</li>
<li>CSS를 파싱하여 CSSOM 트리를 만든다.</li>
<li>DOM 과 CSSOM 을 결합하여 렌더링 트리를 만든다.</li>
<li>렌더링 트리에서 각 노드의 크기와 위치를 계산한다.</li>
<li>개별 노드를 화면에 그린다.</li>
</ul>
<p>렌더링이 완료된 상태에서 사용자의 인터랙션에 의해 화면의 일부 영역이 변경된다면, <code>리플로우</code> 또는 <code>리페인트</code>가 발생한다.</p>
<p>정확히 말하자면, 
레이아웃이 변경될 경우 <code>리플로우</code> <code>리페인트</code> 를 거치고
레이아웃에는 영향을 주지 않지만 가시성에는 영향을 주는 엘리먼트가 변경될 때는 <code>리페인트</code> 만 거치게 된다. (ex : <code>opacity</code>, <code>background-color</code> 등)</p>
<p>여기서 레이아웃의 변경이란 아래와 같은 경우를 말한다.</p>
<ul>
<li>DOM 엘리먼트 추가, 제거 또는 변경</li>
<li>CSS 스타일 추가, 제거 또는 변경</li>
<li>CSS 스타일을 직접 변경하거나, 클래스를 추가함으로써 레이아웃이 변경될 수 있다. 엘리먼트의 길이를 변경하면, DOM 트리에 있는 다른 노드에 영향을 줄 수 있다.</li>
<li>CSS3 애니메이션과 트랜지션. 애니메이션의 모든 프레임에서 리플로우가 발생한다.</li>
<li>offsetWidth 와 offsetHeight 의 사용. offsetWidth 와 offsetHeight 속성을 읽으면, 초기 리플로우가 트리거되어 수치가 계산된다.</li>
<li>유저 행동. 유저 인터랙션으로 발생하는 hover 효과, 필트에 텍스트 입력, 창 크기 조정, 글꼴 크기 변경, 스타일시트 또는 글꼴 전환등을 활성화하여 리플로우를 트리거할 수 있다.</li>
</ul>
<p>따라서, 최적의 렌더링 성능을 얻기 위해서는 위 단계들을 최적화 해야 한다.</p>
<h1 id="최적화-방법">최적화 방법</h1>
<h4 id="리플로우-최소화-방법">리플로우 최소화 방법</h4>
<p><strong>1. 인라인 스타일 사용 X</strong>
인라인 스타일은 가독성을 해칠뿐 아니라, 수차례 리플로우를 발생 시킨다. 그에 반해 외부 스타일을 사용할 경우 한번만 리플로우를 발생시킨다.</p>
<p><strong>2. 작업 그루핑</strong>
DOM 요소의 정보를 요청하고 변경하는 코드는 같은 형태의 작업끼리 묶어 실행시키는 것이 좋다.</p>
<pre><code>function change() {     
  let width = document.getElementById(&quot;layer1&quot;).style.width;
  document.getElementById(&quot;layer2&quot;).style.width = width; 
  let height = document.getElementById(&quot;layer3&quot;).style.height; 
  document.getElementById(&quot;layer4&quot;).style.height = height; 
}

function change() {     
  let width = document.getElementById(&quot;layer1&quot;).style.width;
  let height = document.getElementById(&quot;layer3&quot;).style.height; 
  document.getElementById(&quot;layer2&quot;).style.width = width; 
  document.getElementById(&quot;layer4&quot;).style.height = height; 
}</code></pre><p><strong>3. 노출 제어</strong>
위에서 언급했다싶이 <code>display</code> 속성의 값을 <code>none</code>으로 하면, 렌더링 트리에서 노드가 빠지게 된다. 따라서 노드를 노출시킨채 스타일을 변경하는 것보다, 노드를 감추고 스타일을 변경한 후 노드를 노출 시키는 것이 리플로우와 리페인트 발생횟수를 줄이는 방법이다.</p>
<pre><code>let element = document.getElementById(&quot;box1&quot;); 

for(let i=50; i&lt;100; i++) {     
  element.style.width = i + &quot;px&quot;; 
} 
for(let i=1; i&lt;=50; i++) {  
  element.style.borderWidth = i + &quot;px&quot;; 
}

// 아래와 같이 렌더링 트리에서 빼준 다음, 스타일을 적용하고 다시 노출.
// 이 경우 두번의 리플로우 리페인트만 발생

let element = document.getElementById(&quot;box1&quot;);
element.style.display = &quot;none&quot;; 

for(let i=50; i&lt;100; i++) {    
  element.style.width = i + &quot;px&quot;; 
} 
for(let i=1; i&lt;=50; i++) {  
  element.style.borderWidth = i + &quot;px&quot;; 
} 

element.style.display = &quot;block&quot;;</code></pre><p><strong>4. 노드 복제</strong>
변경하려는 요소의 노드를 복제한 후 복제된 노드에 작업을 하고, 교체를 해주게 되면 리플로우와 리페인트는 한번만 발생한다.</p>
<p><strong>5. 캐싱</strong>
여기서 말하는 캐싱은 별도의 변수에 자주 사용하는 값을 저장하는 것이다. scrollWidth와 같은 값을 호출할 경우 리플로우를 유발하기 때문에, 반복 구문을 실행하기 전에 이를 변수에 담아 놓으면 리플로우 발생을 최소화 할 수 있다.</p>
<p>이 외에도 여러가지 최적화 방법이 있으니, 검색을 통해서 알아보기로 하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 기초 - 3 (position, opacity, z-index, overflow...)]]></title>
            <link>https://velog.io/@ru_bryunak/CSS-%EA%B8%B0%EC%B4%88-3-position-opacity-z-index-overflow</link>
            <guid>https://velog.io/@ru_bryunak/CSS-%EA%B8%B0%EC%B4%88-3-position-opacity-z-index-overflow</guid>
            <pubDate>Mon, 25 May 2020 04:30:41 GMT</pubDate>
            <description><![CDATA[<h1 id="opacity">opacity</h1>
<p>요소의 투명도를 조절하는 스타일 속성이며, 0.0부터 1.0 사이의 값으로 설정할 수 있다.</p>
<div style="padding: 5px; border: 1px solid #ddd;">
  <div style="opacity: 1; background-color: #05ffb0; padding: 5px;">Hello! opacity: 1;</div>
  <div style="opacity: .5; background-color: #05ffb0; padding: 5px;">Hello! opacity: .5;</div>
</div>

<h1 id="position">position</h1>
<p>요소의 위치를 설정할 수 있다.</p>
<p><code>static</code> : 기본 값이며, 마크업 순서대로 배치된다. top, left, right, bottom, z-index 값에 반응하지 않는다. 
<code>relative</code> : 현재 위치를 기준으로 이동할 수 있다.
<code>absolute</code> : <code>static</code> 을 제외한 position 속성을 가진, 가장 가까운 부모를 기준으로 이동할 수 있다.
<code>fixed</code> : 마크업 순서와 무관하게 배치된다.
<code>sticky</code> : 스크롤 위치에 따라 <code>relative</code> 또는 <code>fixed</code> 로 전환한다. 즉, 평소엔 <code>relative</code>, 스크롤이 해당 요소의 위치에 오면 <code>fixed</code>로 전환.</p>
<h1 id="z-index">z-index</h1>
<p>요소의 깊이를 변경할 수 있다.<code>static</code>을 제외한 position 속성이 설정된 상태에서 사용할 수 있다. 음수가 설정될 경우 기본 요소보다도 아래 배치된다.</p>
<h1 id="overflow">overflow</h1>
<p>내부의 요소가 부모의 범위를 벗어날 때의 어떻게 처리할 지 지정할 수 있다.</p>
<p><code>vidible</code> : 기본값으로, 넘치는 부분을 그대로 보여준다.
<code>hidden</code> : 넘치는 부분을 감춘다.
<code>scroll</code> : 스크롤을 생성한다.
<code>auto</code> : 상황에 맞춰 스크롤을 생성한다.</p>
<h1 id="transform">transform</h1>
<p>요소의 위치를 옮기거나 크기를 조절하고, 회전, 왜곡 시킬 수 있다.</p>
<p><code>translate(x, y)</code> : 지정한 값 만큼 수평, 수직 이동시킨다.
<code>translate(x, y, z)</code> : 지정한 값 만큼 수평, 수직, 앞뒤로 이동시킨다.
<code>translateX(x)</code> : 지정한 값 만큼 수평 이동시킨다.
<code>translateY(y)</code> : 지정한 값 만큼 수직 이동시킨다.
<code>translateZ(z)</code> : 지정한 값 만큼 앞뒤로 이동시킨다.</p>
<p>이하는 한개씩만..</p>
<p><code>scale(x, y)</code> : 지정한 값 확대/축소한다. 음수일 경우 해당 축을 기준으로 요소를 뒤짚는다.(invert)
<code>rotate(angle)</code> : 지정한 각 만큼 수평, 수직방향으로 회전한다.</p>
<h1 id="transition">transition</h1>
<p>한 스타일에서 다른 스타일로 변경한다.
시간을 조절함으로써 애니메이션 효과를 낼 수 있다.</p>
<p><code>transition: property, duration, timing-function delay</code></p>
<p><code>property</code> : 트랜지션이 적용될 css 속성 (none, all, 속성이름)
<code>duration</code> : 트랜지션 실행시간
<code>timing-function</code> : 트랜지션효과의 곡선형태</p>
<ul>
<li>liner : 같은 속도로 진행</li>
<li>ease : 느리게 시작, 점점 빨라짐, 느리게 종료 (기본값)</li>
<li>ease-in : 느리게 시작</li>
<li>ease-out : 느리게 종료</li>
<li>ease-in-out : 느리게 시작, 느리게 종료</li>
<li>cubic-bezier(n,n,n,n) : 직접 함수를 정의해서 사용</li>
</ul>
<p><code>delay</code> : 지연시간</p>
<blockquote>
<p>자세한 예는 <a href="https://cssreference.io/property/transform/">cssreferecne.io</a> 에서...</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 기초 - 2 (display, visibility)]]></title>
            <link>https://velog.io/@ru_bryunak/CSS-%EA%B8%B0%EC%B4%88-2-Display</link>
            <guid>https://velog.io/@ru_bryunak/CSS-%EA%B8%B0%EC%B4%88-2-Display</guid>
            <pubDate>Mon, 25 May 2020 02:38:00 GMT</pubDate>
            <description><![CDATA[<h1 id="display">display</h1>
<blockquote>
<p>display는 요소의 종류를 선택하는 속성이다.
block 형식은 한 줄 모두를 차지하며, 넓이와 높이 설정이 가능하다.
inline 형식은 내용만큼 차지하며, 넓이와 높이 속성을 지정할 수 없다. 또한 margin 속성은 좌우로만 지정할 수 있다.
inline-~ 형식은 inline 요소처럼 표현되지만 block 처럼 사용할 수 있다.</p>
</blockquote>
<p>요즘 가장 자주 사용하는 속성은 flex가 아닐까 싶다.
flex 속성은 요소의 크기가 불분명하거나 동적인 경우에도, 각 요소를 정렬할 수 있는 방법을 제공한다.
flex는 Container, Itmes 두 개념으로 나뉜다. Container는 Items를 감싸는 부모 요소이며, 각 Items를 정렬하기 위해선 Container가 필수이다.</p>
<p>Container에는 display, justify-content, align-items 등의 속성이 사용되며,
Items에는 order, flex, align-self 등의 속성이 사용된다.</p>
<blockquote>
<p><a href="https://heropy.blog/2018/11/24/css-flexible-box/">참고 - CSS3 Flex 완벽가이드</a></p>
</blockquote>
<p><strong>자주 사용하는 속성들에 대해 알아보자.</strong></p>
<p><code>flex-direction</code> : Items의 주 축(main axis)을 설정합니다. <code>row</code>가 기본 값이다. <code>column</code>으로 설정할경우 주축이 세로 방향으로 바뀌며, 항목들은 열로 나열된다. <code>*-reverse</code> 로 지정하게 되면 시작 방향이 바뀌게 된다.
<code>flex-wrap</code> : 기본 값은 <code>nowrap</code>이며, 항목들의 넓이의 합이 flex 박스의 넓이를 넘어섰을 때, 줄바꿈을 할지 결정한다.
<code>flex-flow</code> : <code>flex-direction</code> 속성과 <code>flex-wrap</code> 속성을 합친 속성으로, <code>flex-direction</code>, <code>flex-wrap</code> 값 순서로 사용한다.
<code>flex-basis</code> : 항목들의 넓이를 설정한다. flex 컨테이너의 <code>flex-basis</code> 기본 값은 &#39;auto&#39;이다.
<code>flex-grow</code> : flex-grow 값을 양수로 지정하면 항목별로 주축 방향 크기가 flex-basis 값 이상으로 늘어난다.
<code>flex-shrink</code> : flex-shrink 속성은 주축의 공간이 부족할 때 각 항목의 사이즈 줄이는 방법을 정의한다.</p>
<p>위 세 속성은 축약형으로 사용할 수 있으며 flex : <code>flex-grow</code> <code>flex-shrink</code> <code>flex-basis</code> 순서로 사용할 수 있다.</p>
<p>flex 축약형 표현에 사용할 수 있는 미리 정의된 축약 값들도 있다.</p>
<ul>
<li>flex: initial</li>
<li>flex: auto</li>
<li>flex: none</li>
<li>flex: <code>&lt;positive-number&gt;</code></li>
</ul>
<p>flex 항목을 flex: initial로 지정하면 flex: 0 1 auto 로 지정한 것과 동일하게 동작한다. 이 경우, flex 항목들은  flex-grow가 0이므로  flex-basis값보다 커지지 않고  flex-shrink가 1이므로 flex 컨테이너 공간이 모자라면 크기가 줄어든다. 또, flex-basis가 auto이므로 flex 항목은 주축 방향으로 지정된 크기 또는 자기 내부 요소 크기 만큼 공간을 차지한다.</p>
<p>flex: auto 로 지정하면 flex: 1 1 auto로 지정한 것과 동일하며, flex:initial 과는 주축 방향 여유 공간이 있을 때 flex 항목들이 늘어나서 주축 방향 여유 공간을 채우는 점만 다르다.</p>
<p>flex: none으로 지정하면 flex: 0 0 auto으로 지정한 것과 동일하며 flex 컨테이너의 크기 변화에도 flex 항목 크기는 변하지 않고 flex-basis를 auto로 지정했을 때 정해지는 크기로 결정된다.  </p>
<p>이 축약형은 더 축약해서 flex: 1 이나 flex: 2처럼 쓸수도 있는데, 이는 flex-grow 만 지정하고 나머지는 1 0으로 사용한다는 뜻이다. 따라서 flex: 2는 flex: 2 1 0와 동일하게 처리된다.</p>
<p><code>align-items</code> : 교차축(direction이 row라면 y축)에 따른 항목들의 정렬 방법을 정한다.</p>
<ul>
<li>flex-start : 교차축의 시작 부분에 나열</li>
<li>flex-end : 교차축의 끝 부분에 나열</li>
<li>center : 중앙에 나열
기타로 baseline, stretch 가 있다.</li>
</ul>
<p><code>align-self</code> : <code>align-items</code>가 항목들의 정렬 방법을 정한다면, <code>align-self</code>는 항목의 정렬 방법을 정한다.
<code>justify-content</code> : 주축의 방향에 따른 flex 항목들의 정렬 방법을 정한다.</p>
<ul>
<li>flex-start : 주 축의 시작 부분에 나열</li>
<li>flex-end : 주 축의 끝 부분에 나열</li>
<li>center : 중앙에 정렬</li>
<li>space-between : 주축 방향 여유공간을 각 항목들 사이의 공간에 균등 배분</li>
<li>space-around: 시작 부분 및 끝 부분과 flex 항목간 공간도 균등 배분에 고려하므로 시작 부분 및 끝 부분과 flex 항목 간의 공간의 크기를 1로 배분한다면 flex 항목 사이의 공간은 2로 배분</li>
<li>space-evenly : 시작 부분과 끝 부분도 동일한 비율로 공간 배분</li>
</ul>
<blockquote>
<p>참고  - <a href="https://cssreference.io/property/justify-content/">cssreference.io</a>, <a href="https://developer.mozilla.org/ko/docs/Web/CSS/CSS_Flexible_Box_Layout/Flexbox%EC%9D%98_%EA%B8%B0%EB%B3%B8_%EA%B0%9C%EB%85%90">flexbox의 기본개념</a></p>
</blockquote>
<h1 id="visibility">visibility</h1>
<p>대상을 보이거나 보이지 않게 지정하는 속성으로 <code>visible</code>, <code>hidden</code>, <code>collapse</code> 등을 값으로 사용할 수 있다. 
display: none 과 visibility: hidden 을 비교하는 질문이 나올 수 있다.
display: none은 공간을 차지하지 않고, visibility: hidden은 공간을 차지한다.
<strong>즉, display는 레이아웃의 변화를 야기하기 때문에 reflow, repaint 가 발생하지만, visibility 는 레이아웃에 영향을 주지 않으므로 repaint만 발생한다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 기초 - 1]]></title>
            <link>https://velog.io/@ru_bryunak/CSS-%EA%B8%B0%EC%B4%88-1</link>
            <guid>https://velog.io/@ru_bryunak/CSS-%EA%B8%B0%EC%B4%88-1</guid>
            <pubDate>Fri, 22 May 2020 08:15:25 GMT</pubDate>
            <description><![CDATA[<p>css편은 자주 사용하는 속성들에 대해서만 기술</p>
<h1 id="css란">CSS란?</h1>
<p>css란 Cascading Style Sheets의 약자로, HTML문서를 꾸밀 때 사용하는 스타일 시트 언어이다.</p>
<h2 id="텍스트-관련-스타일">텍스트 관련 스타일</h2>
<pre><code>&lt;div style=&quot;
    letter-spacing: 1.4; // 문자와 문자사이의 간격
        line-height: 1.2; // 줄 간격
        white-space: nowrap; // 텍스트와 함께 입력된 공백 처리 방법
        text-align: center; // left | center | right
        font-size: 14px; // 폰트 사이즈!
        font-weight: 400; // bold | lighter | bolder | 100~900
    &quot;&gt;
    &lt;/div&gt;</code></pre><blockquote>
<p><strong>white-space</strong>
<code>normal</code> : 기본값으로 연속하는 공백을 하나로 처리, 자동으로 줄을 바꿈
<code>nowrap</code> : 연속하는 공백을 하나로 처리하고, 줄바꿈은 <code>&lt;br&gt;</code>에서만 일어남
<code>pre</code> : 연속하는 공백을 유지하며, 줄바꿈은 개행문자와 <code>&lt;br&gt;</code>에서만 일어남
<code>pre-line</code> : 연속하는 공백은 하나로 취급, 줄바꿈은 개행문자와 <code>&lt;br&gt;</code>에서만 일어나며, 한줄이 너무 길어서 넘칠 경우 자동으로 줄 바꿈
<code>pre-wrap</code> : 연속하는 공백을 유지하며, 줄바꿈은 개행문자와 <code>&lt;br&gt;</code>에서만 일어나며, 한줄이 너무 길어서 넘칠 경우 자동으로 줄 바꿈
<code>break-spaces</code> : <code>pre-wrap</code>과 비슷하며, 연속 공백이 줄 끝에 위치하더라도 공백을 차지함.</p>
</blockquote>
<p><strong>주로 overflow와 text-overflow와 함께 사용된다.</strong></p>
<pre><code>&lt;div style=&quot;
  white-space: nowrap; 
  overflow: hidden; 
  text-overflow: ellipsis&quot;
&gt;
&lt;/div&gt;</code></pre><h1 id="margin--padding">margin &amp; padding</h1>
<p>margin을 사용해 요소의 바깥쪽 여백을 설정할 수 있다.
padding을 사용해 요소의 안쪽 여백을 설정할 수 있다. 안쪽 여백이란 콘텐츠와 테두리 사이의 공간이다.
<img src="https://ofcourse.kr/images/attach/margin_padding.png" alt="margin padding">
출처 - <a href="https://ofcourse.kr/css-course/margin-padding-%EC%86%8D%EC%84%B1">https://ofcourse.kr/css-course/margin-padding-%EC%86%8D%EC%84%B1</a></p>
<p>margin이 바깥쪽 여백이다보니 서로 겹치는 경우가 발생할 수 있는데, 이를 마진 상쇄(Margin collapsing)라고 한다.</p>
<blockquote>
<p>In CSS, the adjoining margins of two or more boxes (which might or might not be siblings) can combine to form a single margin. Margins that combine this way are said to collapse, and the resulting combined margin is called a collapsed margin.
CSS에서 두 개 이상의 상자(형제일수도 있고 아닐수도 있음)의 인접한 여백은 결합하여 하나의 여백을 형성하고 이를 마진 붕괴라 한다.
-W3C</p>
</blockquote>
<p>자세한 설명은 <a href="https://velog.io/@raram2/CSS-%EB%A7%88%EC%A7%84-%EC%83%81%EC%87%84Margin-collapsing-%EC%9B%90%EB%A6%AC-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4">CSS 마진 상쇄(Margin-collapsing) 원리 완벽 이해</a> 을 참고하자.</p>
<h1 id="border">border</h1>
<p>border를 사용해 테두리를 설정할 수 있다.</p>
<pre><code>&lt;div style=&quot;border: 1px solid #ddd;&quot;&gt;&lt;/div&gt;</code></pre><p>테두리 굵기(<code>border-width</code>), 스타일(<code>border-style</code>), 색(<code>border-color</code>) 중 선택하여 사용할 수 있으며, 순서는 영향을 주지 않는다.
테두리 경계의 모서리를 둥글게 만들고자 할 때는 <code>border-radius</code>를 사용한다.</p>
<pre><code>&lt;div style=&quot;
  width: 100px; 
  height: 50px; 
  border: 1px solid #f5a623;
  border-radius: 20px; 
  background-color: #f5a623;
&quot;&gt;&lt;/div&gt;</code></pre><div style="width: 600px; height: 100px; border: 1px solid #f5a623; border-radius: 50px 10px 50px 10px; background-color: #f5a623;"></div>

<h1 id="box-sizing">box-sizing</h1>
<p>box-sizing 속성은 요소의 넓이와 높이를 계산하는 방법을 지정한다.</p>
<p><code>content-box</code> : 기본 css 박스 크기 결정법을 사용하며, 요소의 너비를 100px로 설정하면 콘텐츠 영역이 100px의 너비를 가지며, border-width와 padding이 더해진다.</p>
<p><code>box-sizing</code> : border와 padding을 요소의 크기로 고려한다. 너비를 100 픽셀로 설정하고 테두리와 안쪽 여백을 추가하면, 콘텐츠 영역이 줄어들어 총 너비 100 픽셀을 유지합니다. 대부분의 경우 이 편이 크기를 조절할 때 쉽다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML 기초 - 2 (시맨틱 태그, 메타태그)]]></title>
            <link>https://velog.io/@ru_bryunak/HTML-%EA%B8%B0%EC%B4%88-2-%EC%8B%9C%EB%A7%A8%ED%8B%B1-%ED%83%9C%EA%B7%B8%EB%9E%80</link>
            <guid>https://velog.io/@ru_bryunak/HTML-%EA%B8%B0%EC%B4%88-2-%EC%8B%9C%EB%A7%A8%ED%8B%B1-%ED%83%9C%EA%B7%B8%EB%9E%80</guid>
            <pubDate>Fri, 22 May 2020 04:26:33 GMT</pubDate>
            <description><![CDATA[<h1 id="시맨틱-태그란">시맨틱 태그란?</h1>
<blockquote>
<p>시맨틱(Semantic)은 &quot;의미의, 의미론적인&quot; 이라는 뜻, 즉 시맨틱 태그란 의미를 가지는 태그이다.</p>
</blockquote>
<p>시맨틱 태그에 의해 컴퓨터가 HTML 요소의 의미를 보다 명확히 해석하고 그 데이터를 활용할 수 있는 시맨틱 웹이 실현될 수 있다. 
시맨틱 웹이란 웹이 존재하는 수많은 웹페이지들에 메타데이터를 부여하여 기존의 잡다한 데이터 집합이었던 웹페이지를 <code>의미</code>와 <code>관련성</code>을 가지는 거대한 데이터 베이스로 구축하고자 하는 발상이다.</p>
<blockquote>
<p><a href="https://poiemaweb.com/html5-semantic-web">https://poiemaweb.com/html5-semantic-web</a></p>
</blockquote>
<p>태그가 의미를 가짐으로써 검색엔진의 크롤링에게 사이트에 대한 정보를 비교적 정확히 제공할 수 있게 된다.</p>
<h2 id="시멘틱-태그의-종류">시멘틱 태그의 종류</h2>
<p><code>form</code>, <code>table</code>, <code>img</code>, 등이 있으며, HTML5에서 추가된 시맨틱 태그는 아래와 같다.
<code>header</code> : 헤더를 의미
<code>nav</code> : 네비게이션을 의미
<code>aside</code> : 사이드에 위치하는 공간을 의미
<code>section</code> : 본문의 여러 내용 (<code>article</code>)을 포함하는 공간을 의미
<code>article</code> : 본문의 주 내용이 들어가는 공간을 의미
<code>footer</code> : 푸터를 의미</p>
<img src="https://poiemaweb.com/img/building-structure.png" alt="sematic elements">

<h1 id="메타태그란">메타태그란?</h1>
<blockquote>
<p>메타태그는 웹 페이지의 보이지 않는 정보를 제공하는데 쓰인다. 페이지에 대한 설명, 제작자, 크롤링 정책 등의 정보를 제공할 수 있다.</p>
</blockquote>
<h2 id="seo">SEO</h2>
<p>SEO는 Search Engine Optimization의 약자로 검색 엔진 최적화를 의미한다.
메타태그를 이용해 <code>description</code>, <code>keywords</code>, <code>author</code>, <code>subject</code>, <code>classification</code> 등의 정보를 표기할 수 있으며, 검색엔진은 이런 정보를 적극적으로 활용한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
    &lt;meta name=&quot;keywords&quot; content=&quot;apple banna&quot;&gt;
    &lt;meta name=&quot;desciption&quot; content=&quot;나의 블로그&quot;&gt;
    &lt;meta name=&quot;robot&quot; content=&quot;noindex nofollow&quot;&gt;
    &lt;meta property=&quot;og:type&quot; content=&quot;website&quot;&gt;
    &lt;meta property=&quot;og:title&quot; content=&quot;페이지 제목&quot;&gt;
    &lt;meta property=&quot;og:description&quot; content=&quot;페이지 설명&quot;&gt;
    &lt;meta property=&quot;og:image&quot; content=&quot;http://www.mysite.com/myimage.jpg&quot;&gt;
    &lt;meta property=&quot;og:url&quot; content=&quot;http://www.mysite.com&quot;&gt;</code></pre><blockquote>
</blockquote>
<p><a href="https://junhobaik.github.io/meta-tag/">https://junhobaik.github.io/meta-tag/</a></p>
<p><code>&lt;meta charset=&quot;utf-8&quot;&gt;</code>
HTML5의 인코딩 지정 방법으로 보통 UTF-8(전세계 거의 모든 문자를 표현할 수 있는 문자 집합)을 값으로 지정하는 편이다. </p>
<p><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;</code>
뷰포트는 화면에 보이는 영역을 뜻한다. 즉, 내가 보고 있는 모니터 화면 영역을 말하며, 스크롤로 봐야할 영역은 제외한다.
위 코드는 문서의 넓이는 기기의 넓이이며, 초기 화면 배율은 1로 한다는 의미이다. </p>
<p><code>&lt;meta name=&quot;keywords&quot; content=&quot;apple banna&quot;&gt;</code>
해당 컨텐츠의 대표적인 키워드를 지정한다.
검색 엔진의 검색 결과에 반영될 수 있다. (구글은 반영 X) </p>
<p><code>&lt;meta name=&quot;desciption&quot; content=&quot;나의 블로그&quot;&gt;</code>
해당 페이지에 대한 설명을 설정한다. 검색사이트에서 검색했을 때 제목 아래에 노출되는 부분이다 </p>
<p><code>&lt;meta name=&quot;author&quot; content=&quot;bumhyun&quot;&gt;</code>
웹 사이트의 소유자를 적는다. </p>
<p><code>&lt;meta name=&quot;robot&quot; content=&quot;noindex nofollow&quot;&gt;</code>
인덱싱을 제어하는데 사용되며, 사용가능한 content는 index, noindex, follow, nofollow가 있다.
index와 noinde는 페이지의 인덱스 여부를 말해주며, follow, nofollow는 페이지내의 링크를 크롤러가 따라갈 수 있는지 여부를 말해준다.</p>
<blockquote>
<p>검색엔진이 검색 키워드에 대한 검색결과를 사용자한테 제공하기 위해서는 크롤러가 끊임없이 온라인 상의 문서를 수집해야하고, 크롤러가 수집한 문서를 인덱서가 잘 정리해서 인덱스 서버에 색인해 둬야 한다. 
크롤러는 사이트와 사이트 사이의 링크를 타고다니며 문서를 수집하는 역할만 하며, 인덱서는 수집된 문서를 검색엔진이 사용자에게 결과물을 빠르고 쉽게 재공할 수 있도록 색인 또는 인덱스 역할을 한다.
<a href="http://www.seo-korea.com/robots-txt-%ED%8C%8C%EC%9D%BC%EA%B3%BC-meta-robots-%ED%83%9C%EA%B7%B8%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90/">참고 - 크롤링과 인덱싱의 차이</a></p>
</blockquote>
<p><code>OpenGraph</code>
오픈그래프는 웹페이지가 소셜 미디어 또는 오픈 그래프를 활용한 사이트로 공유될 때 사용되는 정보다.</p>
<blockquote>
<p>침고 할만한 글 -  <a href="https://medium.com/@euncho/google-%EA%B2%80%EC%83%89%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EA%B0%80-fbc0e421a97a">Google 검색은 어떻게 동작하는가?</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML 기초 - 1 (DTD, 주요 태그)]]></title>
            <link>https://velog.io/@ru_bryunak/HTML-%EA%B8%B0%EC%B4%88-1</link>
            <guid>https://velog.io/@ru_bryunak/HTML-%EA%B8%B0%EC%B4%88-1</guid>
            <pubDate>Thu, 21 May 2020 15:06:46 GMT</pubDate>
            <description><![CDATA[<p>본 글은 MDN의 자료를 요약한 것입니다.</p>
<blockquote>
<p>😄 HTML(Hypertext Markup Language)이란?</p>
</blockquote>
<h4 id="html이란-프로그래밍-언어가-아니다-컨텐츠의-구조를-정의하는-마크업-언어이다">HTML이란 프로그래밍 언어가 아니다. 컨텐츠의 구조를 정의하는 마크업 언어이다.</h4>
<p>HTML은 어떠한 형식으로 보이게 하거나 특정한 방식으로 동작하도록 하는 Element들로 이루어져 있다.
각 Element는 속성을 가질 수 있는데, 속성은 실제 컨텐츠로 표시되길 원하지 않는 추가적인 정보를 담고 있다.
각 속성 사이에는 공백이 있어야 하며, 속성의 이름 뒤에는 등호(=)가 와야 한다. 또한 속성의 값을 확실히 표현해주기 위해서는 인용부호(&quot; 또는 &#39;)가 있어야 하는데 Doublequote(&quot;)를 사용하는 편이다.</p>
<blockquote>
<p>HTML 문서의 각 요소를 알아보자</p>
</blockquote>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;title&gt;My test page&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;img src=&quot;images/firefox-icon.png&quot; alt=&quot;My test image&quot;&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><h2 id="dtddocument-type-definition이란">DTD(Document Type Definition)이란?</h2>
<p>문서 타입 정의는 HTML5, XHTML, HTML 의 세가지 문서 유형이 존재하며, 기술한 유형에 따라 마크업 문서의 요소와 속성들을 처리하는 기준이 되며 유효성 검사에 이용된다.
우리가 사용하는 HTML5는 DTD 참조가 필요없어서, 위 코드와 같이 간단히 선언할 수 있다.
MDN 문서에는 아래와 같이 기술되어 있다.</p>
<blockquote>
<p>최근에는 아무도 그런 것들에 대해 신경쓰지 않으며 그저 모든 것이 올바르게 동작하게 하기 위해 포함되어야 할 역사적인 유물일 뿐입니다. 지금은, 그게 알아야 할 전부 입니다.</p>
</blockquote>
<p><strong>html</strong>
이 요소는 페이지 전체를 감싸며, 루트 요소라고도 한다.
lang 속성을 통해 문서에 사용된 언어를 지정할 수 있다.</p>
<p><strong>head</strong>
이 요소는 페이지를 조회하는 사람들에게 보여주지 않을 컨텐츠의 컨테이너 역할을 한다. 여기에는 검색결과에 표시되기 원하는 페이지 설명, 스타일, 스크립트 등이 포함된다.</p>
<p><strong>body</strong>
이 요소는 페이지 방문자들에게 보여주길 원하는 컨텐츠의 컨테이너 역할을 한다.</p>
<blockquote>
<p>block 형식의 태그와 inline 형식의 태그에 대해 알아보자</p>
</blockquote>
<p><strong>inline</strong> : 줄바꿈 없이 내용만큼만 영역을 차지 ex) span, a, input, img, b, i, strong, em 등 글자 관련 태그
<strong>block</strong> : 내용의 길이와 상관없이 가로 한 줄을 모두 차지 ex) div, h1~h6, p, li, table, form</p>
<p>inline 형식의 태그는 block 형식의 태그를 감쌀 수 없다.</p>
<h2 id="글자-문단-관련-태그">글자, 문단 관련 태그</h2>
<p><code>&lt;br&gt;</code> : 엔터와 동일하게 줄 바꾸는 태그
<code>&lt;p&gt;</code> : 문단을 띄울 때 사용, 연속으로 사용해도 한번만 적용
<code>&lt;p&gt;~&lt;/p&gt;</code> : 문단을 묶을 때 사용
<code>&lt;div&gt;~&lt;/div&gt;</code> : block 형식으로 공간을 분할
<code>&lt;span&gt;~&lt;/span&gt;</code> : inline 형식으로 공간을 분할
<code>&lt;Hn&gt;~&lt;/Hn&gt;</code> : n은 1<del>6까지, 숫자가 작을수록 글자가 크다. color 속성이 적용되지 않는다.
`<b></del></b> 또는 <strong><del></strong><code>: 굵은 글자</code><i></del></i> 또는 <em><del></em><code>: 이탤릭체</code><u></del></u> 또는 <ins><del></ins><code>: 밑줄글자</code><del></del></del><code>: 가운데 줄이 그어진 글자</code><sup><del></sup><code>: 윗첨자</code><sub></del></sub>` : 아래첨자</p>
<h2 id="이미지-삽입-태그">이미지 삽입 태그</h2>
<blockquote>
</blockquote>
<p>이미지를 삽입할 때 사용한다. src 속성에는 이미지의 위치, alt에는 이미지에 대한 설명을 넣을 수 있다. 이미지가 오류로 인하여 표시되지 못할 때나, 시각 장애가 있는 분들을 위해 이미지에 대한 설명을 적을 수 있는 alt 속성을 추가하는 편이 좋다.</p>
<pre><code>&lt;img src=&quot;이미지 위치&quot; alt=&quot;이미지에 대한 설명&quot;&gt;</code></pre><p>이미지에 캡션을 붙일 때</p>
<pre><code>&lt;figure&gt;
  &lt;img src=&quot;apple.png&quot; alt=&quot;사과&quot;&gt;
  &lt;figcaption&gt;이것은 사과입니다.&lt;/figcaption&gt;
&lt;/figure&gt;</code></pre><p>이미지에 여러개의 하이퍼링크를 연결시킬 때</p>
<pre><code>&lt;img src=&quot;apple.png&quot; alt=&quot;사과&quot; usemap=&quot;#map&quot;&gt;
&lt;map name=&quot;map&quot;&gt;
  &lt;area shape=&quot;rect | circle | poly&quot; coords=&quot;좌표&quot; href=&quot;&quot;&gt;
&lt;/map&gt;</code></pre><h2 id="하이퍼링크-태그">하이퍼링크 태그</h2>
<blockquote>
</blockquote>
<pre><code>&lt;a href=&quot;연결할 주소&quot; target=&quot;표현방법&quot;&gt;velog로 이동&lt;/a&gt;</code></pre><p>href 속성의 값으로는 <a href="http://velog.io%EC%99%80">http://velog.io와</a> 같은 사이트나 프로젝트 내부의 파일 또는
특정한 위치 (ex: <code>&lt;a name=&quot;이름&quot;&gt;&lt;/a&gt; &lt;a href=&quot;#이름&quot;&gt;특정한 위치로&lt;/a&gt;</code> 에 연결할 수 있다.
주의할 점은 a태그의 name 속성은 id의 역할을 하기 때문에 다른 태그의 id와 a태그의 name이 같은 경우, 기능이 동작하지 않는다.</p>
<h2 id="폼-태그">폼 태그</h2>
<blockquote>
</blockquote>
<pre><code>&lt;form
  action=&quot;폼을 전송할 서버쪽의 스크립트 파일 지정&quot;
  name=&quot;폼을 식별하기 위한 이름&quot;
  method=&quot;폼을 전송할 방식, GET 또는 POST&quot;&gt;
&lt;/form&gt;</code></pre><p>Form 안에서 사용되는 input의 주요 타입으로는 <code>hidden</code>, <code>email</code>, <code>password</code>, <code>file</code> 등이 있다.
사실 리액트나 뷰로 개발하다보면 잘 만들어진 UI Framework를 사용하기에..직접 form을 만들어 사용할 일은 잘 없다.
개인적으로는 blade template에 작업할 일이 좀 있어서 좀 사용하긴 했다.</p>
<h2 id="인풋-태그">인풋 태그</h2>
<blockquote>
</blockquote>
<p>주로 쓰는 속성은 <code>readonly</code>, <code>placeholder</code> 정도...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Mobx 사용 중 에러 해결기]]></title>
            <link>https://velog.io/@ru_bryunak/Mobx-%EC%82%AC%EC%9A%A9-%EC%A4%91-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0%EA%B8%B0</link>
            <guid>https://velog.io/@ru_bryunak/Mobx-%EC%82%AC%EC%9A%A9-%EC%A4%91-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0%EA%B8%B0</guid>
            <pubDate>Wed, 06 May 2020 11:26:06 GMT</pubDate>
            <description><![CDATA[<p>음...
오늘 오후 내내 고생했던 이야기를 남겨본다.</p>
<p>현재 진행중인 프로젝트는 React와 Typescript, Mobx 를 사용하여 진행 중이다.</p>
<p>api를 공통으로 사용하는 페이지가 많아서, 
공통 스토어를 두고 각 페이지에서 사용할 스토어들에서 상속을 받았다.</p>
<pre><code class="language-typescript">class SaleStore extends common {}
class PurchaseStore extends common {}</code></pre>
<p>common 클래스에 있는 변수값만 각기 다르게 가지면 됐기에, 생성자를 통해 값을 넣어줬다.</p>
<pre><code class="language-typescript">
export const storesContext = React.createContext({
  saleStore: new SaleStore(1),
  purchaseStore: new PurchaseStore(2),
  commonStore: new CommonStore(),
  authStore: new AuthStore(),
  sideMenuStore: new SideMenuStore(),
  dealStore: new DealStore(),
  clientsStore: new ClientsStore(),
  creditsStore: new CreditsStore(),
  productsStore: new ProductsStore(),
  systemsStore: new SystemsStore(),
  categoriesStore: new CategoriesStore(),
  brandsStore: new BrandsStore(),
  stocksStore: new StocksStore(),
  orderStore: new OrderStore(4),
  contractStore: new ContractStore(5)
});

export const useStores = () =&gt; React.useContext(storesContext);</code></pre>
<p>그리고 각 페이지를 테스트 하는데,
왠걸? 스토어 하나를 돌려쓰는 것이었다.</p>
<p>뭐가 문제지?</p>
<p><strong>첫번째</strong> - 꼼수 사용. 스토어를 프로바이더에 넣기 전에 인스턴스를 다시 만들어서 넣어줌 - 실패
<strong>두번째</strong> - 중앙스토어를 컨텍스트를 사용하는 방식으로 변경 - 당연히 실패</p>
<p>스택오버플로우에도 들어가보고, 레딧에 질문을 올리고, 깃허브에 이슈도 생성
레딧에서 착한분이 금방 댓글을 달아주셨는데..내가 올린 샘플코드가 문제 없이 잘 나온다고..</p>
<p>팀장님께 뭐가 문제인지 물어봤지만, 
각기 새로운 인스턴스를 생성했는데 오버라이딩이 되는게 말이 안된다고..</p>
<p>결국 퇴근하고 집에와서,</p>
<p><strong>세번째</strong> - 그냥 다 까보자. 새로운 프로젝트를 만들고, 코드를 옮겨서 하나씩 다 지워나갔다.</p>
<p>그리고 찾은 원인!</p>
<p>store에서 내가 무슨 생각이었는지 모르겠지만 @computed 대신 @get을 아무런 의심없이 사용하고 있었다.
다른 스토어는 @computed를 사용했으면서...
후...이걸로 한 6시간 고민한 문제가 어이없이 해결됐다.</p>
<p>결론: 몹엑스 사용시 스토어에서 @get을 사용하면 클래스 상속이 제대로 안된다.</p>
<p>끗-</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React에 Mobx 얹어서 로그인 구현해보기]]></title>
            <link>https://velog.io/@ru_bryunak/React%EC%97%90-Mobx-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@ru_bryunak/React%EC%97%90-Mobx-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 27 Apr 2020 01:44:52 GMT</pubDate>
            <description><![CDATA[<p>아무래도 아직까지는 Mobx보다 Redux를 사용하는 사람들이 많아서 Mobx에 관한 자료가 적습니다.
게다가 Mobx가 자유도가 높은 것인지 정형화 된 패턴이 보이지 않기도 하고, 리액트 Hook이 나오면서 클래스형 컴포넌트를 사용할 때와 Hook을 사용할 때 Store를 바인딩 하는 방법에 살짝 차이도 있어서, 검색을 하다가 오래 헤맸습니다.
그래서 제가 적용한 방법을 남겨봅니다.
사용 방식에도 클래스를 이용하는 방식과 Hooks를 사용하는 방식이 있는데, 개인적으로는 클래스를 사용하는게 훨씬 이해하기 쉬워서 클래스 방식을 사용중입니다.</p>
<p>Mobx를 활용하여 로그인 기능을 구현해보겠습니다.</p>
<p>먼저, 스토어를 생성해보겠습니다.</p>
<pre><code class="language-typescript">src/store/authStore.ts

import { observable, action, computed } from &quot;mobx&quot;;
import loginApi from &quot;@/Views/Auth/api/authApi&quot;;
import { AuthData } from &quot;@/common/interface/auth&quot;;

class AuthStore {

  @observable userData: authInfo | null = null;

  @computed get isLogged() {
    this.userData = JSON.parse(&lt;string&gt;localStorage.getItem(&#39;authInfo&#39;));
    return !!this.userData;
  }

  @computed get getAccessToken() {
    return this.userData!.token;
  }

  @action
  async login(authData: AuthData) {
    let result = await loginApi.login(authData);
    localStorage.setItem(&#39;authInfo&#39;,JSON.stringify(authInfo.data.authInfo));
    return result;
  }

}

export default AuthStore;

interface authInfo {
  tokenType: string
  token: string
  userId: string | number
  lastLogged: string
}</code></pre>
<p>이 예시에서는 스토어를 하나만 만들겠지만, 차후 늘어날 스토어들을 관리할 중앙 관리소를 만들겠습니다.
그리고 프로바이더를 사용해 스토어를 주입합니다.</p>
<pre><code class="language-typescript">src/store/index.ts


import AuthStore from &quot;@/store/authStore&quot;;

export default {
  authStore = new AuthStore(),
}


src/index.tsx

import store from &#39;@/store&#39;;
import { Provider } from &#39;mobx-react&#39;;

const app = (
    &lt;Provider {...store}&gt;
        &lt;App /&gt;
    &lt;/Provider&gt;
);

ReactDOM.render(app, document.getElementById(&#39;root&#39;));</code></pre>
<p>이제 스토어를 사용해보겠습니다.
컴포넌트에 inject를 사용해서 스토어를 주입하면 props로 사용할 수 있습니다.
만약 여러 스토어를 사용하고 싶다면, &#39;authSotre&#39;, &#39;dealStore&#39; 이런 식으로 추가하시면 됩니다.</p>
<pre><code class="language-typescript">src/Views/Auth/Login.tsx


import React, { useState } from &#39;react&#39;;
import AuthStore from &quot;@/store/authStore&quot;;

interface Props extends RouteComponentProps {
  authStore: AuthStore
}

const Login = inject(&quot;authStore&quot;)(observer(({authStore, history}: Props) =&gt; {

  const [authData, setAuthData] = useState&lt;AuthData&gt;({
    email: &#39;&#39;,
    password: &#39;&#39;,
  });

  async function tryLogin() {
    try {
      const result = await authStore.login(authData);
      if (result.status === 200) {
        history.push(&#39;/dashboard&#39;);
      }
    } catch (e) {
      console.log(e)
    }
  }

  return (
      &lt;&gt;
    &lt;/&gt;
  )

  export default Login;</code></pre>
<p>처음 다뤘던 프레임워크가 뷰였어서 그런지, 뷰엑스와 비슷한 몹엑스가 훨씬 직관적이고 편했습니다.
리덕스를 사용해보진 않았지만...보기만해도 너무 복잡해 보였습니다.
언젠가는 배우겠지만, 선택할 수 있다면 몹엑스를 계속 사용할 듯 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React에 Facebook 로그인 얹어보기]]></title>
            <link>https://velog.io/@ru_bryunak/React%EC%97%90-Facebook-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@ru_bryunak/React%EC%97%90-Facebook-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%96%B9%EC%96%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 21 Apr 2020 07:58:42 GMT</pubDate>
            <description><![CDATA[<p>소셜로그인 기능을 붙이는건 처음이라 검색을 해봤더니, 온통 react-facebook-login 사용방법밖에 없어서 직접 써봅니다.
(사실 위 라이브러리 가져다 쓰는게 편하..ㅂ...)
본 글에서는 간단한 정보만 불러오는 내용만 기재하니 자세한 내용은 페이스북 개발자 사이트를 참고하시면 됩니다.</p>
<p>본 작업에 앞서, 페이스북 개발자 사이트에서 키를 발급받아야 합니다.
하지만 이 부분은 스킵하도록 하죠.</p>
<p>일단, 컴포넌트를 하나 생성해 줍니다.</p>
<pre><code class="language-typescript">const FbLogin = ({}) =&gt; {
  return (
    &lt;Button&gt;페이스북 로그인&lt;/Button&gt;
  );
};  </code></pre>
<p>그 다음, Facebook SDK를 불러오겠습니다.</p>
<pre><code class="language-typescript">
useEffect(() =&gt; {
  setFBAsyncInit();
  loadSdkAsynchronously();
}, []);

function setFBAsyncInit() {
  (window as any).fbAsyncInit = () =&gt; {
    (window as any).FB.init({
      version: `v6.0`, // 작성일 기준 버전이 v6.0 입니다.
      appId: &#39;appId&#39;, // 발급받은 App ID 를 넣어줍니다.
      xfbml: true,
      cookie: true,
    });
  };
}

function loadSdkAsynchronously() {
  ((d, s, id) =&gt; { //이런것 까지 타입을...잡아야 하는지는 잘 모르겠습니다 ;ㅅ;
    const element = d.getElementsByTagName(s)[0];
    const fjs = element as Element;
    let js = element as any;
    if (d.getElementById(id)) {
      return;
    }
    js = d.createElement(s);
    js.id = id;
    js.src = `https://connect.facebook.net/en_US/sdk.js`;
    fjs.parentNode!.insertBefore(js, fjs);
  })(document, &#39;script&#39;, &#39;facebook-jssdk&#39;);
}</code></pre>
<p>그 다음 버튼에 클릭이벤트를 추가하고</p>
<pre><code class="language-typescript">return (
  &lt;Button onClick={checkLogin}&gt;페이스북 로그인&lt;/Button&gt;
);</code></pre>
<p>페이스북에 로그인이 돼있는지 여부를 체크해봅니다.</p>
<pre><code class="language-typescript">function checkLogin() {
  (window as any).FB.getLoginStatus((checkResponse: Response) =&gt; {
    checkState(checkResponse)
  });
}

function checkState(checkResponse: Response) {
  if (checkResponse.status === &#39;connected&#39;) {
    // 로그인 돼있을 때 작업
  } else {
    // 로그인이 되어있지 않다면 (&#39;not_authorized&#39; | &#39;unknown&#39;)
    (window as any).FB.login((response: Response) =&gt; {
     // 로그인 하거나 회원가입 할 수 있는 팝업을 띄웁니다.
    })
  }
}

interface Response {
  status: &#39;connected&#39; | &#39;not_authorized&#39; | &#39;unknown&#39;
  authResponse: null | AuthResponse
}

interface AuthResponse {
  accessToken: string,
  expiresIn: number,
  signedRequest: string,
  userId: string
  data_access_expiration_time: number
  graphDomain: string
}</code></pre>
<p>마지막으로 유저정보를 받아오겠습니다.
저는 이름과 이메일을 받아오기 위해서 필드값으로 이메일과 이름을 넣었습니다.
인자를 넘기지 않을 경우 기본적으로는 이름과 아이디값이 넘어옵니다.</p>
<pre><code class="language-typescript">function checkState(checkResponse: Response) {
    if (checkResponse.status === &#39;connected&#39;) {
      getUser();
    } 
  ...
}

function getUser() {
  FB.api(&#39;/me&#39;, {fields: [&#39;email&#39;, &#39;name&#39;]}, (response: userData) =&gt; {
    console.log(response)
  });
}</code></pre>
<p>이렇게 페이스북 로그인 버튼 컴포넌트를 만들어봤습니다.
저처럼 라이브러리를 사용하기 꺼림칙한데 검색하니 라이브러리밖에 안나와서 고민중인 초보개발자 분들에게 조금이나마 도움이 되길 바랍니다.</p>
]]></description>
        </item>
    </channel>
</rss>