<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>C언어 예술가</title>
    <link>https://thrillfighter.tistory.com/</link>
    <description>C언어,C++ 프로그래밍 개발과 IT관련이야기에 대한 블로그.</description>
    <language>ko</language>
    <pubDate>Thu, 9 Apr 2026 22:27:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>콘파냐</managingEditor>
    <image>
      <title>C언어 예술가</title>
      <url>https://tistory1.daumcdn.net/tistory/878598/attach/5e6203104a9a45ccb7c0d5a51df4742f</url>
      <link>https://thrillfighter.tistory.com</link>
    </image>
    <item>
      <title>외주 개발 문의는 여기로 해주세요.</title>
      <link>https://thrillfighter.tistory.com/notice/758</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아주 작은 소규모 프로젝트 부터 모든 프로젝트 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부담없이 물어보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카톡 ID thrillfighter 입니다.&lt;/p&gt;</description>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/notice/758</guid>
      <pubDate>Fri, 27 Dec 2024 19:03:03 +0900</pubDate>
    </item>
    <item>
      <title>처음 시작하는 fastapi - 웹 백엔드 개발 시작을 위한 든든한 책 리뷰</title>
      <link>https://thrillfighter.tistory.com/757</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastApi는 공식문서가 잘 정리되어 있음에도 초보자의 경우 이해하기 힘든 부분이 있고 어디서부터 공부를 해나가야할 지 난감할 수 있는데 이 책은 그런 부분을 처음부터 간단하지만 잘 정리해서 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastApi의 가장 큰 장점인 비동기처리에 대해서도 잘 설명을 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2024-07-28 12-58-02.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;821&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwqM9f/btsIPjb1ytt/OvZ7WltTKBgD1vJnK7woy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwqM9f/btsIPjb1ytt/OvZ7WltTKBgD1vJnK7woy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwqM9f/btsIPjb1ytt/OvZ7WltTKBgD1vJnK7woy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwqM9f%2FbtsIPjb1ytt%2FOvZ7WltTKBgD1vJnK7woy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;515&quot; data-filename=&quot;Screenshot from 2024-07-28 12-58-02.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;821&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 FastApi에 관심을 가졌던 이유가 요청의 비동기 처리였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastApi는 starlette에 기반을 두었다. starlette은 ASGI(async gateway interface) 로 Fastapi의 기술적인 근본은 Starlette이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 Starlette가 아닌 FastApi가 유명한 이유는 FastApi의 다양한 기능들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 맘에 드는 기능은 자동문서화 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책에서도 자동문서화에 대한 내용을 언급하기는 하지만 자세한 내용을 공식문서를 뒤져봐야할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;orm에 대한 내용도 역시 마찬가지로 공식문서를 뒤져봐야하고 sqlAlchemy에 대해서 공부를 해야할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;django과 같이 자체적 orm이 아닌 sqlAlchemy와 같이 third part library를 사용해야하는데 sqlAlchemy의 내용이 간단하지 않기 때문에 많은 내용을 다루지는 않는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책의 지면의 한계로 뺄 내용은 과감히 빼면서 FastApi로 웹개발 프로세스의 전체적인 뼈대를 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastApi는 python웹 프레임웍인 만큼 Ai와 데이터분석과 함께 많이 사용되는 편이다. 그런 내용도 적절하게 지면에 할애되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자나 FastApi로 개발을 시도하려는 개발자가 이 책으로 감을 잡는 것이 좋다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 고급개발자에게는 부족한 부분이 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1e1e23;&quot;&gt;&lt;b&gt;&quot;한빛미디어 &amp;lt;나는리뷰어다&amp;gt; 활동을 위해서 책을 제공받아 작성된 서평입니다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>관심사/도서</category>
      <category>FastAPI</category>
      <category>백엔드</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/757</guid>
      <comments>https://thrillfighter.tistory.com/757#entry757comment</comments>
      <pubDate>Sun, 28 Jul 2024 13:06:02 +0900</pubDate>
    </item>
    <item>
      <title>밑바닥부터 시작하는 딥러닝 4 도서 리뷰</title>
      <link>https://thrillfighter.tistory.com/754</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 책은 밑바닥부터 시작하는 딥러닝의 4번째 시리즈로 심층 강화학습을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강화학습에 대한 이론은 이미 오래전에 정리가 되었지만 딥러닝의 등장으로 심층 강화학습이라는 형태로 더 발전하여 기존 강화학습의 한계를 뛰어넘게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2024-02-25 20-44-23.png&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVbw3k/btsFh51x4vU/uXpka1erWcShzcVKhgaQp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVbw3k/btsFh51x4vU/uXpka1erWcShzcVKhgaQp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVbw3k/btsFh51x4vU/uXpka1erWcShzcVKhgaQp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVbw3k%2FbtsFh51x4vU%2FuXpka1erWcShzcVKhgaQp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;455&quot; height=&quot;588&quot; data-filename=&quot;Screenshot from 2024-02-25 20-44-23.png&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강화학습에 대한 이론은 그다지 새로울 것이 없기 때문에 이 책에서 어떻게 강화학습에 대한 이론을 풀어가는지에 대해 중점적으로 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 강화학습에 대한 이론을 어느정도 알고서 보는 것이기 때문에 이 책을 더 재밌게 읽을 수가 있었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책은 밑바닥부터 시작하는 딥러닝 3과 연결되는 코드가 있다는 점은 참고해야한다. 비록 3권을 보지 않더라도 큰 문제는 되지 않지만 시간이 되고 가능하면 3편을 보는 것이 좋다는 생각이 든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOHRj9/btsFeSvNir3/etQCkhPN77PyukPHccLdEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOHRj9/btsFeSvNir3/etQCkhPN77PyukPHccLdEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOHRj9/btsFeSvNir3/etQCkhPN77PyukPHccLdEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOHRj9%2FbtsFeSvNir3%2FetQCkhPN77PyukPHccLdEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;626&quot; height=&quot;510&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이 책은 설명이 대단이 구체적이라 느껴졌다. 이론과 예제의 간극을 좁혀 어떤 방식으로 동작하는지에대해 구체적으로 이해하기 쉽게 설명이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 시각화를 통한 설명이 잘되어 있다는 점이 정말 맘에 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋았지만 어쩔 수 없이 아쉬운 부분은 강화학습의 적용 사례에 대한 부분이다. 강화학습은 만능이 아니기 때문에 적용해야할 사례에 대한 한계점이 존재하고 이로인해서 많은 사례가 아직까지는 없는 것 같다. 이 책에서 간단히 소개하는 사례 역시 새로운 것들은 아니지만 잘 정리하여 설명해주어서 좋았고 한계점 역시 잘 설명해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강화학습은 시행을 통해서 발전하는 시스템이므로 시뮬레이터의 중요성도 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간만에 좋은 책을 읽어서 기분이 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 책에 대해서 크게 기대는 하지 않았지만 한층 강화학습에 대한 이해도가 높아진 것 같다. 그만큼 강화학습은 이론을 잘 정리하는 것이 중요하고 이 책은 자세한 설명과 시각화 그리고 적절한 코드로 강화학습을 이해하기 쉽도록 잘 정리한 입문서라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;한빛미디어 &amp;lt;나는 리뷰어다&amp;gt; 활동을 위해서 책을 제공받아 작성된 서평입니다.&quot;&lt;/p&gt;</description>
      <category>A.I./강화학습</category>
      <category>강화학습</category>
      <category>밑바닥부터 시작한는 딥러닝 4</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/754</guid>
      <comments>https://thrillfighter.tistory.com/754#entry754comment</comments>
      <pubDate>Sun, 25 Feb 2024 21:14:23 +0900</pubDate>
    </item>
    <item>
      <title>물감</title>
      <link>https://thrillfighter.tistory.com/753</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;감정은 물감의 색과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 물감이 섞이면 하나의 색으로 표출된다. 이렇게 섞인 색 속에는 다양한 색의 조합이 있겠지만 그 조합을 역으로 알아내기는 어렵다. 그래서 가끔은 내 감정을 잘 모를 때가 많은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 옅은 색은 있었는지도 모른채 섞여있을 수 있고, 너무 짙은 색은 다른 색들을 집어 삼킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금씩 천천히 스며드는 색은 본래의 색을 유지시킨 채 천천히 스며들며 변화된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색은 감정같다. 아무리 옅은 감정도 섞여서 없어지는 것이 아니라 본래의 색을 조금이라도 변화시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감정은 색과 같아서 비 언어적인 형태지만 가끔 글을 쓰는 것은 마치 분광기를 통과시키는 것 같아서 내 마음 속 색의 조합을 정리해주기도 한다.&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/753</guid>
      <comments>https://thrillfighter.tistory.com/753#entry753comment</comments>
      <pubDate>Sun, 14 Jan 2024 17:27:40 +0900</pubDate>
    </item>
    <item>
      <title>2023년을 보내며. 영원한 자유를 찾아서.</title>
      <link>https://thrillfighter.tistory.com/752</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사색하는 것은 인간으로 살아가고 맑은 정신을 유지하는 데 있어 매우 중요한 행동이라고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 사색이 현대인에게는 너무 어울리지 않아 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가와 이런 대화를 한다는 것은 나 자신의 생각, 가치관을 드러내야 해야하는데 보통은 이상한 눈으로 바라볼 것이 뻔할 것 같았다. 아니 그랬었다. 그래서 보통은 내가 생각하는 바와 대부분의 타인들과의 대화의 틀 간의 갭이 존재한다고 생각하고 살아왔다. 어쩌면 익숙해져왔는지도 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 친구 중에 목사 친구가 있어서 예전에 그 친구와 어떤 주제로 깊게 이야기 해 본적이 있었는데 그 땐 내가 그 이야기를 다 이해하지 못했었다. 그 후 화엄경에 대해서 알게되었는데 그 때 이야기한 주제가 아마 화엄경의 내용과 비슷했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 보통은 살아가는 것도 만만치 않은 삶에서 살아가는 데 도움이 안될 것 같은 저런 이야기를 한다는 건 참 시간 낭비라고 느낄 법 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구나 돈 벌고 결혼하고 자식 낳고 그렇게 살다보면 하루하루 먹고 사는 문제와 인간관계 속에서 거미줄처럼 나의 행동과 생각의 자유의 구속은 익숙함과 당연함이 되어가는 것 같다. 그렇게 사색은 커녕 아웅다웅 하다보면 시간은 정말 빠르게 흘러간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추구하는 가치를 어디에 두는지는 역시 중요하다고 생각된다. 사회적 통념 속에서도 가치를 찾을 수 있겠지만 가끔은 그 가치가 사회적인 통념속에 존재한다는 것이 자유를 구속하는 것 같이 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 가끔 저녁 노을을 바라볼 때 자연의 아름다움을 느낀다. 난 이런 아름다움을 가끔은 자유라고 표현하고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 가장 좋아하는 장소다. 그리고 왼편 뿌리가 길게 뻗어있는 소나무는 내 친구다. 10년도 넘은 친구며 항상 그 자리에서 변함없이 나를 맞이해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;photo_2024-01-01_00-01-02.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wgBll/btsCOE0GR75/AMKmh05cIFbokWgXO959U0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wgBll/btsCOE0GR75/AMKmh05cIFbokWgXO959U0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wgBll/btsCOE0GR75/AMKmh05cIFbokWgXO959U0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwgBll%2FbtsCOE0GR75%2FAMKmh05cIFbokWgXO959U0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;960&quot; data-filename=&quot;photo_2024-01-01_00-01-02.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가끔 내가 바람문이라고 부르는 공간이 열리는 날이 있는데 이런 날은 바람을 맞으면서 자연과 하나가 됨을 느끼기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년에는 새로운 목표나 새로운 다짐보다는 기존의 다짐들을 계속 지켜나가려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게 주어진 삶을 더욱 소중히 여길 것이고, 목표에 한걸음 더 다가가기 위해 노력할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 영원한 자유를 찾아서...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 영상은 나에게는 정신적인 스승과도 같은 영상이다.&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/hukmIyNtWqM?si=anbPIrn9WDP087iH&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가에게도 도움이 되길 바라며.&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/752</guid>
      <comments>https://thrillfighter.tistory.com/752#entry752comment</comments>
      <pubDate>Sun, 31 Dec 2023 23:55:09 +0900</pubDate>
    </item>
    <item>
      <title>세상의 흐름을 만들어갈 것인가 아니면 그냥 흐르는데로 살 것인가?</title>
      <link>https://thrillfighter.tistory.com/751</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI기술이 점점 빠르게 삶을 파고든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 회사에서 백엔드 개발자로 개발을 하며 먹고산다. 그럼에도 AI에 관심이 많아 회사 내에서도 AI관련 기술로 문제를 해결하기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리 회사는 AI와는 거리가 멀다. 개발자들이 생각보다 AI 기술에 관심있어하지는 않는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 내 AI기술을 더 발전시키고 싶고 다른 사람들과 교류도 하고 싶지만 그러기 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집에오면 쉬고 싶기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 동안 월급 잘 나오는 회사에서 난 나태해진 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 강화학습은 앞으로 더 두각될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 AI의 꽃이라 불리는 강화학습에 관심이 많다. 현재 몇 권을 책을 정독중이지만 그 이론은 머신러닝에 비해 추상적이고 생소한 부분이 있어 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그도 참 오랫동안 방치해 놨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마도 그 동안 많은 변화도 있었고 블로그를 할 여력이 없어서 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10여년 이상 했던 블로그는 내가 성장해 나감에 있어 많은 도움을 주었다. 그런데 너무 방채해 놓은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간만에 공부하다 주절 거렸다.&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/751</guid>
      <comments>https://thrillfighter.tistory.com/751#entry751comment</comments>
      <pubDate>Sun, 11 Jun 2023 17:07:24 +0900</pubDate>
    </item>
    <item>
      <title>일기(10/3)</title>
      <link>https://thrillfighter.tistory.com/750</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;블로그를 잘 안쓴다. 사실 좀 바쁘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 가을하늘이 참 맑다. 하지만 지금은 비가 많이 내리고 휴일의 마지막 밤 11시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번주에는 회사에서 워크샵을 갔다왔다. 그냥 그랬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루가 지나는 것이 반복되며 시간은 흘러간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 빠르다고 하지만 시간은 정직하게 흘러간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 내가 원하던 삶은 심플하고 단조롭지만 안정적인 삶이었다. 지금이 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 좀 자리가 잡히는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 가끔 나는 왜 이런 삶을 살아 왔을까 하고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지나고 보면 참 내가 왜 그랬나 싶은 시간들이 흘러 지금에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가끔은 그리운 추억같은 기억이지만 다시 돌아간다면 겪고 싶지 않은 일도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든게 꿈 같기만 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금도 미래에 꿈으로 변할 시간을 살고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꿈으로 변한 시간에 나와 같이 했던 사람들이 다 잘 지내길 바란다. 왜냐면 그 사람들이 잘 못지내고 있다면 내 맘이 불편할 것 같아서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 단조로운 삶에서 또 새로운 꿈을 꾸고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 생각보다 많은 것들을 공부해와서 쓸모가 많은 듯 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강화학습을 통한 사회적인 문제해결이다. 이 문제를 풀면 큰 가치를 창출하리라 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강화학습은 AI의 끝판왕 답게 참 어렵다. 이론만 5번을 넘게 읽어서야 겨우 감이 잡힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아무튼 내일 출근하려면 자야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언젠가 강화학습을 통해 원하는 문제해결을 하는 날이 오길 기다린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 2022년이 얼마 안남았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다들 건강하게 잘 지내.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕.!&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/750</guid>
      <comments>https://thrillfighter.tistory.com/750#entry750comment</comments>
      <pubDate>Mon, 3 Oct 2022 23:22:01 +0900</pubDate>
    </item>
    <item>
      <title>플랫폼 서비스 보다는 기술 스타트업</title>
      <link>https://thrillfighter.tistory.com/749</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트로 하는 일에 강화학습을 사용할 일이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 4년전 강화학습에 관심을 갖고 이론적인 부분은 대충 훑어본 후 간단한 길 찾기 강화학습 모델을 만든 적이 있다. 강화학습이론을 제대로 공부하고 만든 건 아니었는데 지금 강화학습을 제대로 공부하면서 그 당시 어떻게 저걸 만들었을 까하는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 강화학습에 대한 이론은 상당히 철학적이다. MDP라는 마르코프 디시전 프로세스는 강화학습을 위한 문제정의 방법이며, 다양한 방정식들을 토대로 밸만 기대 방정식을 만든다. 여기에 신경망을 덧붙여 심층 강화학습을 하거나 다양한 변형이 있는 걸로 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론에 대한 부분만 정독으로 5번 이상 읽고 있다. 보면 볼 수록 참 신비로운 이론이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인간이 걸음마를 배우거나 자전거를 배우는 것이 일종에 강화학습이며 강화학습 이론의 토대가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 현재 회사일 때문에 읽는 책만 여러권인데 강화학습을 공부할 시간이 없긴하지만 사이드 프로젝트를 해결하고 싶은 마음이 간절하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/749</guid>
      <comments>https://thrillfighter.tistory.com/749#entry749comment</comments>
      <pubDate>Mon, 19 Sep 2022 22:51:48 +0900</pubDate>
    </item>
    <item>
      <title>코딩 과외 문의는 여기로</title>
      <link>https://thrillfighter.tistory.com/notice/746</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;(2022년 새롭게 수정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그를 보고 과외 문의주시는 분들이 계셔서 카톡 ID를 남겨둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;관심있으시다면 카톡 ID&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;thrillfighter&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 문의 주시면 답변드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native 및 (react(next.js))로 앱 및 웹 개발 + django, fastapi로 백엔드 설계 + db 설계, 연동 + aws 론칭까지 웹 풀스택으로 1인 개발하여 여러 서비스를 론칭해왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;티칭 분야는 C언어부터 파이썬(python)등의 프로그래밍 문법, 파이썬 백엔드, 파이썬 데이터처리(판다스, 시각화) 등이며 이 외에도 역시 상담을 통해 협의하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과외를 위한 기본적인 프레임웍, 툴들을 사용을 할 수 있습니다.(docker, git, vscode), 코딩을 하기위해 중요.&lt;/p&gt;</description>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/notice/746</guid>
      <pubDate>Sun, 22 May 2022 14:23:16 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 호이스팅(var, 함수)</title>
      <link>https://thrillfighter.tistory.com/744</link>
      <description>&lt;p&gt;호이스팅이란, 메모리에 미리 할당하는 것을 의미한다.&lt;br&gt;자바스크립트에서는 호이스팅이라는 개념이 있지만 호이시팅을 지양하는 분위기다. 호이스팅은 예측하지 못할 결과를 초래할 가능성이 존재하기 때문이다.&lt;br&gt;호이스팅이 적용되는 경우는 &lt;code&gt;var&lt;/code&gt;를 사용하거나 함수 선언을 하는 경우다.&lt;br&gt;먼저 예측하지 못할 가능성에 대한 예제를 몇가지만 만들어 보려고 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;console.log(myVariable); // undefined
