<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>정윤상이다.</title>
    <link>https://kit2013.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 9 Jun 2026 06:14:50 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>담배맛구마</managingEditor>
    <image>
      <title>정윤상이다.</title>
      <url>https://t1.daumcdn.net/cfile/tistory/2406613C56EEDB7A2E</url>
      <link>https://kit2013.tistory.com</link>
    </image>
    <item>
      <title>TCPDUMP로 웹 서비스 패킷을 이쁘게 찍어보자</title>
      <link>https://kit2013.tistory.com/367</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TCPDUMP를 사용해야되는 시점은 결국 뭔가 안 되니까. 네트워크 이슈 등을 점검하기 위해 쓰는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히나 웹 서비스를 많이 쓰고 있으니, TCPDUMP로 웹 서비스에 대한 패킷을 찍어보자.&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;일단 기본적으로 TCPDUMP로 웹 서비스만 파싱하는 코드이다. 물론 HTTP Body는 생략한다.&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;stdbuf -oL -eL /usr/sbin/tcpdump -A -s 10240 -nn &quot;tcp port 80 and (((ip[2:2] - ((ip[0]&amp;amp;0xf)&amp;lt;&amp;lt;2)) - ((tcp[12]&amp;amp;0xf0)&amp;gt;&amp;gt;2)) != 0)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기다가 이것 저것 붙이고 색깔도 넣어보자&lt;/p&gt;
&lt;pre class=&quot;tex&quot;&gt;&lt;code&gt;stdbuf -oL -eL /usr/sbin/tcpdump -A -s 10240 -nn &quot;(tcp port 80) and (((ip[2:2] - ((ip[0]&amp;amp;0xf)&amp;lt;&amp;lt;2)) - ((tcp[12]&amp;amp;0xf0)&amp;gt;&amp;gt;2)) != 0)&quot; | egrep -a --line-buffered &quot;.+(GET |HTTP\/|POST )|^[A-Za-z0-9-]+: &quot; | perl -nle 'BEGIN{$|=1} { s/^(\d{2}:\d{2}:\d{2}.\d+) IP (\d+.\d+.\d+.\d+).(\d+) &amp;gt; (\d+.\d+.\d+.\d+).(\d+):.+/\n[\e[1;31m$1\e[0m] \e[1;32m$2:$3 -&amp;gt; $4:$5\e[0m/g; s/^.+?(GET |POST |HTTP\/[0-9.]*)/$1/g; print }'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;916&quot; data-filename=&quot;shell.gif&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EgLIN/btrd1yqmcPT/stMP3qCoub2TzrrXDN1zqK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EgLIN/btrd1yqmcPT/stMP3qCoub2TzrrXDN1zqK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EgLIN/btrd1yqmcPT/stMP3qCoub2TzrrXDN1zqK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/EgLIN/btrd1yqmcPT/stMP3qCoub2TzrrXDN1zqK/img.gif&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;916&quot; data-filename=&quot;shell.gif&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;</description>
      <category>Note</category>
      <category>tcpdump</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/367</guid>
      <comments>https://kit2013.tistory.com/367#entry367comment</comments>
      <pubDate>Sun, 5 Sep 2021 15:32:26 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 네이버 웹툰과 별점 그리고 언론보도</title>
      <link>https://kit2013.tistory.com/364</link>
      <description>&lt;p&gt;파이썬 스터디 그룹을 진행하면서 웹 파싱해서 Pandas로 데이터를 확인하는 코드를 짤 일이 있었는데 나중에 쓸일이 있을 것 같아 기록해둔다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Naver webtoon&lt;/h2&gt;
&lt;p&gt;간단한 네이버 웹툰에 대한 파싱 코드를 작성했었다. 특정 웹툰에 대한 정보를 Dictionary List로 출력하는 내용이다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;import bs4
import requests
from urllib.parse import urlparse

BASE_URL_WEBTOON = 'https://comic.naver.com'


def get_webtoon_list(base, page, result=[]):
    resp = requests.get(base + page)
    soup = bs4.BeautifulSoup(resp.text, 'html.parser')
    soup = soup.find('div', {'id': 'content', 'class': 'webtoon'})

    result += [ {
        'no': [q.split('=')[1] for q in urlparse(tr.a['href']).query.split('&amp;amp;') if q.startswith('no=')][0],
        'title': tr.find('td', {'class': 'title'}).text.strip(),
        'rank': tr.div.strong.text.strip(),
        'date': tr.find('td', {'class': 'num'}).text.strip(),
        'link': base + tr.a['href']
    } for tr in soup.table.find_all('tr', {'class': ''})[2:] ]

    # If next tag exists, Go to next page.
    _soup = soup.find('div', {'class': 'paginate'})
    if (page := _soup.find('a', {'class': 'next'})):
        return get_webtoon_list(base, page.get('href'), result)
    else:
        return result
        
    return result

# 복학왕 : '/webtoon/list.nhn?titleId=626907'
# 독립일기 : '/webtoon/list.nhn?titleId=748105'
result = get_webtoon_list(BASE_URL_WEBTOON, '/webtoon/list.nhn?titleId=626907')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;result에 대한 값은 다음과 같이 출력된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot; data-ke-language=&quot;json&quot;&gt;&lt;code&gt;[{'no': '335',
  'title': '333화 청첩장 3화',
  'rank': '9.68',
  'date': '2021.03.02',
  'link': 'https://comic.naver.com/webtoon/detail.nhn?titleId=626907&amp;amp;no=335&amp;amp;weekday=wed'},
 {'no': '334',
  'title': '332화 청첩장 2화',
  'rank': '9.61',
  'date': '2021.02.23',
  'link': 'https://comic.naver.com/webtoon/detail.nhn?titleId=626907&amp;amp;no=334&amp;amp;weekday=wed'},
  ...
 ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Pandas and metaplotlib&lt;/h2&gt;
&lt;p&gt;그냥 별점 기준으로 그래프로 그려봤다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import pandas as pd

import seaborn as sns
import matplotlib.pyplot as plt # must be import to use seaborn
from matplotlib import rc

plt.rcParams['axes.unicode_minus'] = False
rc('font', family='AppleGothic')

pd.set_option('display.max_columns', None)  # or 1000
pd.set_option('display.max_rows', None)  # or 1000
pd.set_option('display.max_colwidth', None)  # or 199

data = pd.DataFrame(result)
data.set_index('no', inplace=True)
data.index = data.index.astype('int64')
data.loc[:, 'date'] = data.loc[:, 'date'].astype('datetime64')
data.loc[:, 'rank'] = data.loc[:, 'rank'].astype('float64')
data = data.sort_index(axis=0, ascending=True)
data.head()

x = data.index
y = data.loc[x, 'rank']
plt.figure(figsize=(10,6))
plt.xlabel('회차')
plt.ylabel('별점')
plt.plot(x, y)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;meta.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;738&quot; width=&quot;584&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/un4kv/btqZZTBWFyb/kFXsEuIOfzergQWrlgDvL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/un4kv/btqZZTBWFyb/kFXsEuIOfzergQWrlgDvL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/un4kv/btqZZTBWFyb/kFXsEuIOfzergQWrlgDvL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fun4kv%2FbtqZZTBWFyb%2FkFXsEuIOfzergQWrlgDvL0%2Fimg.png&quot; data-filename=&quot;meta.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;738&quot; width=&quot;584&quot; height=&quot;NaN&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;별점이 왜 저렇게 튀어버렸을까? 제일 낮은 별점은 2015년에 연재된 &lt;b&gt;46화. 바락 우바마1&lt;/b&gt;로 현재 &lt;b&gt;3.86&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Journal&lt;/h2&gt;
&lt;p&gt;저정도로 별점이 튀었다면, 특히나 기안84 정도쯤 되는 셀럽이라면 언론보도가 있었을 것이다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import json
import requests
import datetime

def get_journal(date, title):
    date_ = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(days=30) 
    payload = {
        &quot;indexName&quot;: &quot;news&quot;,
        &quot;searchKey&quot;: title,
        &quot;searchKeys&quot;: [{}],
        &quot;byLine&quot;: &quot;&quot;,
        &quot;searchFilterType&quot;: &quot;1&quot;,
        &quot;searchScopeType&quot;: &quot;1&quot;,
        &quot;searchSortType&quot;: &quot;date&quot;,
        &quot;sortMethod&quot;: &quot;date&quot;,
        &quot;mainTodayPersonYn&quot;: &quot;&quot;,
        &quot;startDate&quot;: date,
        &quot;endDate&quot;: date_.strftime('%Y-%m-%d'),
        &quot;newsIds&quot;:[], &quot;categoryCodes&quot;:[], &quot;providerCodes&quot;:[], &quot;incidentCodes&quot;:[], &quot;networkNodeType&quot;:&quot;&quot;,&quot;topicOrigin&quot;:&quot;&quot;,&quot;dateCodes&quot;:[],
        &quot;editorialIs&quot;: False,
        &quot;startNo&quot;: 1,
        &quot;resultNumber&quot;: 10,
        &quot;isTmUsable&quot;: False,
        &quot;isNotTmUsable&quot;: False
    }
    headers = {
        'Content-Type': 'application/json; charset=utf-8',
        'X-Requested-With': 'XMLHttpRequest',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15',
        'Aceept': 'application/json, text/javascript, */*; q=0.01',
        'Accept-Encoding': 'gzip, deflate, br',
        'Origin': 'https://www.bigkinds.or.kr',
        'Referer': 'https://www.bigkinds.or.kr/v2/news/search.do'
    }
    resp = requests.post('https://www.bigkinds.or.kr/api/news/search.do', headers=headers, data=json.dumps(payload))

    return json.loads(resp.text)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;웹툰이 게시된 일자와 해당 회차의 제목으로 기사를 검색해보니... 그렇다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;journal = get_journal('2015-04-28', '46화. 바락 우바마 1')
for jn in journal['resultList']:
    print(f'''{jn['DATE']} : {jn['TITLE']}''')
    
# 20150429 : 복학왕 '바락 우바마' 등장에 악플 쏟아져 '또 닭 변신하나?'
# 20150429 : 복학왕 '바락 우바마' 등장에 네티즌들 술렁술렁 &quot;무리수&quot;
# 20150429 : 복학왕, &amp;lsquo;우바바&amp;rsquo; 등장에 평점 3.3&amp;hellip;독자들 뿔났다, 왜?
# 20150429 : 복학왕, &amp;lsquo;우바마&amp;rsquo; 등장에 평점 3.3&amp;hellip;독자들 뿔났다, 왜?
# 20150429 : 복학왕, &amp;lsquo;우바바&amp;rsquo; 등장에 평점 3.3&amp;hellip;독자들 뿔났다, 왜?
# 20150429 : 네이버 수요웹툰 복학왕 46화..오바마 아닌 우바마 떴다?..한국 잠행이유는?
# 20150429 : 네이버 인기 웹툰 '복학왕' 오바마 논란&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기사 본문에서 긍정적인 단어들과 부정적인 단어들을 꺼내다가 봐도 재밋지 않을까 싶다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;unnamed.png&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;370&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT8ZbY/btqZ4BfxEkU/EYYPFav5X2zNBsO0CLhYEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT8ZbY/btqZ4BfxEkU/EYYPFav5X2zNBsO0CLhYEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT8ZbY/btqZ4BfxEkU/EYYPFav5X2zNBsO0CLhYEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT8ZbY%2FbtqZ4BfxEkU%2FEYYPFav5X2zNBsO0CLhYEK%2Fimg.png&quot; data-filename=&quot;unnamed.png&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;370&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;</description>
      <category>Dev-</category>
      <category>PYTHON</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/364</guid>
      <comments>https://kit2013.tistory.com/364#entry364comment</comments>
      <pubDate>Sun, 14 Mar 2021 18:56:05 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] How to get EC2 Windows instance password</title>
      <link>https://kit2013.tistory.com/360</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;AWS에서 윈도우 인스턴스를 만들고 접속 패스워드를 얻기 위해서 웹 콘솔에 들어가서 Ker-pair를 주입해야된다. 근데&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;귀찮다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 어떻게 윈도우 인스턴스 패스워드를 가져올까.&lt;/h2&gt;
