<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dingdong.log</title>
        <link>https://velog.io/</link>
        <description>자동화 개발</description>
        <lastBuildDate>Wed, 07 Dec 2022 13:50:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dingdong.log</title>
            <url>https://images.velog.io/images/engineer_km/profile/a711af43-1b76-454e-ae4a-bc228ece5dbe/론다.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dingdong.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/engineer_km" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[zabbix-2] zabbix 구조]]></title>
            <link>https://velog.io/@engineer_km/zabbix-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@engineer_km/zabbix-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Wed, 07 Dec 2022 13:50:42 GMT</pubDate>
            <description><![CDATA[<h2 id="zabbix-구조">zabbix 구조</h2>
<p><img src="https://velog.velcdn.com/images/engineer_km/post/30b2858a-a63c-4749-bafb-fbc266c491fd/image.png" alt=""></p>
<ul>
<li>Zabbix Server : 모니터링을 수행하는 Zabbix 소프트웨어의 중앙 프로세스이자 중앙 데이터 저장소. Zabbix proxy 및 agent와 상호 작용하며 트리거를 계산하고, 알림을 전송한다.</li>
<li>Zabbix Agent : 모니터링 대상에 배포되어 Server로 데이터를 보내주는 프로세스이다.
(장치에서 CPU, 메모리, 디스크 및 네트워크 인터페이스 사용량과 같은 데이터를 수집합니다.)</li>
</ul>
<h2 id="동작-방식">동작 방식</h2>
<ul>
<li><p>Active 방식: 에이전트가 10051/TCP을 사용하여 2분에 한 번씩 서버와 연결하고 모니터링 데이터를 전송하는 방식</p>
<ul>
<li>Agent 가 Server 로 데이터를 전송</li>
</ul>
</li>
<li><p>Passive(기본) 방식: 서버가 10050/TCP를 사용하여 에이전트에 연결하고 데이터를 가져오는 방식</p>
<ul>
<li>Server 가 Agent 의 데이터를 가져오기</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/engineer_km/post/66acda61-6973-4721-aabf-503a47abb47e/image.png" alt=""></p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://honglab.tistory.com/66">https://honglab.tistory.com/66</a></li>
<li><a href="https://blog.zabbix.com/zabbix-agent-active-vs-passive/9207/">https://blog.zabbix.com/zabbix-agent-active-vs-passive/9207/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[zabbix-3] zabbix agent 설치]]></title>
            <link>https://velog.io/@engineer_km/zabbix-agent%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@engineer_km/zabbix-agent%EC%84%A4%EC%B9%98</guid>
            <pubDate>Wed, 07 Dec 2022 13:23:16 GMT</pubDate>
            <description><![CDATA[<h2 id="1-zabbix-repository-설치">1) Zabbix repository 설치</h2>
<pre><code>sudo -i

wget https://repo.zabbix.com/zabbix/5.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_5.0-1%2Bfocal_all.deb

dpkg -i zabbix-release_5.0-1+focal_all.deb

apt update</code></pre><h2 id="2-zabbix-agent-설치">2) Zabbix agent 설치</h2>
<pre><code>apt install zabbix-agent</code></pre><h2 id="3-zabbix-server-ip-로-설정">3) Zabbix server ip 로 설정</h2>
<pre><code>vi /etc/zabbix/zabbix_agentd.conf

# 127.0.0.1 설정을 Zabbix Server ip 로 변경
Server=192.168.0.26</code></pre><h2 id="4-zabbix-agent-process-시작">4) Zabbix agent process 시작</h2>
<pre><code>systemctl restart zabbix-agent


# 시스템 재부팅시, zabbix-agent 재시작하기
systemctl enable zabbix-agent</code></pre><h2 id="5-zabbix-server-에서-host-추가하기">5) Zabbix Server 에서 Host 추가하기</h2>
<p><img src="https://velog.velcdn.com/images/engineer_km/post/88ee3f34-2b80-49b0-a00d-5f134fe60cf3/image.png" alt=""></p>
<p>1) 호스트 명: 호스트 이름을 입력
2) 그룹: 선택 버튼을 클릭하여 하나 또는 여러 개의 기존 그룹을 선택하거나 존재하지 않는 그룹 이름을 입력하여 새 그룹을 만듭니다.
3) IP 주소: 호스트의 IP 주소를 입력합니다. 
<img src="https://velog.velcdn.com/images/engineer_km/post/c65c18ee-b96d-4808-adfd-a7a491c26383/image.png" alt=""></p>
<h2 id="6-추가한-host-에-template-추가하기">6) 추가한 Host 에 template 추가하기</h2>
<p><img src="https://velog.velcdn.com/images/engineer_km/post/022dd5b9-a1d1-48c5-b323-eb66378b9c50/image.png" alt="">
<img src="https://velog.velcdn.com/images/engineer_km/post/b437c349-0d04-4a1c-98dd-0113c4227dc4/image.png" alt="">
<img src="https://velog.velcdn.com/images/engineer_km/post/ef92ae47-9ae2-47c1-9230-4b94b9ba95a4/image.png" alt=""></p>
<ul>
<li>시간이 지나면, 상태가 변경이 됨
<img src="https://velog.velcdn.com/images/engineer_km/post/6ad77670-f6cf-4ed4-acf9-71bd3b357e47/image.png" alt=""></li>
</ul>
<hr>
<h2 id="참고사항-received-empty-response-from-zabbix-agent-at-xxxx-assuming-that-agent-dropped-connection-because-of-access-permissions-발생시">참고사항) Received empty response from Zabbix Agent at [x.x.x.x]. Assuming that agent dropped connection because of access permissions. 발생시</h2>
<p><img src="https://velog.velcdn.com/images/engineer_km/post/044b3825-1dc3-4053-ad25-68dcc3df75e1/image.png" alt=""></p>
<h3 id="1-zabbix-server-에서-host-간의-tcp-연결이-되는지-확인">1) zabbix server 에서 host 간의 tcp 연결이 되는지 확인</h3>
<pre><code>km@km-VirtualBox:~$ nc -vz 192.168.0.28 10050
Connection to 192.168.0.28 10050 port [tcp/zabbix-agent] succeeded!
km@km-VirtualBox:~$
km@km-VirtualBox:~$</code></pre><h3 id="2-host에서-agentconf-설정에-zabbix-server-ip-로-설정-필요">2) host에서 agent.conf 설정에 zabbix server ip 로 설정 필요</h3>
<pre><code>root@km-VirtualBox:~# cat /etc/zabbix/zabbix_agentd.conf | grep ^Server=
Server=127.0.0.1
root@km-VirtualBox:~#
root@km-VirtualBox:~#
vi /etc/zabbix/zabbix_agentd.conf

# 127.0.0.1 설정을 Zabbix Server ip 로 변경
Server=192.168.0.26
root@km-VirtualBox:~# cat /etc/zabbix/zabbix_agentd.conf | grep ^Server=
Server=192.168.0.26</code></pre><h2 id="4-zabbix-agent-process-재시작">4) Zabbix agent process 재시작</h2>
<pre><code>systemctl restart zabbix-agent</code></pre><hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://www.zabbix.com/download?zabbix=5.0&amp;os_distribution=ubuntu&amp;os_version=20.04&amp;components=agent&amp;db=&amp;ws=">https://www.zabbix.com/download?zabbix=5.0&amp;os_distribution=ubuntu&amp;os_version=20.04&amp;components=agent&amp;db=&amp;ws=</a></li>
<li><a href="https://www.zabbix.com/documentation/5.0/en/manual/quickstart/host">https://www.zabbix.com/documentation/5.0/en/manual/quickstart/host</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[zabbix-1] ubuntu 20.04 zabbix 5.0 설치]]></title>
            <link>https://velog.io/@engineer_km/ubuntu-20.04-zabbix-5.0-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@engineer_km/ubuntu-20.04-zabbix-5.0-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Wed, 07 Dec 2022 11:27:56 GMT</pubDate>
            <description><![CDATA[<h2 id="설치-방법-공식-문서">설치 방법: 공식 문서</h2>
<p><a href="https://www.zabbix.com/download?zabbix=5.0&amp;os_distribution=ubuntu&amp;os_version=20.04&amp;components=server_frontend_agent&amp;db=mysql&amp;ws=apache">https://www.zabbix.com/download?zabbix=5.0&amp;os_distribution=ubuntu&amp;os_version=20.04&amp;components=server_frontend_agent&amp;db=mysql&amp;ws=apache</a></p>
<h2 id="1-환경-정보">1) 환경 정보</h2>
<p>OS: Ubuntu 20.04 LTS
DB : MySQL
Web : Apache
zabbix: 5.0 LTS</p>
<h2 id="2-zabbix-repo-설치">2) zabbix repo 설치</h2>
<pre><code>sudo -i

wget https://repo.zabbix.com/zabbix/5.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_5.0-1%2Bfocal_all.deb

dpkg -i zabbix-release_5.0-1+focal_all.deb

apt update</code></pre><h2 id="3-zabbix-server-frontend-agent-설치">3) Zabbix server, frontend, agent 설치</h2>
<pre><code>apt install zabbix-server-mysql zabbix-frontend-php zabbix-apache-conf zabbix-agent</code></pre><h2 id="4-mysql-서버-설치">4) mysql 서버 설치</h2>
<pre><code>apt install mysql-server</code></pre><h2 id="5-mysql-데몬-확인">5) mysql 데몬 확인</h2>
<pre><code>root@km-VirtualBox:~# systemctl status mysql
● mysql.service - MySQL Community Server
     Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2022-12-07 21:14:45 KST; 29s ago
   Main PID: 58748 (mysqld)
     Status: &quot;Server is operational&quot;
      Tasks: 39 (limit: 4588)
     Memory: 361.1M
     CGroup: /system.slice/mysql.service
             mq58748 /usr/sbin/mysqld

12월 07 21:14:44 km-VirtualBox systemd[1]: Starting MySQL Community Server...
12월 07 21:14:45 km-VirtualBox systemd[1]: Started MySQL Community Server.
root@km-VirtualBox:~#</code></pre><h2 id="6-초기-데이터베이스-생성--계정-추가하기">6) 초기 데이터베이스 생성 &amp; 계정 추가하기</h2>
<pre><code># mysql -uroot -p
password
mysql&gt; create database zabbix character set utf8 collate utf8_bin;
mysql&gt; create user zabbix@localhost identified by &#39;password&#39;;
mysql&gt; grant all privileges on zabbix.* to zabbix@localhost;
mysql&gt; set global log_bin_trust_function_creators = 1;
mysql&gt; quit;</code></pre><ul>
<li>database 조회: zabbix 가 있는지 확인<pre><code>mysql&gt; SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| zabbix             |
+--------------------+
5 rows in set (0.00 sec)
</code></pre></li>
</ul>
<p>mysql&gt;
mysql&gt; quit;</p>
<pre><code> - 추가한 계정으로 mysql 로그인이 되는지 확인: zabbix 계정 로그인 해보기</code></pre><p>root@km-VirtualBox:~# mysql -u zabbix -p
Enter password: password</p>
<pre><code>
## 7) Zabbix 서버 에서 초기 스키마와 데이터를 가져오기