var myVariable = 33;
console.log(myVariable); // 33&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;var&lt;/code&gt; 를 사용하게 되면 호이스팅이 되어 위 코드에 선행되어 &lt;code&gt;myVariable&lt;/code&gt;의 선언이 먼저 이루어진다. 다음과 같은 코드로 호이스팅을 표현할 수 있겠다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myVariable; // 선언
console.log(myVariable); // undefined
myVariable = 33; // 할당
console.log(myVariable); // 3당&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 호이스팅이 되면 &lt;code&gt;myVariable&lt;/code&gt;이 원래 목적한 값이 아닌 &lt;code&gt;undefined&lt;/code&gt;으로 사용될 수 있으며 에러도 발생하지 않으므로 예기치 못한 동작을 발생할 수 있음.&lt;/p&gt;
&lt;h1&gt;var 대신 let, const 사용하기&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;let&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt;는 호이스팅을 적용되지 않으며 앞서 코드에서 &lt;code&gt;var&lt;/code&gt;를  &lt;code&gt;let&lt;/code&gt;으로만 바꾸어도 에러가 발생하는 것을 알 수 있음.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ReferenceError: Cannot access &amp;#39;myVariable&amp;#39; before initialization&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;함수의 호이스팅&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;var&lt;/code&gt;와 마찬가지로 함수 역시 호이스팅이 적용된다. 호이스팅되는 동작에 대한 예를 암기한다기 보다는 &lt;code&gt;var&lt;/code&gt;의 경우처럼 호이스팅이 되는 경우에 예측하기 힘든 동작이 발생할 수 있다는 점만 이해하는 것이 중요하다.&lt;br&gt;다음의 경우 예측하기 힘든 동작을 보여준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;console.log(sum); // Function: sum
var sum =33;
function sum(a, b) {
    return a + b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;var&lt;/code&gt; 선언된 &lt;code&gt;sum&lt;/code&gt;과 함수 &lt;code&gt;sum&lt;/code&gt;은 모두 호이스팅되었다. 선언된 순서대로 덮어쓰게 되므로 찍히는 로그는 함수가 맞다. 이제 아래 로그를 찍는 동일한 한줄의 코드(&lt;code&gt;console.log(sum)&lt;/code&gt;)를 추가해 보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;console.log(sum); // Function: sum
var sum =33;
function sum(a, b) {
    return a + b;
}
console.log(sum); // 33&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;동일한 sum을 찍지만 아래는 &lt;code&gt;33&lt;/code&gt;이 찍힌다. 이유는 &lt;code&gt;sum&lt;/code&gt;에 &lt;code&gt;33&lt;/code&gt;이 할당되는 코드가 두 &lt;code&gt;console.log(sum)&lt;/code&gt; 사이에 있어 sum의 값이 변경되어서다.&lt;br&gt;이렇게 예측하기 힘든 코드는 사용하면 안된다.&lt;/p&gt;
&lt;h1&gt;함수 호이스팅을 막는 방법&lt;/h1&gt;
&lt;p&gt;함수 표현식을 사용하면 호이스팅을 막을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;console.log(sum); 

// 함수 표현식
const sum = function sum(a, b) {
    return a + b;
}

//// ReferenceError: Cannot access &amp;#39;sum&amp;#39; before initialization&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;함수 표현식은 호이스팅이 안되므로 함수선언문에서는 가능했던 코드지만 함수표현식을 사용하면 위와같이 에러가 발생한다.&lt;/p&gt;</description>
      <category>프로그래밍/javascript</category>
      <category>var</category>
      <category>자바스크립트</category>
      <category>호이스팅</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/744</guid>
      <comments>https://thrillfighter.tistory.com/744#entry744comment</comments>
      <pubDate>Tue, 17 May 2022 22:22:43 +0900</pubDate>
    </item>
    <item>
      <title>javascript 얕은복사, 깊은복사에 대한 이해</title>
      <link>https://thrillfighter.tistory.com/742</link>
      <description>&lt;p&gt;복사는 값의 전달을 목적으로 합니다. 문서를 복사하는 것처럼 컴퓨터 내에서도 파일을 복사하거나 하죠.&lt;/p&gt;
&lt;p&gt;컴퓨터 사용 시 흔하게 파일을 복사합니다. 원본이 있을테고 복사한 파일은 원본과 똑같지만 원본은 아닙니다.&lt;/p&gt;
&lt;p&gt;그리고 복사본을 수정한다고 해도 원본은 변하지않고 복사본만 수정되겠죠.&lt;/p&gt;
&lt;p&gt;위와 같은 방식이 일반적인 사람들이 익숙해져 있는 사고방식입니다.&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKHeBR/btrzxy92kuQ/Q8mofbNc6k8xawWWywjeP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKHeBR/btrzxy92kuQ/Q8mofbNc6k8xawWWywjeP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKHeBR/btrzxy92kuQ/Q8mofbNc6k8xawWWywjeP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKHeBR%2Fbtrzxy92kuQ%2FQ8mofbNc6k8xawWWywjeP0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br&gt;실제 생활에서의 복사본은 원본과 연결되지 않습니다. 그리고 이 방식을 깊은복사라고 이해해 두시면 되겠습니다.&lt;/p&gt;
&lt;h1&gt;얕은복사 전에 참조를 먼저&lt;/h1&gt;
&lt;p&gt;javascript만 공부하신 분이라면 대략 다음과 같이 얕은 복사를 이해 또는 암기하고 계실겁니다.&lt;br&gt;&lt;strong&gt;객체를 얕은복사를 하면 객체의 원소 중 원시타입이 아닌 객체에 대해 참조를 공유한다.&lt;/strong&gt;&lt;br&gt;네 이 말은 맞습니다. 하지만 전 이렇게 설명하는 글을 읽고 이질감이 느껴졌습니다.&lt;/p&gt;
&lt;h2&gt;왜 이질감이 느껴졌을까?&lt;/h2&gt;
&lt;p&gt;사실 &lt;strong&gt;얕은복사&lt;/strong&gt;라는 말은 c언어와 가은 다른 언어에서도 있는 개념입니다. 포인터를 사용하여 객체를 참조한다는 의미가 있죠.&lt;br&gt;얕은복사의 목적에 &lt;strong&gt;객체의 참조를 공유 한다&lt;/strong&gt;는 측면은 맞지만 &lt;em&gt;Container&lt;/em&gt;가 되는 &lt;strong&gt;원본 객체애 대한 참조에 대한 설명은 하지 않는 설명&lt;/strong&gt;이기 때문입니다.&lt;/p&gt;
&lt;h2&gt;컨테이너에 대한 참조&lt;/h2&gt;
&lt;p&gt;여기에서 컨테이너란 원본 객체 그 자체를 의미하며 다음과 같은 방식으로 객체에 대한 참조를 얻을 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const obj = {a:1, b:2 , c:{d:7, e:8}}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드같이 javascript에서는 &lt;code&gt;obj&lt;/code&gt; 변수에 무언가를 대입하면 이 변수는 값을 참조방식으로 가리킵니다.&lt;br&gt;위 방식이 자바스크립트나 참조를 사용하는 언어의 기본적인 동작방식입니다.&lt;br&gt;그렇다면 이런 참조는 어떤 점이 좋을까요?&lt;/p&gt;
&lt;h2&gt;참조의 좋은 점&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const obj = {a:1, b:2 , c:{d:7, e:8}} // 원본

const obj_copy = obj;

obj_copy[&amp;#39;d&amp;#39;]=99;
console.log(obj); //{ a: 1, b: 2, c: { d: 7, e: 8 }, d: 99 }
console.log(obj_copy); //{ a: 1, b: 2, c: { d: 7, e: 8 }, d: 99 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM26jO/btrzzpxNz4c/4kvdWSNKqWxezQJwXbtGM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM26jO/btrzzpxNz4c/4kvdWSNKqWxezQJwXbtGM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM26jO/btrzzpxNz4c/4kvdWSNKqWxezQJwXbtGM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM26jO%2FbtrzzpxNz4c%2F4kvdWSNKqWxezQJwXbtGM0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br&gt;참조를 사용하면 원본의 수많은 카피본(=연산으로 생성된)이 생기더라도 원본과 똑같은 객체를 생성할필요가 없습니다. 자바스크립트에서의 대입연산은 이렇게 동작합니다.&lt;br&gt;우리가 파일복사하는 개념하고는 다르죠. 즉 복사본도 새로운 카피본이 아닌 원본을 가리킨다는 것입니다.&lt;br&gt;메모리도 아끼고 효율적이겠죠.&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eRlWb2/btrzwQXFOtg/RTmhUg0XkZfG72O1AOfVKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eRlWb2/btrzwQXFOtg/RTmhUg0XkZfG72O1AOfVKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eRlWb2/btrzwQXFOtg/RTmhUg0XkZfG72O1AOfVKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeRlWb2%2FbtrzwQXFOtg%2FRTmhUg0XkZfG72O1AOfVKk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;얕은복사 시 컨테이너에 대한 참조의 변화&lt;/h2&gt;
&lt;p&gt;그렇다면 흔히 말하는 얕은복사와 다른 점을 살펴봅시다.&lt;br&gt;다음은 얕은복사를 수행하는 코드입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const obj = {a:1, b:2 , c:{d:7, e:8}} // 원본

const obj_copy = Object.assign({}, obj); //얕은복사

obj_copy[&amp;#39;d&amp;#39;]=99;
console.log(obj); // { a: 1, b: 2, c: { d: 7, e: 8 } }
console.log(obj_copy); // { a: 1, b: 2, c: { d: 7, e: 8 }, d:99 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4htmo/btrzvLQpndo/WNor45CW8LV9qq3I6TbguK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4htmo/btrzvLQpndo/WNor45CW8LV9qq3I6TbguK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4htmo/btrzvLQpndo/WNor45CW8LV9qq3I6TbguK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4htmo%2FbtrzvLQpndo%2FWNor45CW8LV9qq3I6TbguK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br&gt;컨테이너 객체에 대한 참조가 아닌 컨테이너 객체가 복사가 되었습니다.&lt;br&gt;앞서와 달리 원본 객체에는 &lt;code&gt;obj.d.c&lt;/code&gt;라는 요소는 존재하지 않습니다.&lt;br&gt;다음과 같은 부분이 변하였습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컨테이너 객체 새롭게 생성&lt;/li&gt;
&lt;li&gt;복사본에 내부 원시타입은 새롭게 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 2가지가 기본적인 참조를 통한 객체의 복사와 얕은복사의 차이점이며 &lt;strong&gt;내부 객체요소는 여전히 복사본과 원본이 동일한 참조&lt;/strong&gt;를 가집니다. 이 부분이 일반적이 설명이며 핵심이지만, 외부 컨테이너가 새롭게 생겼다는 점 역시 중요한 포인트입니다.&lt;/p&gt;
&lt;h2&gt;깊은복사&lt;/h2&gt;
&lt;p&gt;이런 방식으로 깊은복사도 이해하시면 됩니다. 깊은 복사는 내부 요소 모두 파일복사하듯 복사하는 것입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const obj = {a:1, b:2 , c:{d:7, e:8}} // 원본

const obj_copy = {...obj, c:{...obj.c}}

obj_copy[&amp;#39;d&amp;#39;]=99;
console.log(obj); // { a: 1, b: 2, c: { d: 7, e: 8 } }
console.log(obj_copy); // { a: 1, b: 2, c: { d: 7, e: 8 }, d: 99 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XLH3A/btrzyttsRpz/WHSjUx632vUqYikxDOU4nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XLH3A/btrzyttsRpz/WHSjUx632vUqYikxDOU4nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XLH3A/btrzyttsRpz/WHSjUx632vUqYikxDOU4nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXLH3A%2FbtrzyttsRpz%2FWHSjUx632vUqYikxDOU4nk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br&gt;이 코드에 다음과 같은 코드를 추가하면&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;obj_copy[&amp;#39;c&amp;#39;][&amp;#39;d&amp;#39;]=&amp;#39;HI&amp;#39;;
console.log(obj); //{ a: 3000, b: 2, c: { d: 7, e: 8 } 
console.log(obj_copy); //{ a: 3000, b: 2, c: { d: &amp;#39;HI&amp;#39;, e: 8 }, d: 99 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;내부의 &lt;code&gt;c&lt;/code&gt; 객체가 깊은복사가 되어 원본과 복사본이 서로 연관이 없게됩니다.&lt;/p&gt;
&lt;h1&gt;참고(원시타입에 대한 참고)&lt;/h1&gt;
&lt;p&gt;이 글을 쓰면서 헷갈렸던 점이 객체 원시타입에 대한 참조입니다. 파이썬의 경우 원시타입도 모두 객체로 다루기 때문에 비슷한 방식의 참조이면서도 위 그림이 달라집니다. 너무 깊에 알 필요는 없지만, 참고로 기록해봅니다.&lt;/p&gt;</description>
      <category>프로그래밍/javascript</category>
      <category>JavaScript</category>
      <category>깊은복사</category>
      <category>얕은복사</category>
      <category>자바스크립트</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/742</guid>
      <comments>https://thrillfighter.tistory.com/742#entry742comment</comments>
      <pubDate>Sat, 16 Apr 2022 14:52:35 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 클래스 getter, setter와 네임맹글링(name mangling)</title>
      <link>https://thrillfighter.tistory.com/738</link>
      <description>&lt;h1&gt;getter와 setter의 사용 의미&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;/&lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt; 클래스의 속성값에 직접 접근 막기 위함에 목적이 있습니다.&lt;/li&gt;
&lt;li&gt;직접 접근을 막고 정해둔 메소드로만 접근하게 함으로서 메소드의 특성도 얻게되므로 값의 조작이나 값의 검증하거나 제한할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;파이썬의 getter/setter의 사용법&lt;/h1&gt;
&lt;p&gt;파이썬에서 getter와 setter는 &lt;code&gt;property&lt;/code&gt; 장식자를 사용하여 만들 수 있습니다.&lt;br&gt;앞으로 제시되는 예제는 3단계를 거쳐 문제점이 수정되어 완성되갑니다.&lt;/p&gt;
&lt;h2&gt;1단계 - 순수한 getter/setter&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# getset.py
class GetSetTest():
    def __init__(self):
        self.count = 1

    def get_count(self): # getter
        return self.count

    def set_count(self, value): # setter
        self.count = value

if __name__ == &amp;quot;__main__&amp;quot;:
    obj = GetSetTest()
    print(obj.get_count())
    obj.set_count(33)
    print(obj.get_count())

# 결과
$ python3 getset.py
1
33&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;get_count&lt;/code&gt;메소드는 &lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;의 역할을, &lt;code&gt;set_count&lt;/code&gt; 메소드는 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;의 역할을 합니다. 이 코드는 단순하지만 제대로 동작합니다. 그런데 앞에서 코드를 완성해나간다고 했으며 우리는 1단계에 있으므로 이 코드는 개선될 부분이 있습니다. 어떤 부분을 개선해야하는지 정리해보겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;와 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt; 메소드를 사용할 때 메소드 형태로 사용하기 때문에 가독성이 떨어집니다. 코드 맥락상 &lt;code&gt;count&lt;/code&gt;라는 속성에 접근하는 것이 가독성이 좋을 것 같습니다. 그렇다면 &lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;/&lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;를 메소드 없애고 &lt;code&gt;count&lt;/code&gt;에 바로 접근하면 다음과 같은 이유로 바람직 하지 않습니다.&lt;br&gt;a. &lt;code&gt;count&lt;/code&gt;에 직접 접근을 하면 &lt;code&gt;count&lt;/code&gt;에 어떤 값이라도 넣을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;만약 &lt;code&gt;GetSetTest&lt;/code&gt; 클래스를 설계할 때 &lt;code&gt;count&lt;/code&gt;의 값이 &lt;em&gt;0보다 크고 10보다 작도록 제한을 둔다고 한다면&lt;/em&gt; &lt;code&gt;count&lt;/code&gt;에 직접 접근하는 방식으로는 이런 제한을 둘 방법이 없습니다.&lt;/p&gt;
&lt;h2&gt;2단계 - count이름을 사용하는 getter, setter&lt;/h2&gt;
&lt;p&gt;파이썬에서는 함수,메소드의 기능을 개선, 추가해주는 장식자라는 문법이 있습니다. 파이썬의 제공하는 &lt;code&gt;property&lt;/code&gt;장식자를 사용하면 &lt;code&gt;count&lt;/code&gt;라는 통일된 이름의 &lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;와 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;를 만들 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# getset.py
class GetSetTest():
    def __init__(self):
        self.mycount = 9 

    @property # property 데코레이터로 count의 getter를 만든다.
    def count(self):
        return self.mycount

    @count.setter # count getter로부터 setter를 만든다.
    def count(self, value):
        if value &amp;gt; 0 and value &amp;lt;10:
            self.mycount = value
        else:
            print(&amp;quot;0 &amp;lt; count &amp;lt; 10 의 범위내에 있어야합니다. 입력은 실패합니다.&amp;quot;)

if __name__ == &amp;quot;__main__&amp;quot;:
    obj = GetSetTest()
    print(obj.count) # 메소드가 아닌 속성으로 다루는 것처럼 보인다.
    obj.count = 33 # 메소드가 아닌 속성에 대입하는 것처럼 보인다.
    print(obj.count)

# python3 getset.py   
9
0 &amp;lt; count &amp;lt; 10 의 범위내에 있어야합니다. 입력은 실패합니다.
9 &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;와 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt; 메소드를 만드는 방법은 위 코드의 주석을 참고해 주세요. 좀 더 깊은 내용이 있지만 이 정도로도 충분합니다.&lt;br&gt;클래스 외부에서는 count라는 속성에 접근하여 직접 값을 읽고 &lt;code&gt;=&lt;/code&gt; 연산자를 이용한 대입이 가능한 것처럼 보입니다.&lt;br&gt;또한 &lt;code&gt;count&lt;/code&gt;의 범위 제한(&lt;em&gt;0 &amp;lt; count &amp;lt; 10&lt;/em&gt;)을 둘 수 있고 위 결과처럼 벗어난 범위값에 대해 처리하는 코드도 작성하였습니다.&lt;br&gt;이렇게 코드의 가독성과 추가 제한이 가능하게 되었습니다.&lt;/p&gt;
&lt;p&gt;그런데 위 코드는 좀 이상한 부분이 있습니다. 원래 사용하던 속성인 &lt;code&gt;count&lt;/code&gt;가 아닌 &lt;code&gt;mycount&lt;/code&gt;를 사용한다는 것이죠.&lt;br&gt;이렇게 한 이유가 있습니다. 한번 위 코드에서 &lt;code&gt;mycount&lt;/code&gt;를 &lt;code&gt;count&lt;/code&gt;로 바꾼 후 실행해보세요. 그렇다면 다음과 같은 오류가 발생합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Traceback (most recent call last):
  File &amp;quot;getset.py&amp;quot;, line 17, in &amp;lt;module&amp;gt;
    obj = GetSetTest()
  File &amp;quot;getset.py&amp;quot;, line 3, in __init__
    self.count = 9 
  File &amp;quot;getset.py&amp;quot;, line 12, in count
    self.count = value
  File &amp;quot;getset.py&amp;quot;, line 12, in count
    self.count = value
  File &amp;quot;getset.py&amp;quot;, line 12, in count
    self.count = value
  [Previous line repeated 494 more times]
RecursionError: maximum recursion depth exceeded&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 오류는 &lt;code&gt;count&lt;/code&gt;라는 속성이름이 이미 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;/&lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;에서 이미 사용하였기 때문에 발생한 것입니다.&lt;br&gt;객체 초기화 시 실행된 코드 &lt;code&gt;self.count = 9&lt;/code&gt;는 원래 목적은 &lt;code&gt;count&lt;/code&gt; 속성을 생성하는 것이지만 실제로는 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;인 count를 참조하게 됩니다.&lt;br&gt;그리고 더 심각한 건 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt; 안에 또 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;인 &lt;code&gt;count&lt;/code&gt;에 값을 넣는 다는 것이죠.&lt;br&gt;이렇게 &lt;strong&gt;&lt;em&gt;Recursive&lt;/em&gt;&lt;/strong&gt; 호출이 일어나 스택오버플로가 발생하는 것입니다.&lt;/p&gt;
&lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;

&lt;h2&gt;3단계 - __(double underbar)를 사용한 네임 맹글링&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;__&lt;/code&gt;는 더블언더바 또는 더던이라고도 불립니다.&lt;br&gt;속성이름 앞에 더던을 붙이면 파이썬은 알아서 네임을 맹글링 합니다. 맹글링은 원래이름의 형체를 알 수없게 변형한다는 의미인데 규칙이 있습니다.&lt;br&gt;&lt;code&gt;__count&lt;/code&gt; 는 &lt;code&gt;_클래스이름__count&lt;/code&gt; 로 변경됩니다.&lt;br&gt;파이썬 컨벤션으로 &lt;strong&gt;getter/setter&lt;/strong&gt; 를 사용할 때 대상 속성에 &lt;strong&gt;더던&lt;/strong&gt;을 붙입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class GetSetTest():
    def __init__(self):
        self.__count = 9 # 더던

    @property
    def count(self):
        return self.__count 

    @count.setter
    def count(self, value):
        if value &amp;gt; 0 and value &amp;lt;10:
            self.__count = value
        else:
            print(&amp;quot;0 &amp;lt; count &amp;lt; 10 의 범위내에 있어야합니다. 입력은 실패합니다.&amp;quot;)

if __name__ == &amp;quot;__main__&amp;quot;:
    obj = GetSetTest()
    print(obj.count)
    obj.count = 33
    print(obj.count)
    obj.count = 7
    # print(obj.__count) # error 
    # print(obj._GetSetTest__count) # 7&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이렇게 더던을 붙여서 &lt;strong&gt;Recursive&lt;/strong&gt; 에러를 해결하며 외부에서 쉽게 접근하지 못하도록 _네임 맹글링_이 됩니다.&lt;br&gt;&lt;strong&gt;C++&lt;/strong&gt;과 같은 언어처럼 &lt;code&gt;private&lt;/code&gt;와 같은 접근 지정자가 있다면 해결되겠지만 파이썬의 태생상 클래스를 엄격하게 통제하지는 않습니다.&lt;br&gt;그래도 이렇게 네임 맹글링을 통해서 혹시 실수로 더던 속성에 접근하는 것을 방지하는 것입니다.&lt;/p&gt;
&lt;h1&gt;추가 4단계, 더 자연스러운 객체 초기화&lt;/h1&gt;
&lt;p&gt;객체 생성시 값을 지정하여 초기화하는 방법이 필요하다면 앞서 코드처럼 초기화 함수 내에 &lt;code&gt;self.__count = 초기화 값&lt;/code&gt; 처럼 사용하는 것은 자연스럽지 않습니다. 왜냐면 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;함수의 제한사항이 초기화 시에도 필요하다면 이런 제한사항이 전혀 검사되지 않기 때문이죠.&lt;br&gt;이런 상황이라면 초기화 시에도 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;함수를 활용하여 초기화 하는 것이 좋습니다.&lt;br&gt;다음은 이런 사항을 고려해 개선한 코드입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class GetSetTest():
    def __init__(self, value):
        self.count = value  # setter로 초기화

    @property
    def count(self):
        return self.__count 

    @count.setter
    def count(self, value):
        if value &amp;gt; 0 and value &amp;lt;10:
            self.__count = value
        else:
            print(&amp;quot;0 &amp;lt; count &amp;lt; 10 의 범위내에 있어야합니다. 입력은 실패합니다.&amp;quot;)
            self.__count=1

if __name__ == &amp;quot;__main__&amp;quot;:
    obj = GetSetTest(11)
    print(obj.count)
    obj.count = 33
    print(obj.count)
    obj.count = 7
    print(obj.count)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 코드는 초기화 시에 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;를 사용했습니다. 그리고 객체 초기화를 11로 하여 제한사항을 어겨봤습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;결과이제 객체 초기화 시에도 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;가 적용되어 제한 사항이 제대로 동작합니다. 이 경우 &lt;code&gt;__count&lt;/code&gt;를 초기화 함수가 아닌 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt; 내에서 런타임에 동적으로 생성합니다. 이런 활용은 동적 타이핑 언어인 파이썬이나 자바스크립트에서 가능하다는 것을 기억해주시면 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ python3 getset.py 0 &amp;lt; count &amp;lt; 10 의 범위내에 있어야합니다. 입력은 실패합니다. 1 0 &amp;lt; count &amp;lt; 10 의 범위내에 있어야합니다. 입력은 실패합니다. 1 7&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/python</category>
      <category>getter</category>
      <category>name mangling</category>
      <category>python</category>
      <category>setter</category>
      <category>네임맹글링</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/738</guid>
      <comments>https://thrillfighter.tistory.com/738#entry738comment</comments>
      <pubDate>Wed, 13 Apr 2022 00:01:50 +0900</pubDate>
    </item>
    <item>
      <title>react-native eslint 설정</title>
      <link>https://thrillfighter.tistory.com/737</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;eslint란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;lint&lt;/code&gt;는 &lt;code&gt;linter&lt;/code&gt;라고도 하며 소스코드의 문법오류나, 버그등을 찾아주는 도구라 할 수 있습니다. 이러한 &lt;code&gt;linter&lt;/code&gt;는 최신 개발환경에는 필수로 자리잡았습니다. 그러므로 어떤 언어든 개발을 하기 앞서 &lt;code&gt;linter&lt;/code&gt;의 활용법을 잘 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 사용하는 &lt;code&gt;linter&lt;/code&gt;는 &lt;code&gt;ESlint&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 강의에서 react-native &lt;b&gt;typescript&lt;/b&gt; 환경을 설치한 후 시작하도록 하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ npx react-native init TestProject --template react-native-template-typescript&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정만으로도 eslint가 설치됩니다. 기본 설치 (V7.32.0) 버전이 설치되는데 최신 버전의 &lt;b&gt;eslint&lt;/b&gt;를 설치하도고 싶다면 다음과 같은 과정을 거쳐야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 방법(현재 최신 eslint 버전 v8.13.0)&lt;/h2&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;$ npm install -D eslint // yarn add -D eslint&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;eslint&lt;/b&gt;를 설치했다면 환경을 설정해야합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;eslint의 동작에 필요한 파일&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.eslintrc&lt;/code&gt; 또는 &lt;code&gt;eslintrc.js&lt;/code&gt; 또는 &lt;code&gt;eslintrc.yml&lt;/code&gt;, &lt;code&gt;eslintrc.json&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;package.json&lt;/code&gt; 가 기본으로 있어야 하며 &lt;b&gt;eslint&lt;/b&gt;의 환경을 설정하는 &lt;code&gt;.eslintrc&lt;/code&gt; 가 &lt;code&gt;package.json&lt;/code&gt;과 함께 프로젝트 루트에 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;b&gt;eslint&lt;/b&gt; 환경 설정 파일이 없다면 다음 명령으로 생성하도록 합니다. 또는 기존 eslintrc 는 백업해 두고 다음 코드를 실행해도 됩니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ yarn add -D @eslint/create-config
// 다음 설치는 타입스크립트를 위한 tslint 대신 eslint를 사용하기 위한 설치.
// tslint는 2019년 이후로 depreacted되어 eslint가 그 기능을 대신한다.
$ yarn add -D eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
$ yarn eslint --init // 이 명령 후 질의 응답을 통해 eslint가 적용될 환경이 설정된다. 
// 또한, eslint 환경 설정 파일의 형식도 정할 수 있다. 
// 마지막 Would you like to install them now with npm에는 No를 선택한다.
// 또한 중간에 Browser와 node를 선택할 때는 spacebar로 체크를 헤제하거나 선택을 하여야 한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;react-native typescript 템플릿 설치 시 eslint의 기본값&lt;/h3&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;module.exports = {
  root: true,
  extends: '@react-native-community',
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  overrides: [
    {
      files: ['*.ts', '*.tsx'],
      rules: {
        '@typescript-eslint/no-shadow': ['error'],
        'no-shadow': 'off',
        'no-undef': 'off',
      },
    },
  ],
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;eslinter 설정을 json형식으로 설정했을 때&lt;/h3&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;env&quot;: {
        &quot;browser&quot;: true,
        &quot;es2021&quot;: true,
        &quot;node&quot;: true
    },
    &quot;extends&quot;: [
        &quot;eslint:recommended&quot;,
        &quot;plugin:react/recommended&quot;,
        &quot;plugin:@typescript-eslint/recommended&quot;
    ],
    &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
    &quot;parserOptions&quot;: {
        &quot;ecmaFeatures&quot;: {
            &quot;jsx&quot;: true
        },
        &quot;ecmaVersion&quot;: 12,
        &quot;sourceType&quot;: &quot;module&quot;
    },
    &quot;plugins&quot;: [
        &quot;react&quot;,
        &quot;@typescript-eslint&quot;
    ],
    &quot;rules&quot;: {
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;eslinter 설정을 javascript 형식으로 했을 때 기본값&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;module.exports = {
    &quot;env&quot;: {
        &quot;es2021&quot;: true,
        &quot;node&quot;: true
    },
    &quot;extends&quot;: [
        &quot;eslint:recommended&quot;,
        &quot;plugin:react/recommended&quot;,
        &quot;plugin:@typescript-eslint/recommended&quot;
    ],
    &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
    &quot;parserOptions&quot;: {
        &quot;ecmaFeatures&quot;: {
            &quot;jsx&quot;: true
        },
        &quot;ecmaVersion&quot;: 12,
        &quot;sourceType&quot;: &quot;module&quot;
    },
    &quot;plugins&quot;: [
        &quot;react&quot;,
        &quot;@typescript-eslint&quot;
    ],
    &quot;rules&quot;: {
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;eslintrc 설정&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eslint의 속성들의 의미를 알아보겠습니다. &lt;a href=&quot;https://eslint.org/docs/user-guide/configuring/&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;env&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;프로젝트의 실행 환경 설정입니다. &lt;code&gt;yarn eslint --init&lt;/code&gt;의 질의 응답으로 설정한 환경인 &lt;code&gt;node&lt;/code&gt;가 설정되어 있네요. &lt;code&gt;browser&lt;/code&gt;가 필요하면 같은 형식으로 추가해주면 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;extends&lt;/b&gt; - 코드 스타일 확장을 설정합니다. 예를들어 문자열에 '문자열'을 사용할 건지 &quot;문자열&quot;을 사용할 건지 등..&lt;code&gt;extends: '@react-native-community',&lt;/code&gt; 을 추가할 수 있습니다. 만약 다른 개발환경이라면 &quot;airbnb-base&quot; 등과 같은 다른 코딩 스타일을 찾아 설정하면 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;parser&lt;/b&gt; - 구문 분석기 설정을 합니다. 우리는 타입스크립트를 사용하므로 &lt;code&gt;@typescript-eslint/parser&lt;/code&gt;를 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;parserOptions&lt;/b&gt; - 지원한 언어 옵션을 제공합니다. &lt;code&gt;jsx&lt;/code&gt;는 리액트에서 사용하는 문법이므로 추가해줍니다. jsx&lt;/li&gt;
&lt;li&gt;&lt;b&gt;plugins&lt;/b&gt; - 처리기(processor)를 지정합니다. 우리가 작성하는 모든 코드는 &lt;code&gt;javascript&lt;/code&gt; 파일로 변환되게 됩니다. 이를 위해 현재 어떤 코드를 작성하는지에 대한 것을 지정합니다. &lt;code&gt;react&lt;/code&gt;라면 react 처리가가 최종적으로 코드를 javascript 파일로 변환시킵니다.
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;$ yarn add -D eslint-plugin-react-native&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// .eslintrc
{
    &quot;env&quot;: {
        &quot;react-native/react-native&quot;: true,
    },
    &quot;extends&quot;: [
    &quot;@react-native-community&quot;,
    &quot;eslint:recommended&quot;,
    &quot;plugin:react/recommended&quot;,
    &quot;plugin:@typescript-eslint/recommended&quot;,
    ],
...
&quot;plugins&quot;: [
   &quot;react&quot;,
   &quot;react-native&quot; 
]
....
{
  &quot;rules&quot;: {
    &quot;react-native/no-unused-styles&quot;: 2,
    &quot;react-native/split-platform-components&quot;: 2,
    &quot;react-native/no-inline-styles&quot;: 2,
    &quot;react-native/no-color-literals&quot;: 2,
    &quot;react-native/no-raw-text&quot;: 2,
    &quot;react-native/no-single-element-style-arrays&quot;: 2,
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;또한, &lt;a href=&quot;https://github.com/Intellicode/eslint-plugin-react-native&quot;&gt;react-native&lt;/a&gt; 관련 플러그인을 설치하여 환경을 추가하여 react-native 관련 몇가지 &lt;code&gt;rules&lt;/code&gt;을 추가설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1649910884485&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install --save-dev eslint-plugin-react-native&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;rules&lt;/b&gt; - &lt;code&gt;eslint&lt;/code&gt; 기본 제공 룰과 다양한 커스텀 룰을 설정할 수 있습니다. 룰에 부여되는 숫자는 해당 룰을 어겼을 때 어떻게 처리할 건지를 의미합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0 : 그냥둠&lt;/li&gt;
&lt;li&gt;1: 경고&lt;/li&gt;
&lt;li&gt;2: error&lt;/li&gt;
&lt;/ul&gt;
다음은 지금까지 설정한 결과입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;// .eslintrc
module.exports = {
  &quot;env&quot;: {
      &quot;es2021&quot;: true,
      &quot;node&quot;: true,
      &quot;react-native/react-native&quot;: true,
  },
  &quot;extends&quot;: [
      &quot;eslint:recommended&quot;,
      &quot;plugin:react/recommended&quot;,
      &quot;plugin:@typescript-eslint/recommended&quot;
  ],
  &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
  &quot;parserOptions&quot;: {
      &quot;ecmaFeatures&quot;: {
          &quot;jsx&quot;: true
      },
      &quot;ecmaVersion&quot;: &quot;latest&quot;,
      &quot;sourceType&quot;: &quot;module&quot;
  },
  &quot;plugins&quot;: [
      &quot;react&quot;,
      &quot;react-native&quot;,
      &quot;@typescript-eslint&quot;
  ],
  &quot;rules&quot;: {
    &quot;react-native/no-unused-styles&quot;: 2,
    &quot;react-native/split-platform-components&quot;: 2,
    &quot;react-native/no-inline-styles&quot;: 2,
    &quot;react-native/no-color-literals&quot;: 2,
    &quot;react-native/no-raw-text&quot;: 2,
    &quot;react-native/no-single-element-style-arrays&quot;: 2,
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 많은 설정을 문서를 보면서 천천히 하면 됩니다.&lt;/p&gt;
&lt;div class=&quot;jb-adsense-cm&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정 후 &lt;code&gt;react-native&lt;/code&gt;의 기본 예제의 &lt;code&gt;App.tsx&lt;/code&gt;에&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b50xyn/btrzelPMePx/b34sw6JWw4pCQm8zVRTA01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b50xyn/btrzelPMePx/b34sw6JWw4pCQm8zVRTA01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b50xyn/btrzelPMePx/b34sw6JWw4pCQm8zVRTA01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb50xyn%2FbtrzelPMePx%2Fb34sw6JWw4pCQm8zVRTA01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;416&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;Raw text (Edit) cannot be used outside of a &amp;lt;Text&amp;gt; tageslint(react-native/no-raw-text)&lt;/code&gt; 가 발생합니다. 태그를 벗어나 텍스트를 작성해서 그렇습니다. 사실 이 코드는 따로 &lt;code&gt;Section&lt;/code&gt;이라는 컴포넌트를 만들어 결론적으로는 &lt;code&gt;Text&lt;/code&gt;태그로 모든 텍스트를 감싸게 되므로 문제는 없습니다. 만약 이 설정이 싫다면 이 &lt;code&gt;rule&lt;/code&gt;을 지우거나 해당 파일에서만 적용되지 않도록 하시면 됩니다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5288775656103953&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;eslint 룰 추가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.eslintrc.js&lt;/code&gt; 파일에 &lt;code&gt;rules&lt;/code&gt;에 다음 속성을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;...
   &quot;rules&quot;: {
       ...
      curly: &quot;error&quot;
    }
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;curly&lt;/b&gt;는 중괄호를 뜻한다. &lt;b&gt;&lt;i&gt;curly brakets&lt;/i&gt;&lt;/b&gt; 또는 &lt;b&gt;&lt;i&gt;curly braces&lt;/i&gt;&lt;/b&gt;라고도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 if문을 {}없이 쓰지 못하도록 하는 설정입니다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;const flag = true;
if (flag) console.log(&quot;true&quot;); &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 자주 사용하지만 중괄호 없이 사용할 경우 실수하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const flag = true;
let a = 0
if (flag) a+=1;
console.log('a value changed'); // 언제나 실행됨.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;flag&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt; 때만 &lt;code&gt;a value changed&lt;/code&gt;를 콘솔에 출력하고 싶었지만 if 문에 중괄호가 없으므로 이 출력은 if문에 포함되지 않게되어 항상 출력됩니다.&lt;/p&gt;
&lt;/div&gt;</description>
      <category>프로그래밍/React-Native</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/737</guid>
      <comments>https://thrillfighter.tistory.com/737#entry737comment</comments>
      <pubDate>Tue, 12 Apr 2022 16:54:07 +0900</pubDate>
    </item>
    <item>
      <title>javascript setter/getter naming convention</title>
      <link>https://thrillfighter.tistory.com/736</link>
      <description>&lt;p&gt;기본적인 자바스크립트 객체의 속성에 접근은 아무런 제한없이 이루어지면 속성의 변환도 자유롭다.&lt;br&gt;이런 자유로운 접근은 간혹 잘못된 입력을하는 등의 실수를 초래한다.&lt;br&gt;어떤 &lt;strong&gt;객체의 속성에 넣을 값의 범위를 알거나 제한할 필요가 있다면&lt;/strong&gt; 속성에 직접 접근은 제한하여 최대한 안전하게 속성값을 사용하도록 만드는 것이 중요한다.&lt;br&gt;이런 역할을 하는 것이 &lt;strong&gt;setter/getter&lt;/strong&gt;이다.&lt;br&gt;setter/getter는 대부분의 언어들이 문법적으로 지원하며 사용방법도 비슷한다.&lt;br&gt;이렇게 setter/getter를 사용하면 속성을 비교적 안전하게 사용하도록 하고 좀 더 정교한 추가적인 처리를 할 수 있다.&lt;br&gt;이 글에서는 setter/getter를 사용하는 핵심을 &lt;strong&gt;객체 리터럴&lt;/strong&gt; 과 &lt;strong&gt;클래스&lt;/strong&gt; 방식 두가지로 설명한다.&lt;br&gt;먼저 property를 가진 객체를 생각해보자.&lt;/p&gt;
&lt;h1&gt;javascript 객체 생성 2가지 방법&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 객체 리터럴
console.log(&amp;#39;객체 리터럴&amp;#39;)
const obj = {
    count:0,
}

console.log(obj.count); //0

console.log(&amp;quot;----------&amp;quot;)

// 클래스
console.log(&amp;#39;클래스&amp;#39;)
class Cobj {
    constructor() {
        this.count = 0
    }
}

const obj1 = new Cobj()
console.log(obj1.count) //0&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;결과&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;방법1
0
----------
방법2
0&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;이 코드에 &lt;strong&gt;&lt;em&gt;setter/getter&lt;/em&gt;&lt;/strong&gt;를 추가해보자.&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 객체 리터럴
console.log(&amp;#39;객체 리터럴&amp;#39;)
const obj = {
    count:0,
    /* getter / setter 추가 */
    get count() {
        return this.count
    },
    set count(count) {
        if (count &amp;lt; 0) {
            console.log(&amp;#39;not allow negative value&amp;#39;);
        } else {
            this.count = count
        }
    }

}

console.log(obj.count); 

console.log(&amp;quot;----------&amp;quot;)

// 클래스
console.log(&amp;#39;클래스&amp;#39;)
class Cobj {
    constructor() {
        this.count = 0
    }
    /* getter / setter 추가 */
    get count() {
        return this.count
    }
    set count(count) {
        if (count &amp;lt; 0) {
            console.log(&amp;#39;not allow negative value&amp;#39;);
        } else {
            this.count = count
        }
    }
}

const obj1 = new Cobj()
console.log(obj1.count) &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드는 기존 코드에 아무런 수정없이 &lt;strong&gt;getter/setter&lt;/strong&gt;를 추가하였다. 그런데 기존 &lt;code&gt;count&lt;/code&gt; 속성과 같은 이름은 &lt;code&gt;setter/getter&lt;/code&gt;를 추가한 것이 문제가 된다.&lt;br&gt;이 코드를 실행하면 문제가 발생한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;결과&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;...
RangeError: Maximum call stack size exceeded
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;recursive 호출이 일어난다. 말 그대로 스택 오버플로우다. 해설하면 &lt;code&gt;obj.count&lt;/code&gt;의 &lt;code&gt;count&lt;/code&gt;는 애초에 몇 개인지를 기록하는 number 형 속성으로 사용했어야 한다. 그런데 &lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;와 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt; 함수를 같은 이름인 &lt;code&gt;count&lt;/code&gt;로 한 것 때문에 문제가 발생한 것아다.&lt;br&gt;그런데 &lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt;와 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;는 외부에서 접근할 이름이므로 &lt;code&gt;count&lt;/code&gt;로 이름 짓는 것이 상식상 자연스럽기 때문에 이렇게 한 것이다. &lt;strong&gt;setter/getter&lt;/strong&gt;의 이름은 외부에서 사용할 때 애초에 의도한 &lt;em&gt;number&lt;/em&gt; 속성인 &lt;code&gt;count&lt;/code&gt;인 것 처럼 보이도록하는 것이 좋기 때문이다.&lt;br&gt;이런 경우 외부에 보여지는 &lt;strong&gt;getter/setter&lt;/strong&gt;의 이름은 &lt;code&gt;count&lt;/code&gt;로 이름 짓고 실제 &lt;em&gt;number&lt;/em&gt; 속성인 &lt;code&gt;count&lt;/code&gt;는 &lt;code&gt;_count&lt;/code&gt;와 같이 언더바를 붙여 준다. 일종의 &lt;strong&gt;naming convention&lt;/strong&gt;으로 봐도 좋으며 대부분의 다른 언어들에서도 각각의 네이밍 컨벤션을 가지고 있다.&lt;br&gt;다음 코드는 네이밍 컨벤션에 맞게 실제 number 속성인 &lt;code&gt;count&lt;/code&gt;를 &lt;code&gt;_count&lt;/code&gt;로 변경한 코드다.&lt;/p&gt;
&lt;h2&gt;네이밍 컨벤션을 사용하여 stack size 에러 해결&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 객체 리터럴
console.log(&amp;#39;객체 리터럴&amp;#39;)
const obj = {
    _count:0,
    /* getter / setter 추가 */
    get count() {
        return this._count
    },
    set count(value) {
        if (value &amp;lt; 0) {
            console.log(&amp;#39;not allow negative value&amp;#39;);
        } else {
            this._count = value
        }
    }

}

console.log(obj.count); //0
obj.count = 33
console.log(obj.count); //0

console.log(&amp;quot;----------&amp;quot;)

// 클래스
console.log(&amp;#39;클래스&amp;#39;)
class Cobj {
    constructor() {
        this._count = 0
    }
    /* getter / setter 추가 */
    get count() {
        return this._count
    }
    set count(value) {
        if (value &amp;lt; 0) {
            console.log(&amp;#39;not allow negative value&amp;#39;);
        } else {
            this._count = value
        }
    }
}

const obj1 = new Cobj()
console.log(obj1.count) //0
obj1.count = 33
console.log(obj1.count) //33&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;결과&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;객체 리터럴
0
33
----------
클래스
0
33&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다시 &lt;strong&gt;&lt;em&gt;setter/getter&lt;/em&gt;&lt;/strong&gt;의 사용 의미를 곰곰히 생각해보면, &lt;strong&gt;대상이 되는 속성에 직접 접근을 막고 제한을 두기 위함&lt;/strong&gt;이다.&lt;br&gt;그럼에도 위 코드에서&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;obj._count = 999; &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;와 같은 코드도 &lt;code&gt;_count&lt;/code&gt;의 값에 접근, 변경시킬 수 있다.&lt;br&gt;애초에 본래의 목적을 이루기 위헤서는 &lt;code&gt;_count&lt;/code&gt;에 접근을 막아야 한다.&lt;br&gt;언어적인 한계로 인해 &lt;strong&gt;python&lt;/strong&gt;, 과 &lt;strong&gt;javascript&lt;/strong&gt;와 같은 언어는 이를 네이밍 컨벤션을 통해서 어느정도 해결하는 것이다.&lt;br&gt;또한 자바스크림트에서 이를 막기 위한 최소한의 노력은 &lt;code&gt;typescript&lt;/code&gt;를 사용하는 것이다. constructor 에서 &lt;code&gt;_count&lt;/code&gt;를 private로 선언하고 외부 접근 시 문법 에러를 알려주는 것이다. 물론 타입스크립트는 자바스크립트에 타입 가이드를 입힌 형태일 뿐 결국 나중에 생성되는 것은 자바스크립트이므로 이런 접근을 언어적 차원에서 막는 것은 아니다.&lt;/p&gt;
&lt;h2&gt;클래스 객체 생성 방식 코드를 더 자연스럽게 수정해 보기.&lt;/h2&gt;
&lt;p&gt;이 방식은 &lt;code&gt;javascript&lt;/code&gt;나 &lt;code&gt;python&lt;/code&gt;과 같이 동적으로 객체에 속성을 추가할 수 있는 언어에서 유효한 방식이다.&lt;br&gt;앞서 코드는 네이밍 컨벤션에 의해 &lt;code&gt;_count&lt;/code&gt;라는 속성을 객체 내부에 직접 정의했다.&lt;br&gt;이 방식을 내부에서도 &lt;strong&gt;&lt;em&gt;setter/getter&lt;/em&gt;&lt;/strong&gt;를 사용하여 정의할 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 클래스
console.log(&amp;#39;클래스&amp;#39;)
class Cobj {
    constructor() {
        this.count = 0
    }
    /* getter / setter 추가 */
    get count() {
        return this._count
    }
    set count(value) {
        if (value &amp;lt;= 0) {
            console.log(&amp;#39;not allow negative value&amp;#39;);
        } else {
            this._count = value
        }
    }
}

const obj1 = new Cobj()
console.log(obj1.count) //1
obj1.count = 33
console.log(obj1.count) //33

obj1._count=999;
console.log(obj1._count) // 999&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이전 코드에서 변한 부분은 객체 내부에서 &lt;code&gt;count&lt;/code&gt; 속성을 초기화 하는 부분이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class Cobj {
    constructor() {
        this.count = 0 // 기존 this._count = 0;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;결과&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;클래스
1
33
999&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 하면 &lt;code&gt;this.count&lt;/code&gt; 의 &lt;code&gt;count&lt;/code&gt;는 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt;를 사용하게 된다. 그리고 &lt;strong&gt;&lt;em&gt;setter&lt;/em&gt;&lt;/strong&gt; 내부에서 &lt;code&gt;_count&lt;/code&gt;를 동적으로 생성하는 것이다. 좀 더 세련된 코드로 변경된 것 같다.&lt;/p&gt;
&lt;h2&gt;객체 리터럴 방식의 count 초기화 문제&lt;/h2&gt;
&lt;p&gt;주의할 점은 &lt;strong&gt;객체 리터럴&lt;/strong&gt; 방식의 경우 앞서 방식으로 사용하지 말아야 한다. 초가화 동작 방식이 다르다. 해보면 알겠지만 클래스의 경우처럼 초기화가 이루어지지 않는다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const obj = {
  count: 1 // setter를 경우하지 않고 속성 count가 1로 초기화
  ...
  get count() {
    ...
  }
  set count(value) {
    ...
  }&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;결과&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;객체 리터럴
undefined
33
777&lt;/code&gt;&lt;/pre&gt;
객체 리터럴로 객체를 초기화 하면 속성 &lt;code&gt;count&lt;/code&gt;와 &lt;strong&gt;&lt;em&gt;setter/getter&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;count&lt;/code&gt; 가 따로 객체에 존재하게 된다. 외부에서는 &lt;strong&gt;&lt;em&gt;getter&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;count&lt;/code&gt;가 있으므로 초기화된 속성값 &lt;code&gt;count&lt;/code&gt;에 접근 할 수 없다.&lt;br&gt;초기화 방식의 차이는 있으나 초기화 후의 동작은 클래스와 동일하게 동작한다. 따라서 객체 리터럴 방식을 사용할 때는 객체 속성을 _count와 같은 형식으로 쓰는 것이 좋다.&lt;br&gt;또한, 이 두 방식이 헷갈린다면 객체 속성은 &lt;em&gt;_count&lt;/em&gt;로 통일하여 언더바를 붙여 사용하자.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/javascript</category>
      <category>getter</category>
      <category>JavaScript</category>
      <category>setter</category>
      <category>TypeScript</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/736</guid>
      <comments>https://thrillfighter.tistory.com/736#entry736comment</comments>
      <pubDate>Tue, 12 Apr 2022 12:46:59 +0900</pubDate>
    </item>
    <item>
      <title>리눅스(Rocky Linux)에서 rust 설치 및 VSCode 설정</title>
      <link>https://thrillfighter.tistory.com/731</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 설치지 과정이지만 윈도우 설치에 대한 내용도 같이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;러스트의 설치는 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.rust-lang.org/tools/install&quot;&gt;공식문서 참고&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ source ~/.cargo/env&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치를 마친 후 설치 확인&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ rustc --version
rustc 1.60.0 (7737e0b5c 2022-04-04)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 보면 일부 러스트 패키지가 C코드에 의존적이기 때문에 &lt;b&gt;&lt;i&gt;링커를 실행할 수 없다&lt;/i&gt;&lt;/b&gt;는 에러가 발생할 수 있다고 한다. 따라서 C컴파일 환경을 구성하는 것이 좋다. 이 부분은 windows 사용자와 linux 사용자의 구성방법이 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;C 개발환경 설치&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;windows 사용자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://visualstudio.microsoft.com/ko/downloads/&quot;&gt;https://visualstudio.microsoft.com/ko/downloads/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Visual Studio Tools 다운로드 - Windows, Mac, Linux용 무료 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio IDE 또는 VS Code를 무료로 다운로드하세요. Windows 또는 Mac에서 Visual Studio Professional 또는 Enterprise Edition을 사용해 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://visualstudio.microsoft.com/ko/downloads/&quot;&gt;https://visualstudio.microsoft.com/ko/downloads/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649508294465&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Visual Studio Tools 다운로드 - Windows, Mac, Linux용 무료 설치&quot; data-og-description=&quot;Visual Studio IDE 또는 VS Code를 무료로 다운로드하세요. Windows 또는 Mac에서 Visual Studio Professional 또는 Enterprise Edition을 사용해 보세요.&quot; data-og-host=&quot;visualstudio.microsoft.com&quot; data-og-source-url=&quot;https://visualstudio.microsoft.com/ko/downloads/&quot; data-og-url=&quot;https://visualstudio.microsoft.com/ko/downloads/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dfBA9B/hyNYJ3qxXb/lWTbZORKp1SZgbvM8uv6CK/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/l3cg7/hyNYBj2KvZ/b57FUa7HS40zSrb1JC7ow0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628&quot;&gt;&lt;a href=&quot;https://visualstudio.microsoft.com/ko/downloads/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://visualstudio.microsoft.com/ko/downloads/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dfBA9B/hyNYJ3qxXb/lWTbZORKp1SZgbvM8uv6CK/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/l3cg7/hyNYBj2KvZ/b57FUa7HS40zSrb1JC7ow0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Visual Studio Tools 다운로드 - Windows, Mac, Linux용 무료 설치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Visual Studio IDE 또는 VS Code를 무료로 다운로드하세요. Windows 또는 Mac에서 Visual Studio Professional 또는 Enterprise Edition을 사용해 보세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;visualstudio.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 링크에서 Visual Studio 20xx용 Build Tools를 설치한 하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리눅스 사용자라면(Rocky linux 등)&lt;/h3&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo yum groupinstall &quot;Development Tools&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;VSCode 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언어확장 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장 프로그램을 설치해야한다. 확장 프로그램 검색에 `rust`라고 입력하면 다음과 같은 확장들이 보인다. 이 확장들은 언어를 지원하여 컴파일 및 문법체크, 자동완성등을 해주는 확장이라 보면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-09 21-57-33.png&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RUvs3/btryV3ITpjQ/miFd3TtmNPISZZoKalq9o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RUvs3/btryV3ITpjQ/miFd3TtmNPISZZoKalq9o0/img.png&quot; data-alt=&quot;vscode extension for rust&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RUvs3/btryV3ITpjQ/miFd3TtmNPISZZoKalq9o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRUvs3%2FbtryV3ITpjQ%2FmiFd3TtmNPISZZoKalq9o0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;476&quot; height=&quot;314&quot; data-filename=&quot;Screenshot from 2022-04-09 21-57-33.png&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vscode extension for rust&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평점을 보면&lt;b&gt; Rust&lt;/b&gt;의 경우&lt;i&gt;&lt;b&gt; 3.5&lt;/b&gt;&lt;/i&gt; &lt;b&gt;rust-analizer&lt;/b&gt;의 경우 &lt;i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/i&gt;다. 리뷰를 보면 &lt;b&gt;Rust&lt;/b&gt;가 잘 동작하지 않는다고 &lt;b&gt;rust-analyzer&lt;/b&gt;를 설치하라고 한다. &lt;b&gt;rust-analizer&lt;/b&gt;를 설치하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;환경 세팅&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;workspace는 프로젝트 단위로 설정해야한다. 예를들어&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1649594606190&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ cargo new --bin Myproject&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 프로젝트를 생성했다면 &lt;b&gt;Myproject&lt;/b&gt; 디렉토리가 &lt;i&gt;워크스페이스&lt;/i&gt;가 되어야 한다. 그렇지 않으면 &lt;b&gt;rust-analizer&lt;/b&gt;는 &lt;b&gt;Cargo.toml&lt;/b&gt; 파일을 찾지 못하여 &lt;b&gt;rust-analyzer failed to discover workspace&lt;/b&gt; 메세지가 뜨고 동작하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Ctrl + Shift + b&lt;/b&gt; 를 누르면 vscode 팔레트에 &lt;b&gt;cargo build&lt;/b&gt; 와&lt;b&gt; cargo check&lt;/b&gt; 가 보인다. 하나를 클릭하면 &lt;b&gt;task.json&lt;/b&gt;이 자동 생성되다. 이 두 명령을 모두 &lt;b&gt;task.json&lt;/b&gt;에 넣어 단축명령시마다 선택하여 실행하게 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;b&gt;Rust&lt;/b&gt; 확장도 동작은 되지만, 외부 패키지 자동완성이 제대로 동작하지 않은 듯 하다. 내가 잘 못 사용해서일 수도...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버거 확장 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버거 확장인 &lt;b&gt;codeLLDB&lt;/b&gt;를 설치한다. 이 확장은 C++, Rust 등과 같은 컴파일 언어들을 지원한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-09 22-07-17.png&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7fOdc/btryRmh7srs/5qq0BLD9g07iEeN8Jk1AqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7fOdc/btryRmh7srs/5qq0BLD9g07iEeN8Jk1AqK/img.png&quot; data-alt=&quot;vscode extension CodeLLDB&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7fOdc/btryRmh7srs/5qq0BLD9g07iEeN8Jk1AqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7fOdc%2FbtryRmh7srs%2F5qq0BLD9g07iEeN8Jk1AqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;312&quot; data-filename=&quot;Screenshot from 2022-04-09 22-07-17.png&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vscode extension CodeLLDB&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버거 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode에서 디버거의 설정은 &lt;i&gt;&lt;b&gt;launch.json&lt;/b&gt;&lt;/i&gt;에서 담당한다. 이 설정은 워크스페이스마다 해야하는 것이며 &lt;b&gt;워크스페이스&lt;/b&gt;는 보통은 VSCode가 실행되는 루트 디렉토리라 볼 수 있다.(워크스페이스에는 .vscode 라는 디렉토리가 만들어진다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.vscode 이 디렉토리에에 lanuch.json 이 없다면 &lt;b&gt;F5&lt;/b&gt;를 눌러 디버깅을 시작하여 기본 &lt;b&gt;launch.json&lt;/b&gt;을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅 최초 실행시 기본으로 만들어진 &lt;b&gt;launch.json&lt;/b&gt;는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1649512493864&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    &quot;version&quot;: &quot;0.2.0&quot;,
    &quot;configurations&quot;: [
        {
            &quot;type&quot;: &quot;lldb&quot;,
            &quot;request&quot;: &quot;launch&quot;,
            &quot;name&quot;: &quot;Debug executable 'guessing_game'&quot;,
            &quot;cargo&quot;: {
                &quot;args&quot;: [
                    &quot;build&quot;,
                    &quot;--bin=guessing_game&quot;,
                    &quot;--package=guessing_game&quot;
                ],
                &quot;filter&quot;: {
                    &quot;name&quot;: &quot;guessing_game&quot;,
                    &quot;kind&quot;: &quot;bin&quot;
                }
            },
            &quot;args&quot;: [],
            &quot;cwd&quot;: &quot;${workspaceFolder}&quot;
        },
        {
            &quot;type&quot;: &quot;lldb&quot;,
            &quot;request&quot;: &quot;launch&quot;,
            &quot;name&quot;: &quot;Debug unit tests in executable 'guessing_game'&quot;,
            &quot;cargo&quot;: {
                &quot;args&quot;: [
                    &quot;test&quot;,
                    &quot;--no-run&quot;,
                    &quot;--bin=guessing_game&quot;,
                    &quot;--package=guessing_game&quot;
                ],
                &quot;filter&quot;: {
                    &quot;name&quot;: &quot;guessing_game&quot;,
                    &quot;kind&quot;: &quot;bin&quot;
                }
            },
            &quot;args&quot;: [],
            &quot;cwd&quot;: &quot;${workspaceFolder}&quot;
        }
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 &lt;b&gt;guessing_name&lt;/b&gt;은 생성된 rust 프로젝트 이름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 내용을 따로 손볼 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드에 &lt;b&gt;BreakPoint&lt;/b&gt;를 만든 후&amp;nbsp; &lt;b&gt;F5&lt;/b&gt;를 눌러 디버깅이 제대로 동작하는지 확인하면 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기타 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;inlay hints 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inlay 설정은 러스트 함수에 전달될 파라미터나 변수의 타입을 보여주는 기능이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-13 22-25-19.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1tp2e/btrzjos2roW/COU3fn8vnoBNeHX8vn8ke1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1tp2e/btrzjos2roW/COU3fn8vnoBNeHX8vn8ke1/img.png&quot; data-alt=&quot;inlay hints 활성화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1tp2e/btrzjos2roW/COU3fn8vnoBNeHX8vn8ke1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1tp2e%2Fbtrzjos2roW%2FCOU3fn8vnoBNeHX8vn8ke1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;338&quot; data-filename=&quot;Screenshot from 2022-04-13 22-25-19.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;inlay hints 활성화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-13 22-25-35.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OPnhr/btrzjZmepEa/7UxgWGPM3kPJq1fYSBUbuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OPnhr/btrzjZmepEa/7UxgWGPM3kPJq1fYSBUbuK/img.png&quot; data-alt=&quot;inlay hints 비활성화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OPnhr/btrzjZmepEa/7UxgWGPM3kPJq1fYSBUbuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOPnhr%2FbtrzjZmepEa%2F7UxgWGPM3kPJq1fYSBUbuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;341&quot; data-filename=&quot;Screenshot from 2022-04-13 22-25-35.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;inlay hints 비활성화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능이 꽤 유용하지만 가독성으 나빠져서 이 기능을 토글로 만들 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ctrl + shift + p&lt;/b&gt; -&amp;gt; &lt;i&gt;open keyboard shortcusts&lt;/i&gt; 를 입력하고 메뉴를 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;rust analyzer: Toggle inlay hint &lt;/i&gt;라고 치면 해당 기능 목록이 검색된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-13 22-31-27.png&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw76ft/btrzifKnk8q/CHGuPKQG9WsjWRtuisGm00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw76ft/btrzifKnk8q/CHGuPKQG9WsjWRtuisGm00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw76ft/btrzifKnk8q/CHGuPKQG9WsjWRtuisGm00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw76ft%2FbtrzifKnk8q%2FCHGuPKQG9WsjWRtuisGm00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;263&quot; data-filename=&quot;Screenshot from 2022-04-13 22-31-27.png&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 더블클릭 후 원하는 숏컷을 만들면 된다. 이미 사용되는 숏컷인지 테스트 해보고 사용할 것!&lt;/p&gt;</description>
      <category>프로그래밍/rust</category>
      <category>rocky linux</category>
      <category>rust</category>
      <category>vscode</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/731</guid>
      <comments>https://thrillfighter.tistory.com/731#entry731comment</comments>
      <pubDate>Sat, 9 Apr 2022 23:03:49 +0900</pubDate>
    </item>
    <item>
      <title>리눅스에서 USB가 안보일 때(인식이 안될 때).</title>
      <link>https://thrillfighter.tistory.com/729</link>
      <description>&lt;p&gt;얼마전 USB 부팅 디스크를 만들다가 USB가 시스템에서 보이지 않는 현상이 일어났다.&lt;/p&gt;
&lt;p&gt;분명 나의 실수일 듯 한데 원인은 모르겠고 GUI 환경에서는 보이지 않았으나 다음 명령을 치면 콘솔에서는 장치가 확인이 되었다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ lsblk
NAME                    MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda                       8:0    0   7.3T  0 disk 
└─pgdata_sub-pgdata_sub 253:0    0   7.3T  0 lvm  
sdb                       8:16   1  30.2G  0 disk 
nvme0n1                 259:0    0 953.9G  0 disk 
├─nvme0n1p1             259:1    0   600M  0 part /boot/efi
├─nvme0n1p2             259:2    0     1G  0 part /boot
├─nvme0n1p3             259:3    0  15.7G  0 part [SWAP]
├─nvme0n1p4             259:4    0    70G  0 part /
└─nvme0n1p5             259:5    0 866.6G  0 part /home&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;장치 이상은 아니고 마운트 포인트가 잡히지 않아서 그런 듯 보인다.&lt;br&gt;그렇다면 왜 마운트 포인트가 잡히지 않는 것일까? 다른 USB를 꼽으면 인식이되는 걸 보면 마운트 포인트는 수동으로 설정하지 않아도 자동으로 생성되는 것 같은데? 하는 의문이 들었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;마운트 포인트(mount point): 마운트 포인트란 리눅스 시스템에 장치를 연결하는 접점이라 볼 수 있다. 마운트 포인트는 디렉토리가 되며 장치를 디렉토리와 연결하는 것을 마운트라(mount)고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;우선 마운트 포인트가 잡히고 안잡히고의 의문은 나중에 풀기로 하고 USB를 포맷해도 괜찮을 때는 포맷하면 바로 해결이 된다.&lt;/p&gt;
&lt;h1&gt;포맷하여 해결(주의: USB 자료 다 날라감)&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;lsblk&lt;/code&gt; 명령으로 장치이름을 확인해야 한다. 내 경우는 &lt;code&gt;sdb&lt;/code&gt;라는 이름을 가진 장치가 30.2G를 가졌고 type은 disk로 USB라 볼 수 있다.&lt;br&gt;&lt;code&gt;parted&lt;/code&gt; 명령으로 다음과 같은 절차로 포맷을 진행한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[my@localhost ~]$ sudo parted /dev/sdb
[sudo] password for my: 
GNU Parted 3.2
Using /dev/sdb
Welcome to GNU Parted! Type &amp;#39;help&amp;#39; to view a list of commands.

(parted) mklabel msdos
Warning: The existing disk label on /dev/sdb will be destroyed and all data on this disk will be
lost. Do you want to continue?
Yes/No? yes                                                               

(parted) mkpart primary fat32 1MiB 100%

(parted) set 1 boot on                                                    

(parted) quit                                                             
Information: You may need to update /etc/fstab.

[my@localhost ~]$ sudo mkfs.vfat /dev/sdb
mkfs.fat 4.1 (2017-01-24)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;장치를 확인해보자.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[my@localhost ~]$ sudo fdisk -l
...
...
Disk /dev/sdb: 30.2 GiB, 32455524352 bytes, 63389696 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000
...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이제는 USB가 제대로 보인다.&lt;/p&gt;
&lt;h1&gt;마운트 포인트가 잡히지 않는 이유&lt;/h1&gt;
&lt;p&gt;작성중...&lt;/p&gt;</description>
      <category>Linux</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/729</guid>
      <comments>https://thrillfighter.tistory.com/729#entry729comment</comments>
      <pubDate>Sat, 9 Apr 2022 12:21:52 +0900</pubDate>
    </item>
    <item>
      <title>리눅스에 유니티3D 설치</title>
      <link>https://thrillfighter.tistory.com/728</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;**유니티 허브**라는 것을 설치하여 유니티 설치 및 프로젝트를 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/kr/2020.3/Manual/GettingStartedInstallingHub.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.unity3d.com/kr/2020.3/Manual/GettingStartedInstallingHub.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649417394597&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Unity 허브 설치 - Unity 매뉴얼&quot; data-og-description=&quot;Unity Hub는 모든 Unity 프로젝트 및 설치를 관리할 수 있는 관리 툴입니다. 허브를 이용하여 여러 개의 Unity 에디터 설치와 관련 컴포넌트를 관리하고, 새 프로젝트를 생성하고, 기존 프로젝트를 열 &quot; data-og-host=&quot;docs.unity3d.com&quot; data-og-source-url=&quot;https://docs.unity3d.com/kr/2020.3/Manual/GettingStartedInstallingHub.html&quot; data-og-url=&quot;https://docs.unity3d.com/kr/2020.3/Manual/GettingStartedInstallingHub.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cTH29Y/hyNXuMDzHj/BdUVoVVeuKQgXOgPjVQKD0/img.png?width=21&amp;amp;height=22&amp;amp;face=0_0_21_22&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/kr/2020.3/Manual/GettingStartedInstallingHub.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.unity3d.com/kr/2020.3/Manual/GettingStartedInstallingHub.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cTH29Y/hyNXuMDzHj/BdUVoVVeuKQgXOgPjVQKD0/img.png?width=21&amp;amp;height=22&amp;amp;face=0_0_21_22');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Unity 허브 설치 - Unity 매뉴얼&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Unity Hub는 모든 Unity 프로젝트 및 설치를 관리할 수 있는 관리 툴입니다. 허브를 이용하여 여러 개의 Unity 에디터 설치와 관련 컴포넌트를 관리하고, 새 프로젝트를 생성하고, 기존 프로젝트를 열&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.unity3d.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 자세히 나와 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 허브를 실행하면 라이센스를 등록해야하므로 유니티에 가입을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가입 후 유니티 허브에서 로그인을 눌러 &lt;b&gt;가입&lt;/b&gt; 또는 &lt;b&gt;로그인&lt;/b&gt;을 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-08 20-31-46.png&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LS7vU/btryOMIOrt6/X4WPjqxtnbOUdyL3uYkRK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LS7vU/btryOMIOrt6/X4WPjqxtnbOUdyL3uYkRK0/img.png&quot; data-alt=&quot;유니티 허브&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LS7vU/btryOMIOrt6/X4WPjqxtnbOUdyL3uYkRK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLS7vU%2FbtryOMIOrt6%2FX4WPjqxtnbOUdyL3uYkRK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;630&quot; data-filename=&quot;Screenshot from 2022-04-08 20-31-46.png&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;유니티 허브&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로그인을 하고 오늘쪽 위 설정(톱니)를 누르자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-08 20-34-49.png&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fj2EQ/btryRtt0TQK/7jExwPenCQfvLUzmp83k4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fj2EQ/btryRtt0TQK/7jExwPenCQfvLUzmp83k4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fj2EQ/btryRtt0TQK/7jExwPenCQfvLUzmp83k4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFj2EQ%2FbtryRtt0TQK%2F7jExwPenCQfvLUzmp83k4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;639&quot; data-filename=&quot;Screenshot from 2022-04-08 20-34-49.png&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;639&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- **새 라이선스 활성화**를 눌러 라이선스를 활성화 할 수 있다. 추가 선택지에서 무료 또는 유로 활성화가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 활성화를 마치면 환경설정을 나가서 &lt;b&gt;설치(install)&lt;/b&gt;을 클릭 후 &lt;b&gt;추가&lt;/b&gt;를 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-08 20-37-19.png&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csYu24/btryPbVEIWX/OVATyl09odkTtU0PJXipDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csYu24/btryPbVEIWX/OVATyl09odkTtU0PJXipDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csYu24/btryPbVEIWX/OVATyl09odkTtU0PJXipDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsYu24%2FbtryPbVEIWX%2FOVATyl09odkTtU0PJXipDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;980&quot; height=&quot;638&quot; data-filename=&quot;Screenshot from 2022-04-08 20-37-19.png&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 모듈 설치는 전부 해주었다. 용량이 꽤 크다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot from 2022-04-08 20-40-53.png&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9l8uC/btryOyD6nkU/RfsDZnvN1IuSdwFddxnHYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9l8uC/btryOyD6nkU/RfsDZnvN1IuSdwFddxnHYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9l8uC/btryOyD6nkU/RfsDZnvN1IuSdwFddxnHYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9l8uC%2FbtryOyD6nkU%2FRfsDZnvN1IuSdwFddxnHYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;974&quot; height=&quot;628&quot; data-filename=&quot;Screenshot from 2022-04-08 20-40-53.png&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 마무리되면 프로젝트를 생성하면 실행이 된다. 추가 의존성이 필요하기도 한데 구체적인 내용은 문서를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;VSCode 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode에서 개발을 하기 위해서는 자동완성 기능이 필요하다. 이를 위해&lt;b&gt; .NET Core SDK&lt;/b&gt;, .&lt;b&gt;NET Core Runtime&lt;/b&gt; 환경 설치가 필요한데 다음 링크에서 설치방법을 확인할 수 있다. (주의할 점은 OS환경별로 .NET Core가 지원하는 버전이 다르다. 예를들어 나의 환경인 &lt;b&gt;Rocky linux(centos8)의 경우 .Net Core 5 버전만 지원&lt;/b&gt;한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/dotnet/core/install/linux-centos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.microsoft.com/ko-kr/dotnet/core/install/linux-centos&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1650287082641&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CentOS에 .NET 설치 - .NET&quot; data-og-description=&quot;CentOS에 .NET SDK 및 .NET 런타임을 설치하는 다양한 방법을 보여 줍니다.&quot; data-og-host=&quot;docs.microsoft.com&quot; data-og-source-url=&quot;https://docs.microsoft.com/ko-kr/dotnet/core/install/linux-centos&quot; data-og-url=&quot;https://docs.microsoft.com/ko-kr/dotnet/core/install/linux-centos&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iNZWc/hyN5vcoaQn/tGcGHxGQTZ9tLonKdPZ2qK/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/dotnet/core/install/linux-centos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.microsoft.com/ko-kr/dotnet/core/install/linux-centos&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iNZWc/hyN5vcoaQn/tGcGHxGQTZ9tLonKdPZ2qK/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CentOS에 .NET 설치 - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CentOS에 .NET SDK 및 .NET 런타임을 설치하는 다양한 방법을 보여 줍니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode 확장 C#, Unity 관련 확장 3개 설치(순서대로),&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위와 같이 설치해도 VSCode 실행시 다음과 같은 에러가 발생할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1650287312294&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Error: Unable to find Mono. Ensure that Mono's '/bin' folder is added to your environment's PATH variable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔&amp;nbsp; Mono가 뭔지 몰랐다. 그런데 공통적으로 위와 같은 에러가 나는 이유는 2가지 원인이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Mono가 설치되지 않았거나 경로가 잘못된 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Mono가 설치되었더라도 VSCode의 설정에 &lt;b&gt;Omnisharp: Use Global Mono&lt;/b&gt; 가 &lt;b&gt;auto&lt;/b&gt;로 설정되어 있는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Mono 설치하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.mono-project.com/download/stable/#download-lin-centos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.mono-project.com/download/stable/#download-lin-centos&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1650287476819&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Download - Stable | Mono&quot; data-og-description=&quot;1 Add the Mono repository to your system The package repository hosts the packages you need, add it with the following commands. Note: the packages should work on newer Ubuntu versions too but we only test the ones listed below. Ubuntu 20.04 (amd64, armhf,&quot; data-og-host=&quot;www.mono-project.com&quot; data-og-source-url=&quot;https://www.mono-project.com/download/stable/#download-lin-centos&quot; data-og-url=&quot;https://www.mono-project.com/download/stable/#download-lin-centos&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.mono-project.com/download/stable/#download-lin-centos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.mono-project.com/download/stable/#download-lin-centos&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Download - Stable | Mono&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1 Add the Mono repository to your system The package repository hosts the packages you need, add it with the following commands. Note: the packages should work on newer Ubuntu versions too but we only test the ones listed below. Ubuntu 20.04 (amd64, armhf,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.mono-project.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후 경로를 환경변수에 추가 (export 해준다.) 해주면 된다.(Linux에서는 필요없음. mono 명령을 터미널에서 쳐보고 재대로 실행되면 export 필요없음.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;b&gt;Omnisharp: Use Global Mono&lt;/b&gt;&lt;span&gt;&lt;span&gt; 를 &lt;b&gt;always&lt;/b&gt; 로 변경해주고 재실행하니 모두 해결되었으면 자동완성이 문제없이 되었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>게임</category>
      <category>unity3d</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/728</guid>
      <comments>https://thrillfighter.tistory.com/728#entry728comment</comments>
      <pubDate>Fri, 8 Apr 2022 21:09:48 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 강좌 - python abc 추상 클래스</title>
      <link>https://thrillfighter.tistory.com/727</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 강의는 python3 문법입니다.&lt;br /&gt;&lt;b&gt;추상클래스&lt;/b&gt;라는 용어는 C++의 추상클래스와 비슷하게 이해할 수 있다. 자바(java)라면 interface라 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++이나 JAVA를 몰라도 상관은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해 추상클래스는 메소드의 구체적인 구현이 없는 클래스를 말한다. 이런 클래스를 왜 만들까 한번 10초만 고민해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추상클래스의 목적&lt;/b&gt;은 일종의 틀을 제공해주는 역할을 한다. 이 틀은 &lt;b&gt;추상클래스를 상속하는 클래스들에게 특정 메소드의 구현을 강제&lt;/b&gt;하는 효과가 있다. 앞서 말한 C++의 추상클래스라면 메소드를 _순수 가상함수_라는 형식으로 정의하여 이를 상속하는 클래스들에게 &lt;b&gt;순수 가상함수&lt;/b&gt;를 강제구현하도록 하는 지시를 하는 것이다. 자바의 경우는 &lt;b&gt;interface&lt;/b&gt;가 같은 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 본론에 들어가기 전에 파이썬에서 일반적인 상속을 알아보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# 부모 클래스
class Animal():
    def act(self):
        ... # 구현해 주세요.

# 자식 클래스
class Cat(Animal):
    def myAct(self):
        print(&quot;Do my Act!&quot;)


if __name__ == &quot;__main__&quot;:
    Cat().myAct()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행해보면 &lt;code&gt;Do my Act!&lt;/code&gt;가 콘솔에 찍힌다. 얼핏 봐서는 이 코드가 문제가 있는 코드인지 모를 수 있다. 위 예는 간단하여 &lt;code&gt;act&lt;/code&gt;가 하위클래스에 구현되지 않았다는 걸 알 수 있지만, 코드가 복잡해진다면 쉽게 발견하기 힘들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해, 이 코드의 문제점은 부모클래스(&lt;code&gt;Animal&lt;/code&gt;)에서 선언한 &lt;code&gt;act&lt;/code&gt; 메소드가 어디에서도 구현되지 않았다는 것이다. 그럼에도 이 코드는 잘 동작하며 자식클래스(&lt;code&gt;Cat&lt;/code&gt;)는 부모클래스의 코드를 들춰보지 않는 한 &lt;code&gt;act&lt;/code&gt;를 구현해야할 지도 모르기 때문에 &lt;code&gt;Animal&lt;/code&gt; 추상클래스를 제공한 자의 의도에 맞지 않는 구현이 된 것이다.&lt;/p&gt;
&lt;h1&gt;추상클래스의 중요성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 추상클래스를 인터페이스와 같다고 생각하자. 인터페이스란 일종의 포맷 스펙이다. 사용자는 특정 물건을 사용할 때 인터페이스가 맞는지 확인해야 한다. 인터페이스는 형식을 강제한다. 우리가 전기를 쓸 때 꼽는 돼지코의 모양이 동일한 규격인 것 처럼.&lt;br /&gt;다시 위 예로 돌아가자.&lt;br /&gt;누군가 &lt;code&gt;Animal&lt;/code&gt; 클래스를 만들 때 &lt;code&gt;act&lt;/code&gt; 메소드가 반드시 구현되길 원한다고 가정하자. 앞서 예제와 같은 방식으로는 &lt;code&gt;abc&lt;/code&gt; 메소드의 구현을 강제할 수 없다.&lt;br /&gt;이를 강제하기 위해 파이썬은 &lt;b&gt;&lt;i&gt;abc&lt;/i&gt;&lt;/b&gt;모듈을 제공한다. &lt;code&gt;abc&lt;/code&gt;는 &lt;b&gt;&lt;i&gt;abstract class&lt;/i&gt;&lt;/b&gt;의 약자다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from abc import ABCMeta, abstractmethod

class Animal(metaclass= ABCMeta):
    @abstractmethod
    def act(self):
        ...

class Cat(Animal):
    def myAct(self):
        print(&quot;Do my Act!&quot;)

if __name__ == &quot;__main__&quot;:
    Cat().myAct()&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앞서 예제의 &lt;code&gt;Animal&lt;/code&gt; 클래스에 &lt;code&gt;ABCMeta&lt;/code&gt; 설정을 하였다.&lt;/li&gt;
&lt;li&gt;상속받는 클래스에서 구현을 강제할 메소드에 @abstratmethod 장식자를 추가하였다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이제 다시 코드를 실행해보면 에러가 발생한다.
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;Traceback (most recent call last):
File &quot;abc.ex.py&quot;, line 13, in &amp;lt;module&amp;gt;
  Cat().myAct()
TypeError: Can't instantiate abstract class Cat with abstract methods act&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;abc&lt;/code&gt; 추상 메소드를 가진 &lt;code&gt;Cat&lt;/code&gt; 추상 클래스를 초기화 할 수 없다고?&lt;br /&gt;이 에러는 C++에서도 비슷한 상황에서 비슷한 에러가 난다. 그냥 &lt;code&gt;abc&lt;/code&gt; 추상메소드를 구현하면 해결된다고 생각하자.&lt;br /&gt;주의할 점은 이 에러는 클래스 정의단계에서는 발생하지 않는다. &lt;code&gt;Cat()&lt;/code&gt;라는 객체 생성단계에서 발생하므로 주의해야한다.&lt;/p&gt;
&lt;h1&gt;추상 메소드의 구현&lt;/h1&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from abc import ABCMeta, abstractmethod

class Animal(metaclass= ABCMeta):
    @abstractmethod
    def act(self):
        ...

class Cat(Animal):
    def act(self):  # 추상메소드 구현
        print(&quot;Do my Act!&quot;)

if __name__ == &quot;__main__&quot;:
    Cat().act()  &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상메소드를 구현하였다. 에러는 발생하지 않고 &lt;code&gt;Do my Act!&lt;/code&gt; 가 깔끔하게 출력된다.&lt;/p&gt;
&lt;h1&gt;참고&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;metaclass=ABCMeta&lt;/code&gt; 가 아닌 &lt;code&gt;ABC&lt;/code&gt;를 상속하여 구현해도 된다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;from abc import ABC, abstractmethod
class Animal(ABC):
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 설명은 &lt;a href=&quot;https://docs.python.org/ko/3/library/abc.html&quot;&gt;추상베이스클래스&lt;/a&gt;에 기반합니다.&lt;/p&gt;</description>
      <category>프로그래밍/python</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/727</guid>
      <comments>https://thrillfighter.tistory.com/727#entry727comment</comments>
      <pubDate>Fri, 8 Apr 2022 10:31:36 +0900</pubDate>
    </item>
    <item>
      <title>Rocky linux에서 Ndivia 드라이버 및 cuda 설치</title>
      <link>https://thrillfighter.tistory.com/724</link>
      <description>&lt;p&gt;CUDA 설치는 선택사항입니다.&lt;/p&gt;
&lt;p&gt;CUDA는 GPU를 사용한 병렬처리, 예를들어 텐서플로우에서 병렬처리 사용 등이 아니라면 설치안해도 됩니다.&lt;/p&gt;
&lt;h1&gt;설치하기&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;EPEL 패키지 저장소 추가하기 - epel은 추가 패키지 저장소를 뜻한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt; $ sudo dnf install epel-release&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ndivia driver 저장소를 추가하기&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  $ sudo dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;kernel devel과 header를 설치 - 그래픽 드라이버를 설치하기 위해서는 kernel devel과 header가 필요하기 때문에 설치합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  $ sudo dnf install kernel-devel-$(uname -r) kernel-headers-$(uname -r)&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nvidia 드라이버를 설치합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  $ sudo dnf install nvidia-driver nvidia-settings&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CUDA 드라이버를 설치합니다.(선택)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  $ sudo dnf install cuda-driver&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  재실행합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;설치 확인하기&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  $ nvidia-smi&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Ndivia 드라이버 삭제하기&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;$ sudo dnf remove nvidia-driver nvidia-settings cuda-driver kernel-devel-$(uname -r) kernel-headers-$(uname -r)  &lt;/code&gt;&lt;/pre&gt;</description>
      <category>Linux</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/724</guid>
      <comments>https://thrillfighter.tistory.com/724#entry724comment</comments>
      <pubDate>Wed, 6 Apr 2022 18:54:58 +0900</pubDate>
    </item>
    <item>
      <title>sql 쿼리 Join시  카테시안 곱 주의(with sqlAlchemy)</title>
      <link>https://thrillfighter.tistory.com/722</link>
      <description>&lt;p&gt;보통 JOIN 시에 조건을 주지 않았을 때 발생한다고 한다.&lt;/p&gt;
&lt;p&gt;상식적으로 생각해봐도 JOIN 조건이 없다면 기준이 되는 테이블(Outer Table)과 대상 테이블(Inner Table) 간에 관계가 만들어지지 않는다.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;postgresql&lt;/strong&gt; 쿼리에서 &lt;code&gt;JOIN&lt;/code&gt; 문이 &lt;code&gt;ON&lt;/code&gt; 조건없이 조인을 할 수 없으며, 위 내용의 조건이란 다음과 같은 쿼리를 의미한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;조건없는 조인&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SECLECT * FROM Table1, Table2;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;조건있는 조인&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT * FROM Table1, Table2 WHERE TABLE1.id = TABLE2.id;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 예의 조건없는 조인과 같이 관계(&lt;code&gt;WHERE&lt;/code&gt;)가 만들어지지 않은 채로 JOIN을 하게 되면 Error가 발생하지 않고 양 테이블의 row간에 대카테시안 곱의 방식의 결합이 일어난다. 기준 테이블 row의 개수 100, 대상 테이블 row 개수 200 이라면 카테시안 곱의 결과 20000개의 row를 가진 테이블이 결과로 반환된다. 만약 1000 * 1000 이라면 1000000 (백만) 개의 결과가 발생한다.&lt;/p&gt;
&lt;p&gt;개발 초기에 데이터의 양이 적을 때는 이런 오류를 발견하지 못할 수도 있다. 그래서 더욱 주의해야한다.SQLAlchemy로 쿼리를 만들 때&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;query_select = session.query( Tbale1, Table2, Table3)
query_join = query_select.join(Table1, condision1 ) # 카테시안 곱 존재
query_join = query_join.join(Table2, condisions2 ) # 카테시안 곱 존재
query_join = query_join.join(Table3, condision3 ) # 카테시안 곱 없음
query_result = query_join.one()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드에서 처음 &lt;code&gt;query_select&lt;/code&gt;에서 명시된 테이블은 &lt;code&gt;Table1&lt;/code&gt;, &lt;code&gt;Table2&lt;/code&gt;, &lt;code&gt;Table3&lt;/code&gt;의 3개다.&lt;br&gt;이 세개의 테이블은 쿼리 결과에 선택되는 테이블이며 &lt;code&gt;JOIN&lt;/code&gt;을 하기 전까지 테이블간에 어떤 관계도 없다. 따라서 결과는 &lt;strong&gt;카테시안 곱&lt;/strong&gt; 이 된다.&lt;br&gt;이런 결과를 의도적일 수도 있지만 일반적인 경우는 의도적이지 않다.&lt;br&gt;이들 간에 관계는 &lt;code&gt;WHERE&lt;/code&gt;  또는 &lt;code&gt;JOIN&lt;/code&gt;문을 사용하여 만들 수 있다.&lt;/p&gt;
&lt;p&gt;query_select에서 선택된 테이블에 대해서는 반드시 &lt;code&gt;JOIN&lt;/code&gt;을 하도록 하자.&lt;br&gt;또는 &lt;code&gt;query_select&lt;/code&gt;에서 테이블 간 관계를 정의하는 &lt;code&gt;filter&lt;/code&gt;를 넣을 수도 있겠다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;query_select = session.query( Tbale1, Table2, Table3).filter( conditions )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;마지막으로 &lt;code&gt;explain&lt;/code&gt;을 사용하여 직접 실행될 쿼리의 계획을 확인하도록 해보자.&lt;/p&gt;</description>
      <category>프로그래밍/database</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/722</guid>
      <comments>https://thrillfighter.tistory.com/722#entry722comment</comments>
      <pubDate>Wed, 26 Jan 2022 13:59:44 +0900</pubDate>
    </item>
    <item>
      <title>react-native-reanimated 에러관련 기록</title>
      <link>https://thrillfighter.tistory.com/721</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;react-navigation/drawer&lt;/code&gt; 6.1.6 를 사용할 때 &lt;code&gt;react-native-reanimated&lt;/code&gt; 2.x.x 을 설치하면 &lt;code&gt;warning&lt;/code&gt; 메세지가 뜬다.&lt;br /&gt;2.x.x 버전 사용을 위해 추가 설정을 하라는 메세지다.&lt;br /&gt;&lt;a href=&quot;https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation&quot;&gt;공식페이지&lt;/a&gt; 에 가보면 버전별 설정 방법이 나와있으면 그렇게 어렵지 않다.&lt;br /&gt;2.x.x 번대를 설치하고 &lt;code&gt;drawer&lt;/code&gt;를 잘 구성하고 &lt;code&gt;debug&lt;/code&gt; 모드로 테스트까지 잘 끝났다고 방심하면 안된다. 우리를 &lt;code&gt;release&lt;/code&gt; 버전이 제대로 돌아가는지도 테스트해야한다. &lt;code&gt;react-native&lt;/code&gt;가 참 개발이 좋게 되어 있지만 이렇게 &lt;code&gt;release&lt;/code&gt; 모드에서 앱이 실행시 런타임에러가 나면서 앱이 종료되는 경우가 많이 생긴다. 그래서 라이브러리를 설치한 후에는 반드시 &lt;code&gt;release&lt;/code&gt; 모드로 제대로 앱이 빌드되는지 확인한 후 개발하는 습관이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 2.x.x의 &lt;code&gt;react-native-reanimated&lt;/code&gt;는 모든 버전이 build 는 성공하지만 앱 실행시 바로 종료되는 현상이 생겼다. &lt;code&gt;sentry&lt;/code&gt;로도 어떤 에러로그도 남겨지지 않아서 단순히 라이버리 호환성 문제라고 밖에 생각하고 넘어가야했으며.&lt;br /&gt;현재는 &lt;code&gt;react-native-reanimated&lt;/code&gt; 1.13.3 버전으로 다운그레이드 하여 문제를 해결할 수 있었다.&lt;br /&gt;&lt;code&gt;react-native&lt;/code&gt; 버전은 0.64.3 이며 삽질 방지 차원에서 기록해 둔다.&lt;/p&gt;</description>
      <category>프로그래밍/React-Native</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/721</guid>
      <comments>https://thrillfighter.tistory.com/721#entry721comment</comments>
      <pubDate>Mon, 24 Jan 2022 14:03:04 +0900</pubDate>
    </item>
    <item>
      <title>sqlAlchemy, Foreign Key, relationship, remote_side 정리</title>
      <link>https://thrillfighter.tistory.com/719</link>
      <description>&lt;p&gt;문서가 너무 방대해서 다 이해하지 못한채로 대략 정리한다.&lt;/p&gt;
&lt;p&gt;sqlAlchemy는 컬럼에 Foreign Key를 설정한 후 orm을 위한 관계 설정을 relation 함수로 한다.&lt;/p&gt;
&lt;p&gt;relation 함수는 기본적으로 Foreign Key는 한개로 가정하므로 Foreign Key 설정된 컬럼을 지정할 필요가 없다.&lt;/p&gt;
&lt;p&gt;그러나 테이블 내에서 Foreign Key를 2개이상 사용할 경우는 지정해야한다.&lt;/p&gt;
&lt;p&gt;지정하는 방법은 relation 함수에서 &lt;code&gt;primaryjoin&lt;/code&gt; 설정 또는 &lt;code&gt;foreign_keys&lt;/code&gt; 를 설정하는 방법인 듯 하다.&lt;a href=&quot;https://stackoverflow.com/questions/4975793/multiple-self-referential-relationships-in-sqlalchemy&quot;&gt;참조&lt;/a&gt;, &lt;a href=&quot;https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;amp;blogId=haje01&amp;amp;logNo=130116280507&quot;&gt;2개 이상의 Foreign key&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;remote_side&lt;/code&gt;는 자기참조 관계에서 primary key 지정에 사용되는 것 같다. 지정은 반드시 &amp;quot;자신의모델(클래스명).primaykey컬럼&amp;quot; 을 한다. 그렇지 않으면 이상한 에러 발생한다고 한다. (위 스택오버플로 링크 참조)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;relationship&lt;/code&gt;의 첫 번째 인자의 값은 모델 클래스 이름 또는 모델 클래스 문자열로 지정한다. 자기 참조일 때는 자기 자신의 클래스 이름을 참조할 수 없기 때문에 클래스 이름을 문자열로 전달한다. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;back_populates&lt;/code&gt; 와 &lt;code&gt;backref&lt;/code&gt;는 모두 자신의(테이블)의 별칭을 지정하는 것 같다. 양방향&lt;code&gt;back_populates&lt;/code&gt; 인지 단방향&lt;code&gt;backref&lt;/code&gt; 으로 각각 사용되는 듯. &lt;/p&gt;</description>
      <category>프로그래밍/database</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/719</guid>
      <comments>https://thrillfighter.tistory.com/719#entry719comment</comments>
      <pubDate>Sat, 8 Jan 2022 14:22:23 +0900</pubDate>
    </item>
    <item>
      <title>react-native release 모드로 배포시 주의할 점(http 안되는 문제).</title>
      <link>https://thrillfighter.tistory.com/717</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발모드에서는 잘 되던 앱이 react-native에서 release 모드로 테스트를 하거나 build를 한 후 먹통이 되는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔히 겪는 문제는 api 호출이 제대로 안되는 경우인데, 원인은 http 프로토콜을 사용하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 react-native는 release 모드에서 http의 호출을 막는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 https를 사용하도록 설정해야하는데, 여의치 않을 때 다음과 같은 설정으로 http 모드를 허용해주는 방법을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;android/app/src/main/AndroidManifest.xml 에서&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;&amp;lt;application
      ....
      android:usesCleartextTraffic=&quot;true&quot;
      ...
             &amp;gt;
  ...
  ...
&amp;lt;/application&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;android:usesCleartextTraffic=&quot;true&quot;&lt;br /&gt;추가&lt;/p&gt;</description>
      <category>프로그래밍/React-Native</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/717</guid>
      <comments>https://thrillfighter.tistory.com/717#entry717comment</comments>
      <pubDate>Fri, 24 Dec 2021 22:58:49 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 앱 등록 프로세스 정리중...</title>
      <link>https://thrillfighter.tistory.com/716</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 React-native로 개발한 앱을 Android 테스트용으로 등록하는 과정(2021/12/23일 기준)에 대한 정리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 두 단계로 나누어 생각하기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 째로 React-Native에서의 준비단계는 abd파일 apk파일을 생성하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로 google play console에 등록하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록과정은 테스트 버전과 배포되는 공개 버전 사이에 큰 차이는 없으므로 참고가 될 것 같다.&lt;/p&gt;
&lt;h1&gt;React-Native 단계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글에 앱을 올리기 위해서 가장 많이 문제가 되는 부분이 바로 &lt;b&gt;업로드 키&lt;/b&gt; 와 &lt;b&gt;사이닝 키라는&lt;/b&gt; 개념인 것 같다.&lt;br /&gt;이 부분을 자세하게 설명한 문서는 보지 못해서 초보 앱 개발자의 경우 많이 헤멜 것이다. 문서가 있긴 하지만 이미 개념을 잘 아는 사람을 위한 설명뿐이었다.&lt;br /&gt;또한 이 부분은 구글 정책에 따라서 바뀌기 때문에 최신 정책과 기존 정책이 다르기도 해서 이미 앱을 등록한 경우와 새롭게 앱을 등록하는 경우 처리 방법이 다를 수 있어서 더욱 그렇다..&lt;br /&gt;여기에서는 앱을 현시점에 처음 올리는 경우를 가정하여 최신 정책을 다룬다. 다음은 프로세스의 핵심만 정리한 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업로드 키(upload key)&lt;br /&gt;앱을 빌드한 결과 파일(abd, apk)을 구글 플레이에 올리기 위해서는 앱이 실제 소유자에 의해서 업로드되었는지 식별할 수 있는 식별키가 필요하다. 이 개념이 업로드 키라고 보면 된다.&lt;br /&gt;업로드 키의 생성은 - &lt;code&gt;keytools&lt;/code&gt;로 하는데 자세한 명령은 아래 쪽에 다시 정리해 둔다.&lt;br /&gt;이렇게 생성한 키로 앱을 서명하면 되는데 서명하는 방법은 &lt;a href=&quot;https://dev-yakuza.posstree.com/ko/react-native/android-running-on-device/#%EC%84%9C%EB%AA%85-%ED%82%A4-%EC%84%A4%EC%A0%95&quot;&gt;서명방법&lt;/a&gt; 를 참고하여 정리해본다.&lt;br /&gt;서명이 끝났다면 빌드를 하고 업로드한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업로드 키의 생성 방법 (패스워드 입력, country를 물어보는 곳에 &lt;code&gt;KR&lt;/code&gt;....correct? 부분에서 &lt;code&gt;y&lt;/code&gt;를 입력하고 나머지는 모두 엔터만 쳐서 넘겼음.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;$ keytool -genkey -v -keystore [key-name].keystore -alias [key alias] -keyalg RSA -keysize 2048 -validity 10000

Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:
What is the name of your organizational unit?
  [Unknown]:
What is the name of your organization?
  [Unknown]:
What is the name of your City or Locality?
  [Unknown]:
What is the name of your State or Province?
  [Unknown]:
What is the two-letter country code for this unit?
  [Unknown]: KR
Is CN=*****, OU=Unknown, O=Unknown, L=*****, ST=*****, C=***** correct?
  [no]: y

Enter key password for &amp;lt;my-key-alias&amp;gt;
    (RETURN if same as keystore password):&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서명 방법 1단계&lt;br /&gt;android/gradle.properties 에 앞에서 생성한 업로드 키에 대한 정보를 넣어줘야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서명 방법 2단계&lt;br /&gt;android/app/build.gradle 파일에 다음 코드를 추가.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;...
android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}
...&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;빌드&lt;/h2&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;# cd android
# ./gradlew assembleRelease  # apk 파일 생성
./gradlew bundelRelease # abd 파일 생성&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드한 결과를 디바이스에서 테스트하는 명령&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;react-native run-android --variant=release&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Error 처리&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Expiring Daemon because JVM heap space is exhausted &lt;a href=&quot;https://stackoverflow.com/questions/56075455/expiring-daemon-because-jvm-heap-space-is-exhausted&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// gradle.properties
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;kakao 로그인 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱에서 kakao, naver, facebook과 같은 sns 로그인을 이용하려면 업로드 키와 서명키의 해시값을 등록해야 한다고 한다.&lt;br /&gt;해시값을 얻는 방법은&lt;br /&gt;구글 플레이 콘솔 - 설정 - 앱 무결성에 들어가면 (앱 서명키 인증서, 업로드 키 인증서)가 보인다. 인증서의 SHA-1 지문인증을 통해서 얻을 수 있다.&lt;br /&gt;예시(keystore로 알아내기&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;keytool -exportcert -alias upload-alias -keystore ./upload.keystore | openssl sha1 -binary | openssl base64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시(구글 콘솔 - 앱 무결성의 SHA-1 값으로 알아내기)&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;echo A1:B2:C3:D5:D1:E3:E1:AB:AC:E2:12:19:3B:1A:17:1E:1D:C6:15:A1  | xxd -r -p | openssl base64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두개의 값 (로컬에서 생성한 keystore로 해싱한 값과 앱 무결성에 있는 업로드 SHA-1 값으로 해싱한 값)이 같아야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얻은 해시값은 카카오 개발자 페이지의 자신의 앱에 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LOCAL 환경 - debug key 해시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DEVELOP, STAGING 환경 - debug 모드일 땐 debug key 해시필요, release 모드일 땐 upload key 해시 필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글 플레이에 등록된 경우 -&amp;gt; 구글 플레이에서 사이닝 키의 해시가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 개발환경의 debug, release는 dubug 키 해시, uploade 키 해시가 필요하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글플레이에 등록된 경우엔 uploade 키가 사이닝 키로 변경되므로 사이닝 키의 해시가 카카오 앱에 등록되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 카카오 앱의 TEST 버전은 2개 또는 3개의 해시가 등록되어 있을 수 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 배포용 앱에는 반드시 사이닝 키 하나만 등록되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업로드 키와 사이닝 키&lt;br /&gt;이 후로는 업로드키로 앱을 서명하여 구글에 올리면 업로드 키만 확인하고 사이닝 키로 재 사인한 APK 파일을 생성한다고 한다. 따라서 로컬에서 구글로 업로드하기 위해 빌드한 APK 파일로 실행해도 유효하지 않게 된다.&lt;br /&gt;현재 검토 중인 상태에서 Kakao 로그인이 막힌다. 검토가 안되어서 그런지는 모르겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native-kakao-loing 라이브러리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포 후 로그인 시 팅기는 문제 해결&lt;a href=&quot;https://github.com/react-native-seoul/react-native-kakao-login/issues/266&quot;&gt;참고&lt;/a&gt;, &lt;a href=&quot;https://devtalk.kakao.com/t/topic/113717&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;android/app/proguard-rules.pro&lt;/code&gt; 에 다음 추가&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;-keep class com.kakao.sdk.**.model.* { &amp;lt;fields&amp;gt;; }
-keep class * extends com.google.gson.TypeAdapter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 테스트&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검토가 필요 없으나 최초 게시 시 최대 48시간 기다려야 한다고 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;앱 등록 단계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리중...&lt;br /&gt;구글 플레이 콘솔 - 설정 - 앱 무결성 에 들어가면 두 개의 인증서를 볼 수 있다.&lt;br /&gt;그중 업로드 키 인증서는 내가 로컬에서 생성한 업로드키에 대한 인증서이다.&lt;br /&gt;앱 서명키 인증서는 구글 자체내에서 생성한 사이닝 키 인증서이다. 업로드 키 인증서는 앱을 등록할 때 앱에 서명된 업로드키를 확인하기 위한 것이며 확인이 되면 사이닝 키로 변경하여 앱을 등록하게 된다. 따라서 업로드 키는 말 그대로 업로드 시에 식별을 위한 것이므로 앱의 배포시에는 제거된다.(주의) 그래서 카카오나 네이버와 같은 API 제공업체에서 앱 사이닝키의 해시값을 필요로 할 때 업로드 키의 해시값이 아닌 사아닝 키의 해시값을 등록해줘야한다.&lt;br /&gt;(자세한 방법은 앞(kakao 로그인 설정)에서 설명했음)&lt;/p&gt;</description>
      <category>프로그래밍/React-Native</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/716</guid>
      <comments>https://thrillfighter.tistory.com/716#entry716comment</comments>
      <pubDate>Wed, 22 Dec 2021 19:00:30 +0900</pubDate>
    </item>
    <item>
      <title>비동기 호출과 ErrorBoundarry에 대해</title>
      <link>https://thrillfighter.tistory.com/715</link>
      <description>&lt;p&gt;우리는 종종 비동기 api호출을 &lt;code&gt;useEffect&lt;/code&gt; 내에서 할 경우가 있다.&lt;br&gt;이 때 &lt;code&gt;async ~ await&lt;/code&gt;을 사용한다 가정한다.&lt;br&gt;에초에 &lt;code&gt;useEffect&lt;/code&gt;에 전달되는 첫 번째 파라미터 함수는 동기함수며 동기 함수 내에서 비동기 호출을 처리를 하기 위해서는 호출할 비동기함수를 또 다른 비동기 함수로 감싸줘야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function fetchData() {
    ...
}
useEffect(()=&amp;gt; {

const func = async ()=&amp;gt; {
    const response = await fetchData();
}
func();
},[])&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;그리고 위 코드 처럼 결국엔 &lt;code&gt;func&lt;/code&gt; 함수가 비동기적으로 호출되기 때문에 호출 결과를 동기적으로 처리할 수 없다.&lt;br&gt;따라서 &lt;code&gt;fetchData&lt;/code&gt;의 결과로 &lt;code&gt;Error&lt;/code&gt;가 발생된다 하더라도 이미 &lt;code&gt;useEffect&lt;/code&gt;의 콜백 함수는 실행이 끝난 후이며 컴포넌트 렌더링까지 마쳤을 지도 모른다. 이렇게 비동기적으로 처리되는 함수의 에러에 대해서는 &lt;strong&gt;ErrorBoudndary&lt;/strong&gt; 로 에러가 전파되지 않는다는 문제가 있다.&lt;br&gt;일반적인 언어들의 비동기 처리를 생각한다면 이해가 된다.&lt;br&gt;그래서 자료를 찾아보니 다음과 같은 내용을 발견할 수 있었다.&lt;br&gt;&lt;a href=&quot;https://reactjs.org/docs/error-boundaries.html&quot;&gt;참고&lt;/a&gt;&lt;br&gt;Note&lt;/p&gt;
&lt;p&gt;Error boundaries do not catch errors for:&lt;/p&gt;
&lt;p&gt;Event handlers (learn more)&lt;br&gt;Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)&lt;br&gt;Server side rendering&lt;br&gt;Errors thrown in the error boundary itself (rather than its children)&lt;/p&gt;
&lt;p&gt;또한 스택 오버플로에서 적절할 해결 방법을 찾을 수 있었음.&lt;br&gt;&lt;a href=&quot;https://stackoverflow.com/questions/67889383/react-error-boundaries-with-useeffect-and-async-function-what-im-missing?noredirect=1&amp;amp;lq=1&quot;&gt;해결&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function MyComponent() {
  const [error, setError] = useState(null);
  if (error) {
    throw error;
  }

  useEffect(() =&amp;gt; {
    // If loading fails, save the error in state so the component throws when it rerenders
    load().catch(err =&amp;gt; setError(err));
  }, []);

  return &amp;lt;div&amp;gt;...&amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;적절하게 응용하면 된다.&lt;br&gt;에러 핸들링의 핵심은 호출할 비동기 함수에서 발생되는 에러와 메인 컴포넌트 컨택스트를 state를 통해서 연결해준 것이라고 이해하면 된다. 비동기 호출 흐름에서 에러 발생 -&amp;gt; 메인 컴포넌트의  error 상태 변경 -&amp;gt; 에러 상태의 변경으로 새롭게 컴포넌트 렌더링 중 &lt;code&gt;throw error&lt;/code&gt; 실행 -&amp;gt; &lt;strong&gt;ErrorBoundary&lt;/strong&gt; 에서 에러 잡아냄.&lt;br&gt;이 해결책이  나쁘지 않아보인다.&lt;/p&gt;</description>
      <category>프로그래밍/React</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/715</guid>
      <comments>https://thrillfighter.tistory.com/715#entry715comment</comments>
      <pubDate>Thu, 16 Dec 2021 18:39:25 +0900</pubDate>
    </item>
    <item>
      <title>오늘도 청명한 하늘과 걷기.</title>
      <link>https://thrillfighter.tistory.com/714</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요사이 미세먼지에 답답했던 하늘이 맑고 청명하게 드러났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벌써 6개월 정도를 매일매일 등산을 한다. 그래서 나에겐 그날 날씨가 중요하다. 그렇다고 날씨에 따라서 등산을 쉬는 건 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비가 많이 몰아치거나 태풍이 불지 않는 한은 매일 운동을 했다. 200여 일 중 4번 정도만 쉬고 하루 2시간은 걸었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 많게는 3시간에서 1시간 30분을 매일 땀을 흘린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운동을 좋아했지만 살아오면서 다른 많은 것들에 집중하느냐 정작 내가 좋아하는 운동, 원하는 삶을 살지 못했다는 걸 느낀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 매일 이렇게 흘리는 땀이 너무나도 좋다. 좋은 걸 알았지만 이렇게 꾸준히 매일매일 운동을 한 적은 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매일 컴퓨터 앞에 앉아서 멍하니 컴퓨터를 바라보고 있는 것보다 능률도 오른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 난 지금 살아있음을 생생하게 느낀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매일 이렇게 꾸준하게 걷기를 5개월이 넘어가니 다리에 근육이 탄탄해졌음이 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체력도 정말 좋아졌고, 특히 좋은 것은 지금 하는 일에 매우 긍정적인 영향을 준다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장기적으로 볼 때 난 매일 매일 충전을 하는 배터리가 되기 때문이다. 방전이 될 틈이 없이 난 매일 걷기를 통해서 충전을 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;걷다 보면서 많은 생각들이 천천히 정리가 되어가는 것 같다. 그러다 보니 처음 걷기를 시작했을 때와 지금의 마음가짐은 정말 다르다. 만약 걷기를 하지 않았다면 그때와 지금의 나는 별 변화가 없었을지도 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나에게 걷는 것은 운동 그 이상이다. 처음엔 홀로 걷다 보니 내 발소리만 들리는 그 걷는 시간에는 시끄러운 내 내면의 생각들이 거침없이 쏟아져 나왔던 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 언제서부터인지 모르겠다. 이런 시끄러운 생각들의 볼륨이 줄어든 것이...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;천천히 나도 모르게 어떤 생각들이 하나씩 하나씩 정리되다 보니 걷는 중 나의 시끄러운 생각들이 대부분이 사라져 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 그것이 최근 이리라.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 삶이 매우 단순한 것이라고 생각한다. 뭔가 심오하고 대단하고 우리가 알지 못하는 무언가가 있는 것이 삶일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리 인간은 억지로 그런 심오함을 탐구할 필요는 없다. 심오함을 탐구하는 것과 우리가 살아가는 이유는 다를 수 있기 때문이다. 누구나 가치관과 살아가는 방식이 다르다. 히지만 인생의 가장 공통적인 목표는 행복해지는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행복은 심오한 탐구로 이루어지는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가에게는 따뜻하고 평온한 집에서 저녁 석양을 바라보며 올드 팝송을 들으며 커피 한잔을 마시며 잠시 눈을 감고 있을 수 있는 여우 그 자체로 우리는 삶의 하나의 이유를 찾을 수 있으리라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그대의 존재 자체로 사랑해주는 가족이 있음에 감사하며 삶의 이유를 찾을 수 있으리라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삶은 복잡하게 생각하면 끝없이 복잡해진다. 복잡함에 자신을 맞춰 가다 보면 어디서 어긋났는지 모를 거대한 구조물이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔지도 모를 거친 구조물들을 모두 걷어내자. 하늘의 계획이 있다한들 우리는 그것을 알 수 없다. 복잡함은 우리를 어딘지 모를 잘못된 차원으로 끌고가버린다. 어떤 대단한 것처럼 구는 인간도 그냥 우주의 점일 뿐이다. 그들이 말하는 것은 점의 영역 그 이상을 벗어날 수가 없다. 우주의 진리를 말하는 인간도 인간이 만든 돈의 법칙을 속에서 허우적 댈 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점으로서 우리는 우리가 받아들일 수 있는 그 단순함 속에서 작은 행복 정도 하나만 찾을 수 있다면 훌륭한 인생이라 생각된다. 남의 점이 아닌 당신의 점을 살아라. 당신의 우주는 당신이 절대 창조주이기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가를 억지로 하고 있다면 그건 나 자신에게 주는 고통일 수도 있다. 하지만 나에게 의미 있는 무언가 작은 일이라도 꾸준히 하는 것은 중요하다고 본다. 심지어 매일 하는 기도처럼 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6개월간 매일 하루도 빠지지 않고 난 기도를 했다. 매일 세 가지를 기도했는데 적어도 난 그 것들이&amp;nbsp;이루어졌거나 이루어지는 것 같다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 당시엔 정말 간절하고 힘들고 절망적이었다고 생각했던 부분은 이젠 전혀 걱정하지 않는 부분이 되었다. 아니 이루어졌다. 난 이 것이 기도의 힘일지 뭔지는 알 수는 없다. 하지만 난 지금도 그것에 대해 감사기도를 매일매일 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 난 그 당시에는 간절했었고 기도를 통해 이루어졌고 또한 마음을 달래는 도움도 받았기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이젠 고마움에 기도를 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이젠 마지막 한 가지만 이루어지면 모든 것이 이루어지게 된다. 6개월이라는 시간 난 모든 것을 쏟아부었다고 생각한다. 이젠 하늘에 맞길 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 지금 이렇게 맘껏 걸을 수 있는 것에 그리고 무언가를 할 수 있음에 감사한다.&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/714</guid>
      <comments>https://thrillfighter.tistory.com/714#entry714comment</comments>
      <pubDate>Sun, 12 Dec 2021 20:29:43 +0900</pubDate>
    </item>
    <item>
      <title>단순한 삶</title>
      <link>https://thrillfighter.tistory.com/713</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 삶은 거창할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘만 생각하며 살면 되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지루할 수도 있는 삶이 단순한 삶이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 삶은 몇가지 원칙만 지키면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘을 거창하게 살지 말것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 많은 것을 할 필요가 없다. 오늘 많은 것을 하려다 보면 내일 지치게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 삶을 사는 자들은 오늘 많을 것을 하려고 하기보다는, 반드시 작은 무언가를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부를 한다면 영어문장 하나를 머리 속에 넣어 두거나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책을 읽는다면 책 속의 한 문장을 기억해 두는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인간은 본래 많은 것을 한번에 하지 못하게 되어있다. 공부, 놀기, 먹기, 쉬기, 걱정 등등 한번에 몰아서 하면 병이난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈이 많아도 한번에 쓰진 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 오늘 내가 할일을 내 욕심의 반에 반에 반에 반 이하로 낮추고 매일 꾸준히 해나가면 단순한 삶이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매일 해야할 무언가가 있고 어렵지 않고 티도나지 않을 것 같지만, 그런 순간들은 수년 뒤에는 성같이 큰 무언가가 되어 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 큰 성이 무엇이 되는지는 오늘 내가 하찮게 생각할 수도 있는 수많은 무언가들로 이루어지게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번에 이루어지는 것이 있다면 이 세상에 노력이라는 말이 생기지 않았을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성을 반드시 쌓으려고 할 필요도 없다. 단순하게 사는 것은 다른 말로 하면 패턴이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴은 철학이다. 그리고 가치관이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가치관과 패턴은 삶을 살아가는 원동력이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쌓은 성이 대단할 필요는 없다. 다만 그 성은 하루아침에 쌓은 성이 아니라는 사실은 불변한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 패턴을 만들어야 한다. 난 그렇게 생각한다.&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/713</guid>
      <comments>https://thrillfighter.tistory.com/713#entry713comment</comments>
      <pubDate>Sun, 5 Dec 2021 22:15:51 +0900</pubDate>
    </item>
    <item>
      <title>로깅 전략 대한...</title>
      <link>https://thrillfighter.tistory.com/712</link>
      <description>&lt;p&gt;로깅은 언제나 항상 중요하다.&lt;/p&gt;
&lt;p&gt;개발 중, 개발 마무리, 운영 중, 모든 상황에서 로깅이 없다면 문제점을 제대로 파악하기 어려워진다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;print(&amp;#39;&amp;lt;&amp;lt;-- here check&amp;quot;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;와 같은 방식의 코드는 쉽게 사용할 수 있고 편리하기는 하다.&lt;br&gt;하지만 실제로는 개발환경인지 테스트 환경인지 배포환경인지에 따라서 전략적인 로깅이 필요하다.&lt;/p&gt;
&lt;p&gt;그런데 코드에 로깅을 어떻게 버무려 놓으면 효율적일지는 답은 없는 듯 하다.&lt;/p&gt;
&lt;p&gt;현재 정리할 방식이 업그레이드 될 거라 생각하지만, 현재 대략적인 로깅 전략을 간단히만 정리하려고 한다.&lt;/p&gt;
&lt;h2&gt;전반적인 로깅 방향&lt;/h2&gt;
&lt;p&gt;우선 서비스는 &lt;strong&gt;AWS&lt;/strong&gt; 를 사용한다. 백엔드 서버(EC2)에서 이벤트가 발생하면 각 EC2 내에 로그 파일을 &lt;code&gt;filebeat&lt;/code&gt;를 사용해 &lt;strong&gt;ELK STACK&lt;/strong&gt; 으로 보내게 된다.&lt;br&gt;filebeat에서 * 패턴 매칭으로 &lt;code&gt;*.log&lt;/code&gt; 파일들을 긁어가도록 한다. &lt;a href=&quot;https://codingdog.tistory.com/entry/filebeat-%EC%97%90%EC%84%9C-inputs-path-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-with-glob&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;로그를 기록할 때 로그파일을 하나로 잡게 되면 하나의 파일이 비대해 지고, 무중단 서비스일 경우 다음 업데이트 때까지 로그 파일을 깔끔하게 처리하지 못할 것이다.&lt;br&gt;따라서 로그파일은 날짜별로 기록하되 기간이 7일이 넘어간 파일이라면 삭제하도록 &lt;code&gt;cron&lt;/code&gt; job을 등록해 두는 편이 좋을 것 같다.&lt;/p&gt;
&lt;p&gt;현재 궁금한 것 &lt;strong&gt;ELK STACK&lt;/strong&gt; 에서 날짜별로 나누어진 로그파일들을 하나로 집계하는 기능이 있냐는 것이다.(당연히 있을 거라 생각)&lt;/p&gt;
&lt;p&gt;여기까지 파일비트와 cron으로 백엔드에 쌓이는 로그파일을 처리하면 나머지는 &lt;strong&gt;ELK STACK&lt;/strong&gt; 에서 처리하면 된다. 이 때 &lt;strong&gt;ESK STACK&lt;/strong&gt; 에서 적절하게 처리 후 S3에 올려 두도록 할 예정이다.&lt;/p&gt;
&lt;h2&gt;logging 모듈&lt;/h2&gt;
&lt;h3&gt;시간대 변경&lt;/h3&gt;
&lt;p&gt;로깅 표시 기본 시간대는 utc가 표준이므로 KST로 바꾸기 위해서는 다음과 같은 코드가 필요하다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;logger.Formatter.converter = lambda *args: (한국 타임존 정보를 갖고 있는 현재시간 datetime객체).timetuple()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;pytz&lt;/code&gt;를 사용하여 한국 타임존 정보를 갖는 현재시간 datetime 객체를 만들 수 있다. 자세한 정보는 구글에서 쉽게 찾을 수 있다.&lt;/p&gt;
&lt;h3&gt;날짜별 로그파일 나누기&lt;/h3&gt;
&lt;p&gt;단순하게 &lt;code&gt;config.json&lt;/code&gt;을 사용한 설정을 할 수도 있다.&lt;br&gt;그런데 날짜별로 log를 찍을 파일 이름을 변경하려면 코드상에서 로그에 붙일 파일핸들러 설정을 해줘야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from datetime import date
def get_my_logger():
    logger = logging.(&amp;#39;mylogger&amp;#39;)
    date_format = str(date.today())
    hdl = logging.FileHandler(f&amp;#39;{log_file_name}_{date_format}.log&amp;#39;)
    logger.addHandler(hdl)
    return logger&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;로거를 사용할 땐 함수 호출을 통해 사용한다. 그래야 날짜가 변경되었을 때 적절하게 로깅파일을 생성하고 기록할 것이다.&lt;br&gt;따로 root 로거를 사용하고 싶을 땐 &lt;code&gt;logging.debug(&amp;#39;message&amp;#39;)&lt;/code&gt;로 사용한다.&lt;/p&gt;
&lt;h3&gt;루트 로거 사용 전략&lt;/h3&gt;
&lt;p&gt;코드에 propagate 설정을 해보자.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from datetime import date

if DEBUG:
    rootLogger = logging.getLogger(&amp;#39;root&amp;#39;)
    rootLogger.setLevel(logging.DEBUG) # root 로거의 기본 level warn이다. DEBUG 모드에서 이를 DEBUG로 변환
    hdl = logging.StreamHandler() # 기본 스트림 핸들러는 stdErr 이다. 
    rootLogger.addHandler(hdl) # 이 설정을 안하면 하위 로거에서 전파시 하위 로거와 동일한 핸들러를 사용하게 되어 로그가 같은 핸들러에 중복 출력된다.

def get_my_logger():
    logger = logging.(&amp;#39;mylogger&amp;#39;)
    logger.propagate = True if DEBUG else logger.propagate = False # 디버그 모드일 때 DEBUG == True
    date_format = str(date.today())
    hdl = logging.FileHandler(f&amp;#39;{log_file_name}_{date_format}.log&amp;#39;)
    logger.addHandler(hdl)
    return logger&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;DEBUG&lt;/strong&gt; 모드에서만 전파를 허용한다. 이렇게 하면 &lt;strong&gt;DEBUG&lt;/strong&gt; 모드에서만 root로거로 전파가 된다.  &lt;/p&gt;
&lt;p&gt;따로 root 로거를 사용하고 싶을 땐 &lt;code&gt;logging.debug(&amp;#39;message&amp;#39;)&lt;/code&gt;로 사용한다. (루트 로거의 기본 로그 레벨은 warn이다. 그래서 위 코드에서 DBUG 모드이면 debug 레벨이 되도록 설정하였다. 이렇게 하면 DEBUG 모드일 때만 &lt;code&gt;logging.debug(&amp;#39;message&amp;#39;)&lt;/code&gt;의 결과가 콘솔에 찍히고 실제 배포에서는 root로거는 warn 레벨이 되므로 위 코드를 무시한다.&lt;br&gt;이렇게 DEBUG 환경설정 변수를 통해 개발과 배포 환경에 따라서 로깅전략을 세웠다.&lt;/p&gt;
&lt;p&gt;이 방식이 개선될 부분이 있을 수 있겠으나 아직은 크게 불편함은 없다.&lt;/p&gt;</description>
      <category>프로그래밍/python</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/712</guid>
      <comments>https://thrillfighter.tistory.com/712#entry712comment</comments>
      <pubDate>Wed, 24 Nov 2021 14:56:51 +0900</pubDate>
    </item>
    <item>
      <title>javascript(typescript) 비구조화 할당과 함수의 keyword argument 다루기</title>
      <link>https://thrillfighter.tistory.com/711</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본론에 들어가기 전에 자바스크립트에 대해서 투덜거리고 싶다.&lt;br /&gt;자바스크립트의 생태계는 너무 크다. 웹의 발전에 지대한 역할을 하며 지금까지 건재해왔지만 필자의 견해로는 언젠가는 이 틀을 깨트려야한다고 생각한다.&lt;br /&gt;설명을 하면 너무 길어져서 결론만 말하면 자바스크립트는 너무 생태계가 복잡하다. 타입스크립트는 훌륭하지만 애초에 javascript의 한계를 보완하기 위해 만들었다. 그런데 typescript 또한 제대로 쓰려면 만만한 것이 아니다.&lt;br /&gt;같은 동적 타이핑 언어인 python 진영에는 pydantic이라는 것이다. 지금까지 typescript와 pydantic을 비교해온 결과 pydantic이 더 깔끔하고 사용하기도 편리하다.&lt;br /&gt;이번에 keyword argument를 자바스크립트에서 사용하는 방법을 정리 하면서 더욱 그렇게 느껴졌다.&lt;br /&gt;자바스크립트는 애초에 언어차원에서 들어가면 좋을 문법들이 마련되지 않은 상황에서 생태계가 넓혀지다 보니 keyword argument와 같은 아주 기본적인 파라미터 지정전달에도 비구조화 할당이라는 es6(2015)의 기능을 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본론은 간단히&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비구조화 할당.&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const {a, b} = {a:3, b:9}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 의 결과는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const a = 3
const b = 9&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 key값과 같은 이름은 변수에 value를 할당하게 된다.&lt;br /&gt;객체라면 {a,b}를 사용하지만,&lt;br /&gt;배열이라면 [a,b] 를 사용하며 value만 존재하므로 a, b,에 배열의 값이 순서대로 할당된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const [c,d] = [1,2]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱히 개수를 맞춰야 할 필요는 없다. 할당되지 않은 경우 &lt;code&gt;undefined&lt;/code&gt;가 되며 넘치는 경우라면 넘치는 부분은 무시된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비구조화 할당의 기본값&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비구조화 할당을 하기전 기본값을 정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const {z=55, x } = {a:33, b:22, x:99}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과과는 다음 코드와 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const z = 55;  //기본값 할당
const x = 99;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당되는 객체에 &lt;code&gt;z&lt;/code&gt; key가 없으므로 기본값 55로 할당된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가장 간단한 키워드 argument 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두가지 성질을 바탕으로 자바스크립트에서 keyword argument를 사용해보자.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;function add({ a, b }) {
  console.log(a + b);
}

add({ a: 1, b: 33 });  //34&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수가 전달되는 과정에 다음과 같은 비구조화 할당이 이루어진다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;var {a, b} = {a:1, b: 33}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;code&gt;a&lt;/code&gt;와 &lt;code&gt;b&lt;/code&gt;는 함수의 지역변수로 사용되어진다.&lt;br /&gt;다음과 같은 방식도 가능하다.&lt;/p&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;function add(params) {
  var { a, b } = params;
  console.log(a + b);
}

add({ a: 1, b: 33 });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;주의할 점은 파라미터로 전달되는 값은 객체라는 점이다.&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;키워드 argument에 기본값 적용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 대부분 동일한 값을 갖거나 특수한 경우를 제외하고는 default롤 사용되어야할 argument가 있을 수 있다. 이럴 때 default 값이 필요한다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;function add({ a = 1, b }) {
  console.log(a + b);
}

add({ b: 33 }); //44&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 a의 기본값을 사용한다면 &lt;code&gt;a&lt;/code&gt;가 객체에 없어도 된다. 만약 기본값이 적용되지 않은 경우에 할당된 객체에도 해당 key가 없다면 &lt;code&gt;undefined&lt;/code&gt;처리가 될 것이다.&lt;br /&gt;다음과 방식도 가능하다.&lt;/p&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;function add(params) {
  var { a = 1, b } = params;
  console.log(a + b);
}

add({ b: 33 }); //34&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다른 파라미터와 혼합&lt;/h3&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;function add(greeging, params) {
  var { a = 1, b } = params;
  console.log(greeging, a + b);
}

add(&quot;hello&quot;, { b: 33 }); // hello 34&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 방식도 가능하다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;function add(greeging, { a = 1, b }) {
  console.log(greeging, a + b);
}

add(&quot;hello&quot;, { b: 33 });&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 할당 객체 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에선 할당 객체의 각 요소에 대해서 필요하다면 기본값을 적용하였다.&lt;br /&gt;할당 객체 자체의 기본값을 적용해보자.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;function add(greeging = &quot;babo&quot;, { a = 1, b } = { a: 3, b: 33 }) {
  console.log(greeging, a + b);
}

add(&quot;hello&quot;);  // hello 36
add(&quot;hello&quot;, { b: 99 }); // hello 100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;당연하지만 기본 할당 객체는 할당 객체가 전달되지 않을 경우만 적용된다&lt;/b&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;b&gt;비구조화 된 파라미터의 기본값과 할당 객체의 기본값을 혼동하지 말자.&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 내용들을 보면 비구조화 할당에 대한 내용을 이해하고 있다면 쉽게 이해할 수 있다. 하지만 흐름을 이해하는 것과 실제사용(결과만 적용하는것)은 차이가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;타입스크립트를 적용해보기&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface MyObj {
  myNumbers: Array&amp;lt;number&amp;gt;;
  myStrings: Array&amp;lt;string&amp;gt;;
}

function add(greeging = &quot;babo&quot;, { myobj, b }: { myobj: MyObj; b: number }) {
  console.log(greeging, myobj.myNumbers[0] + b);
}

add(&quot;hello&quot;, {
  myobj: { myNumbers: [1, 2, 3], myStrings: [&quot;apple&quot;, &quot;banana&quot;] },
  b: 2,
}); // hello 3&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;타입스크립트와 할당 객체 요소의 기본 값&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;function add(
  greeging = &quot;babo&quot;,
  { myobj, b = 33 }: { myobj: MyObj; b: number }
) {
  console.log(greeging, myobj.myNumbers[0] + b);
}

add(&quot;hello&quot;, {
  myobj: { myNumbers: [1, 2, 3], myStrings: [&quot;apple&quot;, &quot;banana&quot;] }, // typescript Error
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 b=33의 기본값을 두면 타입스크립트가 에러를 낸다.&lt;br /&gt;다음과 같이 해줘야 된다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function add(
  greeging = &quot;babo&quot;,
  { myobj, b = 33 }: { myobj: MyObj; b?: number } // Optional 표시
) {
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당 객체 요소에 기본값을 주면 생략이 가능하기 때문에 type정의에 옵셔널 표시가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 다양한 상황이 연출될 수 있다.&lt;br /&gt;비구조화 할당을 keyword argument에 적용하는 것은 매우 멋진 방법이고 인상적이라 생각한다. 그렇지만 사용법이 깔끔하지 않다. 파라미터로 전달되는 객체는 모양이 심히 보기 안좋아보인다.&lt;br /&gt;마치 접착제가 없어서 밥풀로 땜빵하는 느낌같다.&lt;/p&gt;</description>
      <category>프로그래밍/javascript</category>
      <category>JavaScript</category>
      <category>keyword argument</category>
      <category>비구조화 할당</category>
      <category>키워드 인수</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/711</guid>
      <comments>https://thrillfighter.tistory.com/711#entry711comment</comments>
      <pubDate>Thu, 21 Oct 2021 14:56:08 +0900</pubDate>
    </item>
    <item>
      <title>javascript에서 시간 다루기 moment, new Date()</title>
      <link>https://thrillfighter.tistory.com/710</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;시간을 생성하고 표시하는 것은 매우 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 경우&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;new Date();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 사용한다.&lt;br /&gt;하지만, 서비스시에 문제가 생기기 마련이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간은 전 세계 공통이 아니다. 한국 시간과 UTC(협정 세계시 또는 GMT) 시간은 9시간의 차이가 있다. 일명 시차라고 하는 이 시간 간격 때문에 우리가 원하는 지역의 시간을 표시하기 위해서는 여러가지 사항을 고려해야한다. 같은 코드를 사용해도 사용하는 플랫폼, 디바이스에 따라서 다른 결과를 표시하기도 한다.&lt;br /&gt;그래서 우리는 UTC시간을 베이스로 두고 지역마다의 시차를 +-로 표시한다. 한국은 +09가 된다.&lt;br /&gt;지역에 따라서 플랫폼에서 사용하는 locale이 다르기 때문에 시간과 locale 두가지를 정확히 알고 적용해야한다. 한국에서는 3시라고 하지만 UTC 시점에서는 전날 16시가 되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근본은 간단하지만 개발에 사용되는 환경 라이브러리에 따라 변수가 생긴다.&lt;br /&gt;다음은 몇가지 실제 코드 실험이다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;// Chrome 브라우저 콘솔창
new Date(&quot;2021-10-14T09:56:01&quot;) //Thu Oct 14 2021 09:56:01 GMT+0900 (한국 표준시)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;// node 개발환경
new Date(&quot;2021-10-14T09:56:01&quot;) //2021-10-14T09:56:01.000Z&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행환경이 다른 동일한 코드이다. 시간은 9시 56분으로 동일하지만 locale은 다르다. 여기에서시간 표시에 붙어 있는 &lt;code&gt;T&lt;/code&gt; 와 &lt;code&gt;Z&lt;/code&gt;에 대해서 먼저 알고 넘어가자.&lt;br /&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/ISO_8601&quot;&gt;https://ko.wikipedia.org/wiki/ISO_8601&lt;/a&gt;&lt;br /&gt;위키에 아주 자세히 나와 있으며 핵심은, &lt;code&gt;T&lt;/code&gt;는 날자와 시간을 구분하는 것이며 &lt;code&gt;Z&lt;/code&gt;는 &lt;code&gt;UTC&lt;/code&gt;를 의미한다. 만약 Z가 없는 표기라면 해당 시간을 실행 환경 locale로 하라는 뜻이다.&lt;br /&gt;이런 기본적인 규칙을 토대로 위 두가지 결과를 해석하면 크롬 브라우저의 경우는 &lt;code&gt;Z&lt;/code&gt;가 없는 시간 형식을 한국의 locale로 해석하였고 node 환경에서는 &lt;code&gt;UTC&lt;/code&gt;로 해석하였다.&lt;br /&gt;개발환경에서는 node를 사용하기 때문에 실행환경(브라우저)에서 원하는 동작이 이루어지지 않을 가능성이 있다.&lt;br /&gt;브라우저 마다 이런 시간 형식을 parsing하는 방법 역시 달라질 수 있다.&lt;br /&gt;이런 점들을 고려하다보면 이미 혼돈이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;moment의 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 라이브러리를 사용하여 시간형식을 파싱해보니 chrome 환경의 결과와 동일한 결과를 반환한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;moment(&quot;2021-10-14T09:56:01&quot;) // &quot;2021-10-14T00:56:01.000Z&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 &lt;code&gt;Z&lt;/code&gt; 가 시간 표시 끝에 붙어 있다. &lt;code&gt;UTC&lt;/code&gt;표기라는 뜻이다. 따라서 한국 시간으로 환산하면 09:56분이 된다.&lt;br /&gt;실행환경(크롬브라우저)와 동일하게 인식이 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 실험한 시간 형식은 &lt;code&gt;Z&lt;/code&gt;가 표시되지 않은 시간이었다.&lt;br /&gt;&lt;code&gt;Z&lt;/code&gt;가 표시되지 않은 시간은 실행환경에 상대적으로 해석하라는 의미다.&lt;br /&gt;node 환경에서 &lt;code&gt;new Date()&lt;/code&gt;를 사용하면 &lt;code&gt;UTC&lt;/code&gt; 기준으로 시간을 해석했다.&lt;br /&gt;node 환경에서 &lt;code&gt;moment&lt;/code&gt;를 사용하니 OS timezone 설정으로 시간을 해석하는 것 같다.&lt;br /&gt;우리는 Z가 표기된 시간을 사용하는 것이 안정정일 거라 생각하지만, 실제로 개발을 하다보면 지역 시간을 사용하는 것이 더 안정적이다.&lt;br /&gt;지역시간을 사용하면서 &lt;code&gt;locale&lt;/code&gt; 표시를 빼먹고 사용할 수도 있다.&lt;br /&gt;이런 경우 &lt;code&gt;UTC&lt;/code&gt;가 아닌 다른 시간대를 사용한다면 &lt;code&gt;new Date()&lt;/code&gt;보다 &lt;code&gt;moment&lt;/code&gt;를 사용하는 것이 안정정이라 판단한다.&lt;/p&gt;</description>
      <category>프로그래밍/javascript</category>
      <category>moment</category>
      <category>new date()</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/710</guid>
      <comments>https://thrillfighter.tistory.com/710#entry710comment</comments>
      <pubDate>Thu, 14 Oct 2021 11:44:42 +0900</pubDate>
    </item>
    <item>
      <title>나는 내일, 어제의 너와 만난다.</title>
      <link>https://thrillfighter.tistory.com/708</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;약간의 스포.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일본 멜로영화 특유의 잔잔한 감성이 느껴지는 영화.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가끔 유투브에서 배우들이 나온 뮤직비디오를 보고 나서 관심이 생겨 보게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭘까? 이 느낌은, 처음부터 영상이 너무 아름다운 느낌이라 눈물이 날 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 영화는 제목이 바로 영화의 세계관이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 부분에서 에미의 시점으로 전환되었을 땐 너무나도 에미의 감정에 몰입이 되서 눈물이 났다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흘러간 아련하고 아름다웠던 추억 같은 느낌의 아름다운 영화...&lt;/p&gt;</description>
      <category>관심사/영 화</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/708</guid>
      <comments>https://thrillfighter.tistory.com/708#entry708comment</comments>
      <pubDate>Fri, 8 Oct 2021 14:08:50 +0900</pubDate>
    </item>
    <item>
      <title>redux-saga에서 websocket을 이용하기.</title>
      <link>https://thrillfighter.tistory.com/707</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 redux-saga에서 websocket사용을 위해 필요한 사항들을 개인적인 방식으로 정리를 위한 글이므로 참고만 하시길 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발단은 이렇습니다.(1차 실패 과정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react(react-native)에서 socket통신을 위해 websocket을 사용하기로 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 소켓 통신이 필요한 페이지 로딩마다 websocket을 연결하는 방식을 사용할 수도 있으나, 이렇게 하면 소켓 연결과 종료가 빈번히 발생하여 소켓 연결의 의미가 퇴색되게 느껴집니다. 그리하여 로그인 시에 연결을 하고 로그아웃을 할 때 소켓을 종료시키도록 설계하려 합니다. 물론 소켓 통신이 필요없는 페이지에서도 연결 상태를 유지하므로 서버 리소스가 증가할 수 있다는 점은 염두해 둔 결정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 방법을 사용하기 위해서는 기존 웹페이지의 방식처럼 페이지마다 document가 있는 방식에서는 까다롭습니다. 하지만 우리는 redux를 사용할 수 있습니다. 페이지 마다의 state가 아닌 redux의 store를 데이터 저장소로 이용하도록 하여 여러 페이지를 이동하더라도 초기화 되지 않는 상태를 가질 수 있습니다. 그래서 redux-saga를 이용하여 소켓 통신을 처리하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 구현은 어렵지 않았으나 구현 마무리에 한 가지 문제가 발생합니다. generator로 이루어진 리덕스 사가 프레임 안에서 소켓 이벤트로 가져온 메세지를 핸들링을 일반 콜백함수로 해야하기 때문에 이를 해결하기 위해 문서들을 찾아봅니다. 쉽게 말하면 소켓 이벤트 핸들러 내에서 액션을 디스패지하기 위해서는 제너레이트 함수를 소켓 이벤트 핸들러로 등록해야하는데 이 부분은 분명 문제가 발생할 수 밖에 없습니다. 애초에 이 부분을 생각하지 못한 것에 아직 미천한 실력을 드러냅니다.(소켓 연결과 메세지 핸들링, 모바일 상황에서 인터넷 접속이 재연결 되었을 때 자동 재연결 등은 어렵지 않게 처리가 가능하였지만, 액션 디스패치를 하려고 하니... 이게 아닌듯 싶었습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결과정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은, 우선 제너레이트 함수를 websocket 핸들러로 등록하는 방법이 있다면 간단한데, 그럴 일은 없을 거라 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redux-saga&amp;nbsp; 프레임 안에서 해결하는 방법을 찾던 중 channel을 이용하면 해결이 가능할 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 생각한 핵심은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #454b4e;&quot; data-darkreader-inline-color=&quot;&quot;&gt;- 앞서 실패한 이유에서 이벤트 핸들러 내에서 dispatch를 하는 문제는 해결됩니다.(saga frame내가 아니므로)&lt;/span&gt; 그런데 websocket은 양방향 통신을 유지하고 있어야 하기 때문에 특정 컴포넌트에서 연결을 하도록 하면 컴포넌트가 새롭게 mount 될 때 마다 연결이 끊기거나 새롭게 이루어지게 됩니다. 소켓 연결과 종료를 컴포넌트의 상태에 의존하는 방식은 문제가 될 수 밖에 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3c4144;&quot; data-darkreader-inline-color=&quot;&quot;&gt;- 따라서 redux-saga 프레임과 외부를 연결하는 방법이 필요합니다.&lt;/span&gt;&lt;span style=&quot;color: #3c4144;&quot; data-darkreader-inline-color=&quot;&quot;&gt; redux-saga의 채널을 사용하면 외부 이벤트와 saga 프레임을 연결할 수 있습니다. 외부 이벤트가 있을 때마다 saga 프레임에 전달을 하도록 할 수 있는 것이죠. 이렇게 하면 소켓을 saga 프레임 외부에 만들어 놓고 소켓 메세지가 도착했을 때 액션을 디스패치 하는 대신 사가 채널로 전달하면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3c4144;&quot; data-darkreader-inline-color=&quot;&quot;&gt;- 채널을 사용하면 이점이 더 있어보입니다. 채널은 buffer라는 개념이 있어서 연속적인 메세지의 전달의 경우 버퍼에 채워진 순서대로 동기적으로 처리할 수 있습니다. 채널을 사용하지 않으면 특정 액션이 동시에 여럿이 디스패치되었을 때 비동기적으로 처리를 하게되므로 순서를 맞출 수 없습니다. 순서를 맞추려고 한다면 buffer가 없기 때문에 각 액션들이 디스패치된 후 처리되기 전까지 block상태로 처리시켜야 하며 처리 도중에 디스패치되는 동일한 액션의 디스패치는 소실될 수 밖에 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #454b4e;&quot; data-darkreader-inline-color=&quot;&quot;&gt;결론은 채널을 사용하면 아주 쉽게 Saga 내에서 socket 양방향 통신을 처리할 수 있다는 이론적인 배경을 정리하였습니다.&lt;/span&gt;&lt;span style=&quot;color: #454b4e;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이젠 실제로 구현을 해보려고 합니다.&lt;/span&gt;&lt;span style=&quot;color: #4c5256;&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현후 정리는 차차 하겠습니다.&lt;/span&gt;&lt;span style=&quot;color: #454b4e;&quot; data-darkreader-inline-color=&quot;&quot;&gt;...미완성&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/React-Native</category>
      <category>redux-saga</category>
      <category>websocket</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/707</guid>
      <comments>https://thrillfighter.tistory.com/707#entry707comment</comments>
      <pubDate>Sat, 25 Sep 2021 16:51:33 +0900</pubDate>
    </item>
    <item>
      <title>두가지 생각</title>
      <link>https://thrillfighter.tistory.com/706</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;교보문고에서 도서를 구입하면 추가적으로 이벤트성 상품을 싸게 판매한다. 보통은 필요가 없어서 넘기지만 readITzine이라는 매거진을 같이 구입해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성장판을 찾아서라는 제목이 붙은 첫 번째 발행된 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇몇 개발자들의 짧은 개발에 대한 회고 또는 자신의 철학 등을 가벼운 문체로 읽기 좋게 구성하여 100여 페이지지만 읽는 데는 20분 내외로 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략적으로 공감되는 내용이며 비슷한 내용이지만 몇가지 기억에 남는 문구가 있어 기록한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;두 가지 생각&lt;br /&gt;두 개의 마음이 뿌리를 내리게 되면 &lt;br /&gt;진정으로 당신에게 주어진 일, &lt;br /&gt;즉 성공으로 가기 위해 해야만 하는 일에 &lt;br /&gt;제대로 집중하지 못하게 된다. &lt;br /&gt;혼란에 빠진 사람은 결단력 있게 한 가지에 집중하는 사람에 비해 항상 더 약해 보이게 마련이다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밥 보딘이 쓴 [내 안의 100명의 힘, WHO]에서 초심을 잃어버린 면접자들의 예를 들며 한 말이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 인용하여 쓴 글의 저자는 강사로 활동하면서 다양한 사람들을 만나게 되는데 그때마다 사람들의 행동을 보면서 두 가지 생각을 가진 사람들을 많이 봐왔다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 느낀 것은 두 가지 생각을 갖게 되면 흔들리는 배 처럼 갈팡질팡 할 수밖에 없기 때문에 자신의 길을 뚜렷하게 걷지 못할 거란 느낌이 들었다. 비단, 개발에 대한 이야기뿐 아니라 인생의 모든 이벤트에 대해서 우리는 온전한 하나의 생각을 가지고 살아가야 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 내면에 해결되지 않는 여러 생각이 있다면 과감히 벗어 던지고 삶을 명확하게 만들 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 내가 지금 온전히 하나의 생각을 갖고 무언가에 집중할 수 있다면, 우리는 그 기회를 놓치지 말고 노를 저을 때가 아닌가 싶다.&amp;nbsp;&lt;/p&gt;</description>
      <category>일상</category>
      <category>두 가지 생각</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/706</guid>
      <comments>https://thrillfighter.tistory.com/706#entry706comment</comments>
      <pubDate>Sat, 25 Sep 2021 12:23:14 +0900</pubDate>
    </item>
    <item>
      <title>5stack.gg 공지입니다.</title>
      <link>https://thrillfighter.tistory.com/notice/702</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 5stack.gg는 휴식에 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://5stack.gg&quot;&gt;http://5stack.gg&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 확인 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 서비스 중인 매치 애니메이션 외에 새로운 기능들이 추가적으로 개발된 상황입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 사정상 휴식에 들어가지만 휴식이 끝나면5stack.gg의 스케일은 지금과 다른 규모일 것이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5stack의 모토에 맞게 세상에 없는, 식상하지 않은 서비스로 보답해 드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3c4144;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그 동안 5stack.gg를 꾸준히 이용해 주셔서 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3c4144;&quot; data-darkreader-inline-color=&quot;&quot;&gt;2021/09/03&lt;/span&gt;&lt;/p&gt;</description>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/notice/702</guid>
      <pubDate>Fri, 3 Sep 2021 16:46:36 +0900</pubDate>
    </item>
    <item>
      <title>5stack.gg에 대한 공지입니다.</title>
      <link>https://thrillfighter.tistory.com/701</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 5stack.gg는 휴식에 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 &lt;a href=&quot;http://5stack.gg&quot;&gt;http://5stack.gg&lt;/a&gt; 에서 확인 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 서비스 중인 매치 애니메이션 외에 새로운 기능들이 추가적으로 개발된 상황입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 사정상 휴식에 들어가지만 휴식이 끝나면5stack.gg의 스케일은 지금과 다른 규모일 것이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5stack의 모토에 맞게 세상에 없는, 식상하지 않은 서비스로 보답해 드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3c4144;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그 동안 5stack.gg를 꾸준히 이용해 주셔서 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/1인개발 - 5stack.gg</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/701</guid>
      <comments>https://thrillfighter.tistory.com/701#entry701comment</comments>
      <pubDate>Fri, 3 Sep 2021 16:43:01 +0900</pubDate>
    </item>
    <item>
      <title>긴 휴가.</title>
      <link>https://thrillfighter.tistory.com/698</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;롱 베케이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭘 해도 잘 안될 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리에게 내린 긴 휴가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/xNICdO585Cs?list=PLrvo9deznZewZJjjknRYpQQGoSZPhjxay&amp;amp;t=2496&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;롱베케이션&lt;/a&gt;&lt;/p&gt;</description>
      <category>관심사/영 화</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/698</guid>
      <comments>https://thrillfighter.tistory.com/698#entry698comment</comments>
      <pubDate>Mon, 9 Aug 2021 23:36:52 +0900</pubDate>
    </item>
    <item>
      <title>React Native 워밍업.</title>
      <link>https://thrillfighter.tistory.com/693</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드와 IOS 모바일 앱 개발 방식은 여러가지가 있지만 React를 사용해온 경험으로 React Native를 사용하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발상 별 어려움을 없을 것이라 생각했고 까다로운 부분은 초기 개발 환경 설정과 배포라고 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 리액트 네이티브의 개발 방식은 두 가지로 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- expo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- react-native-cli&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 개발자가 사용할 컴퓨터 환경은 3가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- windows&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- linux&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- mac&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워밍업이라 자세한 내용이 아닌 대략 훑어본 것들을 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- expo는 많은 기능들을 패키징 시킨 개발 프레임웍이라 생각하면 된다. 예를들어 android와 ios 개발과 배포를 한번에 정리, react-native API를 좀 더 쓰기 쉽게 모듈화 시켜 제공.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;expo는 매우 사용하기 간편할 것으로 예상된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 처음엔 expo를 사용하기로 맘 먹었다. 하지만 expo에서는&amp;nbsp; react-native가 제공하는 API 직접 사용할 수 없는 문제로 만약 개발자가 원하는 기능이 expo에 없을 땐 문제가 된다. 이런 경우 expo에서 해결할 수 없으므로 프로젝트를 eject 시켜 react-native-cli 개발 환경으로 변경시켜서 필요할 모듈을 개발 또는 설치하여 해결한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react에서 eject 후 여러가지 문제를 경험한 적이 있어서 어짜피 eject이 필요하면 처음부터 expo를 사용하지 않는 것이 좋다고 판단되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 개발 중인 프로젝트에 expo 적용을 검토를 시작하자 마자, 로그인 적용에 문제가 있음을 알았다. 카카오로 로그인 화면을 구성할 시 expo에서는 webview에서 api로 백엔드와 통신을 하여 로그인을 구성해야한다. 반면에 react-native-cli를 사용하면 직접 구현하거나 이미 구현된 카카오 로그인을 통해서 앱 사용자의 카톡에 접근하여 바로 로그인이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능은 매우 편리하고 실제로 여러 앱들을 보면 두 가지 형식 중 하나를 사용하지만 직접 아이디 패스워드를 입력해야하는 전자의 방식은 매우 불편할 수 밖에 없다. 즉, 로그인 편의성이라는 한가지 이유로 react-native-cli를 선택해야 할 것으로 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론&amp;nbsp; expo에서 카카오 로그인을 지원하는 기능을 언젠간 넣어줄 수도 있겠지만, 로그인 말고도 이런 비슷한 문제들이 생길 것으로 예상되기 때문에 expo를 포기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 환경에서 현재 mac 환경은 구성하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 개발로 우선 개발 하면서 맥 환경은 최종적으로 구성할 생각이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 맥북으로 안드로이드+IOS 환경을 구성하여 개발하는 것이 가장 깔끔하다는 생각.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 컴퓨터 cpu가 ryzen 3 3200g인데 모바일 디바이스 에뮬레이터를 종료할 때 항상 멈춘다. centos7의 환경이 문제인 듯 하여 조만간 ubuntu로 환경을 재구성해볼 생각이면 그래도 문제가 된다면 좀 불편하지만 직접 모바일 디바이스를 연결하여 개발해야할 듯 하다. 하지만 가능하면 가상 에뮬레이터를 사용하는 것이 좋다. 이유는 hot-reloading이 지원되기 때문이며 실제 디바이스 연결을 할 경우 코드가 바뀔 때마다 결과를 확인하려면 빌드-&amp;gt;apk파일 생성-&amp;gt;디바이스로 전송-&amp;gt;실행과정이 있어야 한다. 매우 번거롭고 오래걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU의 가상화 지원여부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 CPU라면 가상환경을 모두 지원할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 안드로이드 스튜디오의 에뮬레이터 실행이 실패할 경우가 있는데 이런 경우 bios에서 CPU의&amp;nbsp; 가상화를 enable 시켜줘야 한다. 대략 cpu 주파수 설정에 들어가 (AMD의 경우 SVM 활성화, INTEL의 경우 VT-x???맞나 모르겠다. 활성화 를 해줘야 한다.) 이걸 활성화 하면 OS에서 알아서 가상화를 지원하도록 설정하고, 리눅스의 경우 때에 따라서 KVM을 설치하는 작업을 한번 더 실행해야한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 자세히 환경설정부터 개발 핵심에 대해서 정리하고 싶지만 시간 관계상 간단히 글로 정리하고 정리는 나중에...&lt;/p&gt;</description>
      <category>프로그래밍/React-Native</category>
      <category>react-native</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/693</guid>
      <comments>https://thrillfighter.tistory.com/693#entry693comment</comments>
      <pubDate>Sun, 20 Jun 2021 16:04:07 +0900</pubDate>
    </item>
    <item>
      <title>도커 컨테이너를 사용한 Let's Encrypt ssl 인증서 발급</title>
      <link>https://thrillfighter.tistory.com/691</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;SSL 인증서를 사용하는 방법은 무료, 유료 두 가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서는 보안을 위한 것이므로 중요하고 최신 웹 기술에서는 필수적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무료와 유료는 그다지 차이는 없지만 유료의 경우 보험처럼 생각할 수 있다. 또한 무료(Let's Encrypt)는 3개월 마다 갱신해야하는 번거로움이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 번거로움을 최소한도로 하기위해서는 인증 절차의 숙지가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 도커를 활용하면 매우 간단하게 인증서를 발급받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 단순 기록용으로 핵심만 간단히 기록하고 차 후 시간이 날 때 살을 붙여 정리하도록 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인증서 발급 절차 요약&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인증서를 사용하려는 사이트 도메인 필요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 인증서 발급기관에서 사이트 도메인에 대한 소유권 확인(여러가지 방법이 있다. 도메인 TXT레코드에 발급과정에서 주어진 해시 입력, 또는 직접 사이트 내에 특정 페이지를 만들어&amp;nbsp; txt 파일을 만들어어 인증)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 소유권 확인 후 인증서 발급(핵심 파일 - fullchain.pem, private.pem, )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 사이트의 인프라(nginx, haproxy 등등)에 인증서 파일 적절히 적용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 인증 절차는 매우 간단하지만 인프라 구성에 따라서 번거롭거나 까다로울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 참고 사이트를 참고하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- nginx에서는 발급한 fullchain.pem, private.pem을 설정파일에서 각각 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- haproxy에서는 cat fullchain.pem private.pem &amp;gt; new.pem 으로 통합하여 haprocy.cfg 파일에서 지정하여 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; (예:&amp;nbsp; bind :::443 v4v6 ssl crt /etc/haproxy/certs) -&amp;nbsp; certs 디렉토리 아레 new.pem을 넣음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발급 절차를 간단히 정리하면 - 인증서 발급 사이트가 너네 사이트 맞냐? -&amp;gt; 맞다면 인증서 줄께 적절한 곳에 넣어서 클라이언트가 접속 할 때마다 인증서를 확인할 수 있게 해놔. -&amp;gt; 끝.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 사이트&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lynlab.co.kr/blog/72&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://lynlab.co.kr/blog/72&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발도구/기타</category>
      <category>Let's encrypt</category>
      <category>SSL 인증서</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/691</guid>
      <comments>https://thrillfighter.tistory.com/691#entry691comment</comments>
      <pubDate>Tue, 15 Jun 2021 10:24:39 +0900</pubDate>
    </item>
    <item>
      <title>베르세르크는 유작으로 남겨졌다.</title>
      <link>https://thrillfighter.tistory.com/690</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #aa9878;&quot; data-darkreader-inline-color=&quot;&quot;&gt;6/1일 방금 이 기사를 읽었는데 좀 충격이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1990년대 베르세르크라는 만화를 처음 접하면서 그 디테일과 어두우면서도 묘한 매력의 세계관에 빠져들었었다. 기괴하고 어두운 그림체는 호불호가 갈리겠지만 그의 장인 정신은 누구도 부인하지 않는 사실이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 만화는 30여년간 그려지면서 완성되지 않고 있기에 누군가는 그에 대해서 게으른 것 아니냐는 조롱도 했지만 그의 작업량은 상상을 초월했다고 한다. 항상 베르세르크를 보면 보면서 한 컷 그리는데 일주일 이상 걸리겠다고 생각해왔다. 문하생들이 버티지 못하고 다 떨어져 나갔다는 이야기는 아는 사람은 다 잘 알 것이다. 실력이 있기에 대충 이름 값으로 다작을 했다면더 쉽게 더 큰 돈을 벌었을 것일 텐데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결혼 포기 선언까지 하면서 베르세르크에 매달렸지만 아쉽게 54세의 나이로 최근 건강악화로 베르세르크는 유작이 되었다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미우라 켄타로, 사실 나 역시 그의 삶에 공감하는 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누릴 수 있는 여러 것을 포기하면서 무언가에 매달린다는 것은 참 고독하다고 느낀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그 역시 자신의 선택이겠지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무언가를 이루기에는 인생은 참 짧은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 보면 죽고 나면 허무한 노력같기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완성하지 못한 그의 꿈에 대해&amp;nbsp; 아쉬움과 존경을 표한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼가 고인의 명복을 빕니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상</category>
      <category>미우라켄타로</category>
      <category>베르세르크</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/690</guid>
      <comments>https://thrillfighter.tistory.com/690#entry690comment</comments>
      <pubDate>Tue, 1 Jun 2021 02:06:21 +0900</pubDate>
    </item>
    <item>
      <title>sqlalchemy uselist와 참조무결성 on postgresql</title>
      <link>https://thrillfighter.tistory.com/688</link>
      <description>&lt;p&gt;python으로 된 백엔드 프레임 워크는 django를 사용하고 있습니다.&lt;/p&gt;
&lt;p&gt;최근 몇가지 문제로 다른 프레임워크에 대해 살펴보고 있구요.&lt;/p&gt;
&lt;p&gt;후보군으로는 golang의 gingonic과 python의 fastAPI입니다.&lt;/p&gt;
&lt;p&gt;프레임워크의 이동은 간단한 것이아니기 때문에 다양한 기술에대해 살펴볼 필요가 있습니다.&lt;/p&gt;
&lt;p&gt;최근 뜨고있는 fastAPI를 살펴보면서 ORM으로 sqlalchemy에 대해 알 필요가 있다고 여겨져서 이리저리 살펴보는 중입니다.&lt;/p&gt;
&lt;p&gt;사실 django에서도 raw Query로 대부분을 개발해왔기 때문에 raw Query가 편하긴 합니다. 하지만 개발 속도와 가독성있고 안정적인 코드를 위해 약간의 성능을 포기하는 것도 나쁘지 않다고 생각되었네요.&lt;/p&gt;
&lt;p&gt;어쨌든 sqlalchemy에 대한 해외 문서들을 살펴보던 중 1:1 관계에서 uselist를 사용하는 사례를 보게되었습니다.&lt;/p&gt;
&lt;p&gt;1:1관계는 흔한 관계는 아니기 때문에 우선은 무시하고 지나가도 되겠지만, 현재 개발하는 프로젝트에서 1:1 관계가 여럿 등장하기 때문에 정리하고자 글을 남깁니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;# uselist 예제
class Member(Base):
    __tablename__ = 'member'
    id = Column(Integer, primary_key = True)
    name = Column(String(256))

class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key = True)
    address = Column(String(256))
    member_id = Column(Integer, ForeignKey('member.id'))
    member = relationship(Member, backref=backref('address', uselist=False))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 코드에서 주목할 부분은 마지막 줄에 &lt;code&gt;uselist=False&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CMAB9/btq0SG9qvvF/9KGwBYnr85z6gdCklzs661/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CMAB9/btq0SG9qvvF/9KGwBYnr85z6gdCklzs661/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CMAB9/btq0SG9qvvF/9KGwBYnr85z6gdCklzs661/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCMAB9%2Fbtq0SG9qvvF%2F9KGwBYnr85z6gdCklzs661%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;두 개의 테이블이 생성되었고, &lt;b&gt;address&lt;/b&gt; 테이블의 &lt;code&gt;member_id&lt;/code&gt; 가 외래키로 지정되어 &lt;b&gt;member&lt;/b&gt;의 &lt;code&gt;primary_key&lt;/code&gt;를 참조하고 있습니다.&lt;/p&gt;
&lt;p&gt;사실 단순히 db에만들어진 테이블로만 생각하면 위 관계는 1:1이아니고 1:다 (member: address) 의 관계가 됩니다.&lt;br /&gt;이유는 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;우선 애초에 다:다 관계를 두 테이블 상에서 나타낼 수는 없습니다. 따라서 두 테이블의 관계는 1:1 또는 1:다 관계가 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;address&lt;/b&gt;의 &lt;code&gt;member_id&lt;/code&gt;는 &lt;b&gt;member&lt;/b&gt;테이블의 &lt;code&gt;id&lt;/code&gt;값을 중복해서 가질 수 있습니다. 따라서 하나의 &lt;i&gt;member&lt;/i&gt;인스턴스가 여러 &lt;i&gt;address&lt;/i&gt;인스턴스에 대응될 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;따라서 위 코드에서는 1:1관계로 만들기 위해 &lt;code&gt;uselist=False&lt;/code&gt;로 만들었습니다.&lt;br /&gt;하지만 &lt;code&gt;uselist=False&lt;/code&gt;는 1:1아닌 1: (0..1) 관계를 만듭니다.&lt;br /&gt;실제 동작은 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;address&lt;/b&gt; 테이블에 동일한 &lt;code&gt;member_id&lt;/code&gt;가 입력되려고 한다.&lt;/li&gt;
&lt;li&gt;동일한 &lt;code&gt;member_id&lt;/code&gt; 입력에 대해서는 &lt;code&gt;member_id&lt;/code&gt;를 &lt;i&gt;null&lt;/i&gt;로 만들고 나머지 값들은 그대로 입력한다.&lt;br /&gt;따라서&lt;i&gt;null&lt;/i&gt;로 인해 참조무결성이 희생됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;더 큰 문제는 &lt;code&gt;uselist=True&lt;/code&gt; 였던 테이블을 &lt;code&gt;uselist=False&lt;/code&gt;로 변경한 경우입니다.&lt;br /&gt;이런 경우는 데이터의 입력이 이미 이루어 졌고 입력 데이터 자체에 1:다의 관계가 형성되었다면 참조 무결성은 무참히 깨지게 됩니다.&lt;br /&gt;왜냐면 &lt;code&gt;uselist=False&lt;/code&gt; 자체로 테이블의 참조 무결성을 검사하지 않고 에러를 내지 않기 때문입니다. 몇몇 예제로 실험해보니 이 옵션은 단지 db단이 아닌 python코드 단에서 필터링하는 효과만 있는 것 같습니다.&lt;/p&gt;
&lt;p&gt;저도 처음으로 문서를 살펴보는 중이므로 저 옵션을 어떻게 사용해야할지 궁금합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;postgresql이 아닌 다른 데이터베이스(mysql, SQLite)과 같은 경우에 필요한 옵션이라고 생각합니다.&lt;br /&gt;하지만 1:1관계는 그렇게 흔한 관계는 아니므로 다음과 같은 결론을 내고 마무리하려고 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;참조 무결성을 위해서는 Foreign Key(member_id)의 제약조건에 &lt;code&gt;unique=True&lt;/code&gt; 옵션을 주는 것이 좋다고 생각합니다.&lt;/li&gt;
&lt;li&gt;만약 &lt;code&gt;uselist=False&lt;/code&gt; 옵션을 사용한다면 &lt;code&gt;nullable=False&lt;/code&gt;도 같이 주는 것이 좋다고 생각합니다. 하지만 기존 테이블이 &lt;code&gt;uselist=True&lt;/code&gt; 였다면 참조 무결성을 보장할 수 없으므로 &lt;code&gt;unique=True&lt;/code&gt; 옵션을 주고 데이터 참조 무결성을 해결하는 것이 좋은 해결책일 듯 싶습니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>프로그래밍/database</category>
      <category>1:1관계</category>
      <category>sqlalchemy</category>
      <category>uselist=False</category>
      <category>참조무결설</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/688</guid>
      <comments>https://thrillfighter.tistory.com/688#entry688comment</comments>
      <pubDate>Wed, 24 Mar 2021 21:42:44 +0900</pubDate>
    </item>
    <item>
      <title>메타버스에 탑승하는 인류.</title>
      <link>https://thrillfighter.tistory.com/687</link>
      <description>&lt;p&gt;최근에 메타버스라는 용어가 자주 등장한다.&lt;/p&gt;
&lt;p&gt;메타+버스 이렇게 두 단어의 합성어인데, 그 뜻은 두리뭉실하지만 가상의 공간 정도로 해석하면 될 듯하다.&lt;/p&gt;
&lt;p&gt;느끼고 있는 분들도 있겠지만 최근 인류의 발전 속도는 가히 상상을 초월한다.&lt;/p&gt;
&lt;p&gt;원시시대부터 인류의 기술분야 발전 속도를 거북이의 걸음속도라고 하고 비교해보면 최근 10년은 육상선수의 속도라 해도 이상하지 않을 것이다.&lt;/p&gt;
&lt;p&gt;이러한 발전의 큰 축과 함께 메타버스라는 용어가 대두되었다.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;이 책을 읽고 있는 중이며 지금 딱 읽기 좋은 때 같다. 대략 메타버스는 인간의 인지하는 새로운 세상이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;aaa.png&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;449&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJyaCO/btqZvMuYaAi/fiVI7DPHTvu00qovqhOhpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJyaCO/btqZvMuYaAi/fiVI7DPHTvu00qovqhOhpK/img.png&quot; data-alt=&quot;메타버스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJyaCO/btqZvMuYaAi/fiVI7DPHTvu00qovqhOhpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJyaCO%2FbtqZvMuYaAi%2FfiVI7DPHTvu00qovqhOhpK%2Fimg.png&quot; data-filename=&quot;aaa.png&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;449&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메타버스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코로나로 세상이 바뀌면서 언택트 시대가 되었다고는 하지만 인류는 그 이전부터 언택트를 향해 달려가고 있었다.&lt;/p&gt;
&lt;p&gt;최근 인류는 20여 년간 인터넷 속의 커뮤니티부터 시작해서 게임과 SNS 등 그 속에서 끊임없이 소통해왔다. 그 전에는 없었던 세상이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또한 최근에는 VR(가상현실), AR(증강현실) 등을 통해 인간의 오감을 새로운 메타버스로 안내한다.&lt;/p&gt;
&lt;p&gt;이러한 환경, 기술은 실제 사람과 사람이 만나 소통하는 것이 아닌 가상의 공간에서 소통을 한다.&lt;/p&gt;
&lt;p&gt;나이가 어리면 어릴수록 현실을 부정하는 것은 아니지만 현실의 소통이 어색해지고 있다고 한다. 그리고 이러한 현상은 기술이 발전할수록 가속화되고 있다.&lt;/p&gt;
&lt;p&gt;메타버스의 시대는 코로나 이전부터 이미 시작되었고 단지 코로나는 가속화시켰을 뿐이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;그러면 메타버스를 어떻게 받아들여야 하나?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;새로운 시대가 오면 인간의 사고체계가 변하게 된다. 최근 빠른 발전 속도로 인해 그 부작용이 없을 수는 없다고 생각한다. 특히 세대별 소통의 방식 차이로 인한 것이 제일 크다고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 구글글라스 같은 것으로 실제 현장에 가서 실습하지 않아도 &lt;span&gt;새로운 기술을 쉽게&lt;/span&gt;&amp;nbsp;배울 수 있게 되었다. 멀리에서도 증강현실을 통해서 눈앞에서 알려줄 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;abcdefg.png&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;352&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwkg1f/btqZqo2KBlY/KBA084CE33oDsJ5ukrwMBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwkg1f/btqZqo2KBlY/KBA084CE33oDsJ5ukrwMBK/img.png&quot; data-alt=&quot;구글글라스새로운 기술을&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwkg1f/btqZqo2KBlY/KBA084CE33oDsJ5ukrwMBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwkg1f%2FbtqZqo2KBlY%2FKBA084CE33oDsJ5ukrwMBK%2Fimg.png&quot; data-filename=&quot;abcdefg.png&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;352&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;구글글라스새로운 기술을&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;물론 잘 사용하면 좋은 기능을 하지만 부작용도 있다.&lt;/p&gt;
&lt;p&gt;수동적인 상태로 오감을 쉽게 만족시키다 보니 현실에서 능동적인 행위를 하거나 능동적인 사고능력이 떨어지기도 한다는 것이다.&lt;/p&gt;
&lt;p&gt;현실이 아무런 문구도 없는 그림을 보여주는 것이라면 증강현실은 그림에 대한 설명(느낌)을 보여주는 것이라 할 수 있다. 능동적 느낌보다는 수동적인 느낌을 받을 수도 있는 것이다.&lt;/p&gt;
&lt;p&gt;다시 말해 메타버스에 빠지면 능동적인 사고능력이 떨어질 수도 있겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;어쨌든 앞으로 10년은 지난 10년의 발전 속도보다 매우 매우 빠를 것이다. 인류의 인식도 많이 바뀌고 새로운 비밀도 많이 풀릴 것 같다는 생각이 든다.&lt;/p&gt;</description>
      <category>관심사/도서</category>
      <category>메타버스</category>
      <category>증강현실</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/687</guid>
      <comments>https://thrillfighter.tistory.com/687#entry687comment</comments>
      <pubDate>Sun, 7 Mar 2021 23:02:45 +0900</pubDate>
    </item>
    <item>
      <title>살아온 길을 돌아보며</title>
      <link>https://thrillfighter.tistory.com/685</link>
      <description>&lt;p&gt;누가 그러더라. 인생은 도박이라고.&lt;/p&gt;
&lt;p&gt;어디에 얼마를 걸지는 중요하지 않아.&lt;/p&gt;
&lt;p&gt;단지 믿음이 가는 곳에 거는거지.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;난 주저만 했던 인생을 살았던 건 아닐까?&lt;/p&gt;
&lt;p&gt;믿음이 가는 곳은 어디었나? 남들 거는대로 따라 걸고 있지 않은가?&lt;/p&gt;
&lt;p&gt;주어진 삶에서 나름 열심히 살았다 생각하는데 지금 만족해? 잘 모르겠네.&lt;/p&gt;
&lt;p&gt;내게 소중한 것과 인생의 의미는 뭔지 생각하고 사니? 음...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또 그러더라.&lt;/p&gt;
&lt;p&gt;실제로 그것이 그렇다는 것은 중요하지 않아.&lt;/p&gt;
&lt;p&gt;정말로 중요한건 너의 생각이지.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다. 나의 생각은 나의 가능성의 세계를 변화시킨다. A라는 가능성이 없다고 생각하는 순간 내게서 A라는 세계는 소멸된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;당신이 만들고 싶은 세계는 무엇인가?&lt;/p&gt;
&lt;p&gt;그리고 소멸시키고 싶은 세계는 무엇인가?&lt;/p&gt;
&lt;p&gt;부정적인 생각이 이 두 세계를 자꾸 바꿔치려하지 않는가?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;당신의 생각이 바른 믿음이라면 그것을 믿고 그리고 모든 것을 걸어라.&lt;/p&gt;</description>
      <category>일상</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/685</guid>
      <comments>https://thrillfighter.tistory.com/685#entry685comment</comments>
      <pubDate>Fri, 19 Feb 2021 02:03:00 +0900</pubDate>
    </item>
    <item>
      <title>종로구, 강남구, 해운대구 건물에 대한 통계, 데이터 분석 자료</title>
      <link>https://thrillfighter.tistory.com/680</link>
      <description>&lt;p&gt;개발 중인 프로젝트를 위한 데이터 분석 과정 중 흥미로운 부분을 공유하는 글입니다. 프로젝트 성격상 분석한 모든 부분을 공유하지 못하여 아쉽네요.&lt;br /&gt;한국의 건축물은 약 1000만개 정도 된다 짐작됩니다. 모든 데이터를 분석하기보다는 분석은 지역별 특징이 있을 거라 짐작되는 샘플 데이터 (종로구, 강남구, 해운대구)를 하였고, 데이터 분석 프레임웍은 &lt;code&gt;pandas&lt;/code&gt;, 시각화는 &lt;code&gt;matplotlib.pyplot&lt;/code&gt;, &lt;code&gt;searborn&lt;/code&gt;을 적절히 섞어 사용하였습니다.&lt;br /&gt;코드는 복잡하므로 생략하고 시각화 결과만 공유합니다.&lt;/p&gt;
&lt;h3&gt;종로구, 강남구, 해운대구 에 대한 건축물 vs 공동주택 개수 히스토그램&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;1296&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H1ObY/btqUBvdB070/YPVfev5HHvQG7ax83BI0Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H1ObY/btqUBvdB070/YPVfev5HHvQG7ax83BI0Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H1ObY/btqUBvdB070/YPVfev5HHvQG7ax83BI0Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH1ObY%2FbtqUBvdB070%2FYPVfev5HHvQG7ax83BI0Kk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;1296&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;여기에서 공동주택이라함은 아파트와 다세대 주택, 연립주택을 포함하는 개념입니다. 다세대 주택과 연립주택의 차이는 연면적의 차이입니다. 데이터를 분석해보면 연립주택은 오래된 형태며 최근에는 거의 안지어 지고 있다고 봐도 무방합니다.&lt;/li&gt;
&lt;li&gt;위 히스토그램에서 2000여년 전후로 해운대구와 강남구의 공동주택 건설이 번갈아 붐을 이루었는데 이 때는 다세대 주택의 순간 비율이 높습니다. 하지만 바로 cliff 상태로 떨어집니다. 그리고 많은 비율이 아파트로 지어집니다.&lt;/li&gt;
&lt;li&gt;종로구의 오래된 건축물의 비율이 높네요. 재개발 입지선정에 대한 예측을 할만한 가치가 있다고 판단되네요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;공동주택의 건축물 비율&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;463&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eQfbYh/btqUtWjfxYd/PUkZEzlKikkn81ILo06YZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eQfbYh/btqUtWjfxYd/PUkZEzlKikkn81ILo06YZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eQfbYh/btqUtWjfxYd/PUkZEzlKikkn81ILo06YZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeQfbYh%2FbtqUtWjfxYd%2FPUkZEzlKikkn81ILo06YZ1%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;463&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;아파트의 비율은 공동주택의 15%를 차지하지만 건축물의 사용기준으로 따지면 사실상 매우 높은 비율입니다. 아파트 건물 1면 수십세대가 사니까요.&lt;/li&gt;
&lt;li&gt;연립주택은 연면적이 660m^2이상인 건축물로 최근에는 지어지지 않는다고 볼 수 있습니다. 수십년 후엔 없어질 것 같네요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;년도별 3개구의 내진 설계 통계 자료입니다&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;1212&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1IDCw/btqUwyWexaP/h4eAPCK5WOCY3xy2wtkDm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1IDCw/btqUwyWexaP/h4eAPCK5WOCY3xy2wtkDm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1IDCw/btqUwyWexaP/h4eAPCK5WOCY3xy2wtkDm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1IDCw%2FbtqUwyWexaP%2Fh4eAPCK5WOCY3xy2wtkDm1%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;1212&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위 히스토그램에는 공통점이 있습니다. 1990년 경부터 내진설계가 된 건물이 지어지기 시작했다는 것입니다. 이유가 있을 것 같아 찾아보니 다음과 같은 자료가 있습니다. &lt;a href=&quot;https://namu.wiki/w/%EB%82%B4%EC%A7%84%EC%84%A4%EA%B3%84&quot;&gt;내진설계제정&lt;/a&gt;&lt;br /&gt;요약하면 1985년에 멕시코 대지진 이후 1988년 내진설계 의무규정을 도입했다고 합니다.&lt;/li&gt;
&lt;li&gt;참고로 분설결과 데이터 상에서는 최근 아파트들은 거의 모두 내진설계가 되어있는 것 같습니다. 단 내진 설계가 되어있더라도 내진(강도)을 견디는 강도에 대한 기록은 많이 누락되어 보입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;아파트의 건축구조와 같은 다양한 특성들에 대한 분석 역시 하였지만 행정기록상 간단히 표히거나 동일 카테고리 다른 표현, 또는 상위 카테고리 표현으로 세부적인 구분을 하기 힘든 자료들도 있었습니다. 더 자세히 보여주는 데이터들도 있는 듯 하여 분석이 더 필요할 듯 보입니다.&lt;/p&gt;</description>
      <category>프로그래밍/개발기록</category>
      <category>공동주택</category>
      <category>내진설계</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/680</guid>
      <comments>https://thrillfighter.tistory.com/680#entry680comment</comments>
      <pubDate>Sun, 24 Jan 2021 13:18:18 +0900</pubDate>
    </item>
    <item>
      <title>해외에서 핫하다는 golang 입문 정리 첫번째</title>
      <link>https://thrillfighter.tistory.com/677</link>
      <description>&lt;p&gt;안녕하세요.&lt;/p&gt;&lt;p&gt;최근 웹 개발 위주로&amp;nbsp;해가면서 많은 것들을 배워가고 있습니다.&lt;/p&gt;&lt;p&gt;개인적으로 웹 개발을 위한 언어와 프레임웍의 선택은 단순할 수록&amp;nbsp;좋다고 생각합니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;그리고 언어의 선택은 성능을 따져&amp;nbsp;선택하겠지만 일반적인 웹에서의 언어의 성능은&amp;nbsp;그리 크게 작용하지 않는다고 생각합니다.&lt;/p&gt;&lt;p&gt;일반적인 성능의 지표는 for 루프겠죠. 하지만 네트워크 상에서 지연되는 시간, DB,&amp;nbsp;I/O 처리가 웹 서버의 거의 전부라고 봐도 무방합니다.&lt;/p&gt;&lt;p&gt;그런 의미에서 언어의 성능보다는 요청 처리 모델, DB의 효율적인 설계가 더 중요하다고 생각합니다.&lt;/p&gt;&lt;p&gt;또한 웹 프레임워크가 어떤 방식으로 요청을 처리하는지에 따라서 장단점이 갈리기 때문에 웹 프레임웍의 선택도 따져봐야 하긴 합니다.&lt;/p&gt;&lt;p&gt;이렇게 보통은 DB의 설계 또는 캐시의 올바른 설정 작업의 비동기적인 처리 등이 성능에 미치는 영향의 90% 이상을 차지한다 생각합니다.&lt;/p&gt;&lt;p&gt;그럼에도 Golang의 성능 대비 단순함은 매력적인 요소일 수 밖에 없습니다.&lt;/p&gt;&lt;p&gt;아직 Golang에 대해서 잘 모르지만 기본 문서를 보고 느낀점을 적어보려 합니다. 고 언어를 배우는 이유는 일종의 취미생활입니다. 앞으로 만들 웹 서비스들을 포팅하면서 성능비교도 해볼 생각입니다. 두더지 넘 귀여워서 배워보고 싶어지지 않나요?&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 655px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/998A16455FCA61551C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F998A16455FCA61551C&quot; width=&quot;655&quot; height=&quot;414&quot; filename=&quot;go.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;고 언어의 아이콘은 아주 귀엽습니다. 두더지 요녀석! 하고 매일 30분씩 공식문서를 읽다보니 전체적인 육곽이 잡히네요.&lt;/p&gt;&lt;p&gt;오늘은 간략하게만 정리하고 세세하게 정리하려고합니다.&lt;/p&gt;&lt;p&gt;C언어와 닮았지만 차이를 미묘한 차이점이 존재합니다.&lt;/p&gt;&lt;p&gt;예를들어&lt;/p&gt;&lt;p&gt;1.&amp;nbsp;&lt;/p&gt;&lt;p&gt;C언어라면&lt;/p&gt;&lt;p&gt;int i;&lt;/p&gt;&lt;p&gt;고 라면&lt;/p&gt;&lt;p&gt;var i int&lt;/p&gt;&lt;p&gt;입니다.&lt;/p&gt;&lt;p&gt;타입명이 변수 명 뒤로 간다는 것이 주의해야 합니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 521px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9953E1405FCA61AB1B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9953E1405FCA61AB1B&quot; width=&quot;521&quot; height=&quot;376&quot; filename=&quot;golang.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;고 홈페이지에 있는 이미지입니다. 너무 귀엽네요. &lt;br /&gt;&lt;/p&gt;&lt;p&gt;2. 타입추론이 가능하다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;i := 5&lt;/p&gt;&lt;p&gt;와 같이 :을 추가하여 대입 값에 대한 타입추론을 가능하게 하여 타입명을 명시하지 않는 표현법입니다.&lt;/p&gt;&lt;p&gt;3. (do) while, until, 등의 문법이 없다. for 문으로 모든 걸 표한합니다.&lt;/p&gt;&lt;p&gt;3. 포인터가 있지만 포인터로 값에 접근 시 -&amp;gt; 가아닌 .으로 통일해서 접근합니다. 이 부분은 표현의 단순성을 위한 선택같습니다. 포인터를 모르는 상태에서 이부분은 어려울 수도 있을 것 같습니다.&amp;nbsp;자바와 C의 중간같네요.&lt;/p&gt;&lt;p&gt;4.환경 변수 GOPATH의 설정이 필요한다. 이건 프로젝트에 따라서 변경해야하는 건지 아직은 모르겠지만 좀 불편한 부분 같기도 합니다.&lt;/p&gt;&lt;p&gt;5.&amp;nbsp; header파일이 없고 package를 웹 상의 경로에서 받아오는 형식입니다.&lt;/p&gt;&lt;p&gt;막상 쓰려니 이정도 밖에 생각이 안나네요. 고가 워낙 심플한 언어라 그렇습니다. 하지만 강력하지요.&lt;/p&gt;&lt;p&gt;정리하면 대략적인 것은 C언어와 닮아 있습니다.&lt;/p&gt;&lt;p&gt;한국에서는 아직까지는 외국에 비해서 사용처가 적은 듯 싶습니다. 구글에서 만들었으니 구글내에서는 많이 쓰겠죠.&lt;/p&gt;&lt;p&gt;다음에는 VS코드에서 환경을 설정하고 Hello World를 찍어보겠습니다.&lt;/p&gt;</description>
      <category>프로그래밍/golang</category>
      <category>go</category>
      <category>go 포인터</category>
      <category>Golang</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/677</guid>
      <comments>https://thrillfighter.tistory.com/677#entry677comment</comments>
      <pubDate>Sat, 5 Dec 2020 01:21:15 +0900</pubDate>
    </item>
    <item>
      <title>재즈에 빠지는 중입니다.</title>
      <link>https://thrillfighter.tistory.com/676</link>
      <description>&lt;p&gt;난 재즈가 좋다. 최근 다시 재즈를 듣는다. 계기가 있었다.&lt;/p&gt;&lt;p&gt;원래 작업을 할 때는 항상 유투브로 음악을 듣곤 하는데 얼마전 유투브가 접속불가(다운) 된 적이 있었다.&lt;/p&gt;&lt;p&gt;그래서 10여년전에 애용하던 재즈라디오닷컴(jazzradio.com)에 들어가 재즈를 틀어 놓았다.&lt;/p&gt;&lt;p&gt;그런데 이 때를 계기로&amp;nbsp;다시 애용하는 중이다.&lt;/p&gt;&lt;p&gt;재즈라디오도 무료는 아니다.&lt;/p&gt;&lt;p&gt;먼저 재즈라는 장르가 생각보다 많다. 악기 종류에 따라 보컬이 있냐없냐에 따라 또는 음악의 장르에 따라서 다양한 형식이 있다.&lt;/p&gt;&lt;p&gt;여기에선 다양한 장르를 카테고리 별로 서비스를 하는데 유료회원은 다 들을 수 있고 무료회원은 로테이션으로 그날 잠금해제된 장르만 들을 수 있다. 그래도 5개정도 풀리니 부족함 없고 그날그날 선택의 여지가 적으니 오히려 편한 것 같다. 난 보통 이름에 smooth가 들어간 장르를 듣는 편이다.&lt;/p&gt;&lt;p&gt;그리고 간간히 광고가 나오는데 유투브 처럼 스킵할 필요도 없고 광고 길이도 짧다. 매일 똑같은 광고 3개가 반복해서 나온다. 초콜렛, 자동차, 그리고 한개는 잘 모르겠다. 들을만 하다.&lt;/p&gt;&lt;p&gt;오늘은 시작부터 분위기 있는 음악들이 많이 흘러나와 기록을 남기려고 글을 쓴다.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=oYb4lhgnfIw&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;George Benson - Kisses In The Moonlight&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/results?search_query=Ella+Fitzgerald+-+Misty&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Ella Fitzgerald - Misty&lt;/a&gt;(보컬)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=JJefY15IOFo&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Ella Fitzgerald - Misty&lt;/a&gt;(피아노)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Qy7z_oiN6nQ&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Nothing's Gonna Change My Love For You&lt;/a&gt;&lt;/p&gt;&lt;p&gt;재즈 내에서도 장르가 워낙 다양해서 호불호가 있을 것이다. 난&amp;nbsp; 클래식 재즈보단 목소리가 부드러운 보컬이 들어가거나 낮은 음역대의 악기들을 선호한다. 피아노도 너무 고음이 들어가면 거슬리고 관악기도 마찬가지다. 관악기는 보통 트럼펫보다는 섹소폰이 마음을 편하게 해준다.&lt;/p&gt;</description>
      <category>일상</category>
      <category>Ella Fitzgerald - Misty</category>
      <category>유투브</category>
      <category>재즈</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/676</guid>
      <comments>https://thrillfighter.tistory.com/676#entry676comment</comments>
      <pubDate>Wed, 2 Dec 2020 09:44:39 +0900</pubDate>
    </item>
    <item>
      <title>애드센스 수익 증가를 위해 광고 네트워크 차단을 하라는데 정말 좋은건지?</title>
      <link>https://thrillfighter.tistory.com/674</link>
      <description>&lt;p&gt;안녕하세요. 오늘은 애드센스에 관한 그간 제가 경험한 내용을 가볍게 이야기해보려합니다.&lt;/p&gt;&lt;p&gt;지금부터 하는 이야기는 제 경험적으로 얻은 저만의 생각이므로 절대적인 것은 아니니 납득 안하셔도 괜찮습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;제가 애드센스를 알게된거 거의 10여년 전이었고 소소하게 블로그를 하면서 애드센스와 수익에 관한 경험을 해왔습니다.&lt;/p&gt;&lt;p&gt;가끔은 수익에 대해 욕심을 부리기도 하고 누군가의 블로그를 읽고 수익이 감소했을 때는 이래저래 해봐라 이런이야기들을 많이 보고 따라하게 됩니다.&lt;/p&gt;&lt;p&gt;결론만 우선 말해보면&amp;nbsp;함부로 따라하지 마시라는 겁니다.&lt;/p&gt;&lt;p&gt;물론 그 방법이 수익증가를 가져올 수도 있을 겁니다. 그러나 제가 지금 경험한 통계치로는 수익증가는 그리 크지 않습니다. 그러나 설정을 변경한다는 것은 광고주나 애드센스 입장에서는 기존과 다른 것으로 인식하여 초기화가 잠시나마 일어날 수가 있습니다.&lt;/p&gt;&lt;p&gt;스마트 프라이싱이라는 말도 많이합니다. 이건 구글 애드센스 도움말에도 있는 말로서 실제로 그 알고리즘에 어떻게 일어나는지는 많은 사람들의 경험으과 추측으로 만들어진 지식입니다.&lt;/p&gt;&lt;p&gt;다시말해 환경설정을 다시했을 땐&amp;nbsp;얻기 보다는 잃기가 쉽고, 이득의 폭은 보통은&amp;nbsp;그리 크지 않다는 겁니다.&lt;/p&gt;&lt;p&gt;물론 특별한 카테고리를 다루는 블로거 또는 아주아주 블로그가 커졌을 때는 광고에 대한 설정을 좀 더 잘 다룰 필요는 있습니다. 방문자가&amp;nbsp;많을수록&amp;nbsp;작은 변화에 수익 변동은&amp;nbsp;클 수 밖에 없거든요. &lt;b&gt;이런 경우는 아주 조금씩 변화를 주면서 기간을 두고 통계치를 내어 결정해야합니다.&lt;/b&gt;&lt;/p&gt;&lt;p&gt;그러면 광고에 대해서 어떤 변화들을 줄 수 있는지 알아보겠습니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F53F395FBE07D62B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F53F395FBE07D62B&quot; width=&quot;500&quot; height=&quot;309&quot; filename=&quot;1.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;1. 먼저 각각의 광고를 선별하여 차단할 수 있습니다. 또는 광고주의 URL을 차단할 수도 있습니다.&lt;/p&gt;&lt;p&gt;2 . 또한 구글 애드센스에는 카테고리별로 광고를 선택하여 게재할 수 있습니다. 기본적으로 모두 오픈되어있지만 내가 어떤 카테고리를 차단한다면 관련 광고는 나오지 않게 할 수도 있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;3. 그리고 특정 광고 네트워크(오늘의 주제네요)를 차단한다면 해당 광고네트워크가 송출하는&amp;nbsp;광고가 모조리 차단됩니다.&lt;/p&gt;&lt;p&gt;이렇게 크게 세가지 단계로 차단을 할 수 있는데 보통 애드센스를 오래한 분들이거나 커뮤니티를 가보면 광고 네트워크 차단이 수익증가를 가져온다는 노하우를 알려주기도 합니다.&lt;/p&gt;&lt;p&gt;그런데 장담컨데 근거없다는 생각이 듭니다. 이건 케이스바이케이스이며 대부분 이득을 못봅니다. 이득을 볼 정도라면 단순 차단이 아닌 위에서 말했듯이 전략적으로 통계를 내고 계산을 하면서 결정해야합니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 634px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999AF03F5FBE08212B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999AF03F5FBE08212B&quot; width=&quot;634&quot; height=&quot;333&quot; filename=&quot;2.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;이유를 말씀드리면...&amp;nbsp;&lt;/p&gt;&lt;p&gt;애드센스의 광고는 경쟁을 통해서 단가를 정하게되어있습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;A라는 네트워크와 B라는 네트워크가 있다면 A와&amp;nbsp; B에&amp;nbsp;속한 광고들이 모두 동일한 조건에서 통해서 경쟁을 합니다. 네트워크 단위가 아닌 각 광고단위입니다!&amp;nbsp;따라서&amp;nbsp;광고 네트워크가 많으면 많을 수록&amp;nbsp;경쟁이 높아지기 때문에 단가가 올라갈 수밖에 없습니다.&lt;/p&gt;&lt;p&gt;보통 특정 광고 네트워크를 많이 언급하며 차단하라고 합니다. 실제로도 애드센스 보고서에서 자신의 사이트에 광고를 게시하는 네트워크를 보면 특정 네트워크의 단가가 아주 낮습니다. 그런데 잘보면 자신의 사이트가 아주 저평가되어있지 않는 이상은 일부 단가 낮은 네트워크의 광고&amp;nbsp;게제비율도 보통은 높지 않습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;다시말해 단가가 낮은 네트워크가 광고를 게제할 기회가 있다는 것은 해당 페이지 또는 특정 상황에서 높은 단가를 주는 네트워크도 자신의 사이트나 글에&amp;nbsp;낮은 입찰을 했다는 겁니다.&lt;/p&gt;&lt;p&gt;이런 상황에서 단가가 낮은 네트워크가 몇% 차지한다고해서 차단해버리면 어떻게 될까요?&lt;/p&gt;&lt;p&gt;경쟁은 그만큼 줄어들고 이미 단가를 높게 책정해왔던&amp;nbsp;네트워크도 입찰경쟁에서 낮은 가격에 유리하게 입찰할 수 있게됩니다. 물론 그 비율이 낮지만 최소한의 영향이라도 받게 되어있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9923DB445FBE09E32D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9923DB445FBE09E32D&quot; width=&quot;500&quot; height=&quot;458&quot; filename=&quot;money-40603_960_720.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;쉽게말해 전체적인 광고 단가가 낮아지는 것이죠.&lt;/p&gt;&lt;p&gt;분명 광고 단가는 시기(봄, 여름, 가을, 겨울, 주말, 평일, 명절, 사회적인 분위기, 글이나 사이트의 주제)&amp;nbsp; 등등에도 영향을 받습니다.&lt;/p&gt;&lt;p&gt;그러면 네트워크를 차단했을 때 수익이 올라가는 사람은 뭐냐? 하실겁니다.&lt;/p&gt;&lt;p&gt;이건 단순히 제 생각이지만 다음과 같은 시나리오일 겁니다.&lt;/p&gt;&lt;p&gt;보통의 블로그들은 특정 글에 종속되기 보다는 다양한 주제를 씁니다. 그래서 광고도 다양하게 나오죠...&amp;nbsp;그리고 클릭율이나 클릭 단가도 평균적으로 높지는 않습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;그런데&amp;nbsp;수익이 낮거나 한 네트워크에서 송출되는 광고는 보통 관련 주제보다는 그냥 이것 저것 보내는 경향이 있는 것 같습니다. 단가가 낮기 때문에 그런 것 같습니다. 그래서 클릭율 자체가 낮습니다. 보통 이런 형태의 블로그들은 저수익 광고 네트워크를 차단하면 전체적인 광고입찰 경쟁이 낮아져서&amp;nbsp;수익이 감소될 거라 짐작됩니다.&lt;/p&gt;&lt;p&gt;하지만 특정 주제나 아주 단가 높은 주제를 자주 쓰거나 한 블로그들이면서 아직 사이트가 커가는 상황이라면&amp;nbsp; 사이트 주제와와 무관한 광고를 송출하는 광고네트워크 차단은 매우 실효성 있다고 생각됩니다. 왜냐하면 단가 상승이 아직 되어있지 않더라도 클릭율 자체를 높일 수 있기 때문이죠. 이건 재방문비율하고도 관련되어 있어보입니다. 아무튼 그렇습니다.&lt;/p&gt;&lt;p&gt;하지만 일반적인 블로그라면 역효과가 나서 경쟁율도 낮아지고&amp;nbsp;클릭율은 원래 낮고..클릭 단가도 낮고,..&amp;nbsp;이런 상황이 오는 겁니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;이건 가정된 한 가지 상황이며&amp;nbsp;분명 다른 상황들도 있을 겁니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994D01485FBE0BA92D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F994D01485FBE0BA92D&quot; width=&quot;500&quot; height=&quot;347&quot; filename=&quot;kk.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;한가지 이유를 더 대자면 보통은 한 사이트에 하나의 광고가 아닌 여러개의 광고가 광고위치를 두고 동시 입찰을 하기 때문에 낮은 수익을 가져오는 광고 네트워크의 광고는 명당보다는 사이드나 잘 안보이는 위치에 게재될 가능성이 많습니다. 어짜피 어느정도 게재되는 것은 큰 영향을 미치지 않습니다. 보통 저런 네트워크들은 명당보다는 사이드 자리에 만족하는 것으로 봐도 무방합니다. 굳이 차단한다면 그 자리는 더 낮은 단가의 광고가 차지할 수 밖에 없겠죠.&lt;/p&gt;&lt;p&gt;따라서 제 의견은 초반에는 아주 민감한 카테고리(보시면 알겁니다.).. 한두개 차단하고 광고네트워크를 건드려보고 싶다면&amp;nbsp;나중에 어느정도 블로그가 커졌을 때 생각하거나 천천히 하나씩 변화를 주면서 통계치를 계산하는 것이 좋습니다.&lt;/p&gt;&lt;p&gt;문제는 워낙 적은 방문자라면 통계자체가 의미가 없어지기 때문에 그냥 두는 것이 좋다는 생각이 듭니다. 보통 1년이면 1년 내내 동일한 수익이 아니기 때문에 시기별로 수익이 어떤지도 같이 계산해서 결정해야하거든요.&amp;nbsp;&lt;/p&gt;&lt;p&gt;분명 언젠가는 설정해야할 내용이지만 그것 보다는 자신의 사이트와 블로그를 더 전문적으로 만드는 것이 선행되어야할 겁니다.&lt;/p&gt;</description>
      <category>애드센스</category>
      <category>애드센스</category>
      <category>애드센스 광고네트워크 차단</category>
      <category>애드센스 수익</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/674</guid>
      <comments>https://thrillfighter.tistory.com/674#entry674comment</comments>
      <pubDate>Wed, 25 Nov 2020 16:46:21 +0900</pubDate>
    </item>
    <item>
      <title>html/css 가상요소선택자와 가상클래스선택자의 차이점</title>
      <link>https://thrillfighter.tistory.com/672</link>
      <description>&lt;article class=&quot;markdown-body entry-content&quot; itemprop=&quot;text&quot;&gt; &lt;p&gt;선택자라는 단어의 의미를 짚어보면 타겟이 있어야 한다는 것을 알 수 있다. id선택자, class 선택자, 태크 선택자는 각 타겟들이 요소의 속성인&amp;nbsp;id, class, tag가 된다.&lt;/p&gt;&lt;p&gt;하지만 가상 클래스 선택자,&amp;nbsp;또는 가상요소선택자는 언뜻 이해가 안되기도 한다. 왜 가상, 요소가 붙었나..&lt;/p&gt;&lt;p&gt;그럼 좀 더 의미를 되새겨보자.&lt;/p&gt;&lt;p&gt;DOM에서 &lt;b&gt;요소&lt;/b&gt;는 실체가 있는 것들이다. 그리고 &lt;b&gt;클래스&lt;/b&gt;나 id는 출생년도나, 주민등록번호처럼&amp;nbsp;요소를 카테고리할 수 있는 속성으로 볼 수 있다. 분명 이 둘은 다른 것임을 알 수 있다.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;가상클래스&lt;/b&gt;&lt;/span&gt;는 이런 속성이 실제로 마크업 상에 명시되지는 않은 속성을 지칭하는 말이다. 예를들어 마우스를 올려놓았을 때나 클릭을 했을 때를 하나의 속성으로 본다는 것이다. 이런 속성들은 사용자가 만드는 행동이라 그때 그때 다르다. 한마디로 동적일 수 있다. 이런 순간적인 상태를 선택하여 CSS를 적용하기&amp;nbsp;위해 가상클래스 선택자가 쓰인다.&lt;/p&gt;&lt;p&gt;focus,&amp;nbsp;checked도 어렵지 않게 이해된다. focus 역시 사용자에 의해 포커싱되고, checked도 마찬가지다.&lt;/p&gt;&lt;p&gt;물론 nth-last-child는 언뜻 이해가 안될 수 있다. 곱씹어 살펴보면 class나 id로 지목한 것이 아니라 단지&amp;nbsp;마지막에 있는 상태를 지칭한다. 요소의 배치에 따라서 달라질 수 있기 때문에 id와 class로 선택하는 것과 다르다.&lt;/p&gt;&lt;p&gt;용어가 중요하다기 보다는 대략 이런 맥락으로 선택자가 만들어진 거구나 정도로 이해하면 좋을 듯 하다.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;&lt;b&gt;가상요소선택자&lt;/b&gt;&lt;/span&gt;는 더 쉽게 이해할 수 있다. 문서에 없는 요소를 만들어 선택을 한다는 것이다.&lt;/p&gt;&lt;p&gt;가장 자주 쓰는 선택자는 after가 있다. after 선택자는 지정된 요소에 가상요소를 자식으로 마지막에 붙인다. 이렇게 가상으로 만들어진 요소는 실제로 보이지 않더라도 css를 적용하여 특정 효과를 줄 수 있다. 다음글은 after의 가장 대표적인 사용 예로 아주 중요하다. 모르면 반드시 읽어보면 좋겠다.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://thrillfighter.tistory.com/671&quot; target=&quot;_blank&quot;&gt;프로그래밍/HTML5] - css float 속성 해제 after 대한 이해&lt;/a&gt;&lt;/p&gt;&lt;p&gt;first-letter역시 가상요소 선택자다. 예를들어&lt;/p&gt;&lt;div class=&quot;txc-textbox&quot; style=&quot;border-style: solid; border-width: 1px; border-color: rgb(159, 211, 49); background-color: rgb(231, 253, 181); padding: 10px;&quot;&gt;&lt;p&gt;&amp;lt;p&amp;gt; 안녕하세요 &amp;lt;/p&amp;gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;위와 같은&amp;nbsp; p태그 요소에 내용의 첫 번째 글자만 선택하여 색을 바꾸고 싶다면,&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 586px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99378B4E5FB7BC1019&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99378B4E5FB7BC1019&quot; width=&quot;586&quot; height=&quot;454&quot; filename=&quot;1.jpg&quot; filemime=&quot;image/jpeg&quot; style=&quot;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;이렇게 하면 된다.&lt;/p&gt;&lt;p&gt;안녕하세요 라는 글자는 p태그 자체가 아니라 포함된 내용이므로 가상요소 선택자가 아니면 선택할 방법이 없다.&lt;/p&gt;&lt;p&gt;&lt;b style=&quot;&quot;&gt;'안'&lt;/b&gt;이라는 영역만 가상요소로 만들어(::first-leter 선택자로)&amp;nbsp;선택하여 색을 입힌 것이다.&lt;/p&gt;&lt;h3&gt;그 외에 차이점&lt;/h3&gt;&lt;p&gt;이 두 선택자는 문법적인 차이점도 있는데 콜론의 개수다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;가상 클래스 선택자는 콜론 한개, 가상 요소 선택자는 콜론이 두개다.&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 580px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99FB36475FB7BF5D20&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99FB36475FB7BF5D20&quot; width=&quot;580&quot; height=&quot;376&quot; filename=&quot;2.png&quot; filemime=&quot;image/jpeg&quot; style=&quot;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;단 after와 before는 어떤 이유에선지 :콜론한개로 쓰일 수 있다.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;가상 요소 선택자&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/pseudo-classes&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;가상 클래스 선택자&lt;/a&gt;&lt;/p&gt;&lt;p&gt;위에서 각 선택자의 종류들이 나와 있다.&lt;/p&gt;&lt;p&gt;본 블로그에서도 하나씩 정리할 것이다.&lt;/p&gt;&lt;/article&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>프로그래밍/HTML5</category>
      <category>After</category>
      <category>Before</category>
      <category>css</category>
      <category>html</category>
      <category>가상요선택자</category>
      <category>가상클래스선택자</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/672</guid>
      <comments>https://thrillfighter.tistory.com/672#entry672comment</comments>
      <pubDate>Fri, 20 Nov 2020 22:09:11 +0900</pubDate>
    </item>
    <item>
      <title>css float 속성 해제 after 대한 이해</title>
      <link>https://thrillfighter.tistory.com/671</link>
      <description>&lt;article class=&quot;markdown-body entry-content&quot; itemprop=&quot;text&quot;&gt; &lt;p&gt;안녕하세요. float는 정말 자주 사용하는 속성입니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;float를&amp;nbsp;태그요소에 적용하면 &amp;nbsp;block 속성을 없애버리는 특성을 가졌습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;그리고 자신의 크기만큼만 공간을 차지하며 투명인간처럼 무시되게 되는 속성입니다.&lt;/p&gt;&lt;p&gt;이에 대한 자세한 설명은 제 블로그의 다음 글을&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://thrillfighter.tistory.com/484&quot; target=&quot;_blank&quot;&gt;html css float 속성 정리&lt;/a&gt;&amp;nbsp;참고하시길 바랍니다.&lt;/p&gt;&lt;p&gt;아무튼 이런 성질로 인해&amp;nbsp;다음 요소들이 딸려서 위로 올라오게 됩니다. 보통 웹 페이지의 네비게이션 바의 메뉴를 float:left 로 많이 만들어서 잘못해서 아래 요소들이 딸려올라오는 문제을 자주 겪는 것 같습니다.&lt;/p&gt;&lt;p&gt;제&amp;nbsp;경우는 메뉴바 또는&amp;nbsp;기타 웹의 전체 구성 등을 만들 때&amp;nbsp;diplay: flex;를 사용해서 float를 자주 쓸일이 없습니다. 개인적으로 flex는 반응형 웹을 깔끔한 코드로 만들 수 있다고 생각합니다.&lt;/p&gt;&lt;p&gt;그럼에도 이 글을 쓰는 이유는 after라는 의사요소를 선택하는 선택자 때문입니다.&amp;nbsp; after를 잘 사용하면 툴팁이나 기타 다양한&amp;nbsp;활용을 할 수 있습니다.&lt;/p&gt;&lt;p&gt;우선 float를 사용하여 문제가 발생하는 예제를 살펴보고 after로 해결하는 방법을 설명하고자 합니다.&lt;/p&gt;&lt;h4&gt;float 문제 발생&lt;/h4&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 659px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99CBC0365FB674EF1B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99CBC0365FB674EF1B&quot; width=&quot;659&quot; height=&quot;443&quot; filename=&quot;1.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;nav 내부에 div 태그가 있고 모두 float: left 속성을 주었습니다. 결과는 다음과 같습니다.(img 속성은 block 속성이 아니어서 float을 적용하는 의미가 없기 때문에&amp;nbsp;div로 감싼 후 실험했습니다. 이미지를 버튼이라고 생각하면 편합니다.&amp;nbsp;block에 대해 잘 모르시면 다음 글을 참고하세요&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://thrillfighter.tistory.com/468&quot; target=&quot;_blank&quot;&gt;레이아웃 속성(display, inline, block)&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 618px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999762395FB673131B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999762395FB673131B&quot; width=&quot;618&quot; height=&quot;446&quot; alt=&quot;float:left가 적용되었을 때&quot; filename=&quot;2.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;span class=&quot;cap1&quot; style=&quot;display: block; max-width:100%; &quot;&gt;float:left가 적용되었을 때&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;글들이 영향을 받아 위로 딸려올라갔습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;그럼버튼(그림)에 float 속성을 주지 않았을 때의 결과를 우선 살펴보고 해결해보도록 하죠.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 570px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99303D375FB674A91A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99303D375FB674A91A&quot; width=&quot;570&quot; height=&quot;524&quot; alt=&quot;float 속성이 없을 때&quot; filename=&quot;3.png&quot; filemime=&quot;image/jpeg&quot; original=&quot;yes&quot;/&gt;&lt;span class=&quot;cap1&quot; style=&quot;display: block; max-width:100%; &quot;&gt;float 속성이 없을 때&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;원래 글들은 버튼(이미지)과&amp;nbsp;다른 라인에 있어야 정상인데 이상합니다.&lt;/p&gt;&lt;p&gt;심지어 nav 내부에 있는 요소들에게 적용된 float 속성이 그 외부의 다음 요소까지 영향을 주고 있습니다. 이 때 우리는&amp;nbsp;영향을 받은&amp;nbsp;외부 요소에서&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;txc-textbox&quot; style=&quot;border-style: solid; border-width: 1px; border-color: rgb(159, 211, 49); background-color: rgb(231, 253, 181); padding: 10px;&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-size: 12pt;&quot;&gt;clear: left;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;p style=&quot;text-align: left;&quot;&gt;를 적용하여 해결할 수 있습니다.&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 500px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/996E7D4D5FB677AD1F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F996E7D4D5FB677AD1F&quot; width=&quot;500&quot; height=&quot;299&quot; filename=&quot;4.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;해결이 간단히되었지만 보통 이렇게 하면 완벽한 해결책이 아닙니다. nav에 있는 요소가 원인인데 해결을 외부에서 했다면 외부 환경이 변했을 때 그 문제는 다시 발생하게 됩니다. 쉽게 말해 글들이 다른 요소로 대체된다면 대체될 요소에도 clear: left를 적용해야합니다. 위 예제는 간단하지만 많은 요소들이 복잡하게 얽혀있을 땐 원인 파악도 힘들고&amp;nbsp;복잡해 지죠.&lt;br /&gt;&lt;/p&gt;&lt;h4&gt;문제는 문제가 발생한 곳에서 해결하기&lt;/h4&gt;&lt;p style=&quot;text-align: left;&quot;&gt;이 문제를 해결하는 방법은 4~5가지 정도가 됩니다. overflow hidden은 제가 쓴 글에도 있습니다.&amp;nbsp;그런데 저도 다 알지 못합니다. 알 필요도 없구요. 다 알려고 하는 순간 개발자는 다칩니다. 그냥 필요한 것만 키핑하기에도 바쁜게 개발자입니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;제가 볼 때 after를 활용한 해결책이 가장 직관적으로 보였습니다. 다음이 그 코드입니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 698px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990794465FB679A114&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990794465FB679A114&quot; width=&quot;698&quot; height=&quot;450&quot; filename=&quot;5.jpg&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;여기에서 after는 간단히 설명하면 의사요소입니다. 가상의 요소입니다. 가상의 요소를 붙여서 거기에 clear: after를 한 겁니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;그런데 이상하건&amp;nbsp;왜 float가 적용된 요소의 부모(nav)에 가상요소 선택자를 사용했을까요?&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;가상 요소 선택자 after는 적용된 요소 내부에 가상요소를 만들기 때문입니다. 크롬 브라우저를 사용하시면 F12를 눌러보셔서 확인해 보실 수 있습니다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 473px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9979043C5FB67B491D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9979043C5FB67B491D&quot; width=&quot;473&quot; height=&quot;372&quot; filename=&quot;7.jpg&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;야스오가 아닌 야스오의 부모에 after를 적용하면 야스오의 형제가 하나 생깁니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;이렇게 가상 요소에&amp;nbsp; clear: left가 적용되어 float로 인한 오류는 깔끔하게 해결되었습니다.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;가장 혼동하시는 부분이 바로 after 적용시 가성요소의 생성위치인 듯 보입니다. 적용된 요소의 내부에 생기는 것을 기억하시기 바랍니다.&lt;/p&gt;</description>
      <category>프로그래밍/HTML5</category>
      <category>::after</category>
      <category>After</category>
      <category>css float 해제</category>
      <category>f</category>
      <category>float</category>
      <category>가상요소선택자</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/671</guid>
      <comments>https://thrillfighter.tistory.com/671#entry671comment</comments>
      <pubDate>Thu, 19 Nov 2020 23:19:20 +0900</pubDate>
    </item>
    <item>
      <title>리액트 react setState는 비동기로 동작한다.</title>
      <link>https://thrillfighter.tistory.com/670</link>
      <description>&lt;p style=&quot;text-align: left;&quot;&gt;리액트의 가장 기본적이고 핵심적인 상태값을 변경하는 setter&amp;nbsp;함수(메소드)에 대해서 정리하고자&amp;nbsp;한다.&lt;/p&gt;&lt;p&gt;이 setter 메소드는 클래스형 컴포넌트라면 this.setState가 빌트인 되어 있고 함수형 컴포넌트라면 직접 정의해서 사용할 수 있다.&lt;/p&gt;&lt;p&gt;클래스 방식이나 함수형 모두 글의 맥락을 보면 동일하므로 여기에서는 클래스형 컴포넌트를 사용하여 설명한다.&lt;/p&gt;&lt;p&gt;그러면 왜 setState는&amp;nbsp;비동기로 동작할까?&lt;/p&gt;&lt;p&gt;리액트에서는 상태값은 아주 핵심적이고 특별하다.&amp;nbsp;어떤 상태값이 변경되면 그 영향하에 있는 컴포넌트들은 렌더링이 된다. 그리고 어떤 컴포넌트가 렌더링되면 그 하위 컴포넌트들 역시 리렌더링 된다. 이렇게 연쇄직인 방법으로 데이터의 변화가&amp;nbsp;돔 요소에 실시간으로 반영되어 우리에게 보여지는 것이다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 535px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9993DA485FA785E41F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9993DA485FA785E41F&quot; width=&quot;535&quot; height=&quot;302&quot; filename=&quot;react.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;그러면 &lt;u&gt;왜 setState가 비동기를 동작하는지&lt;/u&gt;에 대한 답을 찾기 전에 우선&amp;nbsp;클래스형 &lt;u&gt;컴포넌트에서 상태값을 변경하는 예&lt;/u&gt;를 살펴보고 진행하도록 하겠다.&lt;/p&gt;&lt;p&gt;다음 코드를 보자.&lt;/p&gt;

&lt;pre style=&quot;color: rgb(0, 0, 0); background: rgb(255, 255, 255); user-select: auto;&quot;&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;import&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;Component&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt; from &lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 230); user-select: auto;&quot;&gt;react&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;

&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;class&lt;/span&gt; SetStateTest &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;extends&lt;/span&gt; Component &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;
    state &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;:&lt;/span&gt; &lt;span style=&quot;color: rgb(0, 140, 0); user-select: auto;&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;;&lt;/span&gt;

   onClick &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;
       &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;setState&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;:&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;state&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;count &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color: rgb(0, 140, 0); user-select: auto;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;,&lt;/span&gt; console&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 230); user-select: auto;&quot;&gt;state changed&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(105, 105, 105); user-select: auto;&quot;&gt;// 1**&lt;/span&gt;
   &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt; 

    render&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;
        console&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 230); user-select: auto;&quot;&gt;rendering...&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(105, 105, 105); user-select: auto;&quot;&gt;// 2** &lt;/span&gt;
        &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;
            &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;state&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;/&lt;/span&gt;div&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;lt;&lt;/span&gt;button onClick &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;onClick&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;/&lt;/span&gt;button&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;
    &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;
&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;

&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;export&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;default&lt;/span&gt; SetStateTest&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;//** 표시한 부분이 핵심이다.&lt;/p&gt;&lt;p&gt;버튼을 누르면 counter가 증가하고 화면에 couter값이 보여지는 어플이다.&lt;/p&gt;&lt;p&gt;setState의 두 번째 파라미터값은 마무리 함수로 state값이 변경된 후 실행된다고 생각하면 된다. 이렇게 setState와 render가 이루어지는 부분에 콘솔로 로그를 찍어보면&amp;nbsp;&lt;/p&gt;&lt;div class=&quot;txc-textbox&quot; style=&quot;border-style: double; border-width: 3px; border-color: rgb(0, 85, 255); background-color: rgb(255, 255, 255); padding: 10px;&quot;&gt;&lt;p&gt;state changed&lt;/p&gt;&lt;p&gt;rendering...&lt;/p&gt;&lt;/div&gt;&lt;p&gt;이렇게 state 값이 바뀐 후 렌더링이 일어난다. 여기까지는 어렵지 않다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h4&gt;이상한 반응&lt;/h4&gt;&lt;p&gt;//1**과 같은 코드를 두번 넣어보자.&lt;/p&gt;

&lt;pre style=&quot;color: rgb(0, 0, 0); background: rgb(255, 255, 255); user-select: auto;&quot;&gt;&lt;p&gt;   onClick &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;
       &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;setState&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;:&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;state&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;count &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color: rgb(0, 140, 0); user-select: auto;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;,&lt;/span&gt; console&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 230); user-select: auto;&quot;&gt;state changed&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(105, 105, 105); user-select: auto;&quot;&gt;// 1-1**&lt;/span&gt;
       &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;setState&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;:&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;state&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;count &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color: rgb(0, 140, 0); user-select: auto;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;,&lt;/span&gt; console&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 230); user-select: auto;&quot;&gt;state changed&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(105, 105, 105); user-select: auto;&quot;&gt;// 1-2**&lt;/span&gt;
   &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt; &lt;/p&gt;&lt;/pre&gt;&lt;p&gt;그런 후 실행화면에서 버튼을 눌러보면 다음과 같이 동작한다.&lt;/p&gt;&lt;p&gt;1. 버튼을 1회 클릭할 때 마다 화면에 보여지는 counter 값은 1씩 증가된다.&lt;/p&gt;&lt;p&gt;2. 버튼을 1회 클릭할 때마다&amp;nbsp;콘솔화면에 다음과 같이 표시된다.&lt;/p&gt;&lt;div class=&quot;txc-textbox&quot; style=&quot;border-style: double; border-width: 3px; border-color: rgb(0, 85, 255); background-color: rgb(255, 255, 255); padding: 10px;&quot;&gt;&lt;p&gt;state changed&lt;/p&gt;&lt;p&gt;state changed&lt;/p&gt;&lt;p&gt;rendering..&lt;/p&gt;&lt;/div&gt;&lt;p&gt;이런 현상이 일어나는 이유는 우리가 처음에 물음을 던진 주제가 원인이다. 바로 setState가&amp;nbsp;비동기적으로 동작하기 때문이다.&amp;nbsp;&lt;/p&gt;&lt;h4&gt;비동기란?&lt;/h4&gt;&lt;p&gt;비동기에 대해서 모르시는 분들을 위해 간단한 설명이 필요할 듯 싶다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;예를 들어 음식점에서 음식을 시켰을 때 주문을 받는 사람이 음식도 같이 한다면 주문이 된 순서대로 음식을 해야하기 때문에 음식이 만들어지는 순서도 주문된 순서와 동일하다. 왜냐면 주문을 받은 후 음식을 하는 동안은 주문을 받지 못하기 때문이다. 이것이 동기라면&lt;/p&gt;&lt;p&gt;비동기는 주문을 받는 자와 주방장이 따로 있어서 주문을 받는 사람은 주문을 주방에 전달하기만 하면 된다.&amp;nbsp;그러면 음식이 주방에서 만들어지는 동안 주문을 받는 사람은 계산도 할 수 있고 기타 자잘은 서비스들을 할 수 있는 것이다. 여기에서 음식을 만드는 작업이 헤비한 작업에 속한다. 컴퓨터로 치면 I/O작업이 그렇다.&lt;/p&gt;&lt;p&gt;현대의 하드디스크나 네트워크 어댑터에는 작은 CPU가 들어가 있어서 메인 CPU가 해당 장치로 작업을 의뢰하면 의뢰한 작업이 끝나기를 기다리지 않고&amp;nbsp;메인 CPU는 다른 일을 처리할 수 있고 그 동안 의뢰한 장치의 cpu는 자신의 일을 한 후 응답을 다시 메인 CPU에 알려준다. 보통의 하나의 작업 사이클에서 I/O 작업이 대부분을 차지하는 것이 현실이기 때문에 이런 방식은&amp;nbsp; 메인 CPU의 자원을 효율적으로 쓸 수 있게 해준다. 만약 수치연산처럼 메인 CPU의 작업이 대부분을 차지하는 작업이라면 비동기는 그다지 효용성이 없다는 것이 이런 이유다.&lt;/p&gt;&lt;p&gt;비동기를 음식점으로 비유하자면&amp;nbsp;실제 메인 프로세스는&amp;nbsp;위 음식점의 예에서 주문을 받는 사람이 하는 작업이다. 주문을 받는 사람은 주문은 받은 즉시 바로 주문을 주방(하드디스크 등)에 전달한다. 그러면 주방에서는 어떤 식으로든 음식을 만들게 될 것이다. 그 동안 메인 태스크에서 여러 작업들이 이루어진다.&lt;/p&gt;&lt;p&gt;인터넷의 경우 응답시간까지 매우 짧아 보이지만 CPU 입장에서는 한세월이다. 따라서 여러 사이트에 10개의 요청을 해서 응답을 받는 일이 있다면 순서대로 하는 것은 매우 비효율적이다.&lt;/p&gt;&lt;p&gt;비동기로 동시에 여러 사이트에 주문을 넣고 다른 일을 하면서 기다릴 수 있다. 여러 응답이 거의 동시에 와도 실제로 처리되는 것은 아주 짧은 순간이기 때문에 거의 1개의 사이트에 요청하는 것과 거의 대등한 속도로 10개의 요청을 처리할 수 있다.&lt;/p&gt;&lt;p&gt;비동기에 대해서는 할 말이 많지만 다시 주제로 돌아가 보자.&lt;/p&gt;&lt;h4&gt;렌더링의 효율성&lt;/h4&gt;&lt;p&gt;내 생각에 리액트에서 위와 같이 setState를 비동기로 처리하는 이유는 효율성 때문이다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;앞서 리액트는 상태값이 바뀌면 렌더링이 된다고 했었다.&amp;nbsp;그렇다면 위와 같이 setState가 컴포넌트에서 여러개 사용될 때는 setState가 사용된 만큼 화면이 렌더링이 되야 한다. 만약&amp;nbsp;화면에 100개의 객체가 각각 값을 바꾸는 앱이 있다고 하자. 그러면 한순간에 100번의 렌더링이 이루어질 것이다. 이건 뭔가 문제가 있다.&lt;/p&gt;&lt;p&gt;사실 리액트에서는 객체의 값이 변할 때 해당 컴포넌트의 setState를 모두 취합한 후에 한번에 주문을 넣어버리고 주문의 결과(음식)이 모두 나온 후에 한번만 렌더링하도록 한다. 이렇게 하면 100개의 값이 한번의&amp;nbsp;렌더링으로 모두 갱신될 수 있다.&lt;/p&gt;&lt;h4&gt;그러면 이상한 현상이 발생하는 이유는? 뭔가?&lt;/h4&gt;&lt;p&gt;사실 이 현상에 대한 프로그래밍적인 정확한 메커니즘은 잘모른다. 하지만 여러 자료들을 찾아본 결과 대략적인 메카니즘은 다음과 같다.&lt;/p&gt;&lt;p&gt;- setState함수는 비동기로 처리되는데&amp;nbsp;메인 프로세스 중에 호출된 모든 setState가 모두 처리되기 전에 컴포넌트가 렌더링 되지는 않는다.&lt;/p&gt;&lt;p&gt;- 상태에 대한 동일한 key에 접근하여 값을 변경하는 setState에 대해서는 가장 마지막에 있는 setState만 적용이 된다.( 정확한 원리는 조사중)&lt;/p&gt;&lt;h4&gt;그렇다면 해결 방법은?&lt;/h4&gt;&lt;p&gt;아주 간단하다. setState에 전달할 인수를 상태값이 아닌 함수를 전달한다.&lt;/p&gt;&lt;pre style=&quot;color: rgb(0, 0, 0); background: rgb(255, 255, 255); user-select: auto;&quot;&gt;   onClick &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;
       &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;setState&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;prev&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;:&lt;/span&gt; prev&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;count &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color: rgb(0, 140, 0); user-select: auto;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;,&lt;/span&gt; console&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 230); user-select: auto;&quot;&gt;state changed&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(105, 105, 105); user-select: auto;&quot;&gt;// **&lt;/span&gt;
       &lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;setState&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;prev&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;{&lt;/span&gt;count&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;:&lt;/span&gt; prev&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;count &lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color: rgb(0, 140, 0); user-select: auto;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;,&lt;/span&gt; console&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); font-weight: bold; user-select: auto;&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 230); user-select: auto;&quot;&gt;state changed&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 0, 0); user-select: auto;&quot;&gt;'&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: rgb(128, 128, 48); user-select: auto;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: rgb(105, 105, 105); user-select: auto;&quot;&gt;// **&lt;/span&gt;
   &lt;span style=&quot;color: rgb(128, 0, 128); user-select: auto;&quot;&gt;}&lt;/span&gt; 
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이렇게 전달된 함수의 안자로 현재 컴포넌트의 상태값(prev)이 통체로 전달된다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;setState는 모두 비동기로 처리되지만 렌더링 전에 모두 묶음으로 다 처리됨이 보장되며, 또한 처리 순서도 실행 순서대로 처리됨이 보장된다. 디시말해 큐(queue)에 setState가 넣어져서 순서대로 처리된다고 보면 된다.(이 역시 정확히 알아보는 중).&lt;/p&gt;&lt;p&gt;&amp;nbsp;또한, setState에 전달된 함수의 인수는 state의 가장 최신값임을 보장받는다는 것이 그 요지다.&lt;/p&gt;&lt;p&gt;다시 정리하면&amp;nbsp;&lt;/p&gt;&lt;p&gt;1. setState는 모두 비동기로 처리됨.&lt;/p&gt;&lt;p&gt;2. setState는 모두 묶음(batch) 방식으로 렌더링 전에 처리됨.&lt;/p&gt;&lt;p&gt;3. setState에 갱신될 상태값을 넣으면 동일한 key에 대해서는 가장 마지막 setState만 갱신됨. (내생각: 혹시 setState가 비동기로 처리될때 클로저처럼 환경을 캡쳐하는 건 아닌지 생각된다. setState는 비동기 큐로 전달되므로 이렇게 하면 마지막 setState만 의미있게 되는건 아닌지..)&lt;/p&gt;&lt;p&gt;4. setState에 함수를 전달하면 &amp;nbsp;함수의 인수는 상태값(state)의 가장 최신값임을 보장받게 된다.&lt;/p&gt;&lt;p&gt;혹시 이 동작의 메커니즘에 대해서 좀 더 정확히 잘 아시는 분은 답글 부탁드립니다.&lt;/p&gt;</description>
      <category>프로그래밍/React</category>
      <category>React</category>
      <category>리액트</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/670</guid>
      <comments>https://thrillfighter.tistory.com/670#entry670comment</comments>
      <pubDate>Sun, 8 Nov 2020 14:47:15 +0900</pubDate>
    </item>
    <item>
      <title>성공하는 스타트업을 위한 101가지 비지니스 모델 이야기 서평</title>
      <link>https://thrillfighter.tistory.com/666</link>
      <description>&lt;p&gt;최근 스타트업에 대한 공부아닌 공부가 필요해서 산 책이다.&lt;/p&gt;&lt;p&gt;이 책은 101가지 성공한 스타트업의 아이디어와, 수익모델등을 분석해 놓았다.&lt;/p&gt;&lt;p&gt;스타트업을 준비하는 팀들은 인사이트를 얻을 수 있으리라 생각된다.&lt;/p&gt;&lt;p&gt;이 책 외에 스타트업 3개월 뒤 당신이 기필코 묻게될 299가지라는 책도 같이 사서 봤는데 이 책은 비추한다. 그냥 일반적인 자기개발서 정도의 느낌으로 읽고나서 아무런 인사이트를 얻을 수 없었다.&lt;/p&gt;&lt;p&gt;다시 101가지 비지니스 모델 이야기로 넘어가면, 생각보다 수익모델이 단순하지 않다는 것이다. 경영을 1도 모르는 자라면 이 책을 읽고 새로운 눈을 뜨게 될 것이라고 생각된다.&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 458px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99139D355F92DD451C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99139D355F92DD451C&quot; width=&quot;458&quot; height=&quot;638&quot; filename=&quot;x9791160074932.jpg&quot; filemime=&quot;image/jpeg&quot; style=&quot;&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 세상은 수 많은 회사들의 수익구조는 서로 맞물려 있다. 무에서 유를 창조하는 수익모델은 존재하지 않는다. 수익이 있는 곳에 사람이 있고 사람을 끌어야 수익이 발생한다.&lt;/p&gt;&lt;p&gt;서비스를 만드는 개발자로서 이 책은 가뭄에 단비 같았다. 그렇다고 이 책이 모든 사람들의 문제를 해결해주지 않는다. 책으로부터 얻는 인사이트는 각자에게 다르기 때문이다.&lt;/p&gt;&lt;p&gt;최근 인공지능 기술스택 관련 스타트업에 관심이 가고 있다. 그래서 지금은 한권으로 끝내는 AI 비지니스 모델이라는 책을 읽는 중이다.&lt;/p&gt;&lt;p&gt;이 책은 개발자가 타겟은 아니지만 AI 관련 스타트업을 준비한다면 반드시 읽어보길 바란다. 아직 다 읽지는 않았지만 101가지 비지니스 모델과는 또 다른 경영적인 지식을 전달해준다. 환상적인 기술과&amp;nbsp;시장의 가치가 비례하지 않는다.&lt;/p&gt;&lt;p&gt;미래를 예측하지말고, 죽은 과거는 묻어버리고, 살아있는 현재에 행동해라. -롱펠로-&lt;/p&gt;</description>
      <category>관심사/도서</category>
      <category>성공하는 스타트업을 위한 101가지 비지니스 모델 이야기</category>
      <category>스타트업</category>
      <author>콘파냐</author>
      <guid isPermaLink="true">https://thrillfighter.tistory.com/666</guid>
      <comments>https://thrillfighter.tistory.com/666#entry666comment</comments>
      <pubDate>Fri, 23 Oct 2020 22:47:04 +0900</pubDate>
    </item>
  </channel>
</rss>