&lt;p&gt;EC2에서 인스턴스가 구성될때, Administrator 계정의 패스워드를 Key-pair로 암호화해서 콘솔로 전송한다. 그래서 그걸 Key-pair로 복호화하면된다. 콘솔에서는 &lt;u&gt;Get System Log&lt;/u&gt;를 통해서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1425&quot; data-origin-height=&quot;182&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oikGm/btqH2dXTsPb/MWMpvUZ8NN7y6Gb4Io3ltk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oikGm/btqH2dXTsPb/MWMpvUZ8NN7y6Gb4Io3ltk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oikGm/btqH2dXTsPb/MWMpvUZ8NN7y6Gb4Io3ltk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoikGm%2FbtqH2dXTsPb%2FMWMpvUZ8NN7y6Gb4Io3ltk%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1425&quot; data-origin-height=&quot;182&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;span&gt;정규표현식으로 &lt;u&gt;&amp;lt;Password&amp;gt;\s&lt;/u&gt;&lt;/span&gt;&lt;u&gt;&lt;span&gt;+?&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;+?&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;\s&lt;/span&gt;&lt;span&gt;+?&lt;/span&gt;&lt;/u&gt;&lt;span&gt;&lt;u&gt;&amp;lt;/Password&amp;gt;&lt;/u&gt; 뽑아내면될 것 같다. 값이 ==으로 끝나는걸 보니 Base64 디코드하고 Key-pair로 복호화하면 될 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;2. 뽀또3&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;ec2 클라이언트에서 &lt;u&gt;&lt;span&gt;get_console_output&lt;/span&gt;&lt;/u&gt;&amp;nbsp;메소드로 확인해야하는데 파라미터로 인스턴스 ID를 필요로한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1599311752630&quot; class=&quot;python&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background-color: #f6f7f8; font-size: 14px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; background-position: initial initial; background-repeat: initial initial;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re
import boto3
import base64

def get_instance_information(cli):
    filters = [
        {'Name': 'platform', 'Values': ['windows']}, 
        {'Name': 'instance-state-name', 'Values': ['running']}
    ]
    if resp := cli.describe_instances(Filters=filters):
        for instance in resp['Reservations']:
            ins = instance['Instances'][0]
            info = {
                'instance_id': ins['InstanceId'],
                'private_ip': ins['PrivateIpAddress'],
                'public_ip': ins['PublicIpAddress']
            }
            if 'Tags' in ins:
                for tags in ins['Tags']:
                    if 'Name' in tags['Key']:
                        info.update({'name_tag': tags['Value']})
            yield info

def get_instance_password(cli, instance_id):
    resp = cli.get_console_output(InstanceId=instance_id)
    encryptd_password = re.search(r'&amp;lt;Password&amp;gt;\s+?(.+?)\s+?&amp;lt;/Password&amp;gt;', resp['Output'])
    if encryptd_password:
        return base64.b64decode(encryptd_password.group(1))
    else:
        return False&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;바이너리로 패스워드가 떨어진다. 이제 keypair로 복호화 하면된다. 모듈로는 &lt;span&gt;&lt;u&gt;pycryptodome&lt;/u&gt;를 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1599315626312&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pathlib
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5

def get_decrypt_password(encrypted_password, keypair):
    if pathlib.Path(keypair).exists():
        key = RSA.importKey(open(keypair, 'rb').read())
        decryptor = PKCS1_v1_5.new(key)
        return decryptor.decrypt(encrypted_password, None)
    else:
        return False&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;띠용.&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;이 정보를 토대로 rdp파일을 만들어서, 활용한다면 좋을것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1599316035417&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cli = boto3.client('ec2')
for instance in get_instance_information(cli):
    if encryptd_password := get_instance_password(cli, instance['instance_id']):
        if decrypt_password := get_decrypt_password(encryptd_password, 'key.pem'):
            if 'name_tag' not in instance:
                print('{} / '.format(instance['name_tag']), end='')
            print('{}'.format(instance['instance_id']))
            print(' └ Private IP : {}'.format(instance['private_ip']))
            print(' └ Public IP  : {}'.format(instance['public_ip']))
            print(' └ Password   : {}\n'.format(decrypt_password.decode()))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;RDP 파일의 스펙을 보면 패스워드를 기입할 수는 있으나 Mac에서 Microsoft Remote Desktop 앱에서는 그걸 읽어들이지는 않는 것 같다. 다른 RDP 앱을 찾아서 Config에 기입한다면 가능할 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Cloud</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/360</guid>
      <comments>https://kit2013.tistory.com/360#entry360comment</comments>
      <pubDate>Sat, 5 Sep 2020 22:45:40 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] AWS Code Series</title>
      <link>https://kit2013.tistory.com/359</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-06-22 오후 10.26.24.png&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;738&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfyBa5/btqE2lLYYZ7/QMhSLqXNpVPhrgkW8oEQK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfyBa5/btqE2lLYYZ7/QMhSLqXNpVPhrgkW8oEQK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfyBa5/btqE2lLYYZ7/QMhSLqXNpVPhrgkW8oEQK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfyBa5%2FbtqE2lLYYZ7%2FQMhSLqXNpVPhrgkW8oEQK0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-06-22 오후 10.26.24.png&quot; data-origin-width=&quot;2394&quot; data-origin-height=&quot;738&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;h5&gt;&amp;nbsp;&lt;/h5&gt;
