<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Flative 기술 블로그</title>
    <description>Flative 기술 블로그입니다. 프로젝트를 통해 얻게되는 경험이나 생각들을 공유합니다.
</description>
    <link>https://flative.github.io/blog/</link>
    <atom:link href="https://flative.github.io/blog/rss" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 18 Nov 2018 07:47:55 +0000</pubDate>
    <lastBuildDate>Sun, 18 Nov 2018 07:47:55 +0000</lastBuildDate>
    <generator>Jekyll v3.7.4</generator>
    
      
    
      
        <item>
          <title>[Python] flake8 git hook의 문제와 해결</title>
          <description>&lt;p&gt;안녕하세요. Flative의 최민석 입니다.&lt;/p&gt;

&lt;p&gt;Python으로 개발을 하며 보다 일관된 규칙으로, 효율적으로 개발하기 위해 &lt;code class=&quot;highlighter-rouge&quot;&gt;flake8&lt;/code&gt; 린터를 붙여 CI에서 체크하게 해두던 중. 매번 새로 push하기 전&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;flake8 &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; .flake8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;하기가 귀찮아 이 과정을 자동으로 할 수는 없을까? 해서 &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt;에서 커밋, 푸시, 커밋 메세지 등 다양한 상황에서 사용할 수 있는 hook이란걸 알게되었습니다.&lt;/p&gt;

&lt;p&gt;그 중 커밋 전에 적용되는 pre-commit훅에 &lt;code class=&quot;highlighter-rouge&quot;&gt;flake8&lt;/code&gt;을 붙이려고 이런저런 시도들을 하다가,&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;flake8&lt;/code&gt;에서 공식적으로 지원하는&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;flake8 &lt;span class=&quot;nt&quot;&gt;--install-hook&lt;/span&gt; git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;명령어로 훅을 설치하고 커밋을 하려 했는데 문제가 발생했습니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;flake8 린터가 파이썬이 아닌 파일들(.md, .txt, .rst, …)을 검사하고 에러 발생&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/var/folders/bb/0_8jdgw15f72f18m_fpqwcqr0000gn/T/tmpqbcrt3vn/Users/Curzy/Workspace/flative/hbnn/hbnn/README.md:3:2:
E999 SyntaxError: invalid syntax
/var/folders/bb/0_8jdgw15f72f18m_fpqwcqr0000gn/T/tmpqbcrt3vn/Users/Curzy/Workspace/flative/hbnn/hbnn/README.md:3:20:
E226 missing whitespace around arithmetic operator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;config파일에 의해 검사에서 제외된 파일들을 포함하여 검사해서 에러 발생&lt;/li&gt;
&lt;/ol&gt;

