<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>25th-Night</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 16 Oct 2022 02:09:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. 25th-Night. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/brown_eyed87" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[221016_[Python] Class]]></title>
            <link>https://velog.io/@brown_eyed87/221016Python-Class</link>
            <guid>https://velog.io/@brown_eyed87/221016Python-Class</guid>
            <pubDate>Sun, 16 Oct 2022 02:09:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 게시글은 <code>Fastcampus</code>에서 제공하는 <span style="color: #FA8072"><strong><code>한 번에 끝내는 파이썬 웹 개발 초격차 패키지 Online.</code></strong></span> 강의를 듣고 작성하였습니다.</p>
</blockquote>
<h1 id="🚀-ch04_01-클래스와-객체">🚀 CH04_01. 클래스와 객체</h1>
<h2 id="🏳🌈-목차">🏳‍🌈 목차</h2>
<ol>
<li>클래스와 객체</li>
<li>여러가지 속성 (인스턴스 속성, 클래스 속성, 비공개 속성)</li>
<li>여러가지 메서드 (인스턴스 메서드, 클래스 메서드, 정적 메서드, 매직 메서드)</li>
<li>상속 (오버 라이딩, 추상클래스)<br>

</li>
</ol>
<h2 id="🏳🌈-절차-지향-vs-객체-지향">🏳‍🌈 절차 지향 vs 객체 지향</h2>
<br>

<h3 id="🚧-절차-지향-프로그래밍">🚧 절차 지향 프로그래밍</h3>
<blockquote>
<p><strong>기능들을 어떤 <code>순서</code>로 처리할 것인가에 초점을 맞춘다.</strong></p>
</blockquote>
<ul>
<li>예를 들어, 아침에 일어나면<blockquote>
<ol>
<li>씻기</li>
<li>옷 갈아입기</li>
<li>밥 먹기</li>
</ol>
</blockquote>
</li>
</ul>
<p>📌 이와 같이 <code>순서가 중심이 되어 프로그래밍</code> 하는 방식
<br></p>
<h3 id="🚧-객체-지향-프로그래밍">🚧 객체 지향 프로그래밍</h3>
<blockquote>
<p><strong>객체가 중심이 되고, 객체를 정의하고 객체간 상호작용에 초점을 맞춘다.</strong></p>
</blockquote>
<ul>
<li>예를 들어,<blockquote>
<ul>
<li>A라는 사람이 있고 B라는 사람이 있음</li>
<li>A, B는 사람이자 객체</li>
<li>A가 B에게 돈을 빌려주는 행동을 하면<ul>
<li>A는 -10 만큼 재산이 깎이고 B는 10이 재산이 늘어남</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
<p>📌 이와 같이 <code>객체간에 서로 어떤 행동에 의해 상호작용</code>이 일어나는 것을 객체 지향 프로그래밍이라고 함
<br></p>
<h3 id="🚧-언제-쓰나">🚧 언제 쓰나?</h3>
<blockquote>
<p><strong><code>프로그램의 규모</code>에 따라 다름</strong></p>
</blockquote>
<ul>
<li><u>프로그램의 규모가 작은 경우</u>에는 <code>절차 지향</code></li>
<li><u>대규모 프로젝트</u>는 <code>객체 지향</code> 방식이 효율적<br>

</li>
</ul>
<h2 id="🏳🌈-클래스와-객체의-개념">🏳‍🌈 클래스와 객체의 개념</h2>
<h3 id="🚧-클래스와-객체">🚧 클래스와 객체</h3>
<p><strong><code>클래스</code></strong>
📌 객체를 만들기 위한 <code>설계도</code></p>
<p><strong><code>객체</code></strong>
📌 설계도로부터 만들어낸 <code>제품</code></p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/37fcc3ff-d0f7-46ef-8d00-f201fd5f08e8/image.png" alt=""></p>
<h3 id="🚧-클래스와-객체-만들기">🚧 클래스와 객체 만들기</h3>
<p><strong><code>클래스 만들기</code></strong></p>
<pre><code class="language-python">class 클래스이름:
    # pass : 아직 구현하지 않고 넘어가고, 나중에 구현하겠다는 것을 의미
    pass </code></pre>
<p><strong><code>객체 만들기</code></strong></p>
<pre><code class="language-python">인스턴스 = 클래스이름()</code></pre>
<br>

<h3 id="🚧-속성과-메서드-추가하기">🚧 속성과 메서드 추가하기</h3>
<p><strong><code>속성 추가하기</code></strong></p>
<pre><code class="language-python">class Unit:
    # __init__ 메서드 : 생성자
    # 생성자의 첫 번째 변수로는 반드시 self가 반드시 들어감
    # self는 객체 자기 자신을 의미
    # 두번째 변수부터는 속성값으로 사용할 데이터를 입력받는 역할을 하게 됨.
    def __init__(self, name, hp, shield, demage):
        # 클래스의 속성 : self.속성명 형태로 작성하고, 위에서 입력받을 변수를 할당하는 형태로 작성
        self.name = name           # 이름
        self.hp = hp               # 체력
        self.shield = shield       # 방어막
        self.demage = demage       # 공격력</code></pre>
<p><strong><code>실제 객체 생성하기</code></strong></p>
<pre><code class="language-python">probe = Unit(&quot;프로브&quot;, 20, 20, 5)
zealot = Unit(&quot;질럿&quot;, 100, 60, 16)
dragoon = Unit(&quot;드라군&quot;, 100, 80, 20)</code></pre>
<p><strong><code>메서드 추가하기</code></strong></p>
<ul>
<li>Unit 클래스에 정보를 출력하는 메서드를 추가해 봅시다.</li>
</ul>
<pre><code class="language-python">class Unit:
    def __init__(self, name, hp, shield, demage):
        self.name = name
        self.hp = hp
        self.shield = shield
        self.demage = demage
    # __str__ 메서드
    def __str__(self):
        return f&quot;[{self.name}] 체력 : {self.hp} 실드 : {self.shield} 공격력 : {self.demage}&quot;</code></pre>
<p><strong><code>객체 생성</code></strong></p>
<pre><code class="language-python">probe = Unit(&quot;프로브&quot;, 20, 20, 5)
# print 실행 시, __str__ 메서드가 실행되게 됨.
print(probe)
# 프로브 체력 : 20 실드 : 20 공격력 : 5</code></pre>
<br>

<h1 id="🚀-ch04_02-여러가지-속성">🚀 CH04_02. 여러가지 속성</h1>
<br>

<h2 id="🏳🌈--인스턴스-속성-instance-attribute">🏳‍🌈  인스턴스 속성 (instance attribute)</h2>
<blockquote>
<p><strong>객체마다 다르게 가지는</strong> 속성</p>
</blockquote>
<pre><code class="language-python">class Unit:
    def __init__(self, name, hp, shield, demage):
        self.name = name          # 인스턴스 속성
        self.hp = hp              # 인스턴스 속성
        self.shield = shield      # 인스턴스 속성
        self.demage = demage      # 인스턴스 속성</code></pre>
<p><code>인스턴스 속성 사용 방법</code></p>
<blockquote>
<ul>
<li><code>self.속성명</code> : 클래스 안에서 인스턴스를 사용할 때</li>
<li><code>객체명.속성명</code> : 클래스 밖에서 인스턴스를 사용할 때</li>
</ul>
</blockquote>
<br>

<h2 id="🏳🌈--클래스-속성-class-attribute">🏳‍🌈  클래스 속성 (class attribute)</h2>
<blockquote>
<p><strong>모든 객체가 공유</strong>하는 속성</p>
</blockquote>
<pre><code class="language-python">class Unit:
    count = 0                    # 클래스 속성
    def __init__(self, name, hp, shield, demage):
        self.name = name
        self.hp = hp
        self.shield = shield
        self.demage = demage
        Unit.count += 1          # 클래스 속성</code></pre>
<br>

<h2 id="🏳🌈--비공개-속성-private-attribute">🏳‍🌈  비공개 속성 (private attribute)</h2>
<blockquote>
<p><strong>클래스 안에서만 접근 가능</strong>한 속성</p>
</blockquote>
<p>📌 <u>클래스 밖에서 속성에 접근하려고 하면 접근 자체가 불가</u>한 속성</p>
<pre><code class="language-python">class Unit:
    def __init__(self, name, hp, shield, demage):
        self.name = name
        self.__hp = hp                   # 비공개 속성
        self.shield = shield
        self.demage = demage</code></pre>
<p>📌 실습 코드</p>
<p><strong><code>클래스 생성</code></strong></p>
<pre><code class="language-python">class Unit:
    count = 0
    def __init__(self, name, hp, shield, demage):
        self.name = name
        self.__hp = hp
        self.shield = shield
        self.demage = demage
        Unit.count += 1
        print(f&quot;[{self.name} (이)가 생성 되었습니다.&quot;)

    def __str__(self):
        return f&quot;[{self.name}] 체력 : {self.__hp} 실드 : {self.shield} 공격력 : {self.demage}&quot;</code></pre>
<p><strong><code>객체 생성</code></strong></p>
<pre><code class="language-python"># 객체 3개 생성
probe = Unit(&quot;프로브&quot;, 20, 20, 5)
zealot = Unit(&quot;질럿&quot;, 100, 60, 16)
dragoon = Unit(&quot;드라군&quot;, 100, 80, 20)</code></pre>
<p><strong><code>출력을 통한 확인</code></strong></p>
<pre><code class="language-python"># 인스턴스 속성
print(probe.name)          # 프로브
print(zealot.hp)           # 9999
print(dragoon.damage)      # 20

# 클래스 속성
print(Unit.count)

# 비공개 속성 접근
prove.__hp = 9999
print(prove)            # [프로브] 체력 : 20 방어막 : 20 공격력 : 6
# 변경되지 않음.</code></pre>
<p>📌 <strong>네임 맹글링 (name mangling)</strong></p>
<blockquote>
<ul>
<li>이름을 뭉개서 해당 속성을 접근할 수 없게 만든다는 뜻</li>
<li>그러나 아래와 같이 접근하면 수정 가능</li>
</ul>
</blockquote>
<pre><code class="language-python">probe._Unit__hp = 9999
print(prove)            # [프로브] 체력 : 9999 방어막 : 20 공격력 : 6</code></pre>
<br>

<h1 id="🚀-ch04_03-여러가지-메서드">🚀 CH04_03. 여러가지 메서드</h1>
<br>

<h2 id="🏳🌈--인스턴스-메서드-instance-method">🏳‍🌈  인스턴스 메서드 (instance method)</h2>
<blockquote>
<p>인스턴스 속성에 접근할 수 있는 메서드.</p>
</blockquote>
<p>📌 <u>항상 첫번째 파라미터로 self를 갖는다.</u></p>
<p><code>hit 메서드 구현하기</code></p>
<ul>
<li><code>데미지</code>를 받으면 <code>체력</code>과 <code>방어막</code>이 깎이는 <code>**hit 메서드</code>**를 구현해 보자.</li>
</ul>
<ol>
<li><p><code>데미지</code>가 <code>방어막</code>보다 <u>작거나 같으면</u> <code>방어막</code>만 깎인다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/42fcbcaa-449f-4314-805e-f5140e294997/image.png" alt=""></p>
</li>
<li><p><code>데미지</code>가 <code>방어막</code>보다 <u>크고 <code>체력</code>보다 작으면</u> <code>체력</code>과 <code>방어막</code>이 깎인다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/7a46e277-8b80-40c2-ae45-02307eddf2fe/image.png" alt=""></p>
</li>
<li><p><code>데미지</code>가 <u><code>체력</code>보다 크면</u> <code>체력</code>을 <code>0</code>으로 만든다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/b91a5082-6a90-4c8c-9588-5f7784d6ee1b/image.png" alt=""></p>
</li>
</ol>
<br>
📌 실습 코드

<p><strong><code>인스턴스 메서드</code></strong></p>
<pre><code class="language-python">class Unit:
    count = 0
    def __init__(self, name, hp, shield, demage):
        self.name = name
        self.__hp = hp
        self.shield = shield
        self.demage = demage
        Unit.count += 1
        print(f&quot;[{self.name} (이)가 생성 되었습니다.&quot;)

    def __str__(self):
        return f&quot;[{self.name}] 체력 : {self.__hp} 실드 : {self.shield} 공격력 : {self.demage}&quot;

    def hit(self, damage):
        # 방어막 변경
        if self.shield &gt;= damage:
            self.shield -= damage
            damage = 0
        elif:
            damage -= shelf.shield
            self.shield = 0

        # 체력 변경
        if damage &gt; 0:
            if self.hp &gt; damage:
                self.hp -= damage
            else:
            self.hp -= 0
</code></pre>
<br>

<h2 id="🏳🌈--클래스-메서드-class-method">🏳‍🌈  클래스 메서드 (class method)</h2>
<blockquote>
<p><code>클래스 속성에 접근하기 위해서 사용</code>한다.</p>
</blockquote>
<p>📌 클래스를 의미하는 <u><code>cls</code>를 파라미터로 받는다.</u>
<br>
📌 실습 코드</p>
<p><strong><code>클래스 메서드</code></strong></p>
<pre><code class="language-python">class Unit:
    count = 0
    ...
    @classmethod      # 데코레이터 형태로 사용
    def print_count(cls):
        print(f&quot;전체 유닛 개수 : {cls.count}&quot;)

#클래스 메서드를 실행할 때는 객체를 거칠 필요 없이 바로 클래스에서 바로 호출 가능
Unit.print_count()         # 생성된 유닛 개수 : 3개</code></pre>
<br>

<h2 id="🏳🌈--정적-메서드-static-method">🏳‍🌈  정적 메서드 (static method)</h2>
<blockquote>
<p>인스턴스를 만들 필요가 없는 메서드</p>
</blockquote>
<p>📌 self를 받지 않는다.</p>
<ul>
<li>메서드가 인스턴스 유무와 관계없이 독립적으로 사용될 때<br>

</li>
</ul>
<p>📌 실습 코드</p>
<p><strong><code>정적 메서드</code></strong></p>
<blockquote>
<p>Math라는 클래스를 만들고 staticmethod 클래스를 math클래스 안에 구현</p>
</blockquote>
<pre><code>class Math:

    @staticmethod
    def add(x, y):
        return x + y

    @staticmethod
    def sub(x, y):
        return x - y

print(Math.add(3, 4))    # 7
print(Math.sub(3, 4))    # -1
</code></pre><br>

<h2 id="🏳🌈--매직-메서드-magic-method">🏳‍🌈  매직 메서드 (magic method)</h2>
<blockquote>
<p><u>클래스 안에서 정의가 가능</u>한 스페셜 메서드</p>
</blockquote>
<p>📌 특별한 상황에 호출된다.</p>
<blockquote>
<ul>
<li><code>__이름__</code> 의 형태로 되어있다.</li>
<li><code>__init__</code> 메서드 : 클래스에서 객체를 생성할 때 호출되는 메서드</li>
<li><code>__str__</code> 메서드 : 클래스에서 객체를 출력할 때 호출되는 메서드</li>
</ul>
</blockquote>
<p>📌 <code>dir</code> : 객체가 가지고 있는 메서드와 속성을 확인할 수 있는 함수</p>
<blockquote>
<ul>
<li>dir(prove) : prove 객체가 가지고 있는 메서드와 속성을 확인</li>
</ul>
</blockquote>
<br>

<h1 id="🚀-ch04_04-상속">🚀 CH04_04. 상속</h1>
<br>

<h2 id="🏳🌈--상속의-개념">🏳‍🌈  상속의 개념</h2>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/1d9cef75-084c-442d-97cd-1f5107836c87/image.png" alt=""></p>
<p>📌 <code>클래스들의 공통된 속성과 메서드</code>를 뽑아내서 부모 클래스를 만든다.</p>
<blockquote>
<p><u>이를 자식 클래스에서 상속받아서 사용</u>한다.</p>
</blockquote>
<br>

<h2 id="🏳🌈--상속의-장점">🏳‍🌈  상속의 장점</h2>
<p>📌 <code>코드의 중복을 제거 가능</code></p>
<blockquote>
<ul>
<li>기존에는 여러가지 자식 클래스에서 각각 공통된, 중복된 코드를 일일히 만들어야 했지만,
<u>부모 클래스에 공통된 코드를 만들어 놓으면 자식 클래스에서는 그대로 받아서 사용</u>하면 됨.</li>
</ul>
</blockquote>
<p>📌 <code>간편한 유지보수</code></p>
<blockquote>
<ul>
<li><code>부모클래스</code>에 <code>공통된 코드</code>를 작성해놓으면 그 코드를 수정하면 되고, 
<code>자식클래스 각각</code>에 구현한 <code>상세 코드</code>는 그 코드만 수정하면 되기 때문</li>
</ul>
</blockquote>
<br>

<h2 id="🏳🌈--상속-구현하기">🏳‍🌈  상속 구현하기</h2>
<blockquote>
<p><code>상속 실습</code> 파트에서 코드 작성 예정</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/2c8d31c4-74a3-44db-9376-bf2243262824/image.png" alt=""></p>
<h2 id="🏳🌈--추상-클래스">🏳‍🌈  추상 클래스</h2>
<blockquote>
<p><code>추상클래스</code>는 <code>추상메서드</code>를 가질 수 있음</p>
<ul>
<li><code>추상메서드</code> : <u>상속받는 자식 클래스에서 구현을 강제하도록 만드는 것</u></li>
<li><code>추상클래스</code> : 추상메서드를 하나라도 가지고 있는 클래스</li>
</ul>
</blockquote>
<p>📌 만약, 추상메서드를 만들었는데 자식클래스에서 해당 메서드를 구현하지 않으면 아래와 같은 오류 메세지 발생</p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/d98634ce-b731-4b0e-9d27-f4e1fb84ab2d/image.png" alt=""></p>
<h2 id="🏳🌈-상속-실습">🏳‍🌈 상속 실습</h2>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/2c8d31c4-74a3-44db-9376-bf2243262824/image.png" alt=""></p>
<p><strong><code>&#39;Item&#39; Class 생성</code></strong></p>
<pre><code class="language-python">class Item:
    &quot;&quot;&quot;
    속성 : 이름
    메서드 : 줍기, 버리기
    &quot;&quot;&quot;
    def __init__(self, name):
        self.name = name

    def pick(self):
        print(f&quot;[{self.name}] 을(를) 주웠습니다.&quot;)

    def discard(self):
        print(f&quot;[{self.name}] 을(를) 버렸습니다.&quot;)</code></pre>
<p><strong><code>&#39;Weapon&#39; Class 생성</code></strong></p>
<pre><code class="language-python">class Weapon(Item):
    &quot;&quot;&quot;
    속성 : 공격력
    메서드 : 공격하기
    &quot;&quot;&quot;
    def __init(self, name, damage):
        # super() : 부모 클래스를 호출
        # 거기에 생성자를 호출해서 이름을 넘겨주겠다!!
        super().__init__(name)
        self.damage = damage

    def attack(self):
        print(f&quot;[{self.name}] 을(를) 이용해 {self.damage} 로(으로) 공격합니다.&quot;)</code></pre>
<p><strong><code>&#39;HealingItem&#39; Class 생성</code></strong></p>
<pre><code class="language-python">class HealingItem(Item):
    &quot;&quot;&quot;
    속성 : 회복량
    메서드 : 사용하기
    &quot;&quot;&quot;
    def __init__(self, name, recovery_amount):
        super().__init(name)
        self.recovery_amount = recovery_amount

    def use(self):
        print(f&quot;[{self.name}] 을(를) 사용합니다. {self.recovery_amount} 회복&quot;)</code></pre>
<p><strong><code>객체 생성</code></strong></p>
<pre><code class="language-python"># 객체 생성
m16 = Weapon(&quot;m16&quot;, 110)
bungdae = HealingItem(&quot;붕대&quot;, 20)

m16.attack()
# m16 을(를) 이용해 110 로(으로) 공격합니다.
bungdae.use()
# bungdae 을(를) 사용합니다. 20 회복</code></pre>
<p><strong><code>추상클래스</code></strong> 실습</p>
<pre><code class="language-python">from abc import *

# &#39;ABCMeta&#39; 라는 &#39;metaclasss&#39;를 상속받으면 Item 클래스가 추상클래스가 됨.
# 추상클래스로 만들고 나면 안에서 추상메서드를 사용할 수 있게 됨.
class Item(metaclass=ABCMeta):
    &quot;&quot;&quot;
    속성 : 이름
    메서드 : 줍기, 버리기
    &quot;&quot;&quot;
    def __init__(self, name):
        self.name = name

    def pick(self):
        print(f&quot;[{self.name}] 을(를) 주웠습니다.&quot;)

    def discard(self):
        print(f&quot;[{self.name}] 을(를) 버렸습니다.&quot;)

    # 추상메서드 만들때, abstractmethod 라는 메서드를 데코레이터로 사용
    # use 라는 메서드를 구현하지 않고 자식클래스로 넘김
    @abstracmethod
    def use(self):
        pass

class Weapon(Item):
    &quot;&quot;&quot;
    속성 : 공격력
    메서드 : 공격하기
    &quot;&quot;&quot;
    def __init(self, name, damage):
        # super() : 부모 클래스를 호출
        # 거기에 생성자를 호출해서 이름을 넘겨주겠다!!
        super().__init__(name)
        self.damage = damage

    def use(self):
        print(f&quot;[{self.name}] 을(를) 이용해 {self.damage} 로(으로) 공격합니다.&quot;)


class HealingItem(Item):
    &quot;&quot;&quot;
    속성 : 회복량
    메서드 : 사용하기
    &quot;&quot;&quot;
    def __init__(self, name, recovery_amount):
        super().__init__(name)
        self.recovery_amount = recovery_amount

    def use(self):
        print(f&quot;[{self.name}] 을(를) 사용합니다. {self.recovery_amount} 회복&quot;)


# 객체 생성
m16 = Weapon(&quot;m16&quot;, 110)
bungdae = HealingItem(&quot;붕대&quot;, 20)

m16.use()
# m16 을(를) 이용해 110 로(으로) 공격합니다.
bungdae.use()
# bungdae 을(를) 사용합니다. 20 회복</code></pre>
<br>

<h1 id="🚀-ch04_05-클래스-실습-문제">🚀 CH04_05 클래스 실습 문제</h1>
<br>

<h2 id="🏳🌈-상속-관계와-포함-관계">🏳‍🌈 상속 관계와 포함 관계</h2>
<p><img src="blob:https://velog.io/f954493c-9e13-48bd-a163-ed50bf87cef0" alt="업로드중.."></p>
<p>📌 <strong><code>is-a</code></strong> 관계: 서로 상속을 할 때 발생하는 관계</p>
<blockquote>
<ul>
<li>*<em>Weapon <code>is-a</code> Item *</em><blockquote>
<p>Weapon &lt; Item</p>
</blockquote>
</li>
</ul>
</blockquote>
<p>📌 <strong><code>has-a</code></strong> 관계: 어떤 특정 클래스가 다른 클래스의 객체를 가지고 있는 관계. <code>1:N 관계</code>라고도 정리 가능.</p>
<blockquote>
<ul>
<li><strong>Player <code>has-a</code> Unit</strong><blockquote>
<p><code>Player</code> 하나의 객체가 여러개의 <code>Unit</code> 객체를 가지고 있음.</p>
</blockquote>
</li>
</ul>
</blockquote>
<br>

<h2 id="🏳🌈-unit-player-클래스-구현">🏳‍🌈 Unit, Player 클래스 구현</h2>
<ol>
<li><p><strong><code>속성</code></strong> : 닉네임, 미네랄, 가스, 유닛리스트</p>
</li>
<li><p><strong><code>메서드</code></strong> : 생산하기
<code>produce(이름, 미네랄, 가스, 체력, 방어막, 공격력)</code></p>
<blockquote>
<ul>
<li><code>Player</code>의 <u>미네랄과 가스가 충분</u>한 경우 : <code>유닛 객체를 생성</code>하고 <code>유닛리스트에 추가</code>한다.</li>
<li><code>Player</code>의 <u>미네랄이 부족</u>한 경우 : <code>&quot;미네랄이 부족합니다&quot;</code>를 출력</li>
<li><code>Player</code>의 <u>가스가 부족</u>한 경우 : <code>&quot;가스가 부족합니다&quot;</code>를 출력</li>
</ul>
</blockquote>
</li>
</ol>
<pre><code class="language-python">
# 유닛 정보
unit_info = {
    &quot;probe&quot; : {
        &quot;name&quot; : &quot;프로브&quot;,
        &quot;mineral&quot; : 50,
        &quot;gas&quot; : 0,
        &quot;hp&quot; : 20,
        &quot;shield&quot; : 20,
        &quot;demage&quot; : 5
    },
    &quot;zealot&quot; : {
        &quot;name&quot; : &quot;질럿&quot;,
        &quot;mineral&quot; : 100,
        &quot;gas&quot; : 0,
        &quot;hp&quot; : 100,
        &quot;shield&quot; : 60,
        &quot;demage&quot; : 16
    },
    &quot;dragoon&quot; : {
        &quot;name&quot; : &quot;드라군&quot;,
        &quot;mineral&quot; : 125,
        &quot;gas&quot; : 50,
        &quot;hp&quot; : 100,
        &quot;shield&quot; : 80,
        &quot;demage&quot; : 20
    }
}

#  -----------

# 클래스 생성

class Unit(Player):
    &quot;&quot;&quot;
    속성 : 이름, 체력, 방어막, 공격력
    &quot;&quot;&quot;
    def __init(self. hp, shield, damage):
        self.hp = hp
        self.shield = shield
        self.damage = damage

class Player:
    &quot;&quot;&quot;
    속성 : 닉네임, 미네랄, 가스, 유닛리스트
    메서드 : 유닛 생산하기
    &quot;&quot;&quot;
    def __init__(self, nickname, mineral, gas, unit_list=[]):
        self.nickname = nickname
        self.mineral = mineral
        self.gas = gas
        self.unit_list = unit_list

    # 생산하기 메서드
    def produce(self, name, mineral, gas, hp, shield, damage):
        if self.mineral - mineral &lt; 0:
            print(&quot;미네랄이 부족합니다.&quot;)
        elif self.gas - gas &lt; 0:
            print(&quot;가스가 부족합니다.&quot;)
        else:
            self.mineral -= mineral
            self.gas -= gas
            unit = Unit(name, hp, shield, damage)
            self.unit_list.append(list)
            print(f&quot;[{name}] 을(를) 생산합니다.&quot;)</code></pre>
<p><strong><code>객체 생성</code></strong></p>
<pre><code class="language-python"># 플레이어 생성 1
# unit_list는 default 값으로 &#39;[]&#39;를 지정해두었기 때문에 인자로 넣지 않아도 빈 리스트로 지정됨.
player1 = Player(&quot;Bisu&quot;, 400, 10)

# 유닛 생성
player1.produce(unit_info[&#39;probe&#39;][&#39;name&#39;], unit_info[&#39;probe&#39;][&#39;mineral&#39;], unit_info[&#39;probe&#39;][&#39;gas&#39;], unit_info[&#39;probe&#39;][&#39;hp&#39;], unit_info[&#39;probe&#39;][&#39;shield&#39;], unit_info[&#39;probe&#39;][&#39;damage&#39;]
# [프로브] 을(를) 생산합니다.
player2.produce(unit_info[&#39;zealot&#39;][&#39;name&#39;], unit_info[&#39;zealot&#39;][&#39;mineral&#39;], unit_info[&#39;zealot&#39;][&#39;gas&#39;], unit_info[&#39;zealot&#39;][&#39;hp&#39;], unit_info[&#39;zealot&#39;][&#39;shield&#39;], unit_info[&#39;zealot&#39;][&#39;damage&#39;]
# [질럿] 을(를) 생산합니다.
player3.produce(unit_info[&#39;dragoon&#39;][&#39;name&#39;], unit_info[&#39;dragoon&#39;][&#39;mineral&#39;], unit_info[&#39;dragoon&#39;][&#39;gas&#39;], unit_info[&#39;dragoon&#39;][&#39;hp&#39;], unit_info[&#39;dragoon&#39;][&#39;shield&#39;], unit_info[&#39;dragoon&#39;][&#39;damage&#39;]
# 가스가 부족합니다.

# 생성된 모든 유닛 확인
for unit in player1.unit_list:
    print(f&quot;[{unit.name}] 체력 : {unit.hp} 방어막 : {unit.shield} 공격력 : {unit.damage}&quot;)

# [프로브] 체력 : 20 방어막 : 20 공격력 : 5
# [질럿] 체력 : 100 방어막 : 60 공격력 : 16



# 플레이어 생성 2
# unit_list는 default 값으로 &#39;[]&#39;를 지정해두었기 때문에 인자로 넣지 않아도 빈 리스트로 지정됨.
player1 = Player(&quot;Bisu&quot;, 500, 200)

# 유닛 생성
player1.produce(unit_info[&#39;probe&#39;][&#39;name&#39;], unit_info[&#39;probe&#39;][&#39;mineral&#39;], unit_info[&#39;probe&#39;][&#39;gas&#39;], unit_info[&#39;probe&#39;][&#39;hp&#39;], unit_info[&#39;probe&#39;][&#39;shield&#39;], unit_info[&#39;probe&#39;][&#39;damage&#39;]
# [프로브] 을(를) 생산합니다.
player2.produce(unit_info[&#39;zealot&#39;][&#39;name&#39;], unit_info[&#39;zealot&#39;][&#39;mineral&#39;], unit_info[&#39;zealot&#39;][&#39;gas&#39;], unit_info[&#39;zealot&#39;][&#39;hp&#39;], unit_info[&#39;zealot&#39;][&#39;shield&#39;], unit_info[&#39;zealot&#39;][&#39;damage&#39;]
# [질럿] 을(를) 생산합니다.
player3.produce(unit_info[&#39;dragoon&#39;][&#39;name&#39;], unit_info[&#39;dragoon&#39;][&#39;mineral&#39;], unit_info[&#39;dragoon&#39;][&#39;gas&#39;], unit_info[&#39;dragoon&#39;][&#39;hp&#39;], unit_info[&#39;dragoon&#39;][&#39;shield&#39;], unit_info[&#39;dragoon&#39;][&#39;damage&#39;]
# [드라군] 을(를) 생산합니다.

# 생성된 모든 유닛 확인
for unit in player1.unit_list:
    print(f&quot;[{unit.name}] 체력 : {unit.hp} 방어막 : {unit.shield} 공격력 : {unit.damage}&quot;)

# [프로브] 체력 : 20 방어막 : 20 공격력 : 5
# [질럿] 체력 : 100 방어막 : 60 공격력 : 16
# [드라군] 체력 : 100 방어막 : 80 공격력 : 20</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[221014_DRF Basic (2)]]></title>
            <link>https://velog.io/@brown_eyed87/221014DRF-Basic-2</link>
            <guid>https://velog.io/@brown_eyed87/221014DRF-Basic-2</guid>
            <pubDate>Sat, 15 Oct 2022 06:12:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 게시글은 <span style="color: #FA8072"><strong><code>김준태 강사님</code></strong></span>의 <span style="color: #FA8072"><strong><code>DRF - Basic</code></strong></span> <strong>강의를 듣고 작성</strong>하였습니다.</p>
</blockquote>
<hr>
<p>📢 <strong>수업 목표</strong></p>
<blockquote>
<ol>
<li><code>DRF</code> 로 간단한 블로그 만들기</li>
</ol>
</blockquote>
<hr>
<br>

<h1 id="✨-카테고리-추가하기">✨ 카테고리 추가하기</h1>
<h2 id="🎨-model-serializers-설정">🎨 Model, Serializers 설정</h2>
<p>📌 글마다 카테고리를 할당할 수 있도록 카테고리 모델을 작성</p>
<blockquote>
<ul>
<li>글을 작성할 때, 존재하지 않는 카테고리를 지정하면 안되기 때문에, <code>이미 존재하는 카테고리 중에서만 카테고리를 선택</code>할 수 있도록 해야함.</li>
<li>그래서 우선 <code>카테고리를 저장해놓기 위한 모델/시리얼라이저</code>를 작성.</li>
<li>그리고 <code>글 모델</code>에는 <code>카테고리 필드를 하나 추가</code>하고, 지금 만든 <code>테고리 모델의 데이터만 받을 수 있도록 수정</code> (카테고리 모델에 없는 데이터는 절대 지정할 수 없도록) </li>
</ul>
</blockquote>
<p><span style="color: #FA8072"><strong><code>blog/models.py</code></strong></span></p>
<pre><code class="language-python">class Category(models.Model):
    name = models.CharField(max_length=50, unique=True)
    description = models.TextField(max_length=255)

    def __str__(self): # 이 코드는 admin 페이지에서 데이터를 object() 형태가 아니라 이름으로 표시할 수 있도록 바꿔주는 코드입니다.
        return f&quot;{self.name}&quot;

class Article(models.Model):
    category = models.ManyToManyField(Category, blank=True) # () 안의 Category 는 위 Category 모델을 명시한 것입니다!
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_time = models.DateTimeField(auto_now_add=True)
    updated_time = models.DateTimeField(auto_now=True)</code></pre>
<ul>
<li><code>하나의 글에 category 를 여러 개 지정</code>할 수 있도록 하기 위해서, <code>ManyToManyField</code> 를 사용<blockquote>
<ul>
<li>Article 의 <code>category 필드</code>에는 카테고리 모델의 데이터 그 자체가 들어가는 것이 아니고, <u>카테고리 모델 중에서 어떤 데이터인지 구별하기 위한 pk 만 들어가게 된다</u>고 생각하시면 됩니다!</li>
</ul>
</blockquote>
</li>
</ul>
<p>📌 모델을 수정했다면 꼭 migration 이 필요</p>
<p><span style="color: #FA8072"><strong><code>Terminal 창</code></strong></span></p>
<pre><code class="language-python">
$ poetry run python manage.py makemigrations

$ poetry run python manage.py migrate</code></pre>
<p>📌 serializer 도 추가</p>
<p><span style="color: #FA8072"><strong><code>blog/serializers.py</code></strong></span></p>
<pre><code class="language-python">class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = [&quot;name&quot;, &quot;description&quot;]</code></pre>
<h2 id="🎨-admin-페이지를-통한-카테고리-추가">🎨 admin 페이지를 통한 카테고리 추가</h2>
<p>📌 admin에 Category 모델 등록</p>
<p><span style="color: #FA8072"><strong><code>blog.admin.py</code></strong></span></p>
<pre><code class="language-python">from django.contrib import admin
from blog.models import Article, Category

# Register your models here.

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = [&quot;name&quot;, &quot;get_description&quot;]

    @admin.display(description=&quot;카테고리 안내&quot;)
    def get_description(self, obj):
        return obj.description[:30]

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = [&quot;title&quot;, &quot;get_content&quot;]

    @admin.display(description=&quot;내용&quot;)
    def get_content(self, obj):
        return  obj.content[:30]</code></pre>
<p>📌 서버 실행 &gt; admin 페이지 접속 &gt; 카테고리 추가</p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/23783363-c0f9-44e9-afd0-2e9a2dc88315/image.png" alt=""></p>
<h2 id="🎨-article-시리얼라이저의-수정">🎨 article 시리얼라이저의 수정</h2>
<ul>
<li>시리얼라이저는 어떤 내용을 사용자에게 보여줄지 (응답으로 전달할지) 결정하는 용도도 있음!!<blockquote>
<ul>
<li>지금은 글을 표시할 때 제목, 내용, 글쓴시각, 수정시각 만 표시하게 됨.</li>
<li>모델을 수정해서 카테고리가 추가된 만큼, 카테고리도 표시하도록 시리얼라이저를 수정!!</li>
</ul>
</blockquote>
</li>
</ul>
<p>📌 <code>serializers.py</code> 수정</p>
<p><span style="color: #FA8072"><strong><code>blog/serializers.py</code></strong></span></p>
<pre><code class="language-python">from rest_framework import serializers
from .models import Article, Category

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = [&quot;name&quot;, &quot;description&quot;]

class ArticleSerializer(serializers.ModelSerializer):
    # article 을 표시할 때 카테고리 객체도 직렬화해서 보기 위한 설정 
    # 이게 없다면 카테고리 이름이 아니라, 그냥 id 숫자로만 표시되게 됨
    category = CategorySerializer(many=True, required=False, read_only=True)

    class Meta:
        model = Article
        fields = [&quot;title&quot;, &quot;content&quot;, &quot;category&quot;]</code></pre>
<blockquote>
<ul>
<li>serializer 안에서는 단순히 어떤 내용을 표시할지 뿐만 아니라, <code>해당 내용(필드)을 표시할 때 정확히 어떻게 표시할 건지</code>도 명시 가능</li>
<li><code>category 는 여러 개 지정 가능</code>하기 때문에, 데이터가 여러개 들어올 수 있다는 것을 알려주기 위해서 <code>many=True</code> 옵션을 명시해야 함.</li>
</ul>
</blockquote>
<p>📌 <code>create 함수</code> 작성</p>
<ul>
<li><p>그런데 위처럼만 작성하게 되면, 사용자로부터 카테고리를 입력받아서 글을 작성하는 과정이 조금 복잡해지게 됨.</p>
<blockquote>
<ul>
<li>카테고리를 받아서 글이 저장되는 과정을 조금 더 간단히 만들기 위해서, create 함수를 추가</li>
</ul>
</blockquote>
</li>
<li><p><code>create 함수</code>의 역할</p>
<blockquote>
<ul>
<li><strong>지금 create 함수는 유저로부터 카테고리 번호를 받고, 글에 해당 번호의 카테고리를 지정해주기 위해서 작성!!</strong></li>
<li><strong><code>foreign key (ManyToMany)</code> 가 포함된 모델의 데이터를 저장할 때면 보통 create 함수를 커스텀해서 사용!!</strong></li>
</ul>
</blockquote>
</li>
<li><p><u>article 시리얼라이저에 아래 내용을 추가</u>할텐데, 이 함수는 views 에서 serializer.save() 시에 실행될 함수임.</p>
<blockquote>
<ul>
<li>만약 create 함수가 없다면 자동으로 db에 저장하는 과정을 수행하지만, 있다면 해당 함수의 내용대로 db에 저장하는 과정이 수행됨.</li>
</ul>
</blockquote>
</li>
</ul>
<pre><code class="language-python">    def create(self, validated_data): # is_valid 이후에 save 함수가 실행되므로, 현재 유저가 작성해서 들어온 데이터를 validated_data 라고 해서 받습니다. 
        get_categories = validated_data.pop(&quot;get_categories&quot;, []) # 유저가 &quot;{title: 제목, content: 내용, get_categories: [카테고리번호들]}&quot; 형태로 데이터를 보냈다면, 여기서 get_categories 부분만 추출합니다. 
        article = Article(**validated_data) # 해당 내용을 토대로 article 객체를 만듭니다.
        article.save() # 해당 객체를 db 에 저장합니다.
        article.category.add(*get_categories) # 저장된 그 글에 대해서 유저가 보낸 카테고리 번호에 맞는 category 를 지정합니다.
        return article</code></pre>
<p><span style="color: #FA8072"><strong><code>blog/serializers.py</code></strong></span> </p>
<blockquote>
<ul>
<li><code>ArticleSerializer</code>완성본</li>
</ul>
</blockquote>
<pre><code class="language-python">from rest_framework import serializers
from .models import Article, Category

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = [&quot;name&quot;, &quot;description&quot;]

class ArticleSerializer(serializers.ModelSerializer):
    category = CategorySerializer(many=True, required=False, read_only=True) # article 을 표시할 때 카테고리 객체도 직렬화해서 보기 위한 설정 (이게 없다면 카테고리 이름이 아니라, 그냥 id 숫자로만 표시되게 됨)
    get_categories = serializers.ListField(required=False) # 유저한테 리스트 형태로 카테고리를 입력받을 수 있도록, 추가한 필드

    class Meta:
        model = Article
        fields = [&quot;title&quot;, &quot;content&quot;, &quot;category&quot;, &quot;get_categories&quot;]

    def create(self, validated_data): # views 에서 serializer.save() 실행 시 아래 코드가 실행됨 (이 create 함수가 없으면 자동으로 알아서 저장하고, 있으면 여기 있는 내용대로 저장과정을 수행함)
        get_categories = validated_data.pop(&quot;get_categories&quot;, [])
        article = Article(**validated_data)
        article.save()
        article.category.add(*get_categories)
        return article</code></pre>
<h2 id="🎨-요청-보내기">🎨 요청 보내기</h2>
<p>📌 <code>Category</code> 모델 생성 직후</p>
<blockquote>
<ul>
<li>게시글마다 등록된 카테고리가 없는 상황
<img src="https://velog.velcdn.com/images/brown_eyed87/post/9738e950-b61d-44c5-8076-5ca27263aeff/image.png" alt=""></li>
</ul>
</blockquote>
<p>📌 <code>POST</code> 요청</p>
<blockquote>
<ul>
<li><code>get_categories</code> 라는 <u><code>키:밸류 쌍</code>을 하나 더 추가해서 Post 요청</u>
<img src="https://velog.velcdn.com/images/brown_eyed87/post/1c9f6445-61a1-4525-b5f8-e1130f89061a/image.png" alt=""></li>
</ul>
</blockquote>
<p>📌 <code>GET</code> 요청</p>
<blockquote>
<ul>
<li><code>blog/</code> 로 접속하여 등록된 모든 글 조회
<img src="https://velog.velcdn.com/images/brown_eyed87/post/7fdcc58e-66b5-4693-9773-9da859b67b2b/image.png" alt=""></li>
</ul>
</blockquote>
<blockquote>
<ul>
<li><code>blog/&lt;int:pk&gt;</code> 로 접속하여 특정 글에 대해 조회<ul>
<li>1번, 3번에 해당하는 카테고리가 글에 잘 할당되어서, 표시되는 것을 확인 가능
<img src="https://velog.velcdn.com/images/brown_eyed87/post/0a72d943-cdf4-49e5-a410-d3a9277b69a0/image.png" alt=""></li>
</ul>
</li>
</ul>
</blockquote>
<h1 id="✨-댓글-추가하기">✨ 댓글 추가하기</h1>
<h2 id="🎨-model-serializers-설정-1">🎨 Model, Serializers 설정</h2>
<p>📌 models.py 에 comment 모델을 추가</p>
<blockquote>
<ul>
<li>댓글은 무조건 <code>특정한 글</code>에 달리는 것이기 때문에, 
foreign key 형태로 Article 모델과 연결하는 article 필드를 추가</li>
</ul>
</blockquote>
<p><span style="color: #FA8072"><strong><code>blog/models.py</code></strong></span></p>
<pre><code class="language-python">class Comment(models.Model):
    content = models.CharField(max_length=100)
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name=&#39;comments&#39;)
    created_time = models.DateTimeField(auto_now_add=True)
    updated_time = models.DateTimeField(auto_now=True)</code></pre>
<ul>
<li><p><code>ManyToManyField</code> vs <code>ForeignKey</code></p>
<blockquote>
<ul>
<li><code>ManyToManyField</code> 는 <code>N:N</code> 관계일 때, <code>ForeignKey</code> 는 <code>1:N</code> 관계일 때 사용하는 필드.</li>
</ul>
</blockquote>
</li>
<li><p><code>related_name</code></p>
<blockquote>
<ul>
<li><code>related_name</code> : Article 쪽에서 Comment 모델 객체에 접근할 때 쓰는 이름</li>
<li>지금은 Comment 모델에 <code>어떤 글</code>에 달린 댓글인지 그 글 번호를 저장하고 있으나,
결국에 우리는 글을 보여줄 때 댓글도 함께 보여줘야 합니다.</li>
<li>그러려면 글을 가져오는 과정 (Article 쪽) 에서 댓글까지 함께 가져와야 하는데,
그때 해당 이름이 사용되는 것</li>
</ul>
</blockquote>
</li>
</ul>
<p>📌 serializer 작성</p>
<p><span style="color: #FA8072"><strong><code>blog/serializers.py</code></strong></span></p>
<pre><code class="language-python">class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = [&quot;content&quot;, &quot;article&quot;]
        extra_kwargs = {&#39;article&#39;: {&#39;write_only&#39;: True}}</code></pre>
<blockquote>
<ul>
<li>보통 블로그에서 댓글을 볼 때는 특정한 글 하단 부분에 내용만 표시 (content) 되고,
해당 댓글이 달린 글 번호 (article) 까지 함께 표시하지는 않기 때문에,
<code>&#39;article&#39;</code> 에 <code>&#39;write_only&#39;: True</code> 옵션을 걸어서 글 번호는 표시하지 않도록 설정</li>
</ul>
</blockquote>
<h2 id="🎨-article-시리얼라이저-수정">🎨 article 시리얼라이저 수정</h2>
<ul>
<li>article 데이터를 전달할 때, 해당 글이 가진 댓글 데이터도 함께 넘겨줄 수 있도록 article 시리얼라이저를 수정<blockquote>
<ul>
<li>comments = CommentSerializer(many=True, read_only=True) 부분과 함께 
필드에 comments 가 추가된 것을 제외하고는 달라진 것이 없음.</li>
</ul>
</blockquote>
</li>
</ul>
<p><span style="color: #FA8072"><strong><code>blog/serializers.py</code></strong></span></p>
<pre><code class="language-python">class ArticleSerializer(serializers.ModelSerializer):
    category = CategorySerializer(many=True, required=False, read_only=True)
    get_categories = serializers.ListField(required=False)
    comments = CommentSerializer(many=True, read_only=True) # comment 객체를 직렬화해서 보기 위한 설정

    class Meta:
        model = Article
        fields = [&quot;id&quot;, &quot;title&quot;, &quot;content&quot;, &quot;category&quot;, &quot;get_categories&quot;, &quot;comments&quot;] # 필드에 comments 추가

    def create(self, validated_data):
        get_categories = validated_data.pop(&quot;get_categories&quot;, [])
        article = Article(**validated_data)
        article.save()
        article.category.add(*get_categories)
        return article</code></pre>
<h2 id="🎨-views-수정-post-요청-작성">🎨 views 수정 (POST 요청 작성)</h2>
<ul>
<li><p>views.py 내부의 BlogDetail 클래스에다가 댓글 작성을 위한 함수를 추가</p>
<blockquote>
<ul>
<li>서버주소/blog/1/ 로 POST 요청을 보내면, 해당 1번 글에 댓글이 달리도록 작성하는 것!!</li>
</ul>
</blockquote>
</li>
<li><p>우선 <u>1번 글이 진짜로 존재하는지 확인</u>하고 →  <u>댓글 내용을 직렬화</u>한 후에 →  <u>해당 1번 글과 댓글을 연결하면서 db 에 저장</u>하도록 할 것임.</p>
</li>
</ul>
<p><span style="color: #FA8072"><strong><code>blog/views.py</code></strong></span></p>
<pre><code class="language-python">    # comment api 를 따로 분리하기도 하는데, 이번에는 간단하게 여기서 post 를 보내도록 하겠습니다
    # 프로젝트의 규모가 커진다면 posts 라는 앱, comments 라는 앱으로 분리해서 각각 진행하시는게 좋습니다
    def post(self, request, pk):
        article = Article.objects.get(pk=pk) # 우선 실제로 존재하는 글에 대해서 댓글을 달고자 하는 것인지 확인
        comment_serializer = CommentSerializer(data=request.data, partial=True) # partial=True 라고 하면, 일부 데이터가 없어도 validation 을 통과합니다.
        if comment_serializer.is_valid():
            comment_serializer.save(article=article) # 유저가 작성한 댓글을 db 에 저장할 때, 앞서 db 에서 찾은 article 과 연결하면서 저장함
            return Response({&quot;message&quot;: &quot;정상&quot;}, status=status.HTTP_200_OK)
        return Response(comment_serializer.errors, status=status.HTTP_400_BAD_REQUEST)</code></pre>
<p><strong>→ 우선은 조금 더 간단한 방식으로 진행한 것이고, 실제로는 지금 같은 경우에 <code>partial=True</code> 를 쓰지는 않음.</strong></p>
<blockquote>
<ul>
<li>** 예외처리도 조금 부족하게 되어있기 때문에, 더 정확한 방식에 대한 공부가 필요함.**</li>
</ul>
</blockquote>
<h2 id="🎨-admin-수정">🎨 admin 수정</h2>
<ul>
<li><code>admin</code> 페이지에서 확인을 좀 더 편하게 하기 위해 설정 추가</li>
</ul>
<p><span style="color: #FA8072"><strong><code>blog/admin.py</code></strong></span></p>
<pre><code class="language-python">from django.contrib import admin
from blog.models import Article, Category, Comment

# Register your models here.

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = [&quot;name&quot;, &quot;get_description&quot;]

    @admin.display(description=&quot;카테고리 안내&quot;)
    def get_description(self, obj):
        return obj.description[:30]

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = [&quot;title&quot;, &quot;get_content&quot;]

    @admin.display(description=&quot;내용&quot;)
    def get_content(self, obj):
        return  obj.content[:30]

@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = [ &quot;get_article&quot;, &quot;get_content&quot;]

    @admin.display(description=&quot;댓글&quot;)
    def get_content(self, obj):
        return  obj.content[:30]

    @admin.display(description=&quot;게시글 제목&quot;)
    def get_article(self, obj):
        return  obj.article.title</code></pre>
<h2 id="🎨-요청-보내기-1">🎨 요청 보내기</h2>
<ul>
<li>blogdetail view 에 작성했기 때문에, 아래처럼 blogdetail url 로 post 요청을 보내면 댓글이 등록됨</li>
</ul>
<blockquote>
<ul>
<li><code>POST</code> 메서드로 댓글 등록
<img src="https://velog.velcdn.com/images/brown_eyed87/post/1285c291-7bd7-43d9-a56a-8d7947a4dc48/image.png" alt=""></li>
</ul>
</blockquote>
<blockquote>
<ul>
<li><code>GET</code> 메서드로 정상 등록 여부 확인
<img src="https://velog.velcdn.com/images/brown_eyed87/post/f22b3a64-6d02-44d5-b9f5-35006af088ae/image.png" alt=""></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[221014_DRF Basic (1)]]></title>
            <link>https://velog.io/@brown_eyed87/221014DRF-Basic-1</link>
            <guid>https://velog.io/@brown_eyed87/221014DRF-Basic-1</guid>
            <pubDate>Sat, 15 Oct 2022 03:56:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 게시글은 <span style="color: #FA8072"><strong><code>김준태 강사님</code></strong></span>의 <span style="color: #FA8072"><strong><code>DRF - Basic</code></strong></span> <strong>강의를 듣고 작성</strong>하였습니다.</p>
</blockquote>
<hr>
<p>📢 <strong>수업 목표</strong></p>
<blockquote>
<ol>
<li><code>DRF</code> 로 간단한 블로그 만들기</li>
</ol>
</blockquote>
<hr>
<br>

<h1 id="✨-간단한-blog-만들기">✨ 간단한 Blog 만들기</h1>
<h2 id="🎨-poetry-로-django-프로젝트-만들기">🎨 poetry 로 django 프로젝트 만들기</h2>
<blockquote>
<p><strong><a href="https://velog.io/@brown_eyed87/220924Django-Project-%EC%B4%88%EA%B8%B0-%EC%85%8B%ED%8C%85">Django Project 초기 셋팅</a></strong> 글을 참고하여 초기 셋팅 진행</p>
</blockquote>
<h2 id="🎨-rdb에-대해서">🎨 rdb에 대해서</h2>
<p>📌 블로그를 만들기 위해 필요한 것은?</p>
<blockquote>
<ul>
<li>이용자가 글을 쓸 수 있고, 이용자가 쓴 글이 계속해서 쌓이면서 이를 다른 사람이 볼 수 있어야 함.</li>
</ul>
</blockquote>
<p>📌 바로 <code>데이터베이스</code>가 필요</p>
<blockquote>
<ul>
<li>어떤 이용자 혹은 게시글이 있는지를 데이터베이스라는 <code>정보 저장소</code>를 통해서 확인하고, 사용자나 게시글을 해당 정보 저장소에 추가하는 형태로 작업이 이루어지게 됩니다.</li>
</ul>
</blockquote>
<p>📌 django 에서는 <code>관계형 데이터베이스</code>를 사용하는데, 이를 <code>rdb</code> (relational database) 라고 함.</p>
<blockquote>
<ul>
<li>즉, 데이터베이스는 데이터베이스인데, 안의 데이터들이 서로 관계를 맺으면서 이루어져있다는 뜻!</li>
</ul>
</blockquote>
<p>📌 db 내부에서 특정 정보에 대해서 정보를 담는 공간을 <code>테이블</code>이라고 함.</p>
<blockquote>
<ul>
<li>비유를 하자면, db 는 엑셀 파일이고, 테이블은 엑셀 파일 내부의 시트임.</li>
<li>엑셀에서 시트를 여러개 만들 수 있듯, db 에서 테이블도 여러개를 만들 수 있음.</li>
</ul>
</blockquote>
<p>📌 유저가 글을 쓰는 기능을 만든다고 생각하면, 우선 <code>user 테이블</code>과 <code>post 테이블</code>이 각각 존재해야 함.</p>
<blockquote>
<ul>
<li>각각 테이블에서 각각의 열 (id, title, body 등) 을 <code>attribute</code> 혹은 <code>field</code> 라고 말함.</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/0b14d515-d5e8-45ef-8470-61528610dd3c/image.png" alt=""></p>
<p>📌 Post 테이블 내부의 <code>userId</code> 를 보고, User 테이블로 가서 해당 유저의 이름을 알 수 있음.</p>
<blockquote>
<ul>
<li>id 가 1인 글의 경우, userId 가 1 이므로, User 테이블에서 id 가 1인 사람을 찾으면,
‘김xx&#39; 이라는 사람이 글을 썼다는 것을 알 수 있음.
→ 이렇게 테이블을 서로 연결짓는 필드를 <code>foreign key</code> 라고 함.
(예시에서 post 테이블의 userId 필드를 foreign key 라고 말함.)</li>
</ul>
<ul>
<li>그리고 이렇게 id로 특정한 유저를 구분짓기 위해서는, 해당 id 가 고유한 값 (unique) 이어야 함.</li>
<li>다른 유저랑 id 가 똑같다면 다른 유저와 구별을 할 수가 없게 됨.</li>
<li>그래서 이렇게 각 테이블마다 존재하는 unique 한 id 를 <code>primary key</code> 라고 함.</li>
</ul>
</blockquote>
<p>📌 보통은 아래와 같이 연결 구조를 표현하는 diagram 을 그려서 db 에 대한 초기 설계를
진행하게 됨.</p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/7c5a58d5-cd7c-4d7f-9e2f-6469b4b755db/image.png" alt=""></p>
<h2 id="🎨-model-설정">🎨 model 설정</h2>
<blockquote>
<p><code>model</code> : 글 저장 공간 만들기</p>
</blockquote>
<p>📌 django 에서는 models.py 파일에 위와 같은 틀을 작성하고, 이후에 <code>migration</code> 이라는 행위를 통해서 실제 database 에 해당 테이블을 만들어주게 됨.</p>
<p>우선 틀을 만들기 위해, blog/models.py 에 모델 작성</p>
<p><span style="color: #FA8072"><strong><code>blog.urls.py</code></strong></span></p>
<pre><code class="language-python">from django.db import models

# Create your models here.
class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_time = models.DateTimeField(auto_now_add=True) # created_at 이라고도 합니다.
    updated_time = models.DateTimeField(auto_now=True)</code></pre>
<p>📌 이렇게 하면 Article 이라는 이름으로 하나의 테이블을 만들 수 있게 됨.</p>
<ul>
<li>모델을 만들 때는 필드마다 타입 (글자인지, 시간인지 등) 을 지정해줘야 하는데, 사용할 수 있는 Field 로는 <code>CharField</code> (단어 / 짧은 텍스트), <code>TextField</code> (긴 글), <code>DateTimeField</code> (시간) 등이 있음.<blockquote>
<ul>
<li><code>auto_now_add</code> : 처음 추가될 때 자동으로 현재 시각을 받아오겠다는 뜻</li>
<li>auto_now : 수정될 때마다 자동으로 현재 시각을 받아오겠다는 뜻
→ 이렇게 하면 사용자는 title, content 만 작성하면 되고, 작성 시각 등은 django 가 알아서 처리해줌.</li>
<li>id 를 굳이 직접 만들지 않은 이유는, primary key 로 쓸 필드는 굳이 직접 만들지 않아도 django 가 알아서 테이블 내에 id (혹은 pk) 라는 이름으로 필드를 만들어 주기 때문!</li>
</ul>
</blockquote>
</li>
</ul>
<h2 id="🎨-serializers-설정">🎨 serializers 설정</h2>
<blockquote>
<p><code>serializers</code> : 글에서 어떤 정보를 보여주거나 / 받을지를 지정</p>
</blockquote>
<p>📌  <code>request</code>와 <code>response</code></p>
<blockquote>
<ul>
<li><code>request</code> : <code>www.naver.com</code> 이라는 주소에 접근하는 것 자체가 하나의 요청. </li>
<li><code>response</code> : 그리고 접근하는 순간에 거기에 대한 응답으로 이미지, 로그인을 위한 js 코드라던지, 데이터 들이 응답으로 오게 됨</li>
</ul>
</blockquote>
<ul>
<li>글을 작성할 때도, 하나의 요청으로 <code>이러이러한 글을 써달라</code>는 요청을 우리가 만든 서버 주소로 보내게 됨.</li>
<li>거기에 대한 응답으로 <code>글 작성에 성공했다</code> 라는 내용을 내려줌.</li>
</ul>
<p>📌 Django RestFramework 에는 <code>serializer</code> 라는 것이 있음.</p>
<blockquote>
<ul>
<li><code>직렬화</code> (serialize) : db 에 저장되어있는 <code>모델 객체</code>를 프론트엔드로 전송 가능한 <code>json 형태</code>로 변형하는 것</li>
<li><code>역질렬화</code> : 직렬화의 반대로 프론트엔드에서 넘어온 데이터를 db 에 저장할 수 있도록 변환하는 과정<blockquote>
<p>정확히는 db 객체 (instance) → dict → ByteString 으로 변환하는 과정을 serialize 라고 말함.</p>
</blockquote>
</li>
<li>db 에 저장된 형태 그대로 웹사이트에 던져줄 수가 없으니, 이를 웹사이트에서 사용할 수 있는 형태로 가공하는 형태로 바꿔주는 과정임.</li>
</ul>
</blockquote>
<p>📌 drf 에서 serializer 를 작성할 때는 <code>필요한 요소만 추출</code>하도록 작성이 가능함.</p>
<blockquote>
<ul>
<li>그래서 회원 정보 (id, password 가 포함된) 를 프론트엔드 (웹사이트) 로 넘기는 과정에서도,
id 만 넘기도록 설정 가능.</li>
<li>또한 serializer 를 통해서 현재 들어온 데이터가 <code>유효한지 검사 (validation)</code> 하는 과정 또한 진행하게 됨.</li>
<li>그래서 우리가 글을 쓰기 위해서는 <code>title</code>, <code>content</code> 가 각각 필요하다고 serializer 에 명시를 했는데, 이에 대한 데이터가 안오거나 혹은 부정확하게 오는 경우에 serializer 는 해당 데이터를  db 에 저장하는 과정을 수행하지 않게 됨.</li>
</ul>
</blockquote>
<p>위 내용을 조금 더 단순화해서 설명하자면,</p>
<p>📌 serializer : 특정한 모델에 대해서, ‘프론트엔드와 통신할 때, <strong><code>주고 받을 데이터의 목록</code></strong> 을 작성’ 하는 과정</p>
<blockquote>
<ul>
<li>즉, 우선 <code>글을 저장할 공간 (db)</code> 에 글을 담기에 앞서서, <code>어떤 정보</code>를 웹사이트 이용자로부터 받을지, 어떤 정보가 필요한지 등을 작성하는 과정.
(또한 저장된 글을 이용자한테 보여줄 때 글의 어떤 정보를 보여줄 지 작성하는 과정이기도 함.)</li>
</ul>
</blockquote>
<p>더 단순하게 설명하자면, 프론트엔드와 통신할 때 주고 받을 데이터의 틀을 작성하는 과정!!</p>
<blockquote>
<ul>
<li>model을 생성함으로써 글을 작성할 공간을 만들어 놓았는데, 그래서 유저로부터 어떤 데이터를 받아서 작성할 것인지는 아직 안정함.</li>
<li>유저로부터 어떤 데이터를 받고 어떤 정보를 유저한테 보여줄 것인지를 <code>serializer.py</code>에 작성</li>
</ul>
</blockquote>
<p>📌 serializer 는 기본적으로 아래와 같이 작성하는데, model 에 앞서 작성한 모델 (데이터를 가져오고 저장할 모델) 을 명시하고 fields 에는 웹사이트 이용자로부터 받을 + 이용자에게 표시해줄 데이터를 명시함.</p>
<p><span style="color: #FA8072"><strong><code>blog/serializers.py</code></strong></span></p>
<pre><code class="language-python">from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = &quot;__all__&quot;   # [&quot;title&quot;, &quot;content&quot;]와 같은 형태로 작성하면 해당 필드만 표시</code></pre>
<blockquote>
<ul>
<li>created_time 등은 어차피 django 가 내용을 채우기 때문에, 그냥 fields 에는 사용자로부터 받을 title, content 만 작성해도 됨.</li>
<li>그 경우 글 데이터를 프론트엔드에 전달할 때에도 title, content 만 넘겨주게 됨.</li>
</ul>
</blockquote>
<p>🎴 cf) 만약 프론트엔드로부터 받을 수도 있고, 안받을 수도 있는 데이터는 아래처럼 추가로 옵션을 작성해서 명시 가능</p>
<p><span style="color: #FA8072"><strong><code>blog/models.py</code></strong></span></p>
<pre><code class="language-python">from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = [&quot;title&quot;, &quot;content&quot;]
        extra_kwargs = {&quot;title&quot;: {&quot;required&quot;: False, &quot;allow_null&quot;: True}}</code></pre>
<blockquote>
<ul>
<li>지금 당장은 사용하지 않을 예정이지만, 위와 같은 방식으로 필요한 데이터 여부를 명시할 수 있음을 참고할 것</li>
<li>models.py 에 모델을 작성할 때에도 이를 명시할 수 있는데, models.py 와 serializer 에 아무런 명시도 하지 않을 경우 작성한 필드 전부 필수적으로 필요한 데이터로 인식함.</li>
</ul>
</blockquote>
<h2 id="🎨-views-설정-get-요청">🎨 views 설정 (GET 요청)</h2>
<blockquote>
<p><code>views</code> : 주소로 들어왔을때 실행될 내부의 작업 (db에 저장, db에서 가져오기) 을 지정</p>
</blockquote>
<p>📌 views : 이용자 (프론트엔드) 로부터 요청 (+ 데이터) 을 받았을 때, 작동할 함수를 작성하는 곳</p>
<ul>
<li><p>실생활 비유</p>
<blockquote>
<ul>
<li>실생활에 비유를 해보자면, 우리가 동사무소에 주민등록증을 발급하러 가면, 민증 발급 창구에 가서 공무원에게 발급 서류를 제출함.</li>
<li>그러면 공무원은 그 서류를 토대로 일정한 작업을 한 뒤에, 주민등록증을 발급해주게 됨.</li>
</ul>
</blockquote>
</li>
<li><p>위의 예시를 웹사이트에 비유</p>
<blockquote>
<ul>
<li>&quot;우리가 <code>(웹사이트 이용자가)</code> 동사무소에 주민등록증을 발급하러 가면, 민증 발급 창구에 가서 <code>(웹사이트 URL로)</code> 공무원에게 발급 서류를 제출 <code>(요청, Request)</code> 함.</li>
<li>그러면 공무원 <code>(django)</code> 은 그 서류 <code>(요청에 포함된 데이터, Request Data)</code> 를 토대로
일정한 작업 <code>(views.py 에 작성된 함수를 실행)</code> 을 한 뒤에, 주민등록증을 발급해주게 됨. <code>(응답, Response)</code>&quot;</li>
</ul>
</blockquote>
</li>
</ul>
<p>📌 django 를 통해 글을 쓰는 과정 <code>(POST 요청)</code> 안내</p>
<ul>
<li>이 과정을 풀어서 설명하자면,</li>
</ul>
<blockquote>
<ul>
<li>웹사이트 이용자가 (글을 작성하려면), <code>글 작성 URL</code> 로 <code>글 데이터</code>를 포함해서 <code>요청</code>을 보냄</li>
<li>그러면 django 는 해당 url 에 대응하는 <code>views.py 내부의 함수를 실행</code></li>
<li>이 views.py 내부의 함수는 <code>글 데이터를 serializer 를 통해서 역직렬화</code> + <code>해당 글을 db 에 저장</code></li>
<li>글 작성 성공 여부를 <code>응답</code>으로 전달해주게 됨.</li>
</ul>
</blockquote>
<p>📌 지금은 우선 글을 가져오는 것 <code>(GET 요청)</code> 부터 먼저 진행!</p>
<ul>
<li>이 과정을 풀어서 설명하자면,<blockquote>
<ol>
<li>웹사이트 이용자가 <code>글 가져오기 URL 로 요청</code>을 보내면,</li>
<li>django 는 해당 url 에 대응하는 <code>views.py 내부의 함수를 실행</code>하고, </li>
<li>이 함수는 db 에 저장된 글들을 serializer 를 통해서 <code>직렬화</code>해서 <code>글 데이터를 응답</code>으로 전달</li>
</ol>
</blockquote>
</li>
</ul>
<ul>
<li>그러면 지금 views.py 에<blockquote>
<ol>
<li>db 에 저장된 글을 가져오고</li>
<li>글을 직렬화하고,</li>
<li>그렇게 직렬화한 글 데이터가 포함된 응답을 return 하는 함수를 작성</li>
</ol>
</blockquote>
</li>
</ul>
<p>📌 <code>CBV (Class Based View)</code></p>
<ul>
<li>특정 url 에 해당하는 views 함수를 하나의 클래스를 통해서 전부 작성하는 방식<blockquote>
<ul>
<li>BlogList 라는 클래스 안에 <code>요청 Method 별로 함수를 생성</code></li>
<li>똑같은 url 이라도 GET 방식의 요청이면 글을 db 에서 가져와서 보여주는 작업을 수행하고,</li>
<li>POST 방식의 요청이면 사용자가 보낸 데이터를 db 에 저장하는 작업을 수행하도록 작성 가능<ul>
<li><code>예시</code> : blog.com/posts/ 로 Get 요청을 보내면 작성된 글들을 받아올 수 있고, Post 요청을 보내면 글을 작성할 수 있음</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
<p><span style="color: #FA8072"><strong><code>blog/models.py</code></strong></span></p>
<pre><code class="language-python">from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleSerializer

# Create your views here.
class BlogList(APIView):
    # permission_classes : (이 클래스와 연결된) Url 로 요청을 보내는 것에 대해 권한을 설정
    # allowany : 누구나 요청을 보낼 수 있도록 하는 것
    permission_classes = [AllowAny] 

    def get(self, request):
        article = Article.objects.all() # db 에서 글 가져오기
        serialized_article_data = ArticleSerializer(article, many=True).data # 가져온 글 직렬화하기
        return Response(serialized_article_data, status=status.HTTP_200_OK) # 직렬화한 데이터 응답으로 보내기</code></pre>
<ul>
<li>우선은 get 요청부터 작성하는 것이므로, <code>def get(self, request):</code> 이라고 작성하고, 그 아래에 글을 가져와서 응답으로 전달하는 과정을 작성.<blockquote>
<ul>
<li>이렇게 하면 자동으로 get 요청에 대응해서 지금 작성한 함수가 실행됨.</li>
</ul>
</blockquote>
</li>
</ul>
<p>📌 django orm</p>
<ul>
<li>db에서 글을 과져오는 과정으로는 다음과 같은 방식들이 있음.<blockquote>
<ol>
<li><code>전체 데이터</code> 가져오기</li>
</ol>
</blockquote>
</li>
<li>*<span style="color: #20B2AA"><code>(models.py 에 작성한 모델명).objects.all()</code></span>**<blockquote>
<pre><code class="language-python"># Article.objects.all()
# Article 테이블에서 모든 데이터를 다 가져옴.</code></pre>
<ol start="2">
<li><code>일부 데이터</code> 가져오기(데이터 <code>여러개</code>)</li>
</ol>
</blockquote>
</li>
<li>*<span style="color: #20B2AA"><code>(models.py 에 작성한 모델명).objects.filter(필드명=&quot;값&quot;)</code></span>**<blockquote>
<pre><code class="language-python">Article.objects.filter(title=&quot;안녕&quot;)
# Article 테이블에서 제목에 &#39;안녕&#39; 이라고 되어있는 &#39;데이터들&#39;만 가져옴</code></pre>
<ol start="3">
<li><code>특정 데이터 하나</code> 가져오기 (데이터 딱 하나)</li>
</ol>
</blockquote>
</li>
<li>*<span style="color: #20B2AA"><code>(models.py 에 작성한 모델명).objects.get(필드명=&quot;값&quot;)</code></span>**<blockquote>
<pre><code class="language-python">Article.objects.get(pk=1)
# Article 테이블에서 id 가 1 인 (첫번째로 작성된) 글을 가져옴</code></pre>
</blockquote>
</li>
</ul>
<p>📌 데이터 직렬화</p>
<ul>
<li>serializer 의 경우, 아래와 같은 형태로 사용해서 지금 가져온 데이터를 직렬화해줄 수 있음.<blockquote>
<p><strong><span style="color: #20B2AA"><code>(serializers.py 에 작성한 시리얼라이저이름)(데이터, many=True).data</code></span></strong></p>
<pre><code class="language-python">ArticleSerializer(article, many=True).data</code></pre>
</blockquote>
</li>
</ul>
<p>📌 마지막으로는 직렬화한 데이터를 포함해서, 200 이라는 HTTP CODE 와 함께 response 형태로 응답함.</p>
<ul>
<li>이러면 프론트엔드에서는 해당 글 데이터를 받아볼 수 있게 됨.<blockquote>
<pre><code class="language-python">return Response(serialized_article_data, status=status.HTTP_200_OK)</code></pre>
</blockquote>
</li>
</ul>
<h2 id="🎨-urls-설정">🎨 urls 설정</h2>
<blockquote>
<p><code>urls</code> : 어떤 주소로 들어와야 글이 작성되거나 / 글을 보여줄 지</p>
<ul>
<li>urls.py 는 앞서 보았던 views.py 에 작성한 함수를 특정한 url 과 연결하는 과정을 수행하는 곳</li>
</ul>
</blockquote>
<ul>
<li><p><a href="http://www.naver.com">www.naver.com</a> 로 들어가면 네이버 화면이 보여지듯, <code>(서버주소)/blog/</code> 로 들어가면 글을 볼 수 있게 작성할 예정.</p>
</li>
<li><p>urls.py 는 앱마다 하나씩 작성하고, 그렇게 작성된 것들을 config (프로젝트 폴더) 내부의 urls.py 에다가 몰아넣는 식으로 작성.</p>
</li>
</ul>
<p><span style="color: #FA8072"><strong><code>blog/urls.py</code></strong></span></p>
<pre><code class="language-python">from django.urls import path
from blog.views import BlogList # 방금 작성한 views.py 내부의 클래스 import

urlpatterns = [
    path(&quot;&quot;, BlogList.as_view()),
]</code></pre>
<p><span style="color: #FA8072"><strong><code>config/urls.py</code></strong></span></p>
<ul>
<li>config/urls.py 는 아래처럼 작성해서, blog/urls.py 내부의 url 로 접근하려면 무조건 blog/ 를 붙여야하도록 작성.<blockquote>
<ul>
<li>만약 위의 blog/urls.py 에 글을 보기 위한 url 을 <code>asd/</code>라고 작성해놓고, config/urls.py 에 <code>path(&quot;blog/&quot;, include(&quot;blog.urls&quot;))</code> 라고 작성했다면, 글을 보려면 <code>(우리 서버주소)/blog/asd/</code> 로 접속해야 함.</li>
</ul>
</blockquote>
</li>
</ul>
<pre><code class="language-python">from django.urls import path
from blog.views import BlogList # 방금 작성한 views.py 내부의 클래스 import

urlpatterns = [
    path(&quot;&quot;, BlogList.as_view()),
]</code></pre>
<h2 id="🎨-admin-설정-후-서버-실행">🎨 admin 설정 후 서버 실행</h2>
<blockquote>
<p><code>admin</code> : 관리자 페이지</p>
<ul>
<li>이를 이용하면 현재 models 에 등록된 테이블에 데이터를 쉽게 채울 수 있음.</li>
</ul>
</blockquote>
<p><span style="color: #FA8072"><strong><code>blog/admin.py</code></strong></span></p>
<pre><code class="language-python">from django.contrib import admin
from .models import Article

# Register your models here.
admin.site.register(Article) # 괄호 안에는 admin 페이지에서 관리할 모델명이 들어가야함
</code></pre>
<p>📌 관리자 계정 생성</p>
<p><span style="color: #FA8072"><strong><code>Terminal에 입력</code></strong></span></p>
<pre><code class="language-python">$ poetry run python manage.py createsuperuser</code></pre>
<p>📌 서버 실행</p>
<p><span style="color: #FA8072"><strong><code>Terminal에 입력</code></strong></span></p>
<pre><code class="language-python">$ poetry run python manage.py runserver</code></pre>
<blockquote>
<ul>
<li>이제 127.0.0.1:8000/admin 로 들어가서, 방금 만든 관리자 계정으로 로그인</li>
<li>아래 메뉴에 들어가서 글을 하나 추가
<img src="https://velog.velcdn.com/images/brown_eyed87/post/6d5910fe-7f12-44ab-b835-8c52fe8eea27/image.png" alt=""></li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>이제 127.0.0.1:8000/blog/ 에 들어가면 아래처럼 표시되며 방금 작성한 글이 확인됨.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/e6fa7058-0252-4c4c-adfe-a4663d92bd24/image.png" alt=""><ul>
<li>127.0.0.1:8000/blog/ 로 GET 요청을 보내면 해당 응답이 온다는 뜻.<ul>
<li>id 는 글이 작성된 순서대로 1번부터 부여됨.</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
<h2 id="🎨-게시글-쓰기-post-요청-작성">🎨 게시글 쓰기 (POST 요청 작성)</h2>
<blockquote>
<ul>
<li>이제 게시글을 작성하기 위한 코드를 작성할텐데, 지금 게시글을 저장할 공간과 필요한 내용 (model/serializers) 는 이미 작성되어 있으니 views.py 만 추가로 작성</li>
</ul>
</blockquote>
<ul>
<li><p><code>POST 요청</code>과 관련해서 앞서 언급했던 내용을 한번 더 정리</p>
<blockquote>
<ol>
<li>웹사이트 이용자가 (글을 작성하려면),</li>
<li><code>글 작성 URL (현재는 글 작성 URL, 가져오기 URL 이 동일함)</code> 로 <code>글 데이터</code>를 포함해서 <code>요청</code> 전송</li>
<li>그러면 django 는 해당 url 에 대응하는 <code>views.py 내부의 함수를 실행</code></li>
<li>이 views.py 내부의 함수는 <code>글 데이터를 serializer 를 통해서 역직렬화</code> + <code>해당 글을 db 에 저장</code></li>
<li>글 작성 성공 여부를 <code>응답</code>으로 전달</li>
</ol>
</blockquote>
</li>
<li><p>위의 과정도 아래와 같은 코드로 표현 가능</p>
</li>
</ul>
<pre><code class="language-python">    def post(self, request):
        article_serializer = ArticleSerializer(data=request.data) # 시리얼라이저(data=request.data) 이라고 하면 request.data(사용자가 보낸 데이터)를 역직렬화하겠다는 뜻 / (data=) 이 없이 그냥 () 라면 직렬화
        if article_serializer.is_valid(): # 유효성 검사 (시리얼라이저가 자체적으로 가지고 있는 기능, 우리가 커스텀할 수 있음)
            article_serializer.save() # 유효하다면 db 에 저장 (시리얼라이저가 자체적으로 가지고 있는 기능, 저장하는 과정도 우리가 커스텀 가능 -&gt; 다음 시간에 할 예정)
            return Response({&quot;message&quot;: &quot;정상&quot;}, status=status.HTTP_200_OK)
        return Response(article_serializer.errors, status=status.HTTP_400_BAD_REQUEST)</code></pre>
<p><span style="color: #FA8072"><strong><code>blog/views.py</code></strong></span>를 아래처럼 수정</p>
<ul>
<li>위의 함수를 추가한 것</li>
</ul>
<pre><code class="language-python">class BlogList(APIView):
    permission_classes = [AllowAny] 

    def get(self, request):
        article = Article.objects.all()
        serialized_article_data = ArticleSerializer(article, many=True).data
        return Response(serialized_article_data, status=status.HTTP_200_OK)

    def post(self, request):
        article_serializer = ArticleSerializer(data=request.data)
        if article_serializer.is_valid():
            article_serializer.save()
            return Response({&quot;message&quot;: &quot;정상&quot;}, status=status.HTTP_200_OK)
        return Response(article_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
</code></pre>
<h2 id="🎨-postman-사용해보기">🎨 postman 사용해보기</h2>
<p>📌 <code>postman</code> : 우리가 만든 서버로 직접 get, post 요청 등을 보내볼 수 있도록 도와주는 프로그램</p>
<blockquote>
<p><a href="https://www.postman.com/downloads/">https://www.postman.com/downloads/</a> 접속 후 다운로드 및 설치</p>
</blockquote>
<ul>
<li>설치 완료 후 앱을 실행<blockquote>
<ul>
<li><code>Create Workspace 혹은 Collections</code> &gt; 생성한 <code>Workspace 혹은 Collections</code> 접속 &gt; <code>+</code> 클릭</li>
</ul>
</blockquote>
<img src="https://velog.velcdn.com/images/brown_eyed87/post/bae26b4e-cebe-416f-b210-72ec1d11ae9e/image.png" alt="">
<img src="https://velog.velcdn.com/images/brown_eyed87/post/17e38095-fdd3-4759-a927-6ca66ca22986/image.png" alt=""></li>
</ul>
<p>📌 GET 요청 : 주소에 localhost:8000/blog/ 라고 입력하고 send 버튼을 누르면, 하단 부분에 응답이 표시됨!</p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/aebea86e-8e9f-42be-84cb-d2cec1becb90/image.png" alt="">
<img src="https://velog.velcdn.com/images/brown_eyed87/post/b7520f3b-be13-46fe-bd36-ffc34569082a/image.png" alt=""></p>
<p>📌 POST 요청 : 글을 적는 것도 가능</p>
<blockquote>
<ol>
<li>주소창 왼쪽의 선택 버튼을 눌러서 GET → POST 로 변경</li>
<li>그 아래에서 Body 를 선택하신 뒤에, raw 를 클릭</li>
<li>그 후에 raw 의 오른쪽에 있는 선택 버튼 (text 라고 되어있는) 을 눌러서 json 으로 변경</li>
<li>이제 빈칸에다가 <code>{&quot;title”: “제목&quot;, “content”: “내용”}</code> <code>(글을 쓰기 위해 필요한 값들)</code> 이라고 작성 후, send 버튼 클릭
→ <code>글쓰기 완료!</code>
<img src="https://velog.velcdn.com/images/brown_eyed87/post/d0db81f0-e6bb-4ae6-a560-83268a0945d1/image.png" alt="">
<img src="https://velog.velcdn.com/images/brown_eyed87/post/366da96c-1052-4e1c-b097-dfffa41bc84e/image.png" alt=""></li>
</ol>
</blockquote>
<ul>
<li>다시 <code>GET</code> 요청으로 조회하여 정상 등록 여부 확인
<img src="https://velog.velcdn.com/images/brown_eyed87/post/8773946f-812b-4382-b653-ef9cf17b85ba/image.png" alt=""></li>
</ul>
<h2 id="🎨-게시글-하나만-보기수정삭제-put--delete-요청-작성">🎨 게시글 하나만 보기/수정/삭제 (PUT / DELETE 요청 작성)</h2>
<p>📌 게시글 하나만 보는 과정은 또 다른 url 과 view 를 통해서 진행할 수 있도록 작성</p>
<ul>
<li><p><code>(서버주소)/blog/</code> 로 요청을 보낸다면 글 리스트를 보는 것 <code>(혹은 글을 작성)</code> 이고,</p>
</li>
<li><p><code>(서버주소)/blog/(글 번호)</code> 로 요청 보낸다면 <code>해당 번호의 글 하나만 조회</code> <code>(혹은 글을 수정/삭제)</code></p>
<blockquote>
<ul>
<li>서버주소/blog/1 과 같은 형식, 여기서 1은 글의 id이자 pk</li>
<li>pk 이기 때문에 1 이라는 숫자만으로 글 하나를 특정할 수 있는 것이고, 해당 글을 보여주게 되는 것</li>
</ul>
</blockquote>
</li>
<li><p>blog/urls.py 에는 <code>path(&quot;&lt;int:pk&gt;&quot;, BlogDetail.as_view())</code>, 를 추가</p>
<ul>
<li>여기서 int:pk 는 글자 그대로의 url 이 아니라, url 에 int(정수형)인 값을 받을 건데,
그 값의 이름을 pk 라고 해서 views.py 에서 변수로 쓸 수 있도록 하겠다는 뜻<blockquote>
<ul>
<li>서버주소/blog/1 라는 주소로 요청이 온다면,
이 1이라는 값을 pk 라고 이름 붙여서 views 에서 사용할 수 있도록 하겠다는 뜻</li>
<li>서버주소/blog/2 라는 주소로 요청이 온다면,
이 2이라는 값을 pk 라고 이름 붙여서 views 에서 사용할 수 있도록 하겠다는 뜻</li>
</ul>
</blockquote>
</li>
</ul>
</li>
</ul>
<p><span style="color: #FA8072"><strong><code>blog/urls.py</code></strong></span></p>
<pre><code class="language-python">from django.urls import path
from blog.views import BlogList, BlogDetail   # BlogDetail 함수 추가


urlpatterns = [
    path(&quot;&quot;, BlogList.as_view()),
    path(&quot;&lt;int:pk&gt;&quot;, BlogDetail.as_view()),   # 해당 경로 추가

]</code></pre>
<ul>
<li>이제 BlogDetail 함수 작성</li>
</ul>
<p><span style="color: #FA8072"><strong><code>blog/views.py</code></strong></span></p>
<pre><code class="language-python">class BlogDetail(APIView):
    permission_classes = [AllowAny]

    def get(self, request, pk): # url 에 있는 숫자를 pk 라는 변수로 받아옴
        article = Article.objects.get(pk=pk) # pk가 1이라면 글 id가 1인 글을 찾아서 가져옴
        serialized_article_data = ArticleSerializer(article).data # 해당 글을 직렬화함
        return Response(serialized_article_data, status=status.HTTP_200_OK) # 직렬화한 글 데이터를 응답으로 보냄

    def put(self, request, pk): # put 은 수정을 요청할 때 쓰는 Method 입니다
        article = Article.objects.get(pk=pk) # 기존의 글 데이터를 db 에서 찾아옴
        # 원래라면 여기서 권한 체크
        article_serializer = ArticleSerializer(article, data=request.data) # 기존의 글과 함께, 현재 유저가 보낸 수정할 데이터 (request.data) 를 명시
        if article_serializer.is_valid(): # 유효성 검사
            article_serializer.save() # 유효하다면 수정작업 완료
            return Response(article_serializer.data) # 수정한 글 데이터를 응답으로 보냄
        return Response(article_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        article = Article.objects.get(pk=pk) # 글을 db 에서 찾아옴
        # 원래라면 여기서 권한 체크
        article.delete() # 해당 글을 db 에서 삭제함
        return Response(status=status.HTTP_204_NO_CONTENT)</code></pre>
<p>📌 Postman으로 확인</p>
<ul>
<li>아래와 같이 <code>/글id번호</code> 를 붙여서 보내면, 해당 글번호에 해당하는 것만 <code>GET</code> 하게 됨.<blockquote>
<ul>
<li>GET : 게시글 하나만 보기
<img src="https://velog.velcdn.com/images/brown_eyed87/post/5267b74f-245f-4828-bd48-bc6b55887e02/image.png" alt=""></li>
</ul>
</blockquote>
</li>
</ul>
<ul>
<li><p><code>method</code>를 <code>PUT</code>으로 변경하여 <code>BODY</code> 내용을 수정 후 보내면, 해당 글번호의 내용이 수정됨.</p>
<blockquote>
<p>PUT : 게시글 수정
<img src="https://velog.velcdn.com/images/brown_eyed87/post/ff90b2bc-a649-43ce-84f1-129c38336cfc/image.png" alt=""></p>
</blockquote>
</li>
<li><p><code>method</code>를 <code>DELETE</code>로 변경하여 보내면, 해당 글이 삭제됨.</p>
<blockquote>
<p>DELETE : 게시글 삭제
<img src="https://velog.velcdn.com/images/brown_eyed87/post/2432c2eb-8704-48c8-bb57-8153ec6a5976/image.png" alt="">
정상 삭제 여부 확인
<img src="https://velog.velcdn.com/images/brown_eyed87/post/6de80e50-ce09-4ddb-bfe4-e364628a15d0/image.png" alt=""></p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[221013_Programmers : 정렬]]></title>
            <link>https://velog.io/@brown_eyed87/221013Programmers-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@brown_eyed87/221013Programmers-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Thu, 13 Oct 2022 09:29:13 GMT</pubDate>
            <description><![CDATA[<h1 id="💰-고득점-kit--정렬">💰 <a href="https://school.programmers.co.kr/learn/courses/30/parts/12198">고득점 Kit : 정렬</a></h1>
<br>

<hr>
<br>

<h2 id="🌞-k번째수">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42748">K번째수</a></h2>
<h3 id="🚊-문제-설명">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>배열 <code>array</code>의 <code>i</code>번째 숫자부터 <code>j</code>번째 숫자까지 자르고 정렬했을 때, <code>k</code>번째에 있는 수를 구하려 합니다.</li>
</ul>
</blockquote>
<p>예를 들어 <code>array</code>가 <code>[1, 5, 2, 6, 3, 7, 4]</code>, <code>i = 2</code>, <code>j = 5</code>, <code>k = 3</code>이라면</p>
<p><code>array</code>의 <code>2</code>번째부터 <code>5</code>번째까지 자르면 <code>[5, 2, 6, 3]</code>입니다.
<code>1</code>에서 나온 배열을 정렬하면 <code>[2, 3, 5, 6]</code>입니다.
<code>2</code>에서 나온 배열의 <code>3</code>번째 숫자는 <code>5</code>입니다.</p>
<p>📢 배열 <code>arr</code>에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 <code>return</code> 하는 <code>solution</code> 함수를 완성해 주세요.</p>
<h3 id="🚊-제한사항">🚊 <code>제한사항</code></h3>
<blockquote>
<ul>
<li><code>array</code>의 길이는 <code>1</code> 이상 <code>100</code> 이하입니다.</li>
<li><code>array</code>의 각 원소는 <code>1</code> 이상 <code>100</code> 이하입니다.</li>
<li><code>commands</code>의 길이는 <code>1</code> 이상 <code>50</code> 이하입니다.</li>
<li><code>commands</code>의 각 원소는 길이가 <code>3</code>입니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/8cafde23-1b07-4bf4-9d61-0431c2f5a427/image.png" alt=""></p>
<p><code>입출력 예 #1, 2</code></p>
<blockquote>
<ul>
<li><code>[1, 5, 2, 6, 3, 7, 4]</code>를 <code>2</code>번째부터 <code>5</code>번째까지 자른 후 정렬합니다. <code>[2, 3, 5, 6]</code>의 세 번째 숫자는 <code>5</code>입니다.</li>
<li><code>[1, 5, 2, 6, 3, 7, 4]</code>를 <code>4</code>번째부터 <code>4</code>번째까지 자른 후 정렬합니다. <code>[6]</code>의 첫 번째 숫자는 6입니다.</li>
<li><code>[1, 5, 2, 6, 3, 7, 4]</code>를 <code>1</code>번째부터 <code>7</code>번째까지 자릅니다. <code>[1, 2, 3, 4, 5, 6, 7]</code>의 세 번째 숫자는 3입니다.</li>
</ul>
</blockquote>
<h3 id="📑-code--내꺼">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>반복문을 돌면서 <code>commands</code>의 각 요소 <code>command</code>를 꺼내옴</li>
<li><code>command</code> 내의 각 요소는 기존 <code>array</code>를 슬라이싱할 <code>start</code>, <code>end</code>, 그리고 슬라이싱된 리스트를 정렬 후 우리가 찾을 요소의 순서를 의미</li>
<li>단, <code>start</code>, <code>end</code>, <code>index</code>는 순번이 아닌 <code>Index No.</code>을 의미하므로 그에 맞게 조정해줘야 함.</li>
<li><code>list comprehension</code>을 이용하여 간단하게 작성</li>
<li>위의 로직을 순서대로 구현 </li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code
def solution(array, commands):
    return  [sorted(array[start-1:end])[index-1] for start,end,index in commands]</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼">📑 <code>Code : 다른 사람꺼</code></h3>
<p><strong><code>-</code></strong></p>
<ul>
<li>없음</li>
</ul>
<pre><code class="language-python">&gt; code</code></pre>
<h3 id="💡-새롭게-알게된-점">💡 새롭게 알게된 점</h3>
<p>📌 -</p>
<pre><code class="language-python">&gt; code
</code></pre>
<br>

<hr>
<br>

<h2 id="🌞-가장-큰-수">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42746">가장 큰 수</a></h2>
<h3 id="🚊-문제-설명-1">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li><code>0</code> 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.</li>
<li>예를 들어, 주어진 정수가 <code>[6, 10, 2]</code>라면 <code>[6102, 6210, 1062, 1026, 2610, 2106]</code>를 만들 수 있고, 이중 가장 큰 수는 <code>6210</code>입니다.</li>
</ul>
</blockquote>
<p>📢 <code>0</code> 또는 양의 정수가 담긴 배열 <code>numbers</code>가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 <code>return</code> 하도록 <code>solution</code> 함수를 작성해주세요.</p>
<h3 id="🚊-제한사항-1">🚊 <code>제한사항</code></h3>
<blockquote>
<ul>
<li><code>numbers</code>의 길이는 <code>1</code> 이상 <code>100,000</code> 이하입니다.</li>
<li><code>numbers</code>의 원소는 <code>0</code> 이상 <code>1,000</code> 이하입니다.</li>
<li>정답이 너무 클 수 있으니 문자열로 바꾸어 <code>return</code> 합니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-1">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/00378336-ba54-477d-adda-8b4cec3a389d/image.png" alt=""></p>
<h3 id="📑-code--내꺼-1">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>주어진 숫자들을 앞자리 수가 큰 것부터 앞에 배열해야 그것들을 이어 붙였을 때 가장 큰 수가 나옴.</li>
<li>그것을 위해 주어진 리스트의 각 숫자들의 타입을 int 가 아닌 str 으로 변환</li>
<li>그 다음에 리스트를 역순으로 정렬을 하게 되면, 앞자리 숫자가 큰 것부터 정렬하게 됨.</li>
<li>그러나 위의 방식대로 하게 되면 3, 30, 34가 주어졌을 때 34, 30, 3 순서로 정렬하게 되고 이는 34, 3, 30 을 이어 붙인 숫자보다 작게됨. (34303 &lt; 34330)</li>
<li>이를 방지하기 위해 각 숫자를 3번 이어 붙여서 최소 3자리 이상의 숫자를 만들고, 그 값들의 크기를 비교하여 역순으로 정렬하면 됨.<blockquote>
<ul>
<li>왜냐하면, 제한 사항에 numbers의 원소는 1000 이하의 숫자라는 내용이 있기 때문</li>
<li>[&#39;3&#39;, &#39;30&#39;, &#39;34&#39;]의 경우, 각 요소를 3번씩 이어 붙이면 &#39;333&#39;, &#39;303030&#39;, &#39;343434&#39;  이 되고, 이를 문자열의 크기라는 기준으로 역순 정렬하면 &#39;343434&#39; &gt; &#39;333&#39; &gt; &#39;303030&#39; 순이 되기 때문에 리스트 정렬 결과가 [&#39;34&#39;, &#39;3&#39;, 30&#39;]이 됨.</li>
</ul>
</blockquote>
</li>
</ol>
<ul>
<li>즉, 1 &gt; 2 &gt; 5 순서대로 동작하게 코드를 작성하면 됨.</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt; code
def solution(numbers):
    # 주어진 리스트의 각 요소를 문자열로 변환
    numbers = list(map(str, numbers))
    # lambda 함수를 이용하여 주어진 리스트를 각 요소를 3번씩 이어붙인 결과의 역순으로 정렬하게 됨.
    numbers.sort(key=lambda x: x * 3, reverse=True)
    # 리스트 내의 문자열을 이어붙인 뒤 정수로 변환 후 다시 문자열로 변환
    return str(int(&#39;&#39;.join(numbers)))</code></pre>
<br>

<h3 id="📑-code--다른-사람꺼-1">📑 <code>Code : 다른 사람꺼</code></h3>
<p>💡 두 수의 크기를 비교하여 <code>1</code>/<code>0</code>/<code>-1</code>을 반환하는 <code>comparator</code> 함수 생성</p>
<p>💡 <code>functools</code> 모듈의 <code>cmp_to_key</code> 함수를 이용하여 내가 만든 함수로 커스텀 정렬</p>
<ul>
<li><code>return</code> 값은 항상 <code>1</code>/<code>0</code>/<code>-1</code> 이어야 함.<blockquote>
<ul>
<li><code>1</code> : 정렬 기준대로 앞으로 가기 위해 <code>True</code>를 의미</li>
<li><code>-1</code> : 정렬 기준과 반대로 가서 뒤로 가기 위해 <code>False</code>를 의미</li>
<li><code>0</code> : <code>0</code>은 아예 정렬 기준에 해당되지 않아 그대로 있을 때를 </li>
</ul>
</blockquote>
</li>
</ul>
<p>💡 참고 : </p>
<blockquote>
<p><strong><a href="https://pearlluck.tistory.com/599">[Python] 커스텀한 기준으로 sort()하기 : cmp_to_key()</a></strong></p>
</blockquote>
<pre><code class="language-python">&gt; code
import functools

# 커스텀 정렬 함수 comparator 생성
def comparator(a,b):
    print(f&quot;a:{a}&quot;)
    print(f&quot;b:{b}&quot;)
    # t1 : 두 문자열의 수를 순서대로 이어 붙인 결과
    t1 = a+b
    # t2 : 두 문자열의 수를 반대로 이어 붙인 결과
    t2 = b+a

    print(f&quot;int(t1) &gt; int(t2) : {int(t1) &gt; int(t2)}&quot;)
    print(f&quot;int(t1) &lt; int(t2) : {int(t1) &lt; int(t2)}&quot;)
    print(f&quot;비교 결과 : {result}&quot;)
    print(&quot;==============================================&quot;)

    # t1의 숫자값이 더 크다면 1 : 더 큰 값이 뒤로 이동
    # t2의 숫자값이 더 크다면 -1  : 더 큰놈이 앞으로 이동
    # 두 값이 동일하다면 0 : 순서는 기존과 동일
    return (int(t1) &gt; int(t2)) - (int(t1) &lt; int(t2)) 

def solution(numbers):
    n = [str(x) for x in numbers]
    n = sorted(n, key=functools.cmp_to_key(comparator),reverse=True)
    answer = str(int(&#39;&#39;.join(n)))
    return answer</code></pre>
<p>🔎 <strong>예시를 통한 확인</strong></p>
<blockquote>
<ul>
<li>위의 코드에는 <code>print</code> 문이 모두 달려있는데, 예시 코드를 돌려보면 아래와 같은 결과가 나옴.
<code>예시를 통한 출력 내용 확인</code><pre><code class="language-python">numbers = [3, 30, 34, 5, 9]
solution(numbers)

</code></pre>
</li>
</ul>
<p>--- 출력 결과 ---</p>
</blockquote>
<p>a:5
b:9
int(t1) &gt; int(t2) : False
int(t1) &lt; int(t2) : True
비교 결과 : -1
==============================================
a:34
b:5
int(t1) &gt; int(t2) : False
int(t1) &lt; int(t2) : True
비교 결과 : -1
==============================================
a:30
b:34
int(t1) &gt; int(t2) : False
int(t1) &lt; int(t2) : True
비교 결과 : -1
==============================================
a:3
b:30
int(t1) &gt; int(t2) : True
int(t1) &lt; int(t2) : False
비교 결과 : 1
==============================================
a:3
b:5
int(t1) &gt; int(t2) : False
int(t1) &lt; int(t2) : True
비교 결과 : -1
==============================================
a:3
b:34
int(t1) &gt; int(t2) : False
int(t1) &lt; int(t2) : True
비교 결과 : -1
==============================================
a:3
b:30
int(t1) &gt; int(t2) : True
int(t1) &lt; int(t2) : False
비교 결과 : 1
==============================================</p>
<blockquote>
<p>--- return 값 ---
&#39;9534330&#39;</p>
</blockquote>
<pre><code>
&lt;br&gt;

---

&lt;br&gt;

## 🌞 [H-Index](https://school.programmers.co.kr/learn/courses/30/lessons/42747)


### 🚊 `문제 설명`
&gt;- **[H-Index](https://en.wikipedia.org/wiki/H-index)**는 과학자의 생산성과 영향력을 나타내는 지표입니다.
&gt;- 어느 과학자의 `H-Index`를 나타내는 값인 `h`를 구하려고 합니다. `위키백과1`에 따르면, `H-Index`는 다음과 같이 구합니다.
&gt;- 어떤 과학자가 발표한 논문 `n`편 중, `h`번 이상 인용된 논문이 `h`편 이상이고 나머지 논문이 `h`번 이하 인용되었다면 `h`의 최댓값이 이 과학자의 `H-Index`입니다.

📢 어떤 과학자가 발표한 논문의 인용 횟수를 담은 배열 `citations`가 매개변수로 주어질 때, 이 과학자의 `H-Index`를 `return` 하도록 `solution` 함수를 작성해주세요.

### 🚊 `제한사항`
&gt;- 과학자가 발표한 논문의 수는 `1편` 이상 `1,000편` 이하입니다.
&gt;- 논문별 인용 횟수는 `0회` 이상 `10,000회` 이하입니다.

### 🚊 `입출력 예`

![](https://velog.velcdn.com/images/brown_eyed87/post/a8d45d8e-77ed-4e5f-867a-a1f332f04993/image.png)

`입출력 예`
&gt;- 이 과학자가 발표한 논문의 수는 `5`편이고, 그중 `3`편의 논문은 `3`회 이상 인용되었습니다.
&gt;- 그리고 나머지 `2`편의 논문은 `3`회 이하 인용되었기 때문에 이 과학자의 `H-Index`는 `3`입니다.

### 📑 `Code : 내꺼 `

&lt;span style=&quot;color: #FA8072&quot;&gt;**`Code 1`**&lt;/span&gt;

📌 `Logic` 설명
&gt; 코드에 직접 적음

```python
&gt; code 1
def solution(citations):
    # 인용이 적게 된 논문들부터 서칭하기 위해 정렬
    citations.sort()
    # idx : 나보다 인용된 수가 적은 논문의 수
    # citation : 논문의 인용된 횟수
    for idx , citation in enumerate(citations):
        # 인용된 횟수가 남은 논문의 수(나를 포함한)보다 크거나 같으면
        if citation &gt;= len(citations) - idx :
            # 인용된 논문의 숫자를 return
            return len(citations) - idx
    # 논문이 1편밖에 없는 경우
    return 0</code></pre><p><span style="color: #FA8072"><strong><code>Code 2</code></strong></span></p>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<p><a href="https://www.ibric.org/myboard/read.php?Board=news&amp;id=270333">여기 사이트 내용 참고</a></p>
</blockquote>
<pre><code class="language-python">&gt; code 2
def solution(citations):
    # 인용이 많이 된 논문들부터 서칭하기 위해 역순으로 정렬
    citations.sort(reverse=True)
    print(f&quot;citations : {citations}&quot;)
    print(&quot;=======~~=======&quot;)
    for idx, citation in enumerate(citations):
        print(f&quot;idx : {idx}&quot;)
        print(f&quot;citation : {citation}&quot;)
        print(&quot;========&quot;)
        # H-Index를 idx라고 가정
        # citation : 나머지 논문들이 인용된 횟수 중 최댓값
        # 나머지 논문들이 인용된 횟수 중 최댓값이 idx번 이하인 최초의 경우를 찾음.
        if idx &gt;= citation:
            # idx를 반환
            return idx
    print(f&quot;citations : {citations}&quot;)
    print(&quot;--------&quot;)
    # citations를 모두 서칭하였는데 H-Index 조건을 만족하는 값이 없다면
    # citations의 길이보다 작은 값으로 리스트를 2갈래로 구분지을 수 없다는 것이기 때문에
    # citations의 길이가 우리가 찾는 H-Index
    return len(citations)</code></pre>
<p>🔎 <strong>예시를 통한 확인</strong></p>
<blockquote>
<ul>
<li>위의 코드에는 <code>print</code> 문이 모두 달려있는데, 예시 코드를 돌려보면 아래와 같은 결과가 나옴.
<code>예시를 통한 출력 내용 확인</code><pre><code class="language-python">citations = [6, 5, 5, 5, 3, 2, 1, 0]
solution(citations)

</code></pre>
</li>
</ul>
<p>--- 출력 결과 ---</p>
</blockquote>
<p>citations : [6, 5, 5, 5, 3, 2, 1, 0]
=======~~=======
idx : 0
citation : 6
========
idx : 1
citation : 5
========
idx : 2
citation : 5
========
idx : 3
citation : 5
========
idx : 4
citation : 3
========</p>
<blockquote>
<p>--- return 값 ---
&#39;9534330&#39;</p>
</blockquote>
<pre><code>


&lt;br&gt;


### 📑 `Code : 다른 사람꺼`

&lt;span style=&quot;color: #FA8072&quot;&gt;**`Code 1`**&lt;/span&gt;

- 문제 조건을 제일 직관적으로 표현한 코드
- 그러나, 주어진 `citations` 배열을 모두 순회해야 하므로 좋은 코드는 아님.

```python
&gt; code
    def solution(citations):
        # 최대값을 구하기 위해 max_라는 변수를 사용
        # 조건을 만족하는 값이 생길때마다 최대값으로 갱신하여 
        max_ = 0
        # 인용된 논문 수가 많은 것부터 탐색하기 위해 역순으로 정렬
        citations.sort(reverse=True)
        # index를 1부터 시작하는 이유는, 나를 포함한 값을 계산하기 위함
        for index, citation in enumerate(citations, start=1):
            # index : 지금 citation보다 인용된 횟수가 크거나 같은 논문의 수
            print(f&quot;index : {index}&quot;)
            # citation : 논문의 인용된 횟수
            print(f&quot;citation : {citation}&quot;)
            # (현재 논문의 인용 회수)가 (현재 논문의 인용된 횟수 이상으로 인용된 논문의 수)보다 더 크거나 같다면
            if citation &gt;= index:
                # H_index가 될 수 있는 후보로 갱신
                # index는 1부터 점차 커지고, 최종적으로 만족하는 값이 max_에 저장되므로
                # 반복문 종료 후 결국 max_ 값은 H-Index를 만족하는 값이 됨.
                max_ = index
                print(f&quot;answer : {answer}&quot;)
        return answer</code></pre><p><span style="color: #FA8072"><strong><code>Code 2</code></strong></span></p>
<ul>
<li>현재 논문보다 인용이 많이 된 논문의 횟수를 바로 체크하기 위해 주어진 배열을 역순으로 정렬</li>
<li><code>enumerate</code>의 시작을 <code>1</code>로 맞춤으로써, 배열의 <code>idx</code> 요소 값 각각이 바로 나보다 인용이 많이된 논문의 횟수를 뜻하게 됨.</li>
</ul>
<pre><code class="language-python">&gt; code
    def solution(citations):
        citations.sort(reverse=True)
        answer = max(map(min, enumerate(citations, start=1)))
        return answer</code></pre>
<ul>
<li><p>예시</p>
<blockquote>
<ul>
<li><code>citation = [1,6,2,4,0]</code> 인 경우, 역순으로 정렬하면 <code>[6,5,4,1,0]</code>이 되고,</li>
<li><code>enumerate(citations, start=1)</code> 함수 로 인해 <code>(1,6), (2,5), (3,4), (4,1), (5,0)</code> 와 같은 객체가 생성됨.</li>
<li><code>map(min, enumerate(citations, start=1))</code>의 결과, <code>(1,2,3,1,0)</code>이라는 객체가 생성됨.</li>
<li>결과적으로 <code>3</code>이 <code>return</code>됨.</li>
</ul>
</blockquote>
</li>
<li><p>해석</p>
<blockquote>
<ul>
<li>위의 코드는 각각의 <code>(idx, citation)</code> 객체에 대해 <code>x = 1</code>부터 시작하여 <code>y = index</code>, <code>y = citation</code> 라고 하는 일차 함수를 그린 후, 각각의 <code>x</code>값에 대해 <code>y</code>값의 <code>min</code> 값들만 체크한 다음에 그 중에서 <code>max</code> 값을 찾은 결과임.</li>
<li><strong><a href="https://ssuamje.tistory.com/47">https://ssuamje.tistory.com/47</a></strong>
→ 이 블로그에 관련된 내용이 더 자세히 적혀 있으니 참고할 것.</li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="💡">💡</h3>
<p>📌 </p>
<br>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[221013_[토픽클리핑 및 독서] API 및 DRF]]></title>
            <link>https://velog.io/@brown_eyed87/221013%ED%86%A0%ED%94%BD%ED%81%B4%EB%A6%AC%ED%95%91-%EB%B0%8F-%EB%8F%85%EC%84%9C-API-%EB%B0%8F-DRF</link>
            <guid>https://velog.io/@brown_eyed87/221013%ED%86%A0%ED%94%BD%ED%81%B4%EB%A6%AC%ED%95%91-%EB%B0%8F-%EB%8F%85%EC%84%9C-API-%EB%B0%8F-DRF</guid>
            <pubDate>Thu, 13 Oct 2022 02:01:07 GMT</pubDate>
            <description><![CDATA[<h1 id="🌞-api란">🌞 <strong>API란</strong></h1>
<ul>
<li><code>API</code>는 <u>정의 및 프로토콜 집합을 사용하여 두 소프트웨어 구성 요소가 서로 통신할 수 있게 하는 메커니즘</u></li>
</ul>
<blockquote>
<p><strong><code>예시</code></strong>
기상청의 소프트웨어 시스템에는 일일 기상 데이터가 들어 있을 때, 휴대폰의 날씨 앱은 API를 통해 이 시스템과 &quot;대화&quot;하고 휴대폰에 매일 최신 날씨 정보를 표시</p>
</blockquote>
<br>


<h2 id="✅-api의-의미">✅ <strong>API의 의미</strong></h2>
<ul>
<li><code>API</code>는 <code>Application Programming Interface</code>(애플리케이션 프로그램 인터페이스)의 줄임말 </li>
<li><code>API</code>의 맥락에서 <code>애플리케이션</code>이라는 단어는 <u>고유한 기능을 가진 모든 소프트웨어</u></li>
<li><code>인터페이스</code>는 <u>두 애플리케이션 간의 서비스 계약</u>이라고 할 수 있습니다. </li>
<li>이 계약은 <u>요청과 응답을 사용하여 두 애플리케이션이 서로 통신하는 방법을 정의</u>함.</li>
<li><code>API 문서</code>에는 <u>개발자가 이러한 요청과 응답을 구성하는 방법에 대한 정보가 포함</u>되어 있음.</li>
</ul>
<br>

<h2 id="✅-api-작동-방식">✅ <strong>API 작동 방식</strong></h2>
<ul>
<li><code>API</code> 아키텍처는 일반적으로 클라이언트와 서버 측면에서 설명됨.</li>
<li><code>클라이언트</code> : 요청을 보내는 애플리케이션</li>
<li><code>서버</code> : 응답을 보내는 애플리케이션<blockquote>
<ul>
<li>날씨 예에서 기상청의 날씨 데이터베이스는 서버이고 모바일 앱은 클라이언트에 해당.</li>
</ul>
</blockquote>
</li>
</ul>
<p><code>API</code>가 생성된 시기와 이유에 따라 <code>API</code>는 네 가지 방식으로 작동 가능.</p>
<h3 id="📋-soap-api">📋 <strong>SOAP API</strong></h3>
<ul>
<li>이 <code>API</code>는 <u>단순 객체 접근 프로토콜을 사용</u></li>
<li><strong>클라이언트</strong>와 <strong>서버</strong>는 <code>XML</code>을 사용하여 <u>메시지를 교환</u><blockquote>
<ul>
<li>과거에 더 많이 사용되었으며 유연성이 떨어지는 <code>API</code></li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="📋-rpc-api">📋 <strong>RPC API</strong></h3>
<ul>
<li>이 <code>API</code>를 <u>원격 프로시저 호출</u>이라고 함.</li>
<li><strong>클라이언트</strong>가 서버에서 함수나 프로시저를 완료하면 <strong>서버</strong>가 출력을 클라이언트로 다시 전송</li>
</ul>
<h3 id="📋-websocket-api">📋 <strong>Websocket API</strong></h3>
<ul>
<li><a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-overview?pg=wianapi&amp;cta=websocketapi">Websocket API</a>는 <code>JSON</code> 객체를 사용하여 데이터를 전달하는 또 다른 최신 웹 <code>API</code> 개발</li>
<li><code>WebSocket API</code>는 클라이언트 앱과 서버 간의 <u>양방향 통신을 지원</u></li>
<li><u>서버가 연결된 클라이언트에 콜백 메시지를 전송할 수 있어 <code>REST API</code>보다 효율적</u></li>
</ul>
<h3 id="📋-rest-api">📋 <strong>REST API</strong></h3>
<ul>
<li>오늘날 웹에서 볼 수 있는 가장 많이 사용되고 유연한 <code>API</code><br></li>
<li><strong>클라이언트</strong>가 서버에 <u>요청을 데이터로 전송</u></li>
<li><strong>서버</strong>가 이 클라이언트 입력을 사용하여 내부 함수를 시작하고 <u>출력 데이터를 다시 클라이언트에 반환</u></li>
</ul>
<br>

<h1 id="🌞-rest-api">🌞 <strong>REST API</strong></h1>
<ul>
<li><code>REST</code>는 <code>Representational State Transfer</code>의 줄임말<blockquote>
<ul>
<li><code>REST</code> :  <u>클라이언트가 서버 데이터에 액세스하는 데 사용할 수 있는 <code>GET</code>, <code>PUT</code>, <code>UPDATE</code>, <code>DELETE</code> 등의 함수 집합</u></li>
</ul>
</blockquote>
</li>
<li><u>클라이언트와 서버는 <code>HTTP</code>를 사용하여 데이터를 교환</u><br></li>
<li><a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest?pg=wianapi&amp;cta=restapi">REST API</a>의 주된 특징은 <strong>무상태</strong><blockquote>
<ul>
<li><code>무상태</code>는 서버가 요청 간에 클라이언트 데이터를 저장하지 않음을 의미</li>
</ul>
</blockquote>
</li>
<li>서버에 대한 클라이언트 요청은 웹 사이트를 방문하기 위해 브라우저에 입력하는 URL과 유사함.</li>
<li>서버의 응답은 웹 페이지의 일반적인 그래픽 렌더링이 없는 일반 데이터</li>
</ul>
<br>

<h2 id="✅-rest-api의-이점">✅ REST API의 이점</h2>
<p><code>REST API는 다음과 같은 네 가지 주요 이점을 제공</code></p>
<p><strong>1. 통합</strong></p>
<ul>
<li><code>API</code>는 새로운 애플리케이션을 기존 소프트웨어 시스템과 통합하는 데 사용됨.</li>
<li>그러면 각 기능을 처음부터 작성할 필요가 없기 때문에 개발 속도가 향상됨.</li>
<li><code>API</code>를 사용하여 기존 코드를 활용 가능.</li>
</ul>
<p><strong>2. 혁신</strong></p>
<ul>
<li>새로운 앱의 등장으로 전체 산업이 바뀔 수 있음.</li>
<li>기업은 신속하게 대응하고 혁신적인 서비스의 신속한 배포를 지원해야 함.</li>
<li>전체 코드를 다시 작성할 필요 없이 <code>API</code> 수준에서 변경하여 이를 수행 가능.</li>
</ul>
<p><strong>3. 확장</strong></p>
<ul>
<li><code>API</code>는 기업이 다양한 플랫폼에서 고객의 요구 사항을 충족할 수 있는 고유한 기회를 제공<blockquote>
<ul>
<li>예를 들어 지도 <code>API</code>를 사용하면 <code>웹 사이트</code>, <code>Android</code>, <code>iOS</code> 등을 통해 지도 정보를 통합 가능.</li>
</ul>
</blockquote>
</li>
<li>어느 기업이나 무료 또는 유료 <code>API</code>를 사용하여 내부 데이터베이스에 유사한 액세스 권한을 부여 가능</li>
</ul>
<p><strong>4. 유지 관리의 용이성</strong></p>
<ul>
<li><code>API</code>는 두 시스템 간의 게이트웨이 역할을 함.</li>
<li><code>API</code>가 영향을 받지 않도록 각 시스템은 내부적으로 변경해야 함. </li>
<li>이렇게 하면 한 시스템의 향후 코드 변경이 다른 시스템에 영향을 미치지 않음.</li>
</ul>
<br>

<h2 id="✅-rest-api-보호-방안">✅ REST API 보호 방안</h2>
<p><strong><code>모든 API는 적절한 인증 및 모니터링을 통해 보호되어야 함.</code></strong></p>
<p><strong>REST API를 보호하는 두 가지 주요 방법</strong></p>
<p>*<em>1. 인증 토큰 *</em></p>
<ul>
<li>인증 토큰은 사용자에게 <code>API</code> 호출을 수행할 수 있는 권한을 부여하는 데 사용됨.</li>
<li>인증 토큰은 사용자가 자신이 누구인지 확인하고 해당 특정 <code>API</code> 호출에 대한 액세스 권한이 있는지 확인<blockquote>
<ul>
<li>예를 들어, 이메일 서버에 로그인하면 이메일 클라이언트는 보안 액세스를 위해 인증 토큰을 사용</li>
</ul>
</blockquote>
</li>
</ul>
<p>*<em>2. API 키 *</em></p>
<ul>
<li><p><code>API</code> 키는 <code>API</code>를 호출하는 프로그램 또는 애플리케이션을 확인</p>
<blockquote>
<ul>
<li>즉, 애플리케이션을 식별하고 애플리케이션에 특정 <code>API</code> 호출을 수행하는 데 필요한 액세스 권한이 있는지 확인</li>
</ul>
</blockquote>
</li>
<li><p><code>API</code> 키는 토큰만큼 안전하지 않지만 사용량에 대한 데이터를 수집하기 위해 <code>API</code> 모니터링을 허용함.</p>
<blockquote>
<ul>
<li>다른 웹 사이트를 방문할 때 브라우저 <code>URL</code>에서 긴 문자열과 숫자를 본 적이 있을 것임. </li>
<li>이 문자열은 웹 사이트가 내부 <code>API</code> 호출을 수행하는 데 사용하는 <code>API</code> 키임.</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h1 id="🌞-django-rest-framework">🌞 Django REST Framework</h1>
<p><strong><code>Django</code></strong> vs <strong><code>Django REST Framework</code></strong></p>
<ul>
<li>기존의 <code>DJango</code> 프로젝트 : 기존 자체적인 웹 템플릿에게 바로 데이터를 전달</li>
<li><code>Django REST Framework</code>를 사용 시,<code>JSON</code>과 같은 양식으로 다양한 플랫폼의 클라이언트에게 데이터를 제공해줄 수 있는 <code>API 서버 프로젝트</code>가 만들어지게 되는 것</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/278e34da-acf8-4b20-bbab-e874398a562e/image.png" alt=""></p>
<p><strong><code>Django REST Framework</code></strong></p>
<ul>
<li><p><code>Django</code>를 기반으로 <code>REST API 서버</code>를 만들기 위한 라이브러리</p>
</li>
<li><p><strong><a href="https://velog.io/@brown_eyed87/220927%EB%8F%85%EC%84%9C-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88">API</a></strong>는 웹 뿐만 아니라 앱과 같은 다양한 플랫폼의 백엔드 서비스를 위해 <code>JSON</code> 과 같은 규격화된 데이터를 제공.</p>
</li>
<li><p><code>DJango REST Framework</code>는 얻디까지나 <code>Django</code>를 기반으로 한 라이브러리이기 때문에 전반적으로 <code>Django</code>의 개발 흐름을 따라감. </p>
<blockquote>
<ul>
<li><code>REST API</code>가 되기 위해 달라지는 부분들이 조금 있음.
→ <code>DJango REST Framework</code> 학습 시, 그 부분이 무엇인지에 초점을 두고 정리하고 숙지하는 과정을 반드시 거쳐야 함!!</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<hr>
<br>

<h1 id="🌞-ref">🌞 REF</h1>
<p><strong><code>Site</code></strong></p>
<blockquote>
<p><strong><a href="https://aws.amazon.com/ko/what-is/restful-api/">AWS : RESTful API란 무엇입니까?</a></strong></p>
</blockquote>
<p><strong><code>Book</code></strong></p>
<blockquote>
<p><strong><a href="https://product.kyobobook.co.kr/detail/S000061350567">백엔드를 위한 Django REST Framework with 파이썬</a></strong></p>
</blockquote>
<h2 id="✅-추가-학습-예정">✅ 추가 학습 예정</h2>
<blockquote>
<p><strong><a href="https://bcho.tistory.com/953">REST API의 이해와 설계 #1-개념 소개</a></strong>
<strong><a href="https://white-mouse.tistory.com/5">Django Rest Framework</a></strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[221010_Programmers : 스택/큐]]></title>
            <link>https://velog.io/@brown_eyed87/221010Programmers-%EC%8A%A4%ED%83%9D%ED%81%90</link>
            <guid>https://velog.io/@brown_eyed87/221010Programmers-%EC%8A%A4%ED%83%9D%ED%81%90</guid>
            <pubDate>Mon, 10 Oct 2022 15:19:38 GMT</pubDate>
            <description><![CDATA[<h1 id="💰-고득점-kit--스택큐">💰 <a href="https://school.programmers.co.kr/learn/courses/30/parts/12081">고득점 Kit : 스택/큐</a></h1>
<br>

<hr>
<br>

<h2 id="🌞-같은-숫자는-싫어">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/12906">같은 숫자는 싫어</a></h2>
<h3 id="🚊-문제-설명">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>배열 <code>arr</code>가 주어집니다. 배열 <code>arr</code>의 각 원소는 숫자 <code>0</code>부터 <code>9</code>까지로 이루어져 있습니다. </li>
<li>이때, 배열 <code>arr</code>에서 연속적으로 나타나는 숫자는 하나만 남기고 전부 제거하려고 합니다.</li>
<li>단, 제거된 후 남은 수들을 반환할 때는 배열 <code>arr</code>의 원소들의 순서를 유지해야 합니다. 예를 들면,<blockquote>
<ul>
<li><code>arr</code> = <code>[1, 1, 3, 3, 0, 1, 1]</code> 이면 <code>[1, 3, 0, 1]</code> 을 <code>return</code> 합니다.</li>
<li><code>arr</code> = <code>[4, 4, 4, 3, 3]</code> 이면 <code>[4, 3]</code> 을 <code>return</code> 합니다.</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
<p>📢 배열 <code>arr</code>에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 <code>return</code> 하는 <code>solution</code> 함수를 완성해 주세요.</p>
<h3 id="🚊-제한사항">🚊 <code>제한사항</code></h3>
<blockquote>
<ul>
<li>배열 <code>arr</code>의 크기 : <code>1,000,000</code> 이하의 자연수</li>
<li>배열 <code>arr</code>의 원소의 크기 : <code>0</code>보다 크거나 같고 <code>9</code>보다 작거나 같은 정수</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/0fec84a5-1612-405c-9571-1d8e20e7330c/image.png" alt=""></p>
<p><code>입출력 예 #1, 2</code></p>
<ul>
<li>문제의 예시와 같습니다.</li>
</ul>
<h3 id="📑-code--내꺼">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>중복을 제거한 요소를 담을 새로운 리스트 <code>answer</code>를 빈 리스트로 지정하고, 문제에서 주어진 <code>array</code>의 0 번째 요소를 담음</li>
<li>주어진 <code>array</code>의 1 번째 요소부터 마지막 요소까지 반복문을 돌면서 </li>
<li><code>answer</code>의 마지막 요소와 현재 <code>array</code>의 요소를 비교하여 동일하지 않은 경우에만 <code>array</code>의 요소를 <code>answer</code>에 담음</li>
<li>반복문 종료 후 <code>answer</code>를 <code>return</code></li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code
def solution(arr):
    answer = [arr[0]]
    length = len(arr)
    for i in range(1,length):
        if answer[-1] != arr[i]:
            answer.append(arr[i])
    return answer</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼">📑 <code>Code : 다른 사람꺼</code></h3>
<p><strong><code>정용님 코드</code></strong></p>
<ul>
<li>변수 지정도 깔끔하고, 초기 비교 대상을 -1로 지정함으로써 첫 번째 요소에 대해 예외적으로 코드를 작성해야 했던 내 코드보다 <code>클-린</code>한듯하여 좋았음.</li>
</ul>
<pre><code class="language-python">&gt; code
def solution(arr):
    # 중복을 제거한 요소를 담을 빈 리스트 생성
    answer = []

    # 기존 array의 요소와 비교할 대상을 front라는 변수로 지정 후 -1을 할당
    #   왜 -1인가?에 대해 반드시 기억해야 함!! 자주 쓰이는 기법
    #   문제의 &#39;제한사항&#39;에서 「 0 ≤ 배열 arr의 원소의 크기 ≤ 9 」라는 조건을 주었기 때문
    #    array의 어떤 요소와도 겹치지 않는 값이기 때문에 비교 대상으로 적절함
    front = -1

    # 반복문을 돌면서
    for item in arr:
        # 만약 array 내의 요소(item)가 비교 대상(front)와 다른 경우에만
        if front != item:
            # answer에 해당 요소를 추가
            answer.append(item)
            # 비교 대상을 answer의 마지막 요소인 item으로 갱신
            front  = item
        return answer</code></pre>
<p><strong><code>프로그래머스 코드</code></strong></p>
<ul>
<li>반복문을 돌면서 현재 요소와 그 뒷 요소를 비교하며 값이 다른 경우에만 새로운 리스트에 담는 방식으로 작성한 코드</li>
</ul>
<pre><code class="language-python">&gt; code
def solution(arr):
    return [arr[i] for i in range(len(arr)) if [arr[i]] != arr[i+1:i+2]]</code></pre>
<h3 id="💡-새롭게-알게된-점">💡 새롭게 알게된 점</h3>
<p>📌 <u>마지막 요소를 확인할 때, 리스트의 <code>indexing</code>이 아닌, <code>slicing</code>으로 코드를 작성함으로써 에러 발생을 방지하였다는 점</u>에 대해 감탄했음.</p>
<blockquote>
<ul>
<li>리스트는 존재하지 않는 범위에 대해 <code>slicing</code>하는 경우, 빈 리스트<code>[]</code>를 반환</li>
<li>반면에, 존재하지 않는 범위의 <code>index</code>에 대해 <code>indexing</code>하는 경우, <code>IndexError</code> 발생</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt; code
    array = [1,2,3,4,5]
    print(array[6])       #   IndexError: list index out of range
    print(array[6:])      #   []</code></pre>
<br>

<hr>
<br>

<h2 id="🌞-올바른-괄호">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/12909">올바른 괄호</a></h2>
<h3 id="🚊-문제-설명-1">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>괄호가 바르게 짝지어졌다는 것은 &#39;(&#39; 문자로 열렸으면 반드시 짝지어서 &#39;)&#39; 문자로 닫혀야 한다는 뜻입니다. </li>
<li>예를 들어<blockquote>
<p><code>()()</code> 또는 <code>(())()</code> 는 올바른 괄호입니다.
<code>)()(</code> 또는 <code>(()(</code> 는 올바르지 않은 괄호입니다.</p>
</blockquote>
</li>
</ul>
</blockquote>
<p>📢 <code>(</code> 또는 <code>)</code> 로만 이루어진 문자열 <code>s</code>가 주어졌을 때, 문자열 s가 올바른 괄호이면 <code>True</code>를 <code>return</code> 하고, 올바르지 않은 괄호이면 <code>False</code>를 <code>return</code> 하는 <code>solution</code> 함수를 완성해 주세요.</p>
<h3 id="🚊-제한사항-1">🚊 <code>제한사항</code></h3>
<blockquote>
<ul>
<li>문자열 <code>s</code>의 길이 : <code>100,000</code> 이하의 자연수</li>
<li>문자열 <code>s</code>는 <code>(</code> 또는 <code>)</code> 로만 이루어져 있습니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-1">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/4774c3f5-fce8-42e6-8b7c-e95a292bad09/image.png" alt=""></p>
<p><code>입출력 예 #1, 2, 3, 4</code></p>
<ul>
<li>문제의 예시와 같습니다.</li>
</ul>
<h3 id="📑-code--내꺼-1">📑 <code>Code : 내꺼</code></h3>
<p><span style="color: #FA8072"><strong><code>Code 1</code></strong></span></p>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li><code>stack</code>으로 활용할 빈 리스트 생성</li>
<li>주어진 배열 <code>s</code>의 요소에 대해 반복문을 돌면서</li>
<li>열린 괄호&#39;<code>(</code>&#39;인 경우에는 <code>stack</code>에 담고</li>
<li>그렇지 않는 경우(닫힌 괄호&#39;<code>)</code>&#39;)에는 <code>stack</code>의 상황을 보고 판단
4-1. <code>stack</code>이 비어 있는 경우에는 짝을 맞출 열린 괄호&#39;<code>(</code>&#39;가 없는 것이기 때문에 <code>False</code>를 반환하고 종료
4-2. <code>stack</code>이 비어 있지 않은 경우에는 <code>stack</code>의 마지막 요소가 열린 괄호&#39;<code>(</code>&#39;일 때 stack의 마지막 요소를 추출<blockquote>
<ul>
<li>왜냐하면, <code>stack</code>의 마지막 요소인 열린 괄호&#39;<code>(</code>&#39;와 주어진 배열에서 현재 요소인 닫힌 괄호&#39;<code>)</code>&#39;가 짝을 맞춰 사라지기 때문</li>
</ul>
</blockquote>
</li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code 1
def solution(s):
    stack = []
    for i in s:
        if i == &quot;(&quot;:
            stack .append(i)
        else:
            if not stack:
                return False
            elif stack[-1] == &quot;(&quot;:
                stack.pop()
    # stack이 비어 있지 않은지의 유무를 참/거짓으로 return
    #   stack이 비었다면 True, 비어있지 않다면 False를 반환
    return not stack </code></pre>
<p><span style="color: #FA8072"><strong><code>Code 2</code></strong></span></p>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>열린 괄호 &#39;<code>(</code>&#39;에는 <code>+1</code>, 닫힌 괄호 &#39;<code>)</code>&#39;에는 <code>-1</code>에 해당하는 점수를 부여</li>
<li>열린 괄호와 닫힌 괄호가 순서대로 짝을 맞추면 점수는 <code>+1</code> 되었다가 <code>-1</code>되어서 결과적으로 <code>0</code>점으로 종료</li>
<li>만약 점수가 <code>음수</code>로 가게 된다면, 열린 괄호 없이 닫힌 괄호가 나왔다는 말이기 때문에 무조건 짝을 맞추지 못하였다는 것을 의미하므로 <code>False</code>를 반환해야 함.</li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code 1
def solution(s):
    score = 0
    for w in s:
        if score &lt; 0:
            return 0
        else:
            if w == &quot;(&quot;:
                score += 1 
            elif w == &quot;)&quot;:
                score -= 1
    # score이 0점과 동일한지 유무를 참/거짓으로 반환
    #   score가 0점이면 참이므로 True를 반환
    return score == 0</code></pre>
<br>

<h3 id="📑-code--다른-사람꺼-1">📑 <code>Code : 다른 사람꺼</code></h3>
<ul>
<li><u>s 내의 요소가 닫힌 괄호&#39;<code>)</code>&#39;인 경우</u>, <code>stack</code>으로 사용하는 리스트 <code>st</code>의 마지막에 어떤 요소가 있는 지와 무관하게 <code>pop</code> 메서드를 실행
💡 단, <u><code>pop</code> 메서드를 실행하였는데 에러가 발생했다면, <code>st</code>가 빈 리스트라는 뜻이기 때문에 닫힌 괄호와 짝을 맞출 요소가 남아있지 않다는 뜻이 되어 <code>False</code>를 반환</u></li>
</ul>
<pre><code class="language-python">&gt; code
def is_pair(s):
    st = list()
    for c in s:
        if c == &#39;(&#39;:
            st.append(c)

        if c == &#39;)&#39;:
            try:
                st.pop()
            except IndexError:
                return False

    return len(st) == </code></pre>
<p><strong><code>프로그래머스 코드</code></strong></p>
<ul>
<li>반복문을 돌면서 현재 요소와 그 뒷 요소를 비교하며 값이 다른 경우에만 새로운 리스트에 담는 방식으로 작성한 코드</li>
</ul>
<pre><code class="language-python">&gt; code
def solution(arr):
    return [arr[i] for i in range(len(arr)) if [arr[i]] != arr[i+1:i+2]]</code></pre>
<br>

<hr>
<br>

<h2 id="🌞-기능개발">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42586">기능개발</a></h2>
<h3 id="🚊-문제-설명-2">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다.</li>
<li>각 기능은 진도가 <code>100%</code>일 때 서비스에 반영할 수 있습니다.</li>
<li>또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.</li>
</ul>
</blockquote>
<p>📢 먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 <code>progresses</code>와 각 작업의 개발 속도가 적힌 정수 배열 <code>speeds</code>가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 <code>return</code> 하도록 <code>solution</code> 함수를 완성하세요.</p>
<h3 id="🚊-제한사항-2">🚊 <code>제한사항</code></h3>
<blockquote>
<ul>
<li>작업의 개수(<code>progresses</code>, <code>speeds</code> 배열의 길이)는 <code>100</code>개 이하입니다.</li>
<li>작업 진도는 <code>100</code> 미만의 자연수입니다.</li>
<li>작업 속도는 <code>100</code> 이하의 자연수입니다.</li>
<li>배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다.</li>
<li>예를 들어 진도율이 <code>95%</code>인 작업의 개발 속도가 하루에 <code>4%</code>라면 배포는 <code>2</code>일 뒤에 이루어집니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-2">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/e6a681f5-0afa-4e2a-8e99-608997ebd2a8/image.png" alt=""></p>
<p><code>입출력 예 #1</code></p>
<blockquote>
<ul>
<li>첫 번째 기능은 <code>93%</code> 완료되어 있고 하루에 <code>1%</code>씩 작업이 가능하므로 <code>7일</code>간 작업 후 배포가 가능합니다.</li>
<li>두 번째 기능은 <code>30%</code>가 완료되어 있고 하루에 <code>30%</code>씩 작업이 가능하므로 <code>3일</code>간 작업 후 배포가 가능합니다.</li>
<li>하지만 이전 첫 번째 기능이 아직 완성된 상태가 아니기 때문에 첫 번째 기능이 배포되는 <code>7일</code>째 배포됩니다.</li>
<li>세 번째 기능은 <code>55%</code>가 완료되어 있고 하루에 <code>5%</code>씩 작업이 가능하므로 <code>9일</code>간 작업 후 배포가 가능합니다.</li>
</ul>
</blockquote>
<ul>
<li>따라서 <code>7</code>일째에 <code>2</code>개의 기능, <code>9</code>일째에 <code>1</code>개의 기능이 배포됩니다.</li>
</ul>
<p><code>입출력 예 #2</code></p>
<blockquote>
<ul>
<li>모든 기능이 하루에 <code>1%</code>씩 작업이 가능하므로, 작업이 끝나기까지 남은 일수는 각각 <code>5</code>일, <code>10</code>일, <code>1</code>일, <code>1</code>일, <code>20</code>일, <code>1</code>일입니다.</li>
<li>어떤 기능이 먼저 완성되었더라도 앞에 있는 모든 기능이 완성되지 않으면 배포가 불가능합니다.</li>
</ul>
</blockquote>
<ul>
<li>따라서 <code>5</code>일째에 <code>1</code>개의 기능, <code>10</code>일째에 <code>3</code>개의 기능, <code>20</code>일째에 <code>2</code>개의 기능이 배포됩니다.</li>
</ul>
<h3 id="📑-code--내꺼-2">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<p>코드에 직접 적음</p>
</blockquote>
<pre><code class="language-python">&gt; code
# 올림 함수 계산을 위해 math 모듈 사용
from math import ceil

def solution(progresses, speeds):
    # 작업 별 필요한 요일을 계산하여 그 수치를 담을 빈 리스트 생성
    day_required = []

    # 작업의 진척율, 개발 속도를 zip 함수로 묶어서 각각의 요소들에 대해 반복문을 실행
    for p, s in zip(progresses, speeds):
        # 잔여 작업 일수 : 남은 진척율을 개발 속도로 나누어 올림 계산한 결과
        # 그 결과를 day_required 리스트에 추가
        day_required.append(ceil((100-p)/s))
    # 마지막 요소까지 계산을 해야 하기 때문에, 해당 리스트의 최대값보다 하나 더 큰 값을 다시 day_required 리스트에 추가
    day_required.append(max(day_required)+1)

    # 아래 반복문을 통해 한꺼번에 배포가 되어야 하는 작업들을 담을 빈 리스트 temp 생성
    temp = []
    # 정답을 담을 빈 리스트 answer 생성
    answer = []

    # 위에서 day_required 리스트의 마지막 요소에 내가 지정한 값을 추가해두었기 때문에
    # 반복문을 실행할 범위를 day_required의 전체 요소의 개수에서 1만큼을 제외시킴.
    for i in range(len(day_required)-1):
        # day_required 리스트에서 0번째부터 i번째 요소까지의 최대값이 (i+1)요소보다 작을 때에만
        if max(day_required[:i+1]) &lt; day_required[i+1]:
            # temp 리스트에 day_required의 현재 요소를 추가
            temp.append(day_required[i])
            # answer 리스트에는 temp 리스트의 개수(이번 배포 시 배포될 기능의 총 개수)를 추가
            answer.append(len(temp))
            # 다음 순서를 실행하기 위해, temp 리스트 초기화
            temp.clear()

        # day_required 리스트에서 0번째부터 i번째 요소까지의 최대값이 (i+1)요소보다 크거나 같다면
        else:
            # temp 리스트에 day_required의 현재 요소를 추가
            temp.append(day_required[i])

    return answer</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼-2">📑 <code>Code : 다른 사람꺼</code></h3>
<p><strong><code>상봉님 코드</code></strong></p>
<ul>
<li><code>defaultdict</code> 함수 사용이 아주 탁월했음.<blockquote>
<ul>
<li><code>key : value</code> 형태의 요소를 담은 <code>dictionary</code> 자료형을 생성 시, 기존 <code>key</code>값이 없는 요소에 대해 자료형에 추가하는 경우 사용하면 좋음.</li>
<li>일반적인 <code>dict</code> 자료형은 기존에 보유하고 있지 않은 <code>key</code>값에 대해 <code>key:value</code>를 넣어주려고 하면 <code>error</code>가 발생.</li>
</ul>
</blockquote>
</li>
</ul>
<pre><code class="language-python">&gt; code
from collections import defaultdict

def solution(progresses, speeds):

    # 역순으로 바꿔서 가장앞의 작업이 스택상 맨 위로 오도록 설정
    progresses = progresses[::-1]
    speeds = speeds[::-1]

    # [(55, 5), (30, 30), (93, 1)] 식으로 현재진행량과 작업속도를 묶어줌
    suc = list(zip(progresses,speeds))
    print(suc)

    # 키값이 없을때 해당 키값으로 value를 추가하면 오류가 발생하지않는 defaultdict() 함수 이용
    dic = defaultdict(int)
    print(dic)

    # 몇일이 경과했는지 저장할 day 선언
    day = 1

    # 모든 작업이 스택을 빠져나갈때까지 반복(작업완료)
    while len(suc) != 0:
        # 날짜 x 속도가 100프로를 넘는다면 맨위의 작업을 완료처리
        if suc[-1][0] + suc[-1][1]*day &gt;= 100:
            print(suc[-1][0] + suc[-1][1]*day)
            print(day)
            suc.pop()
            print(suc)
            dic[day] += 1
        # 작업이 완료되지 않았다면 그냥 day만 1증가
        else:
            day += 1
        print(dic)

    return list(dic.values())</code></pre>
<p><strong><code>프로그래머스 코드</code></strong></p>
<ul>
<li>반복문을 돌면서 현재 요소와 그 뒷 요소를 비교하며 값이 다른 경우에만 새로운 리스트에 담는 방식으로 작성한 코드</li>
</ul>
<pre><code class="language-python">&gt; code
def solution(progresses, speeds):
    # [현재 작업, 누적 배포 예정 기능 수] 를 리스트 형태의 요소로 담을 빈 리스트 Q를 생성
    # Q는 결과적으로 2차원 리스트 형태가 됨.
    Q=[]
    # 작업의 진척율, 개발 속도를 zip 함수로 묶어서 각각의 요소들에 대해 반복문을 실행
    for p, s in zip(progresses, speeds):
        # Q가 비었거나, 가장 마지막 배포 예정 작업의 소요 일수가 이번 순서 작업의 소요 일수보다 작다면
        if len(Q)==0 or Q[-1][0] &lt; -((p-100)//s):
            # Q에 이번 순서의 작업의 소요 일수를 누적 배포 예정 기능 수는 1개(본인꺼)로 지정하여 담음.
            Q.append([-((p-100)//s),1])
        # Q에 어떤 요소가 있고, 가장 마지막 배포 예정 작업의 소요 일수가 이번 순서 작업의 소요 일수보다 크거나 같다면
        else:
            # 가장 마지막 배포 예정의 작업 배포 시, 해당 기능을 함께 배포해야 하므로
            # 누적 배포 예정 기능 수에 +1 해줌.
            Q[-1][1]+=1
    # 배포 예정 작업은 Q의 각 요소 내의 1번째 요소에 해당하므로 아래와 같이 list Comprehension을 이용하여 리스트에 담아 반환
    return [q[1] for q in Q]</code></pre>
<h3 id="💡-새롭게-알게된-점-1">💡 새롭게 알게된 점</h3>
<p>📌 <code>올림</code> 계산을 위해 <code>math</code> 모듈의 <code>ceil</code> 함수를 사용하지 않고, <u><span style="color: #FA8072"><strong><code>-((p-100)//s)</code></strong></span> 의 형태로 계산</u>한 점.</p>
<ul>
<li>파이썬에서 <code>//</code> 연산자의 의미 : <span style='background-color: #ffdce0'>나누기 연산 후 <strong>소수점 버림</strong></span><blockquote>
<ul>
<li><code>p-100</code>은 <span style="color: red">음수</span>가 됨.</li>
<li><code>(p-100)//s</code>는 <span style="color: red">음수에서 내림</span>을 한 결과이며, <u>이는 <code>(100-p)/s</code> 를 올림한 결과와 절대값이 동일</u>함.</li>
<li><span style="color: #0000FF"><strong><code>-(p-100)//s</code></strong></span>는 <u>작업의 진척율을 개발 속도로 나눈 후 <strong>올림</strong>한 결과와 동일</u></li>
</ul>
</blockquote>
</li>
</ul>
<br>

<hr>
<br>

<h2 id="🌞-프린터">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42587">프린터</a></h2>
<h3 id="🚊-문제-설명-3">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>일반적인 프린터는 인쇄 요청이 들어온 순서대로 인쇄합니다. </li>
<li>그렇기 때문에 중요한 문서가 나중에 인쇄될 수 있습니다.</li>
<li>이런 문제를 보완하기 위해 중요도가 높은 문서를 먼저 인쇄하는 프린터를 개발했습니다.</li>
<li>이 새롭게 개발한 프린터는 아래와 같은 방식으로 인쇄 작업을 수행합니다.</li>
</ul>
</blockquote>
<pre><code>   1. 인쇄 대기목록의 가장 앞에 있는 문서(J)를 대기목록에서 꺼냅니다.
   2. 나머지 인쇄 대기목록에서 J보다 중요도가 높은 문서가 한 개라도 존재하면 J를 대기목록의 가장 마지막에 넣습니다.
   3. 그렇지 않으면 J를 인쇄합니다.</code></pre><blockquote>
<ul>
<li>예를 들어, <code>4</code>개의 문서(<code>A, B, C, D</code>)가 순서대로 인쇄 대기목록에 있고 중요도가 <code>2 1 3 2</code> 라면 <code>C D A B</code> 순으로 인쇄하게 됩니다.</li>
<li>내가 인쇄를 요청한 문서가 <code>몇 번째</code>로 인쇄되는지 알고 싶습니다. 위의 예에서 <code>C</code>는 <code>1</code>번째로, <code>A</code>는 <code>3</code>번째로 인쇄됩니다.</li>
</ul>
</blockquote>
<p>📢 현재 대기목록에 있는 문서의 중요도가 순서대로 담긴 배열 <code>priorities</code>와 내가 인쇄를 요청한 문서가 현재 대기목록의 어떤 위치에 있는지를 알려주는 <code>location</code>이 매개변수로 주어질 때, 내가 인쇄를 요청한 문서가 몇 번째로 인쇄되는지 <code>return</code> 하도록 <code>solution</code> 함수를 작성해주세요.</p>
<h3 id="🚊-제한사항-3">🚊 <code>제한사항</code></h3>
<blockquote>
<ul>
<li>현재 대기목록에는 <code>1</code>개 이상 <code>100</code>개 이하의 문서가 있습니다.</li>
<li>인쇄 작업의 중요도는 <code>1~9</code>로 표현하며 숫자가 클수록 중요하다는 뜻입니다.</li>
<li><code>location</code>은 <code>0</code> 이상 (<code>현재 대기목록에 있는 작업 수 - 1</code>) 이하의 값을 가지며 대기목록의 가장 앞에 있으면 <code>0</code>, 두 번째에 있으면 <code>1</code>로 표현합니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-3">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/8dfc73ef-891f-41a4-ab99-614b8867d5b1/image.png" alt=""></p>
<p><code>입출력 예 #2</code></p>
<ul>
<li>문제의 예시와 같습니다.</li>
</ul>
<p><code>입출력 예 #1</code></p>
<ul>
<li><code>6</code>개의 문서(<code>A, B, C, D, E, F</code>)가 인쇄 대기목록에 있고 중요도가 <code>1 1 9 1 1 1</code> 이므로 <code>C D E F A B</code> 순으로 인쇄합니다.</li>
</ul>
<h3 id="📑-code--내꺼-3">📑 <code>Code : 내꺼</code></h3>
<blockquote>
<p>사실 프로그래머스 다른 사람 풀이 보고 공부한 후, 숙지된 내용을 정리하였음.</p>
</blockquote>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>중복을 제거한 요소를 담을 새로운 리스트 <code>answer</code>를 빈 리스트로 지정하고, 문제에서 주어진 <code>array</code>의 0 번째 요소를 담음</li>
<li>주어진 <code>array</code>의 1 번째 요소부터 마지막 요소까지 반복문을 돌면서 </li>
<li><code>answer</code>의 마지막 요소와 현재 <code>array</code>의 요소를 비교하여 동일하지 않은 경우에만 <code>array</code>의 요소를 <code>answer</code>에 담음</li>
<li>반복문 종료 후 <code>answer</code>를 <code>return</code></li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code
from collections import deque

def solution(priorities, location):
    # 기존 배열에서 순서와 요소값을 tuple 형태의 요소로 갖는 새로운 리스트를 생성
    # deque 함수를 이용하여 리스트를 덱으로 변환
    # 명칭을 queue라고 지은 이유는, 선입선출의 방식으로 사용할 예정이기 때문
    # 아래 while문을 보면, 제일 앞에 있는 요소를 뽑은 후 조건 충족 시 다시 맨 뒤로 넣음.
    queue = deque([(idx, priority) for idx, priority in enumerate(priorities)])
    # 문제의 규칙대로 주어진 자료형을 정렬 후, 
    # 인쇄를 요청한 문서의 인쇄 순서를 확인할 변수를 answer로 지정하여 0을 할당.
    answer = 0
    # 조건을 만족할 때까지 반복

    while True:
        # queue의 제일 처음 요소를 뽑아서 target_data로 지정
        # popleft 함수를 사용했기 때문에, queue에서 해당 요소는 제거된 상태
        target_data = queue.popleft()

        # target_data의 중요도가 queue 내의 중요도보다 하나라도 작다면
        if any(target_data[1] &lt; current_data[1] for current_data in queue):
            # 현재 데이터를 다시 queue 에 추가 
            queue.append(target_data)

        # target_data의 중요도가 나머지 모든 data의 중요도보다 높다면
        else:
            # 인쇄 순서를 하나 늘려줌.
            answer += 1
            # 「priorities에서 target_data의 중요도의 idx」가 「priorities에서 우리가 찾는 중요도의 idx」와 같다면
            if target_data[0] == location :
                # 인쇄 순서를 출력
                return answer</code></pre>
<h3 id="🔎-내가-못-푼-이유">🔎 내가 못 푼 이유</h3>
<p><strong><code>1번째</code></strong></p>
<pre><code class="language-python">&gt; code
        priorities = [2,3,3,2,9,3,3]
        location = 3</code></pre>
<blockquote>
<p>위의 코드의 세부 작업 순서를 정리한 것이 다음과 같음.</p>
</blockquote>
<pre><code class="language-python">priorities의 변화 및 인쇄 작업을 서술
(우리가 찾는 &quot;location = 3&quot;에 해당하는 2는 &quot;2&quot;로 표시)
    2,3,3,&quot;2&quot;,9,3,3
    3,3,&quot;2&quot;,9,3,3,2
    3,&quot;2&quot;,9,3,3,2,3
    &quot;2&quot;,9,3,3,2,3,3
    9,3,3,2,3,3,&quot;2&quot;
    9 인쇄     - 1번째 인쇄
    3 인쇄     - 2번째 인쇄
    3 인쇄     - 3번째 인쇄
    2,3,3,&quot;2&quot;
    3,3,&quot;2&quot;,2
    3 인쇄     - 4번째 인쇄
    3 인쇄     - 5번째 인쇄
    &quot;2&quot; 인쇄   - 6번째 인쇄
    2 인쇄     - 7번째 인쇄

    따라서, return 값은 6이 됨.</code></pre>
<blockquote>
<p>그런데 나는 일단, 최대값이 맨 앞으로 오면 거기서 더이상의 정렬이 없을 것이라 생각하였음.</p>
</blockquote>
<p><code>나의 잘못된 생각</code></p>
<pre><code class="language-python">priorities의 변화 및 인쇄 작업을 서술
(우리가 찾는 &quot;location = 3&quot;에 해당하는 2는 &quot;2&quot;로 표시)
    2,3,3,&quot;2&quot;,9,3,3
    3,3,&quot;2&quot;,9,3,3,2
    3,&quot;2&quot;,9,3,3,2,3
    &quot;2&quot;,9,3,3,2,3,3
    9,3,3,2,3,3,&quot;2&quot;
    9 인쇄     - 1번째 인쇄
    3 인쇄     - 2번째 인쇄
    3 인쇄     - 3번째 인쇄
    3 인쇄     - 4번째 인쇄
    3 인쇄     - 5번째 인쇄
    2 인쇄     - 6번째 인쇄
    &quot;2&quot; 인쇄   - 7번째 인쇄


    따라서, return 값은 7이 됨.</code></pre>
<blockquote>
<p><strong><code>Logic</code> 흐름 정리</strong>
<span style="color: red">1. 최대값이 맨 앞에 올때까지 <code>rotate</code>를 반복한 다음 그 값을 인쇄한 후에
2. 나머지 값들에 대해 또다시 최대값이 맨 앞에 올때까지 <code>rotate</code>를 반복 후 그 값을 인쇄.
3. 모든 요청이 인쇄된 후에 작업은 종료됨.
</span></p>
</blockquote>
<p><strong><code>2번째</code></strong></p>
<p>문제 조건 중, 아래 내용을 <code>max</code> 함수로 써서 작성하는 경우, <code>시간 초과</code>의 문제와 <code>ValueError</code>가 발생.</p>
<blockquote>
<ol start="2">
<li>나머지 인쇄 대기목록에서 J보다 중요도가 높은 문서가 한 개라도 존재하면 J를 대기 목록의 가장 마지막에 넣습니다.</li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; 코드 구현
        (... 중간 생략 ...)
            while priorities:
                target = priorities.popleft()
                # 틀린 코드
                if target[1] &lt; max(priority[1] for priority in priorities):
                # 바른 코드
                if any(target[1] &lt; priority[1] for priority in priorities):
         (... 중간 생략 ...)</code></pre>
<blockquote>
<ol>
<li><code>max</code> 함수로 최대값을 구하는 경우에는 <code>priorities</code>의 모든 요소를 다 서칭해야 하기 때문에 시간이 오래 걸림.</li>
<li><code>popleft</code>를 통해 <code>priorities</code>가 빈 리스트가 되게 되면, <code>ValueError</code> 발생</li>
</ol>
</blockquote>
<p>❗❗❗ <strong><code>any</code></strong> 함수에 대해 아래에 정리해두었으니 잊지 말고 다음에는 꼭 써먹자...</p>
<h3 id="💡-새롭게-알게된-점-2">💡 새롭게 알게된 점</h3>
<p>📌 <code>all</code> 함수와 <code>any</code> 함수</p>
<ul>
<li>파이썬 공식 홈페이지에 가보면 <a href="https://docs.python.org/ko/3/library/functions.html?highlight=all#all">all</a> 함수와 <a href="https://docs.python.org/ko/3/library/functions.html?highlight=any#any">any</a> 함수에 대해 코드로 설명되어 있음.</li>
</ul>
<p><code>all</code></p>
<blockquote>
<ul>
<li>인자로 반복 가능한 (iterable) 자료형을 받음.</li>
<li>인자로 받은 데이터의 모든 요소가 True인 경우, True를 반환</li>
<li>인자로 받은 요소 중 하나라도 False이면 False를 반환</li>
<li>인자로 받은 요소가 비어있으면 True</li>
</ul>
</blockquote>
<pre><code class="language-python">def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True</code></pre>
<p><code>any</code></p>
<blockquote>
<ul>
<li>인자로 반복 가능한 (iterable) 자료형을 받음.</li>
<li>인자로 받은 데이터의 요소 중 하나라도 True인 경우, True를 반환</li>
<li>인자로 받은 요소 중 모두 False이면 False를 반환</li>
<li>인자로 받은 요소가 비어있으면 False</li>
</ul>
</blockquote>
<pre><code class="language-python">def any(iterable):
    for element in iterable:
        if element:
            return True
    return False</code></pre>
<br>

<h3 id="📑-code--다른-사람꺼-3">📑 <code>Code : 다른 사람꺼</code></h3>
<p><strong><code>강사님 코드</code></strong></p>
<ul>
<li><code>queue</code> 자료형을 활용하지 않고, <code>while</code>문과 <code>for</code>문을 활용한 풀이 </li>
</ul>
<pre><code class="language-python">&gt; code
def solution(priorities, location):
    # answer는 인쇄 순서를 의미
    answer = 1
    # 대기 목록에 원래 순서를 포함시켜서 살펴보기 위해 enumerate 함수 사용
    queue = [[i,v] for i,v in enumerate(priorities)]
    # print(queue)

    while True:
        # 대기 목록에서 제일 앞에 있는 문서를 뽑음
        check = queue.pop(0)
        # 뽑은 문서가 중요도가 가장 높은지 확인
        # -&gt; 나머지 인쇄 대기 목록에서 중요도가 높은 문서가 한 개라도 있는지 여부를 확인
        if check[1] == max(priorities):
            # 그 문서가 우리가 찾는 문서인지?
            if check[0] == location:
                맞으면 인쇄 순서를 반환
                return answer
            # 그 문서가 우리가 찾는 문서가 아니라면
            else:
                # 현재 중요도가 가장 높은 문서가 인쇄되고 없어지므로 대기 목록에서 제거
                priorities.remove(max(priorities))
                # 인쇄 순서가 하나 뒤로 밀리기 때문에 (+1)처리
                answer += 1
        # 뽑은 문서가 중요도가 가장 높지 않다면
        else:
            # 다시 대기 목록에 넣어줌.
            queue.append(check)

    return answer</code></pre>
<h3 id="📑-code--다른-사람꺼-4">📑 <code>Code : 다른 사람꺼</code></h3>
<p><strong><code>정용님 코드</code></strong></p>
<ul>
<li><code>queue</code> 자료형을 활용하지 않고, <code>while</code>문과 <code>for</code>문을 활용한 풀이 </li>
</ul>
<pre><code class="language-python">&gt; code
def solution(priorities, location):
    answer = 0

    while True:
        # 최대값
        max_num = max(priorities)
        print(f&quot;max_num : {max_num}&quot;)

        for i in range(len(priorities)):
            # 최대값이 일치하면
            if max_num == priorities[i]:
                # 해당 답에 대한 순위 +1
                answer += 1
                # 최대값을 찾는 순위에서 제외
                priorities[i] = 0
                print(f&quot;priorities : {priorities}&quot;)

                # 최대값을 다시 구함
                max_num = max(priorities)
                print(f&quot;max_num : {max_num}&quot;)
                print(f&quot;answer : {answer}&quot;)

                #  i의 위치와 일치하면 answer 반환
                if i == location:
                    return answer</code></pre>
<p>🔎 <strong>예시를 통한 확인</strong></p>
<blockquote>
<p>예시를 통한 출력 내용 확인</p>
<pre><code class="language-python">priorities = [2,3,3,2,9,3,3]
location = 3

solution(priorities, location)
</code></pre>
</blockquote>
<p>--- 출력 결과 ---</p>
<blockquote>
<p>(while문 시작)
max_num : 9</p>
<p>(for문 시작)
priorities : [2, 3, 3, 2, 0, 3, 3]
max_num : 3
answer : 1</p>
</blockquote>
<p>priorities : [2, 3, 3, 2, 0, 0, 3]
max_num : 3
answer : 2</p>
<blockquote>
</blockquote>
<p>priorities : [2, 3, 3, 2, 0, 0, 0]
max_num : 3
answer : 3</p>
<blockquote>
<p>(while문 다시 시작)
max_num : 3</p>
<p>(for문 시작)
priorities : [2, 0, 3, 2, 0, 0, 0]
max_num : 3
answer : 4</p>
</blockquote>
<p>priorities : [2, 0, 0, 2, 0, 0, 0]
max_num : 2
answer : 5</p>
<blockquote>
</blockquote>
<p>priorities : [2, 0, 0, 0, 0, 0, 0]
max_num : 2
answer : 6</p>
<blockquote>
<p>--- return 값 ---
6</p>
</blockquote>
<pre><code>
**`프로그래머스 코드`**
- 

```python
&gt; code
</code></pre><br>

<hr>
<br>

<h2 id="🌞-다리를-지나는-트럭">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42583">다리를 지나는 트럭</a></h2>
<h3 id="🚊-문제-설명-4">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>트럭 여러 대가 강을 가로지르는 일차선 다리를 정해진 순으로 건너려 합니다.</li>
<li>모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 알아내야 합니다.</li>
<li>다리에는 트럭이 최대 <code>bridge_length</code>대 올라갈 수 있으며, 다리는 <code>weight</code> 이하까지의 무게를 견딜 수 있습니다.</li>
<li>단, 다리에 완전히 오르지 않은 트럭의 무게는 무시합니다.</li>
</ul>
</blockquote>
<ul>
<li>예를 들어, 트럭 <code>2대</code>가 올라갈 수 있고 무게를 <code>10kg</code>까지 견디는 다리가 있습니다. 무게가 <code>[7, 4, 5, 6]kg</code>인 트럭이 순서대로 최단 시간 안에 다리를 건너려면 다음과 같이 건너야 합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/4dacdf67-1b98-4557-9528-4d725218da34/image.png" alt=""></p>
<ul>
<li>따라서, 모든 트럭이 다리를 지나려면 최소 8초가 걸립니다.</li>
</ul>
<p>📢 <code>solution</code> 함수의 매개변수로 다리에 올라갈 수 있는 트럭 수 <code>bridge_length</code>, 다리가 견딜 수 있는 무게 <code>weight</code>, 트럭 별 무게 <code>truck_weights</code>가 주어집니다. 이때 모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 <code>return</code> 하도록 <code>solution</code> 함수를 완성하세요.</p>
<h3 id="🚊-제한사항-4">🚊 <code>제한사항</code></h3>
<blockquote>
<ul>
<li><code>bridge_length</code>는 <code>1</code> 이상 <code>10,000</code> 이하입니다.</li>
<li><code>weight</code>는 <code>1</code> 이상 <code>10,000</code> 이하입니다.</li>
<li><code>truck_weights</code>의 길이는 <code>1</code> 이상 <code>10,000</code> 이하입니다.</li>
<li>모든 트럭의 무게는 <code>1</code> 이상 <code>weight</code> 이하입니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-4">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/500fd033-0a3e-4021-8a5a-a7909bdab4f5/image.png" alt=""></p>
<h3 id="📑-code--내꺼-4">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<p>내가 직접 푼 것은 아니고, 다른 사람 풀이 보고 학습 후 해당 풀이를 보지 않고 풀었음.</p>
<ul>
<li>기존 코드를 개선</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt; code
def solution(bridge_length, weight, truck_weights):
    # truck이 지나갈 bridge를 리스트로 생성.
    # bridge의 각 요소는 해당 위치의 무게를 의미함.
    bridge = [0] * bridge_length
    # bridge의 무게의 총 합을 미리 계산
    # 아래에서 계산이 필요할 경우에 sum(bridge)로 계산해도 되지만, 그렇게 할 경우에는 시간 초과가 발생.
    # sum 함수는 시간 복잡도가 O(N)이기 때문.
    bridge_weight_sum = 0
    # 총 소요 시간을 계산할 변수를 sec이라 지정하고 0을 할당.
    # second의 약자
    sec = 0

    # bridge가 남아 있으면 계속해서 반복
    while bridge:
        # 1초가 지나감.
        sec += 1
        # bridge의 제일 앞에 있는 무게는 다리를 빠져 나감.
        bridge_weight_sum -= bridge.pop(0)
        # print(f&quot;1번째 bridge : {bridge}&quot;)

        # 만약 다리를 건너야 할 트럭이 아직 존재하는 경우에
        if truck_weights:
            # 현재 다리 위의 총 무게와 대기 중인 첫 번째 트럭의 무게의 합이 다리가 견딜 수 있는 무게보다 작거나 같다면
            if bridge_weight_sum + truck_weights[0] &lt;= weight:
                # 다리의 마지막 위치에 대기 중인 첫 번째 트럭을 추가
                bridge_weight_sum += truck_weights[0]
                # 다리의 총 무게는 그 트럭의 무게만큼 늘려줌
                bridge.append(truck_weights.pop(0))
                # print(f&quot;2번째 bridge : {bridge}&quot;)
            # 만약, 그 트럭이 다리에 올라오게 되었을 때 다리가 견디지 못한다면
            else:
                # 다리의 마지막 자리를 빈 무게로 채운다.
                bridge.append(0)
                # print(f&quot;3번째 bridge : {bridge}&quot;)

    return sec</code></pre>
<p>🔎 <strong>예시를 통한 확인</strong></p>
<blockquote>
<ul>
<li>위의 코드에는 <code>print</code> 문이 모두 달려있는데, 예시 코드를 돌려보면 아래와 같은 결과가 나옴.</li>
</ul>
<p>예시를 통한 출력 내용 확인</p>
<pre><code class="language-python">bridge_length = 5
weight = 5
truck_weights = [2,2,2,2,1,1,1,1]
solution(bridge_length, weight, truck_weights)


--- 출력 결과 ---
</code></pre>
</blockquote>
<p>sec : 1
1번째 bridge : [0, 0, 0, 0]
2번째 bridge : [0, 0, 0, 0, 2]</p>
<blockquote>
</blockquote>
<p>sec : 2
1번째 bridge : [0, 0, 0, 2]
2번째 bridge : [0, 0, 0, 2, 2]</p>
<blockquote>
</blockquote>
<p>sec : 3
1번째 bridge : [0, 0, 2, 2]
3번째 bridge : [0, 0, 2, 2, 0]</p>
<blockquote>
</blockquote>
<p>sec : 4
1번째 bridge : [0, 2, 2, 0]
3번째 bridge : [0, 2, 2, 0, 0]</p>
<blockquote>
</blockquote>
<p>sec : 5
1번째 bridge : [2, 2, 0, 0]
3번째 bridge : [2, 2, 0, 0, 0]</p>
<blockquote>
</blockquote>
<p>sec : 6
1번째 bridge : [2, 0, 0, 0]
2번째 bridge : [2, 0, 0, 0, 2]</p>
<blockquote>
</blockquote>
<p>sec : 7
1번째 bridge : [0, 0, 0, 2]
2번째 bridge : [0, 0, 0, 2, 2]</p>
<blockquote>
</blockquote>
<p>sec : 8
1번째 bridge : [0, 0, 2, 2]
2번째 bridge : [0, 0, 2, 2, 1]</p>
<blockquote>
</blockquote>
<p>sec : 9
1번째 bridge : [0, 2, 2, 1]
3번째 bridge : [0, 2, 2, 1, 0]</p>
<blockquote>
</blockquote>
<p>sec : 10
1번째 bridge : [2, 2, 1, 0]
3번째 bridge : [2, 2, 1, 0, 0]</p>
<blockquote>
</blockquote>
<p>sec : 11
1번째 bridge : [2, 1, 0, 0]
2번째 bridge : [2, 1, 0, 0, 1]</p>
<blockquote>
</blockquote>
<p>sec : 12
1번째 bridge : [1, 0, 0, 1]
2번째 bridge : [1, 0, 0, 1, 1]</p>
<blockquote>
</blockquote>
<p>sec : 13
1번째 bridge : [0, 0, 1, 1]
2번째 bridge : [0, 0, 1, 1, 1]</p>
<blockquote>
</blockquote>
<p>sec : 14
1번째 bridge : [0, 1, 1, 1]</p>
<blockquote>
</blockquote>
<p>sec : 15
1번째 bridge : [1, 1, 1]</p>
<blockquote>
</blockquote>
<p>sec : 16
1번째 bridge : [1, 1]</p>
<blockquote>
</blockquote>
<p>sec : 17
1번째 bridge : [1]</p>
<blockquote>
</blockquote>
<p>sec : 18
1번째 bridge : []</p>
<blockquote>
<p>--- return 값 ---
18</p>
</blockquote>
<pre><code>
&lt;br&gt;


### 📑 `Code : 다른 사람꺼`

**`상봉님 코드`**
- ..

```python
&gt; code
def solution(bridge_length, weight, truck_weights):
    count = 0            # 경과 시간
    weight_sum = 0       # 다리 위의 트럭들의 무게의 합
    bridge = []          # 다리 위에 있는 트럭들

    while truck_weights:
        count += 1

        # 트럭이 다리 위로 올라갈 수 있는 조건
        if ((weight_sum + truck_weight[0]) &lt;= weight and len(bridge) &lt;= bridge_length):
            # 트럭을 다리 위에 올리고
            bridge.append(truck_weights.pop())
            # 올린 트럭의 무게를 총 무게에 더한다.
            weight_sum += bridge[-1]
        else:
            # 다리 위로 올라갈 수 없다면 빈 공간으로 채운다.
            bridge.append(0)

        # 경과한 시간이 다리 길이보다 크면, 다리에서 나온 트럭들의 무게를 빼준다.
        if count &gt;= bridge_length:
            weight_sum -= bridge.pop(0)

    # 총 경과 시간 + 다리 위에 있는 트럭들이 지나갈 시간
    return count + bridge_length</code></pre><p><strong><code>프로그래머스 코드</code></strong></p>
<ul>
<li>아직 이 코드는 정확히 이해를 하지 못함.</li>
</ul>
<pre><code class="language-python">&gt; code
def solution(bridge_length, weight, truck_weights):
    t = 0
    on = []  # (weight, stayed)
    while truck_weights or on:
        for i, e in enumerate(on):
            on[i] = (e[0], e[1] + 1)
            print(f&quot;1번째 on : {on}&quot;)
        on = list(filter(lambda x: x[1] &lt; bridge_length + 1, on))
        print(f&quot;2번째 on : {on}&quot;)

        weight_sum = 0
        for e in on:
            weight_sum += e[0]
            print(weight_sum)

        if truck_weights:
            if weight_sum + truck_weights[0] &lt;= weight:
                on.append((truck_weights.pop(0), 1))
                print(f&quot;3번째 on : {on}&quot;)
        t += 1
        print(f&quot;t : {t}&quot;)
        # print(str(t)+&quot;:&quot;+str(on))
    return t</code></pre>
<p>🔎 <strong>예시를 통한 확인</strong></p>
<blockquote>
<ul>
<li>위의 코드에는 <code>print</code> 문이 모두 달려있는데, 예시 코드를 돌려보면 아래와 같은 결과가 나옴.
예시를 통한 출력 내용 확인<pre><code class="language-python">bridge_length = 5
weight = 5
truck_weights = [2,2,2,2,1,1,1,1]
solution(bridge_length, weight, truck_weights)

</code></pre>
</li>
</ul>
<p>--- 출력 결과 ---</p>
</blockquote>
<p>2번째 on : []
3번째 on : [(2, 1)]
t : 1
1번째 on : [(2, 2)]
2번째 on : [(2, 2)]
weight_sum : 2
3번째 on : [(2, 2), (2, 1)]
t : 2
1번째 on : [(2, 3), (2, 1)]
1번째 on : [(2, 3), (2, 2)]
2번째 on : [(2, 3), (2, 2)]
weight_sum : 2
weight_sum : 4
t : 3
1번째 on : [(2, 4), (2, 2)]
1번째 on : [(2, 4), (2, 3)]
2번째 on : [(2, 4), (2, 3)]
weight_sum : 2
weight_sum : 4
t : 4
1번째 on : [(2, 5), (2, 3)]
1번째 on : [(2, 5), (2, 4)]
2번째 on : [(2, 5), (2, 4)]
weight_sum : 2
weight_sum : 4
t : 5
1번째 on : [(2, 6), (2, 4)]
1번째 on : [(2, 6), (2, 5)]
2번째 on : [(2, 5)]
weight_sum : 2
3번째 on : [(2, 5), (2, 1)]
t : 6
1번째 on : [(2, 6), (2, 1)]
1번째 on : [(2, 6), (2, 2)]
2번째 on : [(2, 2)]
weight_sum : 2
3번째 on : [(2, 2), (2, 1)]
t : 7
1번째 on : [(2, 3), (2, 1)]
1번째 on : [(2, 3), (2, 2)]
2번째 on : [(2, 3), (2, 2)]
weight_sum : 2
weight_sum : 4
3번째 on : [(2, 3), (2, 2), (1, 1)]
t : 8
1번째 on : [(2, 4), (2, 2), (1, 1)]
1번째 on : [(2, 4), (2, 3), (1, 1)]
1번째 on : [(2, 4), (2, 3), (1, 2)]
2번째 on : [(2, 4), (2, 3), (1, 2)]
weight_sum : 2
weight_sum : 4
weight_sum : 5
t : 9
1번째 on : [(2, 5), (2, 3), (1, 2)]
1번째 on : [(2, 5), (2, 4), (1, 2)]
1번째 on : [(2, 5), (2, 4), (1, 3)]
2번째 on : [(2, 5), (2, 4), (1, 3)]
weight_sum : 2
weight_sum : 4
weight_sum : 5
t : 10
1번째 on : [(2, 6), (2, 4), (1, 3)]
1번째 on : [(2, 6), (2, 5), (1, 3)]
1번째 on : [(2, 6), (2, 5), (1, 4)]
2번째 on : [(2, 5), (1, 4)]
weight_sum : 2
weight_sum : 3
3번째 on : [(2, 5), (1, 4), (1, 1)]
t : 11
1번째 on : [(2, 6), (1, 4), (1, 1)]
1번째 on : [(2, 6), (1, 5), (1, 1)]
1번째 on : [(2, 6), (1, 5), (1, 2)]
2번째 on : [(1, 5), (1, 2)]
weight_sum : 1
weight_sum : 2
3번째 on : [(1, 5), (1, 2), (1, 1)]
t : 12
1번째 on : [(1, 6), (1, 2), (1, 1)]
1번째 on : [(1, 6), (1, 3), (1, 1)]
1번째 on : [(1, 6), (1, 3), (1, 2)]
2번째 on : [(1, 3), (1, 2)]
weight_sum : 1
weight_sum : 2
3번째 on : [(1, 3), (1, 2), (1, 1)]
t : 13
1번째 on : [(1, 4), (1, 2), (1, 1)]
1번째 on : [(1, 4), (1, 3), (1, 1)]
1번째 on : [(1, 4), (1, 3), (1, 2)]
2번째 on : [(1, 4), (1, 3), (1, 2)]
weight_sum : 1
weight_sum : 2
weight_sum : 3
t : 14
1번째 on : [(1, 5), (1, 3), (1, 2)]
1번째 on : [(1, 5), (1, 4), (1, 2)]
1번째 on : [(1, 5), (1, 4), (1, 3)]
2번째 on : [(1, 5), (1, 4), (1, 3)]
weight_sum : 1
weight_sum : 2
weight_sum : 3
t : 15
1번째 on : [(1, 6), (1, 4), (1, 3)]
1번째 on : [(1, 6), (1, 5), (1, 3)]
1번째 on : [(1, 6), (1, 5), (1, 4)]
2번째 on : [(1, 5), (1, 4)]
weight_sum : 1
weight_sum : 2
t : 16
1번째 on : [(1, 6), (1, 4)]
1번째 on : [(1, 6), (1, 5)]
2번째 on : [(1, 5)]
weight_sum : 1
t : 17
1번째 on : [(1, 6)]
2번째 on : []
t : 18</p>
<blockquote>
<p>--- return 값 ---
18</p>
</blockquote>
<pre><code>


### 💡 새롭게 알게된 점


📌 

&lt;br&gt;

---

&lt;br&gt;

## 🌞 [주식가격](https://school.programmers.co.kr/learn/courses/30/lessons/42584)


### 🚊 `문제 설명`

📢 초 단위로 기록된 주식가격이 담긴 배열 `prices`가 매개변수로 주어질 때, 가격이 떨어지지 않은 기간은 몇 초인지를 `return` 하도록 `solution` 함수를 완성하세요.


### 🚊 `제한사항`
&gt;- prices의 각 가격은 1 이상 10,000 이하인 자연수입니다.
&gt;- prices의 길이는 2 이상 100,000 이하입니다.

### 🚊 `입출력 예`

![](https://velog.velcdn.com/images/brown_eyed87/post/317acc77-c5fd-4d29-8aae-57f31ebd990e/image.png)


`입출력 예 설명`
&gt;- 1초 시점의 ₩1은 끝까지 가격이 떨어지지 않았습니다.
&gt;- 2초 시점의 ₩2은 끝까지 가격이 떨어지지 않았습니다.
&gt;- 3초 시점의 ₩3은 1초뒤에 가격이 떨어집니다. 
따라서 1초간 가격이 떨어지지 않은 것으로 봅니다.
&gt;- 4초 시점의 ₩2은 1초간 가격이 떨어지지 않았습니다.
&gt;- 5초 시점의 ₩3은 0초간 가격이 떨어지지 않았습니다.


### 📑 `Code : 내꺼 `

📌 `Logic` 설명
&gt; 코드에 직접 적음

```python
&gt; code
from collections import deque

def solution(prices):
    length = len(prices)
    # 가격이 떨어지지 않은 기간은 몇 초인지를 기록할 리스트를 생성
    # 초기 값은 모두 0으로 셋팅
    answer = [0] * length

    # 리스트 전체를 반복하면서
    for i in range(length):
        # i번째 요소의 다음부터 끝에 있는 모든 요소들을 탐색
        for j in range(i+1, length):
            # 다음 요소로 넘어가서 조건을 만족하는 경우에 answer의 i번째 요소에 1초를 추가
            # 이 코드가 if문보다 위에 있는 이유는 본인보다 뒤에 어떤 비교대상이 있는 경우에
            # 그 대상이 나보다 크든 작든 간에 1초동안은 가격이 떨어지지 않은 것으로 간주할 것이기 때문임.
            answer[i] +=1
            # 만약 나보다 가격이 떨어진 요소를 발견하게 되면
            if prices[i] &gt; prices[j]:
                # 반복문을 멈추고 빠져나감
                break
    return answer</code></pre><br>

<h3 id="📑-code--다른-사람꺼-5">📑 <code>Code : 다른 사람꺼</code></h3>
<p><strong><code>강사님 코드</code></strong></p>
<ul>
<li>&#39;오큰수&#39; 문제 풀이 방식 활용 </li>
<li><code>stack</code>을 활용<blockquote>
<ul>
<li>주어진 요소를 차례대로 하나씩 뽑아서 <code>stack</code>에 담았다가 조건을 만족하면 제거</li>
<li>주식 가격이 떨어졌으면 그 떨어진 시간을 계산 후 <code>stack</code>에서 제거</li>
<li></li>
</ul>
</blockquote>
</li>
</ul>
<pre><code class="language-python">&gt; code
def solution(prices):
    # 각각의 주식가격마다 가격이 떨어지지 않은 기간을 담을 리스트를 생성
    # 기초 시간은 모두 0초로 셋팅
    answer = [0]*len(prices)
    # 비교의 기준이 될 요소의 index를 담을 빈 리스트를 stack으로 활용
    stack = [] 

    # 주식가격 리스트에 대해 반복문을 돌면서 아래 내용을 확인
    for i in range(len(prices)):
        # 비교 기준이 아직 남아 있고, 그런데 어딘가에서 주식 가격이 떨어졌다면
        while stack and prices[stack[-1]] &gt; prices[i]:
            # stack에서 현재 비교 기준 주식가격의 위치를 뽑아옴
            # 주식이 떨어진 시각을 갱신하기 위함
            s = stack.pop()
            # 주식 가격이 떨어진 위치와 현재 비교 기준의 주식가격의 위치의 차이를 구함
            # 가격이 떨어지지 않은 기간을 담은 리스트에서 비교 기준의 위치의 시간을 갱신
            answer[s] = i-s
        # 비교 기준이 남아있지 않거나, 비교 기준이 있더라도 그 다음 요소보다 가격이 떨어지지 않았다면
        # 그 다음 요소를 비교 기준 리스트에 대기시킴
        stack.append(i)

    # 주식 가격이 떨어지지 않은 요소들만 stack에 남아있는 상황이므로
    # 각각의 요소들에 대해 주식 가격이 떨어지지 않은 시간을 계산하면 됨.
    while stack:
        # stack에서 제일 앞에 있는 요소(index 값임)들을 꺼내서
        s = stack.pop()
        # 그 위치의 주식가격이 떨어지지 않은 시간을 계산
        # 전체 리스트의 요소의 개수에서 나를 포함하여 나보다 왼쪽에 있는 요소의 개수만큼을 뺄셈
        # s는 index이고, index가 의미하는 바가 나보다 왼쪽에 있는 요소의 개수이므로
        # 자기 자신인 1개만큼을 포함시킨 (s+1)이 바로 &#39;나를 포함하여 나보다 왼쪽에 있는 요소&#39;의 개수를 의미
        answer[s] = len(prices)-(s+1)

    return answer</code></pre>
<h3 id="📑-code--다른-사람꺼-6">📑 <code>Code : 다른 사람꺼</code></h3>
<p><strong><code>프로그래머스 코드</code></strong></p>
<pre><code class="language-python">&gt; code
def solution(prices):
    # stack으로 활용할 빈 리스트 생성
    # stack에 들어가는 요소: [본인의 index, 본인의 price]
    # 즉, stack은 2차원 리스트 형태로 구성될 예정
    stack = []
    # 가격이 떨어지지 않은 기간은 몇 초인지를 기록할 리스트를 생성
    # 초기 값은 모두 0으로 셋팅
    answer = [0] * len(prices)

    # 리스트 전체에 대해 반복문을 돌면서
    for i in range(len(prices)):
        # print(f&quot;i : {i}&quot;)
        # stack에 어떤 요소가 있고, 
        # stack내의 마지막 요소(기준 요소)의 price가 현재 i번째 대상의 price보다 크다면
        #  └ 이말 인즉, 주식 가격이 떨어진 것을 의미함
        while stack and stack[-1][1] &gt; prices[i]:
            # stack의 마지막 요소(기준 요소)를 추출
            # 기준 요소의 index를 past에 할당
            # 기준 요소의 price는 이미 바로 위의 while문에서 써먹었기 때문에 더이상 쓸일이 없어서 _ 에 할당
            past, _ = stack.pop()
            # print(f&quot;past : {past}&quot;)
            # print(f&quot;_ : {_}&quot;)
            # 기준 요소의 주식 가격이 떨어지지 않은 기간을 갱신
            # i - past의 의미 : i번째 대상의 위치(주식 가격이 떨어진 위치) - 기준 요소의 위치
            answer[past] = i - past
            # print(f&quot;answer : {answer}&quot;)
        # stack에 빈 요소가 없거나 
        # stack의 마지막 요소의 price보다 i번째 대상의 price가 더 크거나 같다면
        # i번째 대상의 요소를 [요소의 index, 요소의 주식 가격] 의 형태로 stack에 추가
        stack.append([i, prices[i]])
        # print(f&quot;stack : {stack}&quot;)

    # 위의 과정까지 거치면, 주식 가격이 떨어지지 않은 요소들은 stack에 남아 있음
    # 이제 stack에 남은 요소들에 대해 반복문을 돌면서
    for i, s in stack:
        # i번째 요소의 주식 가격이 떨어지지 않은 기간을 계산하여 갱신
        # 주식 가격이 떨어지지 않은 기간 = 배열의 전체 길이 - 본인의 index
        # `본인의 index` 가 &#39;나보다 앞에 있는 요소의 개수&#39;를 뜻한다는 점을 이용
        answer[i] = len(prices) - 1 - i
        # print(f&quot;answer : {answer}&quot;)
    return answer</code></pre>
<p>🔎 <strong>예시를 통한 확인</strong></p>
<blockquote>
<ul>
<li>위의 코드에는 <code>print</code> 문이 모두 달려있는데, 예시 코드를 돌려보면 아래와 같은 결과가 나옴.</li>
</ul>
<p>예시를 통한 출력 내용 확인</p>
<pre><code class="language-python">prices = [1,2,3,2,3]
solution(prices)


--- 출력 결과 ---

(for문 시작)
i : 0
stack : [[0, 1]]
</code></pre>
</blockquote>
<p>i : 1
stack : [[0, 1], [1, 2]]</p>
<blockquote>
</blockquote>
<p>i : 2
stack : [[0, 1], [1, 2], [2, 3]]</p>
<blockquote>
</blockquote>
<p>i : 3
(while문 시작)
past : 2
_ : 3
answer : [0, 0, 1, 0, 0]
(while문 끝)
stack : [[0, 1], [1, 2], [3, 2]]</p>
<blockquote>
</blockquote>
<p>i : 4
stack : [[0, 1], [1, 2], [3, 2], [4, 3]]</p>
<blockquote>
</blockquote>
<p>(2번째 for문 시작)
answer : [4, 0, 1, 0, 0]
answer : [4, 3, 1, 0, 0]
answer : [4, 3, 1, 1, 0]
answer : [4, 3, 1, 1, 0]</p>
<blockquote>
<p>--- return 값 ---
[4, 3, 1, 1, 0]</p>
</blockquote>
<p>```</p>
<h3 id="💡-새롭게-알게된-점-3">💡 새롭게 알게된 점</h3>
<p>📌 리스트에서 해당 요소의 <code>index</code>가 의미하는 것이 리스트 내에서 <code>본인보다 앞에 있는 요소의 개수</code> 를 뜻하기도 한다는 점!!</p>
<br>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[221010_[독서] 1일 1로그 003 ~ 004]]></title>
            <link>https://velog.io/@brown_eyed87/221010%EB%8F%85%EC%84%9C-1%EC%9D%BC-1%EB%A1%9C%EA%B7%B8-003-004</link>
            <guid>https://velog.io/@brown_eyed87/221010%EB%8F%85%EC%84%9C-1%EC%9D%BC-1%EB%A1%9C%EA%B7%B8-003-004</guid>
            <pubDate>Mon, 10 Oct 2022 01:56:10 GMT</pubDate>
            <description><![CDATA[<p><strong>※ 본 글은 <code>인사이트</code>에서 출간한 <code>브라이언 W. 커니핸</code> 저자의 <code>『1일 1로그 100일 완성 IT 지식』</code> 책을 읽고 작성하였습니다.</strong></p>
<h1 id="span-stylebackground-color-edf3ec🚀-003-hdd와-ssd의-차이span"><span style='background-color: #edf3ec'>🚀 <code>003. HDD와 SSD의 차이</code></span></h1>
<h2 id="span-stylebackground-color-faf1f5🏰-보조-기억-장치span"><span style='background-color: #faf1f5'>🏰 <code>보조 기억 장치</code></span></h2>
<ul>
<li><code>보조 기억 장치</code>는 전원이 꺼져 있을 때도 정보를 유지</li>
<li><code>주 기억 장치</code>는 정보 저장 용량이 한정적인 데다 전원이 꺼지면 내용이 사라짐</li>
</ul>
<h3 id="span-stylebackground-color-ffffe8🛴-보조-기억-장치의-종류span"><span style='background-color: #ffffe8'>🛴 <code>보조 기억 장치의 종류</code></span></h3>
<ul>
<li><p>공통적인 특징</p>
<blockquote>
<ul>
<li>메모리보다 많은 정보를 저장        </li>
<li>휘발성을 띠지 않아서 드라이브에 저장된 정보는 전력 공급이 없더라도 유지됨.
▶    <code>데이터</code>, <code>명령어</code>, <code>다른 모든 정보</code>는 <u>보조 기억 장치에 장기간 저장</u>되고, <u>주 기억 장치로는 일시적으로만 옮겨짐</u>.    </li>
</ul>
</blockquote>
</li>
<li><p>용량의 크기    </p>
<blockquote>
<ul>
<li>1바이트는 대부분 일반적인 영어 텍스트 표현에서 알파벳 문자 1개를 저장</li>
</ul>
</blockquote>
</li>
</ul>
<h4 id="📌-하드-디스크하드-드라이브">📌 하드 디스크(하드 드라이브)</h4>
<ul>
<li>자기 디스크는 회전하는 금속 표면에 있는 자성 물질의 미세한 영역이 자성을 띠는 방향을 설정하여    정보를 저장        </li>
<li>데이터는 동심원을 따라 나 있는 트랙에 저장되며, 디스크는 트랙 간에 이동하는 센서를 이용하여 데이터를 읽고 씀        <blockquote>
<ul>
<li>오래된 컴퓨터가 작동할 때 들리는 윙윙거리고 딸각거리는 소리는 디스크가 센서를 금속 표면의 적절한 위치로 옮기면서 내는 소리    </li>
<li>디스크 표면은 고속으로(분당 5,400회 이상) 회전함    </li>
<li>디스크는 용량 면에서 RAM보다 바이트당 100배 정도 저렴하지만, 정보에 접근하는 속도는 더 느림        </li>
<li>디스크 드라이브가 금속 표면의 특정 트랙에 접근하는 데는 약 1/100초가 걸림.</li>
<li>접근하고 나면 데이터는 초당 약 100MB의 속도로 전송됨    </li>
</ul>
</blockquote>
</li>
</ul>
<h4 id="📌-ssd">📌 SSD</h4>
<ul>
<li>SSD는 회전하는 기계 장치 대신, 플래시 메모리를 사용함.        </li>
<li>플래시 메모리는 비휘발성을 띔    <blockquote>
<ul>
<li>전원이 꺼져 있어도 개별 소자에 전하를 유지하는 회로에 정보가 전하 형태로 저장됨.</li>
<li>저장된 전하를 읽어 값이 무엇인지 확인 가능하며, 삭제하고 새 값으로 덮어쓰기도 가능</li>
</ul>
</blockquote>
</li>
<li>플래시 메모리는 기존의 디스크 저장 장치보다 더 빠르고 가볍고 안정적임    <blockquote>
<ul>
<li>떨어뜨려도 고장이 덜 나고, 전략을 더 적게 사용하므로 휴대전화/카메라 등에 사용됨</li>
<li>일반적인 노트북용 SSD는 250 ~ 500GB 정도를 저장        </li>
</ul>
</blockquote>
</li>
</ul>
<p>💡 <strong>디스크 드라이브는 컴퓨터의 논리적 구조와 물리적 구현 간의 차이를 보여주는 좋은 예</strong>    </p>
<ul>
<li><p>윈도우의 파일 탐색기나 맥OS의 파인더 같은 프로그램은 드라이브의 내용을 폴더와 파일의 계층 구조로 표시함.    </p>
</li>
<li><p>데이터는 회전하는 기계 장치, 움직이는 부품이 없는 집적회로 또는 완전히 다른 형태의 장치에 저장됨.    </p>
</li>
<li><p>드라이브라는 하드웨어와 파일시스템이라는 운영체제의 소프트웨어가 합작하여 조직화된 구조를 만들어 냄.    </p>
</li>
<li><p>데이터를 저장하는 장치라면 완전히 다른 물리적 수단을 사용하더라도 이와 동일한 구조를 제공    </p>
<blockquote>
<p><code>ex</code> CD-ROM/DVD, USB, 카메라, 이동식 메모리 카드
→    이는 컴퓨팅 곳곳에 스며들어 있는 아이디어인 추상화의 좋은 예</p>
<ul>
<li>추상화는 물리적인 구현의 세부 사항을 숨김.</li>
</ul>
</blockquote>
</li>
<li><p>파일 시스템은 다양한 기술의 물리적 작동 방식과 관계없이 그 내용을 사용자에게 파일과 폴더의 계층 구조로 보혀줌    </p>
</li>
</ul>
<h4 id="📌-다른-장치들">📌 다른 장치들</h4>
<ul>
<li><p>입력 장치        </p>
<blockquote>
<p><code>ex</code> 마우스, 키보드, 터치스크린, 마이크, 카메라, 스캐너    </p>
</blockquote>
</li>
<li><p>출력 장치        </p>
<blockquote>
<p><code>ex</code> 디스플레이, 프린터, 스피커    </p>
</blockquote>
</li>
<li><p>네트워킹 구성 요소        </p>
<blockquote>
<p><code>ex</code> 와이파이, 블루투스    </p>
</blockquote>
</li>
<li><p>시각, 청각, 또는 다른 면에서 접근성을 겪는 사용자를 위한 다양한 보조 장치도 있음</p>
</li>
<li><p>장치의 물리적인 속성은 대개 더 낮은 가격으로 더 작은 패키지에 더 많은 기능을 제공하는 방향으로 빠르게 변해 왔음        </p>
<blockquote>
<ul>
<li>이러한 장치들이 어떻게 단일 기기로 합쳐지고 있는 지에도 주목해야 함    </li>
<li>스마트폰은 노트북과 추상적인 아키텍처는 같지만, 크기와 소모 전력에 제약이 있어서 구현 방법은 크게 다름</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h1 id="span-stylebackground-color-edf3ec🚀-004-가로세로-1cm-프로세서-칩span"><span style='background-color: #edf3ec'>🚀 <code>004. 가로세로 1cm 프로세서 칩</code></span></h1>
<ul>
<li><p>하드웨어 장치들의 내부는 과거 와 비교했을 때 많은 발전된 모습을 발견할 수 있다.</p>
<blockquote>
<ul>
<li>예를 들어, SD카드는 겉보기에 차이는 없으나 용량은 100배까지 늘어났으며, 컴퓨터 부품이 올라가 있는 회로 기판에서는 부품의 수가 더 적어졌다.</li>
<li>왜냐하면 20년 전보다 많은 회로가 부품 내부에 들어가 있고, 배선이 더 미
세하며, 연결 핀의 수가 더 많고 훨씬 더 조밀하게 배치되어 있기 때문이다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/10146173-464c-4890-a936-4823a3d162aa/image.png" alt=""></li>
</ul>
</blockquote>
</li>
<li><p>1990년대 말에 사용되던 데스크톱 PC의 회로 기판을 살펴보면, 프로세서와 메모리 같은 부품은 기판에 장착되거나 꽂혀있으며 회로 기판 반대쪽에 인쇄된 전선으로 연결된다.</p>
</li>
<li><p>컴퓨터의 전자 회로는 몇 가지 기본 소자가 매우 많이 모여 만들어진다.</p>
</li>
</ul>
<h2 id="span-stylebackground-color-faf1f5🏰-논리게이트span"><span style='background-color: #faf1f5'>🏰 <code>논리게이트</code></span></h2>
<ul>
<li>컴퓨터의 전자 회로에서 가장 중요한 기본 소자    </li>
<li>한 개나 두 개의 입력 값을 바탕으로 단일 출력 값을 계산    </li>
<li>전압이나 전류 같은 입력 신호를 이용하여 전압이나 전류인 출력 신호를 제어    <ul>
<li>이러한 게이트가 필요한 만큼 적절한 방식으로 연결되면 어떤 종류의 계산도 수행 가능</li>
</ul>
</li>
</ul>
<h2 id="span-stylebackground-color-faf1f5🏰-트랜지스터span"><span style='background-color: #faf1f5'>🏰 <code>트랜지스터</code></span></h2>
<ul>
<li><p>회로 소자에서 가장 핵심적인 부분</p>
</li>
<li><p>1974년 벨 연구소에서 존 바딘, 월터 브래튼, 윌리엄 쇼클리가 발명</p>
</li>
<li><p>컴퓨터에서 기본적으로 스위치(전압의 제어를 받아 전류를 켜거나 끄는 장치) 역할을 함</p>
<br></li>
<li><p>한때는 논리 게이트가 개별 부품으로 만들어지기도 하였음    </p>
<blockquote>
<ul>
<li>에니악에서는 전구와 크기가 비슷한 진공관으로 만들어졌으며, 1960년대 컴퓨터에서는 지우개 정도 크기의 트랜지스터로 논리 게이트가 만들어졌음</li>
</ul>
</blockquote>
</li>
<li><p>아래 그림은 첫 번쨰 트랜지스터의 복제품, 진공관, 패키지에 들어 있는 프로세서를 보여줌.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/7d74bed6-9465-4df8-87ff-12fc9128b18e/image.png" alt=""></p>
</li>
<li><p>프로세서에서 실제 회로 부분은 중앙에 있고 가로세로 1cm인 데 반해, 진공관은 길이가 약 4인치(10cm) 정도.</p>
<blockquote>
<ul>
<li>최신 프로세서가 이 정도 크기라면 수십억개의 트랜지스터가 들어 있을 것임.</li>
</ul>
</blockquote>
</li>
</ul>
<h2 id="span-stylebackground-color-faf1f5🏰-집적회로span"><span style='background-color: #faf1f5'>🏰 <code>집적회로</code></span></h2>
<ul>
<li><p>논리게이트는 집적회로 상에서 만들어짐</p>
</li>
<li><p><code>집적회로</code>는 흔히 <code>칩</code> 또는 <code>마이크로칩</code>이라고 함</p>
</li>
<li><p>집적회로는 <u>모든 소자와 배선이 단일 평면(얇은 실리콘 판) 위에 들어가 있는데,
이는 개별 부품과 재래식 전선이 없는 회로를 만들기 위해    일련의 복잡한 광학적, 화학적 공정을 거쳐 제조된 것</u>임.</p>
<blockquote>
<p>→ 따라서 집적회로는 개별 부품으로 만들어진 회로보다 훨씬 작고 견고함</p>
</blockquote>
</li>
<li><p>칩은 지름이 약 12인치(30cm)인 원형 웨이퍼 상에서 한꺼번에 제조됨.</p>
<blockquote>
<ul>
<li>웨이퍼는 각 칩으로 나뉘고, 칩은 하나씩 패키징됨.</li>
<li>일반적인 칩은 시스템의 나머지 부분과 칩을 연결해주는 수십~수백개의 핀이 있는 더 큰 패키지에 장착됨.</li>
<li>실제 프로세서는 중앙에 있고, 가로세로 길이는 각각 1cm 정도</li>
</ul>
</blockquote>
</li>
<li><p>집적회로는 1958년경에 로버트 노이스와 잭 킬비가 독자적으로 발명</p>
</li>
<li><p>집적회로는 디지털 전자 장치의 핵심 요소이기는 하지만, 다른 기술과도 함께 사용됨</p>
<blockquote>
<ul>
<li>디스크에는 자기 저장 기술, CD와 DVD에는 레이저, 네트워킹에는 광섬유가 사용됨.</li>
</ul>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[221010_점프투장고 2-07 ~ 2-10]]></title>
            <link>https://velog.io/@brown_eyed87/221010%EC%A0%90%ED%94%84%ED%88%AC%EC%9E%A5%EA%B3%A0-2-07-2-10</link>
            <guid>https://velog.io/@brown_eyed87/221010%EC%A0%90%ED%94%84%ED%88%AC%EC%9E%A5%EA%B3%A0-2-07-2-10</guid>
            <pubDate>Mon, 10 Oct 2022 01:02:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> 🌉 <a href="https://wikidocs.net/book/4223">점프 투 장고</a> 사이트를 참고하여 일정 단위의 <code>chapter</code>에 대해 실습 후 <code>새롭게 알게된 내용</code>, 혹은 <code>복습이 꼭 필요한 내용</code>에 대해 정리!!</p>
</blockquote>
<h1 id="🌞-span-stylebackground-color-f4eeee2-07-스태틱span">🌞 <span style='background-color: #f4eeee'><code>2-07. 스태틱</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8템플릿에-스타일-적용span">🌈 <span style='background-color: #ffffe8'><code>템플릿에 스타일 적용</code></span></h2>
<ul>
<li>작성한 스타일시트 파일을 질문 상세 템플릿에 적용하는 방법<blockquote>
<ul>
<li><code>html</code> 문서 상단에 하단의 코드 추가</li>
<li>템플릿에 스타일시트와 같은 스태틱 파일을 사용하기 위해서는 템플릿 최상단에 <code>{% load static %}</code>태그를 먼저 삽입해야 함.</li>
<li>그래야만 <code>{% static ... %}</code>와 같은 템플릿 태그를 사용 가능.</li>
</ul>
</blockquote>
</li>
</ul>
<pre><code class="language-python">{% load static %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;style.css&#39; %}&quot;&gt;</code></pre>
<br>

<h1 id="🌞-span-stylebackground-color-f4eeee2-08-부트스트랩span">🌞 <span style='background-color: #f4eeee'><code>2-08. 부트스트랩</code></span></h1>
<blockquote>
<ul>
<li>부트스트랩(<code>Bootstrap</code>)은 디자이너의 도움 없이도 개발자 혼자서 상당히 괜찮은 수준의 웹 페이지를 만들수 있게 도와주는 프레임워크.</li>
<li>부트스트랩은 트위터(<code>Twitter</code>)를 개발하면서 만들어졌고 현재 지속적으로 관리되고 있는 <code>오픈소스</code> 프로젝트</li>
</ul>
</blockquote>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8부트스트랩-설치span">🌈 <span style='background-color: #ffffe8'><code>부트스트랩 설치</code></span></h2>
<blockquote>
<ul>
<li>부트스트랩 다운로드 : <a href="https://getbootstrap.com/docs/5.1/getting-started/download/">https://getbootstrap.com/docs/5.1/getting-started/download/</a></li>
</ul>
</blockquote>
<h3 id="🚦-span-stylebackground-color-f0fcfc부트스트랩-주의사항span">🚦 <span style='background-color: #f0fcfc'><code>부트스트랩 주의사항</code></span></h3>
<ul>
<li>부트스트랩은 <code>3.x</code>, <code>4.x</code>, <code>5.x</code> 등의 버전이 존재하고 메이저 번호(<code>3</code>, <code>4</code>, <code>5</code>)에 따라 그 사용방법이 다름.</li>
<li>이 책은 부트스트랩 버전 5 기준으로 실습을 진행.</li>
<li>다른 부트스트랩 버전을 사용하면 이 책의 예제가 정상 동작하지 않음.
<img src="https://wikidocs.net/images/page/70838/C_2-08_1.png" alt=""></li>
<li>압축파일 안에는 많은 파일들이 있는데 이 중에서 <code>bootstrap.min.css</code> 파일과 <code>bootstrap.min.js</code> 파일을 카피하여 <code>static</code> 디렉터리에 저장</li>
</ul>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8부트스트랩-적용span">🌈 <span style='background-color: #ffffe8'><code>부트스트랩 적용</code></span></h2>
<ul>
<li>먼저 <code>질문 목록 template</code>에 부트스트랩을 다음처럼 적용</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">{% load static %}                                                                 # 추가
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static bootstrap.min.css&#39; %}&quot;&gt;    # 추가
{% if question_list %}
(... 생략 ...)</code></pre>
<ul>
<li>이제 부트스트랩 <code>style</code>을 적용 했으니 <code>template</code>도 부트스트랩을 사용하도록 다음과 같이 수정<blockquote>
<ul>
<li>기존에는 <code>&lt;ul&gt;</code> 태그로 심플하게 작성했던 질문 목록을 <code>테이블 구조</code>로 변경.</li>
<li>그리고 번호와 작성일시 항목도 추가함.</li>
<li>번호는 <code>for</code> 문의 현재 순서를 의미하는 <code>{{ forloop.counter }}</code>를 이용.</li>
<li>여기서 사용한 <code>class=&quot;container my-3&quot;</code>, <code>class=&quot;table&quot;</code>, <code>class=&quot;table-dark&quot;</code> 등은 부트스트랩 스타일에 정의되어 있는 클래스들.</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">{% load static %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;table class=&quot;table&quot;&gt;
        &lt;thead&gt;
        &lt;tr class=&quot;table-dark&quot;&gt;
            &lt;th&gt;번호&lt;/th&gt;
            &lt;th&gt;제목&lt;/th&gt;
            &lt;th&gt;작성일시&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
        {% if question_list %}
        {% for question in question_list %}
        &lt;tr&gt;
            &lt;td&gt;{{ forloop.counter }}&lt;/td&gt;
            &lt;td&gt;
                &lt;a href=&quot;{% url &#39;pybo:detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;
            &lt;/td&gt;
            &lt;td&gt;{{ question.create_date }}&lt;/td&gt;
        &lt;/tr&gt;
        {% endfor %}
        {% else %}
        &lt;tr&gt;
            &lt;td colspan=&quot;3&quot;&gt;질문이 없습니다.&lt;/td&gt;
        &lt;/tr&gt;
        {% endif %}
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/div&gt;</code></pre>
<p>📌 부트스트랩에 대한 자세한 내용은 다음 URL을 참조!!!</p>
<blockquote>
<ul>
<li>앞으로 템플릿 작성시에 계속 부트스트랩 스타일들을 사용할 것<blockquote>
<p><a href="https://getbootstrap.com/docs/5.1/getting-started/introduction/">https://getbootstrap.com/docs/5.1/getting-started/introduction/</a></p>
</blockquote>
</li>
</ul>
</blockquote>
<ul>
<li><p>이제 다음처럼 부트스트랩이 적용된 질문 목록을 볼 수 있을 
<img src="https://wikidocs.net/images/page/70838/O_2-08_2.png" alt=""></p>
</li>
<li><p>이어서 <code>질문 상세 template</code>에도 다음처럼 부트스트랩을 적용</p>
<blockquote>
<ul>
<li>이번에는 수정사항이 좀 많음.</li>
<li>부트스트랩으로 화면을 구성하다 보면 가끔은 이렇게 많은 양의 <code>HTML</code> 코드를 작성해야 함.</li>
<li><code>질문</code>이나 <code>답변</code>은 <code>하나의 뭉치</code>에 해당하므로 부트스트랩의 <code>card 컴포넌트</code>를 사용.</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_detail.html</code></strong></span></p>
<pre><code class="language-python">{% load static %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;!-- 질문 --&gt;
    &lt;h2 class=&quot;border-bottom py-2&quot;&gt;{{ question.subject }}&lt;/h2&gt;
    &lt;div class=&quot;card my-3&quot;&gt;
        &lt;div class=&quot;card-body&quot;&gt;
            &lt;div class=&quot;card-text&quot; style=&quot;white-space: pre-line;&quot;&gt;{{ question.content }}&lt;/div&gt;
            &lt;div class=&quot;d-flex justify-content-end&quot;&gt;
                &lt;div class=&quot;badge bg-light text-dark p-2&quot;&gt;
                    {{ question.create_date }}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;!-- 답변 --&gt;
    &lt;h5 class=&quot;border-bottom my-3 py-2&quot;&gt;{{question.answer_set.count}}개의 답변이 있습니다.&lt;/h5&gt;
    {% for answer in question.answer_set.all %}
    &lt;div class=&quot;card my-3&quot;&gt;
        &lt;div class=&quot;card-body&quot;&gt;
            &lt;div class=&quot;card-text&quot; style=&quot;white-space: pre-line;&quot;&gt;{{ answer.content }}&lt;/div&gt;
            &lt;div class=&quot;d-flex justify-content-end&quot;&gt;
                &lt;div class=&quot;badge bg-light text-dark p-2&quot;&gt;
                    {{ answer.create_date }}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    {% endfor %}
    &lt;!-- 답변 등록 --&gt;
    &lt;form action=&quot;{% url &#39;pybo:answer_create&#39; question.id %}&quot; method=&quot;post&quot; class=&quot;my-3&quot;&gt;
        {% csrf_token %}
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;content&quot; class=&quot;form-label&quot;&gt;답변내용&lt;/label&gt;
            &lt;textarea name=&quot;content&quot; id=&quot;content&quot; class=&quot;form-control&quot; rows=&quot;10&quot;&gt;&lt;/textarea&gt;
        &lt;/div&gt;
        &lt;input type=&quot;submit&quot; value=&quot;답변등록&quot; class=&quot;btn btn-primary&quot;&gt;
    &lt;/form&gt;
&lt;/div&gt;</code></pre>
<ul>
<li><p>부트스트랩 card 컴포넌트 :</p>
<blockquote>
<p><a href="https://getbootstrap.com/docs/5.1/components/card/">https://getbootstrap.com/docs/5.1/components/card/</a></p>
</blockquote>
</li>
<li><p>질문 상세 템플릿에 사용한 부트스트랩 클래스</p>
<blockquote>
<ul>
<li>그리고 질문 내용과 답변 내용에는 <code>style=&quot;white-space: pre-line;&quot;</code> 과 같은 스타일을 지정해 줌.</li>
<li>글 내용의 <u>줄 바꿈을 정상적으로 표시하기위해 적용한 스타일</u>임.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/4e43336e-c1b4-43a0-ab20-00eb9b41987e/image.png" alt=""></li>
</ul>
</blockquote>
</li>
<li><p>부트스트랩을 적용한 질문 상세 화면
<img src="https://wikidocs.net/images/page/70838/O_2-08_3.png" alt=""></p>
</li>
</ul>
<br>

<h1 id="🌞-span-stylebackground-color-f4eeee2-09-템플릿-상속span">🌞 <span style='background-color: #f4eeee'><code>2-09. 템플릿 상속</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8표준-html-구조span">🌈 <span style='background-color: #ffffe8'><code>표준 HTML 구조</code></span></h2>
<blockquote>
<ul>
<li>표준 <code>HTML</code> 문서의 구조는 위의 예처럼 <code>html</code>, <code>head</code>, <code>body</code> 엘리먼트가 있어야 하며, <code>CSS</code> 파일 링크는 <code>head</code> 엘리먼트 안에 있어야 함.
또한 <code>head</code> 엘리먼트 안에는 <code>meta</code>, <code>title</code> 엘리먼트 등이 포함되어야 함.</li>
</ul>
</blockquote>
<p>📄 <span style="color: #EB5757"><strong><code>[표준 HTML 구조의 예]</code></strong></span></p>
<pre><code class="language-python">&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/bootstrap.min.css&quot;&gt;
    &lt;title&gt;Hello, pybo!&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
(... 생략 ...)
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<h3 id="🏳🌈-span-stylebackground-color-f0fcfc태그와-엘리먼트span">🏳‍🌈 <span style='background-color: #f0fcfc'>태그와 엘리먼트</span></h3>
<pre><code class="language-html">&lt;table&gt; (... 생략 ...) &lt;/table&gt;  &lt;!-- table 엘리먼트 --&gt;</code></pre>
<p>위에서 <strong><code>&lt;table&gt;</code></strong>은 <span style="color: #EB5757"><code>table 태그</code></span>이고 <strong><code>&lt;table&gt; ~ &lt;/table&gt;</code></strong> 처럼 <code>table</code> 태그로 시작해서 <code>table</code> 태그로 닫힌 구간(<code>Block</code>)은 <span style="color: #EB5757"><code>table 엘리먼트</code></span>이다.</p>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8템플릿-상속span">🌈 <span style='background-color: #ffffe8'><code>템플릿 상속</code></span></h2>
<ul>
<li>앞에서 작성한 질문 목록, 질문 상세 템플릿을 표준 <code>HTML</code> 구조가 되도록 수정하는 방법은 무엇일까?<blockquote>
<ul>
<li>그런데 템플릿 파일들을 모두 표준 <code>HTML</code> 구조로 변경하면 <u><code>body</code> 엘리먼트 바깥 부분(<code>head</code> 엘리먼트 등)은 모두 같은 내용으로 중복</u>될 것임.</li>
<li>그러면 <code>CSS</code> 파일 이름이 변경되거나 새로운 <code>CSS</code> 파일이 추가될 때마다 모든 템플릿 파일을 일일이 수정해야 함.</li>
</ul>
</blockquote>
</li>
<li>장고는 이런 중복과 불편함을 해소하기 위해 <span style="color: #EB5757"><strong><code>템플릿 상속(extend)</code></strong></span> 기능을 제공함.</li>
<li><strong><code>템플릿 상속</code></strong> : 기본 틀이 되는 템플릿을 먼저 작성하고 다른 템플릿에서 그 템플릿을 상속해 사용하는 방법</li>
</ul>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcbasehtml-템플릿span">🚦 <span style='background-color: #f0fcfc'><code>base.html</code> 템플릿</span></h3>
<blockquote>
<ul>
<li>모든 템플릿이 상속해야 하는 템플릿</li>
<li>표준 <code>HTML</code> 문서의 기본 틀</li>
<li><code>body</code> 엘리먼트 안의 <span style="color: #EB5757"><code>{% block content %}</code></span>와 <span style="color: #EB5757"><code>{% endblock %}</code></span> 템플릿 태그는 <code>base.html을</code> 상속한 템플릿에서 개별적으로 구현해야 하는 영역이 됨.</li>
</ul>
</blockquote>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/base.html</code></strong></span></p>
<pre><code class="language-python">{% load static %}
&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;!-- Required meta tags --&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;!-- Bootstrap CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
    &lt;!-- pybo CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;style.css&#39; %}&quot;&gt;
    &lt;title&gt;Hello, pybo!&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- 기본 템플릿 안에 삽입될 내용 Start --&gt;
{% block content %}
{% endblock %}
&lt;!-- 기본 템플릿 안에 삽입될 내용 End --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcquestion_list-템플릿span">🚦 <span style='background-color: #f0fcfc'><code>question_list</code> 템플릿</span></h3>
<ul>
<li><code>question_list.html</code> 템플릿을 다음과 같이 변경<blockquote>
<ul>
<li><code>base.html</code> 템플릿을 상속하기 위해 <span style="color: #EB5757"><code>{% extends &#39;base.html&#39; %}</code></span> 처럼 <code>extends</code> 템플릿 문법을 사용.</li>
<li>상단의 두 줄은 <code>base.html</code>에 이미 있는 내용이므로 삭제.</li>
<li>그리고 <code>{% block content %}</code> 와 <code>{% endblock %}</code> 사이에 <code>question_list.html</code>에서만 쓰이는 내용을 작성.</li>
<li>이렇게 하면 <u>이제 <code>question_list.html</code>은 <code>base.html</code> 템플릿을 상속받아 표준 <code>HTML</code>문서로 바뀌게 됨.</u></li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;table class=&quot;table&quot;&gt;

        (... 생략 ...)

    &lt;/table&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcquestion_detail-템플릿span">🚦 <span style='background-color: #f0fcfc'><code>question_detail</code> 템플릿</span></h3>
<ul>
<li><code>question_detail.html</code> 템플릿을 다음과 같이 변경<blockquote>
<ul>
<li><code>{% extends &#39;base.html&#39; %}</code> 템플릿 태그를 맨 위에 추가하고 기존 내용 위 아래로 <code>{% block content %}</code>와 <code>{% endblock %}</code>를 작성.</li>
<li>템플릿 상속을 적용한 후 질문 목록, 질문 상세를 조회해보면 화면에 보여지는 것은 동일하지만 표준 HTML 구조로 변경된 것을 확인할 수 있음.</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;h2 class=&quot;border-bottom py-2&quot;&gt;{{ question.subject }}&lt;/h2&gt;

    (... 생략 ...)

    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<br>

<h1 id="🌞-span-stylebackground-color-f4eeee2-10-폼span">🌞 <span style='background-color: #f4eeee'><code>2-10. 폼</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8질문-등록span">🌈 <span style='background-color: #ffffe8'><code>질문 등록</code></span></h2>
<ul>
<li><code>html</code> 파일에 <code>질문 등록하기</code> 버튼 만들기<blockquote>
<ul>
<li><code>&lt;a href=&quot;...&quot;&gt;</code> 과 같은 링크이지만 부트스트랩의 <code>btn btn-primary</code> 클래스를 적용하면 버튼으로 보이게 됨.</li>
<li>버튼을 클릭하면 <code>pybo:question_create</code> 별칭에 해당되는 <code>URL</code>이 호출될 것</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">    (... 생략 ...)
    &lt;/table&gt;
    &lt;a href=&quot;{% url &#39;pybo:question_create&#39; %}&quot; class=&quot;btn btn-primary&quot;&gt;질문 등록하기&lt;/a&gt;     # 추가
&lt;/div&gt;
{% endblock %}</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcurl-매핑span">🚦 <span style='background-color: #f0fcfc'><code>URL 매핑</code></span></h3>
<ul>
<li><code>pybo:question_create</code> 별칭에 해당되는 <code>URL 매핑 규칙</code>을 추가<blockquote>
<ul>
<li><code>question_create</code> 함수를 호출하도록 매핑</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span></p>
<pre><code class="language-python">from pybo.views import index, detail, question_create, answer_create     # 추가

(... 생략 ...)
urlpatterns = [
    (... 생략 ...)
    path(&#39;question/create/&#39;, question_create, name=&#39;question_create&#39;),    # 추가
]</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfc폼span">🚦 <span style='background-color: #f0fcfc'><code>폼</code></span></h3>
<ul>
<li><p><code>views.py</code>에서 <code>question_create</code> 함수를 작성하기 전에 폼(<code>Form</code>)을 작성</p>
<blockquote>
<ul>
<li>폼(<code>Form</code>)은 <u><span style="color: red">페이지 요청시 전달되는 파라미터들을 쉽게 관리하기 위해 사용하는 클래스</span></u></li>
<li>폼은 필수 <u>파라미터의 값이 누락되지 않았는지, 파라미터의 형식은 적절한지 등을 검증할 목적으로 사용</u></li>
<li>이 외에도 <u>HTML을 자동으로 생성하거나 폼에 연결된 모델을 이용하여 데이터를 저장하는 기능</u>도 있음.</li>
<li><code>question_create</code> 함수를 호출하도록 매핑</li>
</ul>
</blockquote>
</li>
<li><p>질문 등록시 사용할 <code>QuestionForm</code>을 <code>forms.py</code> 파일에 작성</p>
<blockquote>
<ul>
<li><code>QuestionForm</code>은 <u>모델 폼(<code>forms.ModelForm</code>)을 상속</u></li>
<li>장고의 폼은 일반 폼(<code>forms.Form</code>)과 모델 폼(<code>forms.ModelForm</code>)이 있음.<blockquote>
<ul>
<li>모델 폼(<span style="color: #BA55D3"><code>forms.ModelForm</code></span>) : 모델(<code>Model</code>)과 연결된 폼으로 <u>폼을 저장하면 연결된 모델의 데이터를 저장할수 있는 폼</u>
👉 모델 폼은 이너 클래스인 <code>Meta</code> 클래스가 반드시 필요
👉 <code>Meta</code> 클래스에는 사용할 모델과 모델의 속성을 기재해야 함.</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
</li>
<li><p>즉, <code>QuestionForm</code>은 <u><code>Question</code> 모델과 연결된 폼</u>이고 <u>속성으로 <code>Question</code> 모델의 <code>subject</code>와 <code>content</code>를 사용</u>한다고 정의한 것</p>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/forms.py</code></strong></span></p>
<pre><code class="language-python">from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question  # 사용할 모델
        fields = [&#39;subject&#39;, &#39;content&#39;]  # QuestionForm에서 사용할 Question 모델의 속성</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcview-함수span">🚦 <span style='background-color: #f0fcfc'><code>view 함수</code></span></h3>
<ul>
<li><code>views.py</code>에서 <code>question_create</code> 함수를 작성<blockquote>
<ul>
<li><code>question_create</code> 함수는 위에서 작성한 <code>QuestionForm</code>을 사용</li>
<li><code>render</code> 함수에 전달한 <span style="color: #BA55D3"><code>{&#39;form&#39;: form}</code></span>은 템플릿에서 질문 등록시 사용할 폼 엘리먼트를 생성할 때 사용</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/forms.py</code></strong></span></p>
<pre><code class="language-python">from .forms import QuestionForm

(... 생략 ...)

def question_create(request):
    form = QuestionForm()                                        
    return render(request, &#39;pybo/question_form.html&#39;, {&#39;form&#39;: form})</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfctemplatespan">🚦 <span style='background-color: #f0fcfc'><code>template</code></span></h3>
<ul>
<li><code>pybo/question_form.html</code> 템플릿을 작성<blockquote>
<ul>
<li>템플릿에서 사용한 <span style="color: #BA55D3"><code>{{ form.as_p }}</code></span>의 <strong><code>form</code></strong>은 <u><code>question_create</code> 함수에서 전달한 <code>QuestionForm</code>의 객체</u></li>
<li><strong><code>{{ form.as_p }}</code></strong>는 <u>폼에 정의한 <code>subject</code>, <code>content</code> 속성에 해당하는 <code>HTML</code> 코드를 자동으로 생성</u></li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/template/pybo/question_form.html</code></strong></span></p>
<pre><code class="language-python">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container&quot;&gt;
    &lt;h5 class=&quot;my-3 border-bottom pb-2&quot;&gt;질문등록&lt;/h5&gt;
    &lt;form method=&quot;post&quot;&gt;
        {% csrf_token %}
        {{ form.as_p }}
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;저장하기&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<h4 id="📌-action-속성">📌 <code>action</code> 속성</h4>
<ul>
<li><p>보통 <code>form</code> 태그에는 항상 <span style="color: #BA55D3"><code>action</code></span> 속성을 지정하여 <u><code>submit</code> 실행시 <code>action</code>에 정의된 <code>URL</code>로 폼을 전송</u>해야 함.</p>
<blockquote>
<ul>
<li>하지만 여기서는 특별하게 <code>action</code> 속성을 지정하지 않았음.</li>
<li><code>form</code> 태그에 <code>action</code> 속성을 지정하지 않으면 현재 페이지의 <code>URL</code>이 디폴트 <code>action</code>으로 설정됨.</li>
</ul>
</blockquote>
</li>
<li><p>물론 <code>action</code> 속성을 다음처럼 명확하게 지정해도 됨.</p>
<pre><code class="language-python">&lt;form method=&quot;post&quot; action=&quot;{% url &#39;pybo:question_create&#39; %}&quot;&gt;</code></pre>
</li>
<li><p>위와 같이 action 속성을 지정할 경우, <u><code>question_form.html</code> 템플릿은 <code>질문 등록</code> 에서만 사용 가능</u></p>
<blockquote>
<ul>
<li>이후에 진행할 <code>질문 수정</code> 에서는 이 템플릿을 활용할 수가 없음.</li>
<li>왜냐하면 <code>질문 수정</code>일 경우에는 <code>action</code> 값을 달리해야 하기 때문</li>
<li><span style="color: #EB5757">동일한 템플릿을 여러 기능에서 함께 사용할 경우에는 이처럼 <code>form</code>의 <code>action</code> 속성을 비워두는 트릭을 종종 사용</span></li>
<li>우리는 이후에 <code>질문 수정</code> 기능을 구현할 때도 <code>question_form.html</code> 템플릿을 사용할 것이므로 <u><code>action</code> 속성은 비워둘 것!!</u></li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcget과-postspan">🚦 <span style='background-color: #f0fcfc'><code>GET과 POST</code></span></h3>
<p>서버 재시작 후 브라우저에서 정상 작동 여부 확인</p>
<blockquote>
<ul>
<li>forms.py와 같은 신규 파일 작성시에는 로컬 서버 재시작이 필요</li>
</ul>
</blockquote>
<p><img src="https://wikidocs.net/images/page/70855/C_2-10_1.png" alt=""></p>
<ul>
<li><p>질문 목록 화면 하단에 &quot;질문 등록하기&quot; 버튼이 추가됨.</p>
<ul>
<li>&quot;질문 등록하기&quot; 버튼을 클릭하면 다음과 같이 &quot;질문 등록&quot; 화면이 나타남.
<img src="https://wikidocs.net/images/page/70855/O_2-10_2.png" alt=""></li>
</ul>
</li>
<li><p><code>question_create</code> 함수에 데이터를 저장하는 코드를 작성</p>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<pre><code class="language-python">def question_create(request):
    if request.method == &#39;POST&#39;:
        form = QuestionForm(request.POST)
        if form.is_valid():  # 폼이 유효하다면
            question = form.save(commit=False)  # 임시 저장하여 question 객체를 리턴받고
            question.create_date = timezone.now()  # 실제 저장을 위해 작성일시를 설정.
            question.save()  # 데이터를 실제로 저장.
            return redirect(&#39;pybo:index&#39;)
    else:
        form = QuestionForm()
    context = {&#39;form&#39;: form}
    return render(request, &#39;pybo/question_form.html&#39;, context)</code></pre>
<h4 id="📌-post-get">📌 <code>POST</code>, <code>GET</code></h4>
<ul>
<li><p>동일한 <code>URL</code> 요청을 <code>POST</code>, <code>GET</code> 요청 방식에 따라 다르게 처리</p>
<blockquote>
<ul>
<li><strong>질문 <u>목록</u> 화면</strong>에서 <span style="color: #20B2AA">&quot;질문 등록하기&quot; 버튼을 클릭</span>한 경우에는 <code>/pybo/question/create/</code> 페이지가 <span style="color: #BA55D3"><code>GET</code></span> 방식으로 요청되어 <code>question_create</code> 함수가 실행됨.<ul>
<li>왜냐하면 <span style="color: #EB5757"><code>&lt;a href=&quot;{% url &#39;pybo:question_create&#39; %}&quot; class=&quot;btn btn-primary&quot;&gt;질문 등록하기&lt;/a&gt;</code></span>와 같이 <u>링크를 통해 페이지를 요청할 경우에는 무조건 <code>GET</code> 방식이 사용</u>되기 때문.
따라서 이 경우에는 <code>request.method</code> 값이 <code>GET</code>이 되어 <code>if .. else ..</code> 구문에서 <code>else</code> 구문을 타게 되어 질문을 등록하는 화면을 렌더링<br></li>
</ul>
</li>
<li>그리고 <strong>질문 <u>등록</u> 화면</strong>에서 <span style="color: #20B2AA"><code>subject</code>, <code>content</code> 항목에 값을 기입하고 <code>저장하기</code> 버튼</span>을 누르면 이번에는 <code>/pybo/question/create/</code> 페이지를 <span style="color: #BA55D3"><code>POST</code></span> 방식으로 요청함.<ul>
<li>왜냐하면 앞서 설명했듯이 <u><code>form</code> 태그에 <code>action</code> 속성이 지정되지 않으면 현재 페이지가 디폴트 <code>action</code>으로 설정되기 때문.</u></li>
<li>따라서 질문 등록 화면에서 <code>저장하기</code> 버튼을 클릭하면 <code>question_create</code> 함수가 실행되고 <code>request.method</code> 값은 <code>POST</code>가 되어 해당 코드 블럭이 실행됨.</li>
</ul>
</li>
</ul>
</blockquote>
</li>
<li><p><code>GET</code> 방식에서는 <code>form = QuestionForm()</code> 처럼 <code>QuestionForm</code>을 인수 없이 생성.</p>
</li>
<li><p><code>POST</code> 방식에서는 <code>form = QuestionForm(request.POST)</code> 처럼 <code>request.POST</code>를 인수로 생성.</p>
<blockquote>
<ul>
<li><code>request.POST</code>를 인수로 <code>QuestionForm</code>을 생성할 경우에는 <code>request.POST</code>에 담긴 <code>subject</code>, <code>content</code> 값이 <code>QuestionForm</code>의 <code>subject</code>, <code>content</code> 속성에 자동으로 저장되어 객체가 생성됨.<ul>
<li><u><code>request.POST</code>에는 화면에서 사용자가 입력한 내용들이 담겨있음.</u></li>
</ul>
</li>
<li>그리고 <code>form.is_valid()</code>는 <code>form</code>이 유효한지를 검사<ul>
<li>만약 <code>form</code>에 저장된 <code>subject</code>, <code>content</code>의 값이 올바르지 않다면 <code>form</code>에는 오류 메시지가 저장되고 <code>form.is_valid()</code>가 실패하여 다시 질문 등록 화면을 렌더링.</li>
<li>이 때 form에는 오류 메시지가 저장되므로 화면에 오류를 표시할 수 있음.</li>
</ul>
</li>
</ul>
</blockquote>
</li>
<li><p><span style="color: #EB5757"><code>if form.is_valid()</code></span>: <code>form</code>이 유효하다면 이후의 문장이 수행되어 질문 데이터가 생됨.</p>
</li>
<li><p><span style="color: #EB5757"><code>question = form.save(commit=False)</code></span> : <code>form</code>에 저장된 데이터로 <code>Question</code> 데이터를 저장하기 위한 코드</p>
<blockquote>
<ul>
<li><code>QuestionForm</code>이 <code>Question</code> 모델과 연결된 모델 폼이기 때문에 이와 같이 사용 가능.</li>
<li>여기서 <u><code>commit=False</code>는 임시 저장</u>을 의미.<ul>
<li>즉, <u>실제 데이터는 아직 데이터베이스에 저장되지 않은 상태</u>를 뜻함.</li>
<li>여기서 <code>form.save(commit=False)</code> 대신 <code>form.save()</code>를 수행하면 <code>Question</code> 모델의 <code>create_date</code>에 값이 없다는 오류가 발생.</li>
<li><span style="color: #EB5757">왜냐하면 <code>QuestionForm</code>에는 현재 <code>subject</code>, <code>content</code>속성만 정의되어 있고 <code>create_date</code> 속성은 없기 때문.</span></li>
<li>이러한 이유로 <u>임시 저장을 하여 question 객체를 리턴받고 create_date에 값을 설정한 후 question.save()로 실제 데이터를 저장</u>하는 것<blockquote>
<ul>
<li><code>create_date</code> 속성은 <u>데이터 저장 시점에 생성해야 하는 값</u>이므로 QuestionForm에 등록하여 사용하지 않음.</li>
</ul>
</blockquote>
</li>
</ul>
</li>
</ul>
</blockquote>
</li>
<li><p>이제 브라우저에서 질문 등록이 잘 되는지 확인</p>
</li>
</ul>
<p><img src="https://wikidocs.net/images/page/70855/C_2-10_1.png" alt="">
<img src="https://wikidocs.net/images/page/70855/C_2-10_3.png" alt="">
<img src="https://wikidocs.net/images/page/70855/C_2-10_4.png" alt=""></p>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcform-widgetspan">🚦 <span style='background-color: #f0fcfc'><code>form widget</code></span></h3>
<ul>
<li><span style="color: #EB5757"><code>{{ form.as_p }}</code></span> 태그는 <code>HTML</code> 코드를 자동으로 생성하기 때문에 부트스트랩을 적용할 수가 없음.<blockquote>
<ul>
<li>완벽하지는 않지만 다음처럼 <code>QuestionForm</code>을 조금 수정하면 어느정도 해결이 가능</li>
<li><u><code>widgets</code> 속성을 지정하면 <code>subject</code>, <code>content</code> 입력 필드에 <code>form-control</code>과 같은 부트스트랩 클래스를 추가 가능</u></li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/forms.py</code></strong></span></p>
<pre><code class="language-python">from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = [&#39;subject&#39;, &#39;content&#39;]
        widgets = {
            &#39;subject&#39;: forms.TextInput(attrs={&#39;class&#39;: &#39;form-control&#39;}),
            &#39;content&#39;: forms.Textarea(attrs={&#39;class&#39;: &#39;form-control&#39;, &#39;rows&#39;: 10}),
        }</code></pre>
<ul>
<li>다시 질문 등록 화면을 요청해 보면 다음과 같이 부트스트랩이 적용된 것을 확인 가능</li>
</ul>
<p><img src="https://wikidocs.net/images/page/70855/O_2-10_5.png" alt=""></p>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcform-labelspan">🚦 <span style='background-color: #f0fcfc'><code>form label</code></span></h3>
<ul>
<li>질문 등록 화면에 표시되는 <code>Subject</code>, <code>Content</code>를 영문이 아니라 <strong>한글로 표시</strong>하고 싶다면 다음처럼 <code>labels</code> 속성을 지정</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/forms.py</code></strong></span></p>
<pre><code class="language-python">from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = [&#39;subject&#39;, &#39;content&#39;]
        widgets = {
            &#39;subject&#39;: forms.TextInput(attrs={&#39;class&#39;: &#39;form-control&#39;}),
            &#39;content&#39;: forms.Textarea(attrs={&#39;class&#39;: &#39;form-control&#39;, &#39;rows&#39;: 10}),
        }
        labels = {                  # 추가
            &#39;subject&#39;: &#39;제목&#39;,       # 추가
            &#39;content&#39;: &#39;내용&#39;,       # 추가
        }                           # 추가</code></pre>
<p><img src="https://wikidocs.net/images/page/70855/C_2-10_6.png" alt=""></p>
<ul>
<li>장고 폼(form)에 대한 보다 자세한 내용은 다음 URL을 참고<blockquote>
<p><a href="https://docs.djangoproject.com/en/4.0/topics/forms/">https://docs.djangoproject.com/en/4.0/topics/forms/</a></p>
</blockquote>
</li>
</ul>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfc수동-폼-작성span">🚦 <span style='background-color: #f0fcfc'><code>수동 폼 작성</code></span></h3>
<ul>
<li><p><span style="color: #EB5757"><code>{{ form.as_p }}</code></span></p>
<blockquote>
<ul>
<li><span style="color: #EB5757"><code>{{ form.as_p }}</code></span>를 사용하면 빠르게 템플릿을 만들 수 있지만 <u><code>HTML</code> 코드가 자동으로 생성</u>되므로 디자인 측면에서 많은 제한이 생김.</li>
<li>예를 들어 폼 엘리먼트 내에 특정 태그를 추가하거나 필요한 클래스를 추가하는 작업에 제한이 생김.</li>
<li>또 디자인 영역과 서버 프로그램 영역이 혼재되어 웹 디자이너와 개발자의 역할을 분리하기도 모호해짐.</li>
</ul>
</blockquote>
</li>
<li><p>이용하여 자동으로 <code>HTML</code> 코드를 생성하지 않고 직접 <code>HTML</code> 코드를 작성하는 방법을 사용하기 위해 수작업시 필요없는 widget 속성을 제거</p>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/forms.py</code></strong></span></p>
<pre><code class="language-python">from django import forms
from pybo.models import Question


class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question  # 사용할 모델
        fields = [&#39;subject&#39;, &#39;content&#39;]  # QuestionForm에서 사용할 Question 모델의 속성
        widgets = {
            &#39;subject&#39;: forms.TextInput(attrs={&#39;class&#39;: &#39;form-control&#39;}),
            &#39;content&#39;: forms.Textarea(attrs={&#39;class&#39;: &#39;form-control&#39;, &#39;rows&#39;: 10}),
        }</code></pre>
<ul>
<li>그리고 질문 등록 템플릿을 다음과 같이 수정<blockquote>
<ul>
<li><code>{{ form.as_p }}</code>로 자동으로 생성되는 <code>HTML</code> 대신 <u>제목과 내용에 해당되는 HTML코드를 직접 작성</u></li>
<li>그리고 <code>question_create</code> 함수에서 <u><code>form.is_valid()</code> 가 실패할 경우 발생하는 오류의 내용을 표시하기 위해 <code>오류를 표시하는 영역</code>을 추가</u><br></li>
<li>제목(<code>subject</code>) 항목의 <code>value</code>에는 <span style="color: #EB5757"><code>{{ form.subject.value|default_if_none:&#39;&#39; }}</code></span> 처럼 값을 대입해 주었는데 이것은 <u>오류가 발생했을 경우 기존에 입력했던 값을 유지하기 위함</u>.</li>
<li><span style="color: #EB5757"><code>|default_if_none:&#39;&#39;</code></span>의 의미 : 폼 데이터(<code>form.subject.value</code>)에 값이 없을 경우 <code>None</code> 이라는 문자열이 표시되는데 <u><code>None</code> 대신 <code>공백</code>으로 표시하라</u>는 의미의 <span style="color: #BA55D3">템플릿 필터</span><blockquote>
<ul>
<li>장고의 <span style="color: #BA55D3">템플릿 필터</span>는 <code>|default_if_none:&#39;&#39;</code> 처럼 <span style="color: #EB5757"><code>|</code> </span>기호와 함께 사용됨.</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_form.html</code></strong></span></p>
<pre><code class="language-python">{% extends &#39;base.html&#39; %}

{% block content %}
&lt;div class=&quot;container&quot;&gt;
    &lt;h5 class=&quot;my-3 border-bottom pb-2&quot;&gt;질문등록&lt;/h5&gt;
    &lt;form method=&quot;post&quot;&gt;
        {% csrf_token %}
        &lt;!-- 오류표시 Start --&gt;                                               # 추가 시작
        {% if form.errors %}
        &lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;
            {% for field in form %}
            {% if field.errors %}
            &lt;div&gt;
                &lt;strong&gt;{{ field.label }}&lt;/strong&gt;
                {{ field.errors }}
            &lt;/div&gt;
            {% endif %}
            {% endfor %}
        &lt;/div&gt;
        {% endif %}
        &lt;!-- 오류표시 End --&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;subject&quot; class=&quot;form-label&quot;&gt;제목&lt;/label&gt;
            &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;subject&quot; id=&quot;subject&quot;
                   value=&quot;{{ form.subject.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;content&quot; class=&quot;form-label&quot;&gt;내용&lt;/label&gt;
            &lt;textarea class=&quot;form-control&quot; name=&quot;content&quot;
                      id=&quot;content&quot; rows=&quot;10&quot;&gt;{{ form.content.value|default_if_none:&#39;&#39; }}&lt;/textarea&gt;
        &lt;/div&gt;                                                            # 추가 끝
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;저장하기&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<br>

<ul>
<li>&quot;질문등록&quot; 화면에서 제목에만 &quot;TEST&quot;라고 입력하고 &quot;내용&quot;은 비워둔 채 &quot;저장하기&quot; 버튼을 클릭해보면<blockquote>
<ul>
<li>&quot;내용&quot;에 아무런 값도 입력하지 않았기 때문에 &quot;내용&quot;을 입력하라는 오류메시지 확인 가능</li>
<li>그리고 &quot;제목&quot;에 입력했던 &quot;TEST&quot;는 사라지지 않고 계속 유지되는 것도 확인 가능.</li>
</ul>
</blockquote>
</li>
</ul>
<p><img src="https://wikidocs.net/images/page/70855/O_2-10_7.png" alt=""></p>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8답변-등록span">🌈 <span style='background-color: #ffffe8'><code>답변 등록</code></span></h2>
<ul>
<li>질문 등록에 장고 폼을 적용한 것처럼 답변 등록에도 장고 폼을 적용.</li>
</ul>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcanswerformspan">🚦 <span style='background-color: #f0fcfc'><code>AnswerForm</code></span></h3>
<ul>
<li>답변을 등록할 때 사용할 <code>AnswerForm</code>을 <code>pybo/forms.py</code> 파일에 다음과 같이 작성</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/forms.py</code></strong></span></p>
<pre><code class="language-python">from django import forms
from pybo.models import Question, Answer

(... 생략 ...)

class AnswerForm(forms.ModelForm):
    class Meta:
        model = Answer
        fields = [&#39;content&#39;]
        labels = {
            &#39;content&#39;: &#39;답변내용&#39;,
        }</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcanswer_create-함수span">🚦 <span style='background-color: #f0fcfc'><code>answer_create</code> 함수</span></h3>
<ul>
<li><code>answer_create</code> 함수를 다음과 같이 수정<blockquote>
<ul>
<li><code>question_create</code>와 같은 방법으로 <code>AnswerForm</code>을 이용하도록 변경.</li>
<li>하지만 답변 등록은 <code>POST</code> 방식만 사용되기 때문에 <code>GET</code> 방식으로 요청할 경우에는 <code>HttpResponseNotAllowed</code> 오류가 발생하도록 설정.</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<pre><code class="language-python">(... 생략 ...)
from django.http import HttpResponseNotAllowed
from .forms import QuestionForm, AnswerForm
(... 생략 ...)

def answer_create(request, question_id):
    &quot;&quot;&quot;
    pybo 답변등록
    &quot;&quot;&quot;
    question = get_object_or_404(Question, pk=question_id)
    if request.method == &quot;POST&quot;:
        form = AnswerForm(request.POST)
        if form.is_valid():
            answer = form.save(commit=False)
            answer.create_date = timezone.now()
            answer.question = question
            answer.save()
            return redirect(&#39;pybo:detail&#39;, question_id=question.id)
    else:
        return HttpResponseNotAllowed(&#39;Only POST is possible.&#39;)
    context = {&#39;question&#39;: question, &#39;form&#39;: form}
    return render(request, &#39;pybo/question_detail.html&#39;, context)</code></pre>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcquestion_detailhtmlspan">🚦 <span style='background-color: #f0fcfc'><code>question_detail.html</code></span></h3>
<ul>
<li>질문 상세 템플릿도 오류를 표시하기 위한 영역을 다음처럼 추가</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_detail.html</code></strong></span></p>
<pre><code class="language-python">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    (... 생략 ...)
    &lt;form action=&quot;{% url &#39;pybo:answer_create&#39; question.id %}&quot; method=&quot;post&quot; class=&quot;my-3&quot;&gt;
        {% csrf_token %}
        &lt;!-- 오류표시 Start --&gt;                     # 추가 시작
        {% if form.errors %}
        &lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;
            {% for field in form %}
            {% if field.errors %}
            &lt;div&gt;
                &lt;strong&gt;{{ field.label }}&lt;/strong&gt;
                {{ field.errors }}
            &lt;/div&gt;
            {% endif %}
            {% endfor %}
        &lt;/div&gt;
        {% endif %}
        &lt;!-- 오류표시 End --&gt;                       # 추가 끝
        &lt;div class=&quot;form-group&quot;&gt;
            &lt;textarea name=&quot;content&quot; id=&quot;content&quot; class=&quot;form-control&quot; rows=&quot;10&quot;&gt;&lt;/textarea&gt;
        &lt;/div&gt;
        &lt;input type=&quot;submit&quot; value=&quot;답변등록&quot; class=&quot;btn btn-primary&quot;&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<ul>
<li>이렇게 수정하고 답변 내용 없이 답변을 등록하려고 하면 다음과 같은 오류 메시지가 표시됨.</li>
</ul>
<p><img src="https://wikidocs.net/images/page/70855/C_2-10_8.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[221009_점프투장고 2-04 ~ 2-06]]></title>
            <link>https://velog.io/@brown_eyed87/221008%EC%A0%90%ED%94%84%ED%88%AC%EC%9E%A5%EA%B3%A0-2-04-2-06</link>
            <guid>https://velog.io/@brown_eyed87/221008%EC%A0%90%ED%94%84%ED%88%AC%EC%9E%A5%EA%B3%A0-2-04-2-06</guid>
            <pubDate>Sun, 09 Oct 2022 01:26:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> 🌉 <a href="https://wikidocs.net/book/4223">점프 투 장고</a> 사이트를 참고하여 일정 단위의 <code>chapter</code>에 대해 실습 후 <code>새롭게 알게된 내용</code>, 혹은 <code>복습이 꼭 필요한 내용</code>에 대해 정리!!</p>
</blockquote>
<h1 id="🌞-span-stylebackground-color-f4eeee2-04-조회와-템플릿span">🌞 <span style='background-color: #f4eeee'><code>2-04. 조회와 템플릿</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8질문-목록-조회-구현span">🌈 <span style='background-color: #ffffe8'><code>질문 목록 조회 구현</code></span></h2>
<p><span style='background-color: #E6E6E6'><span style='color: #20B2AA'><a href="http://localhost:8000/pybo">http://localhost:8000/pybo</a></span>
</span>
위의 페이지를 요청하면</p>
<p><code>현재</code> : &quot;안녕하세요 pybo에 오신 것을 환영합니다.&quot; 라는 문구가 출력됨.
<code>변경</code> : <code>질문 목록</code>이 출력되도록 프로젝트 파일들을 수정!</p>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<pre><code class="language-python"># from django.http import HttpResponse                               # 삭제
from django.shortcuts import render
from .models import Question


def index(request):
    # return HttpResponse(&quot;안녕하세요 pybo에 오신 것을 환영합니다.&quot;)     # 삭제
    question_list = Question.objects.order_by(&#39;-create_date&#39;)
    context = {&#39;question_list&#39;: question_list}
    return render(request, &#39;pybo/question_list.html&#39;, context)</code></pre>
<ul>
<li>코드 풀이<blockquote>
<ol>
<li>질문 목록 데이터 : 
<span style="color: #BA55D3"><code>Question.objects.order_by(&#39;-create_date&#39;)</code></span><ul>
<li><code>order_by</code> : 조회 결과를 정렬하는 함수</li>
<li><code>order_by(&#39;-create_date&#39;)</code> : 작성일시를 역순으로 정렬하라는 의미</li>
<li><code>-</code> 기호 : 붙어 있으면 역방향, 없으면 순방향 정렬을 의미
→ <u>게시물은 보통 최신순</u>으로 보기 때문에 작성일시의 <u>역순으로 정렬</u><br></li>
</ul>
</li>
<li><code>render</code> 함수 :<ul>
<li><u>파이썬 데이터를 템플릿에 적용하여 HTML로 반환하는 함수</u></li>
<li>즉, 위에서 사용한 <code>render</code> 함수는 <code>질문 목록으로 조회한 question_list 데이터</code>를 <code>pybo/question_list.html 파일에 적용</code>하여 <code>HTML을 생성한후 리턴</code></li>
<li>여기서 사용된 <code>pybo/question_list.html</code>과 같은 파일을 <code>템플릿(Template)</code>이라고 부름.
→ 템플릿 파일은 HTML 파일과 비슷하지만 파이썬 데이터를 읽어서 사용할수 있는 HTML 파일</li>
</ul>
</li>
</ol>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">{% if question_list %}
    &lt;ul&gt;
    {% for question in question_list %}
        &lt;li&gt;&lt;a href=&quot;/pybo/{{ question.id }}/&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;질문이 없습니다.&lt;/p&gt;
{% endif %}</code></pre>
<ul>
<li>템플릿을 보면 <code>{% if question_list %}</code> 처럼 <code>{% 와 %}</code> 로 둘러싸인 문장들을 볼 수 있는데 이러한 것들을 <span style="color: #BA55D3"><code>템플릿 태그</code></span>라고 함.<ul>
<li><code>question_list.html</code>에 사용된 템플릿 태그들은 다음과 같음.</li>
<li>템플릿에서 사용한 <span style="color: #BA55D3"><code>question_list</code></span>는 <u><code>render</code> 함수로 전달한 &quot;질문 목록&quot; 데이터</u> 
→ <code>views.py</code>의 <code>index</code> 함수에서 2번째 줄에 있는 <code>question_list</code>임
<img src="https://velog.velcdn.com/images/brown_eyed87/post/d5b30fa0-ae93-4202-885e-ea74c0229465/image.png" alt=""></li>
</ul>
</li>
</ul>
<p><span style='color: #20B2AA'><a href="http://localhost:8000/pybo">http://localhost:8000/pybo</a></span> 접속 시, 다음과 같이 변경된 화면을 확인 가능.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/f234e4da-5602-411b-b70c-f511c65b24c9/image.png" alt=""></p>
<h2 id="🌈-span-stylebackground-color-ffffe8템플릿-태그span">🌈 <span style='background-color: #ffffe8'><code>템플릿 태그</code></span></h2>
<blockquote>
<ul>
<li>장고에서 사용하는 템플릿 태그는 다음 3가지 유형만 알면 됨.</li>
</ul>
</blockquote>
<h3 id="🚦-span-stylebackground-color-f0fcfc1-분기span">🚦 <span style='background-color: #f0fcfc'>1. 분기</span></h3>
<ul>
<li>분기문 태그의 사용법<blockquote>
<ul>
<li>파이썬의 <code>if</code> 문과 유사하지만 항상 <span style="color: #EB5757"><strong><code>{% endif %}</code></strong></span> 태그로 닫아야 함.</li>
</ul>
</blockquote>
<pre><code class="language-python">{% if 조건문1 %}
 &lt;p&gt;조건문1에 해당되는 경우&lt;/p&gt;
{% elif 조건문2 %}
 &lt;p&gt;조건문2에 해당되는 경우&lt;/p&gt;
{% else %}
 &lt;p&gt;조건문1, 2에 모두 해당되지 않는 경우&lt;/p&gt;
{% endif %}</code></pre>
</li>
</ul>
<h3 id="🚦-span-stylebackground-color-f0fcfc2-반복span">🚦 <span style='background-color: #f0fcfc'>2. 반복</span></h3>
<ul>
<li>반복문 태그의 사용법<blockquote>
<ul>
<li>파이썬의 <code>for</code> 문과 유사하지만 항상 <span style="color: #EB5757"><strong><code>{% endfor %}</code></strong></span> 태그로 닫아야 함.</li>
</ul>
</blockquote>
<pre><code class="language-python">{% for item in list %}
 &lt;p&gt;순서: {{ forloop.counter }} &lt;/p&gt;
 &lt;p&gt;{{ item }}&lt;/p&gt;
{% endfor %}</code></pre>
<ul>
<li>템플릿 <code>for</code> 문 안에서는 다음과 같은 <code>forloop</code> 객체를 사용
<img src="https://velog.velcdn.com/images/brown_eyed87/post/d44a16d1-dc54-4355-b287-7a926a4e05ed/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="🚦-span-stylebackground-color-f0fcfc3-객체-출력span">🚦 <span style='background-color: #f0fcfc'>3. 객체 출력</span></h3>
<ul>
<li>객체를 출력하기 위한 태그의 사용법<blockquote>
<ul>
<li>파이썬의 <code>for</code> 문과 유사하지만 항상 <span style="color: #EB5757"><strong><code>{% endfor %}</code></strong></span> 태그로 닫아야 함.</li>
</ul>
</blockquote>
<pre><code class="language-python">{{ 객체 }}</code></pre>
예) <span style="color: #EB5757"><code>{{ item }}</code></span></li>
</ul>
<ul>
<li>객체에 속성이 있는 경우는 파이썬과 동일한 방법으로 도트(.) 문자를 이용하여 표시</li>
</ul>
<p>예) <span style="color: #EB5757"><code>{{question.id}}</code></span>, <span style="color: #EB5757"><code>{{question.subject}}</code></span></p>
<blockquote>
<ul>
<li>이 책에서는 파이보에 필요한 템플릿 문법이 나올 때마다 자세하게 알아볼 예정.</li>
<li>템플릿에 대한 보다 자세한 내용은 다음 URL을 참고.
<a href="https://docs.djangoproject.com/en/4.0/topics/templates/">https://docs.djangoproject.com/en/4.0/topics/templates/</a></li>
</ul>
</blockquote>
<h2 id="🌈-span-stylebackground-color-ffffe8질문-상세-조회-구현span">🌈 <span style='background-color: #ffffe8'><code>질문 상세 조회 구현</code></span></h2>
<p><span style='color: #20B2AA'><code>http://localhost:8000/pybo/2/</code></span></p>
<ul>
<li>이 URL의 의도 : <code>id 값이 2인 Question을 상세 조회</code></li>
</ul>
<p>동작을 위해 파일들을 아래와 같이 수정</p>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span></p>
<pre><code class="language-python">from django.urls import path

from pybo.views import index, detail

urlpatterns = [
    path(&#39;&#39;, index),
    path(&#39;&lt;int:question_id&gt;/&#39;, detail),    # 추가
]</code></pre>
<blockquote>
<ul>
<li><code>path(&#39;&lt;int:question_id&gt;/&#39;, detail)</code> 라는 <span style="color: #BA55D3"><code>URL 매핑</code></span>을 추가.</li>
<li>이제 <code>http://localhost:8000/pybo/2/</code> 페이지가 요청되면 여기에 등록한 매핑 룰에 의해 <code>http://localhost:8000/pybo/&lt;int:question_id&gt;/</code> 가 적용되어 <code>question_id</code> 에 <code>2</code>가 저장되고 <code>detail</code> 함수도 실행될 것</li>
<li><code>&lt;int:question_id&gt;</code> 에서 <code>int</code>는 숫자가 매핑됨을 의미</li>
</ul>
</blockquote>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<ul>
<li><code>detail</code> 함수 추가</li>
</ul>
<pre><code class="language-python">def detail(request, question_id):
    question = Question.objects.get(id=question_id)
    context = {&#39;question&#39;: question}
    return render(request, &#39;pybo/question_detail.html&#39;, context)</code></pre>
<blockquote>
<ul>
<li><code>detail</code> 함수 호출시 전달되는 <strong>매개변수</strong>가 <code>request</code> 외에 <span style="color: #BA55D3"><code>question_id</code></span>가 추가됨.</li>
<li>매개변수 <code>question_id</code>에는 <code>URL</code> 매핑시 저장된 <code>question_id</code>가 전달</li>
<li><code>http://localhost:8000/pybo/2/</code> 페이지가 요청되면 매개변수 <code>question_id</code>에 <code>2</code>가 세팅되어 <code>detail</code> 함수가 실행</li>
</ul>
</blockquote>
<p>📄 <span style="color: #EB5757"><strong>`/templates/pybo/question_detail.html</strong>`</span></p>
<ul>
<li>detail 함수에서 사용할 pybo/question_detail.html 템플릿</li>
</ul>
<pre><code class="language-python">&lt;h1&gt;{{ question.subject }}&lt;/h1&gt;
&lt;div&gt;
    {{ question.content }}
&lt;/div&gt;</code></pre>
<blockquote>
<ul>
<li><span style="color: #EB5757"><code>{{ question.subject }}</code></span>과 <span style="color: #EB5757"><code>{{ question.content }}</code></span>의 <strong><code>question</code></strong>은 <code>detail</code> 함수에서 템플릿에 <code>context</code> 변수로 전달한 <code>Question 모델 객체</code></li>
</ul>
</blockquote>
<p><span style="color: #EB5757"><code>http://localhost:8000/pybo/2/</code></span> 접속</p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/beda7f6e-62a4-4f82-a9e6-8682ee42a6ff/image.png" alt=""></p>
<p>❗❗ <strong>오류 페이지</strong>
<span style="color: #EB5757"><code>http://localhost:8000/pybo/30/</code></span> 페이지를 요청
<img src="https://velog.velcdn.com/images/brown_eyed87/post/a6965a8c-9ad4-4ff2-85aa-b03e0a849636/image.png" alt=""></p>
<ul>
<li>그러면 <code>DoesNotExist</code> 오류가 발생. </li>
<li>이 오류는 전달된 <code>question_id</code>가 <code>30</code>이기 때문에 <code>Question.object.get(id=30)</code>이 호출되어 발생한 오류임.</li>
<li>이 때 브라우저에 전달되는 오류코드는 <code>500</code><blockquote>
<p>이렇게 없는 데이터를 요청할 경우 <code>500</code> 오류 페이지 보다는 <span style="color: #BA55D3"><code>Not Found (404)</code></span> 페이지를 리턴하는 것이 바람직</p>
</blockquote>
</li>
</ul>
<p><span style="color: #EB5757"><code>[HTTP 주요 응답코드의 종류]</code></span>
<img src="https://velog.velcdn.com/images/brown_eyed87/post/b66321d2-a66d-452a-9e73-0324f47e6b99/image.png" alt=""></p>
<p><span style="color: #EB5757"><code>http://localhost:8000/pybo/30/</code></span> 처럼 없는 데이터를 요청할 경우 <code>500</code> 페이지 대신 <code>404</code> 페이지를 출력하도록 <code>detail</code> 함수를 수정</p>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<blockquote>
<ul>
<li><code>Question.objects.get(id=question_id)</code>에서 <code>get</code> 함수를<code>get_object_or_404(Question, pk=question_id)</code>로 변경.<ul>
<li>여기서 사용한 <code>pk</code>는 <code>Question</code> 모델의 기본키(<code>Primary Key</code>)에 해당하는 값을 의미</li>
</ul>
</li>
</ul>
</blockquote>
<pre><code class="language-python">from django.shortcuts import render, get_object_or_404            #  get_object_or_404 추가
from .models import Question

(... 생략 ...)

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)        # 수정
    context = {&#39;question&#39;: question}
    return render(request, &#39;pybo/question_detail.html&#39;, context)</code></pre>
<br>

<h3 id="🏳🌈-span-stylebackground-color-f0fcfcgeneric-view-알아두기span">🏳‍🌈 <span style='background-color: #f0fcfc'><code>Generic View</code> 알아두기</span></h3>
<blockquote>
<ul>
<li>이 책은 제네릭뷰를 사용하지 않음.</li>
<li>왜냐하면 제네릭뷰는 매우 편리하지만 내부적으로 어떻게 동작하는지 이해하기 쉽지 않아 장고를 배우는 지금 이 시점에서는 오히려 혼란을 초래할 수 있기 때문</li>
<li>간단한 내용만 살펴볼 것</li>
</ul>
</blockquote>
<ul>
<li><p>장고에는 제네릭뷰(<code>Generic View</code>)라는 것이 있음.</p>
<ul>
<li><p>목록 조회나 상세 조회 같은 특정한 패턴이 있는 뷰를 작성할 때 늘 비슷한 내용을 입력하기 때문에 이것을 패턴화하여 간략하게 만든것이 바로 제네릭 뷰</p>
</li>
<li><p>만약 우리가 <code>views.py</code>에 작성한 <code>index</code>나 <code>detail</code> 함수를 제네릭 뷰로 변경하면 다음처럼 간략하게 작성 가능</p>
</li>
</ul>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<pre><code class="language-python">class IndexView(generic.ListView):
    def get_queryset(self):
        return Question.objects.order_by(&#39;-create_date&#39;)

class DetailView(generic.DetailView):
    model = Question</code></pre>
<blockquote>
<ul>
<li><span style="color: #BA55D3"><code>IndexView</code></span> 클래스가 <code>index</code> 함수를 대체하고 <span style="color: #BA55D3"><code>DetailView</code></span> 클래스가 <code>detail</code> 함수를 대체함.</li>
<li><code>IndexView</code>는 템플릿 명이 명시적으로 지정되지 않은 경우에는 <strong>디폴트</strong>로 <span style="color: #BA55D3"><code>모델명_list.html</code></span> 을 템플릿명으로 사용.</li>
<li>마찬가지로 <code>DetailView</code>는 <span style="color: #BA55D3"><code>모델명_detail.html</code></span>을 <strong>디폴트 템플릿명</strong>으로 사용.</li>
</ul>
</blockquote>
<ul>
<li>그리고 제네릭 뷰 사용을 위해 <code>pybo/urls.py</code> 파일은 다음과 같이 변경되어야 함.</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span></p>
<pre><code class="language-python">from django.urls import path

from . import views

app_name = &#39;pybo&#39;
urlpatterns = [
    path(&#39;&#39;, views.IndexView.as_view()),
    path(&#39;&lt;int:pk&gt;/&#39;, views.DetailView.as_view()),
]</code></pre>
<blockquote>
<ul>
<li>이렇듯 모델의 목록 조회나 상세 조회는 제네릭뷰를 사용하는것이 매우 간편함.</li>
<li>단, 제네릭 뷰를 사용할 경우에는 복잡한 케이스에서 더 어렵게 작성되는 경우가 종종 있으니 주의하여 사용</li>
</ul>
</blockquote>
<br>

<h1 id="🌞-span-stylebackground-color-f4eeee2-05-url-별칭span">🌞 <span style='background-color: #f4eeee'><code>2-05. URL 별칭</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8url-하드코딩span">🌈 <span style='background-color: #ffffe8'><code>URL 하드코딩</code></span></h2>
<p><code>question_list.html</code> 템플릿에 사용된 링크</p>
<pre><code class="language-python">&lt;li&gt;&lt;a href=&quot;/pybo/{{ question.id }}/&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;</code></pre>
<ul>
<li><p>위의 링크는 질문 상세를 위한 <code>URL</code> 링크임.</p>
<blockquote>
<ul>
<li>하지만 이러한 URL 링크는 수정될 가능성이 있음.</li>
<li>예를 들어, <code>http://localhost:8000/pybo/question/2</code> 또는 <code>http://localhost:8000/pybo/2/question</code> 처럼 변경될 가능성이 존재.</li>
<li><em>실제 프로젝트에서 <code>URL 리팩토링</code>은 빈번하게 발생</em></li>
</ul>
</blockquote>
</li>
<li><p><code>URL 링크</code>의 구조가 자주 변경된다면 템플릿에서 사용한 모든 <code>URL</code>들을 일일이 찾아가며 수정해야 하는 리스크가 발생.</p>
</li>
<li><p>이러한 문제점을 해결하기 위해서는 <u>해당 <code>URL</code>에 대한 실제 링크 대신 링크의 주소가 <code>1:1</code> 매핑되어 있는 별칭을 사용</u></p>
</li>
</ul>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8url-별칭span">🌈 <span style='background-color: #ffffe8'><code>URL 별칭</code></span></h2>
<ul>
<li><u>링크의 주소 대신 별칭을 사용하려면 <span style="color: red"><code>URL</code> 매핑에 <code>name 속성</code>을 부여</span></u>하면 됨.</li>
<li><code>/pybo.urls.py</code> 파일을 다음과 같이 수정<blockquote>
<ul>
<li><a href="http://localhost:8000/pybo/">http://localhost:8000/pybo/</a> URL은 <span style="color: #BA55D3"><code>index</code></span>, </li>
<li><a href="http://localhost:8000/pybo/2%EC%99%80">http://localhost:8000/pybo/2와</a> 같은 URL에는 <span style="color: #BA55D3"><code>detail</code></span>라는 별칭을 부여한 것</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span></p>
<pre><code class="language-python">from django.urls import path

from pybo.views import index, detail

urlpatterns = [
    path(&#39;&#39;, index, name=&#39;index&#39;),
    path(&#39;&lt;int:question_id&gt;/&#39;, detail, name=&#39;detail&#39;),
]</code></pre>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8템플릿에서-url-별칭-사용하기span">🌈 <span style='background-color: #ffffe8'><code>템플릿에서 URL 별칭 사용하기</code></span></h2>
<ul>
<li>이렇게 <code>pybo/urls.py</code> 파일에 별칭을 추가하면 템플릿에서 다음처럼 사용<blockquote>
<ul>
<li>하드코딩 되어 있던 <code>/pybo/{{ question.id }}</code> 링크를 <span style="color: #BA55D3"><code>{% url &#39;detail&#39; question.id %}</code></span>로 변경</li>
<li>여기서 <span style="color: #BA55D3"><code>question.id</code></span>는 <code>URL 매핑</code>에 정의된 <u><code>&lt;int:question_id&gt;</code>에 전달해야 하는 값</u>을 의미</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">{% if question_list %}
    &lt;ul&gt;
    {% for question in question_list %}
        &lt;li&gt;&lt;a href=&quot;{% url &#39;detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;질문이 없습니다.&lt;/p&gt;
{% endif %}</code></pre>
<p>📌 <strong>파라미터명 전달</strong></p>
<ol>
<li><code>한 개의 파라미터를 전달</code>할 경우에는 다음과 같이 사용<pre><code class="language-python">{% url &#39;detail&#39; question.id %}</code></pre>
</li>
<li>이 때 다음처럼 <code>파라미터 명을 함께 사용</code> 가능<pre><code class="language-python">{% url &#39;detail&#39; question_id=question.id %}</code></pre>
</li>
<li>만약 <code>2개 이상의 파라미터를 사용</code>해야 한다면 다음과 같이 <code>공백 문자 이후에 덧 붙여주면 됨.</code><pre><code class="language-python">{% url &#39;detail&#39; question_id=question.id page=2 %}</code></pre>
</li>
</ol>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8url-네임스페이스span">🌈 <span style='background-color: #ffffe8'><code>URL 네임스페이스</code></span></h2>
<blockquote>
<ul>
<li>현재는 <code>pybo</code> 앱 하나만 사용중이지만 <code>pybo</code> 앱 이외의 다른 앱이 프로젝트에 추가 될 수도 있을 것임.</li>
<li>이런 경우 서로 다른 앱에서 동일한 <code>URL</code> 별칭을 사용하면 중복이 발생할 것</li>
</ul>
</blockquote>
<p>❗❗ 이 문제를 해결하려면 <code>pybo/urls.py</code> 파일에 <u>네임스페이스를 의미하는 <code>app_name</code> 변수를 지정해야 함.</u></p>
<blockquote>
<ul>
<li><code>app_name</code>을 <code>pybo</code>로 설정</li>
</ul>
</blockquote>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span></p>
<pre><code class="language-python">from django.urls import path
from pybo.views import index, detail

app_name = &#39;pybo&#39;         # 추가

urlpatterns = [
    path(&#39;&#39;, index, name=&#39;index&#39;),
    path(&#39;&lt;int:question_id&gt;/&#39;, detail, name=&#39;detail&#39;),
]</code></pre>
<ul>
<li>위와 같이 수정 후, 템플릿에서 사용한 <code>URL</code> 별칭에 네임스페이스를 다음과 같이 지정해야 함.<blockquote>
<ul>
<li><code>detail</code> 앞에 <code>pybo</code> 라는 네임스페이스를 붙여준 것</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">{% if question_list %}
    &lt;ul&gt;
    {% for question in question_list %}
        &lt;li&gt;&lt;a href=&quot;{% url &#39;pybo:detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;      # 수정
    {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;질문이 없습니다.&lt;/p&gt;
{% endif %}</code></pre>
<p>📌 <strong><code>redirect</code> 함수와 <code>URL</code> 별칭</strong></p>
<ul>
<li><code>URL</code> 별칭은 템플릿 외에 <code>redirect</code> 함수에서도 사용됨.</li>
<li><code>redirect</code>는 특정 페이지로 이동시키는 함수.<pre><code class="language-python">redirect(&#39;pybo:detail&#39;, question_id=question.id)</code></pre>
</li>
</ul>
<br>

<h1 id="🌞-span-stylebackground-color-f4eeee2-06-데이터-저장span">🌞 <span style='background-color: #f4eeee'><code>2-06. 데이터 저장</code></span></h1>
<ul>
<li>이번 장에서는 답변을 등록하는 기능을 생성 예정</li>
</ul>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8답변-등록-폼-추가span">🌈 <span style='background-color: #ffffe8'><code>답변 등록 폼 추가</code></span></h2>
<ul>
<li>질문 상세 템플릿에 다음처럼 답변을 저장할 수 있는 폼(<code>form</code>)을 추가<blockquote>
<ul>
<li>답변의 내용을 입력할 수 있는 텍스트창(<code>textarea</code>)과 답변을 저장 할 수 있는 &quot;답변등록&quot; 버튼을 추가.</li>
<li>답변 저장을 위한 <code>URL</code>은 <code>form 태그</code>의 <code>action 속성</code>에 <span style="color: #BA55D3"><code>{% url &#39;pybo:answer_create&#39; question.id %}</code></span>로 지정</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">&lt;h1&gt;{{ question.subject }}&lt;/h1&gt;
&lt;div&gt;
    {{ question.content }}
&lt;/div&gt;
&lt;form action=&quot;{% url &#39;pybo:answer_create&#39; question.id %}&quot; method=&quot;post&quot;&gt;      # 추가
{% csrf_token %}                                                              # 추가
&lt;textarea name=&quot;content&quot; id=&quot;content&quot; rows=&quot;15&quot;&gt;&lt;/textarea&gt;                   # 추가
&lt;input type=&quot;submit&quot; value=&quot;답변등록&quot;&gt;                                         # 추가
&lt;/form&gt;                                                                       # 추가</code></pre>
<ul>
<li><p><code>form</code> 태그 바로 밑에 보이는 <span style="color: #BA55D3"><code>{% csrf_token %}</code></span>은 <u>보안에 관련된 항목으로 <code>form</code>으로 전송한 데이터가 실제 웹 페이지에서 작성한 데이터인지를 판단하는 가늠자 역할</u>을 함.</p>
<blockquote>
<ul>
<li>만약 어떤 해커가 이상한 방법으로 데이터를 전송할 경우에는 서버에서 발행한 <code>csrf_token</code> 값과 해커가 일방적으로 보낸 <code>csrf_token</code> 값이 일치하지 않기 때문에 블록킹될 것</li>
</ul>
</blockquote>
</li>
<li><p>따라서 <u><code>form 태그</code> 바로 밑에 <code>{% csrf_token %} 태그</code>를 항상 위치시켜야</u> 함. </p>
<blockquote>
<ul>
<li><code>POST</code> 요청시 <code>form 태그</code>에 <code>csrf_token</code>이 없으면 장고는 오류를 냄.</li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="🏳🌈-span-stylebackground-color-f0fcfccsrf란span">🏳‍🌈 <span style='background-color: #f0fcfc'><code>CSRF란?</code></span></h3>
<p><code>CSRF(cross site request forgery)</code></p>
<ul>
<li>웹 사이트 취약점 공격을 방지를 위해 사용하는 기술</li>
<li>장고가 <code>CSRF 토큰 값</code>을 세션을 통해 발행하고 웹 페이지에서는 폼 전송시에 해당 토큰을 함께 전송하여 실제 웹 페이지에서 작성된 데이터가 전달되는지를 검증하는 기술</li>
<li><code>csrf_token</code> 사용을 위해서는 <code>CsrfViewMiddleware 미들웨어</code>가 필요한데 이 미들웨어는 <code>settings.py</code>의 <code>MIDDLEWARE 항목</code>에 디폴트로 추가되어 있으므로 별도의 설정은 필요 없음.</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_list.html</code></strong></span></p>
<pre><code class="language-python">(... 생략 ...)

MIDDLEWARE = [
    (... 생략 ...)
    &#39;django.middleware.csrf.CsrfViewMiddleware&#39;,
    (... 생략 ...)
]
(... 생략 ...)</code></pre>
<ul>
<li>만약 <code>csrf_token</code> 기능을 사용하고 싶지 않다면 저 코드 한 줄을 주석처리하면 됨.</li>
</ul>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8url-매핑span">🌈 <span style='background-color: #ffffe8'><code>URL 매핑</code></span></h2>
<ul>
<li>질문 상세 템플릿을 위와 같이 고친 후 질문 상세 페이지를 요청하기 위해 <code>pybo/urls.py</code>에 다음과 같은 <code>URL 매핑</code>을 등록<blockquote>
<ul>
<li><code>answer_create</code> 별칭에 해당하는 <code>URL</code> 매핑 규칙을 등록.</li>
<li>이제 <a href="http://locahost:8000/pybo/answer/create/2/">http://locahost:8000/pybo/answer/create/2/</a> 와 같은 페이지를 요청하면 <code>URL 매핑 규칙</code>에 의해 <code>answer_create</code> 함수가 호출될 것</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span></p>
<pre><code class="language-python">from django.urls import path

from pybo.views import index, detail, answer_create    # 추가


app_name = &quot;pybo&quot;

urlpatterns = [
    path(&quot;&quot;, index),
    path(&quot;&lt;int:question_id&gt;/&quot;, detail, name=&quot;detail&quot;),
    path(&quot;answer/create/&lt;int:question_id&gt;/&quot;, answer_create, name=&quot;answer_create&quot;),   # 추가
]</code></pre>
<br>

<ul>
<li>그리고 <code>URL 매핑 규칙</code>에 정의된 <code>answer_create</code> 함수를 <code>pybo/views.py</code> 파일에 다음처럼 추가<blockquote>
<ul>
<li><code>answer_create</code> 함수의 매개변수 <code>question_id</code>는 <u><code>URL 매핑</code>에 의해 그 값이 전달됨.</u></li>
<li>만약 <a href="http://locahost:8000/pybo/answer/create/2/">http://locahost:8000/pybo/answer/create/2/</a> 라는 페이지를 요청하면 매개변수 <code>question_id</code>에는 <code>2</code>라는 값이 전달될 것</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<pre><code class="language-python">from django.shortcuts import render, get_object_or_404, redirect   # 추가
from django.utils import timezone   # 추가
from .models import Question

(... 생략 ...)

def answer_create(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    question.answer_set.create(content=request.POST.get(&#39;content&#39;), create_date=timezone.now())
    return redirect(&#39;pybo:detail&#39;, question_id=question.id)</code></pre>
<ul>
<li><p>답변 등록시 텍스트창에 입력한 내용은 <code>answer_create</code> 함수의 첫번째 매개변수인 <code>request</code> 객체를 통해 읽을 수 있음.</p>
<blockquote>
<ul>
<li>즉, <code>request.POST.get(&#39;content&#39;)</code>로 텍스트창에 입력한 내용을 읽을 수 있음.</li>
<li><span style="color: #BA55D3"><code>request.POST.get(&#39;content&#39;)</code></span> : <code>POST</code>로 전송된 폼(<code>form</code>) 데이터 항목 중 <code>content</code> 값을 의미</li>
</ul>
</blockquote>
</li>
<li><p>그리고 답변을 생성하기 위해 <code>question.asnswer_set.create</code> 를 사용.</p>
<blockquote>
<ul>
<li><span style="color: #BA55D3"><code>question.answer_set</code></span> : 질문의 답변을 의미.</li>
<li><code>Question</code>과 <code>Answer</code> 모델은 서로 <code>ForeignKey</code> 로 연결되어 있기 때문에 이처럼 사용 가능.</li>
</ul>
</blockquote>
</li>
</ul>
<p>📌 답변을 저장하는 또 다른 방법은 <strong><code>Answer 모델을 직접 사용</code></strong>하는 방법</p>
<blockquote>
<ul>
<li>어떤것을 사용해도 결과는 동일</li>
</ul>
</blockquote>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/views.py</code></strong></span></p>
<pre><code class="language-python">(... 생략 ...)
from .models import Question, Answer

(... 생략 ...)

def answer_create(request, question_id):
    &quot;&quot;&quot;
    pybo 답변등록
    &quot;&quot;&quot;
    question = get_object_or_404(Question, pk=question_id)
    answer = Answer(question=question, content=request.POST.get(&#39;content&#39;), create_date=timezone.now())     # 변경 및 추가된 부분
    answer.save()                                                                                           # 변경 및 추가된 부분
    return redirect(&#39;pybo:detail&#39;, question_id=question.id)</code></pre>
<ul>
<li>답변을 생성한 후 질문 상세 화면을 다시 보여주기 위해 <code>redirect</code> 함수를 사용<blockquote>
<ul>
<li><span style="color: #BA55D3"><code>redirect 함수</code></span> : 페이지 이동을 위한 함수.</li>
<li><code>pybo:detail</code> 별칭에 해당하는 페이지로 이동하기 위해 <code>redirect</code> 함수를 사용.</li>
<li>그리고 <code>pybo:detail</code> 별칭에 해당하는 <code>URL</code>은 <code>question_id</code>가 필요하므로 <code>question.id</code>를 인수로 전달</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8답변-저장span">🌈 <span style='background-color: #ffffe8'><code>답변 저장</code></span></h2>
<ul>
<li>이렇게 수정한 후, 질문 상세 화면 호출<blockquote>
<ul>
<li>텍스트 창에 아무 값이나 입력하고 답변을 등록해보면 <u>화면에는 아무런 변화가 없음.</u></li>
<li>왜냐하면 <u>아직 등록된 답변을 표시하는 기능을 템플릿에 추가하지 않았기 때문</u></li>
</ul>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/bc21bae0-a4a2-4e50-b65d-86afda712bcb/image.png" alt=""></p>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8답변-조회span">🌈 <span style='background-color: #ffffe8'><code>답변 조회</code></span></h2>
<ul>
<li>등록된 답변을 질문 상세 화면에 표시하려면 다음과 같이 질문 상세 템플릿을 수정해야 함.<blockquote>
<ul>
<li>중간 부분에 질문에 등록된 답변을 확인할 수 있는 영역을 추가.</li>
<li><span style="color: #BA55D3"><code>question.answer_set.count</code></span> : <strong>답변의 총 갯수</strong>를 의미<ul>
<li><span style="color: #BA55D3"><code>question.answer_set</code></span> : <strong>질문과 연결된 답변들</strong>을 의미</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/templates/pybo/question_detail.html</code></strong></span></p>
<pre><code class="language-python">&lt;h1&gt;{{ question.subject }}&lt;/h1&gt;
&lt;div&gt;
    {{ question.content }}
&lt;/div&gt;
&lt;h5&gt;{{ question.answer_set.count }}개의 답변이 있습니다.&lt;/h5&gt;    # 추가
&lt;div&gt;                                                          # 추가
    &lt;ul&gt;                                                       # 추가
    {% for answer in question.answer_set.all %}                # 추가
        &lt;li&gt;{{ answer.content }}&lt;/li&gt;                          # 추가
    {% endfor %}                                               # 추가
    &lt;/ul&gt;                                                      # 추가
&lt;/div&gt;                                                         # 추가
&lt;form action=&quot;{% url &#39;pybo:answer_create&#39; question.id %}&quot; method=&quot;post&quot;&gt;
{% csrf_token %}
&lt;textarea name=&quot;content&quot; id=&quot;content&quot; rows=&quot;15&quot;&gt;&lt;/textarea&gt;
&lt;input type=&quot;submit&quot; value=&quot;답변등록&quot;&gt;
&lt;/form&gt;</code></pre>
<ul>
<li>이렇게 수정 후, 다시 질문 상세 화면을 호출</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/4aeb2aca-2823-4e9c-8859-05ec3afb0092/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[221008_점프투장고 2-01 ~ 2-03]]></title>
            <link>https://velog.io/@brown_eyed87/221008%EC%A0%90%ED%94%84%ED%88%AC%EC%9E%A5%EA%B3%A0-2-01-2-03</link>
            <guid>https://velog.io/@brown_eyed87/221008%EC%A0%90%ED%94%84%ED%88%AC%EC%9E%A5%EA%B3%A0-2-01-2-03</guid>
            <pubDate>Sat, 08 Oct 2022 15:13:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> 🌉 <a href="https://wikidocs.net/book/4223">점프 투 장고</a> 사이트를 참고하여 일정 단위의 <code>chapter</code>에 대해 실습 후 <code>새롭게 알게된 내용</code>, 혹은 <code>복습이 꼭 필요한 내용</code>에 대해 정리!!</p>
</blockquote>
<h1 id="🌞-span-stylebackground-color-f4eeee2-01-url과-viewspan">🌞 <span style='background-color: #f4eeee'><code>2-01. URL과 View</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8장고-개발-흐름-정리하기span">🌈 <span style='background-color: #ffffe8'><code>장고 개발 흐름 정리하기</code></span></h2>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/00a17c27-c3cf-4c81-a76a-1c09782a4df7/image.png" alt=""></p>
<p><span style="color: #EB5757"><strong><code>[1]</code></strong></span> 브라우저에서 로컬 서버로 <span style="color: #EB5757"><strong><code>http://localhost:8000/pybo</code></strong></span> 페이지를 요청
<span style="color: #EB5757"><strong><code>[2]</code></strong></span> urls.py 파일에서 <span style="color: #EB5757"><strong><code>/pybo</code></strong></span> URL 매핑을 확인하여 <code>views.py</code> 파일의 <code>index</code> 함수를 호출
<span style="color: #EB5757"><strong><code>[3]</code></strong></span> 호출한 결과를 브라우저에 반영</p>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8url-분리span">🌈 <span style='background-color: #ffffe8'><code>URL 분리</code></span></h2>
<p>✨ <code>pybo</code> 앱에 관련한 것들은 <code>pybo</code> 앱 디렉터리 하위에 위치해야 함.</p>
<ul>
<li>config의 <code>urls.py</code> 파일은 앱이 아닌 프로젝트 성격의 파일이므로, 이곳에는 프로젝트 성격의 URL 매핑만 추가되어야 함.</li>
<li>따라서 <code>pybo</code> 앱에서만 사용하는 URL 매핑을 <span style="color: #EB5757"><strong><code>pybo/urls.py</code></strong></span> 파일로 분리하여 관리할 것!</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/config/urls.py</code></strong></span> </p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include
# from pybo import views  → 더 이상 필요하지 않으므로 삭제

urlpatterns = [
    path(&#39;admin/&#39;, admin.site.urls),
    path(&#39;pybo/&#39;, include(&#39;pybo.urls&#39;)),  # ← 추가한 코드
]</code></pre>
<p><span style="color: #20B2AA"><code>path(&#39;pybo/&#39;, include(&#39;pybo.urls&#39;))</code></span></p>
<ul>
<li><code>pybo/</code>로 시작하는 페이지를 요청하면 이제 <code>pybo/urls.py</code> 파일의 매핑 정보를 읽어서 처리하라는 의미</li>
<li>따라서 이제 <code>pybo/</code>로 시작하는 <code>URL</code> (예시 : <code>pybo/question/create, pybo/answer/create</code>)을 추가해야 할 때 <code>config/urls.py</code> 파일을 수정할 필요없이 <span style="color: #EB5757"><strong><code>pybo/urls.py</code></strong></span> 파일만 수정하면 됨.<br>

</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span> </p>
<pre><code class="language-python">from django.urls import path

from pybo.views import index

urlpatterns = [
    path(&#39;&#39;, index),
]</code></pre>
<ul>
<li>기존 <span style="color: #EB5757"><strong><code>/config/urls.py</code></strong></span>  파일에 설정했던 내용과 별반 차이가 없음.</li>
<li>다만 <code>path(&#39;&#39;, index)</code> 처럼 <code>pybo/</code> 가 생략된 <code>&#39;&#39;</code> 이 사용됨. <ul>
<li>이렇게 되는 이유는 <code>config/urls.py</code> 파일에서 이미 <code>pybo/</code>로 시작하는 <code>URL</code>이 <code>pybo/urls.py</code> 파일과 먼저 매핑되었기 때문.</li>
</ul>
</li>
<li>즉, <code>pybo/</code> URL은 다음처럼 <code>config/urls.py</code> 파일에 매핑된 <code>pybo/</code> 와 <code>pybo/urls.py</code> 파일에 매핑된 <code>&#39;&#39;</code> 이 더해져 <code>pybo/</code>가 됨.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/9a0a5a77-1fc6-4d33-91dd-9c3770ae81e2/image.png" alt=""></p>
<br>

<h1 id="🌞-span-stylebackground-color-f4eeee2-02-modelspan">🌞 <span style='background-color: #f4eeee'><code>2-02. Model</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8ormspan">🌈 <span style='background-color: #ffffe8'><code>ORM</code></span></h2>
<h3 id="🚦-span-stylebackground-color-f0fcfc쿼리문span">🚦 <span style='background-color: #f0fcfc'>쿼리문</span></h3>
<blockquote>
<p>데이터베이스의 데이터를 생성, 조회, 수정, 삭제하기 위해 사용하는 질의문(문법)</p>
</blockquote>
<h3 id="🚦-span-stylebackground-color-f0fcfcormspan">🚦 <span style='background-color: #f0fcfc'>ORM</span></h3>
<blockquote>
<ol>
<li>전통적으로 데이터베이스를 사용하는 프로그램들은 데이터베이스의 데이터를 조회하거나 저장하기 위해 쿼리문을 사용해야 했다.</li>
<li>이 방식은 여전히 많이 사용되고 있는 방식이지만 몇 가지 단점이 있다. <ul>
<li>개발자마다 다양한 쿼리문이 만들어지고, 또 잘못 작성된 쿼리는 시스템의 성능을 저하 시킬수 있기 때문이다.</li>
<li>그리고 데이터베이스를 <code>MySQL</code>에서 오라클로 변경하면 프로그램에서 사용한 쿼리문을 모두 해당 데이터베이스의 규칙에 맞게 수정해야 하는 어려움도 생긴다.</li>
</ul>
</li>
</ol>
</blockquote>
<ul>
<li><code>ORM(Object Relational Mapping)</code>을 사용하면 데이터베이스의 테이블을 모델화하여 사용하기 때문에 위에서 열거한 <code>SQL</code>방식의 단점이 모두 없어짐.</li>
<li><code>ORM</code>을 사용하면 개발자별로 독특한 쿼리문이 만들어질 수가 없고 또 쿼리를 잘못 작성할 가능성도 낮아짐.</li>
<li>그리고 데이터베이스 종류가 변경되더라도 쿼리문이 아닌 모델을 사용하기 때문에 프로그램을 수정할 필요가 없음.<br>

</li>
</ul>
<h2 id="🌈-span-stylebackground-color-ffffe8cascade-옵션span">🌈 <span style='background-color: #ffffe8'><code>CASCADE 옵션</code></span></h2>
<h3 id="🚦-span-stylebackground-color-f0fcfcforeignkeyspan">🚦 <span style='background-color: #f0fcfc'>ForeignKey</span></h3>
<ul>
<li><p><code>Answer</code> 모델은 질문에 대한 답변에 해당되므로 <code>Question</code> 모델을 속성으로 가져가야함.</p>
</li>
<li><p><u>기존 모델을 속성으로 연결</u>하려면 <span style="color: #EB5757"><strong><code>ForeignKey</code></strong></span>를 사용해야 함.</p>
</li>
<li><p><code>ForeignKey</code>는 다른 모델과 연결하기 위해 사용.</p>
<h3 id="🚦-span-stylebackground-color-f0fcfcon_deletemodelscascadespan">🚦 <span style='background-color: #f0fcfc'>on_delete=models.CASCADE</span></h3>
<ul>
<li>이 답변(<code>Answer</code>)과 연결된 질문(<code>Question</code>)이 삭제될 경우 답변(<code>Answer</code>)도 함께 삭제된다는 의미</li>
<li>질문 하나에는 무수히 많은 답변이 등록될 수 있음.</li>
<li><code>CASCADE 옵션</code>은 <u>질문을 삭제하면 그에 달린 답변들도 모두 함께 삭제</u><br>
</li>
</ul>
</li>
<li><p>장고에서 사용하는 속성(Field)의 타입은 이것 외에도 많음.</p>
<blockquote>
<ul>
<li>다음 URL에서 어떤것들이 있는지 참고할 것
<a href="https://docs.djangoproject.com/en/4.0/ref/models/fields/#field-types">https://docs.djangoproject.com/en/4.0/ref/models/fields/#field-types</a></li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8sqlmigratespan">🌈 <span style='background-color: #ffffe8'><code>sqlmigrate</code></span></h2>
<ul>
<li>makemigrations로 데이터베이스 작업 파일을 생성하고 migrate 명령을 실행하기 전에 실제 어떤 쿼리문이 실행되는지 sqlmigrate 명령으로 확인해 볼 수 있음.</li>
<li>sqlmigrate 명령은 단지 실행되는 쿼리만 조회할 뿐이며, 실제 쿼리가 수행되지는 않음.<br>

</li>
</ul>
<p>📌 다음의 명령어를 <code>Terminal</code> 창에서 실행</p>
<p><span style="color: #EB5757"><strong><code>python manage.py sqlmigrate &#39;앱이름&#39; &#39;makemigrations으로 생성된 파일 이름&#39;</code></strong></span></p>
<ul>
<li>예시
```python
(mysite) c:\projects\mysite&gt; python manage.py sqlmigrate pybo 0001
BEGIN;</li>
<li>-</li>
<li><ul>
<li>Create model Question</li>
</ul>
</li>
<li>-
CREATE TABLE &quot;pybo_question&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY AUTOINCREMENT, &quot;subject&quot; varchar(200) NOT NULL, &quot;content&quot; text NOT NULL, &quot;create_date&quot; datetime NOT NULL);</li>
<li>-</li>
<li><ul>
<li>Create model Answer</li>
</ul>
</li>
<li>-
CREATE TABLE &quot;pybo_answer&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY AUTOINCREMENT, &quot;content&quot; text NOT NULL, &quot;create_date&quot; datetime NOT NULL, &quot;question_id&quot; bigint NOT NULL REFERENCES &quot;pybo_question&quot; (&quot;id&quot;) DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX &quot;pybo_answer_question_id_e174c39f&quot; ON &quot;pybo_answer&quot; (&quot;question_id&quot;);
COMMIT;</li>
</ul>
<p>(mysite) c:\projects\mysite&gt;</p>
<pre><code>&gt;- &lt;span style=&quot;color: #EB5757&quot;&gt;`python manage.py sqlmigrate pybo 0001`&lt;/span&gt;명령에서 `pybo`는 앱 이름을 의미하고 `0001`은 생성된 작업파일(예: `0001_initial.py`)의 일련번호를 의미.
&gt;- `SQL` 문에 익숙한 독자라면 데이터베이스에 어떤 일들이 벌어질지 유추할 수 있을 것이지만, 여기에 출력되는 `SQL` 문들이 무엇인지 몰라도 상관없음.
&gt;- 장고는 이러한 `SQL` 대신 `Model`을 사용하기 때문

&lt;br&gt;

## 🌈 &lt;span style=&#39;background-color: #ffffe8&#39;&gt;`Django shell`&lt;/span&gt;

&gt; 장고 셸을 실행하여 모델을 사용하는 방법
&gt; 장고 셸은 장고에 필요한 환경들이 자동으로 설정되어 실행됨.

📌 다음의 명령어를 `Terminal` 창에서 실행

&lt;span style=&quot;color: #EB5757&quot;&gt;**`python manage.py shell`**&lt;/span&gt;
&lt;br&gt;

### 🚦 &lt;span style=&#39;background-color: #f0fcfc&#39;&gt;Question 생성&lt;/span&gt;

- Question과 Answer 모델은 장고 셸에서 다음처럼 import하여 사용할 수 있음.
```python
&gt;&gt;&gt; from pybo.models import Question, Answer</code></pre><ul>
<li>Question 모델을 이용하여 질문 데이터를 만들기<blockquote>
<ul>
<li>Question 모델의 create_date 속성은 DateTimeField 타입이므로 timezone.now()로 현재일시를 대입.</li>
</ul>
</blockquote>
</li>
<li>아래처럼 Question 모델의 객체 q를 생성한 후 save함수를 실행하면 질문 데이터가 1건 생성됨.<pre><code class="language-python">&gt;&gt;&gt; from django.utils import timezone
&gt;&gt;&gt; q = Question(subject=&#39;pybo가 무엇인가요?&#39;, content=&#39;pybo에 대해서 알고 싶습니다.&#39;, create_date=timezone.now())
&gt;&gt;&gt; q.save()</code></pre>
</li>
<li>데이터가 1건 생성되면 반드시 다음처럼 id 값이 생성됨.<pre><code class="language-python">&gt;&gt;&gt; q.id
1</code></pre>
</li>
<li>id는 모델 데이터의 유일한 값으로 프라이머리 키(PK:Primary Key)라고도 함.</li>
<li>이 id 값은 데이터를 생성할 때마다 1씩 증가됨.<pre><code class="language-python">&gt;&gt;&gt; q = Question(subject=&#39;장고 모델 질문입니다.&#39;, content=&#39;id는 자동으로 생성되나요?&#39;, create_date=timezone.now())
&gt;&gt;&gt; q.save()
&gt;&gt;&gt; q.id
2
1</code></pre>
<br>

</li>
</ul>
<h3 id="🚦-span-stylebackground-color-f0fcfcquestion-조회--전체-데이터-조회span">🚦 <span style='background-color: #f0fcfc'>Question 조회 : 전체 데이터 조회</span></h3>
<ul>
<li><p>저장한 데이터를 조회</p>
<pre><code class="language-python">&gt;&gt;&gt; Question.objects.all()
&lt;QuerySet [&lt;Question: Question object (1)&gt;, &lt;Question: Question object (2)&gt;]&gt;</code></pre>
</li>
<li><p>저장한 Question 모델의 데이터는 Question.objects 를 통해서 조회할 수 있음.</p>
<blockquote>
<ul>
<li>Question.objects.all()은 모든 Question 데이터를 조회하는 함수임.</li>
<li>결과값으로는 QuerySet 객체가 리턴되는데 위처럼 Question 객체를 포함하고 있음.</li>
<li>Question object (1), Question object (2) 에서 1과 2는 Question 데이터의 id 값을 의미.</li>
</ul>
</blockquote>
</li>
<li><p>다음처럼 Question 모델에 <strong>str</strong> 메서드를 추가하면 id 값 대신 제목을 표시할 수 있음.</p>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/urls.py</code></strong></span> </p>
<pre><code class="language-python">(... 생략 ...)

class Question(models.Model):
    subject = models.CharField(max_length=200)
    content = models.TextField()
    create_date = models.DateTimeField()

    def __str__(self):          # 추가한 코드
        return self.subject     # 추가한 코드

(... 생략 ...)</code></pre>
<ul>
<li>모델이 변경되었으므로 장고 셸을 재시작해야 변경된 결과를 확인할 수 있음.<blockquote>
<p>장고 셸을 종료하기 위해서는 장고 셸에서 Ctrl+Z 또는 quit()을 입력.</p>
</blockquote>
</li>
<li>이렇게 수정하고 Question.objects.all() 함수를 다시 실행<pre><code class="language-python">(mysite) c:\projects\mysite&gt;python manage.py shell
&gt;&gt;&gt; from pybo.models import Question, Answer
&gt;&gt;&gt; Question.objects.all()
&lt;QuerySet [&lt;Question: pybo가 무엇인가요?&gt;, &lt;Question: 장고 모델 질문입니다.&gt;]&gt;
&gt;&gt;&gt;</code></pre>
</li>
<li>1과 2라는 id 값 대신 이제 제목이 표시되는 것을 확인 가능.<br>

</li>
</ul>
<p>❗❗ 모델에 메서드가 추가될 경우에는 makemigrations와 migrate를 수행할 필요가 없음.</p>
<ul>
<li>makemigrations, migrate 명령이 필요한 경우는 모델의 속성이 변경되었을때 뿐이다.<br>

</li>
</ul>
<h3 id="🚦-span-stylebackground-color-f0fcfc특정-데이터-조회--filter와-getspan">🚦 <span style='background-color: #f0fcfc'>특정 데이터 조회 : <code>filter</code>와 <code>get</code></span></h3>
<ul>
<li><p>이번에는 filter를 사용하여 id 값이 1인 Quesiton 데이터를 조회.</p>
<blockquote>
<ul>
<li>filter는 조건에 해당되는 데이터를 모두 리턴해 주기 때문에 다건을 의미하는 QuerySet이 리턴됨.</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt;&gt;&gt; Question.objects.filter(id=1)
&lt;QuerySet [&lt;Question: pybo가 무엇인가요?&gt;]&gt;</code></pre>
</li>
<li><p>id는 유일한 값이므로 filter 대신 get을 이용하여 조회할 수도 있다.</p>
<blockquote>
<ul>
<li>get으로 조회할 경우 QuerySet이 아닌 Question 모델 객체가 리턴됨.</li>
<li>filter는 다건을 리턴하지만 get은 한 건만 리턴하기 때문.</li>
<li>하지만 get으로 조회시 조건에 맞는 데이터가 없으면 오류가 발생<ul>
<li>get은 반드시 1건의 데이터를 조회할 때 사용함.</li>
<li>보통 get은 id와 같은 유일한 값으로 조회할 경우에만 사용.</li>
</ul>
</li>
</ul>
</blockquote>
<pre><code class="language-python">Code&gt;
&gt;&gt;&gt; Question.objects.get(id=1)
&lt;Question: pybo가 무엇인가요?&gt;</code></pre>
</li>
</ul>
<h3 id="🚦-span-stylebackground-color-f0fcfc특정-문자열을-포함한-조회--__containsspan">🚦 <span style='background-color: #f0fcfc'>특정 문자열을 포함한 조회 : <code>__contains</code></span></h3>
<ul>
<li>이번에는 subject에 &quot;장고&quot;라는 문자열이 포함된 데이터만 조회<blockquote>
<ul>
<li>subject__contains=&#39;장고&#39;의 의미는 &quot;subject에 &#39;장고&#39;라는 문자열이 포함되어 있는가?&quot; 라고 해석할 수 있다. subject__contains 에서 언더바(_)가 1개가 아닌 2개임에 주의하자.</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt;&gt;&gt; Question.objects.filter(subject__contains=&#39;장고&#39;)
&lt;QuerySet [&lt;Question: 장고 모델 질문입니다.&gt;]&gt;</code></pre>
<br>

</li>
</ul>
<p>📌 데이터를 조회하는 filter의 사용법은 위에서 알아본 것 외에도 아주 많음.</p>
<blockquote>
<ul>
<li>filter에 대한 자세한 사용법은 장고 공식 문서를 참조할 것.</li>
<li>장고 공식 문서는 장고 개발시 필수적으로 참조해야 하는 문서임.<ul>
<li><a href="https://docs.djangoproject.com/en/4.0/topics/db/queries/">https://docs.djangoproject.com/en/4.0/topics/db/queries/</a></li>
</ul>
</li>
</ul>
</blockquote>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcquestion-수정span">🚦 <span style='background-color: #f0fcfc'>Question 수정</span></h3>
<ul>
<li><p>id 값이 2인 Question 데이터를 조회</p>
<pre><code class="language-python">&gt;&gt;&gt; q = Question.objects.get(id=2)
&gt;&gt;&gt; q
&lt;Question: 장고 모델 질문입니다.&gt;</code></pre>
</li>
<li><p>subject 속성을 다음과 같이 수정</p>
<pre><code class="language-python">&gt;&gt;&gt; q.subject = &#39;Django Model Question&#39;
&gt;&gt;&gt;</code></pre>
</li>
<li><p>여기까지만 해서는 수정이 되지 않음.</p>
<blockquote>
<ul>
<li>다음처럼 save를 수행해 주어야 변경된 데이터가 반영된다는 점을 꼭 기억할 것!!</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt;&gt;&gt; q.save()
&gt;&gt;&gt; q
&lt;Question: Django Model Question&gt;</code></pre>
</li>
</ul>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcquestion-삭제span">🚦 <span style='background-color: #f0fcfc'>Question 삭제</span></h3>
<ul>
<li>id 값이 1인 Question 데이터를 삭제<pre><code class="language-python">&gt;&gt;&gt; q = Question.objects.get(id=1)
&gt;&gt;&gt; q.delete()
(1, {&#39;pybo.Question&#39;: 1})</code></pre>
</li>
<li>실제로 삭제되었는지 다음처럼 Question.objects.all() 로 확인<pre><code class="language-python">&gt;&gt;&gt; Question.objects.all()
&lt;QuerySet [&lt;Question: Django Model Question&gt;]&gt;</code></pre>
</li>
</ul>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcanswer-작성span">🚦 <span style='background-color: #f0fcfc'>Answer 작성</span></h3>
<ul>
<li>답변 데이터를 생성<pre><code class="language-python">&gt;&gt;&gt; q = Question.objects.get(id=2)
&gt;&gt;&gt; q
&lt;Question: Django Model Question&gt;
&gt;&gt;&gt; from django.utils import timezone
&gt;&gt;&gt; a = Answer(question=q, content=&#39;네 자동으로 생성됩니다.&#39;, create_date=timezone.now())
&gt;&gt;&gt; a.save()</code></pre>
</li>
<li>답변 데이터를 만들기 위해서는 질문이 필요하므로 id가 2인 질문을 먼저 조회한 후 question 속성에 대입.</li>
<li>Answer 모델도 Question 모델과 마찬가지로 유일한 값을 의미하는 id가 자동으로 생성됨.<pre><code class="language-python">&gt;&gt;&gt; a.id
1</code></pre>
</li>
</ul>
<br>

<h3 id="🚦-span-stylebackground-color-f0fcfcanswer-조회span">🚦 <span style='background-color: #f0fcfc'>Answer 조회</span></h3>
<ul>
<li><p>답변을 조회하는 방법은 질문과 마찬가지로 Answer의 id 값을 사용</p>
<pre><code class="language-python">&gt;&gt;&gt; a = Answer.objects.get(id=1)
&gt;&gt;&gt; a
&lt;Answer: Answer object (1)&gt;</code></pre>
</li>
<li><p>Answer 객체인 a를 사용하면 답변에 연결된 질문도 조회 가능</p>
<pre><code class="language-python">&gt;&gt;&gt; a.question
&lt;Question: Django Model Question&gt;</code></pre>
</li>
</ul>
<p>❗❗ Answer 모델 객체인 a를 통해서 질문을 찾는것은 Answer 모델에 question 속성이 연결되어 있기 때문에 매우 쉬움.</p>
<ul>
<li>그렇다면 질문을 이용하여 답변을 찾는 것은 가능?<blockquote>
<p>Yes</p>
<ul>
<li>q.answer_set을 사용하면 질문에 연결된 답변을 가져올 수 있음.</li>
<li>Question 모델에는 answer_set 이라는 속성이 없지만 Answer 모델에 Question 모델이 ForignKey로 연결되어 있기 때문에 q.answer_set 과 같은 역방향 접근이 가능</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt;&gt;&gt; q.answer_set.all()
&lt;QuerySet [&lt;Answer: Answer object (1)&gt;]&gt;</code></pre>
</li>
</ul>
<h4 id="📌-span-stylecolor-eb5757연결모델명_setspan-방법">📌 <span style="color: #EB5757"><strong><code>연결모델명_set</code></strong></span> 방법</h4>
<ul>
<li><span style="color: #EB5757"><strong><code>연결모델명_set(예:answer_set)</code></strong></span>은 상식적으로 생각하면 더 쉬움.<blockquote>
<ul>
<li>질문 하나에는 여러개의 답변이 가능하므로 <span style="color: #EB5757"><strong><code>q.answer_set</code></strong></span>이 가능하지만 답변 하나에는 여러개의 질문이 있을 수 없으므로 <span style="color: #EB5757"><strong><code>a.question_set</code></strong></span>은 불가능.</li>
<li>답변 하나에는 질문 하나만 가능하기 때문에 <span style="color: #EB5757"><strong><code>a.question</code></strong></span>만 가능.</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h1 id="🌞-span-stylebackground-color-f4eeee2-03-django-adminspan">🌞 <span style='background-color: #f4eeee'><code>2-03. Django Admin</code></span></h1>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8모델-관리span">🌈 <span style='background-color: #ffffe8'><code>모델 관리</code></span></h2>
<ul>
<li>이미 생성한 Question 모델을 Admin에 등록하는 방법</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/admin.py</code></strong></span></p>
<pre><code class="language-python">from django.contrib import admin
from .models import Question         # 추가한 코드

admin.site.register(Question)        # 추가한 코드</code></pre>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/7e31078f-5735-401a-a993-50d9c786a56d/image.png" alt=""></p>
<br>

<h2 id="🌈-span-stylebackground-color-ffffe8모델-검색span">🌈 <span style='background-color: #ffffe8'><code>모델 검색</code></span></h2>
<ul>
<li>관리자 화면에서 제목(subject)으로 질문 데이터를 검색하기 위해 Search 기능 추가<blockquote>
<ul>
<li>다음처럼 pybo/admin.py 파일을 수정<ul>
<li>Question 모델에 세부 기능을 추가할 수 있는 QuestionAdmin 클래스를 생성</li>
<li>제목 검색을 위해 search_fields 속성에 &#39;subject&#39;를 추가</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
<p>📄 <span style="color: #EB5757"><strong><code>/pybo/admin.py</code></strong></span></p>
<pre><code class="language-python">from django.contrib import admin
from .models import Question


class QuestionAdmin(admin.ModelAdmin):          # 추가한 코드 
    search_fields = [&#39;subject&#39;]                 # 추가한 코드 


admin.site.register(Question, QuestionAdmin)    # 추가한 코드 </code></pre>
<ul>
<li>이렇게 수정하면 다음처럼 검색기능이 추가된 화면 확인 가능.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/0b364d8c-49e9-43c3-b14d-3d13467235b2/image.png" alt=""></p>
<ul>
<li>아래 두 가지 방식은 동일<blockquote>
<ul>
<li>2번째 방식은 김형종 강사님 수업 시간에 배운 방식</li>
</ul>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/8316f21f-0b23-4b39-9385-611a39f68d07/image.png" alt=""></p>
<p>❗❗ 장고 관리자에는 이 외에도 무수히 많은 기능들이 있음. </p>
<blockquote>
<ul>
<li>자세한 내용은 다음 URL을 참고.</li>
<li><a href="https://docs.djangoproject.com/en/4.0/ref/contrib/admin/">https://docs.djangoproject.com/en/4.0/ref/contrib/admin/</a></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[221006_Mermaid를 이용한 Markdown 작성]]></title>
            <link>https://velog.io/@brown_eyed87/221006Mermaid%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Markdown-%EC%9E%91%EC%84%B1</link>
            <guid>https://velog.io/@brown_eyed87/221006Mermaid%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Markdown-%EC%9E%91%EC%84%B1</guid>
            <pubDate>Thu, 06 Oct 2022 02:34:55 GMT</pubDate>
            <description><![CDATA[<h1 id="🤩-markdown-작성-시-mermaid-사용해보기">🤩 <code>markdown 작성 시 mermaid 사용해보기</code></h1>
<p><strong>VS Code를 이용해보자!!!!!!!</strong></p>
<h2 id="🥡-확장-플러그인-설치">🥡 <code>확장 플러그인 설치</code></h2>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/9c72793c-8baa-4f46-8cc8-eef4e088c543/image.png" alt=""></p>
<h2 id="🥡-마크다운-파일-생성">🥡 <code>마크다운 파일 생성</code></h2>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/0f200d10-7055-4a75-8f89-168da6845e20/image.png" alt=""></p>
<h2 id="🥡-mermaid-를-이용한-diagram-작성">🥡 <code>mermaid</code> 를 이용한 <code>diagram</code> 작성</h2>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/fe3d3a41-0bc7-41d5-8dbc-dfaf79d798c4/image.png" alt=""></p>
<ul>
<li>작성 방법은 아래와 같다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/42854ec2-9267-437b-8e21-0dd604058d23/image.png" alt=""></li>
</ul>
<blockquote>
<p><strong>작성 예시 참고 : <a href="https://mermaid-js.github.io/mermaid/#/examples">Mermaid github</a></strong></p>
</blockquote>
<h2 id="🥡-mermaid-사용법">🥡 <code>mermaid</code> 사용법</h2>
<p> <code>다이어그램 종류</code></p>
<blockquote>
<p>🥗 <strong>순서도(Flowchart)</strong> : <code>flowchart</code></p>
<ul>
<li>모든 순서도는 노드, 기하학적 모양 및 가장자리, 화살표 또는 선으로 구성됨.</li>
<li>mermaid 코드는 이러한 node와 edge가 만들어지고 상호 작용하는 방식을 정의함.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/9543f107-8bcb-48b0-9c34-3685c3da7fd9/image.png" alt=""></li>
</ul>
<p>🥗 <strong>시퀀스 다이어그램(Sequence Diagram)</strong> : <code>sequenceDiagram</code></p>
<ul>
<li>공정이 서로 어떻게 그리고 어떤 순서로 작동하는지 보여주는 상호 작용 다이어그램
<img src="https://velog.velcdn.com/images/brown_eyed87/post/49226ae7-ae60-4ec4-ac97-e3e60a220ac8/image.png" alt=""></li>
</ul>
<p>🥗 <strong>클래스 다이어그램(Class Diagram)</strong> : <code>classDiagram</code></p>
<ul>
<li>소프트웨어 공학에서 통합 모델링 언어(UML)의 클래스 다이어그램(class diagram)은 시스템의 클래스, 속성, 작업(또는 방법) 및 객체 간의 관계를 보여줌으로써 시스템의 구조를 설명하는 정적 구조 다이어그램의 한 유형
<img src="https://velog.velcdn.com/images/brown_eyed87/post/67ed493c-3d99-4726-8c3f-c1051845dcc2/image.png" alt=""></li>
</ul>
<p>🥗 <strong>상태 다이어그램(State Diagram)</strong> : <code>stateDiagram</code></p>
<ul>
<li>컴퓨터 과학 및 관련 분야에서 시스템의 동작을 설명하기 위해 사용되는 다이어그램의 한 유형.</li>
<li>상태 다이어그램은 기술된 시스템이 유한한 수의 상태로 구성되어 있어야 함.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/70f0e7cd-5cb7-4cf7-a174-567a96eba45a/image.png" alt=""></li>
</ul>
<p>🥗 <strong>엔티티 관계도(Entity relationship diagram)</strong> : <code>erDiagram</code></p>
<ul>
<li>특정 지식의 영역에서 상호 연관된 관심사를 설명.</li>
<li>기본 ER 모형은 (관심 있는 것들을 분류하는) 엔티티 유형으로 구성됨.</li>
<li>엔티티 간에 존재할 수 있는 관계(그 엔티티 유형의 인스턴스)를 명시함.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/5aead096-64d5-443b-9973-bbf710329bf2/image.png" alt=""></li>
</ul>
<p>🥗 <strong>사용자 이동 경로 다이어그램(User Journey Diagram)</strong> : <code>journey</code></p>
<ul>
<li>시스템, 애플리케이션 또는 웹 사이트 내에서 다양한 사용자가 특정 작업을 완료하기 위해 어떤 단계를 밟는지 자세히 설명.</li>
<li>이 기법은 현재(있는 그대로) 사용자 워크플로우를 보여주고, 향후 워크플로우에 대한 개선 영역을 나타냄.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/99b24316-af7d-4564-9291-e952e9a53536/image.png" alt=""></li>
</ul>
<p>🥗 <strong>간트 차트(Gantt)</strong> : <code>gantt</code></p>
<ul>
<li>막대 차트의 일종으로, 프로젝트 일정과 하나의 프로젝트가 완료되는 데 걸리는 시간을 보여줌.</li>
<li>간트 차트는 프로젝트의 터미널 요소와 요약 요소의 시작 날짜와 종료 날짜 사이의 일 수를 보여줌.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/458b98e0-9072-4e7c-873c-f3705f920800/image.png" alt=""></li>
</ul>
<p>🥗 <strong>원형 차트(Pie Chart)</strong> : <code>pie</code></p>
<ul>
<li>숫자 비율을 나타내기 위해 조각으로 나누어 표현.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/bec6d61c-d073-4ce4-8305-d9ec901259f7/image.png" alt=""></li>
</ul>
<p>🥗 <strong>요구사항 다이어그램(Requirement Diagram)</strong> : <code>requirementDiagram</code></p>
<ul>
<li>요구사항과 요구사항의 상호 연결 및 기타 문서화된 요소에 대한 시각화를 제공.</li>
<li>모델링 사양은 SysML v1.6에 정의된 사양을 따름.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/82682d16-7f56-4870-ad19-e65b40cd8e80/image.png" alt=""></li>
</ul>
<p>🥗 <strong>깃 다이어그램(Gitgraph Diagram)</strong> : <code>gitDiagram</code></p>
<ul>
<li>다양한 분기에 대한 git 커밋 및 git 액션(명령)을 그림으로 표현한 것
<img src="https://velog.velcdn.com/images/brown_eyed87/post/32dbda44-9aa7-4bf4-81c4-cf40875e8403/image.png" alt=""></li>
</ul>
<p>🥗 <strong>C4 다이어그램 (C4 Diagram) :</strong> <code>C4Content</code></p>
<ul>
<li>공식문서 기준으로 그릴 수는 있으나 아직 실험 중인 다이어그램
<img src="https://velog.velcdn.com/images/brown_eyed87/post/e28c2029-dccc-4c1f-992a-a9a49940767e/image.png" alt=""></li>
</ul>
</blockquote>
<br>

<hr>
<br>

<h2 id="🥰-ref">🥰 <code>REF</code></h2>
<p>🍷 <code>Mermaid</code>
<a href="https://sabarada.tistory.com/209">https://sabarada.tistory.com/209</a>
<a href="https://richwind.co.kr/147">https://richwind.co.kr/147</a>
<a href="https://velog.io/@skyfishbae/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4-Markdown-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%B0%8F-Velog-%EC%9A%A9-%ED%8E%B8%EC%A7%91%EA%B8%B0-Editor-%EB%A6%AC%EB%B7%B0-2">https://velog.io/@skyfishbae/마크다운-Markdown-사용법-및-Velog-용-편집기-Editor-리뷰-2</a></p>
<blockquote>
<p>🍗 <code>PlantUML</code> : 추후 시간될 때 공부해보기
<a href="https://choboit.tistory.com/96?category=1066529">https://choboit.tistory.com/96?category=1066529</a>
<a href="https://ttottoro.tistory.com/735">https://ttottoro.tistory.com/735</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[221005_DJango ORM]]></title>
            <link>https://velog.io/@brown_eyed87/221005DJango-ORM</link>
            <guid>https://velog.io/@brown_eyed87/221005DJango-ORM</guid>
            <pubDate>Wed, 05 Oct 2022 12:57:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 게시글은 <span style="color: #FA8072"><strong><code>김형종 강사님</code></strong></span>의 <span style="color: #FA8072"><strong><code>Django 웹개발</code></strong></span> <strong>강의를 듣고 작성</strong>하였습니다.</p>
</blockquote>
<h1 id="🍻-span-stylebackground-color-ffdce0django-ormspan">🍻 <span style='background-color: #ffdce0'><code>DJango ORM</code></span></h1>
<blockquote>
<p><a href="https://docs.djangoproject.com/en/3.2/ref/models/querysets/#contains">Django QuerySet API reference</a> 사이트 참고</p>
</blockquote>
<br>

<h2 id="🍸-span-stylebackground-color-fff5b1orm과-querysetsspan">🍸 <span style='background-color: #fff5b1'><code>ORM과 QuerySets</code></span></h2>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0fformspan">🍹 <span style='background-color: #f5f0ff'><code>ORM</code></span></h3>
<ul>
<li><code>ORM</code>이란 <code>Object-Relational Mapping</code>의 약자로 객체(<code>Object)</code>와 관계형 데이터베이스(<code>Relational Database</code>)의 데이터를 매핑(<code>Mapping</code>)해주는 것을 의미.<blockquote>
<ul>
<li>객체 간의 관계를 바탕으로 <code>SQL</code>을 자동 생성하여 <code>sql쿼리문</code> 없이도 데이터베이스의 데이터를 다룰 수 있게 해줌.</li>
<li>데이터베이스의 테이블을 객체지향 프로그래밍에서 흔히 사용하는 객체(<code>Class</code>)처럼 사용할 수 있도록 해주는 기술.</li>
<li>기존 쿼리문을 작성하여 데이터베이스를 조작하는 것을 넘어서서 더 효율적이고 가독성 및 유지 보수에 적합한 코드를 만들기 위해 나온 기술.</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ffquerysetsspan">🍹 <span style='background-color: #f5f0ff'><code>QuerySets</code></span></h3>
<ul>
<li><code>QuerySet</code> 이란 데이터베이스에서 전달 받은 객체의 목록이다. <blockquote>
<ul>
<li><code>Django ORM</code>에서 발생한 자료형</li>
<li>리스트와 구조는 같지만 파이썬 기본 자료구조가 아니기 때문에 파이썬에서 읽고 쓰기 위해 자료형 변환(<code>Casting</code>)</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h2 id="🍸-span-stylebackground-color-fff5b1orm-실습span">🍸 <span style='background-color: #fff5b1'><code>ORM 실습</code></span></h2>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff실습-사전-준비span">🍹 <span style='background-color: #f5f0ff'><code>실습 사전 준비</code></span></h3>
<ol>
<li>현재 프로젝트 폴더 내에 <code>orm</code> 앱 설치 후 기본 셋팅</li>
<li><code>model</code> 설정 및 <code>db</code> 테이블 추가</li>
</ol>
<p><code>models.py</code> 코드</p>
<pre><code class="language-python">
from django.db import models


class Member(models.Model):
    name = models.CharField(verbose_name=&quot;이름&quot;, max_length=10)
    birth = models.DateField(verbose_name=&quot;생년월일&quot;, null=True)
    email = models.EmailField(verbose_name=&quot;이메일&quot;)
    phone = models.CharField(verbose_name=&quot;전화번호&quot;, max_length=11)

    created_at = models.DateTimeField(verbose_name=&quot;생성일&quot;, auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name=&quot;갱신일&quot;, auto_now=True)

    def __str__(self):
        return f&quot;{self.name} ({self.email})&quot;

    class Meta:
        verbose_name = &quot;회원&quot;
        verbose_name_plural = &quot;회원 목록&quot;


class Product(models.Model):
    NEW = &quot;new&quot;
    OLD = &quot;old&quot;
    STATUS_TYPE = (
        (NEW, &quot;신상품&quot;),
        (OLD, &quot;재고상품&quot;),
    )

    code = models.CharField(verbose_name=&quot;상품코드&quot;, max_length=8)
    status = models.CharField(verbose_name=&quot;상품구분&quot;, max_length=3, choices=STATUS_TYPE)
    name = models.CharField(verbose_name=&quot;상품명&quot;, max_length=30)
    price = models.PositiveSmallIntegerField(verbose_name=&quot;가격(원)&quot;, default=0)

    created_at = models.DateTimeField(verbose_name=&quot;생성일&quot;, auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name=&quot;갱신일&quot;, auto_now=True)

    def __str__(self):
        return f&quot;{self.name} ({self.code})&quot;

    class Meta:
        verbose_name = &quot;상품&quot;
        verbose_name_plural = &quot;상품 목록&quot;


class PaymentOrderRecord(models.Model):
    NOT_YET = 0
    PAID = 1
    REFUND = 2
    STATUS_TYPE = (
        (NOT_YET, &quot;결제전&quot;),
        (PAID, &quot;결제완료&quot;),
        (REFUND, &quot;환불완료&quot;),
    )

    code = models.CharField(verbose_name=&quot;결제코드&quot;, max_length=15)
    status = models.IntegerField(verbose_name=&quot;결제구분&quot;, default=0, choices=STATUS_TYPE)
    member = models.ForeignKey(
        Member,
        verbose_name=&quot;회원&quot;,
        on_delete=models.CASCADE,
        null=True,
        related_name=&quot;member_paymentorderrecord&quot;,
    )
    product = models.ManyToManyField(
        Product, verbose_name=&quot;상품&quot;, related_name=&quot;product_paymentorderrecord&quot;
    )

    created_at = models.DateTimeField(verbose_name=&quot;생성일&quot;, auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name=&quot;갱신일&quot;, auto_now=True)

    def __str__(self):
        return f&quot;{self.code} ({self.product.count()}개)&quot;

    class Meta:
        verbose_name = &quot;결제 주문서&quot;
        verbose_name_plural = &quot;결제 주문서 목록&quot;</code></pre>
<ol start="3">
<li><code>orm</code> 실습을 위한 <code>db</code> 생성</li>
</ol>
<ul>
<li>실행 파일 준비<blockquote>
<ul>
<li>아래와 같이 <code>management</code>폴더 구조 만들기<ul>
<li><code>management</code> 폴더와 <code>commands</code> 폴더 내에 아무 내용도 들어 있지 않은 <code>__init__.py</code> 파일 필수 생성</li>
<li><code>commands</code> 폴더 내에 실행 파일 생성<ul>
<li><code>push_orm_data.py</code> : <code>test db</code> 생성 시 사용</li>
<li><code>control_orm_data.py</code> : <code>orm</code> 실습 시 사용</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/b4b02fd3-5a51-40ac-8953-c53e4e0e2557/image.png" alt="">
<code>push_orm_data.py</code> 코드</p>
<ul>
<li>아래 코드를 해당 파일에 붙여넣고 저장</li>
<li><code>terminal</code> 창에서 명령어 실행<ul>
<li><code>poetry run python manage.py push_orm_data</code></li>
</ul>
</li>
</ul>
<pre><code class="language-python">import datetime

from django.core.management.base import BaseCommand

from orm.models import Member, PaymentOrderRecord, Product

# TODO: 커맨드 활용
# https://docs.djangoproject.com/en/3.2/howto/custom-management-commands/#testing
    class Command(BaseCommand):
        help = &quot;PUSH TEST DB&quot;

        def handle(self, *args, **options):
            member_list = [
                (&quot;이병헌&quot;, &quot;lbh@test.com&quot;, &quot;01017472748&quot;, datetime.date(1970, 7, 12)),
                (&quot;박해수&quot;, &quot;phs@test.com&quot;, &quot;01039584378&quot;, datetime.date(1981, 11, 21)),
                (&quot;황정민&quot;, &quot;hjm@test.com&quot;, &quot;01012344624&quot;, datetime.date(1970, 9, 1)),
                (&quot;강동원&quot;, &quot;kdw@dummy.com&quot;, &quot;01048572738&quot;, datetime.date(1981, 1, 18)),
                (&quot;장동건&quot;, &quot;jdk@dummy.com&quot;, &quot;01020302949&quot;, datetime.date(1972, 3, 7)),
                (&quot;송강호&quot;, &quot;skh@movie.com&quot;, &quot;01020584737&quot;, datetime.date(1967, 1, 17)),
            ]

            product_list = [
                (&quot;po3nuw01&quot;, &quot;new&quot;, &quot;휴지&quot;, 1500),
                (&quot;92oif0rj&quot;, &quot;new&quot;, &quot;화장품&quot;, 30000),
                (&quot;9oiuh67y&quot;, &quot;old&quot;, &quot;네스프레소&quot;, 300000),
                (&quot;8ai2k8ww&quot;, &quot;new&quot;, &quot;수저&quot;, 1000),
                (&quot;9oi8ujhy&quot;, &quot;new&quot;, &quot;젓가락&quot;, 500),
                (&quot;8uhyjtd6&quot;, &quot;old&quot;, &quot;압력밥솥&quot;, 250000),
                (&quot;9if56yhg&quot;, &quot;new&quot;, &quot;머그컵&quot;, 5000),
                (&quot;23tyfuen&quot;, &quot;old&quot;, &quot;스마트폰&quot;, 1000000),
            ]

            payment_list = [
                (&quot;20220101-9oi8u7&quot;, 0, &quot;이병헌&quot;, 2),
                (&quot;20220101-8ujhbg&quot;, 0, &quot;이병헌&quot;, 3),
                (&quot;20220101-5tgvfr&quot;, 1, &quot;박해수&quot;, 4),
                (&quot;20220101-4rfcdE&quot;, 2, &quot;박해수&quot;, 1),
                (&quot;20220101-3edcxs&quot;, 1, &quot;송강호&quot;, 2),
                (&quot;20220101-2wsxdd&quot;, 0, &quot;강동원&quot;, 1),
                (&quot;20220101-9iuj78&quot;, 1, &quot;강동원&quot;, 3),
                (&quot;20220101-7ujhy6&quot;, 2, &quot;장동건&quot;, 3),
            ]

            for item in member_list:
                member, is_created = Member.objects.get_or_create(
                    name=item[0], email=item[1], phone=item[2]
                )
                member.birth = item[3]
                member.save()

            for item in product_list:
                Product.objects.get_or_create(code=item[0], status=item[1], name=item[2], price=item[3])

            for item in product_list:
                Product.objects.get_or_create(code=item[0], status=item[1], name=item[2], price=item[3])

            for item in payment_list:
                payment_order_record, is_created = PaymentOrderRecord.objects.get_or_create(
                    code=item[0]
                )

                payment_order_record.status = item[1]
                payment_order_record.member = Member.objects.get(name=item[2])

                for product in Product.objects.order_by(&quot;?&quot;)[: item[3]]:
                    payment_order_record.product.add(product)

                payment_order_record.save()
</code></pre>
<p><code>control_orm_data.py</code> 기초 코드 작성</p>
<pre><code class="language-python">import datetime

from django.core.management.base import BaseCommand

from orm.models import Member, PaymentOrderRecord, Product

# TODO: 커맨드 활용
# https://docs.djangoproject.com/en/3.2/howto/custom-management-commands/#testing
class Command(BaseCommand):
    help = &quot;CONTROL TEST DB&quot;

    def handle(self, *args, **options):</code></pre>
<p>이제 이 다음부터 아래의 실습을 이어가게 될 것이며,
코드 작성 후, <code>terminal</code> 창에서 파일 실행 명령을 하면 됨.</p>
<ul>
<li>실행 명령어 : <code>poetry run python manage.py control_orm_data</code></li>
<li>아래의 코드들은 <code>control_orm_data.py</code> 파일에서 <code>handle</code> 함수 부분의 코드에 해당.</li>
</ul>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff전체-데이터-조회--allspan">🍹 <span style='background-color: #f5f0ff'><code>전체 데이터 조회 : all</code></span></h3>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        all_member = Member.objects.all()
        all_product = Product.objects.all()
        all_payment = PaymentOrderRecord.objects.all()

        # Member 전체 쿼리 조회
        print(all_member)
        # Product 전체 쿼리 조회
        print(all_product)
        # Payment 전체 쿼리 조회
        print(all_payment)</code></pre>
</blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    # Member
    &lt;QuerySet [&lt;Member: 이병헌 (lbh@test.com)&gt;, &lt;Member: 박해수 (phs@test.com)&gt;, &lt;Member: 황정민 (hjm@test.com)&gt;, &lt;Member: 강동원 (kdw@dummy.com)&gt;, &lt;Member: 장동건 (jdk@dummy.com)&gt;, &lt;Member: 송강호 (skh@movie.com)&gt;]&gt;
    # Product
    &lt;QuerySet [&lt;Product: 휴지 (po3nuw01)&gt;, &lt;Product: 화장품 (92oif0rj)&gt;, &lt;Product: 네스프레소 (9oiuh67y)&gt;, &lt;Product: 수저 (8ai2k8ww)&gt;, &lt;Product: 젓가락 (9oi8ujhy)&gt;, &lt;Product: 압력밥솥 (8uhyjtd6)&gt;, &lt;Product: 머그컵 (9if56yhg)&gt;, &lt;Product: 스마트폰 (23tyfuen)&gt;]&gt;   
    # Payment
    &lt;QuerySet [&lt;PaymentOrderRecord: 20220101-9oi8u7 (2개)&gt;, &lt;PaymentOrderRecord: 20220101-8ujhbg (3개)&gt;, &lt;PaymentOrderRecord: 20220101-5tgvfr (4개)&gt;, &lt;PaymentOrderRecord: 20220101-4rfcdE (1개)&gt;, &lt;PaymentOrderRecord: 20220101-3edcxs (2개)&gt;, &lt;PaymentOrderRecord: 20220101-2wsxdd (1개)&gt;, &lt;PaymentOrderRecord: 20220101-9iuj78 (3개)&gt;, &lt;PaymentOrderRecord: 20220101-7ujhy6 (3개)&gt;]&gt;
</code></pre>
</blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-검색-filter--데이터-포함-contains-외span">🍹 <span style='background-color: #f5f0ff'><code>데이터 검색 (filter) : 데이터 포함 &#39;contains&#39; 외</code></span></h3>
<br>

<h4 id="🍾-span-stylebackground-color-dcffe4case-1-전체-일치span">🍾 <span style='background-color: #dcffe4'><code>Case 1. 전체 일치</code></span></h4>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        # Filter : 
        filter_member = Member.objects.filter(name=&quot;강동원&quot;)
        print(filter_member)
</code></pre>
</blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [&lt;Member: 강동원 (kdw@dummy.com)&gt;]&gt;</code></pre>
</blockquote>
<br>

<h4 id="🍾-span-stylebackground-color-dcffe4case-2-일부-포함--__containsspan">🍾 <span style='background-color: #dcffe4'><code>Case 2. 일부 포함 : &#39;__contains=&#39;</code></span></h4>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        filter_member = Member.objects.filter(name__contains=&quot;강&quot;)
        print(filter_member)
</code></pre>
</blockquote>
<pre><code>    # 대소문자 구분
    filter_payment = PaymentOrderRecord.objects.filter(code__contains=&quot;E&quot;)
    print(filter_payment)
    filter_payment = PaymentOrderRecord.objects.filter(code__contains=&quot;e&quot;)
    print(filter_payment)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [&lt;Member: 강동원 (kdw@dummy.com)&gt;, &lt;Member: 송강호 (skh@movie.com)&gt;]&gt;
</code></pre>
</blockquote>
<pre><code># 대문자와 소문자를 다르게 결과가 같다?!!
&lt;QuerySet [&lt;PaymentOrderRecord: 20220101-4rfcdE (1개)&gt;, &lt;PaymentOrderRecord: 20220101-3edcxs (2개)&gt;]&gt;
&lt;QuerySet [&lt;PaymentOrderRecord: 20220101-4rfcdE (1개)&gt;, &lt;PaymentOrderRecord: 20220101-3edcxs (2개)&gt;]&gt;</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p>🍗 <code>contains</code> vs <code>icontains</code> 차이</p>
<ul>
<li><code>contains</code> : <code>대문자</code>와 <code>소문자</code>를 <span style="color: red">구분</span></li>
<li><code>icontains</code> : <code>대문자</code>와 <code>소문자</code>를 동일한 데이터로 취급</li>
</ul>
<blockquote>
<ul>
<li><a href="https://docs.djangoproject.com/en/3.2/ref/models/querysets/#contains">단, <code>sqlite</code>에서는 <code>contains</code>를 사용하더라도 대소문자 구분 없이 동일한 값을 조회.</a></li>
<li><code>Django</code>를 이용한 <code>Search</code> 기능 구현 시, <code>icontains</code>를 많이 사용</li>
<li>이것보다 한 단게 더 진화한게 <code>ES (Elastic Search)</code></li>
</ul>
</blockquote>
<p>🍗 <code>ES (Elastic Search)</code> : 추후 학습 시 내용 보충 예정</p>
<ul>
<li>형태소 단위로 나눠서 <code>db</code>화 시켜서 <code>search</code>를 진행</li>
</ul>
<br>

<h4 id="🍾-span-stylebackground-color-dcffe4-case-3-시작-부분에-포함--startswithspan">🍾 <span style='background-color: #dcffe4'> Case 3. 시작 부분에 포함 : &#39;startswith`</span></h4>
<p>추후 <code>__startswith=</code>, <code>__endswith=</code> 내용 보충 예정
자매품 <code>__istartswith=</code>, <code>__iendswith=</code>도 있음.</p>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-검색-filter--크기-비교-__lt-외span">🍹 <span style='background-color: #f5f0ff'><code>데이터 검색 (filter) : 크기 비교 &#39;__lt=&#39; 외</code></span></h3>
<ul>
<li><code>__gt=</code> : <code>greater than</code> </li>
<li><code>__gte=</code> : <code>greater than or equal to</code>  </li>
<li><code>__lt=</code> : <code>less than</code>  </li>
<li><code>__lte=</code> : <code>less than or equal to</code></li>
</ul>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        filter_product = Product.objects.filter(price__lte=1000)
</code></pre>
</blockquote>
<pre><code>    print(filter_product)</code></pre><blockquote>
</blockquote>
<pre><code>    for item in filter_product:
        print(item.price)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [&lt;Product: 수저 (8ai2k8ww)&gt;, &lt;Product: 젓가락 (9oi8ujhy)&gt;]&gt;
</code></pre>
</blockquote>
<pre><code>1000
500</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-검색-filter--choicefield-관련-조회span">🍹 <span style='background-color: #f5f0ff'><code>데이터 검색 (filter) : ChoiceField 관련 조회</code></span></h3>
<ul>
<li><code>ChoiceField</code> 데이터 관련 <code>filter</code>의 경우에는 <code>status</code>값에 <code>모델명.상태값</code>으로 지정해야 함.</li>
</ul>
<p><code>models.py</code> 에서 <code>Product</code> 모델 클래스 내용</p>
<blockquote>
<pre><code class="language-python">    class Product(models.Model):
        NEW = &quot;new&quot;
        OLD = &quot;old&quot;
        STATUS_TYPE = (
            (NEW, &quot;신상품&quot;),
            (OLD, &quot;재고상품&quot;),
        )
</code></pre>
</blockquote>
<pre><code>    code = models.CharField(verbose_name=&quot;상품코드&quot;, max_length=8)
    status = models.CharField(verbose_name=&quot;상품구분&quot;, max_length=3, choices=STATUS_TYPE)
    name = models.CharField(verbose_name=&quot;상품명&quot;, max_length=30)
    price = models.PositiveSmallIntegerField(verbose_name=&quot;가격(원)&quot;, default=0)</code></pre><blockquote>
</blockquote>
<pre><code>    created_at = models.DateTimeField(verbose_name=&quot;생성일&quot;, auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name=&quot;갱신일&quot;, auto_now=True)</code></pre><blockquote>
</blockquote>
<pre><code>    def __str__(self):
        return f&quot;{self.name} ({self.code})&quot;</code></pre><blockquote>
</blockquote>
<pre><code>    class Meta:
        verbose_name = &quot;상품&quot;
        verbose_name_plural = &quot;상품 목록&quot;</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        filter_product = Product.objects.filter(price__lte=1000, status=Product.NEW)
        print(filter_product)
</code></pre>
</blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [&lt;Product: 수저 (8ai2k8ww)&gt;]&gt;</code></pre>
</blockquote>
<p>🍓 <span style="color: red"><code>filter</code>는 <code>2</code>개 이상 적용 가능</span></p>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-개수-세기-countspan">🍹 <span style='background-color: #f5f0ff'><code>데이터 개수 세기 (Count)</code></span></h3>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
       all_member_count = len(all_member)
        print(all_member_count)
</code></pre>
</blockquote>
<pre><code>    all_member_count = all_member.count()
    print(all_member_count)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    6
    6
</code></pre>
</blockquote>
<p>🍓 <span style="color: red"> 단, <code>len</code>보다 <code>count</code>를 사용할 것!!</span></p>
<ul>
<li><code>len</code>도 결국 <code>QuerySets</code> 내부에 <code>메서드</code>로 구현이 되어 있으나</li>
<li><code>len</code> 함수는 결국 내부에서 <code>count</code> 메서드를 다시 호출하는 구조로 되어있기 때문에 미세하지만 속도가 조금 더 느림.</li>
</ul>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-연산-aggregate---sum-avg--max-minspan">🍹 <span style='background-color: #f5f0ff'><code>데이터 연산 (aggregate) - Sum, Avg / Max, Min</code></span></h3>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python"># 아래 코드에서 &#39;data&#39;는 변수명이며, 임의로 지정 가능
from django.db.models import Sum, Avg, Max, Min
</code></pre>
</blockquote>
<pre><code>def handle(self, *args, **options):
    # Sum
    all_product_total = all_product.aggregate(data=Sum(&quot;price&quot;))
    print(all_product_total)
    # Avg
    all_product_average = all_product.aggregate(data=Avg(&quot;price&quot;))
    print(all_product_average)
    # Max
    all_product_max = all_product.aggregate(data=Max(&quot;price&quot;))
    print(all_product_max)
    # Min
    all_product_min = all_product.aggregate(data=Min(&quot;price&quot;))
    print(all_product_min)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">   # Sum
    {&#39;data&#39;: 1588000}
   # Avg
    {&#39;data&#39;: 198500.0}
   # Max
    {&#39;data&#39;: 1000000}
   # Min
    {&#39;data&#39;: 500}
</code></pre>
</blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff원하는-필드컬럼만-추출-valuesspan">🍹 <span style='background-color: #f5f0ff'><code>원하는 필드(컬럼)만 추출 (values)</code></span></h3>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        all_product_status = all_product.values(&quot;status&quot;)
        print(all_product_status)
        print(type(all_product_status))

        all_product_status_list = all_product.values_list(&quot;status&quot;)
        print(all_product_status_list)
        print(type(all_product_status_list))

        all_product_status_list = all_product.values_list(&quot;status&quot;, flat=True)
        print(all_product_status_list)
        print(type(all_product_status_list))
</code></pre>
</blockquote>
<pre><code>    all_product_status_real_list = list(all_product_status_list)
    print(all_product_status_real_list)
    all_product_status_distinct_list = list(set(lall_product_status_list))
    print(all_product_status_distinct_list)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [{&#39;status&#39;: &#39;new&#39;}, {&#39;status&#39;: &#39;new&#39;}, {&#39;status&#39;: &#39;old&#39;}, {&#39;status&#39;: &#39;new&#39;}, {&#39;status&#39;: &#39;old&#39;}, {&#39;status&#39;: &#39;old&#39;}, {&#39;status&#39;: &#39;new&#39;}, {&#39;status&#39;: &#39;old&#39;}]&gt;
    &lt;class &#39;django.db.models.query.QuerySet&#39;&gt;
</code></pre>
</blockquote>
<pre><code>&lt;QuerySet [(&#39;new&#39;,), (&#39;new&#39;,), (&#39;old&#39;,), (&#39;new&#39;,), (&#39;old&#39;,), (&#39;old&#39;,), (&#39;new&#39;,), (&#39;old&#39;,)]&gt;
&lt;class &#39;django.db.models.query.QuerySet&#39;&gt;</code></pre><blockquote>
</blockquote>
<pre><code>&lt;QuerySet [&#39;new&#39;, &#39;new&#39;, &#39;old&#39;, &#39;new&#39;, &#39;old&#39;, &#39;old&#39;, &#39;new&#39;, &#39;old&#39;]&gt;
&lt;class &#39;django.db.models.query.QuerySet&#39;&gt;</code></pre><blockquote>
</blockquote>
<pre><code>[&#39;new&#39;, &#39;new&#39;, &#39;old&#39;, &#39;new&#39;, &#39;old&#39;, &#39;old&#39;, &#39;new&#39;, &#39;old&#39;]
[&#39;new&#39;, &#39;old&#39;]</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-정렬-order-byspan">🍹 <span style='background-color: #f5f0ff'><code>데이터 정렬 (order by)</code></span></h3>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        # 오름차순
        all_product_asc = all_product.order_by(&quot;price&quot;)
        print(all_product_asc)
        # 내림차순
        all_product_dsc = all_product.order_by(&quot;-price&quot;)
        print(all_product_dsc)
</code></pre>
</blockquote>
<pre><code>    # 내림차순 정렬 후 가격만 확인
    for item in all_product_dsc:
        print(item.price)</code></pre><blockquote>
</blockquote>
<pre><code>    # random으로 정렬
    all_product_random = all_product.order_by(&quot;?&quot;)
    print(all_product_random)
    # 렌덤으로 하나의 결과만 추출
    #   방법 1
    choice_product_random = all_product.order_by(&quot;?&quot;)[0]
    print(choice_product_random)    
    #   방법 2
    index = randint(0, Product.objects.count() - 1)
    product = Product.objects.all()[index]
    print(product)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">        # 오름차순
        &lt;QuerySet [&lt;Product: 젓가락 (9oi8ujhy)&gt;, &lt;Product: 수저 (8ai2k8ww)&gt;, &lt;Product: 휴지 (po3nuw01)&gt;, &lt;Product: 머그컵 (9if56yhg)&gt;, &lt;Product: 화장품 (92oif0rj)&gt;, &lt;Product: 압력밥솥 (8uhyjtd6)&gt;, &lt;Product: 네스프레소 (9oiuh67y)&gt;, &lt;Product: 스마트폰 (23tyfuen)&gt;]&gt;
        # 내림차순
        &lt;QuerySet [&lt;Product: 스마트폰 (23tyfuen)&gt;, &lt;Product: 네스프레소 (9oiuh67y)&gt;, &lt;Product: 압력밥솥 (8uhyjtd6)&gt;, &lt;Product: 화장품 (92oif0rj)&gt;, &lt;Product: 머그컵 (9if56yhg)&gt;, &lt;Product: 휴지 (po3nuw01)&gt;, &lt;Product: 수저 (8ai2k8ww)&gt;, &lt;Product: 젓가락 (9oi8ujhy)&gt;]&gt; 
</code></pre>
</blockquote>
<pre><code>    # 내림차순 정렬 후 가격만 확인
    1000000
    300000
    250000
    30000
    5000
    1500
    1000
    500</code></pre><blockquote>
</blockquote>
<pre><code>    # random으로 정렬
    &lt;QuerySet [&lt;Product: 휴지 (po3nuw01)&gt;, &lt;Product: 네스프레소 (9oiuh67y)&gt;, &lt;Product: 머그컵 (9if56yhg)&gt;, &lt;Product: 압력밥솥 (8uhyjtd6)&gt;, &lt;Product: 젓가락 (9oi8ujhy)&gt;, &lt;Product: 화장품 (92oif0rj)&gt;, &lt;Product: 수저 (8ai2k8ww)&gt;, &lt;Product: 스마트폰 (23tyfuen)&gt;]&gt;  
    # random 정렬 후 하나만 뽑아오기
    #   방법 1
    화장품 (92oif0rj)
    #   방법 2
    압력밥솥 (8uhyjtd6)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-검색-filter--외래키foreign-key-참조span">🍹 <span style='background-color: #f5f0ff'><code>데이터 검색 (filter) : 외래키(Foreign Key) 참조</code></span></h3>
<p>🥡 먼저, 각 모델에 대해 살펴보자.</p>
<p><code>PaymentOrderRecord</code> 모델</p>
<ul>
<li>아래와 같이 <code>member</code>라는 filed를 가지고 있는데, 이것은 <code>Member</code>라는<code>Field</code>를 외래키로 가지고 있음.<blockquote>
<pre><code class="language-python">      member = models.ForeignKey(
              Member,
              verbose_name=&quot;회원&quot;,
              on_delete=models.CASCADE,
              null=True,
              related_name=&quot;member_paymentorderrecord&quot;,
          )</code></pre>
</blockquote>
</li>
</ul>
<p><code>Member</code> 모델</p>
<ul>
<li>아래와 같이 <code>name</code>이라는 <code>Field</code>를 가지고 있음.<blockquote>
<pre><code class="language-python">  class Member(models.Model):
      name = models.CharField(verbose_name=&quot;이름&quot;, max_length=10)
      birth = models.DateField(verbose_name=&quot;생년월일&quot;, null=True)
      email = models.EmailField(verbose_name=&quot;이메일&quot;)
      phone = models.CharField(verbose_name=&quot;전화번호&quot;, max_length=11)
</code></pre>
</blockquote>
<pre><code>  created_at = models.DateTimeField(verbose_name=&quot;생성일&quot;, auto_now_add=True)
  updated_at = models.DateTimeField(verbose_name=&quot;갱신일&quot;, auto_now=True)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
</li>
</ul>
<p>🥡 이것을 이용한 <code>데이터 조회</code></p>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        # 송강호라는 사람이 구매한 주문서 출력
        payment_by_name = all_payment.filter(member__name=&quot;송강호&quot;)
        print(payment_by_name)
        # 머그컵을 구매한 주문서 출력
        payment_by_product = all_payment.filter(product__name=&quot;머그컵&quot;)
        print(payment_by_product)</code></pre>
</blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [&lt;PaymentOrderRecord: 20220101-3edcxs (2개)&gt;]&gt;
</code></pre>
</blockquote>
<pre><code>&lt;QuerySet [&lt;PaymentOrderRecord: 20220101-9oi8u7 (2개)&gt;]&gt;</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-검색-filter--가상의-필드annotate-이용span">🍹 <span style='background-color: #f5f0ff'><code>데이터 검색 (filter) : 가상의 필드(</code><a href="https://docs.djangoproject.com/en/3.2/ref/models/querysets/#annotate">annotate</a><code>) 이용</code></span></h3>
<p>🥡 <code>PaymentOrderRecord</code>라는 모델에 <code>member</code>라는 필드는 있지만, <code>member_name</code>이라는 필드는 없음.</p>
<ul>
<li>이럴 때, 해당 필드를 생성해서 붙여주는 작업을 하는 것이 바로 <code>annotate</code>의 역할<blockquote>
<ul>
<li>주의할 것!!<ul>
<li>어떤 필드에 접근하려면 <code>하나의 객체</code>여야만 함.</li>
<li>쿼리셋에 대한 리스트이면 특정 필드에 접근할 수 없음.</li>
<li>그래서 <code>last()</code> 메서드를 붙여서 하나의 객체를 추출한 것임.</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ul>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        all_payment_annotate = all_payment.annotate(member_name=F(&quot;member__name&quot;))
</code></pre>
</blockquote>
<pre><code>    print(type(all_payment_annotate))
    print(type(all_payment_annotate.last()))</code></pre><blockquote>
</blockquote>
<pre><code>    # print(all_payment_annotate.member_name)
    print(all_payment_annotate.last().member_name)</code></pre><blockquote>
</blockquote>
<pre><code>    for item in all_payment_annotate:
        print(item.member_name)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;class &#39;django.db.models.query.QuerySet&#39;&gt;  # 쿼리셋
    &lt;class &#39;orm.models.PaymentOrderRecord&#39;&gt;    # 하나의 row
</code></pre>
</blockquote>
<pre><code>장동건</code></pre><blockquote>
</blockquote>
<pre><code>이병헌
이병헌
박해수
박해수
송강호
강동원
강동원
장동건</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff데이터-검색-filter--특정-범위-내에서-찾기inspan">🍹 <span style='background-color: #f5f0ff'><code>데이터 검색 (filter) : 특정 범위 내에서 찾기(in)</code></span></h3>
<p>🥡 내가 찾고싶은 리스트 목록을 미리 지정하고, 내가 가진 데이터 값들 중 해당 리스트 내에 있는 것들만 조회할 때 사용</p>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        price_list = [500, 1000]
        product_in_the_list = all_product.filter(price__in=price_list)
</code></pre>
</blockquote>
<pre><code>    print(product_in_the_list)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [&lt;Product: 수저 (8ai2k8ww)&gt;, &lt;Product: 젓가락 (9oi8ujhy)&gt;]&gt;</code></pre>
</blockquote>
<br>

<h3 id="🍹-span-stylebackground-color-f5f0ff개별-실습span">🍹 <span style='background-color: #f5f0ff'><code>개별 실습</code></span></h3>
<h4 id="🍾-span-stylebackground-color-dcffe4q1-결제-전-상태의-고객의-이름을-출력span">🍾 <span style='background-color: #dcffe4'><code>Q1. 결제 전 상태의 고객의 이름을 출력</code></span></h4>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        find_customer_name = all_payment.filter(status=PaymentOrderRecord.NOT_YET).values_list(
            &quot;member__name&quot;, flat=True
        )
</code></pre>
</blockquote>
<pre><code>    print(find_customer_name)
    print(list(set(find_customer_name)))</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet [&#39;이병헌&#39;, &#39;이병헌&#39;, &#39;강동원&#39;]&gt;
    [&#39;이병헌&#39;, &#39;강동원&#39;]</code></pre>
</blockquote>
<h4 id="🍾-span-stylebackground-color-dcffe4q2-화장품을-주문하고-환불한-고객의-이름-출력span">🍾 <span style='background-color: #dcffe4'><code>Q2. 화장품을 주문하고, 환불한 고객의 이름 출력</code></span></h4>
<p><code>control_orm_data.py</code></p>
<blockquote>
<pre><code class="language-python">    def handle(self, *args, **options):
        find_customer_name = all_payment.filter(product__name=&quot;화장품&quot;, status=PaymentOrderRecord.REFUND).values_list(&quot;member__name&quot;, flat=True)
</code></pre>
</blockquote>
<pre><code>    print(find_customer_name)</code></pre><blockquote>
<pre><code></code></pre></blockquote>
<p><code>terminal</code> 실행 결과</p>
<blockquote>
<pre><code class="language-python">    &lt;QuerySet []&gt;</code></pre>
</blockquote>
<br>

<hr>
<br>

<h2 id="🏁-span-stylebackground-color-f0fcfcrefspan">🏁 <span style='background-color: #f0fcfc'>ref</span></h2>
<p><a href="https://docs.djangoproject.com/en/3.2/ref/models/querysets/#">Django 공식 문서 &gt; QuerySet API reference</a>
<a href="https://hyeo-noo.tistory.com/248">ORM 쿼리 최적화</a>
<a href="https://chrisjune-13837.medium.com/django-%EB%8B%B9%EC%8B%A0%EC%9D%B4-%EB%AA%B0%EB%9E%90%EB%8D%98-orm-%EA%B8%B0%EC%B4%88%EC%99%80-%EC%8B%AC%ED%99%94-592a6017b5f5">Django &gt; 반드시 알아야 할 5가지 ORM 쿼리</a>
<a href="http://raccoonyy.github.io/django-annotate-and-aggregate-like-as-excel/">(엑셀만큼 쉬운) Django Annotation/Aggregation</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[221004_[독서] 클린코드 2장. 의미있는 이름]]></title>
            <link>https://velog.io/@brown_eyed87/221004%EB%8F%85%EC%84%9C-%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-2%EC%9E%A5.-%EC%9D%98%EB%AF%B8%EC%9E%88%EB%8A%94-%EC%9D%B4%EB%A6%84</link>
            <guid>https://velog.io/@brown_eyed87/221004%EB%8F%85%EC%84%9C-%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-2%EC%9E%A5.-%EC%9D%98%EB%AF%B8%EC%9E%88%EB%8A%94-%EC%9D%B4%EB%A6%84</guid>
            <pubDate>Tue, 04 Oct 2022 14:39:14 GMT</pubDate>
            <description><![CDATA[<p><strong>※ 본 글은 <code>인사이트</code>에서 출간한 <code>로버트 C. 마틴</code> 저자의 <code>『Clean Code(클린 코드) 애자일 소프트웨어 장인 정신』</code> 책을 읽고 작성하였습니다.</strong></p>
<h1 id="🚦-span-stylebackground-color-fdebec2장-의미있는-이름">🚦 <span style='background-color: #fdebec'><code>2장. 의미있는 이름</code></h1>
</span>

<br>




<h2 id="🚀-span-stylebackground-color-f0fcfc소프트웨어에서-이름은-어디나-쓰임span">🚀 <span style='background-color: #f0fcfc'><code>소프트웨어에서 이름은 어디나 쓰임</code></span></h2>
<ul>
<li><p><code>변수</code>, <code>함수</code>, <code>인자</code>, <code>클래스</code>, <code>패키지</code>, <code>소스파일</code>, 심지어 <code>소스파일이 담긴 디렉토리</code> 등등
&nbsp;&nbsp;→ <u>이렇게나 많이 사용하므로 이름을 잘 지으면 여러모로 편리</u>함.</p>
</li>
<li><p>이름을 잘 짓는 간단한 규칙에 대한 학습을 시-작!!</p>
<br>
## 🚀 <span style='background-color: #f0fcfc'>`의도를 분명히 밝혀라`</span></li>
<li><p>좋은 이름을 지으려면 시간이 걸리지만 <code>좋은 이름으로 절약하는 시간</code>이 훨씬 더 많음.</p>
</li>
<li><p><code>이름을 주의 깊게 살펴</code> 더 나은 이름이 떠오르면 개선할 것.</p>
</li>
<li><p>이름을 잘 지으면 <code>코드를 읽는 사람이 좀 더 행복</code>해 질 수 있음.</p>
</li>
<li><p><code>변수/함수/클래스</code>의 <code>이름</code>은 <code>존재 이유/수행 기능/사용 방법</code>과 같은 질문에 답할 수 있어야 함.</p>
<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc그릇된-정보를-피하라span">🚀 <span style='background-color: #f0fcfc'><code>그릇된 정보를 피하라</code></span></h2>
<ul>
<li>프로그래머는 코드에 <code>그릇된 단서</code>를 남겨서는 안됨
&nbsp;&nbsp;→ 그릇된 단서는 코드 의미를 흐림.</li>
<li>나름대로 <code>널리 쓰이는 의미가 있는 단어</code>를 다른 의미로 사용해도 안됨. (e.g. <code>list</code>)</li>
<li><code>서로 흡사한 이름</code>을 사용하지 않도록 주의할 것</li>
<li><code>유사한 개념</code>은 유사한 표기법을 사용하고 이것도 정보에 해당됨.</li>
<li><code>일관성이 떨어지는 표기법</code>은 그릇된 정보임.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc의미-있게-구분하라span">🚀 <span style='background-color: #f0fcfc'><code>의미 있게 구분하라</code></span></h2>
<ul>
<li><u>컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현</u>하는 프로그래머는 스스로 <u>문제를 일으킴.</u>    </li>
<li>동일한 범위 안에서 다른 두 개념에 같은 이름을 사용하지 못하기 때문에 한쪽 이름을 고치려다가 잘못 고치는 순간 컴파일이 불가능한 상황에 빠짐.    </li>
<li><u>연속적인 숫자를 덧붙인 이름</u>(<code>a1</code>, <code>a2</code>, …, <code>aN</code>)은 그릇된 정보를 제공하는 이름도 아니고, <u>아무런 정보를 제공하지 못하는 이름일 뿐</u>, 저자 의도가 전혀 드러나지 않음.<br><code>불용어를 추가한 이름</code> 역시 아무런 정보도 제공하지 못함.<br>&nbsp;&nbsp;→ <code>a</code>나 <code>the</code>같은 <code>접두사</code>이더라도 <u>의미가 분명히 다르다면 사용해도 무방.</u></li>
<li><code>읽는 사람이 차이를 알도록</code> 이름을 붙여라
&nbsp;&nbsp;→ <strong>나쁜 예시</strong> : <code>getctiveAcount()</code>, <code>getctiveAcounts()</code>, <code>getctiveAcountInfo()</code><br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc발음하기-쉬운-이름을-사용하라span">🚀 <span style='background-color: #f0fcfc'><code>발음하기 쉬운 이름을 사용하라</code></span></h2>
<ul>
<li><u>두뇌에서 상당한 부분은 단어라는 개념만 전적으로 처리하고, 정의상으로 단어는 발음이 가능</u>하므로, <code>발음하기 쉬운 이름</code>을 선택하는 것이 좋음.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc검색하기-쉬운-이름을-사용하라span">🚀 <span style='background-color: #f0fcfc'><code>검색하기 쉬운 이름을 사용하라</code></span></h2>
<ul>
<li>문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다는 문제점이 있음</li>
<li><u>짧은 숫자나 문자는 여러 군데에서 등장</u>하므로 <code>긴 이름</code>이 짧은 이름보다 좋고, <code>검색하기 쉬운 이름</code>이 상수보다 좋음.</li>
<li>한 문자만 사용하고 싶다면, 간단한 메서드에서 로컬 변수를 지정할 때 사용할 것.</li>
<li><code>이름 길이</code>는 <code>범위 크기</code>에 <code>비례</code>해야 하며, <code>변수</code>나 <code>상수</code>를 <u>코드 여러 곳에서 사용한다면 <code>검색하기 쉬운 이름</code>이 바람직</u>함.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc인코딩을-피하라span">🚀 <span style='background-color: #f0fcfc'><code>인코딩을 피하라</code></span></h2>
<ul>
<li>이름에 인코딩할 정보는 매우 많으며, 유형이나 범위까지 인코딩에 넣으면 그만큼 해독이 어려워짐.    </li>
<li>때로는 인코딩이 필요한 경우도 있음.<br>&nbsp;&nbsp;→ 인터페이스 클래스와 구현 클래스<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc자신의-기억력을-자랑하지-마라span">🚀 <span style='background-color: #f0fcfc'><code>자신의 기억력을 자랑하지 마라</code></span></h2>
<ul>
<li>변수 이름을 <code>자신이 아는 이름</code>으로 변환해야 한다면 그 변수 이름은 <u>바람직하지 않음.</u></li>
<li>일반적으로 문제 영역이나 해법 영역에서 사용하지 않는 이름을 선택했기 때문에 생기는 문제.    </li>
<li><code>루프</code>에서 <code>반복 횟수를 세는 변수</code>는 <code>문자 하나</code>만 사용해도 괜찮음.<br>&nbsp;&nbsp;→ <u>소문자 L은 제외</u>하며, 루프 범위가 아주 작고 다른 이름과 충돌하지 않을 경우에 한정.</li>
<li>똑똑한 프로그래머와 전문가 프로그래머 사이에서 나타나는 차이점 하나만 들자면,    전문가 프로그래머는 명료함이 최고라는 사실을 이해함.<br>&nbsp;&nbsp;→ <u>전문가 프로그래머</u>는 자신의 능력을 좋은 방향으로 사용해 <u>남들이 이해하는 코드</u>를 내놓음.    <br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc클래스-이름span">🚀 <span style='background-color: #f0fcfc'><code>클래스 이름</code></span></h2>
<ul>
<li><code>클래스 이름</code>과 <code>객체 이름</code>은 <code>명사</code>나 <code>명사구</code>가 적합함.</li>
<li><strong>좋은 예</strong> : <code>Customer</code>, <code>WikiPage</code>, <code>Account</code>, <code>AddressParser</code></li>
<li><strong>나쁜 예</strong> : <code>Manager</code>, <code>Processor</code>, <code>Data</code>, <code>Info</code> 등과 같은 단어. 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;→ <u>동사는 아예 사용하지 않음.</u><br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc메서드-이름span">🚀 <span style='background-color: #f0fcfc'><code>메서드 이름</code></span></h2>
<ul>
<li><p><code>메서드 이름</code>은 <code>동사</code>나 <code>동사구</code>가 적합.</p>
<blockquote>
<ul>
<li><strong>좋은 예</strong> : <code>postPayment</code>, <code>deletePage</code>, <code>save</code> 등</li>
<li>접근자(<strong>Accessor</strong>), 변경자(<strong>Mutator</strong>), 조건자(<strong>Predicate)</strong>는 <code>javabean 표준</code>에 따라 <u>값 앞에 <code>get</code>, <code>set</code>, <code>is</code>를 붙임.</u></li>
</ul>
</blockquote>
</li>
<li><p>생성자(<code>Constructor</code>)를 중복정의(<code>overload</code>)할 때는 <u>정적 팩토리 메서드를 사용</u>함.
&nbsp;&nbsp;→ <code>메서드</code>는 <code>인수를 설명하는 이름</code>을 <code>사용</code>함.</p>
<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc기발한-이름은-피하라span">🚀 <span style='background-color: #f0fcfc'><code>기발한 이름은 피하라</code></span></h2>
<ul>
<li>이름이 너무 기발하면 기억할 사람만 그 이름을 기억함.</li>
<li>재미난 이름보다 <code>명료한 이름</code>을 <code>선택</code>하라.</li>
<li><u>의도를 분명하고 솔직하게 표현</u>하라<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc한-개념에-한-단어를-사용하라span">🚀 <span style='background-color: #f0fcfc'><code>한 개념에 한 단어를 사용하라</code></span></h2>
<ul>
<li><u>추상적인 개념 하나에 단어 하나를 선택해 이를 고수하게 되면 어느 클래스에서 어느 이름을 썼는지 기억하기 어려움.</u>
&nbsp;&nbsp;→ 예를 들어, 클래스마다 같은 메서드 이름을 사용하는 것.</li>
<li>이클립스, 인텔리제이 등과 같은 최신 IDE는 문맥에 맞는 단어를 제공함.<blockquote>
<ul>
<li>예를 들어, 객체를 사용하면 그 객체가 제공하는 메서드 목록을 보여줌.</li>
<li>하지만 목록은 보통 함수 이름과 매개변수만 보여줄 뿐 주석은 보여주지 않음.</li>
</ul>
</blockquote>
</li>
<li>따라서 <code>메서드 이름</code>은 <code>독자적</code>이고 <code>일관적</code>이어야 함.</li>
<li><u>일관성 있는 어휘는 코드를 사용할 프로그래머가 반갑게 여길 선물</u>임.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc말장난을-하지-마라span">🚀 <span style='background-color: #f0fcfc'><code>말장난을 하지 마라</code></span></h2>
<ul>
<li>한 단어를 두 가지 목적으로 사용하지 말 것</li>
<li><u>여러 클래스에서 한 단어를 동일한 기능의 메서드에 사용하게 되더라도 그 메서드의 매개변수와 반환 값이 의미적으로 똑같다면 문제가 없음.</u></li>
<li><strong>프로그래머는 코드를 최대한 이해하기 쉽게 짜야 함.</strong></li>
<li>집중적인 탐구가 필요한 코드가 아니라 <code>대충 훑어봐도 이해할 코드 작성</code>이 목표임.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc해법-영역에서-가져온-이름을-사용하라span">🚀 <span style='background-color: #f0fcfc'><code>해법 영역에서 가져온 이름을 사용하라</code></span></h2>
<ul>
<li><u>코드를 읽을 사람도 프로그래머</u>이기 때문에 <code>전산 용어</code>, <code>알고리즘 이름</code>, <code>패턴 이름</code>, <code>수학 용어</code> 등은 사용해도 괜찮음.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc문제-영역에서-가져온-이름을-사용하라span">🚀 <span style='background-color: #f0fcfc'><code>문제 영역에서 가져온 이름을 사용하라</code></span></h2>
<ul>
<li><u>적절한 &#39;프로그래머 용어&#39;가 없다면</u> <code>문제 영역</code>에서 <code>이름</code>을 가져올 것.</li>
<li>그러면 코드를 보수하는 프로그래머가 분야 전문가에게 의미를 물어 파악할 수 있음.</li>
<li><code>우수한 프로그래머와 설계자</code>라면 <code>해법 영역</code>과 <code>문제 영역</code>을 <code>구분</code>할 줄 알아야 함.</li>
<li><u>문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 함</u><br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc의미-있는-맥락을-추가하라span">🚀 <span style='background-color: #f0fcfc'><code>의미 있는 맥락을 추가하라</code></span></h2>
<ul>
<li>스스로 의미가 분명한 이름이 없지는 않지만 대다수의 이름은 그렇지 못함.</li>
<li>그래서 클래스, 함수, 이름 공간에 넣어 맥락을 부여함.</li>
<li>모든 방법이 실패하면 마지막 수단으로 접두어를 붙임.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc불필요한-맥락을-없애라span">🚀 <span style='background-color: #f0fcfc'><code>불필요한 맥락을 없애라</code></span></h2>
<ul>
<li>고급 휘발유 충전소(<code>Gas Station Deluxe</code>) 라는 <code>애플리케이션</code>을 짠다고 가정.</li>
<li><strong>모든 클래스 이름을 <code>GSD</code>로 시작</strong>하게 된다면 <u>IDE에서 G를 입력하고 자동완성 키를 누르는 순간 모든 클래스를 열거</u>하기 때문에 현명하지 못함.</li>
<li>일반적으로는 <code>의미가 분명한 경우</code>에 한해 <code>짧은 이름</code>이 긴 이름보다 <code>좋음</code>.</li>
<li><code>accountAddress</code>와 <code>customerAddress</code>는 <u><code>Address</code> 클래스의 인스턴스로는 좋은 이름</u>이나, 클래스 이름으로 적합하지 못함.<br>

</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc마치면서span">🚀 <span style='background-color: #f0fcfc'><code>마치면서</code></span></h2>
<ul>
<li><u>좋은 이름을 선택하려면 <code>설명 능력이 뛰어나야</code> 하고 <code>문화적 배경이 같아야</code> 함</u></li>
<li><code>좋은 이름을 선택하는 능력</code>은 기술, 비즈니스, 관리 문제가 아닌 <code>교육 문제</code>임</li>
<li>사람들이 이름을 바꾸지 않으려는 이유 하나가 다른 개발자가 반대할까 두려워서임.</li>
<li>그러나 <strong>오히려 좋은 이름으로 바꿈으로써 코드 가독성이 높아짐.</strong></li>
<li>다른 사람이 짠 코드를 손본다면 리팩터링 도구를 사용해 문제 해결 목적으로 이름을 개선하는 것이 좋음.</li>
<li>이것은 단기적인 효과는 물론 장기적인 이익도 보장함.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[221003_[독서] 클린코드 1장. 깨끗한 코드]]></title>
            <link>https://velog.io/@brown_eyed87/221003%EB%8F%85%EC%84%9C-%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-1%EC%9E%A5.-%EA%B9%A8%EB%81%97%ED%95%9C-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@brown_eyed87/221003%EB%8F%85%EC%84%9C-%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-1%EC%9E%A5.-%EA%B9%A8%EB%81%97%ED%95%9C-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Mon, 03 Oct 2022 02:00:19 GMT</pubDate>
            <description><![CDATA[<p><strong>※ 본 글은 <code>인사이트</code>에서 출간한 <code>로버트 C. 마틴</code> 저자의 <code>『Clean Code(클린 코드) 애자일 소프트웨어 장인 정신』</code> 책을 읽고 작성하였습니다.</strong></p>
<h1 id="🚦-span-stylebackground-color-fdebec1장-깨끗한-코드-span">🚦 <span style='background-color: #fdebec'><code>1장. 깨끗한 코드</code> </span></h1>
<br>

<h2 id="🚀-span-stylebackground-color-f0fcfccodespan">🚀 <span style='background-color: #f0fcfc'><code>Code</code></span></h2>
<p>✔ 기계가 실행할 정도로 상세하게 요구사항을 명시하는 작업(프로그래밍)의 결과
✔ 요구사항을 표현하는 언어</p>
<ul>
<li>앞으로 프로그래밍 언어에서 추상화 수준은 점차 높아질 것이며 특정 응용 분야에 적합한 프로그래밍 언어 수도 점차 많아질 것임.</li>
<li>고도로 추상화된 언어나 특정 응용 분야 언어로 기술하는 명세 역시 코드이며 어떤 언어를 사용하든    코드는 기계가 이해하고 실행할 정도로 엄밀하고 정확하고 상세하고 정형화되어야 하기 때문에 코드는 사라지지 않을 것임.</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc나쁜-코드span">🚀 <span style='background-color: #f0fcfc'><code>나쁜 코드</code></span></h2>
<p>✔ 나쁜 코드는 개발 속도를 크게 떨어뜨림.
✔ 코드를 고칠 때마다 엉뚱한 곳에서 문제가 생기고, 매번 얽히고 설킨 코드를 &#39;해독&#39;해서 얽히고 설킨 코드를 더함.
✔ 시간이 지나면서 쓰레기 더미는 점점 높아지고 깊어지고 커지기 때문에 청소할 방법이 없는 불가항력의 존재임.</p>
<ul>
<li>나쁜 코드가 쌓일수록 팀 생산성은 떨어지기 시작해 0으로 근접함.</li>
<li>생산성을 증가시키려고 프로젝트에 새  인력을 투입하지만 시스템 설계에 대한 조예가 깊지 않기 때문에 결국은 나쁜 코드를 더 많이 양산하게 되어 생산성은 더더욱 0에 수렴.</li>
<li>이로 인해 원대한 재설계의 꿈을 가지고 새로운 타이거 팀이 구성됨.</li>
<li>가장 유능하고 똑똑한 사람들만 타이거 팀으로 차출되며 나머지는 계속해서 현재 시스템을 유지보수.</li>
<li>그러나 때때로 경주는 아주 오랫동안 이어져 새 시템이 기존 시스템을 따라잡을 즈음이면 초창기 타이거 팀원들은 모두 떠나고 새로운 팀원들이 새 시스템을 설계하고자 나섬. 
( ∵ 현재 시스템이 너무 엉망이어서 )</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc좋은-코드가-나쁜-코드로-전락하게-되는-이유span">🚀 <span style='background-color: #f0fcfc'><code>좋은 코드가 나쁜 코드로 전락하게 되는 이유</code></span></h2>
<ul>
<li>원래 설계를 뒤집는 방향으로 요구사항이 변했기 때문</li>
<li>일정이 촉박해 제대로 할 시간이 없었기 때문</li>
<li>멍청한 관리자와 조급한 고객, 쓸모없는 마케팅 부서 때문</li>
</ul>
<blockquote>
<p>🚩 그러나 결국 잘못은 전적으로 프로그래머에게 있는 것!!!</p>
</blockquote>
<p>✔ 프로그래머라면 누구나 나쁜 코드가 업무 속도를 늦춘다는 사실을 익히 알지만, 기한을 맞추기 위해 나쁜 코드를 양산할 수밖에 없다고 느낌.
✔ 그러나 나쁜 코드를 양산하면 엉망진창인 상태로 인해 속도가 늦어져 결국 기한을 맞추지 못함.</p>
<blockquote>
<p>🚩 기한을 맞추며 빨리 갈 수 있는 유일한 방법은 언제나 코드를 최대한 깨끗하게 유지하는 습관임.</p>
</blockquote>
<h2 id="🚀-span-stylebackground-color-f0fcfc깨끗한-코드는-어떻게-작성하나span">🚀 <span style='background-color: #f0fcfc'><code>깨끗한 코드는 어떻게 작성하나?</code></span></h2>
<ul>
<li><p>누구나 깨끗한 코드와 나쁜 코드를 구분할 줄 알더라도, 깨끗한 코드를 작성할 줄 알지는 않음.</p>
</li>
<li><p>깨끗한 코드를 작성하려면 &#39;청결&#39;이라는 힘겹게 습득한 감각을 활용해 자잘한 기법들을 적용하는 절제와 규율이 필요함.
✔ 여기서 키 포인트는 &#39;코드 감각&#39;임.
✔ 누군가는 그것을 타고나지만, 또 누군가는 투쟁해서 얻어야 함.
✔ 거기에 절제와 규율을 적용해 나쁜 코드를 좋은 코드로 바꾸는 전략도 필요함</p>
</li>
<li><p>&#39;코드 감각&#39;이 없는 프로그래머도 때로는 나쁜 모듈을 알아보지만, &#39;코드 감각&#39; 이 있는 프로그래머는 나쁜 모듈을 보면 좋은 모듈로 개선할 방안을 떠올리고, 최고의 방안을 선택하여 계획을 세움.</p>
</li>
</ul>
<h2 id="🚀-span-stylebackground-color-f0fcfc깨끗한-코드란span">🚀 <span style='background-color: #f0fcfc'><code>깨끗한 코드란?</code></span></h2>
<h3 id="🚢-비야네-스트롭스트룹">🚢 <code>비야네 스트롭스트룹</code></h3>
<p><code>C++ 창시자이자 『The C++ Programming Language』 저자</code></p>
<blockquote>
<ul>
<li>나는 우아하고 효율적인 코드를 좋아한다.</li>
<li>논리가 간단해야 버그가 숨어들지 못한다.</li>
<li>의존성을 최대한 줄여야 유지보수가 쉬워진다.</li>
<li>오류는 명백한 전략에 의거해 철저히 처리한다.</li>
<li>성능을 최적으로 유지해야 사람들이 원칙 없는 최적화로 코드를 망치려는 유혹에 빠지지 않는다.</li>
<li>깨끗한 코드는 한 가지를 제대로 한다.</li>
</ul>
</blockquote>
<p>🌞 그에게 깨끗한 코드란!
✔ 보는 사람에게 즐거움을 선사해야 함.
✔ 세세한 사항까지 꼼꼼하게 처리하는 코드
✔ 각 함수와 클래스와 모듈은 주변 상황에 현혹되거나 오염되지 않고 한 가지 길을 걷는 코드여야 함.</p>
<p>🌑 나쁜 코드는 속도도 느리고 CPU 자원도 낭비하며, 보기에도 즐겁지 않으며, 또 다른 나쁜 코드를 유혹함.</p>
<h3 id="🚢-데이브-토마스와-앤디-헌트">🚢 <code>데이브 토마스와 앤디 헌트</code></h3>
<p><code>실용주의 프로그래머</code>
✔ 나쁜 코드는 깨진 창문과도 같음.
✔ 창문이 깨진 건물은 누구도 상관하지 않는다는 인상을 풍기며 사람들도 관심을 끊음.</p>
<h3 id="🚢-그래디-부치">🚢 <code>그래디 부치</code></h3>
<p><code>『Object Oriented Analysis and Design with Application』 저자</code></p>
<blockquote>
<ul>
<li>깨끗한 코드는 단순하고 직접적임.</li>
<li>깨끗한 코드는 잘 쓴 문장처럼 읽힘</li>
<li>깨끗한 코드는 결코 설계자의 의도를 숨기지 않음.</li>
<li>오히려 명쾌한 추상화와 단순한 제어문으로 가득함.</li>
</ul>
</blockquote>
<p>✔ 가독성을 강조하여 깨끗한 코드가 잘 쓴 문장처럼 읽혀야 함.
✔ 코드는 추측이 아니라 사실에 기반해야 하며 반드시 필요한 내용만 담겨야 함.
✔ 코드를 읽는 사람에게 프로그래머가 단호하다는 인상을 줘야 함.</p>
<h3 id="🚢-큰-데이브-토마스">🚢 <code>&#39;큰&#39; 데이브 토마스</code></h3>
<p><code>PTO 창립자이자 이클립스 전략의 대부</code></p>
<blockquote>
<ul>
<li>깨끗한 코드는 작성자가 아닌 사람도 읽기 쉽고 고치기 쉽다.</li>
<li>단위 테스트 케이스와 인스 테스트 케이스가 존재한다.</li>
<li>깨끗한 코드에는 의미 있는 이름이 붙는다.</li>
<li>특정한 목적을 달성하는 방법은 (여러 가지가 아니라) 하나만 제공한다.</li>
<li>의존성은 최소이며 각 의존성을 명확히 정의한다.</li>
<li>API는 명확하여 최소로 줄였다.</li>
<li>언어에 따라 필요한 모든 정보를 코드만으로 명확히 표현할 수 없기에 코드는 문학적으로 표현해야 마땅하다.</li>
</ul>
</blockquote>
<p>✔ 깨끗한 코드란 다른 사람이 고치기 쉬워야 함.
✔ 테스트 케이스가 없는 코드는 깨끗한 코드가 아님.
✔ 깨끗한 코드를 만들기 위해서는 인간이 읽기 좋은 코드를 작성하면 됨.</p>
<h3 id="🚢-마이클-페더스">🚢 <code>마이클 페더스</code></h3>
<p><code>『Working Effectively with Legacy Code』 저자</code></p>
<blockquote>
<ul>
<li>깨끗한 코드의 특징은 많지만 그 중에서도 모두를 아우르는 특징이 하나 있다.</li>
<li>깨끗한 코드는 언제나 누군가 주의 깊게 짰다는 느낌을 준다.</li>
<li>고치려고 살펴봐도 딱히 손 댈 곳이 없다.</li>
<li>작성자가 이미 모든 사항을 고려했으므로, 고칠 궁리를 하다보면 언제나 제자리로 돌아온다.</li>
<li>그리고는 누군가 남겨준 코드, 누군가 주의 깊게 짜놓은 작품에 감사를 느낀다.</li>
</ul>
</blockquote>
<p>✔ 깨끗한 코드는 주의 깊게 작성한 코드.
✔ 즉, 누군가 시간을 들여 깔끔하고 단정하게 세세한 사항까지 꼼꼼하게 신경쓴 코드.</p>
<h3 id="🚢-론-제프리스">🚢 <code>론 제프리스</code></h3>
<p><code>『Extreme Programming Installed』와 『Extreme Programming Adventure in C#』 저자</code></p>
<blockquote>
<ul>
<li>최근들어 나는 켄트 백이 제안한 단순한 코드 규칙으로 구현을 시작한다.<br>(그리고 같은 규칙으로 구현을 거의 끝낸다.)    </li>
<li>중요한 순으로 나열하자면 간단한 코드는    <blockquote>
<ul>
<li>모든 테스트를 통과한다.</li>
<li>중복이 없다.</li>
<li>시스템 내 모든 설계 아이디어를 표현한다.</li>
<li>클래스, 메서드, 함수 등을 최대한 줄인다.</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
<p>✔ 중복이 있다는 것은 코드가 아이디어를 제대로 표현하지 못한다는 증거임.<br>✔ 표현력은 의미 있는 이름을 포함함.<br>✔ 여러 기능을 수행하는 객체나 메서드를 찾아서,    </p>
<pre><code class="language-python">    1. 객체가 여러 기능을 수행한다면 여러 객체로 나눈다.
    2. 메서드가 여러 기능을 수행한다면 메서드  추출 리팩터링 기법을 적용해
    3. 기능을 명확히 기술하는 메서드 하나와 기능을 실제로 수행하는 메서드 여러 개로 나눈다.</code></pre>
<p>🌝 결론적으로 깨끗한 코드를 만드는 비결이란 중복 줄이기, 표현력 높이기, 초반부터 간단한 추상화 고려하기!!</p>
<h3 id="🚢-워드-커닝햄">🚢 <code>워드 커닝햄</code></h3>
<p><code>위키 창시자. 피트 창시자. 익스트림 프로그래밍 공동 창시자</code></p>
<blockquote>
<ul>
<li>코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다.</li>
<li>코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드라 불러도 되겠다.</li>
</ul>
</blockquote>
<p>✔ 깨끗한 코드는 코드를 독해하느라 머리를 쥐어짤 필요가 없어야 함.
✔ 읽으면서 짐작한 대로 돌아가는 코드가 깨끗한 코드임.
✔ 코드가 그 문제를 풀기 위한 언어처럼 보인다는 것은 언어를 단순하게 보이도록 만든다는 것을 뜻하며 그것은 프로그래머에게 달려있음.</p>
<h3 id="🚢-저자-생각">🚢 <code>저자 생각</code></h3>
<p>🌞 코드를 잘 짜려면 새 코드를 짜면서 끊임없이 기존 코드를 읽어야 함.    </p>
<ul>
<li>주변 코드를 읽지 않으면 새 코드를 짤 수 없음.</li>
<li>주변 코드가 읽기 쉬우면 새 코드를 짜기도 쉬움.</li>
<li>코드를 빠르게 짜기 위해서는 코드를 읽기 쉽게 만들면 됨.<br>

</li>
</ul>
<p>🌞 보이스카우트 규칙    </p>
<ul>
<li>체크아웃할 때보다 좀 더 깨끗한 코드를 체크인한다면 코드는 절대 나빠지지 않음.</li>
<li>변수 이름 하나를 개선하고, 조금 긴 함수 하나를 분할하고, 약간의 중복을 제거하고, 복잡한 if문 하나를 정리하면 충분함.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[221003_Django Template Language]]></title>
            <link>https://velog.io/@brown_eyed87/221003django-template-language</link>
            <guid>https://velog.io/@brown_eyed87/221003django-template-language</guid>
            <pubDate>Sun, 02 Oct 2022 10:05:32 GMT</pubDate>
            <description><![CDATA[<h1 id="django-template-language"><a href="https://docs.djangoproject.com/ko/4.1/ref/templates/language/">Django Template Language</a></h1>
<ul>
<li><a href="https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#ref-templates-builtins-filters">내장 템플릿 태그 및 필터</a><br>

</li>
</ul>
<h2 id="🛴-템플릿template-언어">🛴 <code>템플릿(template) 언어</code></h2>
<ul>
<li><code>Django</code>의 템플릿 언어(<code>template language</code>)는 강력함과 편리함 사이의 균형을 잡고자 설계됨.</li>
<li>템플릿 언어를 사용하면 <code>HTML</code> 작업을 훨씬 수월하게 할 수 있음.</li>
<li><code>python</code> 변수 및 문법을 <code>html</code> 안에서 쓸 수 있도록 <code>Django</code>에서 제공해주는 언어이기 때문에 <code>html</code>과도 <code>python</code>과도 조금 다름.</li>
<li>하지만 직관적이고 사용하는데 있어서 고도의 학습량을 요구하지 않음.<br>

</li>
</ul>
<h2 id="🛴-템플릿-변수">🛴 <code>템플릿 변수</code></h2>
<ul>
<li><code>템플릿 변수</code>를 사용하면 뷰에서 템플릿으로 객체를 전달 가능.</li>
<li>사용 방법 : <code>{{ 변수 }}</code></li>
<li><code>점(.)</code> 은 <code>변수의 속성</code>에 접근할 때 사용.<br></li>
<li><code>Django</code>의 <code>view</code>에서 <code>template</code>으로 <code>context</code>를 전달할 수 있음.</li>
<li>즉, 함수(<code>CBV</code> 혹은 <code>FBV</code>)에서 <code>html</code> 문서로  어떠한 객체를 보낼 수 있다는 것.<blockquote>
<p><code>{{ section.title}}</code></p>
<ul>
<li><code>view</code>에서 <code>section</code>이라는 객체를 <code>html</code> 문서로 보냈을 것이고,</li>
<li>그 <code>section</code>이라는 객체 안에는 <code>title</code>이라는 속성을 가지고 있을 것임.</li>
</ul>
</blockquote>
</li>
</ul>
<p>🔥 <span style="color: red"> <code>Django</code>에서는 장고 <code>템플릿 변수</code>를 사용하여 <code>html</code> 문서 상에서도 어떠한 <code>객체의 속성</code>들에 <code>접근</code>할 수 있고, 이를 <code>출력</code>할 수 있도록 지원.</span>
<br></p>
<h2 id="🛴-템플릿-필터">🛴 <a href="https://django-doc-test-kor.readthedocs.io/en/old_master/ref/templates/builtins.html#ref-templates-builtins-filters">템플릿 필터</a></h2>
<ul>
<li>사용 방법 : <code>{{ 변수.속성 | 필터 }}</code></li>
<li>템플릿 변수의 값을 특정 형식으로 변환할 때 사용하고, 형식은 템플릿 변수 다음에 바(<code>|</code>)를 그은 다음 적용하고자 하는 필터를 명시.<blockquote>
<p><code>{{ story.headline | upper }}</code></p>
<ul>
<li>여기서, <code>upper</code>와 같은 것들이 바로 <code>템플릿 필터</code></li>
<li><code>upper</code>라고 명시하였으므로, <code>story.headline</code>의 값을 <u>대문자 형식으로 변환</u></li>
</ul>
</blockquote>
</li>
</ul>
<p>🔥 위의 링크에서 30개 정도의 필터 내용 확인 가능.
<br></p>
<h2 id="🛴-템플릿-태그">🛴 <a href="https://django-doc-test-kor.readthedocs.io/en/old_master/ref/templates/builtins.html#ref-templates-builtins-tags">템플릿 태그</a></h2>
<ul>
<li>사용 방법 : <code>{%  %}</code></li>
<li><code>{% load static %}</code>와 같이 사용할 수도 있고, 위의 예제에서 볼 수 있듯이 <code>if문</code> 또는 <code>for문</code>과 같이 흐름을 제어할 수 있도록 작성도 가능.<br></li>
<li><code>HTML</code> 문서는 프로그래밍 언어가 아니고 <code>마크업 언어</code>이기 때문에 단지 문서를 웹에서 띄워주는 역할을 하게 됨.</li>
<li>그렇기 때문에 <code>HTML</code> 자체는 <u>프로그래밍적 로직을 구현 불가</u><ul>
<li>그러나, <code>if문</code>, <code>for문</code>와 같은 <span style="color: red"><code>템플릿 태그</code></span>를 사용한다면 프로그래밍적 로직을 구현 가능.</li>
</ul>
</li>
<li>따라서 앞에서 배웠던 템플릿 변수와 템플릿 태그를 활용한다면 간단한 처리도 가능하게 됨.<br></li>
<li><code>{% extends %}</code>와 같이 단독으로 사용할 수 있는 템플릿 태그들도 있지만, <code>{% if %}</code> 처럼 뒤에 <code>{% endif %}</code> 템플릿 태그를 반드시 닫아주어야 하는 것들도 있음.</li>
</ul>
<p>🔥 위의 링크에서 상세 내용 확인 가능.
<br></p>
<h2 id="🛴-템플릿-코멘트">🛴 <code>템플릿 코멘트</code></h2>
<ul>
<li>말 그대로 <code>HTML</code> 문서 상에서 <u><code>코멘트</code>가 필요할 때 사용</u></li>
<li>이는 <code>주석</code>과 비슷한 역할을 하고, 이는 <code>Django</code>로 개발할 시 웹 브라우저에 의해 해석되어 출력되지 않음.<br></li>
<li><code>Django</code>에서는 두 가지의 코멘트 형식을 제공.<ul>
<li><code>1</code>번 형식은 <u>한 줄</u>을 코멘트할 때 사용</li>
<li><code>2</code>번은 <u>여러 줄</u>을 코멘트할 때 <code>코멘트 태그 사이</code>에 코멘트를 작성.<blockquote>
<ol>
<li>{# #}</li>
<li>{% comment %}
{% endcomment %}</li>
</ol>
</blockquote>
</li>
</ul>
</li>
</ul>
<h2 id="🛴-템플릿-상속">🛴 <code>템플릿 상속</code></h2>
<ul>
<li>말 그대로 <code>html</code> 문서 중 <code>기본 뼈대가 되는 문서</code>를 <code>기본 템플릿</code>으로 정하고, <u>이는 공통의 코드이므로 다른 문서에서 기본 템플릿의 코드가 필요하면 상속하여 가져다 쓰는 것</u></li>
<li><strong>기본이 되는 템플릿</strong>을 <code>base.html</code>로 지정.</li>
<li><code>extends</code>를 통해서 어떤 <code>html</code>을 상속받을지를 결정해줌.</li>
<li>다음으로 부모 <code>html</code>에 있는 <code>block``들과 대응시켜 그 안에 내용을 써주면 작성한 내용이 부모</code>html<code>의</code>block` 안으로 들어가서 화면에 출력됨.<blockquote>
<p><code>base.html</code>
<img src="https://velog.velcdn.com/images/brown_eyed87/post/7736e91c-df50-42e2-92b7-55e9cf4a3a25/image.png" alt="">
<code>list.html</code></p>
<blockquote>
<ul>
<li><code>base.html</code> 의 내용을 상속받아 가져다 씀.</li>
<li><code>block content</code>과 <code>endblock content</code> 사이의 내용을 <code>base.html</code>에 지정한 해당 영역에 넣어서 화면에 출력됨.</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/6100f38b-5136-498b-8a39-d0a7e4bc3df1/image.png" alt=""></p>
</blockquote>
</li>
</ul>
<br>
<br>

<h2 id="🏁-span-stylebackground-color-f0fcfcrefspan">🏁 <span style='background-color: #f0fcfc'>ref</span></h2>
<p><a href="https://docs.djangoproject.com/en/4.1/ref/templates/language/">Django 공식 문서</a>
<a href="https://opentutorials.org/module/4034/24665">Opentutorials.org</a>
<a href="https://coding-lks.tistory.com/129">lks의 코딩일기</a></p>
<hr>
<h1 id="django-template-language-실습"><code>Django Template Language 실습</code></h1>
<br>

<h2 id="🛴-model-설정">🛴 <code>Model 설정</code></h2>
<p><code>app/models.py</code></p>
<pre><code class="language-python">from django.db import models

class Attendance(models.Model):
    # ChoiceField 사용법 알아두기
    NONE = &quot;NO&quot;
    ATTENDANCE = &quot;AT&quot;
    ABSENT = &quot;AB&quot;
    PART = &quot;PT&quot;

    STATUS_CHOICES = [
        (NONE, &quot;미지정&quot;),
        (ATTENDANCE, &quot;출석&quot;),
        (ABSENT, &quot;결석&quot;),
        (PART, &quot;일부참여&quot;),
    ]

    name = models.CharField(verbose_name=&quot;이름&quot;, max_length=10)
    date = models.DateField(verbose_name=&quot;날짜&quot;)
    status = models.CharField(
        verbose_name=&quot;출석 여부&quot;, max_length=2, choices=STATUS_CHOICES, default=NONE
    )
    description = models.TextField(verbose_name=&quot;비고&quot;)

    def __str__(self):
        return f&quot;출석부 : {self.name} ({self.date})&quot;

    class Meta:
        verbose_name = &quot;출석부&quot;
        verbose_name_plural = &quot;출석부 목록&quot;</code></pre>
<br>

<h2 id="🛴-view-설정">🛴 <code>View 설정</code></h2>
<p><code>app/views.py</code></p>
<pre><code class="language-python">from django.shortcuts import redirect
from django.views.generic import TemplateView
from .models import Attendance, Question

# 전체 조회
class AttendanceView(TemplateView):
    template_name = &quot;attendance/list.html&quot;

    def get_context_data(self, *args, **kwargs):
        attendance_qs = Attendance.objects.all().order_by(&quot;-date&quot;)

        context = {}
        context[&quot;attendance_qs&quot;] = attendance_qs
        return context

# 특정 페이지 조회
class AttendanceCreateView(TemplateView):
    template_name = &quot;attendance/enrolled.html&quot;

    def get(self, request, **kwargs):
        response = super(AttendanceCreateView, self).get(self, request)
        return response

    def post(self, request):
        name = request.POST.get(&quot;name&quot;, None)
        date = request.POST.get(&quot;date&quot;, None)
        status = request.POST.get(&quot;status&quot;, None)
        description = request.POST.get(&quot;description&quot;, None)

        Attendance.objects.create(name=name, date=date, status=status, description=description)

        return redirect(&quot;attendance_list&quot;)</code></pre>
<br>

<h2 id="🛴-url-설정">🛴 <code>URL 설정</code></h2>
<p><code>config/urls.py</code></p>
<blockquote>
<p>이번 실습에서는 <code>app 폴더</code> 내에 <code>urls.py</code> 파일을 생성해서 분리하지 않았음.</p>
</blockquote>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path
from app.views import index, AttendanceView, AttendanceCreateView
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
    path(&quot;admin/&quot;, admin.site.urls),
    path(&quot;attendance/&quot;, AttendanceView.as_view(), name=&quot;attendance_list&quot;),
    path(&quot;attendance/create/&quot;, AttendanceCreateView.as_view(), name=&quot;attendance_create&quot;),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)</code></pre>
<br>

<h2 id="🛴-html-설정">🛴 <code>HTML 설정</code></h2>
<blockquote>
<p><code>Django Template Language</code> 사용</p>
</blockquote>
<h3 id="📄-listhtml">📄 list.html</h3>
<pre><code class="language-python">{% extends &quot;base.html&quot; %}

{% block content %}
&lt;div class=&quot;warning hidden&quot;&gt;
    &lt;h3&gt;** 지난 일정은 출석 여부를 변경할 수 없습니다.&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;table class=&quot;attendance-table&quot;&gt;
        &lt;thead&gt;
            &lt;tr&gt;
                &lt;td&gt;이름&lt;/td&gt;
                &lt;td&gt;날짜&lt;/td&gt;
                &lt;td&gt;출석여부&lt;/td&gt;
                &lt;td&gt;비고&lt;/td&gt;
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            {% for attendance in attendance_qs%}
            &lt;tr&gt;
                &lt;td class=&quot;name&quot;&gt;{{attendance.name}}&lt;/td&gt;
                &lt;td class=&quot;date&quot;&gt;{{attendance.date|date:&quot;Y.m.d&quot;}}&lt;/td&gt;
                &lt;td class=&quot;status&quot;&gt;{{attendance.get_status_display}}&lt;/td&gt;
                &lt;td class=&quot;description&quot;&gt;{{attendance.description}}&lt;/td&gt;
            &lt;/tr&gt;
            {% endfor %}
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;button type=&quot;button&quot; class=&quot;button-bg-full&quot; onclick=&quot;location.href=&#39;{% url &#39;attendance_create&#39; %}&#39;&quot;&gt;출석체크하기&lt;/button&gt;
&lt;/div&gt;
{% endblock content %}
</code></pre>
<blockquote>
<p><code>{% for attendance in attendance_qs%}</code> 관련</p>
<ul>
<li><code>attendance-qs</code><ul>
<li><code>app/views.py</code> 파일 내의 <code>AttendanceView</code> 함수에서
<code>get_context_data</code> 함수를 통해 <code>Attendance</code> 모델 규격에 맞춰 <code>db</code>에 저장된 데이터들로 만든 <code>QuerySet</code>
-<code>QuerySet</code> : <code>Database</code>에서 전달받은 객체들의 모음(<code>list</code>)</li>
</ul>
</li>
<li><code>attendance</code><ul>
<li><code>attendance-qs</code>이라고 하는 <code>QuerySet</code> 내에 있는 요소 객체<ul>
<li><code>Attendance</code> 모델에서 정의한 <code>field</code>를 본인 객체의 속성 데이터으로 가지고 있음.</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
<blockquote>
<p><code>{{attendance.name}}</code> 관련</p>
<pre><code class="language-python"># list.html
      &lt;td class=&quot;name&quot;&gt;{{attendance.name}}&lt;/td&gt;
      &lt;td class=&quot;date&quot;&gt;{{attendance.date|date:&quot;Y.m.d&quot;}}&lt;/td&gt;
      &lt;td class=&quot;status&quot;&gt;{{attendance.get_status_display}}&lt;/td&gt;
      &lt;td class=&quot;description&quot;&gt;{{attendance.description}}&lt;/td&gt;</code></pre>
<ul>
<li><p><code>attendance</code>는 위에서 설명한 <code>QuerySet</code> 내의 요소 객체 각각을 의미.</p>
</li>
<li><p><code>attendance.name</code>은 그 객체 내의 속성을 의미하며, 이는 <code>views.py</code>에서 생성한 각각의 변수를 뜻함.</p>
<pre><code class="language-python"># views.py
    def post(self, request):
       name = request.POST.get(&quot;name&quot;, None)
       date = request.POST.get(&quot;date&quot;, None)
       status = request.POST.get(&quot;status&quot;, None)
       description = request.POST.get(&quot;description&quot;, None)

       Attendance.objects.create(name=name, date=date, status=status, description=description)

       return redirect(&quot;attendance_list&quot;)</code></pre>
</li>
<li><p><code>post</code> 함수 내에서 <code>name</code>으로 선언한 변수가 바로 위의 <code>html</code> 파일 내에서 사용한 템플릿 변수 <code>attendance.name</code>의 <code>name</code>을 의미.</p>
</li>
<li><p>그리고 <code>post</code> 함수 내에서 <code>name</code> 변수에게 <code>request</code> 함수를 통해 생성한 값을 할당해주었는데, 이 함수 내의 변수 <code>&quot;name&quot;</code>은 <code>models.py</code>에서 정의한 <code>Attendance</code> 모델의 <code>field</code> 이름을 뜻함.</p>
<pre><code class="language-python"># models.py
    class Attendance(models.Model):
        NONE = &quot;NO&quot;
        ATTENDANCE = &quot;AT&quot;
        ABSENT = &quot;AB&quot;
        PART = &quot;PT&quot;

        STATUS_CHOICES = [
            (NONE, &quot;미지정&quot;),
            (ATTENDANCE, &quot;출석&quot;),
            (ABSENT, &quot;결석&quot;),
            (PART, &quot;일부참여&quot;),
        ]

        name = models.CharField(verbose_name=&quot;이름&quot;, max_length=10)
        date = models.DateField(verbose_name=&quot;날짜&quot;)
        status = models.CharField(
            verbose_name=&quot;출석 여부&quot;, max_length=2, choices=STATUS_CHOICES, default=NONE
        )
        description = models.TextField(verbose_name=&quot;비고&quot;, blank=True)

        def __str__(self):
            return f&quot;출석부 : {self.name} ({self.date})&quot;

        class Meta:
            verbose_name = &quot;출석부&quot;
            verbose_name_plural = &quot;출석부 목록&quot;</code></pre>
</li>
<li><p>현재 <code>Attendance</code> 모델은 <code>ChoiceField</code>를 사용하였는데, 이는 <code>enrolled.html</code>에서 <code>form</code>을 이용하여 자료를 입력받을 때 사용.</p>
</li>
<li><p>예를 들어, <code>ATTENDANCE = &quot;AT&quot;</code>라는 부분을 보게 되면, <code>ATTENDANCE</code>라는 변수에 <code>&quot;AT&quot;</code>라는 값을 할당해주게 되는 것이고,</p>
</li>
<li><p><code>STATUS_CHOICE</code> 리스트에서 그 <code>&quot;AT&quot;</code>라는 값과 <code>&quot;출석&quot;</code>이라는 값을 <code>tuple</code> 형태로 매칭.</p>
</li>
<li><p>또한 사용자가 화면을 통해 <code>form</code>을 작성하여 선택하여 POST 요청을 할 경우, <code>&quot;AT&quot;</code>라는 값이 서버로 넘어오게 됨.</p>
<pre><code class="language-python"># enrolled.html
    &lt;div class=&quot;form-item&quot;&gt;
        &lt;div class=&quot;title&quot;&gt;출석여부&lt;/div&gt;

        &lt;div class=&quot;radio-wrap&quot;&gt;
            &lt;input type=&quot;radio&quot; name=&quot;status&quot; value=&quot;AT&quot;&gt;
            &lt;label for=&quot;attendance&quot;&gt;출석&lt;/label&gt;
        &lt;/div&gt;
        &lt;div class=&quot;radio-wrap&quot;&gt;
            &lt;input type=&quot;radio&quot; name=&quot;status&quot; value=&quot;AB&quot;&gt;
            &lt;label for=&quot;absent&quot;&gt;결석 (사유 필히 작성)&lt;/label&gt;
        &lt;/div&gt;
        &lt;div class=&quot;radio-wrap&quot;&gt;
            &lt;input type=&quot;radio&quot; name=&quot;status&quot; value=&quot;part&quot;&gt;
            &lt;label for=&quot;complete&quot;&gt;일부 일정 불참 (사유 필히 작성)&lt;/label&gt;
    &lt;/div&gt;
  &lt;/div&gt;</code></pre>
</li>
<li><p>위에서 보시다 시피, <code>input</code> 태그 내의 <code>value</code> 값으로 해당 변수의 데이터가 동일하게 기재가 되어 있어야함.</p>
</li>
</ul>
</blockquote>
<h3 id="📄-enrolledhtml">📄 enrolled.html</h3>
<pre><code class="language-python">{% extends &#39;base.html&#39; %}

{% block content %}
&lt;div&gt;
    &lt;form method=&quot;post&quot; action=&quot;&quot;&gt;
        {% csrf_token %}
        &lt;div class=&quot;form-item&quot;&gt;
            &lt;div class=&quot;title&quot;&gt;이름&lt;/div&gt;
            &lt;input class=&quot;input-box&quot; type=&quot;text&quot; name=&quot;name&quot; value=&quot;&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;form-item&quot;&gt;
            &lt;div class=&quot;title&quot;&gt;날짜&lt;/div&gt;
            &lt;input class=&quot;&quot;input-box&quot; type=&quot;date&quot; name=&quot;date&quot; value=&quot;&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;form-item&quot;&gt;
            &lt;div class=&quot;title&quot;&gt;출석여부&lt;/div&gt;
            &lt;div class=&quot;radio-wrap&quot;&gt;
                &lt;input type=&quot;radio&quot; name=&quot;status&quot; value=&quot;AT&quot;&gt;
                &lt;label for=&quot;attendance&quot;&gt;출석&lt;/label&gt;
            &lt;/div&gt;
            &lt;div class=&quot;radio-wrap&quot;&gt;
                &lt;input type=&quot;radio&quot; name=&quot;status&quot; value=&quot;AB&quot;&gt;
                &lt;label for=&quot;absent&quot;&gt;결석 (사유 필히 작성)&lt;/label&gt;
            &lt;/div&gt;
            &lt;div class=&quot;radio-wrap&quot;&gt;
                &lt;input type=&quot;radio&quot; name=&quot;status&quot; value=&quot;PT&quot;&gt;
                &lt;label for=&quot;part&quot;&gt;일부 일정 불참 (사유 필히 작성)&lt;/label&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;form-item&quot;&gt;
            &lt;div class=&quot;title&quot;&gt;사유&lt;/div&gt;
            &lt;div class=&quot;textarea-wrap&quot;&gt;
                &lt;textarea name=&quot;description&quot; form-items=&quot;8&quot; cols=&quot;50&quot;&gt;&lt;/textarea&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class=&quot;form-item&quot;&gt;
            &lt;button class=&quot;button-bg-full&quot; type=&quot;submit&quot;&gt;제출하기&lt;/button&gt;
            &lt;button class=&quot;button-bg-empty&quot; type=&quot;button&quot; onclick=&quot;location.href=&#39;{% url &#39;attendance_list&#39; %}&#39;&quot;&gt;목록보기&lt;/button&gt;
        &lt;/div&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock content %}</code></pre>
<br>

<p><code>URL Template Tag</code> 작성 관련</p>
<blockquote>
<p><code>{% url &#39;attendance_list&#39; %}</code></p>
<ul>
<li><p><code>urls.py</code> 파일에서 <code>url name</code>이 <code>attendance_list</code>로 되어 있는 <code>url</code>로 연결해주는 경로임을 의미.</p>
<pre><code class="language-python"># urls.py
urlpatterns = [
  path(&quot;admin/&quot;, admin.site.urls),
  path(&quot;attendance/&quot;, AttendanceView.as_view(), name=&quot;attendance_list&quot;),
  path(&quot;attendance/create/&quot;, AttendanceCreateView.as_view(), name=&quot;attendance_create&quot;),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)</code></pre>
</li>
<li><p>현재 이 경우에는 웹브라우저를 통해 <code>기본 주소/attendance/</code>로 접속 시, 우리가 미리 지정한 <code>html</code> 화면을 보여주겠다는 것을 의미.</p>
</li>
<li><p>지정한 <code>html</code> 파일이 무엇인지 확인하기 위해서는 위의 경로에서 사용한 <code>View</code> 함수로 가서 확인해보면 됨.</p>
<pre><code class="language-python"># views.py
  class AttendanceView(TemplateView):
      template_name = &quot;attendance/list.html&quot;

      def get_context_data(self, *args, **kwargs):
      attendance_qs = Attendance.objects.all().order_by(&quot;-date&quot;)

      context = {}
      context[&quot;attendance_qs&quot;] = attendance_qs
      return context</code></pre>
</li>
<li><p>위의 함수 내에서 <code>template_name = &quot;attendance/list.html</code> 부분을 통해, 이 함수가 <code>template/attencance/list.html</code>의 위치에 해당하는 <code>html</code> 파일을 url에 연결해준다는 것을 알 수 있음.</p>
</li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[221003_Django 강의 복습 - FBV와 CBV 외]]></title>
            <link>https://velog.io/@brown_eyed87/221003django-%EC%88%98%EC%97%85-1%EB%B2%88%EC%A7%B8</link>
            <guid>https://velog.io/@brown_eyed87/221003django-%EC%88%98%EC%97%85-1%EB%B2%88%EC%A7%B8</guid>
            <pubDate>Sun, 02 Oct 2022 07:24:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 게시글은 <span style="color: #B8860B"><strong>김형종 강사님의 <code>Django 웹개발</code> 강의</strong></span>를 듣고 작성하였습니다.</p>
</blockquote>
<pre><code class="language-python">&gt; Django 프로젝트 관련 정보
       - 프로젝트 관리 폴더 이름 : `config`
       - 프로젝트 내에서 실습 시 생성한 앱의 이름 : `board`</code></pre>
<h1 id="view를-생성하는-2가지-방법"><code>View를 생성하는 2가지 방법</code></h1>
<h2 id="🛴-fbv-function-based-view">🛴 <code>FBV (Function Based View)</code></h2>
<p><code>board/views.py</code></p>
<pre><code class="language-python">        def index_function(request):

            # GET
            if request.method == &quot;GET&quot;:
                return HttpResponse(&quot;index by GET function called&quot;)

            # POST
            if request.method == &quot;POST&quot;:
                return HttpResponse(&quot;index by POST function called&quot;)</code></pre>
<ul>
<li><code>view</code> : <code>request</code>를 받아서 <code>response</code>를 보내는 함수</li>
<li><code>url</code> : <code>Browser</code>에서 화면을 출력하는 기준</li>
<li><code>views.py</code>에서 <code>view function</code>을 생성 후, <code>urls.py</code>에 해당 <code>view</code>를 통해 <code>response</code>를 화면에 출력할 <code>url</code>을 연결해줘야 함.<br>

</li>
</ul>
<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import index_function

        urlpatterns = [
            # 127.0.0.1:8000/admin/ 으로 접속 시, admin page 나옴
            path(&quot;admin/&quot;, admin.site.urls),
            # 기본 주소 접속 시 페이지 설정
            path(&quot;&quot;, index, name=&quot;index&quot;),
            # &#39;board&#39; App 관련 urls
            # 127.0.0.1:8000/fbv/ 로 접속 시, views.py에서 생성한 index_function 을 통해 처리한 방법대로 화면이 나옴
            path(&quot;fbv/&quot;, index_function, name=&quot;index_function&quot;),   ◀◀◀ view에서 생성한 FBV 함수를 url로 연결해주는 코드
        ]</code></pre>
<br>

<p><code>browser</code></p>
<p>🔥 <code>path</code> 내에서 지정한 <span style="color: red"><code>name</code></span>은 <code>template</code> 폴더 내 <code>html</code> 파일에서 <code>django 템플릿 언어</code>를 사용하여 링크 연결 시 사용하게 되는 점을 꼭 기억할 것!!!</p>
<ul>
<li>위와 같이 코드 작성 후, 서버를 실행한 다음 해당 <code>url</code>로 접속하면 아래와 같이 나옴.</li>
</ul>
<blockquote>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/3eebc1dc-b5f2-477a-bced-da7d038b192a/image.png" alt=""></p>
</blockquote>
<br>

<h2 id="🛴-cbv-class-based-view">🛴 <code>CBV (Class Based View)</code></h2>
<p><code>board/views.py</code></p>
<pre><code class="language-python">        class IndexClass(View):

            # GET
            def get(self, request):
                return HttpResponse(&quot;index by GET class called&quot;)

            # POST
            def post(self, request):
                return HttpResponse(&quot;index by POST class called&quot;)</code></pre>
<ul>
<li>대표적인 <code>HTTP Methods</code> : <code>GET</code> &amp; <code>post</code></li>
<li><code>CBV</code>의 경우, 요청받은 <code>method</code>에 따라 <code>분기문</code>으로 나누는 <code>FBV</code>와 비교하였을 때 직관적이고 코드가 깔끔하기 때문에 관리하기에 더 용이함.</li>
<li><code>Django</code>는 우리가 원하는 서비스의 <code>logic</code>을 <code>views.py</code>에 모두 작성해야 하는 구조로 되어 있음.</li>
<li>그렇기 때문에 일반적으로 렌더링해야 하는 화면이 많고 <code>logic</code>이 많은 상황에서는 <code>FBV</code>보다는 <code>CBV</code>를 더 선호함.<br>

</li>
</ul>
<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import IndexClass

        urlpatterns = [
            # 127.0.0.1:8000/cbv/ 로 접속 시, views.py에서 생성한 index_function 을 통해 처리한 방법대로 화면이 나옴
            path(&quot;cbv/&quot;, IndexClass.as_view(), name=&quot;index_class&quot;),   ◀◀◀ view에서 생성한 CBV 함수를 url로 연결해주는 코드
        ]</code></pre>
<br>

<p><code>browser</code></p>
<ul>
<li>위와 같이 코드 작성 후, 서버를 실행한 다음 해당 <code>url</code>로 접속하면 아래와 같이 나옴.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/d3729623-8fe5-451a-ae13-6d84bdcadaff/image.png" alt=""></p>
<br>

<h1 id="html-연결하기">HTML 연결하기</h1>
<blockquote>
<p>여러 종류의 데이터를 화면에 보여주기</p>
</blockquote>
<h2 id="🛴-fbv---html을-url과-연결">🛴 <code>FBV - html을 url과 연결</code></h2>
<p><code>board/views.py</code></p>
<pre><code class="language-python">      from django.shortcuts import HttpResponse, render

      # 단순 텍스트가 아닌 html을 url과 연결하여 화면에 보여줄 때 사용
      def index_function2(request):

          # GET
          if request.method == &quot;GET&quot;:
              return render(request, &quot;practice/index_f2.html&quot;, {})

          # POST
          if request.method == &quot;POST&quot;:
              return HttpResponse(&quot;index by POST function2 called&quot;)</code></pre>
<blockquote>
<p>🔥 <code>render</code>는 결국 <code>HttpResponse</code>를 <code>return</code> 하는 것임</p>
<ul>
<li><code>render</code> 클래스 구성
<img src="https://velog.velcdn.com/images/brown_eyed87/post/2957e996-58c9-40ce-aada-1e637932c19d/image.png" alt=""></li>
</ul>
</blockquote>
<br>

<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import index_function2

        urlpatterns = [
            path(&quot;fbv2/&quot;, index_function2, name=&quot;index_function2&quot;),
        ]</code></pre>
<br>

<p><code>templates/practice/index_f2.html</code></p>
<pre><code class="language-html">        &lt;h1&gt;Hard Practice&lt;/h1&gt;
        &lt;h3&gt;test - &#39;index_function2&#39;&lt;/h3&gt;</code></pre>
<br>

<p><code>browser</code></p>
<ul>
<li>위와 같이 코드 작성 후, 서버를 실행한 다음 해당 <code>url</code>로 접속하면 아래와 같이 나옴.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/8b9a6dc6-6416-411e-adc0-954af7903abe/image.png" alt=""></p>
<br>

<h2 id="🛴-fbv---html을-url과-연결---context-활용">🛴 <code>FBV - html을 url과 연결 - context 활용</code></h2>
<blockquote>
<ul>
<li><code>context</code>를 이용하면 <code>context</code> 내에 지정한 <code>data</code>를 우리가 연결하고자 하는 <code>html</code> 내에 삽입하여 화면에 보여줄 수 있음.</li>
<li><code>context</code>는 <code>key : value</code> 형태의 <code>dictionary</code> 자료구조</li>
<li><code>context</code> 내의 <code>key</code>를 그 이름 그대로 <code>html</code>에 <code>템플릿 변수</code>로 넣어주면 됨.</li>
</ul>
</blockquote>
<p><code>board/views.py</code></p>
<pre><code class="language-python">      from django.shortcuts import HttpResponse, render

      def index_function3(request):

          # GET
          if request.method == &quot;GET&quot;:
              # context는 dictionary 형태(key : value)로 되어 있는 우리의 data를 전달함.
              #   웹페이지라는 것은 결국은 어떠한 정보를 전달하기 위한 페이지이고,
              #   그런 데이터를 우리는 context를 이용하여 전달함.
              context = {
                  &quot;method&quot;: request.method,
                  &quot;user&quot;: request.user,
                  &quot;temp&quot;: &quot;welcome to my site!!&quot;,
              }
              return render(request, &quot;practice/index_f3.html&quot;, context)

          # POST
          if request.method == &quot;POST&quot;:
              return HttpResponse(&quot;index by POST function3 called&quot;)</code></pre>
<br>

<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import index_function3

        urlpatterns = [
            path(&quot;fbv3/&quot;, index_function3, name=&quot;index_function3&quot;),
        ]</code></pre>
<br>

<p><code>templates/practice/index_f3.html</code></p>
<pre><code class="language-html">        &lt;h1&gt;Hard Practice&lt;/h1&gt;
        &lt;h3&gt;test - &#39;index_function3&#39;&lt;/h3&gt;
        &lt;br&gt;
        &lt;br&gt;
        &lt;h4&gt;{{method}}&lt;/h4&gt;
        &lt;h4&gt;{{user}}&lt;/h4&gt;
        &lt;h4&gt;{{temp}}&lt;/h4&gt;</code></pre>
<br>

<p><code>browser</code></p>
<ul>
<li>위와 같이 코드 작성 후, 서버를 실행한 다음 해당 <code>url</code>로 접속하면 아래와 같이 나옴.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/79c832d5-a47a-4d51-8156-d1ee332e8036/image.png" alt=""></p>
<br>

<h2 id="🛴-fbv---url-path-지정">🛴 <code>FBV - url path 지정</code></h2>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/7b143f7f-75bb-40c0-a1fe-8a6ab8fe1a1a/image.png" alt=""></p>
<blockquote>
<ul>
<li><code>context</code> 내의 <code>key</code>, <code>value</code>를 이용하여 <code>url</code>의 <code>path</code> 지정 가능</li>
</ul>
</blockquote>
<p><code>board/views.py</code></p>
<pre><code class="language-python">      from django.shortcuts import HttpResponse, render

      def index_function4(request, name, code):

          # GET
          if request.method == &quot;GET&quot;:
              context = {
                  &quot;method&quot;: request.method,
                  &quot;user&quot;: request.user,
                  &quot;temp&quot;: &quot;welcome to my site!!&quot;,
                  &quot;name&quot;: name,
                  &quot;code&quot;: code,
              }
              return render(request, &quot;practice/index_f4.html&quot;, context)

          # POST
          if request.method == &quot;POST&quot;:
              return HttpResponse(&quot;index by POST function4 called&quot;)</code></pre>
<br>

<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import index_function4

        urlpatterns = [
            # main path와 sub path 지정 시, &lt;str:변수명&gt; 으로 코드 작성
            path(&quot;fbv4/&lt;str:name&gt;/&lt;str:code&gt;/&quot;, index_function4, name=&quot;index_function4&quot;),
        ]</code></pre>
<br>

<p><code>templates/practice/index_f4.html</code></p>
<pre><code class="language-html">        &lt;h1&gt;Hard Practice&lt;/h1&gt;
        &lt;h3&gt;test - &#39;index_function4&#39;&lt;/h3&gt;
        &lt;br&gt;
        &lt;br&gt;
        &lt;h4&gt;{{method}}&lt;/h4&gt;
        &lt;h4&gt;{{user}}&lt;/h4&gt;
        &lt;h4&gt;{{temp}}&lt;/h4&gt;
        &lt;h4&gt;{{name}}&lt;/h4&gt;
        &lt;h4&gt;{{code}}&lt;/h4&gt;</code></pre>
<br>

<p><code>browser</code></p>
<ul>
<li>위와 같이 코드 작성 후, 서버를 실행한 다음 해당 <code>url</code>로 접속하면 아래와 같이 나옴.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/a5fb2bde-7f41-4ad0-8913-1daf878df55a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/e5a82aec-b311-4bc6-8dc2-505cb90447f2/image.png" alt=""></p>
<br>

<h2 id="🛴-cbv---html을-url과-연결">🛴 <code>CBV - html을 url과 연결</code></h2>
<p><code>board/views.py</code></p>
<pre><code class="language-python">      from django.shortcuts import HttpResponse, render
      from django.views.generic import TemplateView

      # CBV로 html을 url과 연결시키고자 할 경우에는 TemplateView를 상속받아 class를 선언
      class IndexClass2(TemplateView):
          # 명시적으로 template_name을 선언
          template_name = &quot;practice/index_c2.html&quot;

          # GET
          def get(self, request):
              response = super(IndexClass2, self).get(self, request)
              return response

          # POST
          def post(self, request):
              return HttpResponse(&quot;index by POST class2 called&quot;)</code></pre>
<br>

<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import IndexClass2

        urlpatterns = [
            path(&quot;cbv2/&quot;, IndexClass2.as_view(), name=&quot;index_class2&quot;),
        ]</code></pre>
<br>

<p><code>templates/practice/index_f2.html</code></p>
<pre><code class="language-html">        &lt;h1&gt;Hard Practice&lt;/h1&gt;
        &lt;h3&gt;test - &#39;index_class2&#39;&lt;/h3&gt;
        &lt;br&gt;
        &lt;br&gt;
        &lt;h4&gt;{{method}}&lt;/h4&gt;
        &lt;h4&gt;{{user}}&lt;/h4&gt;
        &lt;h4&gt;{{temp}}&lt;/h4&gt;
        &lt;h4&gt;{{name}}&lt;/h4&gt;
        &lt;h4&gt;{{code}}&lt;/h4&gt;</code></pre>
<br>

<p><code>browser</code></p>
<ul>
<li>위와 같이 코드 작성 후, 서버를 실행한 다음 해당 <code>url</code>로 접속하면 아래와 같이 나옴.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/25024fa0-b110-4f8f-8f2e-21bc94b5872f/image.png" alt=""></p>
<br>

<h2 id="🛴-fbv---query-string-사용">🛴 <code>FBV - query string 사용</code></h2>
<blockquote>
<ul>
<li>주소 뒤에 <code>?query=검색어</code>를 붙여 접속</li>
<li>해당 query는 `request.GET.get(&#39;query&#39;, default) 형태로 작성</li>
</ul>
</blockquote>
<p><code>board/views.py</code></p>
<pre><code class="language-python">      from django.shortcuts import HttpResponse, render

      # context 내의 일부 인자는 query string으로 사용하는 방법
      def index_function5(request, name):

          # GET
          if request.method == &quot;GET&quot;:
              return render(
                  request,
                  &quot;practice/index_f5.html&quot;,
                  {
                      &quot;method&quot;: request.method,
                      &quot;user&quot;: request.user,
                      &quot;temp&quot;: &quot;welcome to my site!!&quot;,
                      &quot;name&quot;: name,
                      # query로 code를 사용
                      &quot;code&quot;: request.GET.get(&quot;code&quot;),
                  },
              )

          # POST
          if request.method == &quot;POST&quot;:
              return HttpResponse(&quot;index by POST function5 called&quot;)</code></pre>
<br>

<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import index_function5

        urlpatterns = [
            path(&quot;fbv5/&lt;str:name&gt;/&quot;, index_function5, name=&quot;index_function5&quot;),
        ]</code></pre>
<br>

<p><code>templates/practice/index_f5.html</code></p>
<pre><code class="language-html">        &lt;h1&gt;Hard Practice&lt;/h1&gt;
        &lt;h3&gt;test - &#39;index_function5&#39;&lt;/h3&gt;
        &lt;br&gt;
        &lt;br&gt;
        &lt;h4&gt;{{method}}&lt;/h4&gt;
        &lt;h4&gt;{{user}}&lt;/h4&gt;
        &lt;h4&gt;{{temp}}&lt;/h4&gt;
        &lt;h4&gt;{{name}}&lt;/h4&gt;
        &lt;h4&gt;{{code}}&lt;/h4&gt;</code></pre>
<br>

<p><code>browser</code></p>
<ul>
<li>위와 같이 코드 작성 후, 서버를 실행한 다음과 같이 <code>url</code>을 입력하여 접속하면 아래와 같이 나옴.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/776338be-bb6d-4c3a-ac1f-5f0dc87adbb9/image.png" alt=""></p>
<h1 id="csrf-token">CSRF TOKEN</h1>
<h2 id="🛴-csrfcross-site-request-forgery">🛴 <code>CSRF(Cross Site Request Forgery)</code></h2>
<blockquote>
<ul>
<li>정상적인 사용자의 의도와는 무관하게, 공격자가 의도한 행위(수정, 삭제, 등록 등)로 웹사이트에 요청하는 것</li>
</ul>
</blockquote>
<p>🚧 <code>Django CSRF</code></p>
<blockquote>
<ol>
<li>클라이언트 요청이 발생시, <code>Django</code>는 자동으로 <code>csrf_token</code>을 클라이언트에게 전송</li>
<li>클라이언트는 <code>form data</code>를 <code>csrf_token</code>과 함께 <code>POST</code>로 전송</li>
<li>전송받은 <code>csrf_token</code>의 유효성을 검증하여 유효한 경우 요청을 처리.</li>
<li>검증 오류(유효하지 않거나, 존재하지 않음)시, <code>403 Forbidden Response</code>반환</li>
</ol>
</blockquote>
<p>🔥 <code>Django</code>는 <code>POST</code> 동작에서 <code>csrf_token</code>을 제공</p>
<blockquote>
<p><code>CSRF</code>를 예외로 처리하기보다, 로직을 점검 및 구현하는 것이 좋습니다.</p>
</blockquote>
<br>

<h3 id="🌍-csrf-예외처리">🌍 <code>CSRF 예외처리</code></h3>
<blockquote>
<p><code>csrf_exempt</code> 함수를 <code>decorator</code>로 사용하면 됨.</p>
</blockquote>
<p><code>board/views.py</code></p>
<pre><code class="language-python">      from django.shortcuts import HttpResponse, render
      from django.views.decorators.csrf import csrf_exempt

      # FBV 위에  csrf_exempt를 decorator로 사용
      @csrf_exempt
      def index_function2(request):

          # GET
          if request.method == &quot;GET&quot;:
              return render(request, &quot;practice/index_f2.html&quot;, {})

          # POST
          if request.method == &quot;POST&quot;:
              return HttpResponse(&quot;index by POST function2 called&quot;)</code></pre>
<br>

<p><code>config/urls.py</code></p>
<pre><code class="language-python">        from django.contrib import admin
        from board.views import index_function2

        urlpatterns = [
            path(&quot;fbv2/&quot;, index_function2, name=&quot;index_function2&quot;),
        ]</code></pre>
<br>

<p><code>templates/practice/index_f2.html</code></p>
<pre><code class="language-html">        &lt;h1&gt;Hard Practice&lt;/h1&gt;
        &lt;h3&gt;test - &#39;index_function2&#39;&lt;/h3&gt;</code></pre>
<br>

<p><code>Postman</code></p>
<ul>
<li><code>Postman</code> 프로그램을 사용하여 해당 링크로 <code>post</code> 요청을 보내면 아래와 같이 정상적인 결과를 <code>return</code>받게 됨을 확인 가능.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/1b0bf075-df83-4868-ae64-e2acd3307d51/image.png" alt=""></p>
<ul>
<li>만약, 위의 <code>csrf_exempt</code> 데코레이터를 제거하게 되면 아래와 같이 에러 메세지가 출력됨.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/f4850c79-fde9-4830-97fa-47ed2dcc8253/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[221001_Programmers : 해시]]></title>
            <link>https://velog.io/@brown_eyed87/221001Programmers-%ED%95%B4%EC%8B%9C</link>
            <guid>https://velog.io/@brown_eyed87/221001Programmers-%ED%95%B4%EC%8B%9C</guid>
            <pubDate>Sat, 01 Oct 2022 14:17:37 GMT</pubDate>
            <description><![CDATA[<h1 id="💰-고득점-kit--hash">💰 <a href="https://school.programmers.co.kr/learn/courses/30/parts/12077">고득점 Kit : Hash</a></h1>
<br>

<hr>
<h2 id="🌞-폰켓몬">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/1845">폰켓몬</a></h2>
<h3 id="🚊-문제-설명">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. </li>
<li>홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋다고 했습니다.</li>
<li>홍 박사님 연구실의 폰켓몬은 종류에 따라 번호를 붙여 구분합니다. </li>
<li>따라서 같은 종류의 폰켓몬은 같은 번호를 가지고 있습니다.</li>
<li>예를 들어 연구실에 총 4마리의 폰켓몬이 있고, 각 폰켓몬의 종류 번호가 <code>[3번, 1번, 2번, 3번]</code>이라면 이는 3번 폰켓몬 두 마리, 1번 폰켓몬 한 마리, 2번 폰켓몬 한 마리가 있음을 나타냅니다.</li>
<li>이때, 4마리의 폰켓몬 중 2마리를 고르는 방법은 다음과 같이 6가지가 있습니다.<blockquote>
<ul>
<li>첫 번째(3번), 두 번째(1번) 폰켓몬을 선택</li>
<li>첫 번째(3번), 세 번째(2번) 폰켓몬을 선택</li>
<li>첫 번째(3번), 네 번째(3번) 폰켓몬을 선택</li>
<li>두 번째(1번), 세 번째(2번) 폰켓몬을 선택</li>
<li>두 번째(1번), 네 번째(3번) 폰켓몬을 선택</li>
<li>세 번째(2번), 네 번째(3번) 폰켓몬을 선택</li>
</ul>
</blockquote>
</li>
<li>이때, 첫 번째(3번) 폰켓몬과 네 번째(3번) 폰켓몬을 선택하는 방법은 한 종류(3번 폰켓몬 두 마리)의 폰켓몬만 가질 수 있지만, 다른 방법들은 모두 두 종류의 폰켓몬을 가질 수 있습니다.</li>
<li>따라서 위 예시에서 가질 수 있는 폰켓몬 종류 수의 최댓값은 2가 됩니다.</li>
</ul>
</blockquote>
<ul>
<li>당신은 최대한 다양한 종류의 폰켓몬을 가지길 원하기 때문에, 최대한 많은 종류의 폰켓몬을 포함해서 N/2마리를 선택하려 합니다.</li>
<li>N마리 폰켓몬의 종류 번호가 담긴 배열 nums가 매개변수로 주어질 때, N/2마리의 폰켓몬을 선택하는 방법 중, 가장 많은 종류의 폰켓몬을 선택하는 방법을 찾아, 그때의 폰켓몬 종류 번호의 개수를 return 하도록 solution 함수를 완성해주세요.</li>
</ul>
<h3 id="🚊-입출력-예">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/be82f7e9-6b49-4aa7-8ccc-b25b42ba7b43/image.png" alt=""></p>
<p><code>입출력 예 #1</code></p>
<ul>
<li>문제의 예시와 같습니다.</li>
</ul>
<p><code>입출력 예 #2</code></p>
<ul>
<li>6마리의 폰켓몬이 있으므로, 3마리의 폰켓몬을 골라야 합니다.</li>
<li>가장 많은 종류의 폰켓몬을 고르기 위해서는 3번 폰켓몬 한 마리, 2번 폰켓몬 한 마리, 4번 폰켓몬 한 마리를 고르면 되며, 따라서 3을 return 합니다.</li>
</ul>
<p><code>입출력 예 #3</code></p>
<ul>
<li>6마리의 폰켓몬이 있으므로, 3마리의 폰켓몬을 골라야 합니다.</li>
<li>가장 많은 종류의 폰켓몬을 고르기 위해서는 3번 폰켓몬 한 마리와 2번 폰켓몬 두 마리를 고르거나, 혹은 3번 폰켓몬 두 마리와 2번 폰켓몬 한 마리를 고르면 됩니다.</li>
<li>따라서 최대 고를 수 있는 폰켓몬 종류의 수는 2입니다.</li>
</ul>
<h3 id="📑-code--내꺼">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>내가 가져갈 수 있는 마리 수 : <code>N/2</code> 계산</li>
<li>현재 폰켓몬의 종류의 수 <code>X</code> 계산</li>
<li><code>X</code>와 <code>N/2</code>의 크기를 비교하여 결과값 계산</li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code

    def solution(ls):
        return min(len(ls)//2, len(set(ls)))</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼">📑 <code>Code : 다른 사람꺼</code></h3>
<pre><code class="language-python">&gt; code

    없음</code></pre>
<br>

<hr>
<h2 id="🌞-완주하지-못한-선수">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42576">완주하지 못한 선수</a></h2>
<h3 id="🚊-문제-설명-1">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>수많은 마라톤 선수들이 마라톤에 참여하였습니다.</li>
<li>단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.<br></li>
<li>마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.</li>
</ul>
</blockquote>
<h3 id="🚊-제한-사항">🚊 <code>제한 사항</code></h3>
<blockquote>
<ul>
<li>마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.</li>
<li>completion의 길이는 participant의 길이보다 1 작습니다.</li>
<li>참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.</li>
<li>참가자 중에는 동명이인이 있을 수 있습니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-1">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/5eefb522-c754-41a4-aef9-a4b163ea9656/image.png" alt=""></p>
<p><code>입출력 예 #1</code></p>
<ul>
<li><code>leo</code>는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.</li>
</ul>
<p><code>입출력 예 #2</code></p>
<ul>
<li><code>vinko</code>는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.</li>
</ul>
<p><code>입출력 예 #3</code></p>
<ul>
<li><code>mislav</code>는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.</li>
</ul>
<h3 id="📑-code--내꺼-1">📑 <code>Code : 내꺼</code></h3>
<pre><code class="language-python">&gt; code 1

    def solution(participant, completion):
        # 동명이인을 고려하여 참가자와 완주자 list를 각각 정렬
        participant.sort()
        completion.sort()

        # 반복문을 통해
        for i in range(len(completion)):
            # 앞에서부터 순서대로 각 list 요소를 비교
            #   일치하지 않는 경우가 바로 완주하지 못한 선수
            if participant[i] != completion[i]:
                return participant[i]

        # 이 코드는 완주하지 못한 선수가 여러 명일 때에는 `error` 발생할 것임.
        # 이 문제에서는 상관 없음.

&gt; code 2

    def solution(participant, completion):
        # `참가자 이름 : 해당 이름의 참가자 수`의 형태로 사용할 빈 dict 생성
        dic = {}
        # 참가자 리스트를 순회하면서 dic에서 참가자 이름별 참가자 수를 하나씩 카운트
        for i in participant:
            dic[i] = dic.get(i, 0) + 1
        # 완주자 리스트를 순회하면서 dic에서 완주자 이름에 해당하는 참가자 수를 하나씩 제거
        for j in completion:
            dic[j] -= 1
        # 참가자 리스트를 순회하면서 dic에서 참가자 수가 남아 있는 참가자 이름을 추출
        #   완주하지 못한 사람만이 참가자 수가 남아있기 때문
        for k in participant:
            if dic[k] != 0:
                return k</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼-1">📑 <code>Code : 다른 사람꺼</code></h3>
<pre><code class="language-python">&gt; code

    from collections import Counter


    def solution(participant, completion):
        answer = Counter(participant) - Counter(completion)
        return list(answer.keys())[0]</code></pre>
<br>

<hr>
<h2 id="🌞-전화번호-목록">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42577">전화번호 목록</a></h2>
<h3 id="🚊-문제-설명-2">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>전화번호부에 적힌 전화번호 중, 한 번호가 다른 번호의 접두어인 경우가 있는지 확인하려 합니다.</li>
<li>전화번호가 다음과 같을 경우, 구조대 전화번호는 영석이의 전화번호의 접두사입니다.<blockquote>
<ul>
<li>구조대 : 119</li>
<li>박준영 : 97 674 223</li>
<li>지영석 : 11 9552 4421</li>
</ul>
</blockquote>
</li>
<li>전화번호부에 적힌 전화번호를 담은 배열 phone_book 이 solution 함수의 매개변수로 주어질 때, 어떤 번호가 다른 번호의 접두어인 경우가 있으면 false를 그렇지 않으면 true를 return 하도록 solution 함수를 작성해주세요.</li>
</ul>
</blockquote>
<h3 id="🚊-제한-사항-1">🚊 <code>제한 사항</code></h3>
<blockquote>
<ul>
<li>phone_book의 길이는 1 이상 1,000,000 이하입니다.<ul>
<li>각 전화번호의 길이는 1 이상 20 이하입니다.</li>
<li>같은 전화번호가 중복해서 들어있지 않습니다.</li>
</ul>
</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-2">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/b61e0031-43c6-4580-acc1-f5ed68b7ee66/image.png" alt=""></p>
<p><code>입출력 예 #1</code></p>
<ul>
<li>앞에서 설명한 예와 같습니다.</li>
</ul>
<p><code>입출력 예 #2</code></p>
<ul>
<li>한 번호가 다른 번호의 접두사인 경우가 없으므로, 답은 true입니다.</li>
</ul>
<p><code>입출력 예 #3</code></p>
<ul>
<li>첫 번째 전화번호, “12”가 두 번째 전화번호 “123”의 접두사입니다. 따라서 답은 false입니다.</li>
</ul>
<h3 id="📑-code--내꺼-2">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ul>
<li>전화번호부를 정렬하게 되면, 인접한 2개씩만 비교하면 한 번호가 다른 번호의 접두어인지 바로 확인 가능</li>
</ul>
</blockquote>
<pre><code class="language-python">&gt; code

    def solution(phone_book):
        phone_book.sort()
        length = len(phone_book)
        for i in range(length-1):
            if phone_book[i] == phone_book[i+1][:len(phone_book[i])]:
                return False
        return True</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼-2">📑 <code>Code : 다른 사람꺼</code></h3>
<pre><code class="language-python">&gt; code

    def solution(phoneBook):
    phoneBook = sorted(phoneBook)

    for p1, p2 in zip(phoneBook, phoneBook[1:]):
        if p2.startswith(p1):
            return False
    return True</code></pre>
<br>

<hr>
<h2 id="🌞-위장">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42578">위장</a></h2>
<h3 id="🚊-문제-설명-3">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>스파이들은 매일 다른 옷을 조합하여 입어 자신을 위장합니다.</li>
<li>예를 들어 스파이가 가진 옷이 아래와 같고 오늘 스파이가 동그란 안경, 긴 코트, 파란색 티셔츠를 입었다면 다음날은 청바지를 추가로 입거나 동그란 안경 대신 검정 선글라스를 착용하거나 해야 합니다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/d3a23c1a-c680-455f-aaf9-8e74aa919ae9/image.png" alt=""></li>
<li>스파이가 가진 의상들이 담긴 2차원 배열 clothes가 주어질 때 서로 다른 옷의 조합의 수를 return 하도록 solution 함수를 작성해주세요.</li>
</ul>
</blockquote>
<h3 id="🚊-제한-사항-2">🚊 <code>제한 사항</code></h3>
<blockquote>
<ul>
<li>clothes의 각 행은 [의상의 이름, 의상의 종류]로 이루어져 있습니다.</li>
<li>스파이가 가진 의상의 수는 1개 이상 30개 이하입니다.</li>
<li>같은 이름을 가진 의상은 존재하지 않습니다.</li>
<li>clothes의 모든 원소는 문자열로 이루어져 있습니다.</li>
<li>모든 문자열의 길이는 1 이상 20 이하인 자연수이고 알파벳 소문자 또는 &#39;_&#39; 로만 이루어져 있습니다.</li>
<li>스파이는 하루에 최소 한 개의 의상은 입습니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-3">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/78bd834a-3aaa-42cd-81c6-01bf937f9164/image.png" alt=""></p>
<p><code>입출력 예 #1</code></p>
<ul>
<li>headgear에 해당하는 의상이 yellow_hat, green_turban이고 eyewear에 해당하는 의상이 blue_sunglasses이므로 아래와 같이 5개의 조합이 가능합니다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/4f2d925b-c959-4f9e-8194-65c8de53aa6a/image.png" alt=""></li>
</ul>
<p><code>입출력 예 #2</code></p>
<ul>
<li>face에 해당하는 의상이 crow_mask, blue_sunglasses, smoky_makeup이므로 아래와 같이 3개의 조합이 가능합니다.
<img src="https://velog.velcdn.com/images/brown_eyed87/post/5fb3438f-dc71-460a-823b-2667bd40b6fb/image.png" alt=""></li>
</ul>
<h3 id="📑-code--내꺼-3">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>옷의 종류별 개수를 <code>dict</code>로 표현</li>
<li>각각의 종류의 옷에 대해 입을 수 있는 경우의 수는 총 (해당 종류의 옷의 개수 + 1)
→ 입지 않는 경우 1가지까지 포함시켜야 하므로</li>
<li>옷을 조합할 수 있는 전체 경우의 수는 위에서 구한 각 종류의 옷들에 대한 경우의 수를 모두 곱한 값에서 전부 입지 않는 경우 1가지를 제외하면 됨.</li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code

    import math

    def solution(clothes):
        answer = {}
        for p in clothes:
            answer[p[1]] = answer.get(p[1], 0) + 1
        array = answer.values()
        array = [i+1 for i in array]
        return math.prod(array) -1</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼-3">📑 <code>Code : 다른 사람꺼</code></h3>
<pre><code class="language-python">&gt; code

    없음</code></pre>
<br>

<hr>
<h2 id="🌞-베스트앨범">🌞 <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42579">베스트앨범</a></h2>
<h3 id="🚊-문제-설명-4">🚊 <code>문제 설명</code></h3>
<blockquote>
<ul>
<li>스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다.</li>
<li>노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다.<blockquote>
<ol>
<li>속한 노래가 많이 재생된 장르를 먼저 수록합니다.</li>
<li>장르 내에서 많이 재생된 노래를 먼저 수록합니다.</li>
<li>장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.</li>
</ol>
</blockquote>
</li>
</ul>
<ul>
<li>노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 plays가 주어질 때, 베스트 앨범에 들어갈 노래의 고유 번호를 순서대로 return 하도록 solution 함수를 완성하세요.</li>
</ul>
</blockquote>
<h3 id="🚊-제한-사항-3">🚊 <code>제한 사항</code></h3>
<blockquote>
<ul>
<li>genres[i]는 고유번호가 i인 노래의 장르입니다.</li>
<li>plays[i]는 고유번호가 i인 노래가 재생된 횟수입니다.</li>
<li>genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.</li>
<li>장르 종류는 100개 미만입니다.</li>
<li>장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.</li>
<li>모든 장르는 재생된 횟수가 다릅니다.</li>
</ul>
</blockquote>
<h3 id="🚊-입출력-예-4">🚊 <code>입출력 예</code></h3>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/0fe3ab4d-8cd3-44f0-a433-31f3fb22cc28/image.png" alt=""></p>
<ol>
<li><p>classic 장르는 1,450회 재생되었으며, classic 노래는 다음과 같습니다.</p>
<blockquote>
<ul>
<li>고유 번호 3: 800회 재생</li>
<li>고유 번호 0: 500회 재생</li>
<li>고유 번호 2: 150회 재생</li>
</ul>
</blockquote>
</li>
<li><p>pop 장르는 3,100회 재생되었으며, pop 노래는 다음과 같습니다.</p>
<blockquote>
<ul>
<li>고유 번호 4: 2,500회 재생</li>
<li>고유 번호 1: 600회 재생</li>
</ul>
</blockquote>
</li>
<li><p>따라서 pop 장르의 [4, 1]번 노래를 먼저, classic 장르의 [3, 0]번 노래를 그다음에 수록합니다.</p>
<blockquote>
<ul>
<li>장르 별로 가장 많이 재생된 노래를 최대 두 개까지 모아 베스트 앨범을 출시하므로 2번 노래는 수록되지 않습니다.</li>
</ul>
</blockquote>
</li>
</ol>
<h3 id="📑-code--내꺼-4">📑 <code>Code : 내꺼</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ol>
<li>내가 가져갈 수 있는 마리 수 : <code>N/2</code> 계산</li>
<li>현재 폰켓몬의 종류의 수 <code>X</code> 계산</li>
<li><code>X</code>와 <code>N/2</code>의 크기를 비교하여 결과값 계산</li>
</ol>
</blockquote>
<pre><code class="language-python">&gt; code

    def solution(genres, plays):

        # dic1 → &#39;장르 : 재생곡 합계&quot; 를 담은 dict 형식으로 만듦
        length = len(genres)
        dic1 = {}
        for i in range(length):
            dic1[genres[i]] = dic1.get(genres[i], 0) + plays[i]
        print(dic1)

        # dic2 → &#39;장르 : (재생곡, 재생곡의 idx)&#39; 형태의 tuple을 담은 dict로 만듦.
        dic2 = {j:[] for j in genres}
        for k in range(length):
            dic2[genres[k]].append([plays[k],k])
        print(dic2)

        # dic3 → &#39;장르별 재생곡 합계 : (재생곡, 재생곡의 idx)&#39; 형태의 tuple을 담은 dict로 만듦
        length2 = len(dic2)
        dic3 = {l:[] for l in dic1.values()}
        for i in range(length2):
            dic3[list(dic1.values())[i]] = dic3.get(list(dic1.values())[i], 0) + list(dic2.values())[i]
        print(dic3)
        # dic3 → 위에서 만든 dict를 &#39;장르별 재생곡 합계&#39;를 기준으로 역순 정렬
        dic3 = sorted(dic3.items(), key=lambda x: x[0], reverse=True)
        print(dic3)


        result = []
        for i in dic3:
            # 한 장르 내에서 재생횟수는 역순으로, 동일 재생횟수에 대해서는 idx를 순서대로 정렬
            arr = sorted(i[1], key=lambda x: (-x[0],x[1]))
            print(arr)
            # 한 장르 내에 곡이 하나라면 그것만 추가
            if len(arr) == 1:
                result.append(arr[0][1])
            # 한 장르 내에 곡이 2개 이상이라면 2개까지만 추가
            else:
                for j in range(2):
                    result.append(arr[j][1])
        return result</code></pre>
<br>


<h3 id="📑-code--다른-사람꺼-4">📑 <code>Code : 다른 사람꺼</code></h3>
<pre><code class="language-python">&gt; code

    def solution(genres, plays):
        answer = []
        d = {e:[] for e in set(genres)}
        for e in zip(genres, plays, range(len(plays))):
            d[e[0]].append([e[1] , e[2]])
        genreSort =sorted(list(d.keys()), key= lambda x: sum( map(lambda y: y[0],d[x])), reverse = True)
        for g in genreSort:
            temp = [e[1] for e in sorted(d[g],key= lambda x: (x[0], -x[1]), reverse = True)]
            answer += temp[:min(len(temp),2)]
        return answer</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[221001_HackerRank : Tree - Top View]]></title>
            <link>https://velog.io/@brown_eyed87/221001HackerRank-Tree-Top-View</link>
            <guid>https://velog.io/@brown_eyed87/221001HackerRank-Tree-Top-View</guid>
            <pubDate>Sat, 01 Oct 2022 11:42:34 GMT</pubDate>
            <description><![CDATA[<h1 id="🌞-tree-top-view">🌞 <a href="https://www.hackerrank.com/challenges/tree-top-view/problem?isFullScreen=true">Tree: Top View</a></h1>
<h2 id="🚊-문제-설명">🚊 <code>문제 설명</code></h2>
<ul>
<li><code>binary tree</code>의 <code>root node</code>에 대한 포인터가 주어지면 <code>binary tree</code>의 <code>top view</code>를 <code>print</code>할 것.</li>
<li><code>top view</code>란, <code>node</code>의 위에서 본 <code>tree</code>를 뜻함.<blockquote>
<p><code>예시 1.</code>
<code>input : 1 2 5 3 4 6</code></p>
<pre><code class="language-python"> 1
  \
   2
    \
     5
    /  \
   3    6
    \
     4</code></pre>
</blockquote>
<pre><code>&gt; `Top View` : `1` → `2` → `5` → `6`
&gt; &lt;br&gt;
&gt;
&gt; `예시 2.`
&gt; `input : 1 10 4 3 2 7 5 6 11 15 13 17`
&gt;```python
      1
        \
         10
        /  \
      4      11
     / \      \
   3    7       15
  /    /  \    /  \  
 2    5    6  13   17</code></pre><blockquote>
<p><code>Top View</code> : <code>2</code> → <code>3</code> → <code>1</code> → <code>10</code> → <code>11</code> → <code>15</code> → <code>17</code></p>
</blockquote>
</li>
</ul>
<p><span style='background-color: #ffffe8'><strong>입력 예시</strong></span></p>
<pre><code class="language-python">             1 2 5 3 4 6

   1
    \
     2
      \
       5
      /  \
     3    6
      \
       4</code></pre>
<p><span style='background-color: #ffffe8'><strong>출력 예시</strong></span></p>
<pre><code class="language-python">1 2 5 6</code></pre>
<p><span style='background-color: #ffffe8'><strong>설명</strong></span></p>
<blockquote>
<p>From the top, only nodes <code>1, 2, 5, 6</code> are visible.</p>
</blockquote>
<br>

<h3 id="🚢-기본-제공-code">🚢 <code>기본 제공 Code</code></h3>
<pre><code class="language-python">class Node:
    def __init__(self, info): 
        self.info = info  
        self.left = None  
        self.right = None 
        self.level = None 

    def __str__(self):
        return str(self.info) 

class BinarySearchTree:
    def __init__(self): 
        self.root = None

    def create(self, val):  
        if self.root == None:
            self.root = Node(val)
        else:
            current = self.root

            while True:
                if val &lt; current.info:
                    if current.left:
                        current = current.left
                    else:
                        current.left = Node(val)
                        break
                elif val &gt; current.info:
                    if current.right:
                        current = current.right
                    else:
                        current.right = Node(val)
                        break
                else:
                    break
</code></pre>
<br>

<h3 id="🚢-input--output">🚢 <code>Input &amp; Output</code></h3>
<pre><code class="language-python">tree = BinarySearchTree()
t = int(input())

arr = list(map(int, input().split()))

for i in range(t):
    tree.create(arr[i])

topView(tree.root)</code></pre>
<br>


<h3 id="📑-code-1--bfs">📑 <code>Code 1 : BFS</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ul>
<li>위에서 바라봤을 때 최초로 보이는 <code>Node</code>만 담아야 하는 조건을 고려하여 tree에서의 상하 <code>level</code> 개념을 빗대어 좌우 <code>level</code> 개념을 <code>depth</code>라는 값으로 활용</li>
<li>전체 <code>Node</code>를 탐색하면서 조건에 맞는 <code>Node</code>만 찾아야 하므로 <code>BFS</code> 개념 사용</li>
</ul>
</blockquote>
<pre><code class="language-python">from collections import deque

def topView(root):
    # 결과를 담을 dict 생성
    #   depth(해당 노드의 좌우 폭의 단계)와 node.info(해당 노드의 value)를 dict 형태로 담을 예정
    result = {}
    # queue 자료구조 이용을 위해 deque 함수 사용
    queue = deque([])
    # root node부터 진행하기 위해 queue에 추가
    #   좌우 폭의 단계(depth)를 숫자로 표현하기 위해 각 노드를 [node, depth] 로 표현
    #   root node는 [root, 0]으로 표현
    queue.append(([root,0]))
    # queue가 빌 때까지 진행
    while(queue):
        # 현재 level에서 가장 앞에 있는(depth가 가장 낮은) node를 선택
        current_node = queue.popleft()
        # 만약, 해당 depth의 node에 대한 데이터가 result에 없다면
        if not current_node[1] in result:
            # 현재 노드의 depth:info 를 result에 추가
            result[current_node[1]] = current_node[0].info
        # 만약, 현재 노드의 왼쪽 자식 노드가 있다면
        if current_node[0].left:
            # 그 노드를 [node, depth] 형태의 자료형으로 queue에 추가
            #   그 노드의 depth는 현재 노드의 depth보다 1이 작음 (왼쪽으로 이동하니까)
            queue.append([current_node[0].left, current_node[1]-1])
        # 만약, 현재 노드의 오른쪽 자식 노드가 있다면
        if current_node[0].right:
            # 그 노드를 [node, depth] 형태의 자료형으로 queue에 추가
            #   그 노드의 depth는 현재 노드의 depth보다 1이 큼 (오른쪽으로 이동하니까)
            queue.append([current_node[0].right, current_node[1]+1])
    # depth 기준으로 dict를 정렬하기 위해 items() 메서드를 사용
    #   depth 기준으로 정렬 시, top view 기준으로 왼쪽부터 순서대로 정렬됨.
    for depth, value in sorted(result.items()):
        print(str(value), end=&quot; &quot;)</code></pre>
<br>


<h3 id="📑-code-2--dfs">📑 <code>Code 2 : DFS</code></h3>
<p>📌 <code>Logic</code> 설명</p>
<blockquote>
<ul>
<li>위에서 바라봤을 때 최초로 보이는 <code>Node</code>만 담아야 하는 조건을 고려하여 tree에서의 상하 <code>level</code> 개념을 빗대어 좌우 <code>level</code> 개념을 <code>depth</code>라는 값으로 활용</li>
<li><code>root Node</code>부터 시작하여 연결된 자식 <code>Node</code>들에 대해 <code>level</code>을 낮춰가며 <code>depth</code>와 <code>count</code>(root node로부터의 이동 횟수)를 계산 →  <code>DFS</code> 개념을 사용하는 이유</li>
<li><code>depth</code>가 동일한 <code>Node</code>들에 대해서는 <code>count</code>가 적은 <code>Node</code>가 top view에서 발견되는 <code>Node</code>라는 점을 코드로 구현</li>
</ul>
</blockquote>
<pre><code class="language-python"># binary tree의 모든 Node에 대해 depth(좌/우 level)와 count(이동 횟수)를 계산하여 위에서 언급한 조건에 맞는 Node만 추출하는 함수를 생성
def traversal(root, depth, count, temp):
    # root Node가 없으면 종료시킴
    if root is None:
        return
    # 현재 노드의 depth가 temp에 없다면
    if depth not in temp:
        # 현재 노드의 depth와 [value, count]를 dictionary 형태로 temp에 담음
        temp[depth] = [root.info, count]
    # 현재 노드와 동일한 depth를 갖는 노드가 temp에 있는 경우,
    #   해당 노드의 count보다 현재 노드의 count가 더 작다면
    if count &lt; temp[depth][1]:
        # 그 depth를 갖는 노드의 정보를 현재 노드의 정보로 갱신
        #  한마디로 지금 노드의 depth, info, count로 바꿔준다 이말임
        #    왜냐면, count가 작다는 것은 이동을 적게했다는 것이고 그 말인즉 
        #    상위 level에 위치한 Node이기 때문에 top view에서 먼저 보인다는 것이기 때문
        temp[depth] = [root.info, count]
    # 재귀를 호출하여 우측/좌측 모두 전부 계산
    #   이동해서 찾는 것인데, 이동은 무조건 count가 (+1)되고, 
    #   좌/우 여부에 따라 depth는 (-1) 혹은 (+1)됨.
    traversal(root.right, depth + 1, count + 1, temp)
    traversal(root.left, depth - 1, count + 1, temp)

def topView(root):
    # top view로 바라보았을 때의 결과를 담을 dict 선언
    temp = dict()
    # root Node부터 전부 탐색하여 결과를 dict에 담음
    traversal(root, 0, 0, temp)
    # depth 기준으로 dict를 정렬하기 위해 items() 메서드를 사용
    #   depth 기준으로 정렬 시, top view 기준으로 왼쪽부터 순서대로 정렬됨.
    for i in sorted(temp.items()):
        print(i[1][0], end = &#39; &#39;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[221001_HackerRank : Tree - Huffman Decoding]]></title>
            <link>https://velog.io/@brown_eyed87/221001HackerRank-Tree-HuffmanDecoding</link>
            <guid>https://velog.io/@brown_eyed87/221001HackerRank-Tree-HuffmanDecoding</guid>
            <pubDate>Sat, 01 Oct 2022 01:48:44 GMT</pubDate>
            <description><![CDATA[<h1 id="🌞-tree-huffman-decoding">🌞 <a href="https://www.hackerrank.com/challenges/tree-huffman-decoding/problem?isFullScreen=true">Tree: Huffman Decoding</a></h1>
<h2 id="🚊-문제-설명">🚊 <code>문제 설명</code></h2>
<blockquote>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Huffman_coding">Huffman coding</a> 은 주파수에 따라 가변 길이의 부호어들을 고정 길이 입력 문자에 할당한다. </li>
</ul>
</blockquote>
<ul>
<li>빈도가 높은 문자일수록 짧은 부호어들이 할당되고 빈도가 낮은 문자일수록 긴 부호어들이 할당됩니다. </li>
<li>문자의 경로를 따라 있는 모든 가장자리에는 코드 번호가 포함되어 있습니다. </li>
<li>트리의 왼쪽에 있으면 <code>0</code>이 됩니다. 오른쪽에 있으면 <code>1</code>이 됩니다. </li>
<li>오직 <code>leaf node</code>만이 글자와 그 빈도수를 포함할 것이다. 다른 모든  <code>node</code>는 문자 대신 <code>null</code>을 포함하며, 모든 노드와 그 하위 문자의 빈도 수를 포함한다.</li>
</ul>
<blockquote>
<ul>
<li>예를 들어 문자열 <code>ABRACADABRA</code>를 생각해 보자.</li>
</ul>
</blockquote>
<ul>
<li>문자열에는 총 <code>11</code>개의 문자가 있고, 이 숫자는 최종적으로 결정된 트리의 루트에 있는 수와 일치해야 한다.</li>
<li>우리의 주파수는 <code>A=5, B=2, R=2, C=1, B=1</code> 이다.</li>
<li>가장 작은 두 개의 주파수는 <code>C</code>와 <code>D</code>에 해당하므로 둘 다 <code>1</code>로 값이 같으므로 이 두 주파수로 트리를 만든다.</li>
<li>루트 노드는 하위 노드 카운트의 합계(이 경우에는 <code>1+1 =2</code>)를 포함한다.</li>
<li>왼쪽 노드는 처음 접하는 문자 <code>C</code>를, 오른쪽 노드는 <code>D</code>를 포함합니다. </li>
<li>다음으로 우리는 방금 만든 트리, 문자 <code>B</code>, 문자 <code>R</code>의 <code>3</code>가지 항목을 가지고 있다. </li>
<li>트리가 먼저 왔기 때문에 새 루트 노드의 왼쪽에 있고, <code>B</code>는 오른쪽으로 가게 된다.</li>
<li>트리가 완료될 때까지 반복한 다음, 가장자리에 <code>1</code>과 <code>0</code>을 입력.</li>
<li>완성된 그래프는 다음과 같다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/81bd48ed-0537-46fe-8026-36f071666331/image.png" alt=""></p>
<blockquote>
<ul>
<li>입력된 문자는 오직 <code>leaf node</code>에만 있다.</li>
</ul>
</blockquote>
<ul>
<li>내부 노드의 문자 값은 <code>Null</code>을 의미한다.</li>
<li>우리는 캐릭터에 대한 우리의 값이 다음과 같은 것을 확인할 수 있다.</li>
</ul>
<pre><code class="language-python">      A - 0
      B - 111
      C - 1100
      D - 1101
      R - 10</code></pre>
<blockquote>
<ul>
<li><code>Huffman encoding</code>된 문자열은 다음과 같다.</li>
</ul>
</blockquote>
<pre><code class="language-python">      A B    R  A C     A D     A B    R  A
      0 111 10 0 1100 0 1101 0 111 10 0
      or
      01111001100011010111100</code></pre>
<blockquote>
<ul>
<li>모호성을 피하기 위해 <code>Huffman encoding</code>은 접두사 없는 인코딩 기법이다. 부호어는 다른 부호어의 접두사로 나타나지 않는다</li>
<li>인코딩된 문자열을 디코딩하려면 <code>0</code>과 <code>1</code>을 따라 <code>leaf node</code>까지 이동한 후 문자를 반환한다.</li>
<li><code>Huffman tree</code>의 루트에 대한 포인터와 디코딩할 이진 코드화된 문자열이 제공되며, 디코딩된 문자열을 출력해야 한다.</li>
</ul>
</blockquote>
<p><span style='background-color: #ffffe8'><strong>입력 예시</strong></span>
<img src="https://velog.velcdn.com/images/brown_eyed87/post/3b9838ee-02e2-4997-853e-c4d58aa9e0b9/image.png" alt=""></p>
<pre><code class="language-pytnhon">s=&quot;1001011&quot;</code></pre>
<p><span style='background-color: #ffffe8'><strong>출력 예시</strong></span></p>
<pre><code class="language-python">ABACA</code></pre>
<p><span style='background-color: #ffffe8'><strong>설명</strong></span></p>
<pre><code class="language-python">S=&quot;1001011&quot;
Processing the string from left to right.
S[0]=&#39;1&#39; : we move to the right child of the root. We encounter a leaf node with value &#39;A&#39;. We add &#39;A&#39; to the decoded string.
We move back to the root.

S[1]=&#39;0&#39; : we move to the left child. 
S[2]=&#39;0&#39; : we move to the left child. We encounter a leaf node with value &#39;B&#39;. We add &#39;B&#39; to the decoded string.
We move back to the root.

S[3] = &#39;1&#39; : we move to the right child of the root. We encounter a leaf node with value &#39;A&#39;. We add &#39;A&#39; to the decoded string.
We move back to the root.

S[4]=&#39;0&#39; : we move to the left child. 
S[5]=&#39;1&#39; : we move to the right child. We encounter a leaf node with value C&#39;. We add &#39;C&#39; to the decoded string.
We move back to the root.

 S[6] = &#39;1&#39; : we move to the right child of the root. We encounter a leaf node with value &#39;A&#39;. We add &#39;A&#39; to the decoded string.
We move back to the root.

Decoded String = &quot;ABACA&quot;</code></pre>
<br>


<h3 id="🚢-기본-제공-code">🚢 <code>기본 제공 Code</code></h3>
<pre><code class="language-python">import queue as Queue

cntr = 0

class Node:
    def __init__(self, freq, data):
        self.freq = freq
        self.data = data
        self.left = None
        self.right = None
        global cntr
        self._count = cntr
        cntr = cntr + 1

    # lt (less than) : 두 객체의 값의 크기를 비교할 때 사용되는 함수
    def __lt__(self, other):
        if self.freq != other.freq:
            return self.freq &lt; other.freq
        return self._count &lt; other._count

def huffman_hidden():#builds the tree and returns root
    q = Queue.PriorityQueue()


    for key in freq:
        q.put((freq[key], key, Node(freq[key], key) ))

    while q.qsize() != 1:
        a = q.get()
        b = q.get()
        obj = Node(a[0] + b[0], &#39;\0&#39; )
        obj.left = a[2]
        obj.right = b[2]
        q.put((obj.freq, obj.data, obj ))

    root = q.get()
    root = root[2]#contains root object
    return root

def dfs_hidden(obj, already):
    if(obj == None):
        return
    elif(obj.data != &#39;\0&#39;):
        code_hidden[obj.data] = already

    dfs_hidden(obj.right, already + &quot;1&quot;)
    dfs_hidden(obj.left, already + &quot;0&quot;)</code></pre>
<br>

<h3 id="🚢-input--output">🚢 <code>Input &amp; Output</code></h3>
<pre><code class="language-python">ip = input()
freq = {}#maps each character to its frequency

cntr = 0

for ch in ip:
    if(freq.get(ch) == None):
        freq[ch] = 1
    else:
        freq[ch]+=1

root = huffman_hidden()#contains root of huffman tree

code_hidden = {}#contains code for each object

dfs_hidden(root, &quot;&quot;)

if len(code_hidden) == 1:#if there is only one character in the i/p
    for key in code_hidden:
        code_hidden[key] = &quot;0&quot;

toBeDecoded = &quot;&quot;

for ch in ip:
   toBeDecoded += code_hidden[ch]

decodeHuff(root, toBeDecoded) # 우리가 구현할 함수를 여기서 실행</code></pre>
<br>


<h3 id="📑-code">📑 <code>Code</code></h3>
<pre><code class="language-python">def decodeHuff(root, s):
    # 현재 노드를 root로 지정
    current_node = root
    # 노드의 데이터를 쌓을 빈 문자열을 선언
    result = &#39;&#39;
    # s의 숫자들을 앞에서부터 확인하는 작업을 반복
    for num in s:
        # 해당 숫자값이 0이라면
        if int(num) == 0:
            # 탐색할 노드의 위치를 현재 노드의 왼쪽 노드로 변경
            current_node = current_node.left
        # 해당 숫자값이 1이라면
        else:
            # 탐색할 노드의 위치를 현재 노드의 오른쪽 노드로 변경
            current_node = current_node.right
        # 만약 현재 노드의 왼쪽 노와 오른쪽 노드가 모두 없다면
        if current_node.left == None and current_node.right == None:
            # 현재 노드의 데이터를 result에 추가
            result += current_node.data
            # 현재 노드를 다시 root로 지정
            # 반복문을 돌 때, 항상 root 노드부터 탐색을 시작하기 위함.
            current = root

    print(result)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[221001_HackerRank : Tree - Search]]></title>
            <link>https://velog.io/@brown_eyed87/221001HackerRank-Tree-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@brown_eyed87/221001HackerRank-Tree-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Sat, 01 Oct 2022 01:04:16 GMT</pubDate>
            <description><![CDATA[<h1 id="🌞-tree-search-문제">🌞 <code>Tree Search 문제</code></h1>
<blockquote>
<p><a href="https://www.hackerrank.com/challenges/tree-preorder-traversal/problem?isFullScreen=true">Tree: Preorder Traversal</a>
<a href="https://www.hackerrank.com/challenges/tree-inorder-traversal/problem?isFullScreen=true">Tree: Inorder Traversal</a>
<a href="https://www.hackerrank.com/challenges/tree-postorder-traversal/problem?isFullScreen=true">Tree: Postorder Traversal</a>
<a href="https://www.hackerrank.com/challenges/tree-level-order-traversal/problem?isFullScreen=true">Tree: Levelorder Traversal</a></p>
</blockquote>
<h2 id="🚊-문제-공통-사항">🚊 <code>문제 공통 사항</code></h2>
<br>

<h3 id="🚢-이진탐색트리-클래스">🚢 <code>이진탐색트리 클래스</code></h3>
<pre><code class="language-python"># 1. Node 클래스 선언
class Node:
    def __init__(self, info): 
        # Node의 데이터 = info
        self.info = info
        # Node의 왼쪽 = None
        self.left = None  
        # Node의 오른쪽 = None
        self.right = None 
        # Node의 level = None
        self.level = None 

    # init에서 규정한 클래스 자체 내용을 출력
    def __str__(self):
        return str(self.info) 

# 이진탐색트리 클래스 선언
class BinarySearchTree:
    def __init__(self):
        # root 노드를 None으로 선언
        self.root = None

    # 이진탐색트리에 데이터 삽입

    def create(self, val):  
        # root 노드가 없으면
        if self.root == None:
            # val 값을 갖는 노드를 생성하여 root 노드에 할당하고 끝냄.
            self.root = Node(val)
        #  root 노드가 있으면
        else:
            # 현재 노드를 root 노드로 지정
            current = self.root
             # 계속 반복
            #   삽입할 위치를 먼저 찾고, 완료되면 해당 노드의 데이터를 val로 지정하는 방식
            while True:
                # 삽입하려는 데이터 값이 현재 노드의 데이터 값보다 작다면 → 왼쪽으로 내려가서 삽입할 위치를 찾음.
                if val &lt; current.info:
                    # 현재 노드의 왼쪽 노드가 있으면
                    if current.left:
                        # 삽입할 탐색 위치를 현재 노드의 왼쪽 노드로 변경
                        current = current.left
                    # 현재 노드의 왼쪽 노드가 없다면
                    else:
                        # val 값을 갖는 노드를 생성하여 현재 노드의 왼쪽 노드에 할당.
                        current.left = Node(val)
                        # 데이터 삽입을 끝냈으니 반복문 종료시키고 끝냄.
                        break
                # 삽입하려는 데이터 값이 현재 노드의 데이터 값보다 크다면
                elif val &gt; current.info:
                    # 현재 노드의 오른쪽 노드가 있다면
                    if current.right:
                        # 삽입할 탐색 위치를 현재 노드의 오른쪽 노드로 변경
                        current = current.right
                    # 현재 노드의 오른쪽 노드가 없다면
                    else:
                        # val 값을 갖는 노드를 생성하여 현재 노드의 오른쪽 노드에 할당
                        current.right = Node(val)
                        # 데이터 삽입을 끝냈으니 반복문 종료시키고 끝냄.
                        break
                # 삽입하려는 데이터 값이 현재 노드의 데이터 값과 동일하다면
                else:
                    # 삽입할 필요가 없으니 그냥 끝냄.
                    break</code></pre>
<br>

<h3 id="🚢-input--output">🚢 <code>Input &amp; Output</code></h3>
<pre><code class="language-python"># 이진 탐색 트리 선언
tree = BinarySearchTree()
# 생성할 노드 개수 입력받기
t = int(input())
# n × 1 사이즈의 리스트 형태의 array 생성
arr = list(map(int, input().split()))
# for문을 돌면서 노드 생성
for i in range(t):
    tree.create(arr[i])

##### 여기에 문제에서 원하는 함수를 입력받아 코드 실행하게 됨 ####</code></pre>
<br>

<h2 id="🚊-tree-순회-구현">🚊 <code>Tree 순회 구현</code></h2>
<p><span style='background-color: #ffffe8'><strong>제약조건</strong></span></p>
<p> Nodes in the tree </p>
<p><span style='background-color: #ffffe8'><strong>출력 형식</strong></span></p>
<p>Print the tree&#39;s preorder traversal as a single line of space-separated values.</p>
<p><span style='background-color: #ffffe8'><strong>입력 예시</strong></span></p>
<pre><code class="language-python">     1
      \
       2
        \
         5
        /  \
       3    6
        \
         4  </code></pre>
<p><span style='background-color: #ffffe8'><strong>출력 예시</strong></span></p>
<pre><code class="language-python"># Preorder
    1 2 5 3 4 6 
# Inorder
    1 2 3 4 5 6 
# Postorder
    4 3 6 5 2 1 
# Levelorder
    1 2 5 3 6 4
</code></pre>
<br>

<h2 id="🚊-순회-방식별-살펴보기">🚊 <code>순회 방식별 살펴보기</code></h2>
<br>

<h3 id="🚢-preorder">🚢 <code>Preorder</code></h3>
<p>🚥 <code>Root Node</code> → <code>left Node</code> → <code>right Node</code></p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/74fba397-18c3-45bf-a2bd-4ca7dee6d8f1/image.png" alt=""></p>
<h4 id="📑-code">📑 <code>Code</code></h4>
<pre><code class="language-python">def PreOrder(root):
    # root 노드가 있다면
    if root:
        # 현재 노드의 데이터를 출력하고
        print(root.info, end=&quot; &quot;)
        # 현재 노드의 왼쪽에서 PreOrder를 다시 진행
        # → 왼쪽으로 한 단계 들어가는 것을 의미
        PreOrder(root.left)
        # 현재 노드의 왼쪽에서 Preorder가 끝났다면, 오른쪽에서 PreOrder를 다시 진행
        # → 오른쪽으로 한 단계 들어가는 것을 의미
        PreOrder(root.right)</code></pre>
<blockquote>
<ul>
<li>root 노드에서 현재 노드의 데이터를 출력하고</li>
<li>그 다음에 왼쪽 노드로 내려가서 해당 노드의 데이터를 출력</li>
<li>위의 과정을 반복</li>
<li>더이상 파고들 왼쪽 노드가 없으면 바로 이전 노드로 돌아가 해당 노드의 오른쪽 노드로 내려감.</li>
<li>이 과정을 끝까지 반복</li>
</ul>
</blockquote>
<br>

<h3 id="🚢-inorder">🚢 <code>Inorder</code></h3>
<p>🚥 <code>left Node</code> → <code>Root Node</code> → <code>right Node</code></p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/1d00a6b7-e523-450e-9eb3-79a5a1f338be/image.png" alt=""></p>
<h4 id="📑-code-1">📑 <code>Code</code></h4>
<pre><code class="language-python">def inOrder(root):
    # root 노드가 있다면
    if root:
        # 현재 노드의 왼쪽에서 inOrder를 다시 진행
        inOrder(root.left)
        # 현재 노드의 데이터를 출력
        print(root.info, end=&quot; &quot;)
        # 현재 노드의 오른쪽에서 inOrder를 다시 진행
        inOrder(root.right)</code></pre>
<blockquote>
<ul>
<li>root 노드부터 현재 노드의 왼쪽 노드로 내려가는 과정을 반복</li>
<li>현재 노드 기준, 더이상 파고들 왼쪽 노드가 없으면 현재 노드의 데이터를 출력</li>
<li>바로 이전 노드로 돌아가 해당 노드의 데이터를 출력</li>
<li>그 노드의 오른쪽 노드로 내려가서 해당 노드의 데이터를 출력</li>
<li>이 과정을 끝까지 반복</li>
</ul>
</blockquote>
<br>

<h3 id="🚢-postorder">🚢 <code>Postorder</code></h3>
<p>🚥 <code>Root Node</code> → <code>left Node</code> → <code>right Node</code></p>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/31e0002b-630e-4bc9-a32b-b0c8a6887aaf/image.png" alt=""></p>
<h4 id="📑-code-2">📑 <code>Code</code></h4>
<pre><code class="language-python">def postOrder(root):
    # root 노드가 있다면
    if root:
        # 현재 노드의 왼쪽에서 postOrder를 다시 진행
        postOrder(root.left)
        # 현재 노드의 오른쪽에서 postOrder를 다시 진행
        postOrder(root.right)
        # 현재 노드의 데이터를 출력
        print(root.info, end=&quot; &quot;)</code></pre>
<blockquote>
<ul>
<li>root 노드부터 현재 노드의 왼쪽 노드로 내려가는 과정을 반복</li>
<li>더이상 파고들 노드가 없으면 현재 노드의 오른쪽 노드로 내려감</li>
<li>현재 노드의 데이터를 출력</li>
<li>이 과정을 끝까지 반복</li>
</ul>
</blockquote>
<br>

<h3 id="🚢-levelorder">🚢 <code>Levelorder</code></h3>
<p>🚥 <code>Root Node</code> → <code>left Node</code> → <code>right Node</code></p>
<blockquote>
<ul>
<li>각 Level의 노드를 왼쪽부터 출력 후, 그 다음 Level로 넘어가 이 과정을 반복</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/brown_eyed87/post/c329814b-21ab-457c-95b3-0422817741e7/image.png" alt=""></p>
<h4 id="📑-code-3">📑 <code>Code</code></h4>
<pre><code class="language-python">from collections import deque

def levelOrder(root):
    if not root:
        return 

    # deque 생성
    queue = deque()
    # root 노드를 추가
    queue.append(root)
    # queue에 아무 것도 없을 때까지 반복
    while queue:
        # queue의 첫 번째 객체를 추출하여 현재 노드에 할당
        current_node = queue.popleft()
        # 현재 노드의 데이터를 출력
        print(current_node.info, end = &#39; &#39;)
        # 현재 노드의 왼쪽 노드가 있다면?
        if current_node.left: 
            # queue에 해당 노드를 추가
            queue.append(current_node.left)
        # 현재 노드의 오른쪽 노드가 있다면?
        if current_node.right: 
            # queue에 해당 노드를 추가
            queue.append(current_node.right)</code></pre>
<blockquote>
<ul>
<li><code>FIFO(First In First Out)</code> 방식의 <code>Queue</code> 자료구조를 사용하기 위해 <code>collections</code> 모듈의 <code>deque</code>을 <code>import</code></li>
<li>상위 <code>Level</code>부터 노드들을 순서대로 담을 <code>queue</code>를 <code>deque</code> 자료구조로 선언</li>
<li><code>root</code> 노드부터 시작하여 해당 노드의 왼쪽 노드와 오른쪽 노드가 순서대로 <code>queue</code>에 쌓이게 됨.</li>
<li>노드 데이터의 출력은 <code>queue</code>에서 왼쪽부터 뽑혀 나와 진행되기 때문에 상위 <code>Level</code>부터 시작해서 현재 노드의 왼쪽부터 오른쪽으로 순서대로 출력되고 그 다음 <code>Level</code>로 넘어가서 반복되는 형태로 진행됨.</li>
</ul>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>