&lt;h5&gt;CodeSeries&lt;/h5&gt;
&lt;p&gt;AWS에서 Developer Tools로 분류가 되어있는 CodeCommit, CodeBuild, CodeDeploy, Codepipeline을 통칭하는 단어이다. CI와 CD를 단계적으로 수행할 수 있도록 적용할 수 있다. 하지만 각 서비스들이 CodeSeries 끼리만 연동되는 것은 아니다. CodeCommit 대신에 S3나 별도 Github을 쓸 수 도있고 CodeBuild 대신에 Jenkins를 쓸 수도 있다.&lt;/p&gt;
&lt;p&gt;이외에도 CodeStar의 경우 프로젝트 단위로 개발언어와 배포대상, IAM, Code Pipeline 등을 종합적으로 쓸 수 있는 서비스이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h5&gt;CodeCommit&lt;/h5&gt;
&lt;p&gt;code series에서 repository를 담당하고 있는 서비스이다. 초기 생성시, 외부의 github, bitbucket 등에서 긁어 오도록 만들 수도 있다. 사용법은 git과 같은 버전관리 툴을 활용하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;git 사용시에는 aws cli에서 제공하는 credential-helper로 접근해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;git config user.name &quot;Yunsang Jeong&quot;
git config user.email &quot;tbvjehr9999@gmail.com&quot;
git config credential.helper &quot;!aws codecommit credential-helper $@&quot;
git config credential.UseHttpPath true&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;HTTP 403 Error 혹은 별도로 ID, PW를 타이핑을 요구할 시에는, git 설치시에 기본적으로 OS에 맞춰 설정되는 &lt;code&gt;credential helper&lt;/code&gt;를 주석처리하고 &lt;code&gt;git config&lt;/code&gt;로 설정하면된다. Windows의 경우 &lt;code&gt;manager&lt;/code&gt;이고 MAC의 경우 &lt;code&gt;osxkeychain&lt;/code&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;git config --list --show-origin # Show all config value with config file location.
...
[credential]
#       helper = manager&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;테스트 해보자.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;git add --all &amp;amp;&amp;amp; git commit -m &quot;First Commit(test)&quot; &amp;amp;&amp;amp; git push&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;EC2 Instance에서 CodeCommit의 소스코드를 가져올때에는 관련된 IAM Role을 만들어서 EC2 Instance에 붙여주면된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h5&gt;CodeBuild&lt;/h5&gt;
&lt;p&gt;code series에서 build를 담당하는 서비스이다. Codecommit, S3, Github 등의 Repository로 부터 resource를 가져와서 buildspec(yml)으로 정의된 절차대로 build를 진행한다. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;build는 별도로 구성된 docker에서 실행되며 docker에 대한 구성은 어느정도 커스텀이 가능하다. 또한, 이 docker가 실행될 VPC와 Subnet, Security group에 대해서도 지정이 가능하다.&amp;nbsp;build의 결과물인 artifacts는 설정에 따라 생성되지 않을 수 있으나, 생성 시에는 지정한 S3로 저장된다. 저장될때 S3에서의 경로, 네임스페이스, 압축여부, 암호화여부 등을 추가적으로 설정할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Code Build 프로젝트에서 artifacts를 A라는 S3에 저장하도록 설정했더라도, Code Pipleline을 생성할 때 지정하게 되는 B라는 S3에만 저장된다.&amp;nbsp;게다가 A와 B를 동일한 S3로 설정하더라도 , pipeline이 생성하는 artifacts는 고정된 디렉토리 구성 및 파일양식(&lt;b&gt;S3 / Pipeline_Name / BuildArtif/ 임의의문자열&lt;/b&gt;)이 존재하므로 동일한 S3에 저장된다는 것 말고는 관리적 이점이 없는 것 같다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;buildspec은 build project 당 1개만 적용 가능하며 경로 및 파일명을 원하는대로 변경이 가능하다. 이를 통해 하나의 Repository에 buildspec_debug.yml과 buildspec_release.yml를 저장해놓고 build project를 debug용과 release용으로 2개 만들어서 운영이 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;build를 진행할 때 설정으로 buildspec를 over-ride가 가능한데, 이를 통해 debug용 build project에서 buildspec_release.yml를 사용하도록 하는 부적절한 관계를 맺을 수도 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;buildspec에는 phases에서 각 단계 별로 실행될 쉘 명령어를 기입하면된다. 명령어는 각 라인별로 순차적으로 한 번씩만 실행되며 명령어들이 실행되는 위치는 resource의 루트 디렉토리이다. 명령어를 실행할 때에는 interactive하게 진행되지 않도록 &lt;code&gt;yes | command&lt;/code&gt;와 같은 조치가 필요하며 환경변수에 대해서도 생각할 필요가 있다.&amp;nbsp;artifacts는 build작업의 산출물을 지정하면된다. 특정 디렉토리의 파일들을 recursive하게 지정하고 싶으면 &lt;code&gt;**/*&lt;/code&gt; 키워드를 이용하면 된다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;# 여기다가 형상관리를 위해 buildspec의 버전넘버를 적으면 안된다... buildspec 포맷에 대한 버전이다.
version: 0.2 

phases:
  install:
    commands:
      - PYTHON=python2 amazon-linux-extras enable nginx1
      - yum clean metadata &amp;amp;&amp;amp; yum install -y nginx
  pre_build:
    commands:
      - yes | cp nginx/imys.conf /etc/nginx/conf.d/imys.conf
      - yes | cp nginx/nginx.conf /etc/nginx/nginx.conf
  build:
    commands:
      - nginx -t
  post_build:
    commands:
      - echo Build completed on `date`

artifacts:
  files:
    - 'codebuild/buildspec_web.yml'
    - 'codedeploy_web/**/*'
    - 'static/**/*'
    - 'nginx/imys.config'
    - 'nginx/nginx.config'
    - 'appspec.yml'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;특정 phases에서 실패를하게 되면 다음 phases는 실행이 되지 않는다. 실패의 기준은 명령어를 실행했을 때 Stderr으로 에러가 출력되면 실패이다. 좋지 않은 방법이지만 &lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt;과 같이 처리해버리면 실패할 일은 없어진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;CodeDeploy&lt;/h3&gt;
&lt;p&gt;CodeDeploy는 Application과 Deployment groups으로 구성된다. Application은 배포하고자 하는 대상을 의미하고 다수의 Deployment group을 가질 수 있다.&amp;nbsp;Deployment group은 Deploy 대상이 되며 EC2/On-premises, AWS Lambda, AWS ECS로 총 3가지 유형이며 유형별로 필요한 IAM policy와 config 파일이 달라질 수 있다.&amp;nbsp;배포를 위해서 EC2의 경우 별도 Agent 설치가 필요하다. Agent는 Ruby로 작성되어 있어서 까봐도 될 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent 설치가 완료된 Instance를 이미지로 만들어서 배포할 경우 제대로 동작하지 않을 수 있다(고 한다). 그러므로 Userdata에 설치를 진행하는 코드를 넣어서 설치해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;구성과정에서 Agent를 이미 설치한 상황에서 IAM을 붙이거나 수정한 경우 Agent를 재시작해줘야 적용된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배포타입(전략; Deployment type)은 In-Place와 Blue/Green 두 가지를 지원한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;In-Place&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;배포 대상인 인스턴스 그룹을 중지시키고 배포를 끝낸 이후에 다시 시작시킨다. 10개의 인스턴스에 배포했을 때, 1개라도 성공하면 성공으로&amp;nbsp;간주된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Blue/Green&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Blue/Green의 경우 배포대상과 동일하게 환경을 구축한 이후, 배포하고 기존의 것을 삭제하는 절차로 진행된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Step 1 : Provisioning replacement instances&lt;br /&gt;Step 2 : Installing application on replacement instances&lt;br /&gt;Step 3 : Rerouting traffic to replacement instances&lt;br /&gt;Step 4 : Terminating original instances&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 과정에서 In-Place보다 더 많은 Policy를 요구한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;&amp;gt; iam:PassRole, ec2:CreateTags, ec2:RunInstances, &lt;/code&gt;&lt;/pre&gt;</description>
      <category>Cloud</category>
      <category>AWS</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/359</guid>
      <comments>https://kit2013.tistory.com/359#entry359comment</comments>
      <pubDate>Mon, 22 Jun 2020 22:20:26 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] AWS Transfer Family</title>
      <link>https://kit2013.tistory.com/358</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;maxresdefault.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ltQYx/btqE2lLYzeZ/8jPKpQj856iEADsSIpywQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ltQYx/btqE2lLYzeZ/8jPKpQj856iEADsSIpywQ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ltQYx/btqE2lLYzeZ/8jPKpQj856iEADsSIpywQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FltQYx%2FbtqE2lLYzeZ%2F8jPKpQj856iEADsSIpywQ1%2Fimg.jpg&quot; data-filename=&quot;maxresdefault.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;뭘 어떻게 기록해둘까 고민하다가, 포이트가 될만한 안건들만 적기로 했다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;General&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;S3에 파일을 안전(보안)하게 전송하기 위한 서비스이다. 이 서비스는 파일 전송을 하는 워크프로우를 AWS로 마이그레이션하는 것을 타겟으로 하고 있다. 현재 지원하는 프로토콜은 FTP, SFTP, FTPS 이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;각 프로토콜이 가지는 몇 가지 특성에 대한 제약이 존재한다. 다음의 문서를 참고한다.&lt;br /&gt;&lt;a href=&quot;https://docs.aws.amazon.com/transfer/latest/userguide/getting-started-use-the-service.html&quot;&gt;https://docs.aws.amazon.com/transfer/latest/userguide/getting-started-use-the-service.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;완전 관리형 서비스로써 서버 인스턴스나 인프라를 구축할 필요가 없고 클라이언트 측에서의 설정값 수정이 필요가 없다. 기존에 클라이언트가 접속하던 hostname을 AWS Transfer Family에서 만든 서버로 연결시키고, 사용자를 추가하고 권한을 설정하면 끝난다. &lt;span style=&quot;color: #333333;&quot;&gt;비용은 서비스를 사용한 시간과 파일의 용량으로 부과된다. 테스트하고 나서는 재빠르게 삭제해주자.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Scope-down Policy&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;chrooted. 서비스 계정별로 서비스되는 루트 디렉토리에 대해서 설정하고 가상의 디렉토리와 파일을 연결지어 버릴 수 있다. 적용하는 방법으론 IAM Policy 양식으로 작성하거나 Custom IdP에서 HomeDirectory- 관련 옵션을 사용하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;bull;&lt;u&gt;IAM Policy&lt;/u&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span&gt;IAM Policy으로 작성하는 경우에는 Transfer Family에서만 전처리되어 동적으로 변환되는 변수들을 쓸 수 있다. 예를 들어 ${Transfer:UserName} 등이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;bull;&lt;u&gt;Custom Idp의 HomeDirectroy-&lt;/u&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span&gt;말 그대로 IdP가 서버로 반환해주는 값에 다가 관련된 내용들을 정의해주면된다. 일단 다음과 같이 HomeDirectoryType에 대해서 LOGICAL값을 설정해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[{ &quot;HomeDirectoryType&quot;: &quot;LOGICAL&quot; }]&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span&gt;그 다음에 HomeDirectoryDetail에 JSON Array로 작성하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[{&quot;Entry&quot;: &quot;/&quot;, &quot;Target&quot;: &quot;/mybucket/jess&quot;}]
