<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>개벨로그</title>
        <link>https://velog.io/</link>
        <description>back-end 개발자</description>
        <lastBuildDate>Sun, 23 Jan 2022 13:52:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>개벨로그</title>
            <url>https://images.velog.io/images/may_soouu/profile/4b8d69f1-e4c8-469d-b4fc-20a99f169338/sooh.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 개벨로그. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/may_soouu" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[✋ mac에서 mongo db 설치]]></title>
            <link>https://velog.io/@may_soouu/mac-mongo-db-%EC%84%A4%EC%B9%98-f0xveyxj</link>
            <guid>https://velog.io/@may_soouu/mac-mongo-db-%EC%84%A4%EC%B9%98-f0xveyxj</guid>
            <pubDate>Sun, 23 Jan 2022 13:52:01 GMT</pubDate>
            <description><![CDATA[<h3 id="몽고-db-설치하기">몽고 db 설치하기</h3>
<p>brew가 터미널에 설치되어 있어야합니다.</p>
<h3 id="1-brew-tap-명령어로-패키지-저장소를-확보">1. brew tap 명령어로 패키지 저장소를 확보</h3>
<pre><code>brew tap mongodb/brew</code></pre><h3 id="2-몽고db-최신-버전-찾기">2. 몽고db 최신 버전 찾기</h3>
<pre><code>brew search mongodb</code></pre><h3 id="3-설치">3. 설치</h3>
<pre><code>brew install mongodb-community@4.4</code></pre><h3 id="4-실행">4. 실행</h3>
<pre><code>brew services start mongodb-community@4.4</code></pre><h3 id="5--프로세스-확인">5.  프로세스 확인</h3>
<pre><code>ps aux | grep -v grep | grep mongod</code></pre><h3 id="6-버전-확인">6. 버전 확인</h3>
<pre><code>mongo --version</code></pre><p>위와 같이 입력하면 버전 정보가 나오는 사람도 있지만,
 아래와 같이 찾을 수 없다는 명령어가 나올 수도 있습니다.
 아래의 명령어가 나온다면 zsh, bash_shell 등 본인이 쓰고 있는 <code>터미널 환경에 맞춰서 환경변수를 추가</code>해주면 됩니다.</p>
<pre><code>zsh: command not found mongo</code></pre><h3 id="7-환경변수-설정">7. 환경변수 설정</h3>
<p>아래의 예는 zsh 기준입니다.</p>
<pre><code>1. 편집기 열고, 
vi ~/.zshrc

2. 아래와 같이 mongodb 환경변수 명령어 입력하고,
export PATH=&quot;$PATH:/usr/local/opt/mongodb-community@4.4/bin&quot;

3. 환경변수 적용
source ~./zshrc</code></pre><blockquote>
<p>local 뒤의 경로는 설치 경로에 따라서 다를 수가 있습니다. 해당 경로는 
<code>ps aux | grep -v grep | grep mongod</code>
를 입력했을 때 나오는 경로에서
usr/local <del>~</del> /bin &lt;&lt; 이 경로를 입력하면 됩니다. </p>
</blockquote>
<h3 id="8-로컬에서-접속하기">8. 로컬에서 접속하기</h3>
<pre><code> mongo &quot;mongodb://&lt;username&gt;:&lt;usrepassword&gt;@IP:PORT&quot;</code></pre><h3 id="9-유저-비밀번호-변경하기">9. 유저 비밀번호 변경하기</h3>
<pre><code>&gt; use admin
switched to db admin
admin&gt; db.changeUserPassword(&#39;username&#39;, passwordPrompt())
Enter password
*************</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[AWS Elastic Beanstalk + Django 배포]]></title>
            <link>https://velog.io/@may_soouu/AWS-Elastic-Beanstalk-Django-%EB%B0%B0%ED%8F%AC-cwztj07o</link>
            <guid>https://velog.io/@may_soouu/AWS-Elastic-Beanstalk-Django-%EB%B0%B0%ED%8F%AC-cwztj07o</guid>
            <pubDate>Sun, 06 Jun 2021 12:10:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-aws-iam-생성">1. AWS IAM 생성</h2>
<p> 1) aws &gt; iam &gt; 엑세스 관리 &gt; 사용자 &gt; 사용자 추가
 2) 사용자 이름 입력 / 프로그래밍 방식 엑세스 클릭
 3) 기존 정책 직접연결 </p>
<pre><code>AmazonEC2FullAccess
AmazonS3FullAccess
AmazonESFullAccess</code></pre><p>4) 다음 &gt; 사용자 만들기 &gt; csv 파일 다운받</p>
<h2 id="2-eb-cli-설치">2. EB CLI 설치</h2>
<pre><code class="language-python">$ brew install awsebcli

pip 으로 설치해도 되는데, 나는 pip 으로 설치했을 때 잘 안됐다.
brew 로 설치하면 따로 환경변수 설정을 안해줘도 된다.

# 설치 확인
$ eb --version</code></pre>
<h2 id="3-django-프로젝트-만들기">3. django 프로젝트 만들기</h2>
<pre><code class="language-python"># 프로젝트 폴더에서 가상환경 끈 상태로 진행!! 
$ eb init 

리전 선택하기. 나의 경우 서울 10번 선택

1 에서 만든 aws id pw 입력하기 

애플리케이션 이름 입력하기
</code></pre>
<h2 id="4-requirementstxt-만들기">4. requirements.txt 만들기</h2>
<pre><code class="language-python">$ pip freeze &gt; requirements.txt</code></pre>
<h2 id="5-ebextensions-설정">5. .ebextensions 설정</h2>
<pre><code class="language-python">manage.py 파일 있는 경로에서

$ mkdir .ebextensions
$ cd .ebextensions

위 파일 경로에서 아래 파일 추가 및 내용 추가

$ vi django.config

아래 내용 추가! 

option_settings:
aws:elasticbeanstalk:container:python:
WSGIPath: test_project/wsgi.py

test_project 이 부분은 본인 프로젝트 넣기
나같은 경우 처음에 두번째줄이랑 세번째줄을 들여쓰기 했더니 계속 json 에러가 났다


:wq 로 파일 저장 및 나오기
</code></pre>
<h2 id="6-eb-create-로-환경-생성하기">6. eb-create 로 환경 생성하기</h2>
<pre><code>$ eb-create 만들려는 환경 이름

safe to Ctrl+C 가 중간중간 나오는데 누르지 말고 다 설치 될 때까지 기다리기
설치가 완료되면 터미널 화면 마지막에

Successfully launched environment: 환경이름

위와 같이 들 것이다.
</code></pre><h2 id="7-eb-status">7. eb-status</h2>
<pre><code>$ eb-status 

위 명령어로 도메인 명 확인
CNAME 부분에 있다.