&lt;pre&gt;&lt;code class=&quot;language-.flake8&quot;&gt;[flake8]
ignore = F401
exclude =
    .git,
    __pycache__,
    hbnn/settings.py,
    manage.py,
    */migrations
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./hbnn/settings.py:102:80: E501 line too long (83 &amp;gt; 79 characters)
./user/migrations/0001_initial.py:20:80: E501 line too long (88 &amp;gt; 79 characters)
./user/migrations/0001_initial.py:21:80: E501 line too long (103 &amp;gt; 79 characters)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;첫 번째 문제는&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-before&quot;&gt;def find_modified_files(lazy):
    diff_index_cmd = [
        'git', 'diff-index', '--cached', '--name-only',
        '--diff-filter=ACMRTUXB', 'HEAD`
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;변경된 파일들을 가져오는 함수를&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-after&quot;&gt;def find_modified_files(lazy):
    diff_index_cmd = [
        'git', 'diff-index', '--cached', '--name-only',
        '--diff-filter=ACMRTUXB', 'HEAD', '|', 'grep', '-e', '\.py$'
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;파이썬 확장자에 해당하는 경우로 필터링하여 해결 했고.&lt;/p&gt;

&lt;p&gt;두번째 문제는
excludes(제외할 리스트)의 앞에 검사에서 임시적으로 사용할 temp path를 붙여서 실제 제외할 파일의 경로와 맞지 않아 생겼던 문제인걸로 판단하여,&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-before&quot;&gt;def update_excludes(exclude_list, temporary_directory_path):
    return [
        (temporary_directory_path + pattern)
        if os.path.isabs(pattern) else pattern
        for pattern in exclude_list
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-after&quot;&gt;def update_excludes(exclude_list, temporary_directory_path):
    return [
        (temporary_directory_path + pattern)
        for pattern in exclude_list
        if os.path.isabs(pattern)
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;원본 file 경로와 임시 경로를 모두 포함하게 해서 해결했습니다.&lt;/p&gt;

&lt;p&gt;이러고 기분좋게 히히 드디어 오픈소스에 첫 기여를 할 수 있다는 상상에 &lt;code class=&quot;highlighter-rouge&quot;&gt;flake8&lt;/code&gt;  &lt;a href=&quot;https://gitlab.com/pycqa/flake8&quot;&gt;PyCQA / flake8 · GitLab&lt;/a&gt; 에 머지 리퀘스트를 날렸는데!&lt;/p&gt;

&lt;p&gt;날리고 보니 현재 진행중인 개발버전에서 처리가 되었고 다음 버전(3.3.0) 릴리즈때 반영될 예정이라고 합니다. ㅜ-ㅜ&lt;/p&gt;

&lt;p&gt;그래도 다음 버전이 나오기 전까지 사용할 수 있는. 위 문제들을 해결한 pre-commit-installer 스크립트를 만들었으니. 필요하신 분들께 도움이 되었으면 합니다.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/Curzy/2381055de0ec160ec0b69d325f7e5ef9&quot;&gt;pre-commit-installer.py&lt;/a&gt;
위 스크립트 파일 다운받고,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;python pre-commit-installer.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;로 설치할 수 있습니다.&lt;/p&gt;

&lt;p&gt;린터를 통과하지 못할 경우 커밋이 불가능하게 해두었는데 원하지 않는 분이라면 set_strict()를 빼고&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git config &lt;span class=&quot;nt&quot;&gt;--bool&lt;/span&gt; flake8.strict &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;로 strict 설정을 변경해주면 됩니다 :)&lt;/p&gt;

&lt;p&gt;감사합니다.&lt;/p&gt;
</description>
          <pubDate>Sun, 05 Feb 2017 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2017/02/05/flake8-git-hook/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2017/02/05/flake8-git-hook/</guid>
          
          <category>python</category>
          
          
        </item>
      
    
      
        <item>
          <title>C나 자바에서 세미콜론을 쓰는 이유</title>
          <description>&lt;p&gt;처음 C언어를 배울 때 세미콜론을 자주 빠트렸던 기억이 있다. 마침표도 아니고, 세미콜론으로 명령어를 끝맺는 건 언뜻 보기에 부자연스럽다. 어셈블리어와 같은 초기의 프로그래밍 언어는 개행으로 명령문을 구별했다. 그 뒤 무슨 일이 있어났는지는 모르겠지만 C나 자바(Java)같은 언어는 세미콜론을 쓰고 파이썬(Python)이나 루비(Ruby)는 쓰지 않는다. 이 세미콜론이라는 게 대체 어디서부터, 어떤 이유로 쓰이게 된 것일까.&lt;/p&gt;

&lt;h1 id=&quot;세미콜론은-알골로부터&quot;&gt;세미콜론은 알골로부터&lt;/h1&gt;

&lt;p&gt;새로운 프로그래밍 언어는 기존에 있던 프로그래밍 언어의 특징이나 구조를 그대로 따르는 경우가 많다. C와 자바 역시 그럴 것이다. 처음 세미콜론을 사용한 언어인 알골(ALGOL)에서 그 이유를 찾아보자. 알골의 설계자들은 최대한 수학 식에 가깝게 코드를 구성하고 싶어했다. 그러나 그 당시의 모든 컴퓨터가 수학 식을 입력할 수 있는 기능을 제공하지 않았을 뿐더러, 컴퓨터마다 지원하는 문자가 달라 서로 호환이 되지 않았다. 때문에 우선 기준이 되는 언어를 정하고, 여기에 맞추어 각 컴퓨터의 문자로 표현할 수 있는 변형된 언어가 존재할 수 있도록 허용했다.&lt;/p&gt;

&lt;p&gt;그러기 위해서는 우선 기준이 되는 언어를 명확히 다루어야 했다. 언어의 여러 구성 요소를 기호로 표현한다면, 특정 시스템에서는 이 기호에 이러이러한 문자가 대응한다라는 것을 쉽게 정의할 수 있다. 명령문과 명령문을 구분하는 문자 역시 기호로서 정의할 필요가 있었다. 개행은 기호라고 치기 어려우므로 채택할 수 없었을 것이다. 무언가를 구분하는 문자로서 가장 유명한 것은 쉼표인데, 쉼표는 이미 리스트를 만들 때 각 요소의 구분 기호로 쓰이고 있었다:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;for a := 1, 3, 5, 9.76, ..., -13.75;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그래서 대신 쉼표와 유사한 의미를 가진 세미콜론을 채택한 것으로 보인다.&lt;/p&gt;

&lt;h1 id=&quot;명령문-사이에서-명령문-끝으로&quot;&gt;명령문 사이에서 명령문 끝으로&lt;/h1&gt;

&lt;p&gt;세미콜론의 원래 의미는 두 대상을 구분하기 위함이지만, 이상하게도 C에서는 명령어의 사이가 아니라 명령어의 끝에 추가하도록 강제하고 있다. 이는 C의 가까운 조상 언어인 PL/I의 설계자들이 세미콜론을 명령문 끝에 추가하는 게 더 배우기 쉽고 오류도 적다고 판단했기 때문이다. 실제로 파스칼(Pascal)은 알골과 유사하게 세미콜론을 두 명령어의 사이에 넣는데, 종종 이러한 문제로 인해 비판의 대상이 되곤 한다. 다음 파스칼 코드를 보자:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if a then begin
    b0;
    b
end;
c;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;여기에 else문을 추가한다면 다음과 같이 &lt;code class=&quot;highlighter-rouge&quot;&gt;end;&lt;/code&gt;에서 세미콜론을 빼야 하는 번거로움이 있다:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if a then begin
    b0;
    b
end
else
    d;
c;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;장단점&quot;&gt;장단점&lt;/h1&gt;

&lt;p&gt;개행이 아니라 세미콜론같이 특정한 문자를 정해서 명령문을 구분한다면 모니터든 종이든 출력 매체에 상관 없이 자유롭게 개행할 수 있어 편리하다는 장점이 있다. 명령문을 개행으로 구분하는 언어에서는 하나의 명령문을 두 줄에 걸쳐 나누어 입력할 때 백슬래시와 같은 줄 연결 문자를 통해 명령문을 연결해야 하는데, 백슬래시 기호를 붙여서 명령문을 잇는 건 그냥 바로 개행하는 것에 비해 부자연스럽다.&lt;/p&gt;

&lt;p&gt;그렇다고 세미콜론을 쓰는 게 마냥 좋은 것만은 아니다. 세미콜론이라는 문자는 우리가 일상 생활에서 잘 사용하지 않는 문자다. C나 자바에서는 문장을 마치는 용도로 쓰는데, 일상 생활에서의 언어에서는 그런 용도로 쓰는 경우가 없다. 때문에, 특히 초심자의 경우 이를 빠트리거나 잘못된 곳에 사용하는 경향이 있다.&lt;/p&gt;

&lt;p&gt;PL/I에서는 세미콜론을 명령문 사이사이가 아닌 명령문 끝에 항상 추가하는 것을 선택했다. 그렇다면 끝에 추가한다는 의미에 걸맞게 차라리 마침표를 쓰면 어떨까? 실제로 코볼(COBOL)은 명령문을 마침표로 끝맺어 자연 언어와 최대한 닮은 꼴을 유지하려 한다. 이런 방법이 사용자에게 친숙함을 줄 수 있겠지만, 모든 언어가 꼭 자연 언어같이 보이는 것만을 중요하게 여겨야 하는 것은 아니다. 언어를 설계할 때 기존의 있었던 언어들과의 유사성에 더 높은 우선순위를 둘 수도 있다. 기존 언어에서 굳이 바꿀 필요가 없는 부분이라고 생각한다면, 그냥 그 부분을 가져오는 게 사용자들에게 친숙한 느낌을 주기도 하고 여러모로 좋다. 여전히 많은 언어가 마침표나 개행이 아닌 세미콜론을 사용하는 이유는, 알골이 그랬고, PL/I가 그랬고, C가 그랬고, C++이 그랬고, 자바와 C#이 그랬기 때문이 아닐까.&lt;/p&gt;

&lt;h1 id=&quot;참조&quot;&gt;참조&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Report on the Algorithmic Language ALGOL, 1959, 42p&lt;/li&gt;
  &lt;li&gt;The Early History and Characteristics of PL/I, 1978, 233p&lt;/li&gt;
  &lt;li&gt;Why Pascal is not my favorite programming language, 1981, 10p&lt;/li&gt;
  &lt;li&gt;Report on the Algorithmic Language ALGOL 60, 1960, 301p&lt;/li&gt;
  &lt;li&gt;Identifying Top Java Errors for Novice Programmers, 2005, 3p&lt;/li&gt;
  &lt;li&gt;The early history of COBOL, 1978, 138p&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Sun, 22 Jan 2017 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2017/01/22/semicolons-c-java/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2017/01/22/semicolons-c-java/</guid>
          
          
        </item>
      
    
      
        <item>
          <title>[Python] 왜 pep8은 pycodestyle이 되었을까?</title>
          <description>&lt;p&gt;안녕하세요. Flative의 최민석 입니다.&lt;/p&gt;

&lt;p&gt;사실 제목의 &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;은 &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/&quot;&gt;PEP-0008&lt;/a&gt;문서가 아닌, Python linter tool &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;입니다.&lt;/p&gt;

&lt;p&gt;Flative에서 새로 준비하고 있는 프로젝트(가제: 혼밥남녀)에 CI를 붙이면서 이왕이면 코드도 보다 효율적으로, 일관된 규칙으로 짜기 위해 linter를 붙이려고 &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;flake8&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;pylint&lt;/code&gt;등등 여러가지 린터들을 찾아보고 비교하고 있던중 본 이야기를 공유하려 합니다.&lt;/p&gt;

&lt;p&gt;그중 &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;이 뭔가 공식적인 느낌이 들어서 검색의 대부분은 &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;을 중심으로 다른 린터들과 비교해 보는것 이었는데,&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/PyCQA/pycodestyle/issues/466&quot;&gt;Please rename this tool · Issue #466 · PyCQA/pycodestyle · GitHub&lt;/a&gt;
(자세한 내용이 궁금하시면 쭉 읽어보시길 추천드립니다.)&lt;/p&gt;

&lt;p&gt;검색도중 이 이슈를 찾았습니다. 처음에는 &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;이라는 네이밍에 단순히 불평이 있는 사람인줄 알았더니 이슈 제안자가 &lt;a href=&quot;https://github.com/gvanrossum&quot;&gt;gvanrossum (Guido van Rossum) · GitHub&lt;/a&gt; 파이썬의 아버지 귀도셨습니다.&lt;/p&gt;

&lt;p&gt;대략적인 대화의 뉘앙스가 이렇게 흘러갑니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;gvanrossum: PEP라는 이름을 사용하면 안된다. Python style guide는 사람을 위해 쓰여진거고 여러 섬세한? 미묘한 부분들이 있다.  &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;(linter)에 문제가 있으면 사람들은 이게 tool의 문제가 아닌 PEP의 문제로 인식할 수 있다.&lt;/p&gt;

  &lt;p&gt;sigmavirus24: 많은 프로젝트들이 &lt;code class=&quot;highlighter-rouge&quot;&gt;flake8&lt;/code&gt;을 활용하고 있고. 대부분 &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;이 &lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/&quot;&gt;PEP-0008&lt;/a&gt;에서 영감을 받은 linter라는것을 알고 있을것이다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;또&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;sigmavirus24: &lt;a href=&quot;https://www.python.org/psf/trademarks/&quot;&gt;PSF Trademark Usage Policy&lt;/a&gt;를 인용하면서 PEP는 상표로 등록되어있지 않다~ 라고 이야기하면서 논쟁이 쭈욱 계속되다가&lt;/p&gt;

  &lt;p&gt;1월 12일  &lt;a href=&quot;https://github.com/Nurdok&quot;&gt;Nurdok (Amir Rachum) · GitHub&lt;/a&gt;이 pep가 붙은 다른 프로젝트 pep257도 곧 이름이 바뀔거다, pep8도 이름이 바뀔거라면 &lt;code class=&quot;highlighter-rouge&quot;&gt;pycodestyle&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;pydocstyle&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;pycodelint&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;pydoclint&lt;/code&gt;등의 이름으로 바꾸는게 좋을거다&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;라고 제안하고.&lt;/p&gt;

&lt;p&gt;결국 &lt;code class=&quot;highlighter-rouge&quot;&gt;pycodestyle&lt;/code&gt;로 이름이 바뀌었습니다
&lt;img src=&quot;/blog/static/images/2017-01-16-python-linter-tool-pep8-renamed-to-pycodestyle/finally.png&quot; alt=&quot;Finally renamed&quot; /&gt;
이 과정을 쭉 지켜보니 느낀점들이&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;네이밍은 신중하게, 그리고 명확하게&lt;/li&gt;
  &lt;li&gt;이름 하나 변경할 뿐인데, &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;이라고 쓰여있던 모든 문서들, 코드 의존성, 사용자에게 &lt;code class=&quot;highlighter-rouge&quot;&gt;pep8&lt;/code&gt;이 &lt;code class=&quot;highlighter-rouge&quot;&gt;pycodestyle&lt;/code&gt;로 변경되었다고 알리기 까지. 많은 번거로움이 뒤따릅니다.&lt;/li&gt;
  &lt;li&gt;네이밍은 정말로 신중하게 하자.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Mon, 16 Jan 2017 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2017/01/16/python-linter-tool-pep8-renamed-to-pycodestyle/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2017/01/16/python-linter-tool-pep8-renamed-to-pycodestyle/</guid>
          
          <category>python</category>
          
          
        </item>
      
    
      
    
      
        <item>
          <title>내가 GraphQL 을 쓰지 않는 이유</title>
          <description>&lt;p&gt;저는 백엔드 개발자이기도 하면서 프론트엔드 개발자이고 iOS 개발자입니다.&lt;/p&gt;

&lt;p&gt;파트별로 서로에 대한 불만은 항상 존재합니다. &lt;code class=&quot;highlighter-rouge&quot;&gt;API 가 별로다&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;요청해야할 것이 너무 많다&lt;/code&gt; 등 여러가지가 있죠. 저는 이 중에서 &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTP 기반 API 호출은 어떻게 써야 효율적인가?&lt;/code&gt; 에 대해서 자주 고민을 합니다.&lt;/p&gt;

&lt;p&gt;프론트엔드 기술들이 빠른속도로 발전하면서 &lt;code class=&quot;highlighter-rouge&quot;&gt;어떤 기술을 사용해야 하나&lt;/code&gt; 도 고민도 자주 합니다. 그러던중 &lt;code class=&quot;highlighter-rouge&quot;&gt;GraphQL&lt;/code&gt; 을 알게됐고, GraphQL 을 통해서 첫 번째 고민을 해결 해보고자 했습니다. 이 과정에서 어떤 것들을 느끼고 뭐라고 결론지었는지 공유해보려 합니다.&lt;/p&gt;

&lt;h2 id=&quot;시작전-간단한-설명&quot;&gt;시작전 간단한 설명&lt;/h2&gt;

&lt;h3 id=&quot;graphql-이란&quot;&gt;GraphQL 이란?&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;GraphQL은 최신 응용 프로그램의 복잡하고 중첩 된 데이터 종속성을 설명하기 위해 고안된 데이터 쿼리 언어입니다. 몇 년 동안 Facebook의 프로덕션에서 사용되었습니다.&lt;/p&gt;

  &lt;p&gt;서버에서는 쿼리를 기본 데이터 패칭 코드에 매핑하도록 GraphQL 시스템을 구성합니다. 이 구성 계층은 GraphQL이 임의의 기본 저장소 메커니즘과 함께 작동하도록합니다. Relay는 GraphQL을 쿼리 언어로 사용하지만 GraphQL의 특정 구현과 관련이 없습니다.&lt;/p&gt;

  &lt;p&gt;출처 : https://facebook.github.io/react/blog/2015/02/20/introducing-relay-and-graphql.html&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;GraphQL&lt;/code&gt; 은 엄밀하게 &lt;code class=&quot;highlighter-rouge&quot;&gt;RESTful API&lt;/code&gt; 와 관계가 없습니다. GraphQL 은 쿼리 문법, &lt;a href=&quot;https://en.wikipedia.org/wiki/Domain-specific_language&quot;&gt;DSL&lt;/a&gt; 입니다. HTTP 와도 관계가 없습니다.
서버와 클라이언트는 이 문법을 &lt;code class=&quot;highlighter-rouge&quot;&gt;GraphQL 구현체&lt;/code&gt; 를 통해서 사용하고, 기존에는 관리하기 힘들었던 nested 한 요청과 응답을 편하게 제어할 수 있습니다.&lt;/p&gt;

&lt;p&gt;javascript 에서는 &lt;a href=&quot;https://github.com/graphql/graphql-js&quot;&gt;graphql-js&lt;/a&gt;, 파이썬에서는 &lt;a href=&quot;https://github.com/graphql-python/graphene&quot;&gt;graphene&lt;/a&gt; 이 대표적인 GraphQL 구현체입니다.&lt;/p&gt;

&lt;p&gt;아래 예시를 보고 한번 느껴보세요.&lt;/p&gt;

&lt;p&gt;RESTful API 예시&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Request:
GET /products/1?fields=%22id%22%2C%20%22name%22%2C%20%22sellers%22 HTTP/1.1
Host: api.ne-go.kr
Accept: application/json

Response:
{
    &quot;id&quot;: 1346463,
    &quot;name&quot;: &quot;BN-NBMD543013IVORY&quot;,
    &quot;sellers&quot;: [{
        &quot;delivery_fee&quot;: null,
        &quot;image&quot;: &quot;http://imageurl&quot;,
        &quot;info&quot;: &quot;뉴발란스 자켓 /BN-NBMD543013IVORY/UNI 우븐 플리스&quot;,
        &quot;mall_name&quot;: &quot;11번가&quot;,
        &quot;old_price&quot;: null,
        &quot;price&quot;: 136040,
        &quot;url&quot;: &quot;http://linkurl&quot;
    }]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;GraphQL 예시 (HTTP 통신을 사용)&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Request:
POST /graphql HTTP/1.1
Host: api.ne-go.kr
Content-Type: application/json
Accept: application/json

query {
  products(id: 1) {
    id
    name
    sellers {
      info
      price
    }
  }
}

Response:
{
    &quot;data&quot;: {
        &quot;id&quot;: 1346463,
        &quot;name&quot;: &quot;BN-NBMD543013IVORY&quot;,
        &quot;sellers&quot;: [
            {
                &quot;info&quot;: &quot;뉴발란스 자켓 /BN-NBMD543013IVORY/UNI 우븐 플리스&quot;,
                &quot;price&quot;: 136040
            }
        ]
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;relay&quot;&gt;Relay&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Relay는 Facebook의 새로운 프레임 워크로서 React 애플리케이션을 위한 데이터 패칭 기능을 제공합니다. React.js Conf (2015 년 1 월)에서 발표되었습니다.&lt;/p&gt;

  &lt;p&gt;각 구성 요소는 GraphQL이라는 쿼리 언어를 사용하여 선언적으로 자체 데이터 종속성을 지정합니다. this.props 의 속성을 통해 구성 요소에서 데이터를 사용할 수 있습니다.&lt;/p&gt;

  &lt;p&gt;개발자는 이러한 React 구성 요소를 자연스럽게 구성하며 Relay는 데이터 쿼리를 효율적인 배치로 구성하고 각 구성 요소에 요청한 데이터를 정확하게 제공하며 데이터 변경시 해당 구성 요소를 업데이트하며 클라이언트 측 모든 데이터를 유지시킵니다.&lt;/p&gt;

  &lt;p&gt;출처 : https://facebook.github.io/react/blog/2015/02/20/introducing-relay-and-graphql.html&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://facebook.github.io/relay/&quot;&gt;Relay&lt;/a&gt; 는 페이스북에서 만든, &lt;a href=&quot;https://facebook.github.io/react/&quot;&gt;React&lt;/a&gt; 와 조합해서 쓰는 라이브러리입니다. react components 마다 필요한 데이터들을 정의해두면 Relay 가 해당 부분들을 관리해주고, 한번에 모아서 요청을 날린 후 받은 결과를 해당되는 컴포넌트들에게 전달해줍니다.&lt;/p&gt;

&lt;h2 id=&quot;본론&quot;&gt;본론&lt;/h2&gt;

&lt;p&gt;서버 개발자가 RESTful 한 API 를 만들어두면, 클라이언트 개발자는 해당 API 를 보다 잘 이해하고 사용할 수 있습니다.&lt;/p&gt;

&lt;p&gt;하지만.. 복잡하고 많은 정보들이 들어가있는 &lt;code class=&quot;highlighter-rouge&quot;&gt;뷰&lt;/code&gt;를 그려야 한다면 ???&lt;/p&gt;

&lt;p&gt;API 를 한번… 다른 API 를 또 한번… 다른 API 를 또 한번… 여기에 순차 요청까지 필요하다면…&lt;/p&gt;

&lt;p&gt;뷰를 보여주기도 전에 네트워크 요청단에서 너무 나도 많은 시간을 소모하게됩니다.&lt;/p&gt;

&lt;p&gt;이 부분에서 GraphQL 은 너무나도 매력있게 느껴졌습니다. 과도하게 많아질 수 있는 호출들을 한방에 해결할 수 있고, 초기 로딩이 빨라지면서 UX 에도 좋은 영향을 줄것이고, 새로운 모델이 추가되거나 기존 모델이 수정되지 않는 한 뷰가 바뀌어도 서버쪽에 추가적인 요청 없이 입맛대로 요청이 가능합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;하지만 문제점들이 있습니다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;첫번째로 (자바스크립트를 제외하고) GraphQL 구현체들의 완성도가 아직 미숙합니다. GraphQL 은 커뮤니티 드리븐 방식이기 때문에 페이스북의 도움또한 없습니다. 그리고 대부분의 커뮤니티는 js 쪽에 형성이 되어있습니다. 파이썬과같은 주류 언어에서는 잘 개발되고있지만 다른 언어들은 미숙합니다. 레포 스타 갯수도 100개 언저리고 과연 지속적으로 사용이 가능할지 장담할 수 없습니다.&lt;/p&gt;

&lt;p&gt;두번째로 (자바스크립트를 제외하고) ORM 모델들과의 결합이 힘듭니다. 파이썬의 경우에는 ORM 모델과 같이 사용할 수 있도록 익스텐션 라이브러리를 만들어놔서 그나마 괜찮지만, 타 비주류 언어들에는 거의 없다시피합니다. ORM 객체와 GraphQL 타입을 둘다 따로 만들어서 따로 관리를 해주어야 합니다. (Go-lang 개발자로서 정말 처참합니다…)&lt;/p&gt;

&lt;p&gt;세번째로 Relay 적용의 어려움과 불편함입니다. 이놈을 적용하기 위해선 꽤나 많은 작업을 해줘야합니다. 기존에 정의된 GraphQL Type 들의 설계를 아예 바꿔야해서 프론트엔드의 React app 에서 뿐만아니라 서버에서도 GraphQL Type 들을 Relay 에 맞게 싹다 다시 작성해야 합니다. 심지어 Relay 를 쓰지않는 다른 iOS 나 Android 앱에서도 재작성해야 합니다.&lt;/p&gt;

&lt;p&gt;네번째로 클라이언트측에서 장점이지만 동시에 단점이 됩니다. API 를 매번 요청할 필요도 없어지고, API 호출을 한번만 해도 되는 강력한 장점과 함께, 클라이언트 개발자가 GraphQL 을 공부해서 쿼리를 직접 짜야한다는 강력한 단점이 생깁니다. 과연 모든 클라이언트 개발자가 이를 받아들일 수 있을지 모르겠네요.&lt;/p&gt;

&lt;h2 id=&quot;결론&quot;&gt;결론&lt;/h2&gt;
&lt;p&gt;만약 백엔드에서 nodejs 를 사용하고 프론트엔드에서는 react + relay 를 사용하고 네이티브 앱에서는 react native + relay 를 사용한다면 첫번째, 두번째, 세번째 문제는 해결이 됩니다.&lt;/p&gt;

&lt;p&gt;하지만 대부분의 서비스들의 기술 스택이 이렇지 않습니다. 보통 네이티브 앱은 Java 와 Swift or Objective-c 를 사용하고 백엔드에서 사용하는 언어는 너무나도 다양하죠. 저 또한 저는 백엔드에서 Python 혹은 Golang 을 사용하고 네이티브 앱에서 Swift  를 사용하고 있습니다.&lt;/p&gt;

&lt;p&gt;이러한 이유들로 GraphQL 을 사용하기에는 상당히 문제가 있다고 판단을 했습니다.&lt;/p&gt;
</description>
          <pubDate>Sat, 17 Dec 2016 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2016/12/17/%EB%82%B4%EA%B0%80-GraphQL-%EC%9D%84-%EC%93%B0%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2016/12/17/%EB%82%B4%EA%B0%80-GraphQL-%EC%9D%84-%EC%93%B0%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0/</guid>
          
          
        </item>
      
    
      
        <item>
          <title>[Python] 간단한 크롤러 만들기</title>
          <description>&lt;h2 id=&quot;서문&quot;&gt;서문&lt;/h2&gt;

&lt;p&gt;  안녕하세요. Flative 의 최현빈입니다.  &lt;/p&gt;

&lt;p&gt;저는 파이썬을 시작한지 얼마 되지 않은 개발자입니다.  그렇기에 혼자 프로젝트를 진행을 한다면 무엇이 좋을까 생각하다 결정하게 된게 &lt;strong&gt;크롤러&lt;/strong&gt; 입니다.&lt;/p&gt;

&lt;p&gt;  뮤지컬에도 관심이 많았고, 나 자신이 필요로 하는 뮤지컬 정보만 보기 좋게 편하게 만들고자는 목적으로 만들게 되었습니다.
그리고 간단하게 크롤러 만드는 방법과 기술을 공유하고자 하여 이 글을 작성하게 되었습니다.
 &lt;/p&gt;

&lt;h2 id=&quot;사용-라이브러리&quot;&gt;사용 라이브러리 &lt;/h2&gt;

&lt;p&gt;  &lt;strong&gt;라이브러리 목록 :&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;BeautifulSoup : HTML 코드를 개발자가 보기 좋게 파싱하는 라이브러리입니다. &lt;/li&gt;
  &lt;li&gt;Requests : 파이썬으로 HTTP 요청을 보내고 받을 수 있는 라이브러리 입니다   .&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;간단하게 설명하자면 Requests를 통해 웹페이지에 요청을 보내어 BeautifulSoup를 통해 HTML 코드를 파싱을 하여 필요 정보를 추출 해내는 것입니다.&lt;/p&gt;

&lt;h2 id=&quot;beautifulsoup-사용하여-크롤러-만들기&quot;&gt;BeautifulSoup 사용하여 크롤러 만들기&lt;/h2&gt;

&lt;p&gt;아래의 코드을 통해 requests를 통해 요청을 주고 받은 후 돌려 받은 데이터를 BeautifulSoup와 lxml를 이용하여 파싱을 진행하였습니다.
BeautifulSoup에서 기본으로 제공을 해주는 html.parser 란 것이 있는데 그 보다 &lt;strong&gt;lxml&lt;/strong&gt; 파서가 더 좋다는 이야기를 들어 해당 파서를 사용하였습니다.
관련된 내용은 추후 작성 할 수 있다면 포스팅을 하도록 하겠습니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;musical_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'http://ticket.interpark.com/Ticket/Goods/GoodsInfo.asp?GroupCode=16009835'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;musical_source_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;musical_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;musical_plain_text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;musical_source_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;musical_soup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BeautifulSoup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;musical_plain_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'lxml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(&lt;strong&gt;lxml&lt;/strong&gt; 파서의 경우 다른 라이브러리들과 마찬가지로 별도로 설치를 진행하셔야 사용 할 수 있습니다. 저자는 간단하게 pip install lxml 을 통해 설치하였습니다.)&lt;/p&gt;

&lt;p&gt;위의 방식으로 파싱을 진행 한 후에는 저자의 경우 간단하게 BeautifulSoup의 두가지의 기능만을 사용하였습니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;musical_title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;musical_soup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IDGoodsName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# find로 찾을 때 class를 이용할 경우&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;musical_etc_tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;musical_soup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'dd'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'class'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'etc'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;첫번째 기능은 find 라는 기능입니다. 주로 하나의 것 아래와 처럼 어떠한 tag에 id로 정확하게 쓰인 것을 찾을 때 사용하였습니다.
물론 find 도 class 를 통해 태그의 클래스 단위로 쓰인 것을 찾을 수 있으나 클래스 단위로 찾을 것이라면 두번째로 소개할 기능을 사용하는게 조금 더 편하다고 생각을 합니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;musical_member_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;musical_soup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'li.members &amp;gt; div &amp;gt; a'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;두번째 기능은 select 라는 기능입니다. 저자는 주로 이 기능을 여러가지의 내용을 가져올 때 사용하였습니다.
위의 처럼 태그의 태그의 태그로 가서 데이터를 찾아 올 수 있고 해당 태그의 class 를 사용하여 좀더 가져올 데이터들의 범위를 좁힐 수 있습니다.&lt;/p&gt;

&lt;p&gt;이 외에도 정말 무식하게는 코드를 뜯어내어 해당 값을 직접 추출 할 수도 있지만 굉장히 노가다가 심한 작업이였기에 최대한 위의 방법들을 사용하여 데이터를 추출하는게 효율적이라고 생각합니다.&lt;/p&gt;

&lt;h2 id=&quot;뮤지컬-크롤러-결과-화면&quot;&gt;뮤지컬 크롤러 결과 화면&lt;/h2&gt;
&lt;p&gt;정말 별것도 아닌데 출력되는 결과물 보고 뿌듯 했습니다.
&lt;img src=&quot;/blog/static/images/2016-11-27-simple-crawler/result.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;작업을-하며-느낀점&quot;&gt;작업을 하며 느낀점&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;가장 크게는 크롤러를 만드는 작업이 정말 무식하게는 노가다적인 일로 만들 수 있지만 크롤링할 페이지가 같은 구조의 연속이 된다면 여러 경우를 자세하게 분석하여 작업한다면 정말 효율적으로 데이터들을 가져올 수 있을거라고 생각합니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;현재에는 많은 페이지들에서 바로 모든 페이지 정보를 가져오는게 아니고 요청 후 비동기 방식으로 이미지 파일이라던가, 기타 다른 정보들을 요청하여 가져오는 경우가 있어 그 부분을 생각하고 웹페이지를 분석하는 능력을 키워야 겠다는 생각을 하게 되었습니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;이제 필요 데이터 크롤링을 해오지만 아직 DB(DataBase)를 붙이지도 못했고, 크론탭을 통해 반복작업을 시키는 작업만 진행하면 그래도 내가 원했던 크롤러가 완성되니 그래도 기쁜 생각이 듭니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;생각보다 예외 상황(데이터가 들죽 날죽)들이 많아서 테스트를 정말 많이 했는데 인터파크에서 차단 때리는거 아닌가 심히 걱정도 했지만 다행히 무사했습니다. 나중에 실제로 돌릴 때 차단 당할까 걱정입니다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(Adviser : Flative team 이재연 님, Flative team 김수홍 님) &lt;/p&gt;

</description>
          <pubDate>Sun, 27 Nov 2016 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2016/11/27/simple-crawler/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2016/11/27/simple-crawler/</guid>
          
          <category>python</category>
          
          <category>crawler</category>
          
          
        </item>
      
    
      
        <item>
          <title>Jekyll + GitHub Pages 의존성 쉽게 관리하기</title>
          <description>&lt;p&gt;안녕하세요. Flative의 이현수입니다. 혹여나 모르는 분들을 위한 짤막한 정보글입니다.&lt;/p&gt;

&lt;p&gt;최근 많은 곳에서 &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;과 &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;를 이용해 블로그를 운영하고 있습니다. Flative도 마찬가지로 두 서비스를 이용해서 블로그를 운영하고 있으며, 제가 현재 근무하고 있는 &lt;a href=&quot;http://www.add2paper.com/about/&quot;&gt;애드투페이퍼&lt;/a&gt;와 &lt;a href=&quot;https://incleaf.github.io&quot;&gt;개인 블로그&lt;/a&gt;에서도 사용하고 있습니다.&lt;/p&gt;

&lt;p&gt;무료로 쉽게 블로그를 운영할 수 있다는 장점도 있지만 Github Pages라는 플랫폼 위에서 돌아가는 블로그이기 때문에 &lt;strong&gt;GitHub Pages의 디펜던시 버전은 점점 올라가는 반면에 블로그의 디펜던시 버전은 일정하게 유지되기 때문에&lt;/strong&gt; 로컬에선 잘 보이던 블로그가 GitHub에 업로드 후에는 제대로 보이지 않는 문제가 발생할 수도 있습니다.&lt;/p&gt;

&lt;p&gt;이와 같은 문제를 해결하기 위해 GitHub에서 &lt;a href=&quot;https://github.com/github/pages-gem&quot;&gt;pages-gem&lt;/a&gt;이라는 Gem을 제공하고 있습니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Gemfile에 &lt;code class=&quot;highlighter-rouge&quot;&gt;gem 'github-pages', group: :jekyll_plugins&lt;/code&gt; 추가&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bundle install&lt;/code&gt; 커맨드를 실행&lt;/li&gt;
  &lt;li&gt;PROFIT!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;도움이 됐으면 좋겠습니다. 다음엔 유익한 글로 찾아오겠습니다.&lt;/p&gt;
</description>
          <pubDate>Fri, 23 Sep 2016 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2016/09/23/manage-github-pages-dependecies-easily/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2016/09/23/manage-github-pages-dependecies-easily/</guid>
          
          <category>jekyll</category>
          
          <category>github-pages</category>
          
          
        </item>
      
    
      
        <item>
          <title>[iOS] 크롬 페이지를 사파리로 열기</title>
          <description>&lt;h2 id=&quot;서문&quot;&gt;서문&lt;/h2&gt;

&lt;p&gt;안녕하세요 이재연입니다.&lt;/p&gt;

&lt;p&gt;저는 출퇴근 시간에 주로 &lt;strong&gt;아이패드 미니4&lt;/strong&gt; 를 통해 슬라이드를 보거나, 개발 블로그를 읽습니다. 이때에는 아이패드용 크롬앱을 사용합니다. 사파리보다는 무겁지만, 윈도우를 포함한 데스크탑끼리와의 동기화를 위함입니다. UI가 깔끔하기도 하구요. 슬라이드는 &lt;a href=&quot;http://www.slideshare.net/&quot;&gt;SlideShare&lt;/a&gt; 에서 보는 편입니다. 아이패드용 앱도 있어서 좋습니다. 하지만, 크롬 브라우저에서 슬라이드쉐어 페이지를 들어가게되면 앱을 깔아뒀어도 웹으로만 볼수가있고, 앱으로는 열수가 없습니다. 제 경우에는 그랬으며 혹시 방법이 있다면 저에게 가르침을 주십시오. 기본 내장앱인 사파리를 통해서는 앱으로 열것인지 제안이 잘 뜨지만, 아무래도 윈도우와의 동기화를 생각해보면 크롬을 계속 쓸수밖에 없었습니다.&lt;/p&gt;

&lt;p&gt;이 불편함을 해결하기 위한 도구로 App extension 을 사용해봤고, 이 경험을 간략하게 적어보려 합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;참고 :&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;xcode 8 기준입니다.&lt;/li&gt;
  &lt;li&gt;swift 3.0 기준입니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;ios-app-extension-이용하기&quot;&gt;iOS App Extension 이용하기&lt;/h2&gt;

&lt;p&gt;우선 새로운 Single View Application 프로젝트를 하나 생성합시다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/Step1.png&quot; alt=&quot;Step1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;정보는 대충 적어주시구요.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/Step2.png&quot; alt=&quot;Step2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;생성후에 프로젝트 기본 화면에서 빨간 네모안의 Targets 부분을 잘 봐주세요&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/Step3.png&quot; alt=&quot;Step3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Add Target&lt;/code&gt; 을 눌러줍시다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/Step4.png&quot; alt=&quot;Step4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Action  Extension  을 선택하고 Next!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/Step5.png&quot; alt=&quot;Step5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;정보도 대충적고 만들어주시면 아래와 같은 그룹이 하나 생성되어있을꺼에요.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/Step6.png&quot; alt=&quot;Step6&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Info.plist 를 열고 &lt;code class=&quot;highlighter-rouge&quot;&gt;App Transport Securuty Setting&lt;/code&gt; , &lt;code class=&quot;highlighter-rouge&quot;&gt;Bundle display name&lt;/code&gt; 이 두가지를 다음과 같이 설정해주세요.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/Step7.png&quot; alt=&quot;Step7&quot; /&gt;&lt;/p&gt;

&lt;p&gt;UIViewController.swift 파일을 하나 만들고 다음과 같이 작성합니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIKit&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;openURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sharedApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;performSelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;inBackground&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;openURL:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sharedApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIApplication&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;responder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIResponder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responder&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIApplication&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;responder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;UIInputViewController+sharedApplication.swift&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;왜 그냥 UIApplication.openURL 함수를 사용하지 않고 우회적으로 실행하는 코드를 작성하는가? 라는 물음에 답해보자면,&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;App Extension 의 경우 self.extensionContext?.openURL 을 통해 실행해야합니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;App Extension 에서는 일반적으로 openURL 을 사용할 수 없습니다. 해당 앱의 Extension 으로 작동해야만 합니다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;이제는 ActionViewController.swift 를 수정해보겠습니다. 이후 스토리보드에서 done action 을 연결해줍니다.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIKit&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;MobileCoreServices&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ActionViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;imageView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIImageView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputItems&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;inputItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as!&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSExtensionItem&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attachments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;itemProvider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as!&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSItemProvider&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemProvider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hasItemConformingToTypeIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;public.url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;itemProvider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;loadItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forTypeIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;public.item&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;completionHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSURL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;openURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;completeRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;returningItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;completionHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;completeRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;returningItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extensionContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;completionHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;실행-화면&quot;&gt;실행 화면&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/blog/static/images/2016-09-16-app-extension/ScreenShot.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;마치며&quot;&gt;마치며&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Action Extension 을 만들었을때 기본적으로 생성되는 MainInterface.storyboard 에는 UIImageView 가 추가되어있는데. 이 부분은 사용하지 않으니, MainInterface.storyboard, ActionViewController.swift 두곳에서 imageView 를 삭제해도 무방합니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;사파리로 열기&lt;/code&gt; 아이콘이 비어있는데, 이 부분은 앱의 아이콘이 들어가게됩니다. 앱 아이콘 이미지를 넣어주세요.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/leejaedus/on-safari&quot; target=&quot;_blank&quot;&gt;https://github.com/leejaedus/on-safari&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;leejaeduss@gmail.com&lt;/li&gt;
&lt;/ol&gt;
</description>
          <pubDate>Fri, 16 Sep 2016 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2016/09/16/app-extension/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2016/09/16/app-extension/</guid>
          
          <category>iOS</category>
          
          
        </item>
      
    
      
        <item>
          <title>[Ubuntu 14.04] AWS EC2 처럼 SSH 사용하기</title>
          <description>&lt;p&gt;Ubuntu 16.04 는 &lt;a href=&quot;http://blog.flative.io/2017/01/03/like-aws/&quot;&gt;여기&lt;/a&gt;에서!!!&lt;/p&gt;

&lt;p&gt;안녕하세요. Flative 의 이재연 입니다.&lt;/p&gt;

&lt;p&gt;저는 &lt;a href=&quot;https://www.rocketpunch.com/companies/nego&quot;&gt;네고&lt;/a&gt;에서 2016년 8월까지 근무를 했으며 이때 네이버 스타트업 지원 사원을 통해 &lt;a href=&quot;https://www.ncloud.com/main/intro&quot;&gt;네이버 클라우드&lt;/a&gt; 서버 4대를 제공받았습니다. 네이버 클라우드의 기본적인 서버 세팅이 마음에 안들어 쭉 사용을 하던중에 AWS EC2 기본 SSH 설정이 마음에 들었고, 이와 같이 SSH 설정을 했던 경험을 쓰고자합니다.&lt;/p&gt;

&lt;h2 id=&quot;불편함&quot;&gt;불편함&lt;/h2&gt;

&lt;h3 id=&quot;1-ssh-세션-유지-시간&quot;&gt;1. SSH 세션 유지 시간&lt;/h3&gt;

&lt;p&gt;기본적인 SSH 세션 시간이 굉장히 짧게 설정되어 있어서 IDE 에서 작업을 하다가 터미널로 돌아와보면 언제나..
&lt;img src=&quot;/blog/static/images/2016-09-11-like-aws/broken-pipe.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;2-매번-쳐야하는-비밀번호&quot;&gt;2. 매번 쳐야하는 비밀번호&lt;/h3&gt;

&lt;p&gt;비밀번호를 왜 매번 쳐야하는가.. 귀찮아..
&lt;img src=&quot;/blog/static/images/2016-09-11-like-aws/password.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;개선-방향&quot;&gt;개선 방향&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;root 권한으로 명령어를 실행할 수 있는 sudo 유저 만들기&lt;/li&gt;
  &lt;li&gt;SSH 연결시 비밀번호 대신 키파일로 접근하도록 하기&lt;/li&gt;
  &lt;li&gt;SSH 커넥션이 기~~~~~~~일게 유지되도록 하기&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;설정-시작&quot;&gt;설정 시작&lt;/h2&gt;

&lt;h3 id=&quot;0-시작전-설명&quot;&gt;0. 시작전 설명&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;root&lt;/strong&gt; 유저만 존재하는 상황부터 시작입니다. 이미 sudo 유저가 존재한다면, &lt;a href=&quot;#Step2&quot;&gt;2번&lt;/a&gt;부터 따라오시면 됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;메인 계정은 AWS EC2&lt;/strong&gt; 와 동일하게 &lt;strong&gt;ubuntu&lt;/strong&gt; 로 합니다. 취향에 따라 바꾸셔도 무방합니다.&lt;/li&gt;
  &lt;li&gt;쉘 입력에서 &lt;strong&gt;# 을 포함한 설명&lt;/strong&gt; 까지 복사해서 치셔도 무방합니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;1-root-권한으로-명령어를-실행할-수-있는-sudo-유저-만들기&quot;&gt;1. root 권한으로 명령어를 실행할 수 있는 sudo 유저 만들기&lt;/h3&gt;

&lt;p&gt;사용자는 &lt;strong&gt;root&lt;/strong&gt; 이어야 합니다.&lt;/p&gt;

&lt;p&gt;쉘 입력&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;adduser ubuntu &lt;span class=&quot;c&quot;&gt;# ubuntu 유저를 생성, 비밀번호는 마음대로 어차피 사용하지 않을겁니다.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EDITOR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;which vi&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# visudo 를 vi 에디터에서 사용하기 위해&lt;/span&gt;
visudo &lt;span class=&quot;c&quot;&gt;# ubuntu 를 sudoer 에 추가&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;에디터에서&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root    ALL=(ALL:ALL) ALL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;부분을&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root    ALL=(ALL:ALL) ALL
ubuntu  ALL=(ALL) NOPASSWD:ALL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;로 수정해주시고 :wq 저장하고 나갑시다.&lt;/p&gt;

&lt;h3 id=&quot;2-1-키파일-만들고-등록하기&quot;&gt;&lt;a name=&quot;Step2&quot;&gt;&lt;/a&gt;2-1. 키파일 만들고 등록하기&lt;/h3&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;su ubuntu &lt;span class=&quot;c&quot;&gt;# ubuntu 사용자로 전환&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# ubuntu 의 홈폴더로 이동&lt;/span&gt;
ssh-keygen &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; rsa &lt;span class=&quot;c&quot;&gt;# 인증에 사용할 키파일 만들기&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; ~/.ssh/id_rsa.pub &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ssh/authorized_keys &lt;span class=&quot;c&quot;&gt;# 인증된 키로 등록하기&lt;/span&gt;
chmod 700 ~/.ssh &lt;span class=&quot;c&quot;&gt;# 폴더 퍼미션 설정&lt;/span&gt;
chmod 600 ~/.ssh/authorized_keys &lt;span class=&quot;c&quot;&gt;# 폴더 퍼미션 설정&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; ~/.ssh/id_rsa  &lt;span class=&quot;c&quot;&gt;# 로컬에서 사용할 키파일 내용을 출력&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;cat 명령어를 통해 출력된 내용중에&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;중요&lt;/strong&gt; : —–BEGIN RSA PRIVATE KEY—– 을 포함하여 —–END RSA PRIVATE KEY—– 까지 복사﻿해주세요.&lt;/p&gt;

&lt;h3 id=&quot;2-2-로컬에서-키파일-사용하기&quot;&gt;2-2. 로컬에서 키파일 사용하기&lt;/h3&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vim keyfile.pem &lt;span class=&quot;c&quot;&gt;# 키파일 생성&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위에서 복사한 내용 그대록 붙여넣기하고 :wq 저장하고 나갑니다. 키파일의 이름 및 저장 위치는 자유롭게 하시면 됩니다.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chmod 400 keyfile.pem &lt;span class=&quot;c&quot;&gt;# 키파일 퍼미션 설정&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-ssh-커넥션-시간-설정-및-비밀번호-대신-키파일을-사용하도록-설정&quot;&gt;3. SSH 커넥션 시간 설정 및 비밀번호 대신 키파일을 사용하도록 설정&lt;/h3&gt;

&lt;p&gt;사용자는 &lt;strong&gt;sudo 권한을 가진&lt;/strong&gt; 사용자이어야 합니다.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;vim /etc/ssh/sshd_config &lt;span class=&quot;c&quot;&gt;# ssh 데몬 설정 수정&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;아래의 내용을 입력해줍니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;참고&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;ClientAliveInterval, ClientAliveCountMax 은 커넥션 유지 시간을 위한 설정입니다.&lt;/li&gt;
  &lt;li&gt;PasswordAuthentication no 는 비밀번호 입력을 통한 세션 연결을 막기 위한 설정입니다.&lt;/li&gt;
  &lt;li&gt;나머지는 원래 쓰던대로 쓰셔도됩니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Port 22
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
UsePrivilegeSeparation yes
ClientAliveInterval 30
ClientAliveCountMax 99999
KeyRegenerationInterval 3600
ServerKeyBits 1024
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 120
PermitRootLogin without-password
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile	%h/.ssh/authorized_keys
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
PasswordAuthentication no
X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server
UsePAM yes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;설정 저장후에는 ssh 데몬을 재시작합니다.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; /etc/init.d/ssh restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;참고&lt;/strong&gt; : 데몬이 꺼졌다 켜졌다는 문구가 출력이 되지 않는다면, 서버를 재시작해주세요. &lt;code class=&quot;highlighter-rouge&quot;&gt;sudo reboot&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;4-로컬에서-서버에-접속해보기&quot;&gt;4. 로컬에서 서버에 접속해보기&lt;/h3&gt;

&lt;p&gt;키파일을 통해 서버에 접속해봅시다.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; keyfile.pem ubuntu@서버주소 &lt;span class=&quot;c&quot;&gt;# ssh 연결&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;마치며&quot;&gt;마치며&lt;/h2&gt;

&lt;p&gt;해당 글은 단순 설정을 위하여 쓰여진 글입니다. EC2 의 설정 파일을 그대로 복사한 내용이며, 설정의 근거는 검색을 통해 알아보시면 더욱 좋을것 같습니다.&lt;/p&gt;

&lt;p&gt;혹시나 문제가 생긴다면.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;서버를 껐다 키세요.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;chmod&lt;/code&gt; 명령을 잘 치셨는지 확인해보시면 좋을것 같습니다.&lt;/li&gt;
  &lt;li&gt;이외에는 메일로!! &lt;a href=&quot;mailto:leejaeduss@gmail.com&quot;&gt;leejaeduss@gmail.com&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
          <pubDate>Sun, 11 Sep 2016 00:00:00 +0000</pubDate>
          <link>https://flative.github.io/blog/2016/09/11/like-aws/</link>
          <guid isPermaLink="true">https://flative.github.io/blog/2016/09/11/like-aws/</guid>
          
          <category>ubuntu</category>
          
          <category>AWS</category>
          
          
        </item>
      
    
  </channel>
</rss>