OR
[{&quot;Entry&quot;: &quot;/&quot;, &quot;Target&quot;: &quot;/mybucket/${Transfer:UserName}&quot;}]&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span&gt;쫌 더 응용하면 더 괴랄하게 만들 수 도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[
{&quot;Entry&quot;: &quot;/pics&quot;, &quot;Target&quot;: &quot;/bucket1/pics&quot;}, 
{&quot;Entry&quot;: &quot;/doc&quot;, &quot;Target&quot;: &quot;/bucket1/anotherpath/docs&quot;},
{&quot;Entry&quot;: &quot;/reporting&quot;, &quot;Target&quot;: &quot;/reportingbucket/Q1&quot;},
{&quot;Entry&quot;: &quot;/anotherpath/subpath/financials&quot;, &quot;Target&quot;: &quot;/reportingbucket/financials&quot;}
]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;위와 같은 방법으로 HomeDirectoryType가 LOGICAL이고 Entry, Target Pair가 설정된 경우에는 HomeDirectory를 정의할 필요가 없다. 당연하겠지만 해당되는 버킷에 대해 권한은 필수적이다. &lt;span&gt;잘&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;사용한다면&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;복잡한&lt;/span&gt;&lt;span&gt; Scope-down Policy&lt;/span&gt;&lt;span&gt;를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;빌드와&lt;/span&gt;&lt;span&gt; Client &lt;/span&gt;&lt;span&gt;측의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수정사항을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;줄일&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있고&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;보안적인&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;이점을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;얻을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;.&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;End Point Type&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;주의&amp;nbsp;할점은&amp;nbsp;해당&amp;nbsp;서비스에서&amp;nbsp;End&amp;nbsp;Point라고&amp;nbsp;하는&amp;nbsp;것들은&amp;nbsp;AWS&amp;nbsp;Transfer&amp;nbsp;Family로&amp;nbsp;구축한&amp;nbsp;서버를&amp;nbsp;칭한다. 즉, 이 서버를 어디에 어떻게 배치 시킬거냐에 대한 설정으로 생각하면 된다. 유연하게 선택하면 될 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;bull;&lt;u&gt;Public Access&lt;/u&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;그냥&amp;nbsp;닉값한다. 생성하게 되면, Public Dynamic&amp;nbsp;IP를&amp;nbsp;가진&amp;nbsp;Public&amp;nbsp;DNS를&amp;nbsp;제공해준다. 이 DNS에 Route53으로 사용자가 가지고 있는 DNS를 붙일 수 있도록 설정을 제공하고 있긴 하다.&lt;br /&gt;단점으론, 외부에&amp;nbsp;대한&amp;nbsp;접근제어가 매우 제한적이다.&amp;nbsp;최근에 Source IP에 대한 제어 기능이 추가됬다고 하던데, 확인해보니 자격 증명 공급자로 Custom IdP를 사용할때에만 가능하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;bull;&lt;u&gt;VPC_ENDPOINT&lt;/u&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이거는 Console에서는 확인이 불가능하고 CLI, SDK를 통해서만 가능하다. 및의 VPC Hosted 2개로 대체되었다고 보면될 것 같다. 웃긴건 이 타입에 대한 자료가 거의 전무하지만 찾아 보면 한 두개는 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;bull;&lt;u&gt;VPC Hosted with internet-facing&lt;/u&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;VPC Hosted들은 기본적으로 ENI와 Interface 타입의 EndPoints을 생성해서 서비스된다. internet-facing의 경우에는 생성과정에서 ENI에 EIP를 붙이는 방식이다.&amp;nbsp;EIP를 쓰니까 Public Static IP를 얻을 수 있고 이에 따른 Public DNS도 얻을 수 있다. 또한 Route53과 연계도 가능하다. 아울러, Subnet 내부니까 Private Static IP도 할당된다.&amp;nbsp;접근제어로 NACL, SG를 기본적으로 쓸 수 있을 것이고 Custom IdP에서도 가능할 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;bull;&lt;u&gt;VPC Hosted with internal&lt;/u&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이거는 위의 internet-facing의 ENI가 Private Subnet에 위치한다고 생각하면 된다. ENI에 Private Static IP만 붙는다. Private DNS도 제공되긴 한다. 접근제어는 동일하게 NACL, SG 그리고 Custom IdP이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;EndPoint를 생성하니까 On-premise 환경과 연동되는 TGW, DX, VPN에서도 사용이 가능하고 VPC 간의 연동도 가능하다. AWS에서 선택 기준에 대해서 간략하게 테이블로 정리한 자료를 참고하면 좋을 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://aws.amazon.com/premiumsupport/knowledge-center/aws-sftp-endpoint-type/&quot;&gt;https://aws.amazon.com/premiumsupport/knowledge-center/aws-sftp-endpoint-type/&lt;/a&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Identity Provider&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;제일 난해했던 부분이다. 개념이 어렵거나 그런건 아니다. 정확하게 어떤 정보가 어떻게 흐른다는 지 확인하는게 어려웠다. 서버를 구축할 때, 다음의 두 가지 유형의 IdP를 설정할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;bull;&lt;u&gt;Service Managed&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;서비스 자체적으로 사용자부터 인증까지 알아서 해주는 유형이다. 서비스 계정별 SSH Public Key를 유지관리하면서 사용자 인증을 하기 때문에 SFTP 프로토콜인 경우에만 사용할 수 있다. 또한, Password 인증이나 출발지 IP에 대한 접근제어가 지원되지 않는다. 그냥 말그대로 Key를 통한 인증만 해준다는 의미이다.&lt;br /&gt;서비스 계정에 대한 관리도 서비스 내에서 수행하게되는데, 서버를 구축하고나서 콘솔이나 api, sdk를 통해 서비스 계정을 생성하고 설정을 할 수가 있어서 관리적 이점이 어느정돈 있는 것 같다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;bull;&lt;u&gt;Custom IdP&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;REST 타입의 API Gateway를 통해 인증을 수행하는 방식이다. 일반적으로 Lambda를 메소드에 붙여서 인증과정에 수행하는 식으로 구성하는 것 같다. Transfer Family에서 제공하는 모든 기능을 사용할 수 있다. SFTP의 경우 Key인증을 실패하면 Password로 재인증을 한다거나 어떤 프로토콜로 접속을 했는지, 출발지 IP는 어떻게 되는지에 대해 인증과정에서 제어할 수 있게 해준다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다루고 싶은건 Custom IdP이다. 어차피 Service Managed의 사용자관리 부분을 제외한 모든 기능을 가져가기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Custom IdP를 구성할 때, 가장 난해했던 부분은 구축한 서버가 어떻게 IdP(API Gateway)와 통신하고, 또 내부적으로 어떤 결과값을 반환해야되는지에 대해서 명확하게 이렇게 저렇게 된다라고 명시되어 있지 않기 때문이다. 수 많은 Documents와 기술블로그들을 참조하여 비교,실험 결과 다음과 같은 결론을 얻을 수 있었고 순차적으로 정리해본다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;bull;[1]&amp;nbsp;&lt;u&gt;Server(Transfer Family) to IdP(API Gateway)&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /servers/{serverId}/users/{username}/config?sourceIp=1.1.1.1&amp;amp;protocol=SFTP
...
Password: P@ssword
...&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;서버에서는 HTTP Request의 Get method로 위와 같은 URI와 Query string, Header로 인증정보를 IdP에 전송하게 된다. 딱 보면 느낌오듯이 serverId와 username, sourceIp, protocol, Password라는 정보가 전부이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;serverId : resource path에서 정의되는 정보
username : resource path에서 정의되는 정보
sourceIp : HTTP Query 구문에 정의되는 정보 (옵션)
protocol : HTTP Query 구문에 정의되는 정보 (옵션)
Password : HTTP Requset Header 구문에 정의되는 정보(옵션)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;serverId와 useranme은 URI에 반드시 포함되어야 하는 필수적인 정보이다. sourceIp와 protocol은 부가적인 옵션이고 Password의 경우 인증요소로 Password를 사용했을 때에 Header에 추가된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;bull;&lt;u&gt;[2] Insdide IdP&lt;/u&gt;&lt;/span&gt;&lt;u&gt;(API Gateway to Lambda)&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;{
  &quot;username&quot;: &quot;$input.params('username')&quot;,
  &quot;Password&quot;: &quot;$util.escapeJavaScript($input.params('Password')).replaceAll(&quot;\\'&quot;,&quot;'&quot;)&quot;,
  &quot;protocol&quot;: &quot;$input.params('protocol')&quot;,
  &quot;serverId&quot;: &quot;$input.params('serverId')&quot;,
  &quot;sourceIp&quot;: &quot;$input.params('sourceIp')&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;그냥 위 5가지 정보를 JSON으로 묶은다음에 POST method로 Lambda에 전송한다. Lambda에서는 event 변수를 통해 참조한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;bull;&lt;u&gt;[3] Insdide IdP&lt;/u&gt;&lt;/span&gt;&lt;u&gt;(Lambda to API Gateway)&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;[필수항목]
Role : S3에 접근할 수 있는 Policy를 가진 Role의 Arn

[옵션항목]
Policy : Scope-down Policy에 대해서 JSON 포맷 (옵션)
  * 해당 Policy가 적용(?)될 때, 전처리되는 변수가 있으니 활용하면 유연하게 사용할 수 있다.
  * 변수목록 : ${Transfer:UserName}, ${Transfer:~~~}, ${Transfer:~~~}
  
PublicKeys: 사용자가 Password를 입력하지 않았다면, 접속하려는 계정의 Public Key를 입력
  * 물론, SFTP의 경우에만 사용가능하다.
  
HomeDirectory : 사용자 계정의 HomeDirectory를 지정할 수 있다.
  * 이거만 정의한다고 Chroot와 같은 효과를 제공하지는 않는다.

HomeDirectoryType: &quot;PATH&quot; 혹은 &quot;LOGICAL&quot; 값이며 
  * HomeDirectory로 제공한 경로를 사용자에게 절대값으로 보여주려면 &quot;PATH&quot;
  * HomeDirectory로 제공한 경로를 '/'로 보여주려면 &quot;LOGICAL&quot;

HomeDirectoryDetails : JSON 양식으로 Entry와 Target 페어의 리스트
  * 당연히 HomeDirectoryType가 &quot;LOGICAL&quot; 인 경우에 사용가능하다.
  * 이 기능으로 Logical Direoctry처럼 Cross(-Account)-Bucket 등의 구성이 가능하다.
  * 이걸 정의한 경우 HomeDirectory 값은 없어도 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;필수적인 Role과 옵션인 Policy, PublicKeys, HomeDriectory- 들이 있다. 이걸 그냥 &lt;span style=&quot;color: #666666;&quot;&gt;JSON으로 묶은다음에 POST method&lt;/span&gt;&amp;nbsp;보내는 방식이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;bull;&lt;u&gt;[4] IdP&lt;/u&gt;&lt;/span&gt;&lt;u&gt;(API Gateway) to Server(Transfer Family)&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;$schema&quot;:&quot;http://json-schema.org/draft-04/schema#&quot;,
  &quot;title&quot;:&quot;UserUserConfig&quot;,
  &quot;type&quot;:&quot;object&quot;,
  &quot;properties&quot;:{
    &quot;Role&quot;:{
      &quot;type&quot;:&quot;string&quot;
    },
    &quot;Policy&quot;:{
      &quot;type&quot;:&quot;string&quot;
    },
    &quot;HomeDirectory&quot;:{
      &quot;type&quot;:&quot;string&quot;
    },
    &quot;HomeDirectoryType&quot;:{
      &quot;type&quot;:&quot;string&quot;
    },
    &quot;HomeDirectoryDetails&quot;:{
      &quot;type&quot;:&quot;string&quot;
    },
    &quot;PublicKeys&quot;:{
      &quot;type&quot;:&quot;array&quot;,
      &quot;items&quot;:{
        &quot;type&quot;:&quot;string&quot;
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;위아 같이 응답되는 Payload의 스키마를 정의한 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Cloud</category>
      <category>AWS</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/358</guid>
      <comments>https://kit2013.tistory.com/358#entry358comment</comments>
      <pubDate>Mon, 22 Jun 2020 00:04:59 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] Set up chrooted SFTP server</title>
      <link>https://kit2013.tistory.com/357</link>
      <description>&lt;p&gt;테스트 목적으로 구축할일이 있었는데 생각보다 똥글이 많아서 기록한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;구축환경은 Amazon Linux 2 on AWS EC2이다. 이미 SFTP 관련 서비스는 구축이 완료되어 있어 설정만 해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요구되는 사항을 정리해봤다.&lt;/h4&gt;
&lt;p&gt;1. 사용자마다 개별적인 SFTP 서비스 환경 제공을 위해 Chroot라고 대충 얼버무려 표현하는 기능을 활용했다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AWS Transfer Family에서는 Scope-down policy라고도 표현한다. 딱히.. 정해진 표현은 없는 것 같다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. 사용자마다 개별적인 SSH key pair를 이용해서 로그인을 하도록 만들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4&gt;Edit sshd_config&lt;/h4&gt;
&lt;p&gt;기존의 Subsystem 구문을 주석처리하고, 파일 하단에 다음과 같이 작성한다. &lt;u&gt;sftp_users&lt;/u&gt; 그룹은 &lt;u&gt;/data/&lt;/u&gt; 디렉토리를 root로 서비스 하겠다는 의미이다. 수정하고나서 서비스 재시작은 필요하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp

Match Group sftp_users
  ChrootDirectory /data/
  ForceCommand internal-sftp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Create group, users and Set it up&lt;/h4&gt;
&lt;p&gt;sftps_users 그룹을 생성하고 서비스 계정을 생성한다. 그리고 그 계정이 사용할 디렉토리를 생성한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;sudo groupadd sftp_users
sudo useradd -g sftp_users -s /sbin/nologin {USERNAME}

sudo mkdir /data
sudo mkdir /data/{USERNAME}
sudo chmod -R 755 /data
sudo chown -R {USERNAME}:sftp_users /data/{USERNAME}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;개별적인 SSH Key pair를 사용하기 사용자 홈디렉토리에 관련 설정을 추가한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;sudo mkdir -p /home/{USERNAME}/.ssh
sudo touch /home/{USERNAME}/.ssh/authorized_keys
sudo chown -R {USERNAME}:sftp_users /home/{USERNAME}/.ssh

sudo ssh-keygen -P &quot;&quot; -f {KEYNAME} -C {USERNAME}
sudo sh -c &quot;cat {KEYNAME}.pub &amp;gt;&amp;gt; /home/{USERNAME}/.ssh/authorized_keys&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;구축이 끝나면 SFTP 로그인 시에, Root 디렉토리로 /data/가 제공되고, 다른 계정의 디렉토리 정도는 보이나 퍼미션에 의해 계정이름의 디렉토리에서만 작업만 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;원래 목적은 계정의 홈 디렉토리를 &lt;u&gt;/data/{USERNAME}&lt;/u&gt;으로 했었는데, 그러면 &lt;u&gt;~/.ssh/authorized_keys&lt;/u&gt;를 sshd가 리드를 하지 못한다. 해당 디렉토리에 대한 퍼미션에 대한 문제인데, &lt;u&gt;chrootDirectory&lt;/u&gt;에서 요구하는 퍼미션 상태가 있어서 그러하다. 그래서 홈 디렉토리는 건들지는 않았다. sshd_config에서 authroized_keys의 위치를 수정할 수 있던것 같은데 그걸로 해결될 것 같은데 여기서 손절한다.&lt;/p&gt;</description>
      <category>Operating System</category>
      <category>sftp</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/357</guid>
      <comments>https://kit2013.tistory.com/357#entry357comment</comments>
      <pubDate>Sun, 21 Jun 2020 23:19:31 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] SSH connect to bastion host by name tag</title>
      <link>https://kit2013.tistory.com/356</link>
      <description>&lt;p&gt;실습하다가, bastion host IP 찾아보는거 너무 귀찮아서 만듬.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

if [ &quot;$1&quot; == &quot;&quot; ]; then
    echo &quot;[ERROR] input key file name by parameter!&quot;
    exit 0
fi

instacneTag='ec2-an2-imys-test-bastion'

publicip=$(aws ec2 describe-instances \ 
                    --filters &quot;Name=tag:Name,Values=$instacneTag&quot; \
                    --query &quot;Reservations[0].Instances[0].PublicIpAddress&quot;  --output text)
ssh -i $1 ec2-user@$publicip&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;무제.png&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4FJmj/btqD3I7719N/EszNh3jCHhR63lAHcKekZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4FJmj/btqD3I7719N/EszNh3jCHhR63lAHcKekZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4FJmj/btqD3I7719N/EszNh3jCHhR63lAHcKekZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4FJmj%2FbtqD3I7719N%2FEszNh3jCHhR63lAHcKekZ1%2Fimg.png&quot; data-filename=&quot;무제.png&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;395&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;</description>
      <category>Cloud</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/356</guid>
      <comments>https://kit2013.tistory.com/356#entry356comment</comments>
      <pubDate>Sun, 10 May 2020 23:08:55 +0900</pubDate>
    </item>
    <item>
      <title>[React-Native] 끄적이기</title>
      <link>https://kit2013.tistory.com/355</link>
      <description>&lt;p&gt;모듈단위로 정리해보자&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Print Image on network&lt;/h2&gt;
&lt;p&gt;이미지는 출력을 &lt;code&gt;Image&lt;/code&gt; 컴포넌트로 출력하면 되고 네트워크 상에 있는 이미지를 가져올 때에는 width와 height를 명확하게 제시를 해줘야 한다. 지정안하면 에러는 안뜨는데 그냥 안 보인다. 그래서 나는 이미지 크기와 디스플레이 크기의 비율인 ratio를 계산해서 사용했다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let imageUri = 'http://~~/a.jpg'
let imageWidth = 1080;
let imageHeight = 1920;
let ratio = Dimensions.get('window').height/imageHeight;
return(
  &amp;lt;Image
    source={{uri: imageUri}} 
    style={{width: imageWidth * ratio, height: imageHeight * ratio}}
    /&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;동일한 이미지를 여러번 호출 하는 경우 캐시에 담아두는 기능이 있다. 뭐.. 그렇다는데 실험은 안해봤다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;let imageUri = 'http://~~/a.jpg'
Image.queryCache([imageUri]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Device Rotate&lt;/h2&gt;
&lt;p&gt;내 폰에 테스트하다가 어쩌다가 가로모드로 바꼇는데, 렌더링이 되지는 않았다. 화면에 전환이 있을 때, 계산해서 강제로 렌더링 시켜버렸다. 강제로 렌더링하는 메소드는 &lt;code&gt;this.forceUpdate();&lt;/code&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;componentDidMount(){
	Dimensions.addEventListener('change', this.onChangeDimensions);
}
componentWillUnmount(){
	Dimensions.removeEventListener('change', this.onChangeDimensions);
}
onChangeDimensions = () =&amp;gt; {
	this.forceUpdate()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;componentDidMount()&lt;/code&gt;는 초기에 &lt;code&gt;render()&lt;/code&gt;가 실행된 이후에 실행되는 걸로 알고있는데, 이거만 하니까 &lt;code&gt;componentWillUnmount&lt;/code&gt; 정의가 안됬다고 워닝떠서 추가했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Touch event on Image&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Image&lt;/code&gt;는 기본적으로 &lt;code&gt;onPress&lt;/code&gt;가 없다. 터치 이벤트를 주고 싶으면 상위에 &lt;code&gt;TouchableHighlight&lt;/code&gt; 와 같은 컴포넌트를 두고 거기에 &lt;code&gt;onPress&lt;/code&gt;를 줘야한다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;TouchableHighlight onPress={this.pressImage} activeOpacity={1}&amp;gt;
  &amp;lt;Image
    source={{uri: imageUri}} 
    style={{width: imageWidth * ratio, height: imageHeight * ratio}}
    /&amp;gt;
&amp;lt;/TouchableHighlight&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Animation on Image&lt;/h2&gt;
&lt;p&gt;클릭했을 때 서서히 어두워지는 효과를 주고 싶었다. 처음에 타이머 만들어서 직접 구현하려고 했는데 이미 &lt;code&gt;Animated&lt;/code&gt;라는 컴포넌트가 정의가 되어 있었다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;state = {
	isShowMenu : false,
	animateValue : new Animated.Value(1)
};

pressImage = () =&amp;gt; {
	this.setState({
    	isShowMenu: !this.state.isShowMenu 
	}, () =&amp;gt; {
		Animated.timing(
			this.state.animateValue, {
				toValue: this.state.isShowMenu? 0.5 : 1,
				duration: 500,
                  useNativeDriver: true
			}).start(()=&amp;gt;{
				// Console.log('Wow~')
			});
	});
  }

&amp;lt;TouchableHighlight onPress={this.pressImage} activeOpacity={1}&amp;gt;
	&amp;lt;Animated.View style={{opacity: this.state.animateValue}}&amp;gt;
		&amp;lt;Image
			source={{uri: imageUri}} 
			style={{width: imageWidth * ratio, height: imageHeight * ratio}}
		/&amp;gt;
	&amp;lt;/Animated.View&amp;gt;
&amp;lt;/TouchableHighlight&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Apr-01-2020 22-15-05.gif&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;500&quot; width=&quot;179&quot; height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddxoNk/btqC6lHrtw7/9xKlQiKknrRHKsva2wS510/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddxoNk/btqC6lHrtw7/9xKlQiKknrRHKsva2wS510/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddxoNk/btqC6lHrtw7/9xKlQiKknrRHKsva2wS510/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ddxoNk/btqC6lHrtw7/9xKlQiKknrRHKsva2wS510/img.gif&quot; data-filename=&quot;Apr-01-2020 22-15-05.gif&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;500&quot; width=&quot;179&quot; height=&quot;280&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;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Filesystem&lt;/h2&gt;
&lt;p&gt;기본적으로 Filesystem에 대해서 제어하는 기능은 없고 &lt;code&gt;react-antive-fs&lt;/code&gt; 모듈을 받아서 진행해야 한다. 이미지 데이터를 받아서 byte로 찍어되야 되나 한숨쉬다가 &lt;code&gt;downloadFile&lt;/code&gt; 메소드 있길래 바로 써봤더니 잘 되지는 않았다. 문제는 퍼미션 문제인 것 같아서 디바이스 설정에서 수동으로 파일시스템 권한 줬더니 잘 된다. 이제 파일시스템 퍼미션 주는 방식에 대해 알아봐야겠다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import * as RNFS from 'react-native-fs';

let imageUri = 'http://~~/a.jpg'

RNFS.downloadFile({
	fromUrl: imageUri,
	toFile: '/storage/emulated/0/Download/test.jpg'
}).promise.then((r)=&amp;gt;{
	console.log(r);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Show menu from bottom&lt;/h2&gt;
&lt;p&gt;이걸.. &lt;code&gt;Animated&lt;/code&gt;로 구현해야 되나 현타왔다가 &lt;code&gt;react-native-bottom-action-sheet&lt;/code&gt; 모듈을 쓰기로 했다. &lt;code&gt;GridView&lt;/code&gt;에서 visible에 state를 주면 깔끔할 것 같다. 그래서 이때까지 했던거 총합해서 메뉴가 보일 때 슈르륵 올라오게 만들었다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;import RNBottomActionSheet from 'react-native-bottom-action-sheet';
import Icon from 'react-native-vector-icons/FontAwesome5';

let saveIcon = &amp;lt;Icon family={'FontAwesome'} name={'save'} color={'#000000'} size={30}/&amp;gt;
let shareIcon = &amp;lt;Icon family={'FontAwesome'} name={'share-alt'} color={'#000000'} size={30} /&amp;gt;
let settingIcon = &amp;lt;Icon family={'FontAwesome'} name={'cog'} color={'#000000'} size={30} /&amp;gt;

&amp;lt;RNBottomActionSheet.GridView 
	visible={this.state.isShowMenu}
	onSelection={(index, value) =&amp;gt; {
		this.pressImage();
		RNFS.downloadFile({
			fromUrl: imageUri,
			toFile: '/storage/emulated/0/Download/test.jpg'
		}).promise.then((r)=&amp;gt;{
			console.log(r);
		});
	}} 
	onCancel={()=&amp;gt;{this.pressImage();}}
&amp;gt;
	&amp;lt;RNBottomActionSheet.GridView.Item title={'저장하기'} icon={saveIcon} /&amp;gt;
	&amp;lt;RNBottomActionSheet.GridView.Item title={'공유하기'} icon={shareIcon} /&amp;gt;
	&amp;lt;RNBottomActionSheet.GridView.Item title={'설정화면'} icon={settingIcon} /&amp;gt;
&amp;lt;/RNBottomActionSheet.GridView&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;잡다한 것들&lt;/h2&gt;
&lt;p&gt;[1] React-Native에서의 이름은 낙타방식이다.&lt;/p&gt;</description>
      <category>Dev-</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/355</guid>
      <comments>https://kit2013.tistory.com/355#entry355comment</comments>
      <pubDate>Wed, 1 Apr 2020 22:16:16 +0900</pubDate>
    </item>
    <item>
      <title>공군 정보보호병, 정보보호장교에 대한 간략한 후기</title>
      <link>https://kit2013.tistory.com/354</link>
      <description>&lt;p&gt;이제는 관련된 정보가 많이 오픈된것 같은데, 저도 지원할 때 많은 도움 받았었기 때문에 간랸하게 후기 남겨볼께요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;[원서접수]&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;병사는 어려울께 없는데 장교의 경우 챙겨야 할 서류들이 상당히 많아요. 설명잘 보시고 접수하시면 되고 설령 의문사항 생겨도 담당부서에 연락하면 정말 친절하게 답변받을 수 있으니까 꼭 확인하고 넘어가세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;[면접]&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;기술면접이 포함된 인성면접으로 생각하시면 돼요. 정확하게 어떤 항목으로 어떻게 배점을 매기는지는 밝힐 수는 없지만 어떻게 준비하면 좋을지에 대해서만 간략하게 말씀드릴께요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;병사의 경우 (제 개인적인 생각이긴 하지만) 그렇게 엄청나게 좋은 스펙과 스킬을 기대하지는 않아요. 정보보호와 관련되어 어떤 분야를 어떻게 공부해왔고 노력해온 사실을 어필만 해주셔도 될 것 같아요. 예를 들어, CTF 대회에 참가한 경험이라든지 프로젝트 진행경험, 개인적으로 공부하고 있는 것들이 있을 것 같아요.&amp;nbsp;그리고 정보보호개론에서 나오는 개념 정도는 모두 알고 계시면 좋을 것 같아요. 예를 들어,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Q&amp;gt; 혹시 ARP 프로토콜에 대해서 설명해주시겠습니까?&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;A&amp;gt; [ARP프로톨에 대한 설명]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Q&amp;gt; 그럼 이 ARP 프로토콜이 가지는 구조적인 취약점에 대해서 설명해주시겠습니까?&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;A&amp;gt; [ARP Spoofing 등에 대한 설명]&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런 느낌이죠. 혹시나 ARP 프로토콜에 대해서 모른다면 모르겠다고 답변하고 잘 알고 있는 프로토콜을 언급해주면 (면접관들이) 고마워 하지 않을까 싶네요.&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&gt;말할 때 다나까를 해야되냐에 대해서는 지키는게 좋지만 병사의 경우 꼭 그렇게 까진 안해도 될것 같아요.&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&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;[훈련소]&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;제가 두 번 가봤는데 시키는 대로 하면 됩니다. 두 번째 할 때는 추억돋으면서 재미도 느끼긴 했는데, 세 번은 힘들 것 같아요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;[실제업무]&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;군에서의 정보보호 업무를 어디 부대를 가서도 수행(은)할 수 있지만 핵심이되는 것들은&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;특정 부대들에 집중되어 있긴해요. 그 부대로 가셔야 정보보호 업무를 할 수 있는 갖춰진 환경에서, 그 업무만하며 부가적으로 인적 네트워크를 구축할 수 있지 않을까 조심스럽게 말하고 싶네요. 물론, 그 부대가 아닌 곳에서도 스스로 노력한다면 환경과 기회가 분명히 생길 수 있다고 봐요... 물론, 힘들지만요;;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;&lt;span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;기술적인 깊이가 없어도 그냥 정해진 절차대로만 수행해도 되니까 발전할 기회가 많지 않다.&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;라는 평이 존재하는 것 같아요. 제 생각엔&amp;nbsp;이러한 평가가 왜 생겼는지를 생각할 필요가 있을것 같아요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;1. 이미 특정상황이 발생 했을 때에 대한 레퍼런스(수행경험)이 많이 있고 기록되어 있다.&lt;br /&gt;2. 발생하는 상황들이 크게 테두리를 벗어나지 않기에 레퍼런스를 따라 대응을 하게되어 지속, 반복적인 업무가 되어 버린다.&lt;br /&gt;3. 새로운 상황이 발생 하더라도 누군가 또다른 레퍼런스를 만들어 낸다.&lt;br /&gt;4. 군 특성상 콜센터...와 같이 정보보호가 아니더라도 전산과 관련된 별의 별 것들을 다물어본다. 한글설치 어디서 하냐 등..;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 내용들이 계속 반복된다고 생각하면 돼요. 3번의 경우가 생산적이긴 한데, 빈도수도 많지 않은데다가 보안관제사 입장에서 이를 주도적으로 하기가 힘든 상황이 많아요. 즉, 실질적으로 1번, 2번, 4번을 반복하다가 가끔씩 3번이 발생하는 것의 루틴이에요. 그래서 발전할 기회가 많지 않다고 평을 한다고 생각해요.&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&gt;&lt;span style=&quot;color: #333333;&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정보보호솔루션 운영에 대한 부분을 제가 빠뜨렸는데 운영은 관련 업무를 하고 있으면 병, 장교없이 다 가능하지만 실제 정책을 수립하고 배포하는 것에 있어서는 병사에게 권한을 주지 않아요.(물론, 이렇게 해보는게 어떻겠습니까? 라고 건의해주면 간부입장에선 고마워요)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;* 여담인데 업무자동화라는 명목으로 Python 스크립팅을 많이 접할텐데, 아무 생각없이 선배(선임)이 만들어 논거 가져다가 쓰지말고 코드리뷰하고 이해하고 쓰시길 바래요.&lt;/p&gt;
&lt;p&gt;* 위에 언급된 업무들 중, 보안관제를 제외한 업무들은 &quot;정보보호의 핵심적인 업무를 하는 부대들&quot;에서 할 수 있어요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;[결론]&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;3줄 요약해보자면&lt;/p&gt;
&lt;blockquote&gt;&lt;span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;1. 면접준비에 자신의 열정(노력)을 어필 할 수 있는 경험과 전공지식(정보보호개론, TCP/IP 등)을 정리해가자.&lt;br /&gt;2. &quot;그 부대&quot;를 못 가더라도 열심히 하면 기회는 온(다고 믿으며 잘하면 된)다.&lt;br /&gt;3. 선배(선임)가 &quot;아 그거 이렇게 하면 되&quot;라고 할때 왜? 라는 의문을 가져보자. (개기진 말자)&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;342&quot; width=&quot;201&quot; height=&quot;215&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q2DoB/btqC9yyjGod/I99jACqaZzkXL8flilOk1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q2DoB/btqC9yyjGod/I99jACqaZzkXL8flilOk1K/img.png&quot; data-alt=&quot;나는 예비군&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q2DoB/btqC9yyjGod/I99jACqaZzkXL8flilOk1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq2DoB%2FbtqC9yyjGod%2FI99jACqaZzkXL8flilOk1K%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;342&quot; width=&quot;201&quot; height=&quot;215&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;</description>
      <category>Air-man</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/354</guid>
      <comments>https://kit2013.tistory.com/354#entry354comment</comments>
      <pubDate>Wed, 1 Apr 2020 17:18:33 +0900</pubDate>
    </item>
    <item>
      <title>[React-Native] 시작하기</title>
      <link>https://kit2013.tistory.com/353</link>
      <description>&lt;p&gt;간단한 어플리케이션 개발을 의뢰받았다. Android와 iOS 둘다 가능한걸 요구하길래 크로스플랫폼을 쓰기로했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;React Native&lt;/h2&gt;
&lt;p&gt;깊게 공부할 시간이 없다. 개념이해 없다. 그냥 일단 Hello world 찍는다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3&gt;Installing dependencies&lt;/h3&gt;
&lt;p&gt;그래도 Expo CLI보다는 React Native CLI로 가보자&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[공통사항]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;맥북샀으니까 Homebrew 써보자. 참고로 16인치다 &lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)&quot;
brew install node
brew install watchman&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[Android]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;JDK 8 이상버전을 설치해아한다. OpenJDK를 많이 쓰는 것 같다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# brew tap AdoptOpenJDK/openjdk
# brew cask install adoptopenjdk8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;안드로이드 스튜디오 설치받고 커스텀 메뉴에서 다음의 항목을 설치한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;* Android SDK
* Android SDK Platform
* Performance(Intel HAXM)
* AVD(Android Virtual Device)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치가 끝나면 SDK Manager에서 다음의 항목을 '잘' 설치한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;* Android SDK Platform 28
* Intel x86 Atom_64 System Image &quot;or&quot; Google APIs Intel x86 Atom System Image
* Android SDK Build-Tools 28.0.3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;환경변수 셋팅을 해주자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[iOS]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;드디어 xCode를 써볼일이 생겼다. 앱스토에서 설치해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;image-20200328185532768.png&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baXWOC/btqC48ujcBo/QmIH8yBrFdfpcnyuPKib9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baXWOC/btqC48ujcBo/QmIH8yBrFdfpcnyuPKib9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baXWOC/btqC48ujcBo/QmIH8yBrFdfpcnyuPKib9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaXWOC%2FbtqC48ujcBo%2FQmIH8yBrFdfpcnyuPKib9k%2Fimg.png&quot; data-filename=&quot;image-20200328185532768.png&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1672&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;cocoapos를 설치해준다. 군 생활할때 신병보고 python 할줄 아냐고 물으니까 ruby는 할 줄 안다고 답했던게 기억난다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;sudo gem install cocoapods
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3&gt;Create a new app&lt;/h3&gt;
&lt;p&gt;프로젝트를 만들면 기본적으로 셋팅이 된다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;npx react-native init AwesomeProject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHeZ70/btqC5pil6Lu/JTpB25Cx7OIYoMMZjsyPe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHeZ70/btqC5pil6Lu/JTpB25Cx7OIYoMMZjsyPe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHeZ70/btqC5pil6Lu/JTpB25Cx7OIYoMMZjsyPe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHeZ70%2FbtqC5pil6Lu%2FJTpB25Cx7OIYoMMZjsyPe1%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;702&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;code&gt;run-ios&lt;/code&gt; 또는 &lt;code&gt;run-android&lt;/code&gt;하면된다. 명령어를 실행하면 에뮬레이터를 환경변수대로 찾아서 실행하고 앱 설치하고 실행시켜준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;초기에 App.js에 따른 앱 구동화면을 보면, &lt;code&gt;r&lt;/code&gt;을 누르면 소스코드 변경된게 적용된다고 되어 있다. 하지만, 시도를 해보면 앱에서 서버를 찾을 수없다는 에러가 뜬다. 확인해보니 Metro bundler라는걸 따로 실행해줘야 이걸 통해서 적용하는 것같다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;npx react-native start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;image-20200328190913594.png&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bASWxB/btqC5n5WrDT/Pwvzz3YZXAAOwGKJlux2U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bASWxB/btqC5n5WrDT/Pwvzz3YZXAAOwGKJlux2U1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bASWxB/btqC5n5WrDT/Pwvzz3YZXAAOwGKJlux2U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbASWxB%2FbtqC5n5WrDT%2FPwvzz3YZXAAOwGKJlux2U1%2Fimg.png&quot; data-filename=&quot;image-20200328190913594.png&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;750&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3&gt;Hello, world!&lt;/h3&gt;
&lt;p&gt;그냥 App.js에 코딩하면 플랫폼에 맞춰 결과물이 나오는 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Hello, world!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;코드 자체는 Javascript인데 ES2015(ES6)을 따르므로 생소하게 느껴진다. import, from, class, extents가 그러한 예이다. 또한 retrun (...)에 있는 구문은 JSX이며 JavaScript XML의 약자이다. 이런 식으로 결국 JSX를 render해주는 component들을 정의해서 조합하면 UI가 완성될 것 같다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import React, { Component } from 'react';
import { Text, View } from 'react-native';

export default class App extends Component {
  render() {
    return (
      &amp;lt;View&amp;gt;&amp;lt;Text&amp;gt;Hello, world!&amp;lt;/Text&amp;gt;&amp;lt;/View&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Component의 Instance가 생성되었을 때, 다음의 순서로 메소드가 실행된다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Constructor()&lt;/li&gt;
&lt;li&gt;static getDerivedStateFromProps()&lt;/li&gt;
&lt;li&gt;render()&lt;/li&gt;
&lt;li&gt;componentDidMount()&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Props(Properties)와 State&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;component에서 데이터를 제어하기 위해 Props와 State가 있다. 다음의 특성이 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Props와 State에 변경이 생겼을 때, render()가 수행된다. 이는 UI에 바로 그려진다고 생각하면 될 것 같다.&lt;/li&gt;
&lt;li&gt;Props는 고정된 값이고 Instance가 생성될 때 Parameter로 넘겨받거나 component에서 정의가 가능하다.&lt;/li&gt;
&lt;li&gt;State는 변경가능한 값으로 상황에 맞춰 설정하면된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Props(Properties)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;source라는 Props를 이용해서 이미지를 출력하고 있다. {pic}은 'let pic' 구문에서 선언한 변수를 참조하는 JSX 문법이다. 이런식으로 Javascript를 JSX에서 중괄호로 참조할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import React, { Component } from 'react';
import { Image } from 'react-native';

export default class Bananas extends Component {
  render() {
    let pic = {
      uri: 'https://a.com/a.jpg'
    };
    return (
      &amp;lt;Image source={pic} style={{width: 193, height: 110}}/&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Greeting 객체 생성 시, name이라는 Prop을 넘겨주어 this.props을 통해 참조할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import React, { Component } from 'react';
import { Text, View } from 'react-native';

class Greeting extends Component {
  render() {
    return (
      &amp;lt;View style={{alignItems: 'center'}}&amp;gt;
        &amp;lt;Text&amp;gt;Hello {this.props.name}!&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
    );
  }
}

export default class LotsOfGreetings extends Component {
  render() {
    return (
      &amp;lt;View style={{alignItems: 'center', top: 50}}&amp;gt;
        &amp;lt;Greeting name='Rexxar' /&amp;gt;
        &amp;lt;Greeting name='Jaina' /&amp;gt;
        &amp;lt;Greeting name='Valeera' /&amp;gt;
      &amp;lt;/View&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;State&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;보통 생성자에서 state를 초기화하고나서 필요할 때, setter를 호출하는 방식을 쓴다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import React, { Component } from 'react';
import { Text, View } from 'react-native';

class Blink extends Component {
  constructor(props){
    super(props);
    this.state = { isShowingText: true };
  }

  componentDidMount(){
    // Toggle the state every second
    setInterval(() =&amp;gt; (
      this.setState(previousState =&amp;gt; (
        { isShowingText: !previousState.isShowingText }
      ))
    ), 1000);
  }
  render() {
    if (!this.state.isShowingText) {
      return null;
    } else {
      return (
      	&amp;lt;Text&amp;gt;{this.props.text}&amp;lt;/Text&amp;gt;
    	);
    }
  }
}

export default class App extends Component {
  render() {
    return (
      &amp;lt;View&amp;gt;
        &amp;lt;Blink text='I love to blink' /&amp;gt;
      &amp;lt;/View&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Design&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;각 디자인을 구성하는 컴포넌트들(View 등) 마다 style 속성으로 지정한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;width : 가로길이를 숫자 혹은 비율(%)로 지정&lt;/li&gt;
&lt;li&gt;height : 세로길이를 숫자 혹은 비율(%)로 지정&lt;/li&gt;
&lt;li&gt;flex : 크기를 비율(정수)로 지정&lt;/li&gt;
&lt;li&gt;backgroundColor : 색깔영문명 혹은 #ffffff 혹은 rgb(255, 255, 255)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기본적으로 레이아웃은 세로로 누적되는 방식인데 flexDirection으로 가로, 세로 설정 가능하다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;flexDirection : 기본값은 column이고 row로 하면 횡으로 쌓임&lt;/li&gt;
&lt;li&gt;alignItems : flexDirection과 수직한 정렬에 대한 값으로 flex-start, center, flex-end, stretch, baseline이 있다.&lt;/li&gt;
&lt;li&gt;justifyContent : flexDirection과 수평한 정렬에 대한 값으로 flex-start, center, flex-end, space-between(양쪽정렬), space-around(공백있는 양쪽정렬)이 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev-</category>
      <author>담배맛구마</author>
      <guid isPermaLink="true">https://kit2013.tistory.com/353</guid>
      <comments>https://kit2013.tistory.com/353#entry353comment</comments>
      <pubDate>Tue, 31 Mar 2020 15:07:44 +0900</pubDate>
    </item>
  </channel>
</rss>