해당 부분을 복사 후
프로젝트 파일 &gt; settings.py 에 
ALLOWED_HOSTS = [&#39;이 안에 넣어준다.&#39;]</code></pre><h2 id="8-배포">8. 배포</h2>
<pre><code>$ eb deploy 로 배포하기</code></pre><h2 id="9-배포-확인">9. 배포 확인</h2>
<pre><code>$ eb open </code></pre><p>출처
<a href="https://djangojeng-e.github.io/2020/09/24/AWS-Elastic-Beanstalk-%EB%B0%B0%ED%8F%AC-6/">출처1</a>
<a href="https://velog.io/@_gyullbb/Elastic-Beanstalk-%EB%B0%B0%ED%8F%AC">출처2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL 문법3]]></title>
            <link>https://velog.io/@may_soouu/SQL-%EB%AC%B8%EB%B2%953</link>
            <guid>https://velog.io/@may_soouu/SQL-%EB%AC%B8%EB%B2%953</guid>
            <pubDate>Wed, 19 May 2021 03:58:15 GMT</pubDate>
            <description><![CDATA[<h2 id="1-round칼럼명값-n">1. ROUND(칼럼명/값, n)</h2>
<p>반올림 ( 소수점 n째 자리까지 남기고 반환 )</p>
<pre><code>ROUND(8.765, 2) 
 &gt; 8.77</code></pre><h2 id="2-truncate칼럼명값-n">2. TRUNCATE(칼럼명/값, n)</h2>
<p>n 자리수까지 남기고 다 버리기</p>
<pre><code>TRUNCATE(8.765, 2)
 &gt; 8.76</code></pre><h2 id="3-power칼럼명값-n">3. POWER(칼럼명/값, n)</h2>
<p> 값을 n 제곱해서 반환</p>
<pre><code>power(칼럼명/값, n)
 = pow(칼럼명/값, n)

ex. power(2,3) = 8</code></pre><h2 id="4-sqrt">4. SQRT</h2>
<p>값의 제곱근을 반환 (어떤 수를 제곱했을 때 그 수가 나오냐)</p>
<pre><code>SQRT(4) = 2</code></pre><h2 id="5-mod칼럼명값-n">5. MOD(칼럼명/값, n)</h2>
<p>값을 n으로 나누었을 때 나머지를 반환</p>
<pre><code>MOD(칼럼명/값, n)
값을 n으로 나누었을 때의 나머지를 반환
 = 칼럼명 % n &gt;&gt; 이것도 나머지 구하는 거 !

ex. mod(4,2) = 0
    mod(5,2) = 1

ex. ID 컬럼이 홀수 인 것만 찾고 싶다

select *
from ex_table
where ID % 2 = 1;
</code></pre><h2 id="6-abs칼럼명값">6. ABS(칼럼명/값)</h2>
<p>값의 절대값을 반환</p>
<h2 id="7-lower--upper">7. LOWER / UPPER</h2>
<p>LOWER : 모든 문자를 소문자로 
UPPER : 모든 문자를 대문자로</p>
<pre><code>SELECT lower(memberID)
FROM sample_table;

SELECT UPPER(memberID)
FROM sample_table;</code></pre><h2 id="8-replace칼럼명문자열-패턴1-패턴2">8. REPLACE(칼럼명/문자열, 패턴1, 패턴2)</h2>
<p>문자열에 포함된 패턴1을 패턴2로 대체해 반환</p>
<pre><code>SELECT REPLACE(MemberID, &#39;A&#39;, &#39;B&#39;)
FROM sample_table;</code></pre><h2 id="9-concat칼럼명문자열1-칼럼명문자열2">9. CONCAT(칼럼명/문자열1, 칼럼명/문자열2....)</h2>
<p>여러개의 문자열을 차례대로 연결해 하나의 문자열로 반환</p>
<pre><code>SELECT REPLACE(ID, &#39;:&#39;, MemberID) AS MEMBER
FROM sample_table;

ID   MemberID
1     A
2     B
3     C


result

MEMBER
1:A
2:B
3:C
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[🧷 SQL 문법 2]]></title>
            <link>https://velog.io/@may_soouu/sql-2</link>
            <guid>https://velog.io/@may_soouu/sql-2</guid>
            <pubDate>Tue, 11 May 2021 03:02:17 GMT</pubDate>
            <description><![CDATA[<h2 id="1-count-갯수-세기">1. COUNT 갯수 세기</h2>
<pre><code class="language-sql">SELECT COUNT (*)
FROM 테이블 이름;

 &gt; Products 테이블에 있는 레코드의 갯수를 세라


특정 칼럼만 세고 싶다면?
SELECT COUNT(세고 싶은 칼럼)
FROM 테이블 이름


중복제거 하고 센다면?
SELECT COUNT(DISTINCT 칼럼)
FROM 테이블 이름
</code></pre>
<h2 id="2-sum--avg">2. SUM / AVG</h2>
<pre><code class="language-sql">합계
SELECT SUM(칼럼)
FROM 테이블 이름

평균
SELECT AVG(칼럼)
FROM 테이블 이름

만약 평균 내려는 값에 NULL 값이 있을 때
  1) NULL 을 데이터가 없다고 해석하고 평균 낸다면

  SELECT AVG(칼럼)
  FROM 테이블 이름


  2) NULL 을 0으로 해석한다면,

  SELECT SUM(칼럼)/count(*)
  FROM 테이블 이름</code></pre>
<h2 id="3-최대--최소">3. 최대 / 최소</h2>
<pre><code class="language-sql">SELECT min(price)
SELECT max(price)</code></pre>
<h2 id="4-그룹---group-by">4. 그룹 - GROUP BY</h2>
<pre><code class="language-sql">ex. 공급자별 평균 가격

SELECT SupperlierID, AVG(Price)
FROM Products
GROUP BY SupperlierID</code></pre>
<h2 id="5-그-외">5. 그 외</h2>
<pre><code class="language-sql">평균 낸 결과에 대해 필터링을 걸고 싶으면

SELECT SupperlierID, AVG(Price)
FROM Products
GROUP BY SupperlierID
HAVING AVG(Price) &gt;= 100 

 &gt; WHERE 절 쓰면 안됨 !! HAVING 써야 함

------------------------------------------------------------
select SupperlierID, 
       AVG(Price) AS avg_price
      # as 는 별명 붙이기
</code></pre>
<p><a href="https://www.inflearn.com/course/sql-%EC%A4%91%EA%B8%89-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4">출처_인프런 인강</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[⏱ django + redis]]></title>
            <link>https://velog.io/@may_soouu/django-redis</link>
            <guid>https://velog.io/@may_soouu/django-redis</guid>
            <pubDate>Wed, 07 Apr 2021 09:54:55 GMT</pubDate>
            <description><![CDATA[<p>django + redis 조합으로 쿼리 성능 향상 시키기!</p>
<h4 id="🖥--mac-기준--🖥">🖥  mac 기준  🖥</h4>
<h2 id="1-redis-설치">1. redis 설치</h2>
<pre><code class="language-python">터미널에서 

$ brew install redis

위의 명령어로 redis 설치하기</code></pre>
<h2 id="2-redis-셋팅">2. redis 셋팅</h2>
<pre><code class="language-python">설치 후

$ cd /usr/local/etc

위의 경로에 들어가서

$ vi redis.conf

파일 열기
</code></pre>
<h2 id="3-비밀번호와-외부-접속-설정">3. 비밀번호와 외부 접속 설정</h2>
<pre><code class="language-python">requirepass foobared
라고 되어 있는 부분 찾아서

requirepass 내가 설정하려는 비밀번호로
ex. requirepass abcde

-------------------------------------

bind 127.0.0.1 로 되어 있는 부분 찾아서

bind 0.0.0.0 으로 수정해주기

:wq!   &lt;- 명령어로 파일 저장 후 나오기
</code></pre>
<h2 id="4-레디스-실행해보기">4. 레디스 실행해보기</h2>
<pre><code>$ redis-server</code></pre><p><img src="https://images.velog.io/images/may_soouu/post/c6a1058d-f911-4222-b602-ee65d03805e7/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-07%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.41.55.png" alt=""></p>
<p>위의 그림처럼 나오면 성공!</p>
<pre><code>터미널 하나 더 키고,

$ redis-cli 

입력하면 아래와 같이 나온다.
</code></pre><p><img src="https://images.velog.io/images/may_soouu/post/0533386e-39d9-49a1-9d6d-9085f0c0cc1d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-07%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.43.42.png" alt="">
레디스 키에 저장된 값이 없어서 조회해보면 empty array로 나온다.</p>
<h2 id="5-django-세팅">5. django 세팅</h2>
<pre><code class="language-python"># project파일/settings.py

CACHES = {
    &quot;default&quot;: {
        &quot;BACKEND&quot;: &quot;django_redis.cache.RedisCache&quot;,
        &quot;LOCATION&quot;: &quot;redis://127.0.0.1:6379&quot;,
        &quot;OPTIONS&quot;: {
            &quot;CLIENT_CLASS&quot;: &quot;django_redis.client.DefaultClient&quot;,
        }
    }
}

위의 내용 넣어주기
</code></pre>
<h2 id="6-views-테스트">6. views 테스트</h2>
<pre><code class="language-python"># 앱/views.py

import redis
from django.core.cache import cache

class ServiceType(APIView):
    def get(self,request):
        types = request.GET.get(&quot;type&quot;, None)

        start = time.time()

        today = datetime.datetime.now().date()
        if types == &#39;E&#39; or types == &#39;N&#39;:

            get_redis = cache.get(&#39;test&#39;)

            if not get_redis:

                eventlist = Event.objects.filter( type=types)
                contentslist = []
                for contents in eventlist:
                    if contents.active == &#39;Y&#39;:
                        if types == &#39;E&#39;:
                            if contents.end_date &gt; today:
                                badge = f&#39;{contents.end_date.month}/{contents.end_date.day}까지&#39;
                            else:
                                badge  = &#39;종료&#39;

                        elif types == &#39;N&#39;:
                            badge = contents.badge

                        contentslist.append ( {
                            &#39;id&#39; : str(contents.id),
                            &#39;title&#39; : contents.title,
                            &#39;badge&#39; : badge,
                            &#39;startDate&#39; : contents.start_date
                        } )

                data = {
                    &#39;contentsList&#39; : contentslist
                }

                cache.set(&#39;test&#39;, data)

                end = time.time()
                print(&#39;레디스에 키 없을 때 조회 시간 : &#39;, end-start)

                return BaseResponse(resultcode=int(&#39;00&#39;), data=data, status=200)

            end = time.time()
            print(&#39;레디스에 키 있을 때 조회 시간 : &#39;, end-start)
            return BaseResponse(resultcode=int(&#39;00&#39;), data=get_redis, status=200)

        return BaseResponse(resultcode=int(&#39;01&#39;), status=400)

</code></pre>
<blockquote>
<p>9번째 &amp; 10번째 줄에</p>
</blockquote>
<pre><code>get_redis = cache.get(&#39;event4&#39;)
if not get_redis:</code></pre><p>를 추가해서, 레디스에 내가 저장해 놓은 키값이 있는지 찾은 후 (get 이 조회하는 명령어)
있다면 get_redis 그대로 return
없다면, DB에서 조회 후, 해당 키 값으로 레디스에 저장하기 (set이 저장)</p>
<h3 id="6-1-레디스에-저장-안-됐을-때">6-1. 레디스에 저장 안 됐을 때</h3>
<p><img src="https://images.velog.io/images/may_soouu/post/d6cc86d9-40e2-4c75-be6e-88aa5df4fa76/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-07%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.51.37.png" alt=""></p>
<p>레디스에 키가 없는 상태기 때문에 데이터 조회하는 코드로 데이터 조회 후, 레디스에 키 저장한 후, 데이터 리턴</p>
<p><img src="https://images.velog.io/images/may_soouu/post/d2c436ca-7af2-4792-8255-6ac0a9432578/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-07%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.52.26.png" alt=""></p>
<p>redis-cli 에서 keys * 로 조회해보면 위의 실행으로 키가 저장된 걸 알 수 있다.</p>
<p>그리고 다시 조회 해보면,
<img src="https://images.velog.io/images/may_soouu/post/97ea85a6-863d-4dc7-9b84-0b4bf5ed2bda/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-07%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.53.58.png" alt=""></p>
<p>레디스에 저장된 키 값으로 데이터를 찾아서 리턴하면서 시간이 단축된 걸 알 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[✨ nginx swagger-ui ]]></title>
            <link>https://velog.io/@may_soouu/nginx-swagger-ui</link>
            <guid>https://velog.io/@may_soouu/nginx-swagger-ui</guid>
            <pubDate>Sat, 06 Feb 2021 03:08:32 GMT</pubDate>
            <description><![CDATA[<p>ec2 &gt; nginx &gt; gunicorn &gt; django 를 붙이고 나서 스웨거에 접속을 했는데, ui 가 전혀 없는 상태였습니다. 😂</p>
<p>개발자 도구로 확인해보니, not found.... 
<img src="https://images.velog.io/images/may_soouu/post/54be12cf-ce1c-424e-9836-76cc9fcb58bc/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-02-06%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.12.40.png" alt="">
nginx 를 붙이면서, static 파일들을 한 곳으로 모아줬어야 했는데 모아주지 않아서 ec2 서버로 swagger에 접속하니 ui를 찾지 못한 채 blank page 가 나왔습니다.</p>
<h2 id="1-django에서-static-은">1. django에서 static 은?</h2>
<p>django에서 _<strong>static 파일</strong>_을 보통 아래 3가지의 경우로 설정합니다.</p>
<pre><code>1. STATICFILES_DIRS
2. STATIC_URL
3. STATIC_ROOT</code></pre><h4 id="1--staticfiles_dirs">1)  STATICFILES_DIRS</h4>
<p>   개발 단계에서 사용하는 정적 파일들의 경로를 지정하는 설정입니다. 
  특정 django app 에만 사용하는 정적 파일이 있거나 여러 경로에 정적 파일들을 배치했다면, 해당 경로들을 list, tuple 로 담으면 됩니다,</p>
<pre><code class="language-python">STATICFILES_DIRS = (
    os.path.join(BASE_DIR, &#39;static&#39;),
)</code></pre>
<h4 id="2-static_url">2) STATIC_URL</h4>
<p>웹 페이지에서 사용할 정적 파일의 최상위 URL 경로입니다. 
이 경로는 디렉터리는 아니며 URL로 존재하는 단위입니다.</p>
<p>처음 django project를 생성하면 settings.py에 아래와 같이 기본값으로 설정되어 있습니다.</p>
<pre><code>STATIC_URL = &#39;/static/&#39;</code></pre><h4 id="2-static_root">2) STATIC_ROOT</h4>
<p>django 프로젝트에서 사용하는 모든 정적 파일을 한 곳에 모아넣는 경로입니다.
<code>python manage.py collectstatic</code> 이라는 명령어를 입력하여 한 곳에 모아줍니다.</p>
<p>개발 과정에선, settings.py에서 <code>DEBUG=True</code>로 설정되어 있으면 STATIC_ROOT 설정은 작용하지 않으며, STATIC_ROOT는 실 서비스 환경을 위한 설정 항목입니다. </p>
<p>django가 접근하여 다루는 곳은 <code>STATICFILES_DIRS</code> 이고, <code>STATIC_ROOT</code> 는 웹 서버가 접근 합니다.</p>
<h2 id="2-django-static-파일-모아주기">2) django static 파일 모아주기</h2>
<pre><code class="language-python">로컬이나 ec2 우분투 서버에서 장고 프로젝트 settings.py로 이동
저는 ec2 우분투 서버에 클론 받은 프로젝트 파일에서 수정했습니다.


# settings.py

STATIC_URL = &#39;/static/&#39;
STATIC_ROOT = os.path.join(BASE_DIR,&#39;static&#39;)

STATIC_URL 밑에, STATIC_ROOT 추가하기</code></pre>
<pre><code class="language-python"># 프로젝트 urls.py

urlpatterns = [
   &quot;&quot;&quot;내가 추가한 url 들 &quot;&quot;&quot;
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)



# 아래 명령어로 정적 파일 모아주기
python manage.py collectstatic</code></pre>
<p>그럼 manage.py 있는 경로에 static 폴더가 생기면서 안에 swagger 관련 js,css 등 정적 파일들이 모아져 있는 것을 볼 수 있습니다.</p>
<h2 id="3-nginx-설정">3) nginx 설정</h2>
<pre><code class="language-python"># cd /etc/nginx/sites-enabled 경로로 이동
# nginx 설정 시 만들었던 프로젝트명.py 파일 열기

sudo vi 프로젝트명.py

server {
        listen 80;
        server_name ec2퍼블릭 주소;

        charset utf-8;

        location / {
        include proxy_params;
        proxy_pass http://127.0.0.1:8000;
        }

        location /static/ {
                alias /home/ubuntu/프로젝트 경로/static/;
        }
}


&gt;&gt;&gt; static 경로 지정 : alias /home/ubuntu/프로젝트 경로/static/</code></pre>
<h2 id="4-재실행">4) 재실행</h2>
<pre><code>sudo systemctl daemon-reload
sudo systemctl restart gunicorn
sudo systemctl restart nginx</code></pre><p><a href="https://blog.hannal.com/2015/04/start_with_django_webframework_06/">참고!</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[django swagger (drf-yasg)]]></title>
            <link>https://velog.io/@may_soouu/django-swagger-drf-yasg</link>
            <guid>https://velog.io/@may_soouu/django-swagger-drf-yasg</guid>
            <pubDate>Fri, 22 Jan 2021 07:34:27 GMT</pubDate>
            <description><![CDATA[<p>rest api 문서 자동화를 위해 swagger를 사용했습니다.</p>
<h2 id="1-swagger-설치">1. swagger 설치</h2>
<pre><code>pip install -U drf-yasg
pip install flex
</code></pre><blockquote>
</blockquote>
<p><a href="https://django-rest-swagger.readthedocs.io/en/latest/">스웨거 공식문서</a>에는 
<code>pip install django-rest-swagger</code> 를 install 하라고 해서 찾아보니,
<code>django-rest-swagger</code> 은 패키지 관리가 잘 안되고 있는 것 같습니다.
그래서 <code>drf-yasg</code> 를 install 해줍니다.<br>
<code>flex</code>  : JSON schema를 체크하는 패키지</p>
<h2 id="2-settingspy-에-설정하기">2. settings.py 에 설정하기</h2>
<pre><code class="language-python"># settings.py
INSTALLED_APPS = [
    &#39;drf_yasg&#39;,  # 추가하기 
]

여기서 오전 시간을 다 썼는데, 모든 설정을 다 했는데도 런타임에러가 났었습니다.
&#39;django.contrib.auth&#39; 부분을 제가 주석 처리 해놨었어서 이 부분이 주석이 되어 있다면 꼭! 풀어주셔야 합니다.</code></pre>
<h2 id=""></h2>
<pre><code class="language-python"># 프로젝트 &gt; urls.py

from django.urls import path, include, re_path
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from django.conf import settings
from rest_framework.permissions import AllowAny
from rest_framework import routers, permissions


schema_view = get_schema_view(
    openapi.Info(
        title=&quot;project A title&quot;, # 타이틀
        default_version=&#39;v1&#39;, # 버전
        description=&quot;프로젝트 API 문서&quot;, # 설명
        terms_of_service=&quot;https://www.google.com/policies/terms/&quot;,
        contact=openapi.Contact(email=&quot;이메일&quot;),
        license=openapi.License(name=&quot;&quot;),
    ),
    validators=[&#39;flex&#39;],
    public=True,
    permission_classes=(AllowAny,)
)


urlpatterns = [
    path(r&#39;swagger(?P&lt;format&gt;\.json|\.yaml)&#39;, schema_view.without_ui(cache_timeout=0), name=&#39;schema-json&#39;),
    path(r&#39;swagger&#39;, schema_view.with_ui(&#39;swagger&#39;, cache_timeout=0), name=&#39;schema-swagger-ui&#39;),
    path(r&#39;redoc&#39;, schema_view.with_ui(&#39;redoc&#39;, cache_timeout=0), name=&#39;schema-redoc-v1&#39;),
    # django 앱 
    path(&#39;scooter&#39;, include(&#39;scooter.urls&#39;)),
    path(&#39;bus&#39;, include(&#39;bus.urls&#39;))
]</code></pre>
<p>프로젝트 urls.py에서 위와 같이 설정해주고 나서
런서버 &gt; <a href="http://localhost:8000/swagger">http://localhost:8000/swagger</a> 에 접속해도 내가 짠 api들이 나오지 않았습니다.</p>
<h2 id="3-viewspy-에-데코레이터-작성하기">3. views.py 에 데코레이터 작성하기</h2>
<p>저는 get해오는 과정에서 파라미터가 있는 상황이었기 때문에 아래와 같이 스키마를 작성해주었습니다.
만약, 파마리터가 따로 없다면, class 와 def 사이에 있는 두 줄은 안 적으셔도 됩니다.</p>
<pre><code class="language-python">
from rest_framework.views import APIView
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi


class Test(APIView):
    bstopId = openapi.Parameter(&#39;stationId&#39;, openapi.IN_QUERY, description=&#39;정류소 고유번호&#39;, required=True, default=165000381, type=openapi.TYPE_NUMBER)

    @swagger_auto_schema(manual_parameters=[bstopId], responses={200: &quot;Success&quot;})
    def get(self, request):
        bstopId = request.GET.get(&quot;stationId&quot;, None)
</code></pre>
<p>클래스형 뷰를 사용했기 때문에, class 와 함수 사이에 <code>swagger_auto_schema 데코레이터</code> 를 작성해주면 된다.</p>
<h2 id="4-view-vs-apiview">4. View vs APIview</h2>
<p>처음에는 위와 같이 설정하고 나서도, api들이 나오지 않았습니다.
확인해보니 , </p>
<pre><code>class Test(View):</code></pre><p>그냥 View 로 했을 때는 <code>스웨거에 api가 나오지 않았고,</code> </p>
<pre><code>class Test(APIView):</code></pre><p>APIView로 바꿔주니 스웨거에 api가 나왔습니다.</p>
<blockquote>
<h3 id="schemas">Schemas</h3>
</blockquote>
<ul>
<li>API schema는 참조 문서를 생성하거나 API와 상호 작용 할 수 있는 동적 클라이언트 라이브러리를 구동하는 등 다양한 사용 사례를 허용하는 유용한 도구입니다. </li>
<li>REST 프레임워크의 스키마 생성은 docstring을 사용하여 스키마 문서의 설명을 자동으로 채웁니다.</li>
<li>명시적인 메서드 docstring이 있는 APIView입니다.
<a href="https://kimdoky.github.io/django/2018/08/04/drf-Schemas/">참고</a></li>
</ul>
<p>결과적으로 그냥 View가 아닌 APIView로 바꿔줘야 api문서가 완성 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django 시리얼라이저]]></title>
            <link>https://velog.io/@may_soouu/Django-%EC%8B%9C%EB%A6%AC%EC%96%BC%EB%9D%BC%EC%9D%B4%EC%A0%80</link>
            <guid>https://velog.io/@may_soouu/Django-%EC%8B%9C%EB%A6%AC%EC%96%BC%EB%9D%BC%EC%9D%B4%EC%A0%80</guid>
            <pubDate>Wed, 20 Jan 2021 00:58:33 GMT</pubDate>
            <description><![CDATA[<p>시리얼라이저로 가져오고 싶은 데이터가 있었는데, 
잘 되지 않아 몇 시간을 헤매다가 결국 도움을 받았다.. </p>
<h3 id="내가-하고-싶었던-것">내가 하고 싶었던 것</h3>
<ol>
<li>계속 외부 사이트에 콜 날려서 데이터를 가져옴</li>
<li>로컬 DB에는 아무것도 없고, 저장도 안 할 거임</li>
<li>가져온 데이터에 필드가 여러개인데, 그 중 내가 원하는 필드만 뽑아서 프론트로 반환</li>
</ol>
<h3 id="기존에-알던-방식">기존에 알던 방식</h3>
<pre><code class="language-python">data = [{
    &#39;fare&#39;     : i[&#39;fare&#39;],
    &#39;baseFare&#39; : i[&#39;baseFare&#39;],
    &#39;latitude&#39; : i[&#39;latitude&#39;],
    &#39;fareTime&#39; : i[&#39;fareTime&#39;],
    &#39;longitude&#39; : i[&#39;longitude&#39;],
    }for i in all_list]

return JsonResponse({&#39;data&#39;: data }, status=200)</code></pre>
<p>가져오고자 하는 필드를 변수로 지정해서, 리프트 컴프리헨션으로 for문을 돌리고, 변수에 지정해주는 방법이다.
하지만, 변수 하나하나 지정하는 방법 말고 시리얼라이저를 적용해서 원하는 필드만 빼오고 싶었다.</p>
<h3 id="시리얼라이저-적용">시리얼라이저 적용</h3>
<pre><code class="language-python">1. models.py 에 가져오고자 하는 필드 지정

class Location(models.Model):
    fare = models.IntegerField()
    baseFare = models.IntegerField()
    latitude = models.FloatField()
    fareTime = models.IntegerField()
    longitude = models.FloatField()

2. seriailizers.py 에 모델쓰에 있는 필드 전부 가져오겠다 명시

from rest_framework import serializers
from scooter.models import Location


class LocationSerialzer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = &#39;__all__&#39;

3. views.py 에 외부에서 가져온 데이터 중에 
   내가 시리얼라이저로 지정한 필드만 가져오겠다 작성하기

serializer = LocationSerialzer(data = location[&#39;data&#39;], many=True)
serializer.is_valid(raise_exception=True)
data = json.dumps(serializer.data)
</code></pre>
<p>여기서 포인트!! 
*<em>serializer.is_valid(raise_exception=True)
*</em>를 꼭 써줘야 한다.</p>
<p>나는 처음에 내가 가져오려는 필드만 DB에 넣고, 시리얼라이저에
        fields = &#39;<strong>all</strong>&#39;</p>
<p>라고 선언했기 때문에 가져오는 외부 데이터에서 내가 원하는 필드만 알아서 가져온다고 생각했다.
하지만,
<strong>serializer.is_valid(raise_exception=True)</strong>
를 입력해서 꼭 벨리데이션 체크를 해줘야지 원하는 값만 걸러서 들어온다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django 로그인 후 토큰 받아오기]]></title>
            <link>https://velog.io/@may_soouu/Django-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9B%84-%ED%86%A0%ED%81%B0-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@may_soouu/Django-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9B%84-%ED%86%A0%ED%81%B0-%EB%B0%9B%EC%95%84%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Mon, 18 Jan 2021 13:46:06 GMT</pubDate>
            <description><![CDATA[<p>주말 내내 해도 안 되던 코드를 오늘에서야 해결했다 ... ㅠㅠ
참 이것저것 많이 짰다고 생각했는데, 주석이랑 프린트 찍은 거랑 지우고 나니 참 작고 귀여운 내 코드... 🤣</p>
<h3 id="body에-이메일-비밀번호-토큰을-넣고-외부-사이트로-로그인해서-토큰-및-그-외-여러-데이터를-받아와야했다">body에 이메일, 비밀번호, 토큰을 넣고 외부 사이트로 로그인해서 토큰 및 그 외 여러 데이터를 받아와야했다.</h3>
<h3 id="기존-코드">기존 코드</h3>
<pre><code class="language-python">def post(self,request):
        data = json.loads(request.body)
        try:
            login_url = &quot;https://로그인하려는 사이트 주소&quot;
            header = {&quot;Content-Type&quot;: &quot;application/json; charset=utf-8&quot;}
            response = requests.post(login_url, data=data, headers=header)</code></pre>
<p>위와 같이 body에 데이터를 담고, request.post로 요청하는데 url, data, headers를 담아서 print(response)를 하면! 안나온다.... </p>
<p>한참 찾아본 결과 문제는 <code>data=data</code> 이 부분이었다.
나는 당연히 
<code>data = json.loads(request.body)</code>
로, body에 담아져서 온 데이터 자체가 json이기 때문에 data=data 라고 선언만 해주면 되는 줄 알았다.</p>
<p>보여지는 건 json이지만, json object일 뿐 실제 json 데이터는 아니었다.
그래서 
<code>data=json.dumps(data)</code>
코드로 수정했고,
<code>data=json.dumps(data)</code> 는 <code>json=data</code> 와 같았다.
즉, json 은 단순한 변수가 아니고, json뒤에 있는 걸 json화 시켜주는 문법이었다!</p>
<h3 id="수정-코드">수정 코드</h3>
<pre><code class="language-python">def post(self,request):
        data = json.loads(request.body)
        try:
            login_url = &quot;https://로그인하려는 사이트 주소&quot;
            header = {&quot;Content-Type&quot;: &quot;application/json; charset=utf-8&quot;}
            response = requests.post(login_url, json=data, headers=header)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[pip install -r requirements.txt error]]></title>
            <link>https://velog.io/@may_soouu/pip-install-r-requirements.txt-error</link>
            <guid>https://velog.io/@may_soouu/pip-install-r-requirements.txt-error</guid>
            <pubDate>Fri, 15 Jan 2021 01:14:04 GMT</pubDate>
            <description><![CDATA[<p>기존 레포를 클론 받고, 내 가상환경에 똑같은 환경을 구성하기 위해, <code>pip install -r requirements.txt error</code> 를 했습니다.</p>
<p>수많은 에러 ㅠㅠ,,가 발생했습니다.
gcc error, no moduled django, psycopg2, command &#39;/usr/bin/clang&#39; failed with exit code 등등 어떤 에러라고 특정 지을 수도 없었습니다.</p>
<p>결과적으로, </p>
<p>1) 기존 requirements.txt 파일에 <code>cffi</code> 와 <code>psycopg2</code> 와 <code>psycopg2-binary</code> 의 버전 문제
2) psycopg2 가 posgresql을 연결해주는 라이브러리이기 때문에 <code>postgresql</code> 도 설치</p>
<blockquote>
<p><code>cffi</code> : 파이썬과 c언어를 이어붙을 수 있게 도와 주는 라이브러리
<code>psycopg2 / psycopg2-binary</code> : Python과 PosgreSQL을 연결하는 라이브러리</p>
</blockquote>
<p>처음엔 <code>psycopg2</code> 을 requirements 파일에 있는 버전 그대로 pip install 을 했습니다.
그랬더니 당연히 fail..........🤣</p>
<h3 id="1-postgresql-설치하기">1. PostgreSql 설치하기</h3>
<p>pip install -r requirements.txt 하기 전에 postgresql 먼저 설치하기!!!</p>
<pre><code class="language-python">brew install postgresql</code></pre>
<h3 id="2-cffi--psycopg2-버전-바꾸기">2. cffi , psycopg2 버전 바꾸기</h3>
<ul>
<li>requirements.txt 파일에 있는 cffi 1.11.5 를 cffi 1.13.2 로 수정</li>
<li>requirements.txt 파일에 있는 psycopg2==2.7.5 / psycopg2-binary==2.7.5 를
psycopg2==2.8.5 / psycopg2-binary==2.8.5 로 바꾸기</li>
</ul>
<h3 id="3-재시도">3. 재시도</h3>
<pre><code class="language-python">pip install -r requirements.txt</code></pre>
<p>를 다시 하면 나의 가상환경에 라이브러리 내용이 전부 들어갑니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🙌🏻 Django 모델 필드 정의하기]]></title>
            <link>https://velog.io/@may_soouu/Django-%EB%AA%A8%EB%8D%B8-%ED%95%84%EB%93%9C-%EC%A0%95%EC%9D%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@may_soouu/Django-%EB%AA%A8%EB%8D%B8-%ED%95%84%EB%93%9C-%EC%A0%95%EC%9D%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 11 Jan 2021 02:30:57 GMT</pubDate>
            <description><![CDATA[<p>django에서 모델 필드를 정의할때 <code>null = True</code> 와 <code>blank=True</code> 의 옵션 (<em>기본값은 둘 다 false로 되어 있습니다</em>) 등 일반적인 가이드를 정리하려고 합니다.</p>
<table>
<thead>
<tr>
<th align="left">필드 타입</th>
<th align="center">null=True 설정하기</th>
<th align="center">blank = True 설정하기</th>
</tr>
</thead>
<tbody><tr>
<td align="left">CharField, TextField, SlugField, EmailField, CommaSeparatedIntegerField, UUIDField</td>
<td align="center">이용하지 않습니다. 장고 표준은 널 값 또는 빈 값을 빈 문자열로 저장하는 것입니다.</td>
<td align="center">이용합니다. 위젯이 빈 값을 허용하기를 원한다면 설정합니다. 위와 같이 설정하면 DB에서는 빈 값이 빈 문자열로 저장됩니다.</td>
</tr>
<tr>
<td align="left">FileField, ImageField</td>
<td align="center">이용하지 않습니다. CharField와 같은 패턴입니다.</td>
<td align="center">이용합니다. CharField와 같은 패턴입니다.</td>
</tr>
<tr>
<td align="left">BooleanField</td>
<td align="center">이용하지 않습니다. 대신 NullBooleanField를 이용합니다.</td>
<td align="center">이용하지 않습니다.</td>
</tr>
<tr>
<td align="left">IntegerField, FloatField, DecimalField, DurationField</td>
<td align="center">DB에서 해당 값들을 Null로 설정하는 게 가능하다면 이용합니다.</td>
<td align="center">null = True를 이용하는 것이 좋습니다.</td>
</tr>
<tr>
<td align="left">DateTimeField, DateField, TimeField</td>
<td align="center">DB에서 해당 값들을 Null로 설정하는 게 가능하다면 이용합니다.</td>
<td align="center">null = True를 이용하는 것이 좋습니다.</td>
</tr>
<tr>
<td align="left">ForeignKey, ManyToManyField, OneToOneField</td>
<td align="center">DB에서 해당 값들을 Null로 설정하는 게 가능하다면 이용합니다.</td>
<td align="center">위젯에서 해당 값들을 빈 값으로 설정하는 게 가능하다면 이용합니다.</td>
</tr>
<tr>
<td align="left">GenericIPAddressField</td>
<td align="center">DB에서 해당 값들을 Null로 설정하는 게 가능하다면 이용합니다.</td>
<td align="center">위젯에서 해당 값들을 빈 값으로 설정하는 게 가능하다면 이용합니다.</td>
</tr>
</tbody></table>
<p>출처 : Two Soops of Django</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🔥 이터레이터 병렬처리는 zip 으로]]></title>
            <link>https://velog.io/@may_soouu/%EC%9D%B4%ED%84%B0%EB%A0%88%EC%9D%B4%ED%84%B0-%EB%B3%91%EB%A0%AC%EC%B2%98%EB%A6%AC%EB%8A%94-zip-%EC%9C%BC%EB%A1%9C</link>
            <guid>https://velog.io/@may_soouu/%EC%9D%B4%ED%84%B0%EB%A0%88%EC%9D%B4%ED%84%B0-%EB%B3%91%EB%A0%AC%EC%B2%98%EB%A6%AC%EB%8A%94-zip-%EC%9C%BC%EB%A1%9C</guid>
            <pubDate>Wed, 06 Jan 2021 05:20:01 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-python">names = [&#39;computer&#39;,&#39;book&#39;, &#39;abc&#39;]
letters = [len(n) for n in names]</code></pre>
<p>가장 긴 이름을 찾는 코드를 작성한다면,</p>
<pre><code class="language-python">names = [&#39;computer&#39;,&#39;book&#39;, &#39;abc&#39;]
letters = [len(n) for n in names]

longest_name = None
max_letters = 0

for i in range(len(names)):
    count = letters[i]
    if count &gt; max_letters:
        longest_name = names[i]
        max_letters = count

print(longest_name)
&gt; computer</code></pre>
<p>로 찾을 수 있습니다. 
하지만, 인덱스 i로 루프에 접근하는 과정이 두 번 일어납니다.
위의 코드를 좀 더 명료하게 하기 위해 내장 함수 zip을 사용할 수 있습니다.</p>
<p><code>파이썬에서 zip은 이터레이터 두개 이상을 감쌉니다. 또한 각 이터레이터로부터 다음 값을 담은 튜플을 얻어옵니다.</code></p>
<p>zip 함수 사용한 코드</p>
<pre><code class="language-python">names = [&#39;computer&#39;,&#39;book&#39;, &#39;abc&#39;]
letters = [len(n) for n in names]
longest_name = None
max_letters = 0
for name, count in zip(names, letters):
    if count &gt; max_letters:
        longest_name = name
        max_letters = count
</code></pre>
<p>📌 내장함수 zip을 사용할 때는 문제가 있습니다. </p>
<p>이터레이터들의 길이가 다르면 zip이 제대로 작동하지 않는다는 것입니다.
만약, <code>names 리스트에 새로운 단어를 추가</code>하고, <code>letters 의 카운터를 업데이트하지 못했다면</code> 결과가 정상적으로 나오지 않습니다.</p>
<pre><code class="language-python">names = [&#39;computer&#39;,&#39;book&#39;, &#39;abc&#39;]
letters = [len(n) for n in names]
longest_name = None
max_letters = 0

names.append(&#39;helloworld&#39;)
for name, count in zip(names, letters):
    if count &gt; max_letters:
        longest_name = name
        max_letters = count

print(longest_name)
&gt; computer


helloworld 를 append 했지만, letters에 반영이 되지 않았기 때문에, 
computer가 제일 긴 단어로 프린트됩니다.</code></pre>
<blockquote>
<p>만약 zip으로 실행할 리스트의 길이가 같다고 확신 할 수 없다면, 내장 모듈 itertools의 zip_longest를 고려하는 것이 좋습니다.</p>
</blockquote>
<p><code>출처 : 파이썬 코딩의 기술 도서</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🕴🏻매직 메서드]]></title>
            <link>https://velog.io/@may_soouu/%EB%A7%A4%EC%A7%81-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@may_soouu/%EB%A7%A4%EC%A7%81-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Tue, 05 Jan 2021 02:22:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>매직 메서드
: 클래스를 다채롭게 사용할 수 있게 만들어 주는 방법입니다.</p>
</blockquote>
<h2 id="객체가-실행되거나-종료-될-때">객체가 실행되거나 종료 될 때</h2>
<table>
<thead>
<tr>
<th align="left">이름</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>__init__</code>(cls, .. )</td>
<td align="center">인스턴스가 생성되면 처음 하는 동작 지정</td>
</tr>
<tr>
<td align="left"><code>__new__</code>(self, .. )</td>
<td align="center">인스터스가 생성되면 처음 실행하는 동작 지정</td>
</tr>
<tr>
<td align="left"><code>__del__</code>(self)</td>
<td align="center">객체가 소멸할 때 동작 지정</td>
</tr>
</tbody></table>
<h2 id="객체를-표현하는-방법">객체를 표현하는 방법</h2>
<table>
<thead>
<tr>
<th align="left">이름</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>__str__</code>(self)</td>
<td align="center">객체의 데이터를 문자열로 만들어서 반환</td>
</tr>
</tbody></table>
<h2 id="데이터-참조-시">데이터 참조 시</h2>
<table>
<thead>
<tr>
<th align="left">이름</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>__getattribute__</code>(self,name,.. )</td>
<td align="center">객체의 속성(데이터)를 참조할 때 무조건 호출</td>
</tr>
<tr>
<td align="left"><code>__getattr__</code>(self,name,.. )</td>
<td align="center">참조 시, 속성(데이터)을 참조할 때 무조건 호출</td>
</tr>
<tr>
<td align="left"><code>__setattr__</code>(self,name,.. )</td>
<td align="center">객체의 속성을 변경할 때 호출</td>
</tr>
</tbody></table>
<h2 id="다른-객체를-변경하는데-사용하는-클래스에서-사용">다른 객체를 변경하는데 사용하는 클래스에서 사용</h2>
<table>
<thead>
<tr>
<th align="left">이름</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>__get__</code>(self,instance, owner)</td>
<td align="center">특정 객체의 값을 참조할 때 호출</td>
</tr>
<tr>
<td align="left"><code>__set__</code>(self,instance,value)</td>
<td align="center">특정 객체의 값을 변경할 때 호출</td>
</tr>
</tbody></table>
<h2 id="연산자들을-재정의하기-위해-사용">연산자들을 재정의하기 위해 사용</h2>
<table>
<thead>
<tr>
<th align="left">이름</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>__neg__</code>(self)</td>
<td align="center">-a(객체)를 정의한다.</td>
</tr>
<tr>
<td align="left"><code>__gt__</code>(self,other)</td>
<td align="center">x &gt; y 를 정의한다</td>
</tr>
<tr>
<td align="left"><code>__add__</code>(self,other)</td>
<td align="center">x + y 를 정의한다</td>
</tr>
<tr>
<td align="left"><code>__int__</code>(self)</td>
<td align="center">int(a) 를 정의한다</td>
</tr>
</tbody></table>
<p>출처 : &quot;컴퓨팅사고를 위한 파이썬&quot; 도서</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[✅ 우선순위 큐와 힙]]></title>
            <link>https://velog.io/@may_soouu/%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90%EC%99%80-%ED%9E%99</link>
            <guid>https://velog.io/@may_soouu/%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90%EC%99%80-%ED%9E%99</guid>
            <pubDate>Sun, 03 Jan 2021 05:34:39 GMT</pubDate>
            <description><![CDATA[<p>우선순위 큐는 각 항목마다 연관된 우선순위가 있습니다. 우선순위 큐는 힙을 사용하여 구현합니다.</p>
<h2 id="힙">힙</h2>
<p>최댓값과 최솟값을 빠르게 찾기 위해 고안된 자료구조입니다.
그래서, 리스트에서 가장 작거나 큰 요소에 반복적으로 접근하는 프로그램에 유용합니다. 
<code>시간복잡도 : O(log n)</code></p>
<ul>
<li>힙을 사용하는 이유<ul>
<li>배열에 데이터를 넣고 최대/최소값을 찾으면 시간 복잡도는 O(n)</li>
<li>힙에 데이터를 넣고 최대/최소값을 구하면 O(log n) 이 걸림</li>
</ul>
</li>
</ul>
<h3 id="1-1-heapq-모듈">1-1. heapq 모듈</h3>
<p><strong>리스트를 힙으로 변환하기</strong></p>
<pre><code class="language-python">import heapq
list1 = [4, 6, 8, 1]
heapq.heapify(list1)

print(list1)
&gt; [1, 4, 8, 6]</code></pre>
<p><strong>항목에 힙 삽입하기 &gt; heapq.heappush(heap, item)</strong></p>
<pre><code class="language-python">import heapq
h = []
heapq.heappush(h, (1, &#39;food&#39;))
heapq.heappush(h, (2, &#39;fruit&#39;))
heapq.heappush(h, (1, &#39;drink&#39;))

print(h)
&gt; [(1, &#39;drink&#39;), (2, &#39;fruit&#39;), (1, &#39;food&#39;)]</code></pre>
<p><strong>힙에서 가장 작은 항목 제거하고 결과 리턴</strong></p>
<pre><code class="language-python">import heapq

list1 = [1,3,4,6]
heapq.heappop(list1)

print(list1)
&gt; [3, 4, 6]</code></pre>
<p><strong>여러 리스트의 값을 이터레이터로 반환하기</strong></p>
<pre><code class="language-python">for x in heapq.merge([1,4,7],[2,9,10]):
    print(x)
    &gt; 1
    &gt; 2
    &gt; 4
    &gt; 7
    &gt; 9
    &gt; 10</code></pre>
<h2 id="우선순위-큐">우선순위 큐</h2>
<p>우선순위가 가장 높은 자료를 가장 먼저 꺼낼 수 있는 자료 구조입니다.</p>
<pre><code class="language-python">import heapq

hq = []
heapq.heappush(hq, (30,&#39;red&#39;))
heapq.heappush(hq, (15,&#39;blue&#39;))
heapq.heappush(hq, (19,&#39;white&#39;))

print(hq)
&gt; [(15, &#39;blue&#39;), (30, &#39;red&#39;), (19, &#39;white&#39;)]

first = heapq.heappop(hq)
print(first)
 &gt; (15, &#39;blue&#39;)
print(hq)
 &gt; [(19, &#39;white&#39;), (30, &#39;red&#39;)]

second = heapq.heappop(hq)
print(second)
 &gt; (19, &#39;white&#39;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[🧭 빅오 표기법]]></title>
            <link>https://velog.io/@may_soouu/%EB%B9%85%EC%98%A4-%ED%91%9C%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@may_soouu/%EB%B9%85%EC%98%A4-%ED%91%9C%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Sat, 02 Jan 2021 08:18:25 GMT</pubDate>
            <description><![CDATA[<p>알고리즘에서의 시간 복잡도를 주로 빅오 표기법(Big-o)을 사용하여 나타냅니다.
<code>시간 복잡도 : 시간이 얼마나 걸리냐를 나타내는 것</code></p>
<blockquote>
</blockquote>
<p>O(1) &lt; O(log n) &lt; O(n) &lt; O(n log n) &lt; O(n^2) &lt; O(2^n) &lt; O(n!)</p>
<h2 id="1-o1">1. O(1)</h2>
<p>입력 데이터의 양과 상관없이 실행 시간이 일정합니다. 
ex. 해시 테이블의 조회 및 삽입</p>
<h2 id="2-olog-n">2. O(log n)</h2>
<p>데이터 양이 많아져도 시간은 조금씩만 증가합니다.
시간에 비례하여 탐색 가능한 데이터양이 2의 n승이 됩니다.
이는 주로 큰 문제를 일정한 크기를 갖는 작은 문제로 쪼갤 때 나타나는 유형입니다.
ex. 이진 검색 ( 가운데에 위치한 값과 찾고자 하는 값 비교하면서 가운데 값을 바꿔가는 방법)</p>
<h2 id="3-on">3. O(n)</h2>
<p>데이터양과 시간이 정비례합니다.
ex. for 문을 통한 탐색, 리스트 순회 등</p>
<h2 id="4-on-log-n">4. O(n log n)</h2>
<p>데이터 양이 n만큼 많아지면 실행 시간은 n배 보다 조금 더 많아집니다.
큰 문제를 독립적인 작은 문제로 쪼개어, 독립적으로 해결 후 다시 하나로 모으는 경우에 나타납니다.
ex. 퀵 정렬, 병합 정렬 등</p>
<h2 id="5-onk">5. O(N^k)</h2>
<p>데이터 양에 따라 걸리는 시간은 제곱에 비례합니다.(효율이 좋지 않음)
ex. n번 순회하는 반복문을 k번 중첩</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[💣 스케일 업 vs 스케일 아웃]]></title>
            <link>https://velog.io/@may_soouu/%EC%8A%A4%EC%BC%80%EC%9D%BC-%EC%97%85-vs-%EC%8A%A4%EC%BC%80%EC%9D%BC-%EC%95%84%EC%9B%83</link>
            <guid>https://velog.io/@may_soouu/%EC%8A%A4%EC%BC%80%EC%9D%BC-%EC%97%85-vs-%EC%8A%A4%EC%BC%80%EC%9D%BC-%EC%95%84%EC%9B%83</guid>
            <pubDate>Fri, 01 Jan 2021 10:02:47 GMT</pubDate>
            <description><![CDATA[<p>서버의 성능을 향상 시킬 수 있는 스케일업과 스케일아웃에 대해 정리하겠습니다.</p>
<h2 id="스케일아웃">스케일아웃</h2>
<p>서버를 여러 대 추가하여 시스템을 확장하는 것입니다.
서버가 여러 대가 되면, 각 서버에 걸리는 부하를 균등하게 분할 해주는 <code>로드밸런싱</code>이 필요합니다.</p>
<blockquote>
<p>장점 : 서버 한 대가 다운되면, 다른 서버로 서비스 제공이 가능합니다.
단점 : 모든 서버가 동일한 데이터를 가지고 있어야하므로, 데이터변화가 적은 <code>웹서버</code>에 적합한 방식입니다.</p>
</blockquote>
<h2 id="스케일업">스케일업</h2>
<p>서버에 CPU나 RAM 등을 추가하거나, 고성능의 부품, 서버로 교환하는 등 서버의 성능 자체를 증가시키는 것입니다.</p>
<blockquote>
<p>단점: 서버 한 대에 모든 부하가 집중되기 때문에 장애 시 영향을 크게 받을 수 있습니다.
용량 확장 제한이 다다르면 새 시스템에 추가해야하는데 이때 마이그레이션 비용이 발생할 수 있습니다.
<code>데이터 갱신이 번번히 일어나는 경우</code>에 적합한방식입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[🏄🏻 파이썬 제너레이터]]></title>
            <link>https://velog.io/@may_soouu/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@may_soouu/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Fri, 01 Jan 2021 08:19:29 GMT</pubDate>
            <description><![CDATA[<p>이전에 제너레이터를 아주 잠깐 정리하고 넘어갔는데 제대로 공부하고자 다시 정리하려고 합니다.</p>
<h2 id="제너레이터">제너레이터</h2>
<p>제너레이터 : <strong><em>이터레이터</em></strong>를 생성해주는 함수이며, 파이썬의 시퀀스를 생성하는 객체입니다.(제너레이터는 yield 문을 사용합니다)
<code>이터레이터 : 반복문을 활용해서 데이터를 순회하면서 처리하는 것</code>
   <code>(리스트, 딕셔너리, 튜플 등)</code>
 <code>시퀀스 : 앞뒤가 꼭 지켜져야 하는 시/공간적 순서</code>  </p>
<pre><code class="language-python">1. 제너레이터 없는 for문

def square_numbers(nums):
    result = []
    for i in nums:
        result.append(i * i)
    return result

my_nums = square_numbers([1, 2, 3, 4, 5])

print(my_nums)
&gt;&gt; [1, 4, 9, 16, 25]</code></pre>
<p>print를 실행해보면 위의 함수가 전부 실행되어 값이 반환된 것을 볼 수 있습니다.</p>
<pre><code class="language-python">2. 제너레이터를 사용한 for문 

def square_numbers(nums):
    for i in nums:
        yield i * i

my_nums = square_numbers([1, 2, 3, 4, 5])  #1

print(my_nums)
&gt;&gt; &lt;generator object gensquare_numbers at 0x7fc5fe928f90&gt;</code></pre>
<p>위의 내용을 프린트해보면 값이 나오는 것이 아닌, 제너레이터 오브젝트가 리턴이 됩니다. 
자신이 리턴할 모든 값을 메모리에 저장하는 것이 아닌 호출 될 때마다 하나씩 값을 전달하기 때문에 기다리는 상태입니다.</p>
<pre><code class="language-python">def square_numbers(nums):
    for i in nums:
        yield i * i

my_nums = square_numbers([1, 2, 3, 4, 5])  #1

# 그냥 프린트가 아닌 next로 감싸서 프린트 해보면 1이 찍혀서 나옵니다.
print(next(my_nums))
&gt;&gt; 1
print(next(my_nums))
&gt;&gt; 4
print(next(my_nums))
&gt;&gt; 9
print(next(my_nums))
&gt;&gt; 16
print(next(my_nums))
&gt;&gt; 25

프린트를 계속 반복해보면 값이 나오는 것을 알 수 있습니다.
값이 전부 반환된 상태에서 한번 더 next 를 프린트 하면,

print(next(my_nums))
&gt;&gt; Traceback (most recent call last):
  File &quot;generator.py&quot;, line 22, in 
    print(next(my_nums))
StopIteration

StopIteration 예외가 발생합니다.</code></pre>
<p>제너레이터는 일반적으로 for문을 통해서 호출합니다.</p>
<pre><code class="language-python">def square_numbers(nums):
    for i in nums:
        yield i * i

my_nums = square_numbers([1, 2, 3, 4, 5])

for num in my_nums:
    print(num)</code></pre>
<p>위의 코드를 실행해보면 예외처리가 발생하지 않고, 알아서 멈춥니다.</p>
<p>데이터의 양이 많아지고 메모리 사용량을 최적화 해야하는 상황이 온다면 제너레이터를 사용해야합니다.</p>
<blockquote>
<p>yield 를 사용하지 않고, 제너레이터를 만들 수 있습니다.</p>
</blockquote>
<blockquote>
<pre><code class="language-python">result = (i*i for i in range(0,10))
for num in result:
    print(num)</code></pre>
</blockquote>
<p>```</p>
<p>리스트 컴프리헨션 구조에서 괄호를 &#39;[]&#39; 가 아닌 &#39;()&#39; 로 바꿔주면 됩니다.</p>
<p><a href="http://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0-generator/">출처</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🙈 동적 계획법]]></title>
            <link>https://velog.io/@may_soouu/%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</link>
            <guid>https://velog.io/@may_soouu/%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</guid>
            <pubDate>Thu, 31 Dec 2020 14:51:22 GMT</pubDate>
            <description><![CDATA[<p>2020년 올해의 마지막 블로그를 작성하겠습니다.</p>
<blockquote>
<p><strong>동적 계획법이란?</strong> <br>
복잡한 문제를 재귀를 통해 간단한 하위 문제로 분류 및 단순화하여 해결하는 방법
최적 부분 구조 / 중복되는 부분 문제가 있다면 동적 계획법을 사용하면 좋습니다.
<code>최적부분구조 : 답을 구하기 위해 했던 계산을 반복해야하는 구조</code></p>
</blockquote>
<pre><code class="language-python">최장 증가 부분열 찾기

1. 동적계획법

from bisect import bisect
from itertools import combinations
from functools import wraps

def solution(seq):
    L = [1] * len(seq)
    res = []
    for cur, val in enumerate(seq):
        for i in range(cur):
            if seq[i] &lt;= val:
                L[cur] = max(L[cur], 1 + L[pre])
    return max(L)
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[🖥 네트워크 원리 (웹 브라우저 2 - 3)]]></title>
            <link>https://velog.io/@may_soouu/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9B%90%EB%A6%AC-%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-2-3</link>
            <guid>https://velog.io/@may_soouu/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9B%90%EB%A6%AC-%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-2-3</guid>
            <pubDate>Thu, 31 Dec 2020 02:54:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>애플리케이션에서 의뢰받은 프로토콜 스택이 메세지를 송신하는 방법</strong><br></p>
</blockquote>
<ol>
<li>소켓 작성</li>
<li>서버 접속</li>
<li>데이터 송 수신</li>
<li>서버에서 연결을 끊고 소켓 말소</li>
<li>IP와 이더넷의 패킷 송,수신 동작</li>
<li>UDP 프로토콜을 이용한 송 수신 동작</li>
</ol>
<p>이전에 정리한 1,2번에 이어서 3,4번에 대해서 정리하겠습니다.</p>
<h2 id="3-데이터--송-수신">3. 데이터  송, 수신</h2>
<h3 id="3-1-프로토콜-스택에-http-리퀘스트-메시지를-넘기기">3-1. 프로토콜 스택에 HTTP 리퀘스트 메시지를 넘기기</h3>
<p>connect에서 애플리케이션에 제어가 되돌아오면 데이터 송수신 을 할 수 있습니다.
<code>커넥션 :</code>
<code>- 데이터를 송/수신 할 수 있도록 파이프와 같은 것으로 소켓이 연결되어 있는데, 이 파이프와 같은 것을 커넥션이라고 함.</code>
<code>- 이 커넥션은 close를 호출하여 연결을 끊기 전까지 계속 존재합니다.</code></p>
<p>데이터 송,수신의 첫 작업은 애플리케이션이 <code>write</code>를 호출하여 송신 데이터를 프로토콜 스택에 건네주는 것입니다.</p>
<p>프로토콜 스택은 받은 데이터를 <code>곧바로 송신하는 것이 아닌</code> , 자체 내부에 있는 <code>송신용 버퍼 메모리 영역에 저장</code> 하고, 애플리케이션이 다음 데이터를 건네주기를 기다립니다.</p>
<blockquote>
<p>📌 받은 데이터를 왜 일단 저장할까?<br>
프로토콜 스택으로 전달하는 데이터의 길이는 애플리케이션의 종류나 만드는 방법에 따라 결정됩니다. 즉, 항상 다르기 때문에 프로토콜 스택에서 제어할 수가 없습니다. 
만약, 작은 단위의 데이터를 받을 때마다 데이터를 보내게 되면 네트워크의 효율이 저하되기 때문에, 어느정도 데이터를 쌓고 나서 송수신 동작을 합니다.<br>
📌 프로토콜 스택에 데이터를 저장하지 않고 바로 전송하도록 애플리케이션에서 옵션을 설정할 수도 있습니다.
브라우저와 같은 대화형 애플리케이션이 서버에 메시지를 보내는 경우라면 프로토콜 스택에 저장해놓는 동안 응답 시간이 지연되므로 이와 같은 옵션을 사용하는 경우가 많습니다.</p>
</blockquote>
<h3 id="3-2-패킷이-도착했는지-확인">3-2. 패킷이 도착했는지 확인</h3>
<p><code>패킷 : 데이터의 단위</code> 
TCP는 송신한 패킷이 상대에게 올바르게 도착했는지 확인하고, 도착하지 않았으면 다시 송신하는 기능이 있기 때문에, 패킷 송신 후 확인 동작으로 넘어갑니다.</p>
<p>📌 <strong>확인 과정</strong></p>
<ol>
<li>서버로 넘기는 데이터를 조각으로 분할</li>
<li>데이터 맨 앞에서부터 따져서 각 조각이 몇 번째 바이트에 해당하는지를 <code>시퀀스 번호</code>라는 항목에 저장 &gt;&gt; TCP 헤더에 기록</li>
<li>데이터의 크기도 같이 전달</li>
<li>데이터의 누락 여부를 확인 할 수 있음<pre><code>예를 들어, 1,460바이트까지 수신 완료함. 1,461 패킷이 도착하면 누락이 없는 것임
근데 시퀀스 번호가 1,461인 패킷이 도착하지 않았는데 
2,921 패킷이 도착하면 1,461 패킷이 누락된 것임</code></pre></li>
<li>이렇게 데이터의 누락여부를 확인 후에, 이전에 수신한 데이터를 합쳐서 데이터를 몇 번째 바이트까지 수신한 것인지 계산하고, 그 값을 <code>ACK 번호(아크번호)</code>에 기록해서 송신측에 전달</li>
</ol>
<blockquote>
<p>송신 : 00 번째 바이트부터 시작되는 데이터를 xx바이트만큼 보내세요
수신 : xx번째 바이트까지 수신했습니다.<br>
ACK번호를 되돌려주는 위의 동작을 <code>수신확인 응답</code>이라고 부름</p>
</blockquote>
<p>--틈틈히 정리중--</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬의 예외 처리]]></title>
            <link>https://velog.io/@may_soouu/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%9D%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@may_soouu/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%9D%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 30 Dec 2020 15:34:42 GMT</pubDate>
            <description><![CDATA[<p>try/except/else/finally 에서 각 블록의 장점을 이용해서 사용하는 방법을 정리하려고 합니다.</p>
<h2 id="1-finally-----블록">1. finally     블록</h2>
<p>예외를 전달하고 싶지만, 예외가 발생해도 정리 코드를 실행하고 싶을 때는 <code>try/finally</code>를 사용하면 됩니다</p>
<pre><code class="language-python">ex)

file_open = open(&#39;/test.txt&#39;)
try:
    data = file_open.read()
finally:
    file_open.close()</code></pre>
<h2 id="2-else-블록">2. else 블록</h2>
<p>어떤 예외를 처리하고, 어떤 예외를 전달할지를 명확하게 하고자 한다면 <code>try/except/else</code>를 사용하면 됩니다. </p>
<pre><code class="language-python">def load_json_key(data, key):
    try:
        result_dict = json.loads(data)
    except ValueError as e:
        raise KeyError from e
    else:
        return result_dict[key]</code></pre>
<p>except와 else를 나눠서 예외 전달 행위를 명확하게 할 수 있습니다.</p>
<p>1) json.loads 과정에서 ValueError가 일어나면
2) except에서 처리 됨
3) 에러 없으면 else로 넘어가서 벨류값 찾음
4) 3번의 과정에서 에러가 발생한다면, try/except와 구분되는 에러이기 때문에 시각적으로 except와 구분함</p>
<h2 id="3-모두-사용">3. 모두 사용</h2>
<p>복합문 하나로 모든 것을 처리하고자 한다면 <code>try/except/else/finally</code> 를 전부 사용하면 됩니다.</p>
<p>ex. 파일에서 수행할 작업 설명을 읽고, 처리한 후 바로 업데이트 하는 작업
<code>try</code> : 파일을 읽고 처리
<code>except</code> : try블록에서 일어난 예외 처리
<code>else</code> : 파일을 바로 업데이트하고, 관련한 예외가 전달되게 하는데 사용
<code>finally</code> : 파일 핸들을 정리하는데 사용</p>
<pre><code class="language-python">undefined = object()

def divide_json(path):
    handle = open(paht, &#39;r+&#39;)
    try:
        data = handle.read()
    op = json.loads(data)
        value = (
            op[&#39;numerator&#39;] / 
            op[&#39;denominator&#39;])
    except ZeroDivisionError as e:
        return undefined
    else:
        op[&#39;result&#39;] = value
        result = json.dumps(op)
        handle.seek(0)
        handle.write(result)
        return value
    finally:
        handle.close()</code></pre>
<p>출처 : 도서 [파이썬 코딩의 기술]</p>
]]></description>
        </item>
    </channel>
</rss>