<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>끄적끄적</title>
    <link>https://monkeydev.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 4 Jul 2026 13:24:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>monkeydev</managingEditor>
    <image>
      <title>끄적끄적</title>
      <url>https://tistory1.daumcdn.net/tistory/3153645/attach/e74b31ca63d349fc852e7b8d4fe9851d</url>
      <link>https://monkeydev.tistory.com</link>
    </image>
    <item>
      <title>책 - Learning Spark 3장</title>
      <link>https://monkeydev.tistory.com/65</link>
      <description></description>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/65</guid>
      <comments>https://monkeydev.tistory.com/65#entry65comment</comments>
      <pubDate>Mon, 13 Nov 2023 14:57:26 +0900</pubDate>
    </item>
    <item>
      <title>Ubuntu OS UI refresh(UI만 재시작)</title>
      <link>https://monkeydev.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본인은 ubuntu 18.04를 사용중인데, UI가 버그가 걸리는 현상이 잦다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들면, ubuntu의 좌측패널로 존재하는 독이 숨기기 되었음에도, 아이콘이 튀어나와있거나 하는 경우가 잦다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련하여 구글링했더니 아래의 글을 찾았다.&lt;br /&gt;&lt;a href=&quot;https://www.fosslinux.com/3495/how-to-refresh-desktop-in-ubuntu-without-rebooting-pc.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.fosslinux.com/3495/how-to-refresh-desktop-in-ubuntu-without-rebooting-pc.htm&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1650281532601&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;How to refresh desktop in Ubuntu without rebooting PC&quot; data-og-description=&quot;You don't need to reboot entire PC everytime if you experience sluggish performance or may be if the desktop got non-responsive. Refreshing desktop environment is sometimes equally as effective as reboot. It saves you bunch of time and you don't need to ki&quot; data-og-host=&quot;www.fosslinux.com&quot; data-og-source-url=&quot;https://www.fosslinux.com/3495/how-to-refresh-desktop-in-ubuntu-without-rebooting-pc.htm&quot; data-og-url=&quot;https://www.fosslinux.com/3495/how-to-refresh-desktop-in-ubuntu-without-rebooting-pc.htm/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbctBg/hyN5DnPOGV/Q6gKKPZCEhvJK46TMPTpyk/img.jpg?width=956&amp;amp;height=556&amp;amp;face=0_0_956_556,https://scrap.kakaocdn.net/dn/csgsWO/hyN5CJf5EU/cyi2PygfgIjmI9S7fqQ0zK/img.jpg?width=956&amp;amp;height=556&amp;amp;face=0_0_956_556&quot;&gt;&lt;a href=&quot;https://www.fosslinux.com/3495/how-to-refresh-desktop-in-ubuntu-without-rebooting-pc.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.fosslinux.com/3495/how-to-refresh-desktop-in-ubuntu-without-rebooting-pc.htm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbctBg/hyN5DnPOGV/Q6gKKPZCEhvJK46TMPTpyk/img.jpg?width=956&amp;amp;height=556&amp;amp;face=0_0_956_556,https://scrap.kakaocdn.net/dn/csgsWO/hyN5CJf5EU/cyi2PygfgIjmI9S7fqQ0zK/img.jpg?width=956&amp;amp;height=556&amp;amp;face=0_0_956_556');&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;How to refresh desktop in Ubuntu without rebooting PC&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;You don't need to reboot entire PC everytime if you experience sluggish performance or may be if the desktop got non-responsive. Refreshing desktop environment is sometimes equally as effective as reboot. It saves you bunch of time and you don't need to ki&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.fosslinux.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ALT + F2누르고, 소문자 r을 입력하면 ui가 refresh된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Ubuntu 관련 편의사항 정리</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/40</guid>
      <comments>https://monkeydev.tistory.com/40#entry40comment</comments>
      <pubDate>Mon, 18 Apr 2022 20:34:30 +0900</pubDate>
    </item>
    <item>
      <title>ssl 인증서 생성 &amp;amp; nginx</title>
      <link>https://monkeydev.tistory.com/29</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx를 통해 모든 웹서비스에 대해 https 접근을 할 수 있도록 ssl 인증서 생성이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx 띄우는법 참고&lt;br /&gt;&lt;a href=&quot;https://hub.docker.com/_/nginx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/_/nginx&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 생성은 아래의 URL을 참고하여 작업하면된다.&lt;br /&gt;&lt;a href=&quot;https://sh-safer.tistory.com/89&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sh-safer.tistory.com/89&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 파일 포맷 종류&lt;br /&gt;&lt;a href=&quot;https://www.sslcert.co.kr/guides/kb/54&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.sslcert.co.kr/guides/kb/54&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx Https 리다이렉트 설정 방법&lt;br /&gt;&lt;a href=&quot;https://www.ucert.co.kr/wiki/w/Nginx_SSL_%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8_%EC%84%A4%EC%A0%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.ucert.co.kr/wiki/w/Nginx_SSL_%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8_%EC%84%A4%EC%A0%95&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 포함시켜서 Nginx 구동하는 커맨드&lt;/p&gt;
&lt;pre id=&quot;code_1626006686246&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Host머신의 /home/sungil/test/nginx/cert에 key, crt파일을 넣는다.
$ docker run --name nginx -v /home/sungil/test/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro -v /home/sungil/test/nginx/cert:/cert:ro -p 8080:80 -d nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 문서에 따라 nginx.conf를 설정하면 된다.&lt;br /&gt;&lt;a href=&quot;https://help.sonatype.com/repomanager3/installation/run-behind-a-reverse-proxy&quot;&gt;https://help.sonatype.com/repomanager3/installation/run-behind-a-reverse-proxy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;937&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y7W4E/btq9gAmwYzv/oVflkkl9ESuxBtCqiQvnAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y7W4E/btq9gAmwYzv/oVflkkl9ESuxBtCqiQvnAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y7W4E/btq9gAmwYzv/oVflkkl9ESuxBtCqiQvnAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy7W4E%2Fbtq9gAmwYzv%2FoVflkkl9ESuxBtCqiQvnAk%2Fimg.png&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;937&quot; data-ke-mobilestyle=&quot;widthOrigin&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 data-ke-size=&quot;size16&quot;&gt;nginx 인증서가 무조건 https기반으로 동작하도록 설정한 nginx의 default.conf이다.&lt;/p&gt;
&lt;pre id=&quot;code_1626015562373&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen       80 ;
    server_name  sungil.ahnlab.com;
    rewrite ^(.*) https://sungil.ahnlab.com$1 permanent;
}

