<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Today is Doday.log</title>
        <link>https://velog.io/</link>
        <description>ㅎㅇ</description>
        <lastBuildDate>Wed, 30 Apr 2025 08:41:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Today is Doday.log</title>
            <url>https://images.velog.io/images/hyunj_523/profile/8ae43796-e248-4f4a-bbba-00abc52c6199/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Today is Doday.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyunj_523" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[OpenStack] 로드밸런서 Octavia #1]]></title>
            <link>https://velog.io/@hyunj_523/OpenStack-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-1</link>
            <guid>https://velog.io/@hyunj_523/OpenStack-%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%84%9C-1</guid>
            <pubDate>Wed, 30 Apr 2025 08:41:24 GMT</pubDate>
            <description><![CDATA[<p>오픈스택에서 로드밸런서 생성하는거 기록해보기~</p>
<h2 id="로드밸런서란">로드밸런서란?</h2>
<p>간단히 말해서 <strong>들어오는 트래픽을 여러 서버로 나눠주는 역할</strong>
오픈스택에서는 Octavia컴포넌트가 로드밸런서 기능을 제공한다.</p>
<h3 id="구성요소">구성요소</h3>
<p>오픈스택의 로드밸런서는 단일 리소스가 아니라 여러 구성 요소가 유기적으로 연결되어 동작한다.
하나의 트래픽이 로드밸런서를 거쳐 백엔드까지 도달하기 위해 거치는 경로</p>
<pre><code>             [Floating IP]
                  ↓
            [Load Balancer]
                  ↓
              [Listener]
                  ↓
                [Pool]
               ↙       ↘
         [Member 1]  [Member 2]</code></pre><ol>
<li>로드밸런서 LoadBalancer</li>
</ol>
<ul>
<li>외부에서 들어오는 트래픽을 받아들이는 진입 지점</li>
<li>로드밸런서 자체는 트래픽을 받아주지만 , 포트 열어주는 건 listner가 한다.</li>
<li>가<img src="https://velog.velcdn.com/images/hyunj_523/post/4b60ffb5-7259-4d2f-964c-fab0e1f3c510/image.png" alt="">
속성 필요</li>
</ul>
<ol start="2">
<li>리스너 Listener</li>
</ol>
<ul>
<li>특정 포트를 열고, 어떤 방식으로 들어온 요청을 처리할지 정의한다.</li>
<li>프로토콜, 프로토콜 포트 입력</li>
<li>리소스 하나당 한 가지 프로토콜 + 포트 조합만 가능</li>
</ul>
<ol start="3">
<li>풀 Pool</li>
</ol>
<ul>
<li>리스터를 통해 들어온 요청을 어떤 서버 그룹에 분배할지 정의하는 단위</li>
<li>알고리즘(ROUND_ROBIN, LEAST_CONNECTIONS 등), 프로토콜(리스터와 동일) 입력</li>
<li>하나의 리스너에는 하나의 풀만 연결 가능</li>
</ul>
<ol start="4">
<li>풀 멤버 PoolMember</li>
</ol>
<ul>
<li>실제 트래픽을 받아 처리할 서버들</li>
<li>예시: 192.168.0.11, 192.168.0.12 같은 웹 인스턴스 IP들</li>
<li>내부망 ip, port, subnet_id 필요</li>
<li>상태가 DOWN이면 트래픽 분배에서 빠짐 </li>
<li>헬스모니터가 자동으로 상태 확인함</li>
</ul>
<ol start="5">
<li>헬스모니터 Health Monitor</li>
</ol>
<ul>
<li>각 멤버 서버가 살아있는지, 정상인지 주기적으로 체크해주는 기능</li>
<li>예) 5초마다 /health 호출 -&gt; 응답 없으면 해당 서버 off</li>
<li>delay(체크 주기), timeout(응답 대기 시간), max_retries(몇 번 실패하면 DOWN 처리할지) 설정</li>
<li>/health 경로는 인스턴스 내부에 미리 만들어둬야 한다.</li>
</ul>
<h2 id="octavia">Octavia</h2>
<p>오픈스택에서 제공하는 로드밸런서 서비스 구성요소
Neutron의 확장 형태였던 LBaaS가 독립 프로젝트로 분리되어, 더 유연하고 강력한 로드밸런싱 기능 제공</p>
<ul>
<li>api 사용</li>
</ul>
<ol>
<li>로드밸런서 생성<pre><code>POST /v2.0/lbaas/loadbalancers
Content-Type: application/json
X-Auth-Token: &lt;token&gt;
</code></pre></li>
</ol>
<p>{
  &quot;loadbalancer&quot;: {
    &quot;name&quot;: &quot;my-lb&quot;,
    &quot;vip_subnet_id&quot;: &quot;<SUBNET_ID>&quot;,
    &quot;provider&quot;: &quot;octavia&quot;
  }
}</p>
<pre><code>-&gt; 응답 : 로드밸런서 id, vip_address,  provisioning_status

2. 리스너 생성</code></pre><p>POST /v2.0/lbaas/listeners
X-Auth-Token: <token></p>
<p>{
  &quot;listener&quot;: {
    &quot;name&quot;: &quot;http-listener&quot;,
    &quot;protocol&quot;: &quot;HTTP&quot;,
    &quot;protocol_port&quot;: 80,
    &quot;loadbalancer_id&quot;: &quot;<LB_ID>&quot;
  }
}</p>
<pre><code>3. 풀 생성</code></pre><p>POST /v2.0/lbaas/pools
X-Auth-Token: <token></p>
<p>{
  &quot;pool&quot;: {
    &quot;name&quot;: &quot;web-pool&quot;,
    &quot;protocol&quot;: &quot;HTTP&quot;,
    &quot;lb_algorithm&quot;: &quot;ROUND_ROBIN&quot;,
    &quot;listener_id&quot;: &quot;<LISTENER_ID>&quot;
  }
}</p>
<pre><code>4. 풀멤버 추가</code></pre><p>POST /v2.0/lbaas/pools/<POOL_ID>/members
X-Auth-Token: <token></p>
<p>{
  &quot;member&quot;: {
    &quot;address&quot;: &quot;<INSTANCE_IP>&quot;,
    &quot;protocol_port&quot;: 80,
    &quot;subnet_id&quot;: &quot;<SUBNET_ID>&quot;
  }
}</p>
<pre><code>
### 작동 흐름
1.    로드밸런서(LB) 생성 → VIP 부여
2.    리스너 생성 → LB에 포트 바인딩
3.    풀(Pool) 생성 → 리스너에 연결
4.    멤버(Member) 추가 → 풀에 서버 연결
5.    헬스모니터 연결 → 상태 자동 점검
6.    클라이언트는 VIP에 접속 → 자동 분산</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] INSERT Trigger → [Spring] SSE로 응답]]></title>
            <link>https://velog.io/@hyunj_523/DB-INSERT-Trigger-Notify-Spring-Listen-%ED%95%98%EA%B3%A0-SSE%EB%A1%9C-%EC%9D%91%EB%8B%B5</link>
            <guid>https://velog.io/@hyunj_523/DB-INSERT-Trigger-Notify-Spring-Listen-%ED%95%98%EA%B3%A0-SSE%EB%A1%9C-%EC%9D%91%EB%8B%B5</guid>
            <pubDate>Thu, 15 Feb 2024 08:14:28 GMT</pubDate>
            <description><![CDATA[<p>외부 api에서 db에 새로운 데이터가 저장됨을 감지하고 실시간으로 내보내주는 기능을 만들기 위한 테스트해봤음  </p>
<p>SPRING에서 DB 변동감지하는 방법 검색하다가 postgres의 notify/listen 이용했음</p>
<p>DB에 특정테이블에 새로운 로우가 추가될 때 트리거 이벤트를 실행하고 Spring에서 해당 이벤트를 Listen하고 Server-Sent Events(SSE)를 통해 내보낸다.</p>
<h2 id="trigger-생성">TRIGGER 생성</h2>
<ol>
<li>트리거 함수를 만든다.</li>
</ol>
<pre><code class="language-sql">CREATE OR REPLACE FUNCTION public.cctv_trigger_function()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
    PERFORM pg_notify(cast(&#39;new_data_inserted&#39; as text) , row_to_json(NEW)::text); -- 이벤트 이름과,데이터 전달
    RETURN NULL;
END;
$function$;</code></pre>
<p><strong><code>pg_notify</code></strong> 함수 PostgreSQL의 LISTEN/NOTIFY 이용하여 &quot;new_data_inserted&quot;라는 이벤트를 발생시키고, 해당 이벤트와 함께 새로운 데이터를 JSON 형식으로 전달</p>
<p>채널명 : new_data_inserted</p>
<p>새로 INSERT되는 row를 json으로 내보냄</p>
<ol start="2">
<li>cctv_trans_data 테이블에 INSERT시 위에서 생성한 함수를 실행하는 트리거를 만든다. </li>
</ol>
<pre><code class="language-sql">CREATE TRIGGER after_insert_trigger
AFTER INSERT ON public.cctv_trans_data 
FOR EACH ROW
EXECUTE FUNCTION cctv_trigger_function();</code></pre>
<h2 id="sse-객체-생성-클래스">SSE 객체 생성 클래스</h2>
<pre><code class="language-java">@Component
public class SseEmitterManager {

    private final CopyOnWriteArrayList&lt;SseEmitter&gt; emitters = new CopyOnWriteArrayList&lt;&gt;();

    public SseEmitter createEmitter(HttpServletRequest request) {
        System.out.println(&quot;====SSE stream 접근 : &quot; + request.getRemoteAddr());
        SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
        this.emitters.add(emitter);
        try {
            emitter.send(SseEmitter.event()
                    .name(&quot;connect&quot;)
                    .data(&quot;connected!&quot;, MediaType.APPLICATION_JSON));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        emitter.onCompletion(() -&gt; this.emitters.remove(emitter));
        emitter.onTimeout(() -&gt; this.emitters.remove(emitter));

        return emitter;
    }

    public CopyOnWriteArrayList&lt;SseEmitter&gt; getEmitters() {
        return this.emitters;
    }

}</code></pre>
<h2 id="postgresnotificationlistener-생성"><code>PostgresNotificationListener</code> 생성</h2>
<ol>
<li>LISTEN 핸들러 메서드 </li>
</ol>
<pre><code class="language-java">public Runnable createNotificationHandler(Consumer&lt;PGNotification&gt; consumer) {
    return() -&gt; {
            jdbcTemplate.execute((Connection c) -&gt; {
    try{
                    c.createStatement().execute(&quot;LISTEN new_data_inserted&quot;);
                    System.out.println(&quot;====LISTEN====&quot;);

                    PGConnection pgconn = c.unwrap(PGConnection.class);
    while(!Thread.currentThread().isInterrupted()) {
                        PGNotification[] nts = pgconn.getNotifications(10000);
    if(nts ==null|| nts.length == 0) {
    continue;
                        }
    for(PGNotification nt : nts) {
                            consumer.accept(nt);
                        }
                    }
                }catch(SQLException e) {
                    e.printStackTrace();
                }
    return0;
            });
        };
    }</code></pre>
<p>PostgreSQL의 LISTEN/NOTIFY 기능을 사용하여 데이터베이스에서 보낸 NOTIFY를 감지</p>
<ul>
<li><p><strong><code>Consumer</code> :</strong>  PGNotification 객체를 입력으로 받아서 해당 객체에 대한 처리를 수행</p>
</li>
<li><p><strong><code>jdbcTemplate</code></strong> : 데이터베이스 커넥션 후  &quot;LISTEN new_data_inserted&quot;라는 명령을 실행하여 DB에서 &quot;new_data_inserted&quot;라는 이벤트를 구독함</p>
</li>
<li><p><strong><code>PGConnection</code></strong> : 10초마다 DB이벤트를 가져옴. 이벤트가 발생하지 않을 경우에는 대기</p>
<p>  이벤트가 발생하면 PGNotification 객체들의 배열을 가져오고,  각각 PGNotification에 대해 <strong><code>consumer.accept(nt)</code></strong>를 호출하여 입력받은 Consumer에 해당 이벤트를 전달</p>
</li>
<li><p><strong><code>Runnable</code></strong> 을 반환한다. 해당 메서드가 호출되면 이벤트 핸들링을 담당하는 쓰레드를 생성하고, 그 쓰레드가 실행되는 로직을 Runnable 객체로 래핑하여 반환.</p>
</li>
</ul>
<ol start="2">
<li>@PostConstruct 초기화 메서드</li>
</ol>
<pre><code class="language-java">@PostConstruct
    public void init() {
        Runnable notificationHandler = createNotificationHandler((PGNotification notification) -&gt; {
            System.out.println(&quot;=====Received noti: &quot; + notification.getName() + &quot;, payload: &quot; + notification.getParameter() + &quot;=====&quot;);
            // sse 로 내보내는 로직 추가
            String payload = notification.getParameter();
            // 각 emitter에 payload를 전송
            sseEmitterManager.getEmitters().forEach(sseEmitter -&gt; {
                try {
                    sseEmitter.send(payload, MediaType.APPLICATION_JSON);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        });
        Thread thread = new Thread(notificationHandler);
        thread.start();
    }</code></pre>
<ul>
<li><strong><code>createNotificationHandler()</code></strong> 메서드로 <strong><code>notificationHandler</code></strong>라는 Runnable 객체를 생성</li>
<li>알림을 받으면 해당 알림의 이름과 payload를 출력하고 <strong><code>sseEmitterManager</code></strong>를 통해 관리되는 모든 SSE emitter에게 payload를 전달</li>
<li><strong><code>getEmitters()</code></strong>  현재 활성화된 모든 SSE emitter를 반환</li>
</ul>
<h2 id="controller">Controller</h2>
<pre><code class="language-java">@GetMapping(value =&quot;/connect&quot;, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter handle(HttpServletRequest request) {
        return sseEmitterManager.createEmitter(request);
    }</code></pre>
<p><code>/connect</code> 진입 시 sseEmitter 객체 생성</p>
<h2 id="client-javascript">Client (javascript)</h2>
<pre><code class="language-java">const eventSource = new EventSource(&quot;/connect&quot;)
        eventSource.onmessage = event =&gt; {
            const p = document.createElement(&quot;p&quot;)
            p.innerText = event.data

            document.getElementById(&quot;messages&quot;).appendChild(p)
        }</code></pre>
<p>SSE 연결 후</p>
<p>디비버로 DB에 INSERT하면 화면에 데이터 출력됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[윈도우 postgresql 백업, 복원]]></title>
            <link>https://velog.io/@hyunj_523/%EC%9C%88%EB%8F%84%EC%9A%B0-postgresql-%EB%B0%B1%EC%97%85-%EB%B3%B5%EC%9B%90</link>
            <guid>https://velog.io/@hyunj_523/%EC%9C%88%EB%8F%84%EC%9A%B0-postgresql-%EB%B0%B1%EC%97%85-%EB%B3%B5%EC%9B%90</guid>
            <pubDate>Tue, 19 Dec 2023 06:47:08 GMT</pubDate>
            <description><![CDATA[<p><a href="https://kindloveit.tistory.com/27">https://kindloveit.tistory.com/27</a></p>
<h2 id="pg_bump-백업">pg_bump 백업</h2>
<p>윈도우 powershell 관리자권한으로 실행
C:\Program Files\PostgreSQL\14\bin 으로 이동해서 우클릭 명령터미널 열기</p>
<blockquote>
<p>PS C:\Program Files\PostgreSQL\14\bin&gt; .\pg_dump.exe --help</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hyunj_523/post/e37cdaf7-29f4-4613-bffe-48f13433e2a6/image.png" alt=""></p>
<blockquote>
<p>./pg_dump.exe -h localhost -d postgres -p 5432 -U postgres *<em>-Fc *</em>&gt; C:\파일위치\파일명.dump</p>
</blockquote>
<p>-Fc 옵션 있어야 gis 데이터도 백업됨
비밀번호 입력하면 파일위치에 백업파일 생성됨</p>
<h2 id="pg_restore-복원">pg_restore 복원</h2>
<blockquote>
<p>.\pg_restore.exe -d postgres -U postgres <strong>-Fc</strong> -v -W C:\파일위치\파일명.dump</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Django] URL 처리 순서]]></title>
            <link>https://velog.io/@hyunj_523/Django-URL-%EC%B2%98%EB%A6%AC-%EC%88%9C%EC%84%9C</link>
            <guid>https://velog.io/@hyunj_523/Django-URL-%EC%B2%98%EB%A6%AC-%EC%88%9C%EC%84%9C</guid>
            <pubDate>Thu, 27 Jan 2022 04:55:33 GMT</pubDate>
            <description><![CDATA[<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn7Iw5%2FbtqRqQNmRur%2FkVx9k7CsVmkBGFPBh5J8y0%2Fimg.png">

<ul>
<li><p><strong><a href="http://www.domain.com">www.domain.com</a></strong>
도메인 주소 : 서비스를 제공하는 컴퓨터(서버)의 위치
ip주소를 DNS 서버에서 도메인 이름으로 변경해 준다. 서버내부 IP가 바뀌어도 그래도 이용할 수 있다. 192.<del>~</del> 안되워도 된다.</p>
</li>
<li><p><strong>:8000</strong>
포트번호 : 동일한 IP주소에 접속하더라도 어떤 통로로 들어왔는지 구분하기 위한 논리적 채널</p>
<IMG SRC="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzyvha%2FbtqRCWZxL3o%2FpA9qkDl9lFpL7Pn7TT6ZAk%2Fimg.png">
</li>
<li><p><strong>docs/search</strong>
Path에 해당하는 부분 : urls.py에서 URL 주소를 연결하는 부분</p>
</li>
<li><p><strong>?category=python&amp;time=today</strong>
쿼리 : 웹 사이트에서 검색기능 이용할 때 
검색 시 검색 내용들이 url에 포함된다.
GET 메소드로 요청을 보낼 때 주소창에 정보를 넣어 전송하는 역할한다.</p>
</li>
<li><p><strong>#here</strong>
Fragment : 해당 문서의 일부를 나타내는 부분</p>
<br>

</li>
</ul>
<h3 id="django-내부">Django 내부</h3>
  <img src = "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuM8pz%2FbtqRGpmREuh%2FNtQoBQJzYQeFz1QNKkgCzk%2Fimg.png">
  1. 장고 내부 구성<br>로컬에서 개발 중이라서 runserver하면 127.0.0.1:8000 이 도메인 주소가 된다. 
  <br>
  2. 우리가 브라우저 주소창에 127.0.0.1:8000/board/notice/ 입력하면 127.0.0.1:8000에 해당하는 서버를 찾는다.
  <br>
  3. testDjango프로젝트에 연결이된다. 프로젝트 폴더에 있는 urls.py파일에서 처음으로 URL처리를 시작한다. 
  <BR>
  <IMG SRC = "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR16dT%2FbtqRAlkZrmA%2FWaeorhfpcKaZN4FEdkotoK%2Fimg.png">
    4. URL의 Path에 해당하는 부분을 살펴 본다.
    path에 board가 증장하면 include(board.urls)가서 알아보라고 요청 보낸다. 
  <br>

<pre><code>5. </code></pre><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwo9ZG%2FbtqRpuRA2G2%2F6qPUlFRzd5P69wc2xx3581%2Fimg.png">
board/urls.py 
    <br>
    path에 notice가 있으면 views.py에 있는 notice함수의 규칙대로 처리를 한다. 이 경로의 이름은 notice이다. 


<pre><code>&lt;br&gt;
&lt;br&gt;
출처) https://arotein.tistory.com/6?category=936406#here</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Django] MVT패턴]]></title>
            <link>https://velog.io/@hyunj_523/Django-MVT%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@hyunj_523/Django-MVT%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 27 Jan 2022 01:23:33 GMT</pubDate>
            <description><![CDATA[<h2 id="mvt-패턴이란">MVT 패턴이란?</h2>
<ul>
<li>Model = DB</li>
<li>View = Template (UI)</li>
<li>Controller = view (로직, 템플릿에 전달)</li>
</ul>
<img src = "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpdQ3m%2FbtqwhTpC3gU%2FvXB2IGfXViX7cGFQgXjlR1%2Fimg.png">
<BR>

<ol>
<li>클라이언트로부터 요청 받으면  URLconf이용하여 URL 분석</li>
<li>URL 분석 결과를 통해 해당 URL에 대한 처리를 <strong>담당할 뷰</strong> 결정</li>
<li>뷰는 자신의 로직을 실행하면서 데이터 베이스 처리가 필요하다면 모델을 통해 처리하고 그 결과를 반환 받는다. </li>
<li>뷰는 자신의 로직 처리가 끝나면 템플릿을 사용해 클라이언트에 전송할 HTML 파일 생성한다.</li>
<li>뷰는 최종 결과로 HTML 파일을 클라이언트에게 보내 응답한다. <BR>

</li>
</ol>
<h2 id="urlconf---url-정의">URLconf - URL 정의</h2>
<ul>
<li>클라이언트에서 받을 요청에 들어있는 URL이 urls.py 파일에 정의된 URL 패턴과 매칭되는지 분석한다. </li>
<li>URL 정의하기 위해서는 urls.py파일에 URL과 처리함수(View)로 매핑하는 파이썬 코드를 작성한다. 이런 URL/View 매핑을 URLconf 라고 한다.</li>
</ul>
<pre><code class="language-python">from django.urls import path
  from . import views
  urlpatterns = [
    path(&#39;articles/2003/&#39;, views.special_case_2003),
    path(&#39;articles/&lt;int:year&gt;&#39;, views.year_archive),
    path(&#39;articles/&lt;int:year&gt;/&lt;int:month&gt;/&#39;, views.month_archive),
    path(&#39;articles/&lt;int:year&gt;/&lt;int:month&gt;/&lt;slug:slug&gt;&#39;, views.article_detail),
]</code></pre>
<p>   article/2003 부분이 URL이고 views.special_case_2003 부분이 처리함수(뷰)</p>
<h3 id="url-분석-순서">URL 분석 순서</h3>
<p>  웹 클라이언트가 웹 서버에 페이지 요청 시, 장고에서 URL을 분석하는 순서</p>
<ol>
<li>setting.py 파일의 ROOT_URLCONF 항목을 읽어 최상위 URLconf(urls.py)의 위치를     알아낸다. </li>
<li>URLconf 로딩하여 urlpatterns 변수에 지정되어 있는 URL 리스트를 검사한다.</li>
<li>위에서 부터 순서대로 URL 리스트의 내용을 검사하면서 URL패턴이 매치되면 검사 종료한다.</li>
<li>매치된 URL의 View(method) 를 호출한다. 여기서 뷰는 함수 또는 클래스의 메소드 이다. 호출 시 HttpRequest 객체와 매칭할 때 추출된 단어들을 뷰에 인자로 넘긴다. </li>
<li>URL리스트 끝까지 검사했는데도 매칭에 실패하면 에러처리 뷰 호출한다. </li>
</ol>
<h3 id="path-converter">Path Converter</h3>
<p>위 예제에서 URL패턴을 정의할 때 <a href="int:year">int:year</a> 부분을 path converter라고 한다.
URL패턴의 일부 문자열을 추출하기 위해 사용
__ <a href="type:name">type:name</a> 형식으로 사용한다. __</p>
<h2 id="view---로직정의">View - 로직정의</h2>
<ul>
<li>뷰는 웹 요청을 받아서 DB에 접속 등 해당 어플리케이션의 로직에 맞는 처리, 그 결과 데이터를 HTML로 변환하기 위해 템플릿 처리 한 후 최종 HTML로 응답 데이터를 웹 클라이언트로 변환한다. </li>
<li>장고에서 뷰는 함수 또는 클래스의 메소드로 작성된다.
 다양한 형태의 응답데이터를 만들어 내기 위해 로직을 뷰에 작성한다. 보통 views.py 파일에 작성하지만 원한다면 다른 파일에 작성해도 무방하다. 다만 파이썬 경로에 있는 파일이어야 장고가 찾을 수 있다.<pre><code class="language-python">from django.http import HttpResponse
import datetime
</code></pre>
</li>
</ul>
<p>def current_datetime(request):
    now = datetime.datetime.now();
    html = &quot;<html><body>It is now %s</body></html>&quot; % now
    return HttpResponse(html)</p>
<pre><code>
&lt;현재 날짜와 시간을 HTML로 반환하는 뷰&gt;
함수로 뷰를 작성한 예시
  뷰 함수는 첫번째 인자로 HttpRequest객체를 받는다. 필요한 처리를 한 후에 최종적으로 HttpResponse객체를 반환해준다.

  보통은 별도의 템플릿 파일에 html코드를 작성한다. 
  뷰는 별도로 작성된 템플릿 파일을 해석해서 html코드를 생성하고 이를 httpResponse 객체에 담아서 클라이언트에게 응답한다. 

## Template - 화면 UI 정의
장고가 클라이언트에 반환하는 최종 응답은 HTML 텍스트이다. 개발자가 응답에 사용할 *.html 작성하면 장고는 이를 해석해서 최종 HTML 텍스트 응답을 생성하고, 클라이언트한테 보내준다.

  템플릿 파일은 *.html 확장자를 가지고, 장고의 템플릿 시스템 문법에 맞게 작성한다. 장고에서 템플릿 파일을 찾는 방식알고 적절한 디렉토리에 위치시켜야 한다. 

  장고에서 템플릿 파일 찾을 때 __TEMPLATE_DIRS__ 및 __INSTALLED_APPS__에서 지정된 앱의 디렉토리를 검색한다. 이 항목들은 프로젝트 설정 파일인 __settings.py__에 정의되어 있다. 여러 개의 디렉토리를 지정할 수 있는데, 지정된 순서대로 디렉토리를 검색해 템플릿 파일을 찾는다. 

## MVT 코딩 순서
무엇을 먼저 만들어야하는지 정해진 것은 없다. 

  책에서는 아래 순서로 한다고 한다. 

1. 프로젝트 뼈대 만들기 : 프로젝트 및 앱 개발에 필요한 디렉토리와 파일 생성
2. 모델 코딩하기 : 테이블 관련 사항을 개발(models.py, admin.py 파일)
3. URLconf 코딩하기 : URL 및 뷰 매핑 관계를 정의 (urls.py 파일)
4. 템플릿 코딩하기 : 화면 UI 개발 (templates/디렉토리 하위의 *.html 파일들)
5. 뷰 코딩하기 : 어플리케이션 로직 개발 (views.py 파일) 






</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[REST API ]]></title>
            <link>https://velog.io/@hyunj_523/REST-API-http</link>
            <guid>https://velog.io/@hyunj_523/REST-API-http</guid>
            <pubDate>Mon, 17 Jan 2022 06:20:17 GMT</pubDate>
            <description><![CDATA[<h4 id="api">API</h4>
<p>프로그래밍 언어가 제공하는 기능을 수행할 수 있게 만든 interface</p>
<h4 id="web-api">Web API</h4>
<ul>
<li>웹 어플리케이션 개발에서 다른 서비스에 요청을 보내고 응답 받기 위해 정의된 명세</li>
<li>open api로 활용한다.
** 요청에 JSON으로 응답하는 서버를 만든다.**</li>
</ul>
<h2 id="rest-api-구성">REST API 구성</h2>
<ol>
<li>자원 ULI </li>
<li>행위 HTTP Method</li>
<li>표현 Representations</li>
</ol>
<h3 id="uli">ULI</h3>
<ul>
<li>URL<ul>
<li>통합 자원 위치 (Uniform Resource Locatopr)</li>
<li>네트워크 상에서 자원이 어디 있는지 주소를 알려주는 약속</li>
<li>자원 : html 페이지 , css 문서, 이미지 등..</li>
<li>웹주소, 링크 라고 부름</li>
</ul>
</li>
<li>URN<ul>
<li>통합 자원 이름 (-- Name)</li>
<li>자원의 위치에 영향을 받지 않는 유일한 이름 역할<h4 id="uri-구조">URI 구조</h4>
</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/hyunj_523/post/41e86bac-e436-49e4-8ab2-9fc71865b8a6/image.png" alt=""></p>
<h2 id="http">HTTP</h2>
<ul>
<li>HTML 문서 같은 자원들을 가져올 수 있게 하는 프로토콜</li>
<li>웹에서 이뤄지는 데이터 교환의 기초</li>
<li>요청 : 클라이언트가 전송하는 메세지</li>
<li>응답 : 서버에서 응답으로 전송하는 메세지</li>
<li>특징 <ul>
<li>비연결 지향</li>
<li>무상태 stateless
접속이 끊어지면 클라이언트와 서버 간의 통신이 끝나고 상태를 저장하지 않음<h2 id="http-method">HTTP method</h2>
<h3 id="get">GET</h3>
특정 자원의 표시를 요청한다. 리소스 조회
오직 데이터를 받기만 한다.
서버에 전달하고 싶은 데이터는 query를 통해 전달한다. <h3 id="post">POST</h3>
바디를 통해 서버로 데이터를 전송, 서버에 변경사항을 만든다. 
요청 받은 데이터를 처리, 주로 데이터 등록에 사용한다.
신규 리소스를 등록하거나 프로세스 데이터 처리에 사용<h3 id="put">PUT</h3>
요청한 주소의 자원을 수정한다.<h3 id="delete">DELETE</h3>
지정한 자원을 삭제한다.</li>
</ul>
</li>
</ul>
<h2 id="http-상태-코드">HTTP 상태 코드</h2>
<p>http 상태 코드는 클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능이다.
보통 100번대에서 500번대를 사용하는데 크게 다음과 같이 나눌 수 있다.</p>
<ul>
<li>1xx (Informational): 요청이 수신되어 처리중</li>
<li>2xx (Successful): 요청 정상 처리</li>
<li>3xx (Redirection): 요청을 완료하려면 추가 행동이 필요</li>
<li>4xx (Client Error): 클라이언트 오류, 잘못된 문법등으로 서버가 요청을 수행할 수 없음</li>
<li>5xx (Server Error): 서버 오류, 서버가 정상 요청을 처리하지 못함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Python .whl 파일 만들기]]></title>
            <link>https://velog.io/@hyunj_523/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%B9%8C%EB%93%9C%ED%8C%8C%EC%9D%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@hyunj_523/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%B9%8C%EB%93%9C%ED%8C%8C%EC%9D%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 12 Jan 2022 06:42:56 GMT</pubDate>
            <description><![CDATA[<h3 id="빌드-방법-종류">빌드 방법 종류</h3>
<ul>
<li><p>python setup.py <strong>sdist</strong></p>
<p>   <BR>source distribution을 생성한다.</p>
<p>   source distribution : metadata와 필수 소스들을 제공해주는 포맷</p>
<p>   pip로 설치를 하기 위해서는 build 단계를 거쳐야 한다.</p>
</li>
</ul>
<ul>
<li><p>python setup.py <strong>bdist_wheel</strong></p>
<p>  <BR> pure python wheel 또는 platform wheel을 생성할때 사용한다. 주로 사용한다. </p>
<p>   빌드하게 되면 dist 디렉토리 아래에 whl 파일이 생성되게 된다.</p>
</li>
</ul>
<h3 id="whl-파일-만들기">.whl 파일 만들기</h3>
<pre><code class="language-markdown">&gt; cd setup파일이 있는 디렉토리
&gt; pip install wheel
&gt; python setup.py bdist_wheel
</code></pre>
<p> 위의 커맨드를 실행하면 <code>dist</code> 폴더가 생기고 그 안에 빌드 파일 (.whl)이 생성된다.</p>
<p><strong>[excalc-1.0.0-py3-none-any.whl]</strong></p>
<h3 id="whl-파일-사용하기">.whl 파일 사용하기</h3>
<ol>
<li>.whl 파일 다운받기</li>
<li>개발 작업 진행 중인 디렉터리로 파일을 옮긴다.</li>
<li>cmd에 명령어 입력해 설치</li>
</ol>
<pre><code>pip install ‘파일 명’

    pip install excalc-1.0.0-py3-none-any.whl</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Python 배포 파일 만들기]]></title>
            <link>https://velog.io/@hyunj_523/Python-%EB%B0%B0%ED%8F%AC-%ED%8C%8C%EC%9D%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@hyunj_523/Python-%EB%B0%B0%ED%8F%AC-%ED%8C%8C%EC%9D%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 12 Jan 2022 06:36:45 GMT</pubDate>
            <description><![CDATA[<h3 id="패키지-디렉토리-구조">패키지 디렉토리 구조</h3>
<pre><code class="language-markdown">py_test
|   LICENSE
|   MANIFEST.in
|   README.md
|   setup.cfg
|   setup.py
|
\---calc
    |   main.py
    |   util.py
    |   __init__.py
    |
    \---__pycache__
            util.cpython-39.pyc
            __init__.cpython-39.pyc</code></pre>
<h3 id="순서">순서</h3>
<ol>
<li><p>setuptools 라이브러리  설치</p>
</li>
<li><p><code>setup.py</code>  </p>
<p><code>setup.py</code>을 통해 프로젝트의 최상위 디렉토리를 결정하고 빌드 설정을 한다. </p>
<p>프로젝트 폴더를 만들면 최상위 폴더에 <code>setup.py</code> 파일을 둔다.</p>
<p>python 프로젝트를 빌드하고 배포하려면 <code>setup.py</code> 작성해야 한다.</p>
</li>
<li><p>README.md 파일</p>
<p> 내용 없어도 되지만 파일이 있어야 한다.</p>
</li>
<li><p>패키징 하려는 모듈</p>
</li>
</ol>
<h2 id="setuppy-작성"><strong>setup.py 작성</strong></h2>
<p>setuptools사용해 설정한다. </p>
<p>명령 프롬포트(cmd) 에서 </p>
<pre><code class="language-markdown">pip install setuptools</code></pre>
<h3 id="setuptools-import">setuptools import</h3>
<pre><code class="language-python">import io
from setuptools import find_packages, setup</code></pre>
<h3 id="setup-함수-작성">setup() 함수 작성</h3>
<pre><code class="language-python">import io
from setuptools import find_packages, setup

setup(
    name=&quot;ex-pkg-calc&quot;,
    version=&quot;1.0.0&quot;,
    author=&quot;hyunji&quot;,
    author_email=&quot;hyun_jjj523@naver.com&quot;,
    description=&quot;Simple Calculation by Python&quot;,
    keywords=[&#39;test&#39;,&#39;calculate&#39;],
    install_requires = [ ],
    packages=setuptools.find_packages(),
    classifiers=[ 
        &quot;Programming Language :: Python :: 3&quot;, 
        &quot;License :: OSI Approved :: MIT License&quot;, 
        &quot;Operating System :: OS Independent&quot;,],
    python_requires=&#39;&gt;=3.6&#39;,   
)</code></pre>
<h4 id="파라미터-설명">파라미터 설명</h4>
<ul>
<li><p><strong>name</strong>  = “프로젝트 이름”</p>
<p>일반적으로 모듈명을 배포 파일명으로 사용한다. </p>
<p>이 패키지를 설치/삭제할 때 사용할 이름을 의미한다. 이 이름과 import할 때 쓰이는 이름은 다르다.</p>
</li>
</ul>
<ul>
<li><p><strong>version</strong> = “배포버전”</p>
</li>
<li><p><strong>author</strong> = “저자”</p>
</li>
<li><p><strong>author_email</strong> = “저자의 이메일”</p>
</li>
<li><p><strong>description</strong> = “프로젝트 설명”</p>
</li>
<li><p><strong>keywords</strong></p>
<p>  해당 프로젝트 관련 키워드</p>
</li>
<li><p><strong>python_requires</strong></p>
<p>  지원하는 파이썬 버전 정보</p>
</li>
<li><p><strong>install_requires</strong></p>
<p>  패키지 내에서 import해서 사용하는 다른 라이브러리 패키지를 써 놓는 곳이다. </p>
<p>  여기에 써 놓으면 나중에 pip를 통해 다른 컴퓨터에 설치할 때, 써 놓은 패키지들은 자동으로 같이 설치가 된다.</p>
</li>
<li><p><strong>package_data</strong></p>
<p>  패키지 안에 .py 파일이 아닌 다른 파일을 더 포함 시키고 싶을 때 쓴다.</p>
</li>
<li><p><strong>zip_safe</strong></p>
<p>  위의 package_data 설정을 하였으면 zip_safe 설정도 해주어야 하며 False로 설정해주어야 한다.</p>
</li>
<li><p><strong>classifiers</strong></p>
<p>  PYPI에 등록될 메타 데이터 설정이다. 예를 들어 서포트 하는 python 버젼 정보를 명시할수 있다.\</p>
<p>  하지만 이건 PYPI에 등록될 메타 데이터일 뿐이고, 실제 빌드에는 영향을 주지 않는다.</p>
</li>
<li><p><strong>packages</strong></p>
<p>  업로드할 패키지들을 묶은 폴더명</p>
<p>  find_packages() 함수를 사용하여 package 디렉토리 아래에 있는 모든 패키지들을 포함한다.</p>
  <br>

</li>
</ul>
<h3 id="setupcfg">Setup.cfg</h3>
<p>만일 README 파일이 마크다운 형태로 되어 있다면 <code>setup.cfg</code> 파일을 아래와 같이 설정 해준다.</p>
<pre><code class="language-markdown">[metadata]
description-file = README.md</code></pre>
<h3 id="initpy"><strong><strong>init</strong></strong>.py</h3>
<p>빈 내용의 파일</p>
<p>폴더 안에 <code>__**init__**.py</code> 파일이 있으면 해당 폴더는 패키지로 인식</p>
<p> → 배포할 때 <code>__**init__**.py</code>없으면 배포 되지 않음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모듈과 패키지]]></title>
            <link>https://velog.io/@hyunj_523/Python-%ED%8C%A8%ED%82%A4%EC%A7%80-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@hyunj_523/Python-%ED%8C%A8%ED%82%A4%EC%A7%80-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Wed, 12 Jan 2022 06:31:48 GMT</pubDate>
            <description><![CDATA[<h3 id="모듈">모듈</h3>
<p>함수나 전역 변수, 클래스를 모아놓은 파일</p>
<p>다른 파이썬 프로그램에서 불러와서 사용 할 수 있게 만든 파이썬 파일</p>
<h3 id="모듈-불러올-때">모듈 불러올 때</h3>
<pre><code class="language-python">from 모듈이름 import 함수이름
#변수, 함수, 클래스가 여러개 
from 모듈이름 import *

------------------------------------------------------------------------------------------
import 모듈이름

모듈.함수()</code></pre>
<h3 id="모듈-시작점">모듈 시작점</h3>
<pre><code class="language-python">if __name__ == &quot;__main__&quot;:</code></pre>
<p>이 구문이 있으면 모듈을 명령행으로 실행할 때 바로 실행되도록 한다. 모듈로서 import 될 때에는 실행되지 않는다.</p>
<h3 id="모듈-위치-탐색-순서">모듈 위치 탐색 순서</h3>
<ol>
<li>프로그램 실행된 디렉토리 내 모듈</li>
<li>환경변수 (python path) 에 지정된 디렉토리 내 모듈</li>
<li>파이썬 라이브러리 디렉토리 내모듈 (파이썬 설치 한 곳 아래 Lib)</li>
</ol>
<h3 id="import로-모듈-가져오기">import로 모듈 가져오기</h3>
<ul>
<li>import 모듈</li>
<li>import 모듈1, 모듈2</li>
<li>모듈.변수</li>
<li>모듈.함수</li>
<li>모듈.클래스</li>
<li>import 모듈 as 별칭</li>
<li>별칭.변수 ...</li>
</ul>
<h3 id="패키지">패키지</h3>
<p>패키징 : 모듈별로 생성한 실행 파일들을 하나로 합쳐서 배포 및 설치가 가능한 파일을 만드는 것</p>
<p>모듈을 디렉토리 형식으로 구조화 / 모듈들을 넣어 둔 디렉토리 이름 = 패키지 이름</p>
<p>특정 기능과 관련된 여러 모듈을 묶은 것</p>
<p>폴더 안에 <code>__**init__**.py</code> 파일이 있으면 해당 폴더는 패키지로 인식</p>
<p> → 배포할 때 <code>__**init__**.py</code>없으면 배포 되지 않음</p>
<h3 id="패키지-import-하기">패키지 import 하기</h3>
<p>패키지에 들어있는 모듈 import로 가져옴</p>
<ul>
<li>import 패키지.모듈</li>
<li>import 패키지.모듈1, 패키지.모듈2</li>
<li>패키지.모듈.변수</li>
<li>패키지.모듈.함수</li>
<li>패키지.모듈.클래스</li>
<li>import 패키지.모듈 as 이름</li>
<li>from 패키지 import 모듈</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>