</code></pre><p>root@km-VirtualBox:<del># zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix
Enter password:
root@km-VirtualBox:</del>#</p>
<pre><code>
## 8) log_bin_trust_function_creators 옵션을 비활성화</code></pre><h1 id="mysql--uroot--p">mysql -uroot -p</h1>
<p>password
mysql&gt; set global log_bin_trust_function_creators = 0;
mysql&gt; quit;</p>
<pre><code>
## 9) zabbix 서버: DB 설정</code></pre><p>vi /etc/zabbix/zabbix_server.conf</p>
<p>DBPassword=password</p>
<pre><code>
## 10) Zabbix 프런트엔드: PHP 구성</code></pre><p>vi /etc/zabbix/apache.conf</p>
<p>php_value date.timezone Asia/Seoul</p>
<pre><code>![](https://velog.velcdn.com/images/engineer_km/post/06771c1c-a472-4da7-81d3-a842452ce9e6/image.png)


## 11) Zabbix server 와 agent processes 재시작</code></pre><p>systemctl restart zabbix-server zabbix-agent apache2</p>
<h1 id="zabbix-server-와-agent-processes-재부팅시-system-재시작되면-프로세스-자동으로-시작되도록-설정">Zabbix server 와 agent processes 재부팅시 system 재시작되면 프로세스 자동으로 시작되도록 설정</h1>
<p>systemctl enable zabbix-server zabbix-agent apache2</p>
<pre><code>
## 12) zabbix url 접속 및 설정
 - http://IP/zabbix

![](https://velog.velcdn.com/images/engineer_km/post/5247460e-2a0c-4904-aeac-d4febd16ecab/image.png)

![](https://velog.velcdn.com/images/engineer_km/post/0ea427fd-9d06-41e2-925c-b8c60f7c3c6f/image.png)

![](https://velog.velcdn.com/images/engineer_km/post/4d20eb69-5ed3-497d-9b24-6567ebef892b/image.png)

![](https://velog.velcdn.com/images/engineer_km/post/bb2731a5-bad6-4654-b3c6-02977ee09fd8/image.png)

![](https://velog.velcdn.com/images/engineer_km/post/6ffd42e2-d7ff-472c-8316-18007bd78b77/image.png)

![](https://velog.velcdn.com/images/engineer_km/post/abedffa8-574b-4040-8e9e-39badf8b42b6/image.png)


## 13) Zabbix 로그인
- Username : Admin
  Password : zabbix 
![](https://velog.velcdn.com/images/engineer_km/post/901d107d-4420-415a-b2f0-00fab1c8de6b/image.png)

![](https://velog.velcdn.com/images/engineer_km/post/1d588a62-0305-48c5-bf74-914a8727fed0/image.png)


## (참고사항) 언어 한글 변경하기
![](https://velog.velcdn.com/images/engineer_km/post/c09a1449-d2e1-4fda-887c-29eeab3adf26/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[django migrate 초기화 - 1]]></title>
            <link>https://velog.io/@engineer_km/django-migrate-%EC%B4%88%EA%B8%B0%ED%99%94-eqg8qwo3</link>
            <guid>https://velog.io/@engineer_km/django-migrate-%EC%B4%88%EA%B8%B0%ED%99%94-eqg8qwo3</guid>
            <pubDate>Sat, 22 Jan 2022 11:07:50 GMT</pubDate>
            <description><![CDATA[<p>마이그레이션 파일은 모두 지우지만 데이터베이스 데이터는 유지하고 싶은 경우 사용하는 방법이다.</p>
<p>1) python manage.py showmigrations &lt;앱 이름&gt;</p>
<pre><code>현재 migrations 내역을 확인합니다.
0001_initial, 0002_post_author 에 [x] 가 되어있다. (migrate 적용된 상태)
(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py showmigrations blog1
blog1
 [X] 0001_initial
 [X] 0002_post_author
(venv) root@km-VirtualBox:~/django-with-react-study#</code></pre><h3 id="1-python-managepy-showmigrations-앱-이름">1) python manage.py showmigrations &lt;앱 이름&gt;</h3>
<ul>
<li>현재 migrations 내역을 확인합니다.</li>
<li>0001_initial, 0002_post_author 에 [x] 가 되어있다. (migrate 적용된 상태)<pre><code>(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py showmigrations blog1
blog1
[X] 0001_initial
[X] 0002_post_author
(venv) root@km-VirtualBox:~/django-with-react-study#</code></pre></li>
</ul>
<h3 id="2-마이그레이션-히스토리-삭제">2) 마이그레이션 히스토리 삭제</h3>
<ul>
<li>zero 는 migrations 처음 상태로 돌아감.<ul>
<li><code>__init__.py</code> 까지 돌아감.</li>
<li><code>__init__.py</code> 은 남아있어야됨. </li>
<li>.pyc 파일도 삭제할 것</li>
<li>0001_initial, 0002_post_author 에 [ ] 로 되어있어야된다.<blockquote>
<p>python manage.py migrate --fake &lt;앱 이름&gt; zero</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<pre><code>
(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py migrate --fake blog1 zero
Operations to perform:
  Unapply all migrations: blog1
Running migrations:
  Rendering model states... DONE
  Unapplying blog1.0002_post_author... FAKED
  Unapplying blog1.0001_initial... FAKED
(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py showmigrations blog1
blog1
 [ ] 0001_initial
 [ ] 0002_post_author
(venv) root@km-VirtualBox:~/django-with-react-study#
</code></pre><h4 id="3--초기화-경우__init__py만-남기고-나머지-파일들을-다-지우기">3)  초기화 경우:<code>__init__.py</code>만 남기고 나머지 파일들을 다 지우기</h4>
<ul>
<li>migrations 폴더에 <code>__init__.py</code> 를 제외하고 다 삭제</li>
</ul>
<pre><code>rm blog1/migrations/0001_initial.py

rm blog1/migrations/0002_post_author.py</code></pre><h3 id="4-python-managepy-makemigrations-앱-이름">4) python manage.py makemigrations &lt;앱 이름&gt;</h3>
<ul>
<li>초기 마이그레이션 파일 생성<pre><code></code></pre></li>
</ul>
<p>(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py makemigrations blog1
Migrations for &#39;blog1&#39;:
  blog1/migrations/0001_initial.py
    - Create model Post</p>
<pre><code>

### 5) 페이크 마이그레이션
  - 데이터베이스 테이블이 이미 존재하기 때문에 초기 마이그레이션 파일을 적용할 수 없다. 
    따라서 마치 마이그레이션을 한 것처럼 아래와 같이 명령하여 페이크 마이그레이션한다.
  - `--fake` 는 현재 마이그레이션 적용 상태를 변경
  - `--fake-initial` 는 첫 마이그레이션을 페이크

</code></pre><p>python manage.py migrate --fake-initial</p>
<p>```</p>
<br>
<br>

<h3 id="참고">참고</h3>
<ul>
<li><a href="https://wikidocs.net/9926">django 마이그레이션 파일 제거</a></li>
<li><a href="https://stackoverflow.com/questions/42695629/fake-initial-vs-fake-in-django-migration">--fake-initial vs --fake in Django migration?</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[django-bootstrap4]]></title>
            <link>https://velog.io/@engineer_km/django-bootstrap4-oq28gz16</link>
            <guid>https://velog.io/@engineer_km/django-bootstrap4-oq28gz16</guid>
            <pubDate>Sat, 22 Jan 2022 02:03:31 GMT</pubDate>
            <description><![CDATA[<p>django-bootstrap4 는
Django에서 bootstrap을 사용하기위해 사용되는 라이브러리이다.
django-bootstrap4 사용 방법에 대해서 간략히 소개한다.</p>
<br>
<br>

<h2 id="공식-문서">공식 문서</h2>
<ul>
<li><a href="https://django-bootstrap4.readthedocs.io/en/latest/">https://django-bootstrap4.readthedocs.io/en/latest/</a></li>
</ul>
<h2 id="django-bootstrap4-github">django-bootstrap4 github</h2>
<ul>
<li><a href="https://github.com/zostera/django-bootstrap4">https://github.com/zostera/django-bootstrap4</a></li>
</ul>
<br>
<br>

<h2 id="설치-방법">설치 방법</h2>
<pre><code>pip install django-bootstrap4</code></pre><ul>
<li>settings.INSTALLED_APPS 에 <code>bootstrap4</code> 추가 합니다.<pre><code>INSTALLED_APPS = (
# ...
&quot;bootstrap4&quot;,
# ...
)</code></pre></li>
</ul>
<br>
<br>

<h2 id="예제-코드">예제 코드</h2>
<ul>
<li>{% load bootstrap4 %}<ul>
<li>bootstrap4 에 정의된 tag(함수) 를 사용하기 위해서
load (import) 시켜준다.</li>
</ul>
</li>
<li>{% bootstrap_form form %}<ul>
<li>bootstrap 으로 form 을 꾸며주기 위해서 사용된 tag(함수) </li>
</ul>
</li>
<li><a href="https://django-bootstrap4.readthedocs.io/en/latest/templatetags.html">그밖의 TAG 정보: 공식문서 참고</a></li>
</ul>
<pre><code class="language-html">{% load bootstrap4 %}

{# Display a form #}

&lt;form action=&quot;/url/to/submit/&quot; method=&quot;post&quot; class=&quot;form&quot;&gt;
    {% csrf_token %}
    {% bootstrap_form form %}
    {% buttons %}
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;Submit&lt;/button&gt;
    {% endbuttons %}
&lt;/form&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django ORM (QuerySet)구조와 원리 그리고 최적화전략 - PyCon Korea 2020 발표 정리]]></title>
            <link>https://velog.io/@engineer_km/Django-ORM-QuerySet%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B5%9C%EC%A0%81%ED%99%94%EC%A0%84%EB%9E%B5-PyCon-Korea-2020-%EB%B0%9C%ED%91%9C-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@engineer_km/Django-ORM-QuerySet%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B5%9C%EC%A0%81%ED%99%94%EC%A0%84%EB%9E%B5-PyCon-Korea-2020-%EB%B0%9C%ED%91%9C-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 21 Jan 2022 11:45:02 GMT</pubDate>
            <description><![CDATA[<p>이전에 파이콘 2020 에서 들었던 내용인데
세미나 발표 내용이 개인적으로 알게 된 부분들이 많았었습니다.👍
(김성렬님께 감사합니다🙏)
개인적으로 다시 한번 리마인드 차원에서 블로그에 한번 정리하려고 합니다. </p>
<br>
<br>

<h1 id="발표-정보-👀">발표 정보 👀</h1>
<ul>
<li><p><a href="https://www.youtube.com/watch?v=EZgLfDrUlrk&amp;ab_channel=PyConKorea">https://www.youtube.com/watch?v=EZgLfDrUlrk&amp;ab_channel=PyConKorea</a></p>
</li>
<li><p><a href="https://github.com/KimSoungRyoul/Django_ORM_pratice_project/issues/7">https://github.com/KimSoungRyoul/Django_ORM_pratice_project/issues/7</a></p>
</li>
</ul>
<br>
<br>

<h2 id="queryset을-통해-알아보는-orm의-특징">QuerySet을 통해 알아보는 ORM의 특징</h2>
<h3 id="1-lazy-loading-지연로딩--정말-필요한-시점에-sql을-호출">1) Lazy Loading 지연로딩 : 정말 필요한 시점에 SQL을 호출</h3>
<h4 id="1-1-실제로-쿼리가-호출되는-시점">1-1) 실제로 쿼리가 호출되는 시점</h4>
<ul>
<li>list()로 쿼리셋을 묶는 로직이 수행될때 SQL이 호출된다</li>
<li><span style="color:slateblue"> [내가 추가한 내용] : QuerySet을 만드는 동안에는 DB접근을 하지 않는다.<ul>
<li>print(query_set)</li>
<li>iteration : for instance in query_set: print(instance)</li>
<li>Slicing</li>
<li>repr() : 파이썬 인터프리터를 위한 것, API를 인터프리터로 사용할 때 즉시 결과를 볼 수 있다.</li>
<li>len()</li>
<li>bool()
...</span>

</li>
</ul>
</li>
</ul>
<pre><code class="language-python">
from .models import Post

query_set = Post.objects.all()  # QuerySet. 아직 SQL 호출안됨.

list(query_set)</code></pre>
<h4 id="1-2-만약-queryset을-선언했지만-사용하지-않는다면-sql은-호출되지-않는다">1-2) 만약 QuerySet을 선언했지만 사용하지 않는다면 SQL은 호출되지 않는다.</h4>
<pre><code class="language-python">
from .models import Post

query_set = Post.objects.all()  # QuerySet. 아직 SQL 호출안됨. 사용하는 곳 없음.</code></pre>
<h4 id="1-3-현-시점에-딱-필요한-만큼만-데이터를-가져옴">1-3) 현 시점에 딱 필요한 만큼만 데이터를 가져옴</h4>
<pre><code>- ORM은 이후 로직을 알지 못한다.
  - 현 시점에 딱 필요한 만큼만 데이터를 가져오려 하기 때문에 비효율적으로 데이터를 가져올 수 있다.
- SQL을 한번만 호출해서 데이터를 재사용할 것</code></pre><pre><code class="language-python">
# 불필요하게 SQL이 두번 호출
# 실수 예제


from .models import Post

query_set = Post.objects.all()  # QuerySet. 아직 SQL 호출안됨.

first_post = query_set[0]   # &lt;Post: Post object (1)&gt;

post_list = list(query_set)  # [&lt;Post: Post object (1)&gt;, &lt;Post: Post object (2)&gt;, &lt;Post: Post object (3)&gt;]
</code></pre>
<br>
<br>

<h3 id="2-caching--queryset-캐싱을-재사용-하는-법">2) Caching : QuerySet 캐싱을 재사용 하는 법</h3>
<h4 id="2-1-쿼리셋을-호출하는-순서가-바뀌는것-만으로도-queryset-캐싱-때문에-발생하는-sql이-달라질-수-있다">2-1) 쿼리셋을 호출하는 순서가 바뀌는것 만으로도 QuerySet 캐싱 때문에 발생하는 SQL이 달라질 수 있다.</h4>
<ul>
<li><code>1-3 실수예제</code>를 해결 할 수 있음.</li>
<li>모든 post 를 가져오는 sql이 호출되어서
post_list 쿼리셋에는 모든 post 데이터가 캐싱되어있음</li>
</ul>
<pre><code class="language-python">from .models import Post


query_set = Post.objects.all()  # QuerySet. 아직 SQL 호출안됨.

post_list = list(query_set)  

first_post = query_set[0]  # QuerySet 캐싱으로 인해서 SQL 호출되지 않음.
                           # 이미 모든 post_list 에 대한 정보를 가지고 있고
                           # 0번째 post 를 캐싱해서 가져옴.


</code></pre>
<br>
<br>

<h3 id="3-eager-loading-즉시로딩--n1-problem">3) Eager Loading 즉시로딩 : N+1 Problem</h3>
<h4 id="3-1-n1-problem-">3-1) N+1 Problem ?</h4>
<ul>
<li>쿼리 1번으로 N건의 데이터를 가져왔는데 원하는 데이터를 얻기 위해 
이 N건의 데이터를 데이터 수 만큼 반복해서 2차적으로 쿼리를 수행하는 문제다.</li>
<li>Lazy Loading 으로 인해서 발생되는 문제 중 하나이다.</li>
</ul>
<p>(밑에 예제코드를 기준으로 설명)</p>
<ul>
<li>for post in posts:<ul>
<li>모든 Post 조회함 (SQL 실행: 1)</li>
</ul>
</li>
<li>post.title  <ul>
<li>반복문 돌면서 title 접근 (SQL 실행: N번)
post의 title 값은 캐싱되어 있지 않아서 SQL 호출<ul>
<li>사실 모든 post 정보가 posts에 담겨있지만 QuerySet은 모른다.  </li>
</ul>
</li>
</ul>
</li>
<li>N+1 Problem을 해결하기 위해 (==즉시로딩을 하기위해) Django는 select_related()와 prefetch_related() 라는 메서드를 제공</li>
</ul>
<pre><code class="language-python">
from .models import Post


posts = Post.objects.all()  # QuerySet. 아직 SQL 호출안됨.


# 개발자 입장에서는 각 post의 title 정보가 posts에 담겨 있는 것을 알지만 QuerySet은 모른다. 
for post in posts:  # 모든 Post 조회함 (SQL 호출: 1)
    # QuerySet 입장에서는 post의 title 정보가 필요한 시점은 여기.
    # 따라서 title 를 알기위해서 SQL이 for 문을 돌때마다 N번 호출한다. (SQL 호출: N번)
    post.title    # post.title를 조회할때마다 sql이 계속 호출되는 문제가 발생
</code></pre>
<br>
<br>

<h3 id="4-queryset-상세">4) QuerySet 상세</h3>
<h3 id="4-1-queryset은-1개의-쿼리와-0n개의-추가쿼리셋로-구성되어있다">4-1) QuerySet은 1개의 쿼리와 0~N개의 추가쿼리(셋)로 구성되어있다.</h3>
<p><img src="https://images.velog.io/images/engineer_km/post/499e0b12-7496-41a0-a7aa-d54dff0e959c/image.png" alt=""></p>
<h3 id="4-2-select_related-와-prefetch_related">4-2) select_related() 와 prefetch_related()</h3>
<ul>
<li><p>select_related()</p>
<ul>
<li>join을 통해서 데이터를 즉시 로딩하는 방식</li>
</ul>
</li>
<li><p>prefetch_related()</p>
<ul>
<li>추가 쿼리를 통해서 데이터를 즉시 로딩하는 방식</li>
</ul>
</li>
<li><p>모델M:모델R 가 (1 : N) 관계 일때</p>
<ul>
<li>모델M 입장에서 모델R은 <code>역방향 참조 모델</code></li>
<li>모델R 입장에서 모델M은 <code>정방향 참조 모델</code></li>
</ul>
</li>
<li><p>역방향참조 모델은 select_related()에 줄수없다.</p>
<ul>
<li>Model.objects().filter(조건절).prefetch_related().select_related()<ul>
<li>위의 순서 django 에서 지원 안됨.</li>
</ul>
</li>
<li>Model.objects().filter(조건절).select_related().prefetch_related()<ul>
<li>django 에서 지원 됨.</li>
</ul>
</li>
</ul>
</li>
<li><p>정방향참조 모델은 select_related(), prefetch_related()에 옵션으로 둘다 가능
하지만 특별한 이유가 없다면 select_related를 권장함</p>
</li>
</ul>
<pre><code class="language-python">

(
  Model.objects
  .filter(조건절)
  .select_related(&#39;정방향 참조 필드&#39;)    # join으로 가져옴
  .prefetch_related(&#39;역방향 참조 필드&#39;)  # 추가쿼리로 가져옴

)</code></pre>
<ul>
<li>inner/outer 여부는
QuerySet조건절 변경에 따라 JOIN 옵션이 변할수있음
하지만 일반적으로
ForiegnKey(null=True) 이면 OUTER JOIN
ForigenKey(null=False) 이면 INNER JOIN </li>
</ul>
<pre><code class="language-sql">
select * from &#39;Model&#39; m
(inner OR left outer) join &#39;정방향 참조 필드&#39; r on m.r_id == r.id where 조건절;


select * from &#39;역방향 참조 필드&#39; where id in (&#39;첫번째 쿼리 결과의 id 리스트&#39;);
</code></pre>
<h3 id="4-3-prefetch_related는-추가-쿼리셋이다">4-3) prefetch_related()는 추가 쿼리셋이다</h3>
<ul>
<li><p>QuerySet은 1개의 쿼리와 N개의 쿼리(셋)로 이루어져있다.</p>
<ul>
<li>1개의 메인 쿼리</li>
<li>N개의 추가 쿼리</li>
</ul>
</li>
<li><p>prefetch_related()에 주어진 옵션들은 전부 새로운 쿼리셋으로 수행된다.</p>
</li>
<li><p>query_set_1 과 query_set_2 의 SQL 문은 동일하다.</p>
</li>
</ul>
<h4 id="예제-코드1">예제 코드1</h4>
<pre><code class="language-python">
&quot;&quot;&quot;
 1. 메인모델에 역참조된 모델들을 전부 조회하고 싶다면
단순히 역참조필드만 선언해주면
해당 필드를 조회하기위한 SQL이 1개가 더 수행된다.
(이 예제의 경우는 2개를 선언했기때문에 SQL이 2개 더 추가 수행된다.)

&quot;&quot;&quot;

 query_set_1 = (
   AModel.objects.
   prefetch_related(
       &#39;b_model_set&#39;,
       &#39;c_models&#39;,
   )
 )



from djngo.db.models import Prefetch


query_set_2 = (
   AModel.objects.
   prefetch_related(
       Prefetch(to_attr=&#39;b_model_set&#39;, queryset=BModel.objects.all()),
       Prefetch(to_attr=&#39;c_models&#39;, queryset=CModel.objects.all())
   )
)

</code></pre>
<h4 id="예제-코드1--sql-결과">예제 코드1 : SQL 결과</h4>
<pre><code class="language-sql">
select * from a_model;
select * from b_model where id in (~~~);
select * from c_model where id in (~~~);
</code></pre>
<h4 id="예제-코드2">예제 코드2</h4>
<pre><code class="language-python">
&quot;&quot;&quot;
 2. 추가적인 조건절을 걸고싶다면 ?
Prefetch() 문법을 사용해서 추가쿼리(셋)을 제어하면된다.

&quot;&quot;&quot;python

from djngo.db.models import Prefetch


query_set_2 = (
   AModel.objects.
   prefetch_related(
       Prefetch(to_attr=&#39;b_model_set&#39;, queryset=BModel.objects.filter(is_deleted=False)),
       Prefetch(to_attr=&#39;c_model&#39;, queryset=CModel.objects.all())
   )
)

</code></pre>
<h4 id="예제-코드2--sql-결과">예제 코드2 : SQL 결과</h4>
<pre><code class="language-sql">
select * from a_model;
select * from b_model where id in (~~~) and is_deleted is False;
select * from c_model where id in (~~~);
</code></pre>
<h3 id="4-4-sql-preformance를-커버하는-testcase-capturequeriescontext를-활용하자">4-4) SQL Preformance를 커버하는 Testcase [CaptureQueriesContext를 활용하자]</h3>
<ul>
<li>N+1문제로 인한 크리티컬한 성능 이슈만 커버할때 사용</li>
<li>assertNumQueries()로 테스트케이스를 작성해놓으면
API가 수정될때 마다 달라지는 SQL갯수를 체크해줘야됨.</li>
</ul>
<br>
<br>

<h2 id="5-실수하기-쉬운-queryset의-특성들">5) 실수하기 쉬운 QuerySet의 특성들</h2>
<h3 id="5-1-prefetch_related-와-filter-는-완전-별개다">5-1) prefetch_related() 와 filter() 는 완전 별개다</h3>
<h4 id="실수-예제--비효율적">실수 예제 : 비효율적</h4>
<ul>
<li>실제 SQL 동작: 메인쿼리에서 JOIN하고 추가쿼리에서 한번더 조회하게 됨.</li>
<li>실제 SQL 동작 순서: filter -&gt; prefatch_related<ul>
<li>filter 에서 join 하고
prefatch_related 에서 추가 쿼리</li>
</ul>
</li>
</ul>
<pre><code class="language-python">
company_queryset = (
   Company.objects.
   prefatch_related(&#39;prodcut_set&#39;).
   filter(name=&#39;company_name1&#39;, BModel__name__isnull=False)

)</code></pre>
<pre><code class="language-sql">
# SQL: 1번째 호출: Company, filter
select * from &quot;company&quot;
inner join &quot;product&quot;
  on (&quot;company&quot;.&quot;id&quot; = &quot;product&quot;.&quot;product_owned_company_id&quot;)
where (&quot;company&quot;.&quot;name&quot; = &quot;company_name1&quot;
       and &quot;product&quot;.&quot;name&quot; IS NOT NULL)


# SQL: 2번째 호출
# prefatch_related
select * from &quot;product&quot;
where &quot;product&quot;.&quot;product_owned_company_id&quot; in (1, 43, 75, 23)
</code></pre>
<h4 id="해결방법-예제1-prefatch_related-옵션-제거">해결방법 예제1) prefatch_related 옵션 제거</h4>
<pre><code class="language-python">
queryset = (
   Company.objects.
   # prefatch_related(&#39;product_set&#39;).
   filter(name=&#39;company_name1&#39;, product__name__isnull=False)

)</code></pre>
<h4 id="해결방법-예제2-filter에-넣었던-bmodel-관련-조건절을-prefetch-에-제공">해결방법 예제2) filter()에 넣었던 BModel 관련 조건절을 Prefetch() 에 제공</h4>
<ul>
<li>추가쿼리에 where문이 수정됨</li>
</ul>
<pre><code class="language-python">

queryset = (
   Company.objects.
   filter(name=&#39;company_name1&#39;).
   prefatch_related(
       &#39;product_set&#39;,
       Prefetch(queryset=Product.objects.filter(BModel__name__isnull=False))
   )
)</code></pre>
<h2 id="6-queryset-작성-순서">6) QuerySet 작성 순서</h2>
<p><img src="https://images.velog.io/images/engineer_km/post/4081c6b5-2e32-491f-8e9f-d0a4ebbad52d/image.png" alt=""></p>
<h2 id="7-queryset-캐시를-재활용하지-못하는-queryset-호출">7) queryset 캐시를 재활용하지 못하는 queryset 호출</h2>
<ul>
<li>first_company.product_set.all()<ul>
<li>all() 로 질의 하면 result_cache 를 재사용함.</li>
</ul>
</li>
<li>first_company.product_set.filter(name=&quot;상품명_1&quot;)<ul>
<li>이전에 all()이 캐싱되어 있었음.
filter 를 통해서 특정 상품을 찾으려 하면 result_cache 를 재사용하지 않고 SQL 로 질의함.</li>
</ul>
</li>
</ul>
<pre><code class="language-python">
company_list = list(Company.objects.prefetch_related(&quot;produtct_set&quot;.all())  # EagerLoading함


first_company = company_list[0]



first_company.product_set.all()  # 여기서는 EagerLoading해서 SQL 발생 안함


first_company.product_set.filter(name=&quot;상품명_1&quot;)  # 하지만 이러면 SQL이 발생됨


# sql 을 발생시키지 않으려면 아래처럼 써야됨.
filtered_product_list = [product for product in first_company.product_set.all() if product.name==&quot;상품명1&quot;]</code></pre>
<h2 id="8-rawqueryset은-nativesql이-아니다">8) RawQuerySet은 NativeSQL이 아니다</h2>
<ul>
<li>QuerySet 과 RawQuerySet 의 가장 큰 차이점은<ul>
<li>QuerySet._query 는 sql.Query<ul>
<li>sql.Query: django 에서 제공해주는 query 구현체</li>
</ul>
</li>
<li>RawQuerySet._query 는 sql.RawQuery<ul>
<li>sql.RawQuery: 사용자가 입력해주는 SQL 문</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>RawQuerySet은 QuerySet의 또다른 유형이기 때문에 prefetch_related(), Prefetch()는 사용가능</p>
</li>
<li><p>RawQuerySet은 아래와 같은 메서드는 사용할 수 없다. (아래의 메서드를 사용하려면 NativeSQL로 작성해야됨)</p>
<ul>
<li><code>.select_related()</code><ul>
<li>메인쿼리의 JOIN 옵션을 주는 메서드</li>
</ul>
</li>
<li><code>FilterRelation()</code><ul>
<li>JOIN이 안되니 ON절 제어 옵션 사용 불가</li>
</ul>
</li>
<li><code>.annotate()</code><ul>
<li>메인쿼리에 AS 옵션을 주는 메서드</li>
</ul>
</li>
<li><code>.order_by()</code><ul>
<li>메인쿼리에 order by 옵션 주는 메서드</li>
</ul>
</li>
<li>&#39;.extra()&#39;<ul>
<li>메인쿼리에 sql을 추가 반영하는 메서드</li>
</ul>
</li>
<li>&#39;[:N]&#39;<ul>
<li>메인쿼리에 limit 옵션을 걸수 없음</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">from django.db.models.query import RawQuerySet
from django.db.models import QuerySet


# RawQuerySet
a_model_queryset = (
    AModel.objects.
    raw(raw_query=&quot;&quot;&quot;
        SELECT *
        FROM &quot;order&quot;
        INNER JOIN &quot;user&quot; on (&quot;order&quot;.&quot;order_owner_id&quot; = &quot;user&quot;.&quot;id&quot;)
        WHERE &quot;user&quot;.&quot;username&quot; = %(name_param1)s
    &quot;&quot;&quot;),
        params={&quot;name_param1&quot;: &quot;name_test&quot;}
    ).
    prefetch_reladted(&#39;product_set_included_order&#39;)
)


# 위의 Raw 쿼리셋과 아래 쿼리셋은 동일한 SQL 문을 수행


a_model_queryset = (
    AModel.objects.
    select_related(&#39;order_owner&#39;).
    filter(order_owner_username=&#39;name_test&#39;).
    prefetch_related(&#39;product_set_included_order&#39;)
)
</code></pre>
<h2 id="9-서브쿼리-발생-조건--queryset-in-queryset">9) 서브쿼리 발생 조건 : QuerySet In QuerySet</h2>
<ul>
<li>서브쿼리를 사용하면 slow query 가 야기됨</li>
</ul>
<h4 id="실수-예제">실수 예제</h4>
<ul>
<li>django ORM에서 서브쿼리를 발생시키는 옵션을 주지 않고 발생되는 예제</li>
</ul>
<pre><code class="language-python">
company_queryset = Company.objects.filter(id__lte=20).values_list(&quot;id&quot;, flat=True)  # 아직 SQL문 실행안됨



product_queryset = Product.objects.filter(product_owned_company__id__in=company_queryset)  
# company_queryset 은 product queryset안으로 들어가는 시점에도 아직 queryset 상태임
# 그래서 product queryset과 company_queryset 이 합쳐져서 수행하게됨.

</code></pre>
<pre><code class="language-sql">
select * from product
where &quot;product&quot;.&quot;product_owned_company_id&quot; in (
  select U0.&quot;id&quot; from &quot;company&quot; U0 
  where U0.&quot;id&quot; &lt;= 20
);
</code></pre>
<h4 id="해결방법-예제">해결방법 예제</h4>
<ul>
<li>list 묶어서 querset을 바로 수행<pre><code class="language-python"></code></pre>
</li>
</ul>
<p>company_queryset = list(Company.objects.filter(id__lte=20))  # SQL 수행됨.</p>
<p>product_queryset = Product.objects.filter(product_owned_company__id__in=company_queryset)  </p>
<pre><code>

```sql

# 첫번째 SQL
select * from company
where &quot;company&quot;.&quot;id&quot; &lt;= 20;


# 두번째 SQL
select * from product
where &quot;product&quot;.&quot;product_owned_company_id&quot; in (&#39;첫번째 SQL 실행 결과&#39;)
</code></pre><h2 id="10-서브쿼리-발생-조건--exclude-조건절의-함정">10) 서브쿼리 발생 조건 : .exclude() 조건절의 함정</h2>
<ul>
<li>역방향참조모델을 filter()절에 넣어서 JOIN을 유도함 (의도한SQL이 정상적으로 수행됨)</li>
<li><code>역방향 참조 모델</code>에서 <code>.exclude() 조건 절 사용시 서브쿼리 발생</code><ul>
<li>역방향 참조 모델을 exclude()절에 넣어서 JOIN을 유도. 서브쿼리 발생</li>
<li>~Q() 절로 바꿔서 filter()절에 넣어줘도 여전히 서브쿼리 발생</li>
<li>이런경우 JOIN으로 풀리게 유도하는 것이 불가능해서 차선책으로 prefetch_related(Prefetch()) 를 사용하는 방식으로 대체</li>
<li><code>정방향참조모델</code>은 exclude()절에 넣어서 JOIN을 유도하면 <code>의도한SQL수행</code></li>
</ul>
</li>
</ul>
<h2 id="11-values-values_list-사용시-주의점--eagerloading-옵션-무시">11) values() values_list() 사용시 주의점 : EagerLoading 옵션 무시</h2>
<ul>
<li><p>values(),values_list() 사용하면 해당 QuerySet에 주어진 select_related(), prefetch_related()옵션들을 전부 무시</p>
<ul>
<li>단 select_related (join) 하려면 values()안에 join해야지만 가져올 수 있는 데이터를 명시해야지 join 을 함.</li>
</ul>
</li>
</ul>
<h3 id="11-1-select_related-prefetch_related옵션들-전부-무시는-어찌보면-당연한-결과">11-1) select_related(), prefetch_related()옵션들 전부 무시는 어찌보면 당연한 결과</h3>
<ul>
<li><p>values(), values_list()를 사용하면 아래와 같은 DB Raw단위로 데이터를 반환한다</p>
<ul>
<li>Object(객체)와 Relational(관계지향) 간에 Mapping(매핑)이 일어나지 않는다.</li>
</ul>
</li>
<li><p>ORM EagerLoading이란 개념의 구현체인 select_related()&amp;prefetch_related()
는 DB Raw단위로 데이터를 조회하는 values() values_list() 에서는 무의미한 옵션</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[pysmp snmpv3]]></title>
            <link>https://velog.io/@engineer_km/pysmp-snmpv3</link>
            <guid>https://velog.io/@engineer_km/pysmp-snmpv3</guid>
            <pubDate>Fri, 21 Jan 2022 06:03:23 GMT</pubDate>
            <description><![CDATA[<p>pysnmp snmpv2 글에 이어서
이번 포스팅은 snmpv3 관련 예제를 간략히 소개하려고 합니다.</p>
<br>
<br>

<h2 id="pysnmp-github">pysnmp github</h2>
<ul>
<li><a href="https://github.com/etingof/pysnmp">https://github.com/etingof/pysnmp</a></li>
</ul>
<br>
<br>


<h2 id="get--set-예제">Get + Set 예제</h2>
<h3 id="get--set-예제-코드">Get + Set 예제 코드</h3>
<pre><code class="language-python">
from pysnmp.hlapi import *


HOST = &quot;20.20.20.20&quot;
PORT = 161
USER = &quot;LHZwj&quot;
AUTH_KEY = &quot;YEexkuDIkI304&quot;
AUTH_PROTO = usmHMACMD5AuthProtocol


engine = SnmpEngine()
host = UdpTransportTarget((HOST, PORT))
user_data = UsmUserData(USER, AUTH_KEY, authProtocol=AUTH_PROTO)


cmd_list = [
        (getCmd, ObjectType(ObjectIdentity(&#39;1.3.6.1.2.1.1.5.0&#39;))),
        (setCmd, ObjectType(ObjectIdentity(&#39;1.3.6.1.2.1.1.5.0&#39;), &quot;SET_HOST_NAME_1&quot;)),
        (getCmd, ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0))),
        (setCmd, ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0), &quot;SET_HOST_NAME_2&quot;)),
        (getCmd, ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0)))
]



for cmd_func, identity_obj in cmd_list:
    iterator = cmd_func(engine, user_data, host, ContextData(), identity_obj)
    errorIndication, errorStatus, errorIndex, varBinds = next(iterator)
    if errorIndication:  # SNMP engine errors
        print(errorIndication)
    else:
        if errorStatus:  # SNMP agent errors
            print(&#39;%s at %s&#39; % (errorStatus.prettyPrint(),
                  varBinds[int(errorIndex)-1] if errorIndex else &#39;?&#39;))
        else:
            for varBind in varBinds:  # SNMP response contents
                print(&quot;[{}]  &quot;.format(cmd_func.__name__)  +\
                      &#39; = &#39;.join([x.prettyPrint() for x in varBind]))</code></pre>
<h3 id="출력">출력</h3>
<pre><code>[getCmd]  SNMPv2-MIB::sysName.0 = V8102_02
[setCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_1
[getCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_1
[setCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_2
[getCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_2</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[pysnmp snmpv2]]></title>
            <link>https://velog.io/@engineer_km/pysnmp-snmpv2</link>
            <guid>https://velog.io/@engineer_km/pysnmp-snmpv2</guid>
            <pubDate>Fri, 21 Jan 2022 05:17:52 GMT</pubDate>
            <description><![CDATA[<p>Python 으로 snmp 로 접속하여 제어 하고 싶다면
Python 라이브러리 중 pysnmp 사용 방법을 간략하게 소개해보려고 한다.</p>
<h2 id="pysnmp-github">pysnmp github</h2>
<ul>
<li><a href="https://github.com/etingof/pysnmp">https://github.com/etingof/pysnmp</a></li>
</ul>
<br>
<br>

<h2 id="get-예제">Get 예제</h2>
<h3 id="get-예제-코드">Get 예제 코드</h3>
<ul>
<li>&#39;1.3.6.1.2.1.1.5.0&#39;<ul>
<li>장비의 HOST명을 가져오라는 SNMP OID</li>
</ul>
</li>
<li>(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0)<ul>
<li>장비의 HOST명을 가져오라는 MID</li>
</ul>
</li>
</ul>
<pre><code class="language-python">
from pysnmp.hlapi import *


HOST = &quot;192.168.1.1&quot;   # 접속할 장비의 IP 입력할 것
PORT = 161
COMMUNITY = &quot;aqXXlaKqjS&quot;  # 접속할 장비의 community 정보 입력할 것


engine = SnmpEngine()
host = UdpTransportTarget((HOST, PORT))
community = CommunityData(COMMUNITY, mpModel=1)
identity_obj_list = [
        ObjectType(ObjectIdentity(&#39;1.3.6.1.2.1.1.5.0&#39;)),
        ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0))
]


for identity_obj in identity_obj_list:
    iterator = getCmd(engine, community, host, ContextData(), identity_obj)
    errorIndication, errorStatus, errorIndex, varBinds = next(iterator)
    if errorIndication:  # SNMP engine errors
        print(errorIndication)
    else:
        if errorStatus:  # SNMP agent errors
            print(&#39;%s at %s&#39; % (errorStatus.prettyPrint(),
                  varBinds[int(errorIndex)-1] if errorIndex else &#39;?&#39;))
        else:
            for varBind in varBinds:  # SNMP response contents
                print(&#39; = &#39;.join([x.prettyPrint() for x in varBind]))


</code></pre>
<h3 id="출력">출력</h3>
<pre><code>SNMPv2-MIB::sysName.0 = kTLgIzt
SNMPv2-MIB::sysName.0 = kTLgIzt</code></pre><br>
<br>

<h2 id="set-예제">Set 예제</h2>
<h3 id="set-예제-코드">Set 예제 코드</h3>
<pre><code class="language-python">
from pysnmp.hlapi import *


HOST = &quot;20.20.20.20&quot;
PORT = 161
COMMUNITY = &quot;aqXXlaKqjS&quot;


engine = SnmpEngine()
host = UdpTransportTarget((HOST, PORT))
community = CommunityData(COMMUNITY, mpModel=1)
identity_obj_list = [
        ObjectType(ObjectIdentity(&#39;1.3.6.1.2.1.1.5.0&#39;), &quot;SET_HOST_NAME_1&quot;),
        ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0), &quot;SET_HOST_NAME_2&quot;)
]



for identity_obj in identity_obj_list:
    iterator = setCmd(engine, community, host, ContextData(), identity_obj)
    errorIndication, errorStatus, errorIndex, varBinds = next(iterator)
    if errorIndication:  # SNMP engine errors
        print(errorIndication)
    else:
        if errorStatus:  # SNMP agent errors
            print(&#39;%s at %s&#39; % (errorStatus.prettyPrint(),
                  varBinds[int(errorIndex)-1] if errorIndex else &#39;?&#39;))
        else:
            for varBind in varBinds:  # SNMP response contents
                print(&#39; = &#39;.join([x.prettyPrint() for x in varBind]))

</code></pre>
<h3 id="출력-1">출력</h3>
<pre><code>SNMPv2-MIB::sysName.0 = SET_HOST_NAME_1
SNMPv2-MIB::sysName.0 = SET_HOST_NAME_2
</code></pre><h3 id="host-로-접속하여-확인">HOST 로 접속하여 확인</h3>
<ul>
<li>HOST Name 이 변경된 것을 확인</li>
<li>반드시 HOST에는 SNMP 서비스가 실행되어 있어야지 접속가능함<ul>
<li>HOST 의 SNMP 정보가 일치해야됨.<pre><code>Escape character is &#39;^]&#39;.
</code></pre></li>
</ul>
</li>
</ul>
<p>SET_HOST_NAME_2 login:</p>
<pre><code>
&lt;br&gt;
&lt;br&gt;


## Get + Set 예제

### Get + Set 예제 코드

```python

from pysnmp.hlapi import *


HOST = &quot;20.20.20.20&quot;
PORT = 161
COMMUNITY = &quot;aqXXlaKqjS&quot;


engine = SnmpEngine()
host = UdpTransportTarget((HOST, PORT))
community = CommunityData(COMMUNITY, mpModel=1)
cmd_list = [
        (getCmd, ObjectType(ObjectIdentity(&#39;1.3.6.1.2.1.1.5.0&#39;))),
        (setCmd, ObjectType(ObjectIdentity(&#39;1.3.6.1.2.1.1.5.0&#39;), &quot;SET_HOST_NAME_1&quot;)),
        (getCmd, ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0))),
        (setCmd, ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0), &quot;SET_HOST_NAME_2&quot;)),
        (getCmd, ObjectType(ObjectIdentity(&#39;SNMPv2-MIB&#39;, &#39;sysName&#39;, 0)))
]



for cmd_func, identity_obj in cmd_list:
    iterator = cmd_func(engine, community, host, ContextData(), identity_obj)
    errorIndication, errorStatus, errorIndex, varBinds = next(iterator)
    if errorIndication:  # SNMP engine errors
        print(errorIndication)
    else:
        if errorStatus:  # SNMP agent errors
            print(&#39;%s at %s&#39; % (errorStatus.prettyPrint(),
                  varBinds[int(errorIndex)-1] if errorIndex else &#39;?&#39;))
        else:
            for varBind in varBinds:  # SNMP response contents
                print(&quot;[{}]  &quot;.format(cmd_func.__name__)  +\
                      &#39; = &#39;.join([x.prettyPrint() for x in varBind]))


</code></pre><h3 id="출력-2">출력</h3>
<pre><code>[getCmd]  SNMPv2-MIB::sysName.0 = kTLgIzt
[setCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_1
[getCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_1
[setCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_2
[getCmd]  SNMPv2-MIB::sysName.0 = SET_HOST_NAME_2
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[django migrate 초기화 -2 (최후의 방법)]]></title>
            <link>https://velog.io/@engineer_km/django-migrate-%EC%B4%88%EA%B8%B0%ED%99%94</link>
            <guid>https://velog.io/@engineer_km/django-migrate-%EC%B4%88%EA%B8%B0%ED%99%94</guid>
            <pubDate>Mon, 17 Jan 2022 12:38:12 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolorred-해당-방법을-사용하면-데이터-날라갑니다-migrate-문제-발생시-최후의-방법입니다-span"><span style="color:red"> 해당 방법을 사용하면 데이터 날라갑니다. migrate 문제 발생시 최후의 방법입니다. </span></h2>
<br>
<br>

<h2 id="django-migrate-초기화-최후의-방법">django migrate 초기화 (최후의 방법)</h2>
<blockquote>
<p>해당 방법은 migrate 초기화를 해도 안될때 사용하는 방법입니다.
  해당 방법은 DB의 데이터를 날리기 때문에 💣
  데이터를 날리면 안 된다면 아래의 방법을 사용하지마세요.</p>
</blockquote>
<h3 id="1-python-managepy-showmigrations-앱-이름">1) python manage.py showmigrations &lt;앱 이름&gt;</h3>
<ul>
<li>현재 migrations 내역을 확인합니다.</li>
<li>0001_initial, 0002_post_author 에 [x] 가 되어있다. (migrate 적용된 상태)<pre><code>(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py showmigrations blog1
blog1
[X] 0001_initial
[X] 0002_post_author
(venv) root@km-VirtualBox:~/django-with-react-study#</code></pre></li>
</ul>
<h3 id="2-migrate-초기화-or-특정시점">2) migrate 초기화 or 특정시점</h3>
<h4 id="2-1-migrate-초기화">2-1) migrate 초기화</h4>
<ul>
<li>zero 는 migrations 처음 상태로 돌아감.<ul>
<li><code>__init__.py</code> 까지 돌아감.</li>
<li><code>__init__.py</code> 은 남아있어야됨. </li>
<li>0001_initial, 0002_post_author 에 [ ] 로 되어있어야된다.<blockquote>
<p>python manage.py migrate --fake &lt;앱 이름&gt; zero</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<pre><code>
(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py migrate --fake blog1 zero
Operations to perform:
  Unapply all migrations: blog1
Running migrations:
  Rendering model states... DONE
  Unapplying blog1.0002_post_author... FAKED
  Unapplying blog1.0001_initial... FAKED
(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py showmigrations blog1
blog1
 [ ] 0001_initial
 [ ] 0002_post_author
(venv) root@km-VirtualBox:~/django-with-react-study#
</code></pre><h4 id="2-2-migrate-특정-시점">2-2) migrate 특정 시점</h4>
<ul>
<li>Ex) python manage.py migrate --fake blog1 002</li>
</ul>
<blockquote>
<p>python manage.py migrate --fake &lt;앱 이름&gt; &lt;마이그레이션 지점&gt;</p>
</blockquote>
<h4 id="3-1--초기화-경우__init__py만-남기고-나머지-파일들을-다-지우기">3-1)  초기화 경우:<code>__init__.py</code>만 남기고 나머지 파일들을 다 지우기</h4>
<ul>
<li>migrations 폴더에 <code>__init__.py</code> 를 제외하고 다 삭제</li>
</ul>
<pre><code>rm blog1/migrations/0001_initial.py

rm blog1/migrations/0002_post_author.py</code></pre><h4 id="3-2--migrate-특정-시점">3-2)  migrate 특정 시점</h4>
<ul>
<li>돌아간 시점의 이후 migrate 파일 삭제
Ex) 002 로 돌아갔다면<pre><code>003 파일 삭제, 
004 파일 삭제,
005 파일 삭제 ....</code></pre></li>
</ul>
<h3 id="4-db도-migration-파일과-sync를-맟춘다">4) DB도 migration 파일과 Sync를 맟춘다.</h3>
<ul>
<li>Ex) sqlite3 DB 를 사용
blog_post 테이블을 삭제한다. (migration 초기화 시켰던 app이 blog_post 였기 때문에.
그리고 나는 초기(zero)로 돌렸으니 테이블을 삭제해야한다.)<ul>
<li>단 테이블을 날렸으니
테이블에 등록한 데이터도 날라간다.</li>
</ul>
</li>
</ul>
<pre><code>
(venv) root@km-VirtualBox:~/django-with-react-study# sqlite3  db.sqlite3
SQLite version 3.31.1 2020-01-27 19:55:54
Enter &quot;.help&quot; for usage hints.
sqlite&gt; DROP TABLE blog1_post;
sqlite&gt;
</code></pre><h3 id="5-python-managepy-makemigrations-앱-이름">5) python manage.py makemigrations &lt;앱 이름&gt;</h3>
<pre><code>
(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py makemigrations blog1
Migrations for &#39;blog1&#39;:
  blog1/migrations/0001_initial.py
    - Create model Post</code></pre><h3 id="6-python-managepy-migrate-앱-이름">6) python manage.py migrate &lt;앱 이름&gt;</h3>
<pre><code>(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py migrate blog1
Operations to perform:
  Apply all migrations: blog1
Running migrations:
  Applying blog1.0001_initial... OK
(venv) </code></pre><h3 id="6-python-managepy-showmigrations-앱-이름">6) python manage.py showmigrations &lt;앱 이름&gt;</h3>
<ul>
<li>migrate 가 적용되었는지 확인한다.<ul>
<li><code>[X] 0001_initial</code><pre><code>(venv) root@km-VirtualBox:~/django-with-react-study# python manage.py showmigrations blog1
blog1
[X] 0001_initial
(venv) root@km-VirtualBox:~/django-with-react-study#
</code></pre></li>
</ul>
</li>
</ul>
<pre><code>
### 7) DB 확인
  - .tables 조회시 blog1_post 가 존재
  - SELECT * FROM blog1_post; 시 아무것도 안나오는데
    이건 테이블을 날렸다가 다시 생성한거라서
    기존에 데이터는 테이블 날리면서 사라짐.</code></pre><p>(venv) root@km-VirtualBox:~/django-with-react-study# sqlite3  db.sqlite3
SQLite version 3.31.1 2020-01-27 19:55:54
Enter &quot;.help&quot; for usage hints.
sqlite&gt; .tables
accounts_profile            django_admin_log
auth_group                  django_content_type
auth_group_permissions      django_migrations
auth_permission             django_session
auth_user                   instagram_comment
auth_user_groups            instagram_post
auth_user_user_permissions  instagram_post_tag_set
blog1_post                  instagram_tag
sqlite&gt; 
sqlite&gt; SELECT * FROM blog1_post;
sqlite&gt;</p>
<pre><code>
&lt;br&gt;
&lt;br&gt;

### 참고
   - [django migrate 초기화](https://velog.io/@shrhkddh/Django-migrate-%EC%B4%88%EA%B8%B0%ED%99%94-%ED%95%B4%EB%8F%84-%EC%95%88%EB%90%A0%EB%95%8C)
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[나의 첫 오픈소스 contribution]]></title>
            <link>https://velog.io/@engineer_km/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-scapy-contribution</link>
            <guid>https://velog.io/@engineer_km/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-scapy-contribution</guid>
            <pubDate>Sat, 15 Jan 2022 16:40:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>나의 첫 opensource 기여했던 경험을 적어본다.
  아래의 링크는 내가 올렸던 Pull Request 다.</p>
</blockquote>
<ul>
<li><a href="https://github.com/secdev/scapy/pull/2757">https://github.com/secdev/scapy/pull/2757</a></li>
</ul>
<br>
<br>

<h2 id="scapy-">scapy ?</h2>
<p>나의 첫 기여했던 프로젝트는 scapy 이다.</p>
<ul>
<li>scapy: python으로 작성한 packet control 라이브러리 입니다.</li>
</ul>
<p><img src="https://images.velog.io/images/engineer_km/post/dc233b6b-527e-4990-a819-692b59a9fc47/image.png" alt=""></p>
<ul>
<li><a href="https://scapy.net/">https://scapy.net/</a></li>
<li><a href="https://scapy.readthedocs.io/en/latest/">https://scapy.readthedocs.io/en/latest/</a></li>
<li><a href="https://github.com/secdev/scapy">https://github.com/secdev/scapy</a></li>
</ul>
<br>
<br>

<h2 id="scapy-contribution을-하게-된-계기">scapy contribution을 하게 된 계기</h2>
<p>PIMv2 Hello, Join/Prune 패킷이 scapy 에서 지원을 하지 않아서
만들어야 되겠다고 생각했다.
내가 필요했다. <del>(급한 사람이 만든다.)</del></p>
<br>
<br>

<h2 id="scapy-contribution-하기">scapy contribution 하기</h2>
<h3 id="1-개발-대상이-되는-pimv2-hello-joinprune-packet-구조가-어떻게-되는지-파악-🔍">1) 개발 대상이 되는 <code>PIMv2 Hello</code>, <code>Join/Prune</code> packet 구조가 어떻게 되는지 파악 🔍</h3>
<ul>
<li>지피지기는 백전백승
개발 대상에 대해서 잘아야된다.</li>
<li>RFC 문서 참고하기<ul>
<li><a href="https://tools.ietf.org/html/rfc4601">https://tools.ietf.org/html/rfc4601</a></li>
</ul>
</li>
<li>pim parameter 참고하기<ul>
<li><a href="https://www.iana.org/assignments/pim-parameters/pim-parameters.xhtml#pim-parameters-1">https://www.iana.org/assignments/pim-parameters/pim-parameters.xhtml#pim-parameters-1</a></li>
</ul>
</li>
<li>.pcap 으로 확인하기<ul>
<li><a href="https://packetlife.net/captures/protocol/pim/">https://packetlife.net/captures/protocol/pim/</a></li>
</ul>
</li>
</ul>
<br>

<h3 id="2-contribute-방법을-파악한다-🔍">2) Contribute 방법을 파악한다. 🔍</h3>
<ul>
<li><a href="https://github.com/secdev/scapy/blob/master/CONTRIBUTING.md">https://github.com/secdev/scapy/blob/master/CONTRIBUTING.md</a></li>
</ul>
<br>


<h3 id="3-scapy-프로젝트를-fork-한다">3) scapy 프로젝트를 fork 한다.</h3>
<ul>
<li>나의 repo 에 scapy 프로젝트가 copy 될 것이다.</li>
</ul>
<br>

<h3 id="4-prototcol-추가-하는-방법을-파악한다-🔍">4) Prototcol 추가 하는 방법을 파악한다. 🔍</h3>
<ul>
<li><a href="https://scapy.readthedocs.io/en/latest/build_dissect.html">https://scapy.readthedocs.io/en/latest/build_dissect.html</a></li>
</ul>
<br>

<h3 id="5-개발-한다-💻">5) 개발 한다. 💻</h3>
<ul>
<li>나의 repo 에 copy 된 scapy 프로젝트로 commit 을 올리면 된다.</li>
<li>추후 작업이 완료되면 실제 scapy 프로젝트로 pull request 를 올려야된다.</li>
<li>이건 나의 팁인데 scapy master에 들어간 코드를 참고를 했었다. 개인적으로 도움이 많이 되었다.<ul>
<li><a href="https://github.com/secdev/scapy/blob/44d3e8646fd09f1ba82c1c886b05d8ce249c364a/scapy/contrib/ospf.py">ospf layer</a></li>
<li><a href="https://github.com/secdev/scapy/blob/44d3e8646fd09f1ba82c1c886b05d8ce249c364a/scapy/contrib/bgp.py">bgp layer</a></li>
</ul>
</li>
</ul>
<br>

<h3 id="6-test-를-작성한다-✔">6) test 를 작성한다. ✔</h3>
<ul>
<li>scapy 에서 요구했던 <code>.uts</code> 를 스타일로<br>테스트 케이스를 추가하였고 잘 돌때까지 <code>개발&lt;-&gt;테스트</code> 를 반복했다.</li>
</ul>
<br>

<h3 id="7-pull-request-올리기-😖">7) Pull Request 올리기 😖</h3>
<p>나의 첫 Opensource 기여라서 떨렸다.</p>
<ul>
<li><a href="https://github.com/secdev/scapy/pull/2757">https://github.com/secdev/scapy/pull/2757</a></li>
</ul>
<h3 id="8-feedback-받고-수정하기-🔧">8) Feedback 받고 수정하기 🔧</h3>
<p>minor 한 부분에 대해서 feedback 을 받아서
그 부분을 수정하였다.</p>
<p><img src="https://images.velog.io/images/engineer_km/post/5d9b0f0e-1558-40f8-b496-d5d47956fb8f/image.png" alt=""></p>
<h3 id="9-master에-적용되다-😆">9) master에 적용되다. 😆</h3>
<ul>
<li>나의 코드는 Release v2.4.5 버전부터 적용이 되었다.</li>
</ul>
<p><img src="https://images.velog.io/images/engineer_km/post/b8390fbf-f133-41ab-8b84-ed58ae5966ce/image.png" alt=""></p>
<br>
<br>


<h2 id="회고-☕">회고 ☕</h2>
<h3 id="1-나는-사실-contribution을-하기가-망설여었다">1) 나는 사실 contribution을 하기가 망설여었다.</h3>
<ul>
<li>오픈소스 기여하는건 처음인데 프로젝트 규모가 커서 또한 겁을 먹어었다.</li>
<li>근데 생각해보니
나도 여러 오픈소스 프로젝트를 통해서 도움을 받았으니
누군가에게 도움을 주었으면 좋겠다고 생각이 바뀌게 되었다.<ul>
<li>나처럼 PIMv2 Hello, Join/Prune 기능을 원하시는 분들이 있으니 용기를 내어 보았다.<ul>
<li><a href="https://github.com/secdev/scapy/issues/2003">https://github.com/secdev/scapy/issues/2003</a></li>
<li><a href="https://github.com/secdev/scapy/issues/2621">https://github.com/secdev/scapy/issues/2621</a></li>
<li><a href="https://stackoverflow.com/questions/59801410/python-socket-programming-crafting-pim-join-prune-packet">https://stackoverflow.com/questions/59801410/python-socket-programming-crafting-pim-join-prune-packet</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<br>


<h3 id="2-해당-프로젝트의-코드를-참고할-것">2) 해당 프로젝트의 코드를 참고할 것</h3>
<ul>
<li>이미 master에 올라간 코드는 approve 를 받은 코드이다.<ul>
<li>올라간 코드를 참고하여 개발을 했는데 
Pull Request를 별 무리 없이 통과하는데 도움이 되었다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>내가 올린 기능이 누군가에게 도움이 되었으면 좋겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 장비 제어를 위한 추상화 API - 2: 회고]]></title>
            <link>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B6%94%EC%83%81%ED%99%94-API-2-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B6%94%EC%83%81%ED%99%94-API-2-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 15 Jan 2022 15:23:09 GMT</pubDate>
            <description><![CDATA[<h2 id="2차-방법-폭발💣진행👌">2차 방법 폭발💣?/진행👌?</h2>
<p>2019년 2월 1일
만들었던 내용을 팀원분들에게 공유를 하였다.
결과는 2차 방법을 진행하기로 결정하게 되었다.😄
<br></p>
<p>미션을 받게 되었다. 📩
전파 교육을 하고
2019년 2월 28일 까지 📅
4명이서 1년 동안 만들었던 Feature API 구현해라</p>
<br>


<p>2019년 2월 28일 까지 
4명이서 힘을 합쳐서 💪
기존에 1년동안 만들었던 Feature API 를 다 만들 수 있었다. 🤜🤛</p>
<br>
<br>

<h2 id="2차-방법-회고-☕">2차 방법: 회고 ☕</h2>
<p>1) 1차 방법을 실패를 딛고 실패원인을 가지고 만들어었다.</p>
<ul>
<li>다시 한번 사전조사의 중요성</li>
<li>실패를 했다면 원인 반드시 분석하기<ul>
<li>실패는 성공의 어머니 (물론 처음부터 잘하면 좋긴하지만...😢)</li>
</ul>
</li>
</ul>
<p>2) 나름 <code>첫 SEMI 페어프로그래밍?</code>을 경험했었다. </p>
<ul>
<li>약 1달 동안 내 옆자리로
동료 분이 잠시 이사를 왔는데
이때 모니터를 가운데 두고
서로 막히는 부분이 있다면 빠르게 공유하고
어떻게 풀어갈지 고민하면서 개발했던 기억이 있다.<ul>
<li>옆자리여서 커뮤니케이션이 빠르게 진행했었다.</li>
<li>가운데 둔 모니터는 서로가 문제가 생겼을때 
한 화면을 보면서 어떻게 풀어갈지 논의할때 효율적이였다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 장비 제어를 위한 추상화 API - 2: 명령 입력 전/후 모드 이동]]></title>
            <link>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B6%94%EC%83%81%ED%99%94-API-%EB%AA%85%EB%A0%B9-%EC%9E%85%EB%A0%A5-%EC%A0%84%ED%9B%84-%EB%AA%A8%EB%93%9C-%EC%9D%B4%EB%8F%99</link>
            <guid>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B6%94%EC%83%81%ED%99%94-API-%EB%AA%85%EB%A0%B9-%EC%9E%85%EB%A0%A5-%EC%A0%84%ED%9B%84-%EB%AA%A8%EB%93%9C-%EC%9D%B4%EB%8F%99</guid>
            <pubDate>Sat, 15 Jan 2022 14:37:12 GMT</pubDate>
            <description><![CDATA[<h2 id="previously">Previously</h2>
<h3 id="1차-방법의-명령-입력-전후-모드-이동-문제">1차 방법의 명령 입력 전/후 모드 이동 문제</h3>
<ul>
<li>네트워크 장비 설정 예시 : port disable 과정<pre><code># config terminal
(config)# interface ge1/1
(ge1/1)# shutdown
(ge1/1)# exit
(config)# exit
#</code></pre></li>
</ul>
<h4 id="port-ge11-를-disable-했다가-enable-할-경우">port ge1/1 를 disable 했다가 Enable 할 경우</h4>
<ul>
<li>1차 방법의 문제점: 비효율적인 모드 이동</li>
</ul>
<pre><code># config terminal
(config)# interface ge1/1
(ge1/1)# shutdown
(ge1/1)# exit
(config)# exit
#
# config terminal
(config)# interface ge1/1
(ge1/1)# no shutdown
(ge1/1)# exit
(config)# exit
#</code></pre><ul>
<li>원했던 sequence: 효율적인 모드 이동</li>
</ul>
<pre><code># config terminal
(config)# interface ge1/1
(ge1/1)# shutdown
(ge1/1)# no shutdown
(ge1/1)# exit
(config)# exit
#</code></pre><p><strong>위와 같은 상황 처럼 
비효율적으로 모드를 이동하여 소요시간이 오래 걸리게됨.</strong></p>
<br>

<blockquote>
<p>1차 방법 문제점 중에 명령 입력 전/후 모드 이동 문제가 있었다.
효율적으로 모드 이동을 하도록 접근이 필요했다.</p>
</blockquote>
<br>
<br>

<h2 id="2차-방법-접근-방법-❗">2차 방법: 접근 방법 ❗</h2>
<blockquote>
<p>규칙을 만들었다.
기준이 되는 모드가 있다.
기준이 되는 모드에서 
명령을 입력하여
모드를 이동하고 
다시 기준이되는 모드로 돌아와야된다.</p>
</blockquote>
<table>
<thead>
<tr>
<th>순서</th>
<th align="center">1</th>
<th align="right">2</th>
<th align="right">3</th>
<th align="right">4</th>
<th align="right">5</th>
</tr>
</thead>
<tbody><tr>
<td>명령</td>
<td align="center"></td>
<td align="right">config terminal</td>
<td align="right">interface ge1/1</td>
<td align="right">exit</td>
<td align="right">end</td>
</tr>
<tr>
<td>모드 위치</td>
<td align="center">기준 모드 (enable)</td>
<td align="right">config</td>
<td align="right">iface ge1/1</td>
<td align="right">config</td>
<td align="right">기준모드 (enable)</td>
</tr>
</tbody></table>
<h3 id="pseudocode">pseudocode</h3>
<ul>
<li>Mode Class 는 <code>모드에 대한 정의</code>와 <code>모드로 이동하는 명령어</code>, <code>기준모드로 돌아가는 명령들</code>이 정의되어있다.</li>
<li>session 이랑 의존이 되어있지 않기 때문에 Mode.모드명1() 하더라도 장비로 입력되지 않는다.</li>
</ul>
<pre><code class="language-python">
Class ModeBase(object):
   def 모드명1(self):
       return (
           모드를_이동하기_위한_명령어들,
           모드명들,
           기준모드로_돌아가는_명령어들
       )

   def 모드명2(self):
       return (
           모드를_이동하기_위한_명령어들,
           모드명들,
           기준모드로_돌아가는_명령어들
       )

Class ModeClass1(ModeBase):
   def 모드명3(self):
       return (
           모드를_이동하기_위한_명령어들,
           모드명들,
           기준모드로_돌아가는_명령어들
       )


 모드_별칭_이름_1 = ModeClass1()


Class 기능1SubFeatureClass1(FeatureCommon):
    def __init__(self):
        self._모드 = 모드_별칭_이름_1

    def 명령1(self, 필요한_데이터):
        메세지.CLI = 메세지_CLI(self._모드.모드1, 필요한_데이터)
        return 메세지

    def 명령2(self, 필요한_데이터):
        메세지.CLI = 메세지_CLI(self._모드.모드2, 필요한_데이터)
        return 메세지

    def 명령3(self, 필요한_데이터):
        메세지.CLI = 메세지_CLI(self._모드.모드3, 필요한_데이터)
        return 메세지</code></pre>
<h3 id="내가-원했던거-이거-👉">내가 원했던거 이거 👉</h3>
<ul>
<li>1차 방법보다 효율적으로 모드 이동을 하고 실행속도 또한 빨라졌다. 
(기준 모드에서 모드가 더 안으로 들어갈수록 속도는 더욱 줄었다.)</li>
</ul>
<pre><code># config terminal
(config)# interface ge1/1
(ge1/1)# shutdown
(ge1/1)# no shutdown
(ge1/1)# exit
(config)# end
#


# config terminal
(config)# 모드1_이동_명령어
(config-모드1)# 모드2_이동_명령어
(config-모드1-모드2)# 명령1
(config-모드1-모드2)# 명령2
(config-모드1-모드2)# exit
(config-모드1)# 명령3
(config)# 명령4
(config)# end
#</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 장비 제어를 위한 추상화 API - 2]]></title>
            <link>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B6%94%EC%83%81%ED%99%94-API-2</link>
            <guid>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B6%94%EC%83%81%ED%99%94-API-2</guid>
            <pubDate>Sat, 15 Jan 2022 13:25:48 GMT</pubDate>
            <description><![CDATA[<p>2019년 1월 9일 
4명이서 1년 넘게 작업한 프로젝트를 폭발시켰다. 💣
<br></p>
<p>그와 동일한 2019년 1월 9일 
새로운 프로젝트를 만들었다. 🏁
<br></p>
<p>미션을 받게 되었다. 📩
동료 한분과(총 2명) 2월 1일까지 📅
Core 로직 구현과 
20여개 정도의 Feature API 구현
Prototype 의 절차를 만들어라.
<br></p>
<p>그리고 이렇게 Prototype 으로 만들어진 것을 보고
2차 방법으로 갈지/말지에 대해서 선택하는 시간을 갖기로 하였다.</p>
<br>
<br>

<h2 id="2차-방법-고려-사항🤔">2차 방법: 고려 사항🤔</h2>
<ul>
<li>1차 실패를 경험하고 고려사항이 늘어나게 되었다.</li>
</ul>
<p>1) 코드 중복을 줄이고 싶다.</p>
<blockquote>
<p>1) 장비별로 코드를 복사해서 CLI 가 다른부분만 바꿔준다. (비효율적이다.)
  1차때 경험하게 되었다. 장비의 CLI 가 다양하기에 확장성을 고려해야된다.</p>
</blockquote>
<p>2) 경험이 부족한 사람은 설정을 헤맨다. learning curve 를 줄여 주고 싶다.</p>
<blockquote>
<p>2) CLI가 다르면 경험이 부족한 사람의 경우 설정 방법을 해맬 수 있다.</p>
</blockquote>
<p>3) 명령 입력 전/후 모드 이동 문제</p>
<blockquote>
<p>3) 1차 방법에서 문제였던 효율적인 모드 이동 필요</p>
</blockquote>
<p>4) 장비의 Command 기능과 Session (telnet) 와의 의존성 문제</p>
<blockquote>
<p>4) 1차방법에서는 기능(Feature) API 를 호출했을때 
telnet session을 통해서 장비로 입력이 바로 전송되어었다.
기능(Feature) API 와 Session (telnet) 의존성을 줄인다.</p>
</blockquote>
<p>5) SSH, Serial(direct) 사용이 필요해짐 (지원하는 Session 종류를 늘려야됨) </p>
<blockquote>
<p>5) session 을 telnet, ssh, serial 로 만들어서 장비를 제어할 수 있어야됨.</p>
</blockquote>
<p>6) 윈도우 환경에서도 API 사용이 필요해짐</p>
<blockquote>
<p>6) 1차 방법때는 pexpect 를 이용해서 telnet을 접속했는데
pexpect를 이용해서 윈도우PC에서 장비로 telnet 접속이 안되어었다.</p>
</blockquote>
<br>
<br>

<h2 id="2차-방법-추상화-접근-방법-❗">2차 방법: 추상화 접근 방법 ❗</h2>
<p>아래와 같은 접근 방법으로 추상화를 디자인을 했었다.</p>
<ul>
<li><a href="https://refactoring.guru/design-patterns/builder">Builder Pattern</a></li>
</ul>
<ul>
<li><a href="https://refactoring.guru/design-patterns/abstract-factory">Abstract Factory Pettern</a></li>
</ul>
<br>

<h3 id="장비-명세서-📄">장비 명세서 📄</h3>
<p><code>장비A</code>에 대해서 명세를 해준다.
<code>장비A</code>는 어떤 기능을 가지고 있고 ...
마치 장비에 대한 명세서라는 것을 작성하여 
<code>작성한 명세서를 통해서 장비가 인스턴스화</code> 될 수 있도록 하게 하였다. </p>
<h5 id="명세서-예시">명세서 예시)</h5>
<pre><code>어떤_SESSION_COLLECTION: SESSION_COLLECTION_1
분류_기능: MainFeatureClass
  기능1: 기능1SubFeatureClass1
  기능2: 기능2SubFeatureClass2
...</code></pre><h3 id="feature-api-사용-방법-예시">Feature API 사용 방법 예시</h3>
<ol>
<li><p>분류를 한단계 더 추가하였다.
1차 방법처럼 접근한다면 분류가 1개만 있으니깐 
Class가 양이 너무 커져서 기능에 대해서 찾기가 어려워졌기 때문에 
분류를 한단계 더 추가하게 되었다.</p>
</li>
<li><p>Feature API 를 호출하면 메세지를 반환해준다.
실제 장비에 들어가서 명령을 하지는 않는다.</p>
<pre><code>메세지 = 장비.Main분류기능.Sub분류기능.함수명(필요한 데이터)</code></pre></li>
</ol>
<h3 id="윈도우-환경에서도-api-사용--ssh-serialdirect-지원-필요">윈도우 환경에서도 API 사용 &amp; SSH, Serial(direct) 지원 필요</h3>
<ul>
<li>pexpect를 이용해서 윈도우PC에서 장비로 telnet 접속이 안되어었다.
telnet 세션 관련 라이브러리를 새로운걸로 변경이 필요했다.</li>
<li>SSH, Serial(dicect) 에 대해서 지원이 필요했다.</li>
<li>어떤 라이브러리를 사용할지 조사를 통해서 결정하였고
telnetlib, paramiko, pyserial 를 사용하기로 했다.</li>
</ul>
<h3 id="connectortelnetsshserial-디자인-방법">Connector(telnet/ssh/serial) 디자인 방법</h3>
<ul>
<li><p><a href="https://refactoring.guru/design-patterns/builder">Builder Pattern</a></p>
</li>
<li><p><a href="https://refactoring.guru/design-patterns/factory-method">Factory Pettern</a></p>
</li>
<li><p><a href="https://refactoring.guru/design-patterns/abstract-factory">Abstract Factory Pettern</a></p>
<br>

<ul>
<li>장비 명세서에 <code>어떤_SESSION_COLLECTION</code>  를 어떤걸 사용하겠다는 것을 명세 해준다.</li>
<li>명세된 정보를 통해서 SESSION_COLLECTION_1 을 사용하게 될 것 이다.</li>
<li>Session (Telnet, SSH, Serial) 은 동일한 행위를 하는 API 로 만든다.</li>
<li>필요한 시점에 원하는 정보로에 세션을 만들어 준다.</li>
<li>인스턴스화한 장비에 telnet 으로 접속하고 싶다면, telnet 에 대한 정보를 입력해서 세션을 만든다.</li>
<li>인스턴스화한 장비에 ssh 로 접속하고 싶다면, ssh 에 대한 정보를 입력해서 세션을 만든다.</li>
<li>인스턴스화한 장비에 serial 로 접속하고 싶다면, serial 에 대한 정보를 입력해서 세션을 만든다.</li>
</ul>
</li>
</ul>
<h4 id="명세서-예시-1">명세서 예시)</h4>
<pre><code>어떤_SESSION_COLLECTION: SESSION_COLLECTION_1</code></pre><h4 id="pseudocode">pseudocode</h4>
<pre><code class="language-python">
Class Seesion(object):
    def 세션_만들기(self, 필요한_데이터_정보들):
        세션 = self._자식클래스의_세션만들기(**필요한_데이터_정보들)
        return 세션

    def 입력(self, 명령):
        self._자식클래스의_입력(명령)
        읽어온_정보 = self._자식클래스의_읽기 
        return 읽어온_정보

    def 로그인하기(self, 필요한_데이터_정보들):
        로그인_정보 = self._자식클래스의_로그인하기(**필요한_데이터_정보들)
        return 로그인_정보


Class Telnet(Seesion):
    def _세션만들기(self):
         return telnetlib을 이용한 세션만들기

    def _입력(self):
        telnetlib을 이용한 입력

    def _읽기(self):
        return telnetlib을 이용한 읽기


Class SSH(Seesion):
    def _세션만들기(self):
         return paramiko을 이용한 세션만들기

    def _입력(self):
        paramiko을 이용한 입력

    def _읽기(self):
        return paramiko을 이용한 읽기


 Class Serial(Seesion):
    def _세션만들기(self):
         return pyserial을 이용한 세션만들기

    def _입력(self):
        pyserial을 이용한 입력

    def _읽기(self):
        return pyserial을 이용한 읽기


 Class TelnetClass1(Telnet):
     def _로그인하기(self, 필요한_데이터_정보들):
          # 특정 Vendor 마다 로그인 방법이 달랐다.
         return telnetlib을 이용해서 로그인하게 하기


 Class SSHClass1(SSH):
     def _로그인하기(self, 필요한_데이터_정보들):
          # 특정 Vendor 마다 로그인 방법이 달랐다.
         return paramiko을 이용해서 로그인하게 하기


 Class SerialClass1(Serial):
     def _로그인하기(self, 필요한_데이터_정보들):
          # 특정 Vendor 마다 로그인 방법이 달랐다.
         return pyserial을 이용해서 로그인하게 하기


SESSION_COLLECTION_MAP = {

    &#39;SESSION_COLLECTION_1&#39;: {
        &#39;telnet&#39;: TelnetClass1,
        &#39;ssh&#39;: SSHClass2,
        &#39;serial&#39;: SerialClass2,

    },
    ...
}


TELNET_정보 = TELNET 만들기 위한 정보
SSH_정보 = SSH 만들기 위한 정보
SERIAL_정보 = Serial 만들기 위한 정보


장비.세션_만들기(TELNET_정보)
  # 세션만들기 안에 로직중에는
  # 선택된_SESSION_COLLECTION = 사용할_SESSION_선택하기(SESSION_COLLECTION_1, 사용할_SESSION은_Telnet_정보)


장비.세션[0].로그인하기()


장비.세션[0].입력(&#39;안녕하세요&#39;)
장비.세션[0].입력(&#39;你好!&#39;)
장비.세션[0].입력(&#39;Hello&#39;)</code></pre>
<h3 id="feature-api-를-이용해서-장비에-명령내리기">Feature API 를 이용해서 장비에 명령내리기</h3>
<pre><code class="language-python">
장비.세션_만들기(TELNET_정보)
장비.세션[0].로그인하기()

메세지들 = [
    장비.Main분류기능.Sub분류기능.함수1(필요한 데이터),
    장비.Main분류기능.Sub분류기능.함수2(필요한 데이터),
]


장비.세션[0].메세지_보내기(메세지들)</code></pre>
<h3 id="명령-입력-전후-모드-이동-관련-다음-포스팅에서">명령 입력 전/후 모드 이동 관련 (다음 포스팅에서...)</h3>
<ul>
<li>이 부분에 대해서는 다음 포스팅에서 이야기를 다뤄보겠다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 장비 제어를 위한 추상화 API - 1]]></title>
            <link>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B8%B0%EB%8A%A5-%EC%B6%94%EC%83%81%ED%99%94</link>
            <guid>https://velog.io/@engineer_km/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%A5%EB%B9%84-%EC%A0%9C%EC%96%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B8%B0%EB%8A%A5-%EC%B6%94%EC%83%81%ED%99%94</guid>
            <pubDate>Fri, 14 Jan 2022 15:47:57 GMT</pubDate>
            <description><![CDATA[<h2 id="네트워크-장비-제어를-위해-했던-여정-🚗">네트워크 장비 제어를 위해 했던 여정 🚗</h2>
<h3 id="고민거리-🤔">고민거리 🤔</h3>
<blockquote>
<p>네트워크 장비를 제어하다보면
같은 기능인데
장비 제조사에 따라서 CLI 가 다르고
같은 제조사라도 시기에 따라서 CLI 스타일이 다르고
같은 제조사라도 vender 의 요청으로 인해서 조금씩 다른 상황을 경험하게 되었다.</p>
</blockquote>
<br>

<p> 예를 들어본다면 
 &quot;안녕하세요&quot; 라는 기능을 수행시키려면
 각 장비마다 설정 방법이 다른 상황을 경험 할 수 있다.</p>
<p><img src="https://images.velog.io/images/engineer_km/post/bd4a5bea-8e40-4849-9c42-7dae6ed41cca/image.png" alt=""></p>
<ul>
<li><a href="https://www.flaticon.com/kr/authors/chattapat">첫번째 그림 출처</a></li>
<li><a href="https://www.flaticon.com/kr/authors/juicy-fish">두번째 그림 출처</a></li>
<li><a href="https://www.flaticon.com/kr/authors/smalllikeart/lineal-color">세번째 그림 출처</a></li>
</ul>
<br>
<br>



<h4 id="cli가-다르다보니-자동화-개발자-입장에서-아래와-같은-고민이-생겼다">CLI가 다르다보니 자동화 개발자 입장에서 아래와 같은 고민이 생겼다.</h4>
<p>1) 코드 중복을 줄이고 싶다.</p>
<blockquote>
<p>1) 장비별로 코드를 복사해서 CLI 가 다른부분만 바꿔준다. (비효율적이다.)</p>
</blockquote>
<p>2) 경험이 부족한 사람은 설정을 헤맨다. learning curve 를 줄여 주고 싶다.</p>
<blockquote>
<p>2) CLI가 다르면 경험이 부족한 사람의 경우 설정 방법을 해맬 수 있다.</p>
</blockquote>
<br>
<br>

<h3 id="1차-방법-접근-방법-❗">1차 방법: 접근 방법 ❗</h3>
<p>예를 든다면 아래와 같은 상속 구조로 디자인을 했었다.</p>
<ul>
<li><a href="https://refactoring.guru/design-patterns/factory-method">Factory Pattern</a></li>
</ul>
<p><img src="https://images.velog.io/images/engineer_km/post/0fcf78bd-8744-4bf8-82c8-2169334d07cb/image.png" alt=""></p>
<h3 id="pseudocode">pseudocode</h3>
<pre><code class="language-python">
Class AllGroupCommon(object):
  def __init__(self, 데이터들):
      telnet 접속하기
      login 하기

  @abc.abstractmethod
  def get_mactable(self):
     pass

  @abc.abstractmethod
  def set_port_disable(self):
     pass

  def get_config(self):
     # 모든 장비가 동일한 명령을 사용할 경우
     장비로_부터_받은_정보 = CONFIG_정보_확인 명령 실행
     return 장비로_부터_받은_정보


Class AGroupCommon(AllGroupCommon):
  def get_mactable(self):
     장비로_부터_받은_정보 = MAC_정보_확인 명령 실행
     return 장비로_부터_받은_정보

  def set_port_disable(self):
     장비로_부터_받은_정보 += PORT_DISABLE_실행전_모드로_접속
     장비로_부터_받은_정보 += PORT_DISABLE_실행
     장비로_부터_받은_정보 += PORT_DISABLE_실행후_모드_나가기
     return 장비로_부터_받은_정보
</code></pre>
<h4 id="api-사용-방법-예시">API 사용 방법 예시</h4>
<ul>
<li>아래 같은 느낌으로 접근할 수 있도록 작업하여었다.</li>
<li>장비.기능분류.함수명(필요한 데이터) 호출시
장비에 따라서 &quot;안녕하세요&quot;, &quot;你好&quot;, &quot;Hello&quot; 명령을 수행한다.</li>
</ul>
<pre><code class="language-python">
장비.기능분류.함수명(필요한 데이터)</code></pre>
<h3 id="1차-방법-무언가-단단히-잘못되었다-💦">1차 방법: 무언가 단단히 잘못되었다 💦</h3>
<p>사용하다보니 여러 문제점들이있었다.</p>
<h4 id="1-명령-입력-전후-모드-이동-문제">1. 명령 입력 전/후 모드 이동 문제</h4>
<ul>
<li>네트워크 장비 설정 예시 : port disable 과정<pre><code># config terminal
(config)# interface ge1/1
(ge1/1)# shutdown
(ge1/1)# exit
(config)# exit
#</code></pre></li>
</ul>
<h5 id="port-ge11-를-disable-했다가-enable-할-경우">port ge1/1 를 disable 했다가 Enable 할 경우</h5>
<ul>
<li>1차 방법의 문제점: 비효율적인 모드 이동</li>
</ul>
<pre><code># config terminal
(config)# interface ge1/1
(ge1/1)# shutdown
(ge1/1)# exit
(config)# exit
#
# config terminal
(config)# interface ge1/1
(ge1/1)# no shutdown
(ge1/1)# exit
(config)# exit
#</code></pre><ul>
<li>원했던 sequence: 효율적인 모드 이동</li>
</ul>
<pre><code># config terminal
(config)# interface ge1/1
(ge1/1)# shutdown
(ge1/1)# no shutdown
(ge1/1)# exit
(config)# exit
#</code></pre><p>위와 같은 상황 처럼 
비효율적으로 모드를 이동하여 소요시간이 오래 걸리게됨.</p>
<h4 id="2-빈번해지는-코드-중복이-발생됨">2. 빈번해지는 코드 중복이 발생됨</h4>
<ul>
<li>Model2는 AGroupCommon 이랑 70% 일치하고 BGroupCommon 이랑은 30% 일치하는 이런 경우가 빈번하게 발생하게 되었다.</li>
</ul>
<p><img src="https://images.velog.io/images/engineer_km/post/8c9fb2a2-18a6-4fcd-8618-b0ca09c08f54/image.png" alt=""></p>
<h4 id="3-장비의-command-기능과-session-telnet-와의-의존성-문제">3. 장비의 Command 기능과 Session (telnet) 와의 의존성 문제</h4>
<ul>
<li>connector 와 장비 기능(feature) 을 따로 디자인 하는게
의존성을 줄이는 방법이였다.</li>
</ul>
<h4 id="4-ssh-serialdirect-등-사용이-필요해짐-지원하는-session-종류를-늘려야됨">4. SSH, Serial(direct) 등 사용이 필요해짐 (지원하는 Session 종류를 늘려야됨)</h4>
<ul>
<li>telnet 이외의 세션 접속 방법에 대해서 다양화가 필요했다. </li>
</ul>
<h4 id="5-윈도우-환경에서도-api-사용이-필요해짐">5. 윈도우 환경에서도 API 사용이 필요해짐</h4>
<ul>
<li>1차 방법은 리눅스 디펜던시가 있는 상태였다.</li>
</ul>
<br>
<br>


<h3 id="1차-방법-회고-☕">1차 방법: 회고 ☕</h3>
<p>1) 디자인 단계에서 다양한 CASE 를 확보하지 못했었다.</p>
<ul>
<li>다양한 네트워크 장비에 대한 경험이 부족하였다. </li>
</ul>
<p>2) 개발관련 지식이 부족하였다.</p>
<ul>
<li>이때 당시 나 포함하여 프로젝트를 참여하신분들이 개발을 시작한지 얼마 안된 시점이였다.
그래서 지식적으로도 많이 부족하였었다.</li>
<li>&#39;Model1과 Model2가 겹치네!&#39; 묶어서 상속받는 구조로 가자 해서 만들어진 디자인이다.</li>
<li>디자인 패턴에 대해서도 몰랐었다.
이때 당시 저 방식이 Factory Pattern 인지도 몰랐었다.</li>
</ul>
<br>

<p>나는 이번 1차 방법 실패를 통해서
사전 조사의 중요함을 알게되었다.
개발의 대상이되는 것에 충분히 파악해야되고
여러 USECASE 에 대해서 충분한 확보가 필요하다는 것을 알게되었다.
사전조사가 미흡 할수록 설계가 자칫 산🌄 으로 갈 수 있다는 것을 알게 되었다.</p>
<br>
<br>

<h3 id="1차-방법-결국-폭발-💣">1차 방법: 결국 폭발 💣</h3>
<p>2019년 1월 9일 
4명이서 1년 넘게 작업한 프로젝트를 폭발시켰다. 💣
위의 나열한 문제점들로 인해서 다시 처음부터 시작을 하기로 결단을 내렸었다.
1년 넘게 한 프로젝트를 폭발하였지만 나는 개인적으로 아쉽거나, 미련이 남지는 않았다.</p>
<p>왜냐면 나는 <code>미련이 남았던 경험</code>이 있었다.
Python 을 처음 시작하였을때 
나의 멘토 분께서 코드리뷰의 시간을 가지면서
내가 어떠한 의도로 코드를 작성했는지에 대해서 설명하고 피드백을 받는 시간을 가졌었다.
어떠한 과제를 받고, 나 나름대로 열심히 했지만
결국 코드를 날리고, 다시 해야되는 상황이였다.
나는 매우 실망을 했었던거 같다.😔
그때 멘토분께서 이런 말씀을 해주셨었다.
<code>너의 코드를 너무 사랑하지마</code> 라고 ...</p>
<p><code>너의 코드를 너무 사랑하지마</code> 라는 말은
아직도 나의 개발할때의 마음가짐 중 중요한 하나이다.
개발을 할때 당시에 나는 열렬히 최선을 다해서 만들었겠지만
어느 순간 돌이킬 수 없는 문제가 발생되었다면
고칠 수 없을 지경이라면
더 좋은 방식이 있다면
과감하게 놓아주어야된다고 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[textfsm 사용법]]></title>
            <link>https://velog.io/@engineer_km/textfsm-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@engineer_km/textfsm-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Fri, 14 Jan 2022 12:47:39 GMT</pubDate>
            <description><![CDATA[<h3 id="textfsm">textfsm</h3>
<p>Python 을 이용하여 데이터를 Parsing 해주는 라이브러리중 하나이다.</p>
<blockquote>
<p>아래의 데이터는
사람이 보았을때는 테이블 형태여서 
어떤 부분(행, 열)이 어떤걸 의미하는지 알 수 있다.</p>
</blockquote>
<pre><code>SWITCH# show mac
================================================================================
  vid   port          mac addr          permission     status      in use
================================================================================
     1  eth0/1    22:22:33:33:44:aa     OK         static          0.00
</code></pre><br>
<br>


<blockquote>
<p>Python 에서 변수에 저장이되면, 단지 문자열이다.</p>
</blockquote>
<pre><code class="language-python">&gt;&gt;&gt; raw = &#39;&#39;&#39;
SWITCH# show mac
================================================================================
  vid   port          mac addr          permission     status      in use
================================================================================
     1  eth0/1    22:22:33:33:44:aa     OK         static          0.00

&#39;&#39;&#39;


&gt;&gt;&gt; raw
&#39;\nSWITCH# show mac\n================================================================================\n  vid   port          mac addr          permission     status      in use\n================================================================================\n     1  eth0/122:22:33:33:44:aa     OK         static          0.00\n\n&#39;
&gt;&gt;&gt;</code></pre>
<blockquote>
<p>textfsm 사용을 한다면, 아래와 같은 데이터(테이블)를 얻을 수 있다.</p>
</blockquote>
<pre><code class="language-python">  VLAN   PORTS                MAC PERMISSION  STATUS IN_USE
0    1  eth0/1  22:22:33:33:44:aa         OK  static   0.00</code></pre>
<p>문자들이 의미있는 데이터로 parsing 할 수 있는 것이 필요하다.
그래서 Parser 라이브러리 중에 textfsm 이라는 라이브러리를 소개한다.</p>
<br>
<br>

<h3 id="공식문서">공식문서</h3>
<ul>
<li><a href="https://github.com/google/textfsm/wiki/TextFSM">https://github.com/google/textfsm/wiki/TextFSM</a></li>
</ul>
<h3 id="textfsm-github">textfsm github</h3>
<ul>
<li><a href="https://github.com/google/textfsm">https://github.com/google/textfsm</a></li>
</ul>
<h3 id="textfsm-참고">textfsm 참고</h3>
<ul>
<li><a href="https://github.com/networktocode/ntc-templates">https://github.com/networktocode/ntc-templates</a></li>
</ul>
<h3 id="설치-방법">설치 방법</h3>
<pre><code>pip install textfsm</code></pre><h3 id="1-사용해보기---raw-한-문자열">1) 사용해보기 - RAW 한 문자열</h3>
<pre><code>SWITCH# show mac
================================================================================
  vid   port          mac addr          permission     status      in use
================================================================================
     1  eth0/1    22:22:33:33:44:aa     OK         static          0.00</code></pre><h3 id="2-몇가지-추가적으로-설치-필요">2) 몇가지 추가적으로 설치 필요</h3>
<ul>
<li>아래의 코드를 실행하기 위해서는 다른 외부 라이브러리가 필요함.<pre><code>root@VirtualBox:~# cat requirements.txt
tabulate
pandas
textfsm
</code></pre></li>
</ul>
<p>root@VirtualBox:<del># pip install -r requirements.txt
Requirement already satisfied: tabulate in /usr/local/lib/python3.8/dist-packages (from -r requirements.txt (line 1)) (0.8.9)
Requirement already satisfied: pandas in /usr/local/lib/python3.8/dist-packages (from -r requirements.txt (line 2)) (1.3.5)
Requirement already satisfied: textfsm in /usr/local/lib/python3.8/dist-packages (from -r requirements.txt (line 3)) (1.1.2)
Requirement already satisfied: python-dateutil&gt;=2.7.3 in /usr/lib/python3/dist-packages (from pandas-&gt;-r requirements.txt (line 2)) (2.7.3)
Requirement already satisfied: numpy&gt;=1.17.3; platform_machine != &quot;aarch64&quot; and platform_machine != &quot;arm64&quot; and python_version &lt; &quot;3.10&quot; in /usr/local/lib/python3.8/dist-packages (from pandas-&gt;-r requirements.txt (line 2)) (1.22.0)
Requirement already satisfied: pytz&gt;=2017.3 in /usr/lib/python3/dist-packages (from pandas-&gt;-r requirements.txt (line 2)) (2019.3)
Requirement already satisfied: future in /usr/lib/python3/dist-packages (from textfsm-&gt;-r requirements.txt (line 3)) (0.18.2)
Requirement already satisfied: six in /usr/lib/python3/dist-packages (from textfsm-&gt;-r requirements.txt (line 3)) (1.14.0)
root@VirtualBox:</del>#</p>
<pre><code>
### 3) 사용해보기 - .template file
  - [texfsm 문법](https://github.com/google/textfsm)
  - [정규표현식](https://wikidocs.net/4308)
  - .py 에서 string으로 불러올 수 있지만 따로 관리함.
    Python 코드와 무관하게 </code></pre><p>Value VLAN (\d+)
Value PORTS (\S+)
Value MAC ((\S{2}(:))(\S{2}(:))(\S{2}(:))(\S{2}(:))(\S{2}(:))(\S{2}))
Value PERMISSION (\S+)
Value STATUS (\S+)
Value IN_USE (\S+)</p>
<p>Start
  ^(\s+)?${VLAN}\s+${PORTS}\s+${MAC}\s+${PERMISSION}\s+${STATUS}\s+${IN_USE} -&gt; Record</p>
<p>EOF</p>
<pre><code>

### 4) 사용해보기 - textfsm_exmple.py 
  - dir2textfsm(path) : path str 을 받으면 
    해당 path 의 파일을 읽어서
    TextFSM 을 통해서 parser 를 만들어서 반환해줍니다.
  - parser.ParseText(raw_data) : parser를 통해서 raw_data 를 parsing 합니다.
  - Dataframe 은 Table 형태로 데이터를 담아준다라고 생각
  - [tablulate](https://pypi.org/project/tabulate/) 를 통해서 Datafram 을 꾸며서 출력해준다.

코드📄</code></pre><p>import pdb
import unittest
from io import StringIO
from tabulate import tabulate
from textfsm import TextFSM
from pandas import DataFrame</p>
<p>def dir2textfsm(path):
    parser = None
    try:
        with open(path) as f:
            data = f.read()
            parser = TextFSM(StringIO(data))
    except IOError:
        print(&quot;No such TextFSM file : {}&quot;.format(path))
    return parser</p>
<p>class TextfsmExample(unittest.TestCase):
    def test_01(self):
        parser = dir2textfsm(&#39;textfsm_exmple.template&#39;)
        raw_data = &quot;&quot;&quot;SWITCH# show mac
================================================================================
  vid   port          mac addr          permission     status      in use
================================================================================
     1  eth0/1  22:22:33:33:44:aa     OK         static          0.00</p>
<pre><code>    &quot;&quot;&quot;
    print(raw_data)
    res = DataFrame(parser.ParseText(raw_data), columns=parser.header)
    res_str = str(tabulate(res, headers=&quot;keys&quot;, tablefmt=&quot;grid&quot;))
    print(res_str)</code></pre><pre><code>
실행💻</code></pre><p>root@VirtualBox:~# python3 -m unittest textfsm_exmple.py</p>
<pre><code>

출력💻</code></pre><h1 id="switch-show-mac">SWITCH# show mac</h1>
<h1 id="vid---port----------mac-addr----------permission-----status------in-use">  vid   port          mac addr          permission     status      in use</h1>
<pre><code> 1  eth0/1  22:22:33:33:44:aa     OK         static          0.00</code></pre><p>+----+--------+---------+-------------------+--------------+----------+----------+
|    |   VLAN | PORTS   | MAC               | PERMISSION   | STATUS   |   IN_USE |
+====+========+=========+===================+==============+==========+==========+
|  0 |      1 | eth0/1  | 22:22:33:33:44:aa | OK           | static   |        0 |
+----+--------+---------+-------------------+--------------+----------+----------+
.</p>
<hr>
<p>Ran 1 test in 0.004s</p>
<p>OK
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[paramiko 사용법]]></title>
            <link>https://velog.io/@engineer_km/paramiko</link>
            <guid>https://velog.io/@engineer_km/paramiko</guid>
            <pubDate>Fri, 14 Jan 2022 07:31:35 GMT</pubDate>
            <description><![CDATA[<p>Python 으로  SSH 원격(텔넷 서버)으로 접속하여 제어 하고 싶다면
paramiko 사용 방법을 간략하게 소개해보려고 한다.</p>
<h3 id="공식-문서">공식 문서</h3>
<ul>
<li><a href="https://www.paramiko.org/">https://www.paramiko.org/</a></li>
</ul>
<h3 id="paramiko-github">paramiko github</h3>
<ul>
<li><a href="https://github.com/paramiko/paramiko">https://github.com/paramiko/paramiko</a></li>
</ul>
<h3 id="설치-방법">설치 방법</h3>
<pre><code>pip install paramiko</code></pre><h3 id="1-예제-코드-전체">1) 예제 코드 전체</h3>
<pre><code class="language-python">
&gt;&gt;&gt; import paramiko

&gt;&gt;&gt; HOST = &#39;127.0.0.1&#39;
&gt;&gt;&gt; ID = &#39;ID&#39;
&gt;&gt;&gt; PASSWD = &#39;PWD&#39;
&gt;&gt;&gt;


&gt;&gt;&gt; ssh = paramiko.SSHClient()
&gt;&gt;&gt; ssh
&lt;paramiko.client.SSHClient object at 0x7f017ca69b50&gt;
&gt;&gt;&gt;
&gt;&gt;&gt; ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
&gt;&gt;&gt;
&gt;&gt;&gt; ssh.connect(HOST, username=ID, password=PASSWD)
&gt;&gt;&gt;



&gt;&gt;&gt; ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(&#39;df -h&#39;)


&gt;&gt;&gt; msg = ssh_stdout.read().decode(&quot;euc-kr&quot;)
&gt;&gt;&gt; print(msg)
Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           392M   21M  371M   6% /run
/dev/sda1        39G   32G  4.4G  89% /
tmpfs           2.0G  188K  2.0G   1% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup
/dev/loop12     163M  163M     0 100% /snap/gnome-3-28-1804/145
/dev/loop4      253M  253M     0 100% /snap/pynsource/20
/dev/loop1       66M   66M     0 100% /snap/gtk-common-themes/1515
/dev/loop3       56M   56M     0 100% /snap/core18/2246
/dev/loop5       44M   44M     0 100% /snap/snapd/14295
/dev/loop6       43M   43M     0 100% /snap/snapd/14066
/dev/loop8      128K  128K     0 100% /snap/bare/5
/dev/loop9      165M  165M     0 100% /snap/gnome-3-28-1804/161
/dev/loop7      100M  100M     0 100% /snap/core/11798
/dev/loop2       66M   66M     0 100% /snap/gtk-common-themes/1519
/dev/loop10     252M  252M     0 100% /snap/pynsource/19
/dev/loop11      56M   56M     0 100% /snap/core18/2253
/dev/loop0      100M  100M     0 100% /snap/core/11993
tmpfs           392M   24K  392M   1% /run/user/108
tmpfs           392M     0  392M   0% /run/user/0
overlay          39G   32G  4.4G  89% /var/lib/docker/overlay2/a699597c9aa33bc4924c9bc82f8decc538ffe3fe5e3231b512d768bc5ba63b19/merged
shm              64M     0   64M   0% /var/lib/docker/containers/7c001393a1153608c1cae1a0b2f2466a02934d1051faec389a3ce4393e1e3e24/shm

&gt;&gt;&gt; err_msg = ssh_stderr.read().decode(&quot;euc-kr&quot;)
&gt;&gt;&gt; err_msg
&#39;&#39;
&gt;&gt;&gt;



&gt;&gt;&gt; ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(&#39;invalid cmd&#39;)
&gt;&gt;&gt; msg = ssh_stderr.read().decode(&#39;utf-8&#39;)
&gt;&gt;&gt; msg
&#39;bash: invalid: 명령어를 찾을 수 없음\n&#39;
&gt;&gt;&gt;



&gt;&gt;&gt; ssh.get_transport()
&lt;paramiko.Transport at 0xa9839c50 (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))&gt;
&gt;&gt;&gt;

&gt;&gt;&gt; ssh.close()
&gt;&gt;&gt;
&gt;&gt;&gt;
&gt;&gt;&gt; ssh.get_transport()
&gt;&gt;&gt;
</code></pre>
<h4 id="1-1-ssh-세션-접속">1-1) SSH 세션 접속</h4>
<ul>
<li><p>paramiko.SSHClient : </p>
<ul>
<li>SSH 서버(원격)으로 접속하기 위한 클래스</li>
<li>서버로 접속하기 때문에 현재 LOCAL PC는 Client 가 됨. 
그래서 Class 명이 Client 임.</li>
<li>쓰기 쉽도록 상위 레벨로 wraps 한 클래스 (<code>.Transport</code>, <code>.Channel</code>, <code>.SFTPClient</code>)</li>
</ul>
</li>
<li><p>set_missing_host_key_policy() : ssh 세션에 key 규칙을 paramiko.AutoAddPolicy() 로 함.</p>
<ul>
<li>paramiko.AutoAddPolicy() 는 호스트 이름과 새 호스트 키를 로컬 <code>.HostKeys</code> 개체에 자동으로 추가하고 저장하는 정책</li>
</ul>
</li>
<li><p>ssh.connect() : SSH 서버(원격)으로 접속</p>
</li>
</ul>
<pre><code class="language-python">
&gt;&gt;&gt; ssh = paramiko.SSHClient()
&gt;&gt;&gt; ssh
&lt;paramiko.client.SSHClient object at 0x7f017ca69b50&gt;
&gt;&gt;&gt;
&gt;&gt;&gt; ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
&gt;&gt;&gt;
&gt;&gt;&gt; ssh.connect(HOST, username=ID, password=PASSWD)
&gt;&gt;&gt;</code></pre>
<h4 id="1-2-ssh-서버원격으로-입력">1-2) SSH 서버(원격)으로 입력</h4>
<ul>
<li>exec_command() : 원격서버에서 명령을 입력<ul>
<li>ssh_stdin : 입력한 명령에 대한 데이터<ul>
<li>stdin : write-only </li>
</ul>
</li>
<li>ssh_stdout : 입력한 명령에 대한 출력이 잘못되었을 경우의 데이터<ul>
<li>stdin : read-only </li>
</ul>
</li>
<li>ssh_stderr : 입력한 명령에 대한 올바르게 출력되었다고 판단한 데이터<ul>
<li>stdin : read-only <pre><code class="language-python"></code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<blockquote>
<blockquote>
<p>ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(&#39;df -h&#39;)</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>

#### 1-3) SSH 서버(원격)로 부터 데이터 읽기

```python
&gt;&gt;&gt; msg = ssh_stdout.read().decode(&quot;euc-kr&quot;)
&gt;&gt;&gt; print(msg)
Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           392M   21M  371M   6% /run
/dev/sda1        39G   32G  4.4G  89% /
tmpfs           2.0G  188K  2.0G   1% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup
/dev/loop12     163M  163M     0 100% /snap/gnome-3-28-1804/145
/dev/loop4      253M  253M     0 100% /snap/pynsource/20
/dev/loop1       66M   66M     0 100% /snap/gtk-common-themes/1515
/dev/loop3       56M   56M     0 100% /snap/core18/2246
/dev/loop5       44M   44M     0 100% /snap/snapd/14295
/dev/loop6       43M   43M     0 100% /snap/snapd/14066
/dev/loop8      128K  128K     0 100% /snap/bare/5
/dev/loop9      165M  165M     0 100% /snap/gnome-3-28-1804/161
/dev/loop7      100M  100M     0 100% /snap/core/11798
/dev/loop2       66M   66M     0 100% /snap/gtk-common-themes/1519
/dev/loop10     252M  252M     0 100% /snap/pynsource/19
/dev/loop11      56M   56M     0 100% /snap/core18/2253
/dev/loop0      100M  100M     0 100% /snap/core/11993
tmpfs           392M   24K  392M   1% /run/user/108
tmpfs           392M     0  392M   0% /run/user/0
overlay          39G   32G  4.4G  89% /var/lib/docker/overlay2/a699597c9aa33bc4924c9bc82f8decc538ffe3fe5e3231b512d768bc5ba63b19/merged
shm              64M     0   64M   0% /var/lib/docker/containers/7c001393a1153608c1cae1a0b2f2466a02934d1051faec389a3ce4393e1e3e24/shm

&gt;&gt;&gt; err_msg = ssh_stderr.read().decode(&quot;euc-kr&quot;)
&gt;&gt;&gt; err_msg
&#39;&#39;
&gt;&gt;&gt;





&gt;&gt;&gt; ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(&#39;invalid cmd&#39;)
&gt;&gt;&gt; msg = ssh_stderr.read().decode(&#39;utf-8&#39;)
&gt;&gt;&gt; msg
&#39;bash: invalid: 명령어를 찾을 수 없음\n&#39;
&gt;&gt;&gt;</code></pre><h4 id="1-4-ssh-세션-살았는지-확인">1-4) SSH 세션 살았는지 확인</h4>
<ul>
<li>.get_transport() : <ul>
<li>세션이 살아있다면 paramiko.Transport 를 반환</li>
<li>세션 연결이 안되었다면(끊겼다면) None을 반환</li>
</ul>
</li>
</ul>
<pre><code class="language-python">

&gt;&gt;&gt; ssh.get_transport()
&lt;paramiko.Transport at 0xa9839c50 (cipher aes128-ctr, 128 bits) (active; 0 open channel(s))&gt;
&gt;&gt;&gt;

&gt;&gt;&gt; ssh.close()
&gt;&gt;&gt;
&gt;&gt;&gt;
&gt;&gt;&gt; ssh.get_transport()
&gt;&gt;&gt;</code></pre>
<h4 id="1-5-ssh-세션-끊기">1-5) SSH 세션 끊기</h4>
<pre><code class="language-python">&gt;&gt;&gt; ssh.close()
&gt;&gt;&gt;</code></pre>
<h3 id="2-예제-코드-전체-raw-한-api-로-사용해보기">2) 예제 코드 전체 (Raw 한 API 로 사용해보기)</h3>
<ul>
<li>high level 로 접근하여 사용해도 되지만
<code>굳이</code> 한번 Raw 하게 접근해보고 싶었다.</li>
<li>이 방법은 Linux, 여러 네트워크 장비로 접속이 가능했다.</li>
<li>WINDOWS 환경에서의 SSH 서버로의 접근할 경우 접근이 안되었다.<ul>
<li>이유는? 기본적으로 윈도우는 vt100 터미널을 지원하지 않음. 지원하게 하려면 설치가 필요함. <ul>
<li><a href="https://stackoverflow.com/questions/64474568/how-to-enable-vt100-terminal-emulation-in-windows-10">https://stackoverflow.com/questions/64474568/how-to-enable-vt100-terminal-emulation-in-windows-10</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">
&gt;&gt;&gt; ssh = paramiko.SSHClient()
&gt;&gt;&gt; ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
&gt;&gt;&gt; ssh.connect(HOST, username=ID, password=PASSWD)
&gt;&gt;&gt;



&gt;&gt;&gt; session = ssh.invoke_shell()
&gt;&gt;&gt; session.settimeout(0.0)



&gt;&gt;&gt; 
&gt;&gt;&gt; session.sendall(&#39;df -h&#39;.encode(&#39;ascii&#39;) + b&#39;\n&#39;)
&gt;&gt;&gt; session.recv(50000)
b&#39;df -h\r\nFilesystem      Size  Used Avail Use% Mounted on\r\nudev            1.9G     0  1.9G   0% /dev\r\ntmpfs           392M   21M  371M   6% /run\r\n/dev/sda1        39G   32G  4.4G  89% /\r\ntmpfs           2.0G  188K  2.0G   1% /dev/shm\r\ntmpfs           5.0M  4.0K  5.0M   1% /run/lock\r\ntmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup\r\n/dev/loop12     163M  163M     0 100% /snap/gnome-3-28-1804/145\r\n/dev/loop4      253M  253M     0 100% /snap/pynsource/20\r\n/dev/loop1       66M   66M     0 100% /snap/gtk-common-themes/1515\r\n/dev/loop3       56M   56M     0 100% /snap/core18/2246\r\n/dev/loop5       44M   44M     0 100% /snap/snapd/14295\r\n/dev/loop6       43M   43M     0 100% /snap/snapd/14066\r\n/dev/loop8      128K  128K     0 100% /snap/bare/5\r\n/dev/loop9      165M  165M     0 100% /snap/gnome-3-28-1804/161\r\n/dev/loop7      100M  100M     0 100% /snap/core/11798\r\n/dev/loop2       66M   66M     0 100% /snap/gtk-common-themes/1519\r\n/dev/loop10     252M  252M     0 100% /snap/pynsource/19\r\n/dev/loop11      56M   56M     0 100% /snap/core18/2253\r\n/dev/loop0      100M  100M     0 100% /snap/core/11993\r\ntmpfs           392M   24K  392M   1% /run/user/108\r\ntmpfs           392M     0  392M   0% /run/user/0\r\noverlay          39G   32G  4.4G  89% /var/lib/docker/overlay2/a699597c9aa33bc4924c9bc82f8decc538ffe3fe5e3231b512d768bc5ba63b19/merged\r\nshm              64M     0   64M   0% /var/lib/docker/containers/7c001393a1153608c1cae1a0b2f2466a02934d1051faec389a3ce4393e1e3e24/shm\r\nroot@ytest-VirtualBox:~# &#39;
&gt;&gt;&gt; session.recv(50000)
Traceback (most recent call last):
  File &quot;/usr/local/lib/python3.7/dist-packages/paramiko/channel.py&quot;, line 699, in recv
    out = self.in_buffer.read(nbytes, self.timeout)
  File &quot;/usr/local/lib/python3.7/dist-packages/paramiko/buffered_pipe.py&quot;, line 155, in read
    raise PipeTimeout()
paramiko.buffered_pipe.PipeTimeout

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
  File &quot;/usr/local/lib/python3.7/dist-packages/paramiko/channel.py&quot;, line 701, in recv
    raise socket.timeout()
socket.timeout
&gt;&gt;&gt;
</code></pre>
<h4 id="2-1-invoke_shell">2-1) invoke_shell</h4>
<ul>
<li>터미널을 설정합니다.<ul>
<li>term : 터미널의 종류 을 설정
기본 vt100</li>
<li>width: 터미널 가로 길이 설정</li>
<li>height: 터미널 세로 길이 설정</li>
<li>width_pixels : 터미널 가로 픽셀 설정</li>
<li>height_pixels : 터미널 세로 픽셀 설정</li>
</ul>
</li>
</ul>
<pre><code class="language-paramiko">
        Start an interactive shell session on the SSH server.  A new `.Channel`
        is opened and connected to a pseudo-terminal using the requested
        terminal type and size.

        :param str term:
            the terminal type to emulate (for example, ``&quot;vt100&quot;``)
        :param int width: the width (in characters) of the terminal window
        :param int height: the height (in characters) of the terminal window
        :param int width_pixels: the width (in pixels) of the terminal window
        :param int height_pixels: the height (in pixels) of the terminal window
        :param dict environment: the command&#39;s environment
        :return: a new `.Channel` connected to the remote shell</code></pre>
<pre><code class="language-python">
&gt;&gt;&gt; session = ssh.invoke_shell()
</code></pre>
<h4 id="2-2-세션-timeout-설정">2-2) 세션 Timeout 설정</h4>
<ul>
<li>.settimeout(0.0) : 세션 창이 끊길때까지 설정<pre><code class="language-python">&gt;&gt;&gt; session.settimeout(0.0)</code></pre>
</li>
</ul>
<h4 id="2-3-ssh-서버원격으로-입력">2-3) SSH 서버(원격)으로 입력</h4>
<ul>
<li>RAW 한 API 인<code>.sendall</code> 를 사용</li>
</ul>
<pre><code class="language-python">
&gt;&gt;&gt; session.sendall(&#39;df -h&#39;.encode(&#39;ascii&#39;) + b&#39;\n&#39;)
</code></pre>
<h4 id="2-4-ssh-서버원격으로-데이터-읽기">2-4) SSH 서버(원격)으로 데이터 읽기</h4>
<ul>
<li>RAW 한 API 인<code>.recv</code> 를 사용</li>
<li>.recv 의 인자값은 데이터를 읽어올 사이즈 입니다.</li>
<li>서버로 부터 현재 읽어올 데이터가 없었다면 socket.timeout 에러 발생<pre><code class="language-python">&gt;&gt;&gt; session.recv(50000)
b&#39;df -h\r\nFilesystem      Size  Used Avail Use% Mounted on\r\nudev            1.9G     0  1.9G   0% /dev\r\ntmpfs           392M   21M  371M   6% /run\r\n/dev/sda1        39G   32G  4.4G  89% /\r\ntmpfs           2.0G  188K  2.0G   1% /dev/shm\r\ntmpfs           5.0M  4.0K  5.0M   1% /run/lock\r\ntmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup\r\n/dev/loop12     163M  163M     0 100% /snap/gnome-3-28-1804/145\r\n/dev/loop4      253M  253M     0 100% /snap/pynsource/20\r\n/dev/loop1       66M   66M     0 100% /snap/gtk-common-themes/1515\r\n/dev/loop3       56M   56M     0 100% /snap/core18/2246\r\n/dev/loop5       44M   44M     0 100% /snap/snapd/14295\r\n/dev/loop6       43M   43M     0 100% /snap/snapd/14066\r\n/dev/loop8      128K  128K     0 100% /snap/bare/5\r\n/dev/loop9      165M  165M     0 100% /snap/gnome-3-28-1804/161\r\n/dev/loop7      100M  100M     0 100% /snap/core/11798\r\n/dev/loop2       66M   66M     0 100% /snap/gtk-common-themes/1519\r\n/dev/loop10     252M  252M     0 100% /snap/pynsource/19\r\n/dev/loop11      56M   56M     0 100% /snap/core18/2253\r\n/dev/loop0      100M  100M     0 100% /snap/core/11993\r\ntmpfs           392M   24K  392M   1% /run/user/108\r\ntmpfs           392M     0  392M   0% /run/user/0\r\noverlay          39G   32G  4.4G  89% /var/lib/docker/overlay2/a699597c9aa33bc4924c9bc82f8decc538ffe3fe5e3231b512d768bc5ba63b19/merged\r\nshm              64M     0   64M   0% /var/lib/docker/containers/7c001393a1153608c1cae1a0b2f2466a02934d1051faec389a3ce4393e1e3e24/shm\r\nroot@ytest-VirtualBox:~# &#39;
&gt;&gt;&gt; session.recv(50000)
Traceback (most recent call last):
File &quot;/usr/local/lib/python3.7/dist-packages/paramiko/channel.py&quot;, line 699, in recv
out = self.in_buffer.read(nbytes, self.timeout)
File &quot;/usr/local/lib/python3.7/dist-packages/paramiko/buffered_pipe.py&quot;, line 155, in read
raise PipeTimeout()
paramiko.buffered_pipe.PipeTimeout
</code></pre>
</li>
</ul>
<p>During handling of the above exception, another exception occurred:</p>
<p>Traceback (most recent call last):
  File &quot;<stdin>&quot;, line 1, in <module>
  File &quot;/usr/local/lib/python3.7/dist-packages/paramiko/channel.py&quot;, line 701, in recv
    raise socket.timeout()
socket.timeout</p>
<blockquote>
<blockquote>
<blockquote>
</blockquote>
<p>```</p>
</blockquote>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[telnetlib 사용법]]></title>
            <link>https://velog.io/@engineer_km/telnetlib</link>
            <guid>https://velog.io/@engineer_km/telnetlib</guid>
            <pubDate>Fri, 14 Jan 2022 05:50:56 GMT</pubDate>
            <description><![CDATA[<p>Python 으로 telnet 원격(텔넷 서버)으로 접속하여 제어 하고 싶다면
Python 기본 라이브러리인 telnetlib 사용 방법을 간략하게 소개해보려고 한다.</p>
<p>Python이 설치 되어있다면 telnetlib을 따로 설치할 필요없다.</p>
<h3 id="공식-문서">공식 문서</h3>
<ul>
<li><a href="https://docs.python.org/ko/3/library/telnetlib.html">https://docs.python.org/ko/3/library/telnetlib.html</a></li>
</ul>
<h3 id="telnetlib-github">telnetlib github</h3>
<ul>
<li><a href="https://github.com/python/cpython/blob/main/Lib/telnetlib.py">https://github.com/python/cpython/blob/main/Lib/telnetlib.py</a></li>
</ul>
<h3 id="예제-전체-코드">예제 전체 코드</h3>
<pre><code class="language-python">
&gt;&gt;&gt; import telnetlib
&gt;&gt;&gt; import socket



&gt;&gt;&gt; HOST = &#39;127.0.0.1&#39;      # telnet으로 접속할 대상
&gt;&gt;&gt; PORT = &#39;23&#39;             # telnet port: 23
&gt;&gt;&gt; SESSION_TIMEOUT = None
&gt;&gt;&gt; session = telnetlib.Telnet(HOST, PORT, SESSION_TIMEOUT)
&gt;&gt;&gt;


&gt;&gt;&gt; def nego(sock, cmd, opt):
        if cmd == telnetlib.WILL and opt == telnetlib.ECHO:
            sock.sendall(telnetlib.IAC + telnetlib.DO + telnetlib.ECHO)
        elif cmd in (telnetlib.DO, telnetlib.DONT):
            sock.sendall(telnetlib.IAC + telnetlib.WONT + opt)
        elif cmd in (telnetlib.WILL, telnetlib.WONT):
            sock.sendall(telnetlib.IAC + telnetlib.DONT + opt)

&gt;&gt;&gt; session.set_option_negotiation_callback(nego)
&gt;&gt;&gt;


&gt;&gt;&gt; session.write(&#39;show interface summary&#39;.encode(&#39;ascii&#39;) + b&quot;\n&quot;)
&gt;&gt;&gt;


&gt;&gt;&gt; msgs = []
&gt;&gt;&gt; while True:
    msgs += [session.read_eager().decode(&#39;ascii&#39;)]
    if msgs[-1] == &#39;&#39;:
        break


&gt;&gt;&gt; 
&gt;&gt;&gt; 

&gt;&gt;&gt; 
&gt;&gt;&gt; print(&#39;&#39;.join(msgs))
show interface summary

 *: interface is up
 IHQ: pkts in input hold queue     IQD: pkts dropped from input queue
 OHQ: pkts in output hold queue    OQD: pkts dropped from output queue
 RXBS: rx rate (bits/sec)          RXPS: rx rate (pkts/sec)
 TXBS: tx rate (bits/sec)          TXPS: tx rate (pkts/sec)
 TRTL: throttle count

  Interface                   IHQ       IQD       OHQ       OQD      RXBS      RXPS      TXBS      TXPS      TRTL
-----------------------------------------------------------------------------------------------------------------
* GigabitEthernet1              0         0         0         0         0         0         0         0         0
  GigabitEthernet2              0         0         0         0         0         0         0         0         0
  GigabitEthernet3              0         0         0         0         0         0         0         0         0
  GigabitEthernet4              0         0         0         0         0         0         0         0         0
  GigabitEthernet5              0         0         0         0         0         0         0         0         0
  GigabitEthernet6              0         0         0         0         0         0         0         0         0
  GigabitEthernet7              0         0         0         0         0         0         0         0         0
* GigabitEthernet8              0         0         0         0      4000         8         0         0         0
* Loopback0                     0         0         0         0         0         0         0         0         0
* Loopback1                     0         0         0         0         0         0         0         0         0
* Loopback2                     0         0         0         0         0         0         0         0         0
IOSXE#
&gt;&gt;&gt;
&gt;&gt;&gt;



&gt;&gt;&gt; session.close()
&gt;&gt;&gt;</code></pre>
<h4 id="1-telnet-session-접속">1) telnet session 접속</h4>
<ul>
<li>HOST : 접속할 IP</li>
<li>PORT : TCP port<ul>
<li>23 </li>
<li>Remote Console를 이용해서 사용할 경우, 그에 맞는 port 번호를 사용</li>
</ul>
</li>
<li>SESSION_TIMEOUT : 세션의 타임아웃 값
None, int <ul>
<li>None 일 경우, 
접속한 텔넷 서버에서 연결을 끊을 때까지 세션을 유지
아니면 망 상황이 안 좋아서 끊길때까지 유지</li>
<li>Int 일 경우,
세션 유지하는 시간을 숫자로 입력</li>
</ul>
</li>
</ul>
<pre><code class="language-python">
session = telnetlib.Telnet(HOST, PORT, SESSION_TIMEOUT)</code></pre>
<h4 id="2-입력시-telnet-option-과정에-대해서-정의를-해줌">2) 입력시 Telnet option 과정에 대해서 정의를 해줌</h4>
<ul>
<li>write 할 데이터 전송시 Nego 하는 방법을 정의해줌</li>
<li>IAC (Interrupt As Command): IAC 명령을 사용해야지 뒤에 BIT를 명령으로 인식함.<ul>
<li>Ex) telnetlib.DO 만 보내면 단순히 string 인 줄 안다.</li>
</ul>
</li>
<li>DO : 접속한 서버(원격)에게 옵션을 사용하자고 요청</li>
<li>WILL : LOCAL 에서 옵션을 사용하겠다고 보냄</li>
<li>DONT : 접속한 서버(원격)에게 옵션을 사용하지말자고 요청</li>
<li>DONT : 접속한 서버(원격)에게 옵션을 사용하지하지 말자고 보냄</li>
<li>WONT : LOCAL 에서 옵션을 사용하지않는다고 보냄</li>
<li>ECHO : 접속한 서버(원격)가 확인차원에서 한번더 데이터를 보냄</li>
</ul>
<h5 id="왜-nego-사용">왜 Nego 사용?</h5>
<ul>
<li>현재 제가 사용중인 IOS 에서는 nego option을 사용하지 않아도 입력한 데이터에 대해서
읽어올때 정상적으로 데이터를 가지고 왔음.</li>
<li>하지만 특정 텔넷 서버에 경우는 nego option 이 필요한 경우가 있어서 남겨 보았음.</li>
<li>nego option 이 필요한 텔넷 서버에서 nego option을 안 할 경우, 서버로 부터 write 한 데이터를 받지 못했음.</li>
</ul>
<pre><code>&gt;&gt;&gt; def nego(sock, cmd, opt):
        if cmd == telnetlib.WILL and opt == telnetlib.ECHO:
            sock.sendall(telnetlib.IAC + telnetlib.DO + telnetlib.ECHO)
        elif cmd in (telnetlib.DO, telnetlib.DONT):
            sock.sendall(telnetlib.IAC + telnetlib.WONT + opt)
        elif cmd in (telnetlib.WILL, telnetlib.WONT):
            sock.sendall(telnetlib.IAC + telnetlib.DONT + opt)

&gt;&gt;&gt; session.set_option_negotiation_callback(nego)
&gt;&gt;&gt;
</code></pre><h4 id="3-텔넷서버에-데이터-입력">3) 텔넷서버에 데이터 입력</h4>
<ul>
<li>bytes 타입으로 전달 해줘야됨</li>
<li>문자열에 끝에는 텔넷서버가 인식하는 <code>command 명령의 끝</code>이라는 것을 알려는 문자를 넣어줘야됩니다.<ul>
<li><code>\n</code> : new line</li>
</ul>
</li>
</ul>
<pre><code>&gt;&gt;&gt; session.write(&#39;show interface summary&#39;.encode(&#39;ascii&#39;) + b&quot;\n&quot;)
&gt;&gt;&gt;</code></pre><h4 id="4-텔넷서버에-데이터-읽기">4) 텔넷서버에 데이터 읽기</h4>
<ul>
<li>read_eager() : <ul>
<li>telnetlib에서 데이터를 읽는 방법 중 하나입니다.</li>
<li>세션 연결이 닫히면 EOFError를 발생</li>
<li>데이터가 없으면 b&#39;&#39;를 반환합니다.<pre><code>&gt;&gt;&gt; msgs = []
&gt;&gt;&gt; while True:
msgs += [session.read_eager().decode(&#39;ascii&#39;)]
if msgs[-1] == &#39;&#39;:
  break
</code></pre></li>
</ul>
</li>
</ul>
<blockquote>
<blockquote>
<blockquote>
<p>print(&#39;&#39;.join(msgs))</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>
#### 5) 텔넷 세션 끊기
</code></pre><p>session.close()
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[django-debug-toolbar]]></title>
            <link>https://velog.io/@engineer_km/django-debug-toolbar</link>
            <guid>https://velog.io/@engineer_km/django-debug-toolbar</guid>
            <pubDate>Thu, 13 Jan 2022 14:55:22 GMT</pubDate>
            <description><![CDATA[<p>현재 HTML 포맷의 request/reponse 에 대한 다양한 디버깅 정보를 보여줌</p>
<ul>
<li>요청처리시에 발생한 SQL 내역 확인 가능</li>
</ul>
<h3 id="공식-문서">공식 문서</h3>
<p><a href="https://django-debug-toolbar.readthedocs.io/en/latest/">https://django-debug-toolbar.readthedocs.io/en/latest/</a></p>
<h3 id="1-설치">1) 설치</h3>
<pre><code>pip install django-debug-toolbar</code></pre><h3 id="2-staticfile-설정">2) staticfile 설정</h3>
<ul>
<li>프로젝트 settings.py</li>
<li>INSTALLED_APPS 에 
&#39;django.contrib.staticfiles&quot;</li>
</ul>
<pre><code class="language-python">
INSTALLED_APPS = [
    &#39;django.contrib.staticfiles&#39;,
]

STATIC_URL = &quot;/static/&quot;</code></pre>
<h3 id="3--templates-설정">3)  TEMPLATES 설정</h3>
<ul>
<li>프로젝트 settings.py</li>
</ul>
<pre><code class="language-python">TEMPLATES = [
    {
        &quot;BACKEND&quot;: &quot;django.template.backends.django.DjangoTemplates&quot;,
        &quot;APP_DIRS&quot;: True,
        # ...
    }
]</code></pre>
<h3 id="4-debug_toolbar-app-설정">4) debug_toolbar APP 설정</h3>
<ul>
<li>프로젝트 settings.py</li>
</ul>
<pre><code class="language-python">INSTALLED_APPS = [
    # ...
    &quot;debug_toolbar&quot;,
    # ...
]</code></pre>
<h3 id="5-urls-추가">5) URLs 추가</h3>
<ul>
<li>프로젝트 urls.py</li>
<li>debug 모드일 때만 실행시키기<pre><code class="language-python"></code></pre>
</li>
</ul>
<p>urlpatterns = [
    ...
]</p>
<p>if settings.DEBUG:
    import debug_toolbar
    urlpatterns += [
        path(&#39;<strong>debug</strong>/&#39;, include(&#39;debug_toolbar.urls&#39;)),
    ]</p>
<pre><code>
### 6) MIDDLEWARE 에 추가
  - django 에서 MIDDLEWARE 란 
    요청을 처리하는 뷰가 호출되기 전/후에 
    호출되는 함수들
```python

MIDDLEWARE = [
    # ...
    &quot;debug_toolbar.middleware.DebugToolbarMiddleware&quot;,
    # ...
]</code></pre><h3 id="7-internal-ip-설정">7) Internal IP 설정</h3>
<ul>
<li>django 는 기본적으로 IP 에 대해서 막아버림
접속하는 IP를 넣어줘서 허용시킴</li>
<li>debug toolbar에 있는거는 민감한 정보기 때문에, LOCAL 에서만 접속할 수 있도록<pre><code>INTERNAL_IPS = [
# ...
&quot;127.0.0.1&quot;,
# ...
]</code></pre></li>
</ul>
<h3 id="주의사항">(주의사항)</h3>
<ul>
<li>웹페이지의 template 은 반드시 <code>&lt;body&gt;</code> 태그가 있어야, django-debug-toolbar가 동작
이유는? django-debug-toolbar 가 html/script 코드를 <body> 태그에 안에 추가되기 때문에
<img src="https://images.velog.io/images/engineer_km/post/f0ec6cd0-c3f4-44ef-ba05-d46f39d45909/image.png" alt=""></li>
</ul>
<h3 id="django-debug-toolbar">django-debug-toolbar</h3>
<p> <img src="https://images.velog.io/images/engineer_km/post/5f755b3d-5794-43e7-a2ef-fd4720fbf328/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/397466a4-688f-41a9-a4ec-e2c20f700e18/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/89a7d8db-3371-4e09-970a-e9717f271992/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/890f6355-240b-4ef6-aa27-9cbec7f2f2bc/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/e8a86b2b-6a49-4781-9f50-595cee36174e/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/8d5a1dbb-c00f-4fb8-b503-d0ff2f8ca99c/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/79bc0197-3b5d-44be-9759-13e88e825f24/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/a30fe4aa-6740-45c2-aa29-f3d1e0d306b0/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/e38390f4-bacc-4bb9-a0b5-b799f247915c/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/ab952cc3-b7ac-408d-8df4-ad6fc1bab60b/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/2f92e046-4cd5-4350-ac65-a2cb59a633a3/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/88d45586-683a-47eb-ba81-515f38588cb7/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/engineer_km/post/6fdd82cf-76fa-484e-a108-3219aa719348/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[django-extensions]]></title>
            <link>https://velog.io/@engineer_km/django-extensions</link>
            <guid>https://velog.io/@engineer_km/django-extensions</guid>
            <pubDate>Thu, 13 Jan 2022 13:54:48 GMT</pubDate>
            <description><![CDATA[<h2 id="django-extensions">django-extensions</h2>
<p><a href="https://django-extensions.readthedocs.io/en/latest/">https://django-extensions.readthedocs.io/en/latest/</a></p>
<h3 id="설치방법">설치방법</h3>
<pre><code>$ pip install django-extensions</code></pre><h3 id="사용법">사용법</h3>
<p> 1) django 프로젝트의 settings.py
    INSTALLED_APPS 에 &#39;django_extensions&#39; 추가</p>
<pre><code class="language-python">
INSTALLED_APPS = [
    ...
    &#39;django_extensions&#39;,

]</code></pre>
<p> 2)  python manage.py shell_plus --print-sql 실행</p>
<ul>
<li>사용할 Class, Fcuntion 을 자동으로 import 시켜줌<ul>
<li>단, from 위치는 다르더라도 import 시킬 Class 가 중복이 되면 Overwirte 되서, 따로 Import 시켜줘야됨.</li>
</ul>
</li>
<li>실행할때 SQL 문이 출력됨</li>
</ul>
<pre><code class="language-python">
(venv) # python manage.py shell_plus --print-sql

# Shell Plus Model Imports
from instagram.models import Post
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.models import Session
# Shell Plus Django Imports
from django.core.cache import cache
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import transaction
from django.db.models import Avg, Case, Count, F, Max, Min, Prefetch, Q, Sum, When
from django.utils import timezone
from django.urls import reverse
from django.db.models import Exists, OuterRef, Subquery
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
(InteractiveConsole)
&gt;&gt;&gt;
&gt;&gt;&gt;
&gt;&gt;&gt; Post.objects.all()
SELECT &quot;instagram_post&quot;.&quot;id&quot;,
       &quot;instagram_post&quot;.&quot;message&quot;,
       &quot;instagram_post&quot;.&quot;photo&quot;,
       &quot;instagram_post&quot;.&quot;is_public&quot;,
       &quot;instagram_post&quot;.&quot;created_at&quot;,
       &quot;instagram_post&quot;.&quot;updated_at&quot;
  FROM &quot;instagram_post&quot;
 ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC
 LIMIT 21
Execution time: 0.001612s [Database: default]
&lt;QuerySet [&lt;Post: 세번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 첫번째 메세지&gt;]&gt;
&gt;&gt;&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[django QuerySet 기본]]></title>
            <link>https://velog.io/@engineer_km/%EA%B8%B0%EB%B3%B8-django-QuerySet</link>
            <guid>https://velog.io/@engineer_km/%EA%B8%B0%EB%B3%B8-django-QuerySet</guid>
            <pubDate>Wed, 12 Jan 2022 13:18:46 GMT</pubDate>
            <description><![CDATA[<h2 id="model-manager">Model Manager</h2>
<p>데이터베이스의 질의 인터페이스를 제공
디폴트 Manager 로서 ModelCls.objects 가 제공</p>
<pre><code class="language-python"># SELCT * FROM app_model;
ModelsCls.objects.all()


# SELCT * FROM app_model ORDER BY ID DESC LIMIT 10;
ModelsCls.objects.all().order_by(&#39;-id&#39;)[:10]


# INSERT INTO app_model (title) VALUES (&quot;New Title&quot;);

ModelsCls.objects.create(title=&quot;New Title&quot;)</code></pre>
<pre><code>
&gt;&gt;&gt; qs = Post.objects.all()

&gt;&gt;&gt; print(qs)
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 세번째 메세지&gt;]&gt;
&gt;&gt;&gt;

&gt;&gt;&gt; print(type(qs))
&lt;class &#39;django.db.models.query.QuerySet&#39;&gt;
&gt;&gt;&gt; 

print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot;
&gt;&gt;&gt;</code></pre><h3 id="오름차순">오름차순</h3>
<ul>
<li>단, 여러개의 필드를 정렬할 수 있지만
1~2개 정도만 정렬을하고
그 이상은 성능저하를 야기 시킬수 있기 때문에
사용하지 않는다.
ex) order_by(&#39;id&#39;, &#39;is_pulic&#39;)</li>
</ul>
<pre><code>&gt;&gt;&gt; qs = Post.objects.all().order_by(&#39;id&#39;)


&gt;&gt;&gt; print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; ORDER BY &quot;instagram_post&quot;.&quot;id&quot; ASC
&gt;&gt;&gt;

&gt;&gt;&gt; qs
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 세번째 메세지&gt;]&gt;
&gt;&gt;&gt;

## id 에 대해서 내림차순
&gt;&gt;&gt; qs = Post.objects.all().order_by(&#39;-id&#39;)

&gt;&gt;&gt; print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC


&gt;&gt;&gt; qs
&lt;QuerySet [&lt;Post: 세번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 첫번째 메세지&gt;]&gt;
&gt;&gt;&gt;</code></pre><h3 id="내림차순">내림차순</h3>
<ul>
<li>단, 여러개의 필드를 정렬할 수 있지만
1~2개 정도만 정렬을하고
그 이상은 성능저하를 야기 시킬수 있기 때문에
사용하지 않는다.
ex) order_by(&#39;-id&#39;, &#39;-is_pulic&#39;)<pre><code>&gt;&gt;&gt; qs = Post.objects.all().order_by(&#39;-id&#39;)[:2]

</code></pre></li>
</ul>
<blockquote>
<blockquote>
<blockquote>
<p>print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC LIMIT 2</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs
&lt;QuerySet [&lt;Post: 세번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>
### 순회 가능 (for, loop 가능하다)</code></pre><blockquote>
<blockquote>
<blockquote>
<p>for post in qs:
...     print(post.id, post.message, post.created_at)
...     print(&quot;id: {id}, message: {message} {created_at}&quot;.format(**post.<strong>dict</strong>))</p>
</blockquote>
</blockquote>
</blockquote>
<p>...
4 세번째 메세지 2022-01-12 11:07:37.336965+00:00
id: 4, message: 세번째 메세지 2022-01-12 11:07:37.336965+00:00
2 두번째 메세지 2022-01-11 10:56:29.125509+00:00
id: 2, message: 두번째 메세지 2022-01-11 10:56:29.125509+00:00</p>
<blockquote>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
</blockquote>
<pre><code>
### QuerySet은 Chaining 을 지원
  - 속성에 따라서 filter 를 사용하는 것이 다름.
    - (ex) message__startswith=&#39;첫번째&#39;
      message 필드에서 첫번째로 시작하는 것을 필터링

  - TIP. 한줄이 길어지면 `\` 사용</code></pre><blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all().filter(message__startswith=&#39;첫번째&#39;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE &quot;instagram_post&quot;.&quot;message&quot; LIKE 첫번째% ESCAPE &#39;&#39;</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all().filter(message__icontains=&#39;첫번째&#39;).order_by(&#39;-id&#39;)
print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE &quot;instagram_post&quot;.&quot;message&quot; LIKE %첫번째% ESCAPE &#39;&#39; ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC
qs
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()<br>...      .filter(message__icontains=&#39;첫번째&#39;)<br>...      .order_by(&#39;-id&#39;)</p>
<p>qs
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>

## QuerySet
- SQL 을 생성해주는 인터페이스
- 순회가능한 객체 (for, loop 에서 사용 가능)
- Model Manager 를 통해, 해당 Model 에 대한 QuerySet 을 획득
  - Post.objects.all() 코드는 &quot;SELECT * FROM post ...;&quot;
  - Post.objects.create(...) 코드는 &quot;INSERT INTO ...;&quot;
 - QuerySet은 Chaining 을 지원
   - Post.objects.all()**.**filter(...)**.**exclude(...)**.**filter(...)

 - QuerySet은 Lazy한 특성
   - QuerySet을 만드는 동안에는 DB접근을 하지 않는다.
   - 실제로 데이터가 필요한 시점에 접근을 합니다.
     - 데이터가 필요한 시점은 언제?
       1) queryset
          - repr : 파이썬 인터프리터를 위한 것, API를 인터프리터로 사용할 때 즉시 결과를 볼 수 있다.

       2) print(queryset)
       3) list(queryset)
       4) for instance in queryset: print(instance)


## 다양한 조회 요청 방법 (SECLT SQL 생성)


### 조건을 추가한 Queryset, 획득할 준비

 - queryset.filter(...) -&gt; queryset
 - queryset.exclude(...) -&gt; queryset
</code></pre><h1 id="filter---where-문">filter  : WHERE 문</h1>
<blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all().filter(message__icontains=&#39;첫번째&#39;).order_by(&#39;-id&#39;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE &quot;instagram_post&quot;.&quot;message&quot; LIKE %첫번째% ESCAPE &#39;&#39; ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code></code></pre><h1 id="exclude---where-not-문">exclude  : WHERE NOT 문</h1>
<blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all().exclude(message__icontains=&#39;첫번째&#39;).order_by(&#39;-id&#39;)
print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE NOT (&quot;instagram_post&quot;.&quot;message&quot; LIKE %첫번째% ESCAPE &#39;&#39;) ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC
qs
&lt;QuerySet [&lt;Post: 세번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>



 ### 특정 모델객체 1개 획득을 시도

  - queryset[숫자인덱스]
    - 모델 객체 혹은 예외발생 (IndexError)</code></pre><blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()
qs[1]
&lt;Post: 두번째 메세지&gt;
qs[0]
&lt;Post: 첫번째 메세지&gt;</p>
<p>qs[3]
Traceback (most recent call last):
  File &quot;<console>&quot;, line 1, in <module>
  File &quot;/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py&quot;, line 318, in <strong>getitem</strong>
    return qs._result_cache[0]
IndexError: list index out of range</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>

  - queryset.get(...)
     - 모델 객체 혹은 예외 발생 (DoseNotExist, MultipleObjectsReturned)
</code></pre><blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()
qs.get(pk=1)
&lt;Post: 첫번째 메세지&gt;
qs.get(pk=2)
&lt;Post: 두번째 메세지&gt;</p>
<p>qs.get(pk=3)
Traceback (most recent call last):
  File &quot;<console>&quot;, line 1, in <module>
  File &quot;/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py&quot;, line 435, in get
    raise self.model.DoesNotExist(
instagram.models.Post.DoesNotExist: Post matching query does not exist.</p>
</blockquote>
</blockquote>
</blockquote>
<h1 id="lte-less-than-equal">lte: less than equal</h1>
<blockquote>
<blockquote>
<blockquote>
<p>qs.get(id__lte=2)<br>Traceback (most recent call last):
  File &quot;<console>&quot;, line 1, in <module>
  File &quot;/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py&quot;, line 439, in get
    raise self.model.MultipleObjectsReturned(
instagram.models.Post.MultipleObjectsReturned: get() returned more than one Post -- it returned 2!</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>
  - queryset.first()
     - 모델 객체 혹은 None
</code></pre><blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()
qs.first()
&lt;Post: 첫번째 메세지&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs.none()
&lt;QuerySet []&gt;
qs.none().first()</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>  - queryset.last()
     - 모델 객체 혹은 None</code></pre><blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()
qs.last()
&lt;Post: 세번째 메세지&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>

### filter &lt; - &gt; exclude
 - filter 와 exclude는 서로 반대되는 개념
 - SELECT 쿼리에 WHERE 조건 추가
 - 인자로 &#39;필드명 = 조건값&#39; 지정
 - 1개 이상의 인자 지정 -&gt; 모두 AND 조건으로 묶임.
</code></pre><blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()
qs = qs.exclude(id__gte=2, message__icontains=&quot;첫번째&quot;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE NOT (&quot;instagram_post&quot;.&quot;id&quot; &gt;= 2 AND &quot;instagram_post&quot;.&quot;message&quot; LIKE %첫번째% ESCAPE &#39;&#39;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 세번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs = qs.filter(id__gte=2, message__icontains=&quot;첫번째&quot;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE (&quot;instagram_post&quot;.&quot;id&quot; &gt;= 2 AND &quot;instagram_post&quot;.&quot;message&quot; LIKE %첫번째% ESCAPE &#39;&#39;)
qs
&lt;QuerySet []&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>
 - OR 조건을 묶으려면, django.db.models.Q 활용</code></pre><blockquote>
<blockquote>
<blockquote>
<p>from django.db.models import Q</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()
qs = qs.filter(Q(id__gte=2) | Q(message__icontains=&quot;첫번째&quot;))</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs.query
&lt;django.db.models.sql.query.Query object at 0x7f68dbe7ab80&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE (&quot;instagram_post&quot;.&quot;id&quot; &gt;= 2 OR &quot;instagram_post&quot;.&quot;message&quot; LIKE %첫번째% ESCAPE &#39;&#39;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 세번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs = Post.objects.all()</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>cond = Q(id__gte=2) | Q(message__icontains=&#39;첫번째&#39;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs = qs.filter(cond)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>print(qs.query)
SELECT &quot;instagram_post&quot;.&quot;id&quot;, &quot;instagram_post&quot;.&quot;message&quot;, &quot;instagram_post&quot;.&quot;photo&quot;, &quot;instagram_post&quot;.&quot;is_public&quot;, &quot;instagram_post&quot;.&quot;created_at&quot;, &quot;instagram_post&quot;.&quot;updated_at&quot; FROM &quot;instagram_post&quot; WHERE (&quot;instagram_post&quot;.&quot;id&quot; &gt;= 2 OR &quot;instagram_post&quot;.&quot;message&quot; LIKE %첫번째% ESCAPE &#39;&#39;)</p>
</blockquote>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>qs
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 세번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>

## 필드 타입별 다양한 조건 매칭
주의) 데이터베이스 타입에 따라서 생성되는 SQL이 다릅니다.

### 숫자/날짜/시간 필드의 경우
1) 필드명__lt = 조건값 
  - less than
  - 필드명 &lt; 조건값
2) 필드명__lte = 조건값
  - less than equal
  - 필드명 &lt;= 조건값
3) 필드명__gt = 조건값
  - greater than
  - 필드명 &gt; 조건값
4) 필드명__gte = 조건값
  - greater than equal
  - 필드명 &gt;= 조건값

### 문자열 필드의 경우
1) 필드명__startswith = 조건값
  - 필드명 LIKE &quot;조건값%&quot;
2) 필드명__istartswith = 조건값
  - 필드명 ILIKE &quot;조건값%&quot;
  - I는 ignore case : 대소문자 구별 안함
3) 필드명__endswith = 조건값
  - 필드명 LIKE &quot;%조건값&quot;
4) 필드명__iendswith = 조건값
  - 필드명 ILIKE &quot;%조건값&quot;
  - I는 ignore case : 대소문자 구별 안함
5) 필드명__contains = 조건값
  - 필드명 LIKE &quot;%조건값%&quot;
6) 필드명__icontains = 조건값
  - 필드명 ILIKE &quot;%조건값%&quot;
  - I는 ignore case : 대소문자 구별 안함



### 정렬 조건 추가
#### SELECT 쿼리에 ORDER BY 추가

정렬 조건을 추가하지 않으면 일관된 순서를 보장받을 수 없음
##### DB에서 다수의 필드에 대한 정렬을 지원

하지만 가급적 단일 필드로 하는 것이 성능에 이익
시간숙/역순 정렬이 필요할 경우, ID 필드를 활용

##### 정렬 조건을 지정하는 2가지 방법
  1. 모델 클래스의 Meta 속성으로 ordering 설정: list 로 지정</code></pre><p>class Post(models.Model):</p>
<pre><code>class Meta:
    ordering = [&#39;-id&#39;]</code></pre><pre><code>
  2. 모든 Queryset에 order_by(...)에 지정

    - queryset에서 order_by 를 지정했다면,
      모델 클래스의 Meta 속성을 통해서 기본 정렬한게 무시됨.</code></pre><blockquote>
<blockquote>
<blockquote>
<p>Post.objects.all().order_by(&#39;created_at&#39;)
SELECT &quot;instagram_post&quot;.&quot;id&quot;,
       &quot;instagram_post&quot;.&quot;message&quot;,
       &quot;instagram_post&quot;.&quot;photo&quot;,
       &quot;instagram_post&quot;.&quot;is_public&quot;,
       &quot;instagram_post&quot;.&quot;created_at&quot;,
       &quot;instagram_post&quot;.&quot;updated_at&quot;
  FROM &quot;instagram_post&quot;
 ORDER BY &quot;instagram_post&quot;.&quot;created_at&quot; ASC
 LIMIT 21
Execution time: 0.000249s [Database: default]
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;, &lt;Post: 세번째 메세지&gt;]&gt;</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code>

### QuerySet에 범위 조건 추가

#### 슬라이싱을 통한 범위조건 추가
 - SELECT 쿼리에 OFFSET/LIMIT
 - 슬라이싱 지원됨. 단, 역순슬라이싱은 지원 안됨
   - 왜 지원이 안되는가? 데이터베이스에서 지원하지 않기 때문에
</code></pre><p> 객체[start:stop:step]</p>
<ul>
<li>offset : start</li>
<li>LIMIT : stop - start</li>
<li>STEP은 Query에 대응되지 않음.<pre><code></code></pre></li>
</ul>
<pre><code>&gt;&gt;&gt; Post.objects.all()[:2]
SELECT &quot;instagram_post&quot;.&quot;id&quot;,
       &quot;instagram_post&quot;.&quot;message&quot;,
       &quot;instagram_post&quot;.&quot;photo&quot;,
       &quot;instagram_post&quot;.&quot;is_public&quot;,
       &quot;instagram_post&quot;.&quot;created_at&quot;,
       &quot;instagram_post&quot;.&quot;updated_at&quot;
  FROM &quot;instagram_post&quot;
 ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC
 LIMIT 2
Execution time: 0.000153s [Database: default]
&lt;QuerySet [&lt;Post: 세번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;]&gt;



&gt;&gt;&gt; Post.objects.all()[-2:]
Traceback (most recent call last):
  File &quot;&lt;console&gt;&quot;, line 1, in &lt;module&gt;
  File &quot;/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py&quot;, line 294, in __getitem__
    assert ((not isinstance(k, slice) and (k &gt;= 0)) or
AssertionError: Negative indexing is not supported.
&gt;&gt;&gt;


&gt;&gt;&gt; Post.objects.all().order_by(&#39;id&#39;)[:2]
SELECT &quot;instagram_post&quot;.&quot;id&quot;,
       &quot;instagram_post&quot;.&quot;message&quot;,
       &quot;instagram_post&quot;.&quot;photo&quot;,
       &quot;instagram_post&quot;.&quot;is_public&quot;,
       &quot;instagram_post&quot;.&quot;created_at&quot;,
       &quot;instagram_post&quot;.&quot;updated_at&quot;
  FROM &quot;instagram_post&quot;
 ORDER BY &quot;instagram_post&quot;.&quot;id&quot; ASC
 LIMIT 2
Execution time: 0.000208s [Database: default]
&lt;QuerySet [&lt;Post: 첫번째 메세지&gt;, &lt;Post: 두번째 메세지&gt;]&gt;
&gt;&gt;&gt;</code></pre><ul>
<li><p>step 이 들어가면
STEP 사용하지 않으면 QuerySet 으로 return 됨.
STEP을 사용하면 List 로 return 이됨.</p>
<ul>
<li>step 사용 X : &lt;QuerySet [&lt;Post: 두번째 메세지&gt;, &lt;Post: 첫번째 메세지&gt;]&gt;</li>
<li>step 사용 O: [&lt;Post: 두번째 메세지&gt;, &lt;Post: 첫번째 메세지&gt;]</li>
</ul>
</li>
<li><p>STEP은 SQL에 개념이 없음.
STEP은 django의 QuerySet이 처리함.</p>
</li>
<li><p>밑에 예제를 기준으로 [1:3] 만큼 SQL로 처리를 한 다음 django가 <code>2</code>(STEP) 을 처리함.</p>
<pre><code>&gt;&gt;&gt; Post.objects.all()[1:3]
SELECT &quot;instagram_post&quot;.&quot;id&quot;,
    &quot;instagram_post&quot;.&quot;message&quot;,
    &quot;instagram_post&quot;.&quot;photo&quot;,
    &quot;instagram_post&quot;.&quot;is_public&quot;,
    &quot;instagram_post&quot;.&quot;created_at&quot;,
    &quot;instagram_post&quot;.&quot;updated_at&quot;
FROM &quot;instagram_post&quot;
ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC
LIMIT 2
OFFSET 1
Execution time: 0.000204s [Database: default]
&lt;QuerySet [&lt;Post: 두번째 메세지&gt;, &lt;Post: 첫번째 메세지&gt;]&gt;
&gt;&gt;&gt;

</code></pre></li>
</ul>
<blockquote>
<blockquote>
<blockquote>
<p>Post.objects.all()[1:3:1]
SELECT &quot;instagram_post&quot;.&quot;id&quot;,
       &quot;instagram_post&quot;.&quot;message&quot;,
       &quot;instagram_post&quot;.&quot;photo&quot;,
       &quot;instagram_post&quot;.&quot;is_public&quot;,
       &quot;instagram_post&quot;.&quot;created_at&quot;,
       &quot;instagram_post&quot;.&quot;updated_at&quot;
  FROM &quot;instagram_post&quot;
 ORDER BY &quot;instagram_post&quot;.&quot;id&quot; DESC
 LIMIT 2
OFFSET 1
Execution time: 0.000120s [Database: default]
[&lt;Post: 두번째 메세지&gt;, &lt;Post: 첫번째 메세지&gt;]</p>
</blockquote>
</blockquote>
</blockquote>
<pre><code></code></pre>]]></description>
        </item>
    </channel>
</rss>