server {
    listen 443 ssl ;
    server_name  sungil.ahnlab.com;

    ssl          on;
    ssl_certificate      /cert/ssl_1.crt;
    ssl_certificate_key  /cert/ssl_1_nopass.key;

    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;
    ssl_ciphers    HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers   on;

    location / {
        keepalive_timeout 3600;
        proxy_read_timeout 2400;
        proxy_connect_timeout 1800;
        proxy_pass_header   Server;
        proxy_cookie_path   ~*^/.* /;
        proxy_buffer_size 128k;
        proxy_buffers 40 128k;
        proxy_busy_buffers_size 128k;
        proxy_pass  http://110.9.97.178:8081/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto &quot;https&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx rewrite 룰 참고&lt;br /&gt;&lt;a href=&quot;https://www.nginx.com/blog/creating-nginx-rewrite-rules/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.nginx.com/blog/creating-nginx-rewrite-rules/&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;nginx로 nexus3에 대한 https 지원 시 443 이외의 다른 포트 이용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx로 nexus3에 대해 https 방식으로 접근하도록 리버스 프록시 기, 위처럼 443포트를 Listen하면 정상적으로 접근되는데. LISTEN 포트를 443이 아닌 다른 포트로 설정 시 UI가 온전히 표시되지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NGINX를 Reverse proxy로 이용하여 https방식의 통신을 지원할 때, 위처럼 443포트를 Listen하면 정상적으로 https 지원이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, Listen중인 포트를 443이 아닌 다른 포트를 이용하려하면, UI에 접근은 되는데 화면이 온전히 나타나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에 대해 아래 stackoverflow에서 방법을 올려줘서 적용했다.&lt;br /&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44695535/nginx-ssl-termination-proxy-to-nexus-not-working-with-ports-different-than-443&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/44695535/nginx-ssl-termination-proxy-to-nexus-not-working-with-ports-different-than-443&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;&lt;span style=&quot;color: #242729;&quot;&gt;위 stackoverflow에서 필자에게 제대로 동작한 답변은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #242729;&quot;&gt;I met the same problem. Change&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;proxy_set_header Host $host;&lt;span style=&quot;color: #242729;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;proxy_set_header Host $host:$server_port;&lt;span style=&quot;color: #242729;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;solve it&lt;/span&gt;&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;즉, proxy_set_header Host $host; 를 &lt;i&gt;proxy_set_header Host $host:$server_port;&lt;span style=&quot;color: #242729;&quot;&gt;&lt;span&gt; 로 바꿔줬더니 동작한다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #242729;&quot;&gt;&lt;i&gt;시간있을 때, 왜 위와같이 수정해줬을 때 제대로 동작하는건지 디버깅해보는 시간을 갖도록한다...&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Synology 관련</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/29</guid>
      <comments>https://monkeydev.tistory.com/29#entry29comment</comments>
      <pubDate>Sun, 11 Jul 2021 21:16:01 +0900</pubDate>
    </item>
    <item>
      <title>Synology 관련 지식들 정리</title>
      <link>https://monkeydev.tistory.com/28</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Synology 메모리 추가 장착&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Synology 정품에서 제공하는 메모리 종류와 클록수를 확인한다.&lt;br /&gt;&lt;br /&gt;현재 본인이 사용중인 DS220+의 경우 DDR4 2666MHz의 클록수를 갖는 노트북용 램을 인식하는 것으로 보인다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 추가장착한 제품은 아래와 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;https://www.11st.co.kr/products/3623415156?&amp;amp;xfrom=&amp;amp;xzone=&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 제품은 8GB인데, 추후 메모리에 압박이 심하다면 16GB 추가 장착을 해보려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Synology 관련</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/28</guid>
      <comments>https://monkeydev.tistory.com/28#entry28comment</comments>
      <pubDate>Sun, 11 Jul 2021 16:43:35 +0900</pubDate>
    </item>
    <item>
      <title>유용한 명령어 정리</title>
      <link>https://monkeydev.tistory.com/24</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 특정 경로의 파일들에서 특정 키워드가 들어가는 파일 찾기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1623254041700&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 현재 디렉토리의 모든 파일들에 대해, &quot;hello world&quot; 라는 키워드를 찾는다.
# -d &quot;recurse&quot;은 grep에서 제공하는 옵션으로 디렉토리에 대해 recursive하게 찾으라는 뜻이다.
# default 옵션은 -d &quot;read&quot;이기 때문에 -d 옵션을 주지 않으면 지저분하게 아래와 같은 출력이 생긴다.
# folder1 Is a directory
# folder2 Is a directory
$ find . -name &quot;*&quot; | xargs grep -d &quot;recurse&quot; &quot;hello world&quot;&lt;/code&gt;&lt;/pre&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;b&gt;2. 복잡한 문자열에서 ip만 추출하여 print 찍어주는 파이썬 스크립트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624869796110&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re
from functools import cmp_to_key
text = '&amp;lt;table class=&quot;fixed-table wrapped confluenceTable&quot;&amp;gt;&amp;lt;colgroup&amp;gt;&amp;lt;col style=&quot;width: 140.0px;&quot; /&amp;gt;&amp;lt;col style=&quot;width: 212.0px;&quot; /&amp;gt;&amp;lt;col style=&quot;width: 91.0px;&quot; /&amp;gt;&amp;lt;col style=&quot;width: 129.0px;&quot; /&amp;gt;&amp;lt;col style=&quot;width: 357.0px;&quot; /&amp;gt;&amp;lt;/colgroup&amp;gt;&amp;lt;tbody&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;p class=&quot;p1&quot;&amp;gt;172.20.15.12&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.26&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span&amp;gt;172.20.15.27&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.21&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span&amp;gt;172.20.15.22&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.25&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.24&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;p class=&quot;p1&quot;&amp;gt;172.20.15.28&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.31&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span&amp;gt;172.20.15.33&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span style=&quot;color: #000000;&quot;&amp;gt;172.20.15.18&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.32&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.39&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span style=&quot;color: #172b4d;&quot;&amp;gt;172.20.15.36&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span style=&quot;color: #172b4d;&quot;&amp;gt;172.20.15.37&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span style=&quot;color: #172b4d;&quot;&amp;gt;172.20.15.29&amp;lt;br /&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span style=&quot;color: #172b4d;&quot;&amp;gt;172.20.15.40&amp;lt;br /&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.51&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.52&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.54&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.55&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;p&amp;gt;&amp;lt;span&amp;gt;172.20.15.48&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;p&amp;gt;&amp;lt;span&amp;gt;172.20.15.49&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.62&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.64&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.44&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.45&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.46&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span style=&quot;color: #172b4d; text-decoration: none;&quot;&amp;gt;172.20.15.61&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;&amp;lt;span style=&quot;color: #393939;&quot;&amp;gt;172.20.15.53&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.60&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.58&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.66&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class=&quot;confluenceTd&quot;&amp;gt;172.20.15.67&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;'
pattern = r&quot;((([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])[ (\[]?(\.|dot)[ )\]]?){3}([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]))&quot;
ips = [match[0] for match in re.findall(pattern, text)]
#ips.sort(key=lambda x: (x[0].split(&quot;.&quot;)[-1], x[1].split(&quot;.&quot;)[-1]))
def compare(item1, item2):
    if item1.split(&quot;.&quot;)[-1] &amp;gt; item2.split(&quot;.&quot;)[-1]:
        return 1
    elif item1.split(&quot;.&quot;)[-1] &amp;lt; item2.split(&quot;.&quot;)[-1]:
        return -1
    else:
        return 0
result = sorted(ips, key=cmp_to_key(compare))
for ip in result:
    print(ip)
&lt;/code&gt;&lt;/pre&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;b&gt;3. ssh를 이용한 포트 터널링 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1624888297765&quot; class=&quot;bash&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# local의 10002 포트를 aiv01.aml.ahnlab.co.kr 서버로의 10001 포트로 tunneling 한다.
$&amp;gt; ssh -v -L 10002:aiv01.aml.ahnlab.co.kr:10001 user@aiv01.aml.ahnlab.co.kr &lt;/code&gt;&lt;/pre&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;b&gt;4. vim으로 문자열 모두 replace&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625213488227&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1 ~ 전체라인($)에 대해 http://172.18.200.70:8999 문자열을 모두 https://livy.blabla.com으로 수정한다.
:1,$s/http:\/\/172.18.200.70:8999/https:\/\/livy.blabla.com/g&lt;/code&gt;&lt;/pre&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;b&gt;5. 정상적으로 요청하고 있는지 tcpdump로 확인&lt;/b&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;분명히 요청을 보냈는데, 그 호스트에 대해 connection timeout등이 발생하는 상황을 캐치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1628820737463&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# tcpdump -i [네트워크 인터페이스명] host [도메인네임]

# 입력 예시
$ tcpdump -i ens33 host abis.ahnlab.com&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 툴 사용관련</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/24</guid>
      <comments>https://monkeydev.tistory.com/24#entry24comment</comments>
      <pubDate>Thu, 10 Jun 2021 00:54:38 +0900</pubDate>
    </item>
    <item>
      <title>React 스터디 1장:  리액트 이해</title>
      <link>https://monkeydev.tistory.com/23</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 리액트 탄생 배경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 객체 값을 반영하는 뷰가 있다고 가정하자.&lt;/p&gt;
&lt;pre id=&quot;code_1622685767396&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;title&quot;: &quot;0603 diary&quot;,
  &quot;contents&quot;: &quot;today will be happy&quot;,
  &quot;author&quot;: &quot;sungil&quot;,
  &quot;likes&quot;: 1
}

&amp;lt;div id=&quot;post-1&quot;&amp;gt;
  &amp;lt;div class=&quot;title&quot;&amp;gt;0603 diary&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;contents&quot;&amp;gt;today will be happy&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;author&quot;&amp;gt;sungil&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;likes&quot;&amp;gt;1&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;likes값이 2로 변한다면 애플리케이션에서 post-1의 likes 요소를 찾아 값을 변경해서 보여주면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 간단한 상황에 대해서는 단순한 규칙으로 likes에 들어가는 값을 바꿔줄 수 있다.&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;위처럼 정말 모든 뷰를 날려버리고 새로 렌더링하면 CPU가 일을 너무 많이해야한다.&lt;br /&gt;Virtual Dom이라는 개념을 이용하여 최소한의 렌더링으로 화면을 다시 보여줄 수 있도록 만들어진것이 React이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Virtual Dom에 대해서는 이어지는 챕터에서 소개한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;2. 리액트 이해&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조가 MVC인 여러 프레임워크와 달리, 리액트는 오직 V(View)만 신경쓰는 라이브러리이다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UI 생김새&lt;/li&gt;
&lt;li&gt;작동 방식&lt;/li&gt;
&lt;/ul&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;b&gt;2.1 초기 렌더링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 UI 관련 프레임워크, 라이브러리를 사용하든지 간에 맨 처음 어떻게 보일지를 정하는 초기렌더링이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서는 이를 다루는 render 함수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;render() { ... }&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 html 형식의 문자열을 반환하지 않고, 뷰가 어떻게 생겼고 어떻게 작동하는지에 대한 정보를 지닌 객체를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트&amp;nbsp; 내부에는 또 다른 컴포넌트들이 들어갈 수 있고, render함수가 실행되면서 내부의 컴포넌트들도 재귀적으로 렌더링된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최상위 컴포넌트의 렌더링 작어이 완료되면, HTML 마크업이 만들어지고 사용자가 지정한 DOM 요소 안에 주입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(렌더링 -&amp;gt; HTML 마크업 -&amp;gt; DOM)&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;b&gt;2.2 리렌더링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 업데이트시마다 전체 UI를 실제로 다시 렌더링하게된다면 부하가 크기 때문에 아래와 이 과정에 Virtual DOM이 이용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Virtual DOM은 실제 DOM을 조작하는 대신, 이를 추상화한 자바스크립트 객체를 구성하여 달라진 지점을 찾아내서 DOM에 적용할 수 있도록 해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;266&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q9cA3/btq6sekobE2/0QKe7AcxrjDklrm4N3AbaK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q9cA3/btq6sekobE2/0QKe7AcxrjDklrm4N3AbaK/img.gif&quot; data-alt=&quot;HTML의 DOM 트리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q9cA3/btq6sekobE2/0QKe7AcxrjDklrm4N3AbaK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/q9cA3/btq6sekobE2/0QKe7AcxrjDklrm4N3AbaK/img.gif&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;266&quot; data-ke-mobilestyle=&quot;widthOrigin&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;HTML의 DOM 트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 실제 HTML의 DOM 트리 구조를 그림으로 설명한것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서는 Virtual Dom의 비교과정을 이용하여, 실제 DOM에서 리렌더링되어야할 부분만 DOM에 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리렌더링되는 절차는 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;데이터를 업데이트하면 전체 UI를 Virtual DOM에 리렌더링한다.&lt;/li&gt;
&lt;li&gt;이전 Virtual DOM에 있던 내용과 현재 내용을 비교한다.&lt;/li&gt;
&lt;li&gt;바뀐 부분만 실제 DOM에 적용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;736&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ev6rNi/btq6q0mGtbN/lzV7jdTA6QUvm54CBfn0Wk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ev6rNi/btq6q0mGtbN/lzV7jdTA6QUvm54CBfn0Wk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ev6rNi/btq6q0mGtbN/lzV7jdTA6QUvm54CBfn0Wk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fev6rNi%2Fbtq6q0mGtbN%2FlzV7jdTA6QUvm54CBfn0Wk%2Fimg.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;736&quot; data-ke-mobilestyle=&quot;widthOrigin&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 data-ke-size=&quot;size16&quot;&gt;또한 리렌더링 과정도 동일한 render 함수내에서 수행된다.&lt;/p&gt;</description>
      <category>React 스터디</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/23</guid>
      <comments>https://monkeydev.tistory.com/23#entry23comment</comments>
      <pubDate>Thu, 3 Jun 2021 10:59:12 +0900</pubDate>
    </item>
    <item>
      <title>React 스터디 2장: JSX</title>
      <link>https://monkeydev.tistory.com/22</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 사용되는 JSX가 무엇인지 공부하고, 기본 문법을 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) JSX란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 확장 문법으로, 브라우저에서 실행되기 전에 코드가 번들링되는 과정에서 바벨을 사용하여 일반 자바스크립트 형태의 코드로 변환됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1622639844466&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* JSX 코드 예시 */
function App() {
  return {
    &amp;lt;div&amp;gt;
      Hello &amp;lt;b&amp;gt;react&amp;lt;/b&amp;gt;
    &amp;lt;/div&amp;gt;
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1622639962679&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*변환된 일반 Javascript 코드*/
function App() {
  return React.createElement(&quot;div&quot;, null, &quot;Hello &quot;, React.createElement(&quot;b&quot;, null, &quot;react&quot;));
}&lt;/code&gt;&lt;/pre&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;&lt;br /&gt;JSX코드를 이용한 쪽이 코드작성이 쉽고, 가독성도 좋아보인다.&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;b&gt;2) JSX 문법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2-1) 감싸인 요소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트에 여러 요소가 있다면 반드시 부모 요소 하나로 감싸야한다.&lt;br /&gt;즉, 아래와 같은 코드는 동작하지 않는다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1622642844421&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';

function App() {
  return (
    &amp;lt;h1&amp;gt;리액트 안녕!&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;리잘 동작하니&amp;lt;/h2&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 에러 내용이 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1622642921897&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./src/App.js
SyntaxError: /home/sungil/WebstormProjects/hello-react/src/App.js: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment &amp;lt;&amp;gt;...&amp;lt;/&amp;gt;? (6:4)

  4 |   return (
  5 |     &amp;lt;h1&amp;gt;리액트 안녕!&amp;lt;/h1&amp;gt;
&amp;gt; 6 |     &amp;lt;h2&amp;gt;리잘 동작하니&amp;lt;/h2&amp;gt;
    |     ^
  7 |   );
  8 | }
  9 |&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드가 정상동작하도록 하기 위해서는 요소 여러개를 아래와 같이 하나로 감싸야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;div&amp;gt; 혹은 &amp;lt;Fragment&amp;gt;등을 이용하여 아래와 같이 감싸주면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fragment는 리액트 v16 이상부터 도입된 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1622643425982&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* &amp;lt;div&amp;gt; 이용 예시*/

import React from 'react';

function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;리액트 안녕!&amp;lt;/h1&amp;gt;
      &amp;lt;h2&amp;gt;리잘 동작하니&amp;lt;/h2&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;



/* &amp;lt;Fragment&amp;gt; 이용 예시*/

import React, { Fragment } from 'react';

function App() {
  return (
    &amp;lt;Fragment&amp;gt;
      &amp;lt;h1&amp;gt;리액트 안녕!&amp;lt;/h1&amp;gt;
      &amp;lt;h2&amp;gt;리잘 동작하니&amp;lt;/h2&amp;gt;
    &amp;lt;/Fragment&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서는 Virtual DOM에서 컴포넌트 변화를 감지해낼 때 효율적으로 비교할 수 있도록하기 위해 아래의 제약사항을 걸고있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트 내부는 하나의 DOM 트리 구조로 이루어져야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 컴포넌트를 &amp;lt;div&amp;gt;같은 태그로 묶어서 return시켜야만 위 제약사항을 지킬 수 있는것으로 보인다.&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;b&gt;2-2) 자바스크립트 표현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSX내 안에서 아래와같이 자바스크립트 표현식을 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1622644421446&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  const name = '리액트'
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{name} 안녕!&amp;lt;/h1&amp;gt;
      &amp;lt;h2&amp;gt;{name} 동작하니&amp;lt;/h2&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위와 관련하여, 자바스크립트의 변수 종류로는 아래 3가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const&lt;/li&gt;
&lt;li&gt;let&lt;/li&gt;
&lt;li&gt;var(ES6이전)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const는 상수이고, let과 var은 값이 변할수 있다.&lt;br /&gt;&lt;br /&gt;단, let은 scope가 블록이고, var은 scope가 함수 단위이다.&lt;/p&gt;
&lt;pre id=&quot;code_1622645040047&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function myFunction() {
  var a = &quot;hello&quot;;
  if(true) {
    var a = &quot;bye&quot;;
    console.log(a); // bye
  }
  console.log(a); // bye
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, var을 사용한 경우, scope가&amp;nbsp; 함수 단위이기 때문에, if문 안에서 새롭게 선언한 var a가 if문 밖에서까지 유효하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6부터는 const, let만 사용하기 때문에, 블록단위로만 생각하면 될 것 같다.&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;b&gt;2-3) 조건부 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSX 내부의 자바스크립트 표현식에서 if문을 사용할 수는 없다. &lt;br /&gt;JSX 밖에서 if문을 사용하여 사전에 값을 설정하거나, { } 안에 조건부 연산자를 이용해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1622645865718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';

function App() {
  const name = '리액트'
  return (
    &amp;lt;div&amp;gt;
        {name === '리액트' ? (&amp;lt;h1&amp;gt;리액트입니다.&amp;lt;/h1&amp;gt;) : (&amp;lt;h2&amp;gt;리액트가 아닙니다.&amp;lt;/h2&amp;gt;)}
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&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;b&gt;2-4) AND 연산자(&amp;amp;&amp;amp;)를 사용한 조건부 렌더링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AND 연산자를 이용하여, 특정 값이 일치하지않는경우, 어떠한것도 렌더링하지 않는 코드 예시이다.&lt;/p&gt;
&lt;pre id=&quot;code_1622646666263&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';

function App() {
  const name = '리액트'
  return (
      &amp;lt;div&amp;gt;{name === '뤼액트' &amp;amp;&amp;amp; &amp;lt;h1&amp;gt;리액트입니다.&amp;lt;/h1&amp;gt;}&amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&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; false를 랜더링할 때는 아무것도 렌더링하지 않는다.&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;b&gt;2-5) undefined 렌더링하지 않기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 컴포넌트에서는 함수에서 undefined만 반환하여 랜더링하는 상황을 만들면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;undefined가 반환되지 않도록 아래처럼 OR(||)영산자를 이용하여 처리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1622648419280&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';

function App() {
  const name = undefined;
  return (
      name || '값이 undefined입니다.'
  );
}

export default App;
&lt;/code&gt;&lt;/pre&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;b&gt;2-6) 인라인 스타일링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 DOM 요소에 스타일을 적용할 때는 문자열 형태가 아닌 객체 형태로 넣어주어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 스타일이름중 background-color처럼 '-'가 포함되는 경우 camelCase로 작성해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1622649840997&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  const name = &quot;리액트&quot;;
  const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: '48px',
    fontWeight: 'bold',
    padding: 16
  };
  return (
      &amp;lt;div style={style}&amp;gt;name&amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&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;b&gt;2-7) CSS 적용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 HTML에서 CSS를 div에 적용할때 아래와 같은 방법으로 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;div class=&quot;myclass&quot;&amp;gt; &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSX에서는 class가 아닌 className으로 설정해주어야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1622652040185&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.css
.style1 {
  background: aqua;
  color: black;
  font-size: 48px;
  font-weight: bold;
  padding: 16px;
}

.style2 {
  background: aqua;
  color: red;
  font-size: 48px;
  font-weight: bold;
  padding: 16px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1622652071209&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.js

import React from 'react';
import './App.css';

function App() {
  return (
      &amp;lt;div&amp;gt;
        &amp;lt;p className=&quot;style1&quot;&amp;gt;This is style1&amp;lt;/p&amp;gt;
        &amp;lt;p className=&quot;style2&quot;&amp;gt;This is style2&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React 스터디</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/22</guid>
      <comments>https://monkeydev.tistory.com/22#entry22comment</comments>
      <pubDate>Tue, 1 Jun 2021 00:06:42 +0900</pubDate>
    </item>
    <item>
      <title>SVN 유저의 github 사용기</title>
      <link>https://monkeydev.tistory.com/12</link>
      <description>&lt;p&gt;회사에서 처음 배운게 SVN인데, github도 사용법을 익혀놓을 필요성이 있어서 공부한 기록을 해당 페이지에 남겨놓으려 한다.&lt;br /&gt;깃허브의 기본 개념은 다른사람이 정리해놓은 아래의 url을 통해서 공부해보려 한다.&lt;/p&gt;
&lt;p&gt;일단 Git은 SVN과 다르게 저장소가 두개이다.(SVN과는 다르게 github의 경우는 분산 저장 시스템이라고 하는데, 해당 글에서는 이런 부분까지 다루지는 않는다.)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;SVN에서의 commit이라는 용어는 (Local PC -&amp;gt; 원격 저장소)로의 저장을 의미했다.&lt;br /&gt;&lt;br /&gt;그러나, Git에서의 commit은 내 로컬 저장소로의 commit을 의미한다.&lt;br /&gt;즉, 다른사람은 볼 수 없고, 내가 가진 PC 고유의 영역에 작업내용을 저장하는 기능이&amp;nbsp; Git의 push 이다.&lt;br /&gt;&lt;br /&gt;내 로컬 영역에 있는 데이터를 master라고 하고, 원격지에 있는 데이터의 기본 이름은 origin 이다.&lt;br /&gt;&lt;br /&gt;더 진행하기전에 아래의 문서 두개를 정독 하고 오자.&lt;br /&gt;&amp;nbsp;- &lt;a href=&quot;https://cupjoo.tistory.com/6&quot; rel=&quot;noopener&quot;&gt;https://cupjoo.tistory.com/6&lt;/a&gt;&amp;nbsp;(Git의 개념과 Git Flow에 대해 다룬다.)&lt;br /&gt;&amp;nbsp;- &lt;a href=&quot;https://trustyoo86.github.io/git/2017/11/28/git-remote-branch-create.html&quot; rel=&quot;noopener&quot;&gt;https://trustyoo86.github.io/git/2017/11/28/git-remote-branch-create.html&lt;/a&gt;&amp;nbsp;(Git의 develop Branch 생성 방법)&lt;br /&gt;&amp;nbsp;-&amp;nbsp;&lt;a href=&quot;https://medium.com/@han7096/git-work-flow%EC%99%80-branch-merge-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-fb05d98428b2&quot; rel=&quot;noopener&quot;&gt;https://medium.com/@han7096/git-work-flow%EC%99%80-branch-merge-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-fb05d98428b2&lt;/a&gt;&amp;nbsp;(Git의 develop Branch 실 사용 예)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;필자는 SVN의 trunk와 branches를&amp;nbsp; 관리하듯, Git Flow의 Master Branch와 Develop Branch만을 사용해서 개발하고자 한다.&lt;br /&gt;(Git Flow전체의 그림은 아래의 그림과 같이 표현되어 있는데, 개인개발을 할 때에 위를 전부 지킬 필요는 없어보여서이다...)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOSif4/btqyeIUSYyM/TtZcA1Pnui7UPdZ3XD3ERK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOSif4/btqyeIUSYyM/TtZcA1Pnui7UPdZ3XD3ERK/img.png&quot; data-alt=&quot;Git Flow - feature - develop - release - hotfix&amp;amp;amp;nbsp;- master &amp;amp;amp;nbsp;5단계로 나누어 코드를 관리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOSif4/btqyeIUSYyM/TtZcA1Pnui7UPdZ3XD3ERK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOSif4%2FbtqyeIUSYyM%2FTtZcA1Pnui7UPdZ3XD3ERK%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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Git Flow - feature - develop - release - hotfix&amp;nbsp;- master &amp;nbsp;5단계로 나누어 코드를 관리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;필자는 위의 3가지 url 중에 마지막 url을 참고하여 origin/master branch(원격에 있는 master branch)로부터 develop branch를 생성하는 이해했다.&lt;br /&gt;&lt;br /&gt;위의 url에서는 다음과 같은 명령어를 이용하여 branch를 생성한다.&lt;br /&gt;&amp;nbsp;- $&amp;gt;git checkout -b &amp;lt;생성하고자 하는 branch 이름&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;어디로부터 branch를 생성하는지 어떻게 시스템이 아는거지? 라는 의문이 들어서 git checkout 창에 대한 terminal help 창을 찾아보니아래와 같은 결과를 찾을 수 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1568272846473&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$&amp;gt; git checkout --help
       git checkout &amp;lt;branch&amp;gt;
           To prepare for working on &amp;lt;branch&amp;gt;, switch to it by updating the
           index and the files in the working tree, and by pointing HEAD at
           the branch. Local modifications to the files in the working tree
           are kept, so that they can be committed to the &amp;lt;branch&amp;gt;.

           If &amp;lt;branch&amp;gt; is not found but there does exist a tracking branch in
           exactly one remote (call it &amp;lt;remote&amp;gt;) with a matching name, treat
           as equivalent to

               $ git checkout -b &amp;lt;branch&amp;gt; --track &amp;lt;remote&amp;gt;/&amp;lt;branch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위는 git checkout에 대한 terminal help 창인데, 확인해 보면 &quot;git checkout &amp;lt;사용자가 생성하고자 하는 branch명&amp;gt;&quot; 명령만으로 branch를 생성할 수 있는 것을 확인했다.&lt;br /&gt;&lt;br /&gt;결론은 시스템의 원격지에 master branch가 하나 뿐이라면, 알아서 다음과 같은 형태로 바꾸는 듯 하다.&lt;br /&gt;&amp;nbsp;- git checkout -b &amp;lt;branch&amp;gt; --track &amp;lt;remote&amp;gt;/&amp;lt;branch&amp;gt;&lt;br /&gt;&lt;br /&gt;즉 사실 다음의 명령어를 입력하는게 훨씬 직관적이고 좋을 것 같다.&lt;br /&gt;$&amp;gt; git checkout -b mydevbranch --track origin/master&lt;/p&gt;
&lt;p&gt;그리고 만약, remote에 이미 있는 branch를 이쪽으로 복사하고자 한다면 다음과 같이 checkout받을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1573551808371&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
$&amp;gt; git checkout -b feature/ai --track remotes/origin/feature/ai
# 참고로 branch 이름의 경우, remote에 있는것과 이름이 다르다면, git push 명령어로 작업이 안된다.
# 예를들어 만약 remote에 있는 ai라는 이름의 feature branch를 다음과 같이 brnach를 생성했다고 하자.

$&amp;gt; git checkout -b ai --track remotes/origin/feature/ai

# 이렇게 돼면 local에서 ai branch에 작업 후 commit한 다음 해당 commit을 push하려고 하려고 
# 다음과 같은 명령어를 입력하면 아래와 같은 오류가 발생한다.

(HEAD가 ai branch에 있다고 가정) $&amp;gt; git push
[대략적인 오류내용: local에 있는 branch와 remote의 branch의 이름이 맞지 않다.
push 하고자 한다면, 다음과 같이 해라.
$&amp;gt; git push origin HEAD:feature/ai]

# 즉 위는 현재의 HEAD를 feature/ai라는 이름으로 강제로 치환해서 push하는 것이다.

# 위와 같은 기교를 부리기 싫으면 애초에 branch type(feature, develop 등등...)이름을 잘 주어서
# branch를 생성하도록 한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;remote에 어떤 branch들이 생성돼있는지 확인하기 위해서는 다음의 명령어를 이용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1573551842791&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$&amp;gt; git branch -a
# 위의 출력결과로 remote와 local에 있는 모든 branch 목록을 보여준다.
# remote에 있는 master는 remotes/origin/master로 표기해주는 등, local과 구분되도록 보여준다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;위의 checkout 명령은 사실 바라보는 branch를 바꾸는 명령이다. 그런데 여기서는 -b 옵션을 이용하여서 없으면 branch를 생성해서 그 branch를 보도록 하고 있는 것이다.&lt;br /&gt;&lt;br /&gt;현재 사용하고 있는 branch가 어떤것인지 알고 싶으면 &quot;$&amp;gt; git branch&quot;를 입력하면 된다.&lt;br /&gt;&lt;br /&gt;어쨌든 위의 입력 &quot;&lt;span style=&quot;color: #333333;&quot;&gt;$&amp;gt; git checkout -b mydevbranch --track origin/master&lt;/span&gt;&quot; 결과로 mydevbranch라는 branch가 생성되면서, 해당 branch에서 작업하는 것이 된다.&lt;br /&gt;&lt;br /&gt;이제 여기서 생성한 mydevbranch에 대해 작업 하고 commit하고, 또 그 내용을 push하면 해당작업은 master branch가 아닌 생성된 mydevbranch에 반영되게 된다.&lt;br /&gt;&lt;br /&gt;참고로, mydevbranch를 push하려면 다음과 같이 하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1568303393608&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$&amp;gt; git push origin mydevbranch&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;그리고 mydevbranch의 작업 내용을 origin/master에 반영하고 싶다면, 다음과 같이 작업하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1568301363121&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# master branch로 제어를 변경한다.
$&amp;gt; git checkout master

# master에서 mydevminio와의 머지명령을 내린다.
$&amp;gt; git merge mydevbranch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;merge가 되고 나면 이제 branch를 제거해주어야 하는데, 로컬에 남아있는 branch와 remote repo의 branch를 모두 지워줘야 한다.&lt;br /&gt;&lt;br /&gt;아래와 같은 명령어로 제거하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1568302572182&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 로컬에 있는 branch가 제거된다.
git branch -d mydevbranch

# 원격지에 있는 branch를 제거한다.
git push origin --delete mydevbranch&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;마지막으로 꽤 많이 쓰이는 프로젝트 클론해오기.&lt;/b&gt;&lt;br /&gt;$&amp;gt; git&amp;nbsp;clone&amp;nbsp;git@github.com:brachio/minio-with-mc.git&lt;br /&gt;&lt;br /&gt;위의&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;git@github.com:brachio/minio-with-mc.git는 github 주소로부터 얻을 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ubuntu에서 git 사용을 위한 UI 툴&lt;br /&gt;&lt;/b&gt;아래의 명령어를 이용하여 RabbitVCS Git을 이용하길 바란다.&lt;br /&gt;여러 툴이 있겠지만, 이 툴이 제법 편하다.&lt;br /&gt;CLI와 ui를 하이브리드로 같이 쓰면 상당히 편하다.&amp;nbsp;&lt;br /&gt;CLI에서 답이 안나올 땐 ui를 이용하고, ui를 쓰면 답이 안나올 땐 CLI를 이용하자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568306420094&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo add-apt-repository ppa:rabbitvcs/ppa
sudo apt-get update
sudo apt-get install rabbitvcs-nautilus&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;git에서 실수를 저질렀을 때 되돌리는 방법&lt;/b&gt;&lt;br /&gt;아래에 만화그림으로 재밌게 설명을 해놓은 것이 있다.&lt;br /&gt;reset과 revert에 대해 재밌게 그려봤으니, git을 이용해서 실수를 복구해야 하는 상황이 있다면 이걸 보고 잘 해결해보자.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;git에서 파일을 실수로 지웠을 때 복구하는 방법&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7HwB9/btqyg8ElEuT/KGOj8COVhBiYijKkQKER0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7HwB9/btqyg8ElEuT/KGOj8COVhBiYijKkQKER0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7HwB9/btqyg8ElEuT/KGOj8COVhBiYijKkQKER0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7HwB9%2Fbtqyg8ElEuT%2FKGOj8COVhBiYijKkQKER0K%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;참고로 &quot;$&amp;gt; git checkout&quot; 명령어만 쳐봐도 사라진 파일이 나온다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;git의 revert과 reset을 기막히게 그림으로 설명해준 만화가 있다.&lt;br /&gt;&lt;a href=&quot;http://www.devpools.kr/2017/01/31/%EA%B0%9C%EB%B0%9C%EB%B0%94%EB%B3%B4%EB%93%A4-1%ED%99%94-git-back-to-the-future/&quot;&gt;http://www.devpools.kr/2017/01/31/%EA%B0%9C%EB%B0%9C%EB%B0%94%EB%B3%B4%EB%93%A4-1%ED%99%94-git-back-to-the-future/&lt;/a&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;추가정보&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SVN을 사용할 때, 다른 사람의 변경사항이 있을 수 있기 때문에, svn update를 받아서, 최신 변경사항을 받은 이후에 커밋하도록 권장한다.&lt;br /&gt;&lt;br /&gt;이처럼 github를 사용할 때도, commit하기 전에 git pull 명령어를 통해서 remote로부터 local의 갱신상태를 최신화하는게 좋다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;그리고 git status를 입력하면 modify된 파일, 추가된 파일, 지워진 파일 등의 정보를 얻을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;git tag 조회 추가 삭제 방법&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;http://minsone.github.io/git/git-addtion-and-modified-delete-tag&quot;&gt;http://minsone.github.io/git/git-addtion-and-modified-delete-tag&lt;/a&gt; &lt;/span&gt;&lt;/p&gt;</description>
      <category>깃허브 사용법 공부</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/12</guid>
      <comments>https://monkeydev.tistory.com/12#entry12comment</comments>
      <pubDate>Thu, 12 Sep 2019 14:52:45 +0900</pubDate>
    </item>
    <item>
      <title>XFS: 5 possible memory allocation deadlock in kmem_zone_alloc (mode: 0x8250) : 이슈</title>
      <link>https://monkeydev.tistory.com/10</link>
      <description>&lt;p&gt;사내에서 관리하는 서버에 ssh 접근과, 관리하는 서비스들로도 접근이 안되서, 서버실에 올라가서 직접 보니 위와 같은 로그를 터미널에 계속해서 찍고 있었다.&lt;br /&gt;&lt;br /&gt;아래와 같은 로그를 계속 발생시키고 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNuKh2/btqyb0ti1dI/SM6n9TyruUaEbUpzKEtyd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNuKh2/btqyb0ti1dI/SM6n9TyruUaEbUpzKEtyd0/img.png&quot; data-alt=&quot;커널 메모리 에러 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNuKh2/btqyb0ti1dI/SM6n9TyruUaEbUpzKEtyd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNuKh2%2Fbtqyb0ti1dI%2FSM6n9TyruUaEbUpzKEtyd0%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;/&gt;&lt;/span&gt;&lt;figcaption&gt;커널 메모리 에러 로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;위와 관련하여 아래의 url을 참고하면 정보를 얻을 수 있다.&amp;nbsp;&lt;br /&gt;&lt;a href=&quot;https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=884938&quot;&gt;https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=884938&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;얻을 수 있는 주요 정보는 아래와 같다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 19px;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 19px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Writing&amp;nbsp;a&amp;nbsp;large&amp;nbsp;number&amp;nbsp;of&amp;nbsp;files&amp;nbsp;to&amp;nbsp;an&amp;nbsp;XFS&amp;nbsp;system&amp;nbsp;with&amp;nbsp;a&amp;nbsp;larger &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;directory&amp;nbsp;block&amp;nbsp;size&amp;nbsp;causes&amp;nbsp;slow&amp;nbsp;performance&amp;nbsp;and&amp;nbsp;kernel&amp;nbsp;log&amp;nbsp;errors&amp;nbsp;re: &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;memory&amp;nbsp;allocation&amp;nbsp;deadlocks.&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;즉 XFS 파일시스템을 갖는 디스크에서, block size가 큰 디렉토리에 대해 많은 양의 파일을 write 할 때, 위의 이미지와 같은 에러로그가 발생한다고 한다. 따라서 smaller directory block size를 이용하도록 해서 해결한다고 한다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;위의 설명처럼 block size를 줄여야 하는 이유를 아래의 url에서 찾을 수 있었다.&lt;br /&gt;&lt;a href=&quot;https://www.suse.com/support/kb/doc/?id=7023344&quot;&gt;https://www.suse.com/support/kb/doc/?id=7023344&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;위 문서에서는 커널 메모리 오류의 원인을 아래와 같이 정의한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csNVBJ/btqx9Ah5yY6/QpDwGISKimjXnwMiEnmvrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csNVBJ/btqx9Ah5yY6/QpDwGISKimjXnwMiEnmvrK/img.png&quot; data-alt=&quot;kmem_zone_alloc 이슈에 대한 원인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csNVBJ/btqx9Ah5yY6/QpDwGISKimjXnwMiEnmvrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsNVBJ%2Fbtqx9Ah5yY6%2FQpDwGISKimjXnwMiEnmvrK%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;/&gt;&lt;/span&gt;&lt;figcaption&gt;kmem_zone_alloc 이슈에 대한 원인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;fragmentation이 점점 커져서 4MB 이상의 contiguous memory를 할당할 수 없을 때 위의 문제가 발생한다고 한다.&lt;br /&gt;즉, fragmentation을 줄이기 위해서 위와 같이 block size를 줄여야 하는 것으로 보인다.&lt;br /&gt;&lt;br /&gt;위에 대해 임시적인 해결방법과 xfs_db 명령어를 활용한 fragmentation 조회 방법을 설명한 url이 있다.&lt;br /&gt;&lt;a href=&quot;https://blog.codecentric.de/en/2017/04/xfs-possible-memory-allocation-deadlock-kmem_alloc/&quot;&gt;https://blog.codecentric.de/en/2017/04/xfs-possible-memory-allocation-deadlock-kmem_alloc/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>서버 관리 이슈 기록</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/10</guid>
      <comments>https://monkeydev.tistory.com/10#entry10comment</comments>
      <pubDate>Tue, 10 Sep 2019 10:23:16 +0900</pubDate>
    </item>
    <item>
      <title>스파크 9장</title>
      <link>https://monkeydev.tistory.com/5</link>
      <description>&lt;h2&gt;9.5 ORC 파일&lt;/h2&gt;
&lt;p&gt;ORC는 하듭 워크로드를 위해 설계된 자기 기술적이며, 데이터 타입을 인식할 수 있는 컬럼 기반의 파일 포맷이다.&lt;/p&gt;
&lt;p&gt;파케이처럼 별도의 옵션 지정 없이 데이터를 읽을 수 있다.&lt;/p&gt;
&lt;p&gt;파케이, ORC 두 포맷은 매우 유사하지만 근본적인 차이점이 있다.&lt;/p&gt;
&lt;p&gt;파케이는 스파크에 최적화된 포맷이고, ORC는 하이브에 최적화되어 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;9.5.1 ORC 파일 읽기&lt;/h2&gt;
&lt;p&gt;ORC 파일 읽기 예시&lt;/p&gt;
&lt;pre id=&quot;code_1560257150124&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spark.read.format(&quot;orc&quot;).load(&quot;/data/flight-data/orc/2010-summary.orc&quot;).show(5)

+-----------------+-------------------+-----+
|DEST_COUNTRY_NAME|ORIGIN_COUNTRY_NAME|count|
+-----------------+-------------------+-----+
|    United States|            Romania|    1|
|    United States|            Ireland|  264|
|    United States|              India|   69|
|            Egypt|      United States|   24|
|Equatorial Guinea|      United States|    1|
+-----------------+-------------------+-----+&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;9.5.2 ORC 파일 쓰기&lt;/h2&gt;
&lt;p&gt;ORC 파일 쓰기 예시&lt;/p&gt;
&lt;pre id=&quot;code_1560257581350&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val csvFile = spark.read.format(&quot;csv&quot;).option(&quot;header&quot;, &quot;true&quot;).load(&quot;/data/flight-data/csv/2010-summary.csv&quot;)
csvFile.write.format(&quot;orc&quot;).mode(&quot;overwrite&quot;).save(&quot;my-orc-file.orc&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;9.6 SQL 데이터베이스&lt;/h2&gt;
&lt;p&gt;SQL 데이터 소스는 매우 강력한 데이터 소스 중 하나이다.&lt;/p&gt;
&lt;p&gt;이 장의 예제는 책의 조언에 따라 MySQL을 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;9.6.1, 9.6.2 SQL 데이터베이스 읽기 및 쿼리 푸시다운&lt;/h2&gt;
&lt;p&gt;Spark에서는 JDBC(&lt;b&gt;JDBC&lt;/b&gt;&lt;span&gt;(Java Database Connectivity)는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;JDBC&lt;/b&gt;&lt;span&gt;는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다.)를 이용한 여러 RDB와의 연결이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;RDB와의 연결을 통해서 RDB의 테이블을 읽을 수 있고, RDB에 filter 과정을 위임할 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;실행 계획의 PushedFilters 부분에서 관련 내용을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;lt;MySQL mysql 데이터베이스의 ailab 테이블&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1560181972770&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mysql&amp;gt; select * from ailab;
+---------+-----+
| name    | age |
+---------+-----+
| jaehyun |  31 |
| kyungah |  27 |
| sungil  |  28 |
| wonhyuk |  29 |
| wooseok |  29 |
+---------+-----+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;lt;explain을 통한 PushedFilters 확인&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1560182043152&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scala&amp;gt; val driver = &quot;com.mysql.jdbc.Driver&quot;
driver: String = com.mysql.jdbc.Driver

# 맨 끝에 mysql은 DataBase 이름입니다. 
scala&amp;gt; val url = &quot;jdbc:mysql://172.17.0.3/mysql&quot;
url: String = jdbc:mysql://172.17.0.3/mysql

# mysql 데이터 베이스의 ailab이라는 테이블의 전체 데이터를 load한다.
scala&amp;gt; val dbDataFrame = spark.read.format(&quot;jdbc&quot;).option(&quot;url&quot;, url).option(&quot;dbtable&quot;, &quot;ailab&quot;).option(&quot;driver&quot;, driver).option(&quot;user&quot;, &quot;root&quot;).option(&quot;password&quot;, &quot;hellworld&quot;).load()

scala&amp;gt; dbDataFrame.filter(col(&quot;age&quot;) &amp;gt; 28).explain
== Physical Plan ==
*(1) Scan JDBCRelation(ailab) [numPartitions=1] [name#0,age#1] PushedFilters: [*IsNotNull(age), *GreaterThan(age,28)], ReadSchema: struct&amp;lt;name:string,age:int&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;전체 쿼리 위임&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;때로는 전체 쿼리를 RDB에 전달해 결과를 DataFrame으로 받아야 하는 경우도 있다.&lt;/p&gt;
&lt;p&gt;테이블명 대신 SQL 쿼리를 명시해 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1560183420622&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scala&amp;gt; val pushdownQuery = &quot;&quot;&quot;(SELECT DISTINCT(age) FROM ailab) AS ailab&quot;&quot;&quot;
pushdownQuery: String = (SELECT DISTINCT(age) FROM ailab) AS ailab

scala&amp;gt; val dbDataFrame = spark.read.format(&quot;jdbc&quot;).option(&quot;url&quot;, url).option(&quot;dbtable&quot;, pushdownQuery).option(&quot;driver&quot;, driver).option(&quot;user&quot;, &quot;root&quot;).option(&quot;password&quot;, &quot;tjddlf6277&quot;).load()
dbDataFrame: org.apache.spark.sql.DataFrame = [age: int]

scala&amp;gt; dbDataFrame.explain()
== Physical Plan ==
*(1) Scan JDBCRelation((SELECT DISTINCT(age) FROM ailab) AS ailab) [numPartitions=1] [age#30] PushedFilters: [], ReadSchema: struct&amp;lt;age:int&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;스파크 자체 파티션에 결과 데이터를 저장함으로서 더 많은 처리를 할 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1560185009869&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scala&amp;gt; val props = new java.util.Properties
props: java.util.Properties = {}

scala&amp;gt; props.setProperty(&quot;driver&quot;, &quot;com.mysql.jdbc.Driver&quot;)
res13: Object = null

scala&amp;gt; props.setProperty(&quot;user&quot;, &quot;root&quot;)
res14: Object = null

scala&amp;gt; props.setProperty(&quot;password&quot;, &quot;tjddlf6277&quot;)
res15: Object = null

scala&amp;gt; val predicates = Array(&quot;age = 28 OR age = 29&quot;)
predicates: Array[String] = Array(age = 28 OR age = 29)

scala&amp;gt; spark.read.jdbc(url, &quot;ailab&quot;, predicates, props).show()
+-------+---+
|   name|age|
+-------+---+
| sungil| 28|
|wonhyuk| 29|
|wooseok| 29|
+-------+---+

# rdd로 만들 수 있음.
scala&amp;gt; spark.read.jdbc(url, &quot;ailab&quot;, predicates, props).rdd.getNumPartitions
res19: Int = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;RDB를 spark에서 parquet로 저장&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1560185327979&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scala&amp;gt; val df = spark.read.jdbc(url, &quot;ailab&quot;, predicates, props)
df: org.apache.spark.sql.DataFrame = [name: string, age: int]

scala&amp;gt; df.printSchema()
root
 |-- name: string (nullable = true)
 |-- age: integer (nullable = true)


scala&amp;gt; df.show()
+-------+---+
|   name|age|
+-------+---+
| sungil| 28|
|wonhyuk| 29|
|wooseok| 29|
+-------+---+

scala&amp;gt; df.write.format(&quot;parquet&quot;).save(&quot;./parquetFile&quot;)

scala&amp;gt; spark.read.load(&quot;./parquetFile&quot;).show()
+-------+---+
|   name|age|
+-------+---+
| sungil| 28|
|wonhyuk| 29|
|wooseok| 29|
+-------+---+


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;RDB의 CSV를 이용하여 RDB에 write&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1560185747413&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# spark 영역
scala&amp;gt; val pf = spark.read.format(&quot;parquet&quot;).load(&quot;./parquetFile&quot;)
pf: org.apache.spark.sql.DataFrame = [name: string, age: int]

scala&amp;gt; pf.show()
+-------+---+
|   name|age|
+-------+---+
| sungil| 28|
|wonhyuk| 29|
|wooseok| 29|
+-------+---+


scala&amp;gt; pf.write.jdbc(url, &quot;newnewailab&quot;, props)


# mysql 영역
mysql&amp;gt; select * from newnewailab;
+---------+------+
| name    | age  |
+---------+------+
| sungil  |   28 |
| wonhyuk |   29 |
| wooseok |   29 |
+---------+------+
3 rows in set (0.00 sec)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;font-size: 1.25em;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;9.8.5 파일 크기 관리&lt;/h2&gt;
&lt;p&gt;파일 크기를 관리하는 것은 저장할때는 중요하지 않지만, 읽을 때는 중요한 요소다.&lt;/p&gt;
&lt;p&gt;작은 파일을 많이 생성하면 메타데이터에 엄청난 관리 부하가 발생한다.&lt;/p&gt;
&lt;p&gt;HDFS, 스파크는 작은 크기의 파일을 잘 다루지 못한다.&lt;/p&gt;
&lt;p&gt;이런 상황을 &lt;span style=&quot;background-color: #ffcd00;&quot;&gt;'작은 크기의 파일 문제'&lt;/span&gt; 라고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 반대로 너무 큰 크기의 파일은 몇 개의 로우가 필요하더라도 전체 데이터 블록을 읽어야 하기 때문에 비효율적이다.&lt;/p&gt;
&lt;p&gt;앞서 9.8.5 이전에, 결과 파일 수는 파일을 쓰는 시점에서의 파티션 수에서 파생된다고 말했다.&lt;/p&gt;
&lt;p&gt;(&lt;b&gt;9.8.3 병렬로 데이터 쓰기 &lt;/b&gt;챕터에서 repartition의 조정으로 파일의 수를 조정할 수 있었다. 그럼 파일 당 크기도 마음대로 지정할 수 있나?)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;책에 의하면 DataFrameWriter에 다음의 옵션을 지정하면 파일당 최대 5,000개의 로우를 포함하도록 보장할 수 있다고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1560258536453&quot; class=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// df는 데이터프레임이라고 가정한다.
df.write.option(&quot;maxRecordsPerFile&quot;, 5000)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>스파크 스터디/스파크 9장</category>
      <author>monkeydev</author>
      <guid isPermaLink="true">https://monkeydev.tistory.com/5</guid>
      <comments>https://monkeydev.tistory.com/5#entry5comment</comments>
      <pubDate>Sat, 8 Jun 2019 14:29:14 +0900</pubDate>
    </item>
  </channel>
</rss>