<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>mini_y.log</title>
        <link>https://velog.io/</link>
        <description>Hello! </description>
        <lastBuildDate>Mon, 10 May 2021 13:51:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>mini_y.log</title>
            <url>https://images.velog.io/images/mini_y/profile/1a92005d-99f1-4c70-af12-914b4de3df32/뒷모습.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. mini_y.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/mini_y" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[<Git> 하나의 local에서 두개의 github계정 사용하기]]></title>
            <link>https://velog.io/@mini_y/Git-%ED%95%98%EB%82%98%EC%9D%98-local%EC%97%90%EC%84%9C-%EB%91%90%EA%B0%9C%EC%9D%98-github%EA%B3%84%EC%A0%95-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@mini_y/Git-%ED%95%98%EB%82%98%EC%9D%98-local%EC%97%90%EC%84%9C-%EB%91%90%EA%B0%9C%EC%9D%98-github%EA%B3%84%EC%A0%95-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 10 May 2021 13:51:13 GMT</pubDate>
            <description><![CDATA[<p>개인 깃헙계정과 회사의 깃헙계정이 있을 때 하나의 로컬머신에서 두 계정 모두 쓰고 싶을 때 어떻게 설정해야하는지 정리해보았다.
(구글링해보니 정말 잘 정리해놓은 블로그가 많아서 참고하기 좋았다. 이 글은 그냥 내가 혼자 다시 정리할 겸 쓰는 글~)</p>
<p>크게 정리를 해보자면</p>
<ol>
<li>ssh-key 만들기</li>
<li>github에서 ssh설정하기</li>
<li>ssh config 설정</li>
<li>git config 설정<h2 id="1-ssh-key-만들기">1. ssh-key 만들기</h2>
우선 <code>cd ~/.ssh</code> 로 이동하여<code>ssh-key</code> 만들기
<code>$ ssh-keygen -t rsa -b 4096 -C &quot;본인의 깃헙메일주소&quot;</code>
ex) <code>ssh-keygen -t rsa -b 4096 -C &quot;wjdals.yoon14@gmail.com&quot;</code></li>
</ol>
<p>개인용과 회사용을 구분하기 위해 <code>id_rsa_personal</code>라고 해주기
<code>Enter file in which to save the key (~/.ssh/id_rsa): id_rsa_personal</code>
그러면 예쁜그림?과 함께 ssh-key가 생성된다.</p>
<p>동일한 방법으로 회사용 ssh-key도 만들기! <code>id_rsa_work</code></p>
<pre><code> $ cd ~/.ssh
 $ ls
config              
id_rsa_personal     
id_rsa_work.pub
id_rsa              
id_rsa_personal.pub 
known_hosts
id_rsa.pub          
id_rsa_work</code></pre><p><code>~.pub</code>파일을 복사하여 github에 붙여넣기
터미널창에서 해당 파일 열고 싶을 때? <code>$ cat id_rsa_personal.pub</code>
자세한 설명은 <a href="https://www.youtube.com/watch?v=78rykXw9_0g&amp;t=765s">생활코딩 ssh Youtube</a></p>
<h2 id="2-ssh-config-설정">2. ssh config 설정</h2>
<p><code>~/.ssh</code>에서 <code>config</code>파일 생성하기</p>
<pre><code class="language-vim"># personal
Host personal
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_personal

# work
Host work
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_work</code></pre>
<p><code>IdentityFile</code>에는 private key 파일의 위치를 입력한다.</p>
<h2 id="3-gitconfig-작성">3. .gitconfig 작성</h2>
<p>터미널창에 <code>vim ~/.gitconfig</code> 입력하여 파일 작성하기</p>
<pre><code># This is Git&#39;s per-user configuration file.
[user]
    name = jmyoon     
    email = wjdals.yoon14@gmail.com
# work
[includeIf &quot;/Users/jmin/devel/work/&quot;]     
    path = .gitconfig-work</code></pre><p><code>includeIf &quot;파일경로&quot;</code> 는 해당 파일경로 있는 것들은 <code>.gitconfig-work</code>로 <code>path</code>설정
<code>work</code>디렉토리에 있는 파일들은 회사계정으로 git push, pull되게끔 설정</p>
<h3 id="gitconfig-work-파일-작성하기">.gitconfig-work 파일 작성하기</h3>
<pre><code>[user]
    email = 회사깃헙계정메일주소
    name = 이름
[github]
user = 유저명</code></pre><h2 id="4-확인해보기">4. 확인해보기</h2>
<h3 id="개인계정">개인계정</h3>
<p><code>$ ssh -T git@personal</code>
<code>Hi jeongmin14! You&#39;ve successfully authenticated, but GitHub does not provide shell access.</code></p>
<h3 id="회사계정">회사계정</h3>
<p><code>$ ssh -T git@work</code>
<code>Hi miayoon14! You&#39;ve successfully authenticated, but GitHub does not provide shell access.</code></p>
<h2 id="5-git-clone">5. git clone</h2>
<p>이제 git clone할 때 ssh를 이용한다.
<code>$ git clone git@github.com:jeongmin14/algorithm.git</code>에서
<code>git@github.com</code> 부분을 개인계정일 경우 <code>git@personal</code>로, 회사계정일 경우 <code>git@work</code>로 바꿔주기 (<code>ssh/config</code>에서 작성한 <code>host</code>로 바꿔주면 된다. ex)<code>git@{host}</code>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Rails> 서버 실행시 오류]]></title>
            <link>https://velog.io/@mini_y/Ruby-rails-%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89%EC%8B%9C-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@mini_y/Ruby-rails-%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89%EC%8B%9C-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Sat, 24 Apr 2021 16:42:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/mini_y/post/d0737f3e-ef78-4f27-9d56-592343bdd566/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-25%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.43.38.png" alt=""></p>
<pre><code>Exiting
/Users/jmin/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/puma-5.2.2/lib/puma/binder.rb:288:in `initialize&#39;: Address already in use - bind(2) for &quot;127.0.0.1&quot; port 3000 (Errno::EADDRINUSE)</code></pre><p>오잉? 이미 사용이라고?
구글링ㄱㄱ</p>
<p><code>$ lsof -wni tcp:3000</code> 
입력하니깐
<img src="https://images.velog.io/images/mini_y/post/10801e29-3e34-47a0-9b7d-0db3213f02f4/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-25%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.37.51.png" alt="">
이게 나옴</p>
<p><code>$ kill -9 29768</code> 하고 다시 서버켜주기!
<img src="https://images.velog.io/images/mini_y/post/92c843ad-6214-48c4-ab9d-1485b91a6bd6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-25%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.39.37.png" alt="">
<img src="https://images.velog.io/images/mini_y/post/a2c85d01-18d5-447e-8cec-b0758df8c1bc/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-25%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.41.39.png" alt=""></p>
<p><a href="https://stackoverflow.com/questions/31039998/rails-address-already-in-use-bind2-errnoeaddrinuse">참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Rails> rails 시작하기]]></title>
            <link>https://velog.io/@mini_y/Rails-rails-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@mini_y/Rails-rails-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 22 Apr 2021 08:23:34 GMT</pubDate>
            <description><![CDATA[<h1 id="ruby-install">ruby install</h1>
<p>우선 ruby 버전 확인하기
<code>$ ruby --version</code>
아마 2.6?어쩌고 일거다. stable version으로 해주자</p>
<p>ruby 버전 관리자인 <code>rbenv</code>설치하기
<code>$ brew update</code>
<code>$ brew install rbenv</code></p>
<p>설치할 수 있는 ruby버전 확인하기
<code>$ rbenv install -l</code>
3.0.1설치~
<code>$ rbenv install 3.0.1</code>
<code>$ rbenv rehash</code></p>
<p>ruby 버전 기본값을 3.0.1로 바꿔주기
<code>$ rbenv local 3.0.1</code>
<code>$ rbenv global 3.0.1</code>
버전확인해보기! <code>$ ruby --version</code>
아직도 2.6.어쩌구라면?!
<code>$ vim ~/.zshrc</code> 열어서 맨 아래</p>
<pre><code>eval &quot;$(rbenv init -)&quot;
export PATH=&quot;$HOME/.rbenv/bin:$PATH&quot;</code></pre><p>위 내용 추가한 후 :wq
<img src="https://images.velog.io/images/mini_y/post/4d3a3f88-b391-41cc-9c76-1b7203cc9f40/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.00.58.png" alt="">
끝~</p>
<h1 id="rails-install">Rails install</h1>
<p><code>rbenv</code>환경에는 <code>bundler</code>설치되어 있지않기때문에 설치해주기
<code>$ gem install bundler</code>
그 다음 rails 설치하기
<code>$ gem install rails</code>
<code>$ rbenv rehash</code> 
<code>$ rails -v</code> 버전확인</p>
<h1 id="project-생성하기">project 생성하기</h1>
<p>원하는 디렉토리안에서
<code>$ rails new 프로젝트이름</code></p>
<h1 id="서버구동하기">서버구동하기</h1>
<p><code>$ cd 프로젝트이름</code>
<code>$ rails server -d</code>
뭔가 안된다면..?
<code>$ sqlite3 --version</code> 없으면 설치하기
<code>$ node --version</code> 없으면 설치하기
<code>$ yarn --version</code> 설치하기! -&gt; <code>$ brew install yarn</code>
이제 되겠지? 했는데 안됐음...
<a href="https://www.reddit.com/r/rails/comments/by5b24/error_while_starting_rails_server_v600/">참고</a>
<code>$ rails webpacker:install</code> 설치
<a href="https://edgeguides.rubyonrails.org/webpacker.html">what is webpacker?</a></p>
<p>다시 
<code>$ rails server -d</code>
크롬창켜서
<code>http://127.0.0.1:3000</code> 들어가보면
<img src="https://images.velog.io/images/mini_y/post/4fff9b7f-4a13-4e81-b0aa-6c2ce90db5d1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.47.26.png" alt="">
짠~</p>
<p>갈길이 멀다...</p>
<p><a href="https://guides.rubyonrails.org/getting_started.html">rails guides</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django>[instamg] Direct Message]]></title>
            <link>https://velog.io/@mini_y/instamg-Direct-Message</link>
            <guid>https://velog.io/@mini_y/instamg-Direct-Message</guid>
            <pubDate>Mon, 19 Apr 2021 05:31:44 GMT</pubDate>
            <description><![CDATA[<p>기업협업에서 진행한 프로젝트 instamg 기능 중 DM기능 정리가 필요한 것 같아 포스팅하였다.</p>
<h1 id="direct-message">direct message</h1>
<h2 id="웹소켓-django-channels">웹소켓, django channels</h2>
<p>실시간 채팅을 구현하기 위해서 웹소켓을 사용해야했고 django에서 channels라는 라이브러리를 제공해준다.</p>
<blockquote>
<p>websoket이란?
웹소켓은 프로토콜로서, 실시간으로 데이터를 양방향 통신을 할 수 있게 해주는 기술이다.
소켓이란 쉽게 생각해서 통신을 위한 통로라고 생각하면 된다. web환경은 필요한 정보를 http 기반으로 request를 보내면 response을 보내주는 단순한 프로토콜이다. http 프로토콜은 매 요청과 응답마자 연결을 수립하고 끊는 과정을 반복해야 했기 때문에 유사한 통신을 계속 주고받는 실시간 채팅 같은 기능을 하는데 있어 비효율적이다.
http를 이용하여 실시간 통신의 문제를 해결하기 위해 html5부터 웹소켓이 등장했다.</p>
</blockquote>
<p>웹소켓은 실시간 양방향 통신을 지원하며 한번 연결이 수립되면 클라이언트와 서버 모두 자유롭게 데이터를 보낼 수 있다. 채팅과 같은 연속적인 통신에 대해 계속 유사한 통신을 반복하지 않게 해주어 통신의 효율성도 개선하였다.</p>
<h3 id="웹소켓-핸드쉐이크">웹소켓 핸드쉐이크</h3>
<p>핸드쉐이크는 한번의 http요청과 http요청으로 이루어져 있다.
핸드쉐이크가 끝나면 http 프로토콜을 웹소켓 프로토콜로 변환하여 통신을 하는 구조이다.</p>
<h2 id="channels">channels</h2>
<p>channels는 django에서 HTTP프로토콜이 아닌 다른 프로토콜을 사용할 수 있게 해준다.
웹소켓은 프로토콜의 일종으로서 이를 사용하기 위해서 channels을 이용하게 된다.
channels layer가 생겨 일반적인 HTTP메시지 외에도 웹소켓메시지도 처리할 수 있게 된다.</p>
<h3 id="asgi">ASGI</h3>
<p>ASGI(Asynchronous Server Gateway Interface)는WSGI(Web Server Gateway Interface)를 계승한 것으로 비동기 방식으로 실행된다.
웹 서버와 python 응용프로그램 간의 표준 인터페이스를 제공하기 위해 Django Channels와 배포에 사용되는 Daphne 서버에서 정의한 사양으로서 HTTP, HTTP/2 및 WebSocket와 같은 프로토콜을 지원한다.
ASGI는 비동기 요청인 웹 소켓을 처리하는 이벤트로서 connect, send, receive, disconnect가 있다.</p>
<blockquote>
<p><strong>전체적인 flow</strong>
장고 프로젝트 생성 -&gt; 앱 생성(direct_message) -&gt; channels 라이브러리 설치 -&gt; settings.py의 installes_apps 맨 위에 추가하기 -&gt; consumers.py 작성</p>
</blockquote>
<pre><code class="language-python">import json

from channels.generic.websocket import AsyncWebsocketConsumer

from direct_messages.models     import DirectMessage, DirectMessageAttachFiles, Room
from users.models               import User
from decorators                 import login_check


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        pass


    # Receive message from WebSocket
    def receive(self, text_data):
        data         = json.loads(text_data)
        message      = data[&#39;message&#39;]
        user_account = data[&#39;user_account&#39;]
        room_name    = data[&#39;room_name&#39;]
        rooms        = Room.objects.filter(name__contains=user_account)

        self.send(data=json.dumps({&#39;message&#39;:message}))</code></pre>
<blockquote>
<p>-&gt; routing.py 추가 -&gt; channel layer 구현하기</p>
</blockquote>
<h3 id="channel-layer란">channel layer란?</h3>
<p>의사소통 시스템이다. 즉 많은 소비자들 더 나아가서 django의 다른 부분과 의사소통을 할 수 있게 해준다. channel layer에는 두 가지 개념이 존재한다.</p>
<p><strong>channel</strong>
channel은 메시지를 보낼 수 있는 우편함이라고 생각하면 된다. 각 채널은 고유한 이름이 있으며 누구든지 채널에 메시지를 보낼 수 있다.</p>
<p><strong>group</strong>
group은 채널과 관련된 그룹이다. 그룹도 마찬가지로 이름을 가지며 그룹 이름을 가진 사용자는 누구나 그룹에 채널을 추가, 삭제 가능하고 그룹의 모든 채널에게 메세지를 보낼 수 있다. 그러나 그룹에 속한 채널을 나열 할 수는 없다.</p>
<p>소비자들은 채널 이름을 하나씩 가지고 있으며 channel layer를 통해 메시지를 주고 받을 수 있다.</p>
<p><strong>channel layer 구현하기</strong>
<code>$ brew install redis redis</code> 설치하기
<code>$ redis-server redis-server</code> 돌려주기
<code>$ pip install channels_redis</code> channels 가 redis 인터페이스를 얻을 수 있도록 channels_redis 패키지를 설치</p>
<blockquote>
<p>-&gt; settings.py에서 channel layer 설정하기</p>
</blockquote>
<pre><code class="language-python">
# Channels
ASGI_APPLICATION = &#39;instamg.asgi.application&#39;
CHANNEL_LAYERS = {
    &#39;default&#39;: {
        &#39;BACKEND&#39;: &#39;channels_redis.core.RedisChannelLayer&#39;,
        &#39;CONFIG&#39;: {
            &quot;hosts&quot;: [(&#39;127.0.0.1&#39;, 6379)],
        },
    },</code></pre>
<blockquote>
<p>-&gt; consumers.py 추가하기 -&gt; consumers.py 비동기식으로 작성하기</p>
</blockquote>
<p>consumer를 비동기식으로 작성하게 되면 요청을 처리할 때, 추가적인 스레드를 생성하지 않는다. 즉 성능개선을 불러올 수 있다. 실시간 채팅같은 경우 성능이 굉장히 중요하기 때문에 이 과정이 필요하다.
sync_to_async를 통해서 django 코드를 비동기적으로 바꿀 수 있지만 async-native라이브러리보다 성능이 떨어진다.</p>
<p>ChatConsumer가 상속받는 것이 WebsocketConsumer 에서 <code>AsyncWebsocketConsumer</code> 으로 바뀌었다. 또한 메소드 선언 시, <code>async</code> 가 붙고, 메소드를 호출 하는 경우에도 <code>await</code> 를 붙여서 비동기 처리를 했다.</p>
<p><strong>Database Access</strong>
Django ORM은 동기식 코드이기 때문에 위와 같이 consumer를 비동기식으로 작성한 상태에서 접근하기 위해서는 해줘야할 작업이 있다.</p>
<pre><code class="language-python"># database_sync_to_async import 해주기
from channels.db  import database_sync_to_async

    @database_sync_to_async
    def create_message(self, user_account, message, rooms, room_name):
        if len(rooms) &gt; 0:
            talked_user = sorted(room_name.split(user_account))[-1]
            for room in rooms:
                if talked_user in room.name and user_account in room.name:
                    room_name = room.name

        if not Room.objects.filter(name=room_name).exists():
            Room.objects.create(
                name = room_name
            )

        return DirectMessage.objects.create(
            room_id = Room.objects.get(name=room_name),
            user_id = User.objects.get(account=user_account),
            message = message
            )

    @database_sync_to_async
    def get_created_at(self):
        return DirectMessage.objects.last().created_at

 # 데코레이터로 사용하기 @database_sync_to_async</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL 첫걸음 정리1]]></title>
            <link>https://velog.io/@mini_y/SQL-%EC%B2%AB%EA%B1%B8%EC%9D%8C-%EC%A0%95%EB%A6%AC1</link>
            <guid>https://velog.io/@mini_y/SQL-%EC%B2%AB%EA%B1%B8%EC%9D%8C-%EC%A0%95%EB%A6%AC1</guid>
            <pubDate>Wed, 14 Apr 2021 09:26:41 GMT</pubDate>
            <description><![CDATA[<p>데이터베이스는 2차 프로젝트때 쓰인 데이터(bearbnb)와 책에서 제공하는 데이터(sample)를 이용함</p>
<h1 id="mysql">MySQL</h1>
<h3 id="sql에서의-hello-world">SQL에서의 &quot;Hello World&quot;</h3>
<pre><code class="language-sql">select * from &lt;테이블명&gt;; //*은 모든 컬럼(열)을 의미한다.

select id, name from users; //users테이블에서 id, name 가져오기
// 지정한 컬럼 순서대로 표시됨(id, name)</code></pre>
<h3 id="where-조건문">where 조건문</h3>
<pre><code class="language-sql">select * from &lt;테이블명&gt; where &lt;조건&gt;;
// 조건식을 만족한(참) 행만 결과값으로 반환한다.

select * from users where name=&#39;서진호&#39;;
select * from users where birthdate=&#39;1980-07-08&#39;;
select * from users where country_id=6;
select * from users where country_id&lt;&gt;1;
//문자열형, 시간날짜형은 &#39; &#39;으로 감싸기
// = 같다, &lt;&gt;같지않다

select * from users where profile_photo is null;
// null값을 검색할 경우 is null쓰기!

select * from users where profile_photo=null; //잘못된 예
Empty set (0.00 sec)</code></pre>
<h3 id="and-or-조합하기">and, or 조합하기</h3>
<p>and 먼저 계산됨</p>
<pre><code class="language-sql">select * from users where gender=&#39;여자&#39; or country_id&lt;&gt;1 and is_email_valid=1;

//(country_id&lt;&gt;1 and is_email_valid=1)먼저 실행한 뒤 
// or gender=&#39;여자&#39; 실행
country_id가 1이 아니고 is_email_valid가 1인 user이거나 또는 gender=&#39;여자&#39;인 경우</code></pre>
<h3 id="like-패턴-매칭">like 패턴 매칭</h3>
<pre><code class="language-sql">select * from users where name like &#39;김%&#39;;
// name컬럼에 &#39;김&#39;이 들어간(매칭되는) 데이터 검색</code></pre>
<p>?% 전방일치
%?% 중간일치
%? 후방일치</p>
<h3 id="order-by-정렬하기">order by 정렬하기</h3>
<pre><code class="language-sql">select * from &lt;table명&gt; where &lt;조건식&gt; order by &lt;컬럼명&gt;;
// 지정된 커럼명에 따라 행 순서가 변경된다. default 오름차순

select * from users where gender=&#39;여자&#39; order by birthdate;
// 여자 users를 생년월일 오름차순(나이많은-&gt;젊은)으로 정렬
select * from users where gender=&#39;여자&#39; order by birthdate desc; // 내림차순 정렬</code></pre>
<p>desc 내림차순
asc 오름차순
null값인 경우, 가장 작은 값으로 취급한다.</p>
<h4 id="복수-정렬">복수 정렬</h4>
<pre><code class="language-sql">select * from &lt;table명&gt; order by &lt;컬럼1&gt;, &lt;컬럼2&gt;;
// 먼저 &lt;컬럼1&gt;로 정렬한 뒤 &lt;컬럼2&gt; 정렬
// order by &lt;컬럼1&gt;, &lt;컬럼2&gt;와 order by &lt;컬럼2&gt;, &lt;컬럼1&gt;는 다름</code></pre>
<h3 id="limit-결과-행-제한하기">limit 결과 행 제한하기</h3>
<pre><code class="language-sql">select &lt;컬럼명&gt; from &lt;table명&gt; limit &lt;표시할 행 수&gt;[offset 시작행];
// offset에 의한 시작 위치 지정은 limit뒤에 기술한다.
// &#39;시작할 행-1&#39;기억하기! ex)첫 번째 행부터 5개씩 가져온다면 1-1로 위치는 0이 되어 offset은0으로 지정하면 된다.
// ~limit 5 (offset0은 생략가능);
// ~limit 5 offset5; -&gt;6번째 행부터 5개씩 정렬

select &lt;컬럼명&gt; from &lt;table명&gt; where &lt;조건식&gt; order by &lt;컬럼명&gt; limit &lt;표시할 행 수&gt;;
// limit은 반환할 행수를 제한하는 기능, where구로 검색한 뒤 order by로 정렬한 뒤에 최종적으로 처리된다.</code></pre>
<p>서버내부 처리 순서 
where구 -&gt; select구 -&gt; order by구</p>
<h3 id="수치-연산">수치 연산</h3>
<p>컬럼명이 id, price, quantity인 table1이 있다.</p>
<pre><code class="language-sql">select *, price*quantity as &#39;금액&#39; from table1;
// price*quantity, 가격과 수량을 곱한 값을 새로운 컬럼 생성
// 연산하여 생긴 컬럼은 as와 함께 별명을 붙일 수 있다.(as생략가능)</code></pre>
<h4 id="round-함수">round 함수</h4>
<p><code>round()</code>를 이용하여 반올림하기, 기본적으로 소수점 첫째 자리를 기준으로 반올림한 값을 반환한다.</p>
<pre><code class="language-sql">select amount, round(amount, 1) from table1;
// amount열에 있는 값들을 소수점 둘째자리에서 반올림</code></pre>
<h3 id="문자열-연산">문자열 연산</h3>
<p><code>concat()</code>를 이용하여 단위 연결하기</p>
<pre><code class="language-sql">mysql&gt; select *, concat(quantity,unit) as du from sample35;
+------+-------+----------+------+-------+
| no   | price | quantity | unit | du    |
+------+-------+----------+------+-------+
|    1 |   100 |       10 | 개   | 10개  |
|    2 |   230 |       24 | 통   | 24통  |
|    3 |  1980 |        1 | 장   | 1장   |
+------+-------+----------+------+-------+</code></pre>
<p><code>substring()</code>은 문자열의 일부분을 계산해서 반환해주는 함수이다.</p>
<pre><code class="language-sql">mysql&gt; select id,name,birthdate,substring(birthdate,1,4) as year from users where name=&#39;조경숙&#39;;
+----+-----------+------------+------+
| id | name      | birthdate  | year |
+----+-----------+------------+------+
|  5 | 조경숙    | 2017-06-25 | 2017 |
+----+-----------+------------+------+</code></pre>
<p><code>trim()</code>은 문자열의 앞뒤로 여분의 스페이스가 있을 경우 이를 제거해주는 함수이다.
<code>character_length()</code>는 문자열의 길이를 계산하여 반환해주는 함수이다. <code>char_length()</code>로 줄여서 쓸 수 있다.</p>
<h3 id="case문으로-데이터-변환하기">case문으로 데이터 변환하기</h3>
<pre><code class="language-sql">case when &lt;조건식1&gt; then &lt;식1&gt; [when &lt;조건식2&gt; then &lt;식2&gt;] [else &lt;식3&gt;] end</code></pre>
<p>when에는 참과 거짓을 반환할 수 있는 조건식을 쓴다. when절이 참일 경우 then절에 쓰인 식이 처리된다. 조건절에 만족하지 못할 경우 else에 쓰인 식이 처리됨(else가 생력되어 있을 경우 null값으로 처리됨)</p>
<pre><code class="language-sql">mysql&gt; select id, title, story_profile, case when story_profile=1 then &quot;저장&quot; else &quot;저장안함&quot; end as &quot;저장여부&quot; from stories limit 5;

select &lt;컬럼명&gt; ,case &lt;조건문이 쓰일 컬럼명&gt; when &lt;조건식&gt; then &lt;식&gt; else &lt;식2&gt; end as &lt;지정할 컬럼명&gt; from &lt;테이블명&gt;;

mysql&gt; select id, title, story_profile, case story_profile when 1 then &quot;저장&quot; else &quot;저장안함&quot; end as &quot;저장여부&quot; from stories limit 5;
+----+------------------------+---------------+--------------+
| id | title                  | story_profile | 저장여부     |
+----+------------------------+---------------+--------------+
|  1 | 성현님 생일파티        |             0 | 저장안함     |
|  2 | 화이링!                |             0 | 저장안함     |
|  3 | 화이링!                |             0 | 저장안함     |
|  4 | 우리팀 화이링!         |             1 | 저장         |
|  5 | story save!            |             1 | 저장         |
+----+------------------------+---------------+--------------+</code></pre>
<h4 id="coalesce">coalesce</h4>
<p>null값을 변환하는 경우 쓰이는 식</p>
<pre><code class="language-sql">mysql&gt; select * from sample37;
+------+
| a    |
+------+
|    1 |
|    2 |
| NULL |
+------+

mysql&gt; select a,coalesce(a,0) as&quot;null값 바꾸기&quot; from sample37;
+------+-------------------+
| a    | null값 바꾸기     |
+------+-------------------+
|    1 |                 1 |
|    2 |                 2 |
| NULL |                 0 |
+------+-------------------+</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[RESTful API]]></title>
            <link>https://velog.io/@mini_y/RESTful-API</link>
            <guid>https://velog.io/@mini_y/RESTful-API</guid>
            <pubDate>Thu, 25 Mar 2021 09:31:15 GMT</pubDate>
            <description><![CDATA[<h1 id="rest란">REST란?</h1>
<blockquote>
<p>REST의 정의 : Representation State Transfer의 약자</p>
</blockquote>
<p>자원의 이름(자원의 표현)으로 구분하여 해당 자원의 상태(정보)를 주고 받는 모든 것을 의미한다.
자원(resource) : 해당 소프트웨어가 관리하는 모든 것(문서, 그림, 데이터 등)
상태(정보) 전달 : 데이터가 요청되어지는 시점에서 자원의 정보를 전달한다.</p>
<h2 id="rest의-구체적인-개념">REST의 구체적인 개념</h2>
<p>HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD 기능을 적용하는 것을 의미한다.</p>
<h3 id="구성요소">구성요소</h3>
<ol>
<li>자원(resource) : URI</li>
<li>행위 : HTTP 프로토콜의 Method를 사용한다.</li>
<li>표현 : client가 자원의 상태(정보)에 대한 조작을 요청하면 server는 이에 적절한 응답을 보낸다. REST에서 하나의 자원은 JSON, XML, TEXT, RSS 등 여러형태의 응답으로 나타내어 질 수 있다.(JSON으로 데이터를 주고 받는 것이 일반적이다.)</li>
</ol>
<h3 id="장점">장점</h3>
<ul>
<li>HTTP 프로토콜의 인프라를 그대로 사용하므로 REST API 사용을 위한 별도의 인프라를 구축할 필요가 없다.</li>
<li>REST API 메시지가 의도하는 바를 명확하게 나타내므로 의도하는 바를 쉽게 파악할 수 있다.</li>
<li>서버와 클라이언트의 역할을 명확하게 구분한다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>표준이 존재하지 않는다.</li>
<li>사용할 수 있는 메소드가 4가지 밖에 없다.(HTTP method 형태가 제한적)</li>
</ul>
<h3 id="rest가-필요한-이유">REST가 필요한 이유</h3>
<ul>
<li>애플리케이션 분리 및 통합</li>
<li>다양한 클라이언트의 등장</li>
<li>죄근의 서버 프로그램은 다양한 브라우저와 안드로이폰, 아이폰과 같은 모바일 디바이스에도 통신을 할 수 있어야 한다.</li>
</ul>
<h3 id="rest-특징">REST 특징</h3>
<ol>
<li>Server-Client(서버-클라이언트 구조)</li>
<li>Stateless(무상태)</li>
<li>Cacheable(캐시 처리 가능)</li>
<li>Layered System(계층화)</li>
<li>Code-On-Demand(optional)</li>
<li>Uniform Interface(인터페이스 일관성)</li>
</ol>
<h1 id="rest-api">REST API</h1>
<h2 id="api란">API란?</h2>
<p>데이터와 기능의 집합을 제공하여 컴퓨터 프로그램간 상호작용을 촉진하며, 서로 정보를 교환가능 하도록하는것</p>
<p><code>REST API의 정의 : REST 기반으로 서비스 API를 구현한 것</code></p>
<h3 id="rest-api의-특징">REST API의 특징</h3>
<ul>
<li>REST 기반으로 시스템을 분산해 확장성과 재사용성을 높여 유지보수 및 운용을 편리하게 할 수 있다.</li>
<li>REST는 HTTP 표준으로 기반으로 구현하므로, HTTP를 지원하는 프로그램 언어로 클라이언트, 서버를 구현할 수 있다.</li>
</ul>
<h3 id="restful-api-설계시-주의할-점">RESTful API 설계시 주의할 점</h3>
<ul>
<li>url는 정보의 자원을 표현해야한다.(resource명은 동사보다 명사로 사용)</li>
<li>자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE 등)으로 표현</li>
<li>슬래시(/)는 계층관계를 나타내는데 사용, uri마지막 문자로 포함하지 않는다.</li>
<li>하이픈(-)운 uri 가독성을 높이는데 사용</li>
<li>uri 경로에는 소문자가 적합하다.</li>
<li>파일확장자는 uri에 포함하지 않는다.</li>
</ul>
<h1 id="그래서-restful한-api뭔데">그래서 RESTful한 API뭔데???</h1>
<p>이해하기 쉽고 사용하기 쉬운 api를 restful한 api라고한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django> [instamg] JsonResponse 2개이상 보내기]]></title>
            <link>https://velog.io/@mini_y/instamg-JsonResponse-2%EA%B0%9C%EC%9D%B4%EC%83%81-%EB%B3%B4%EB%82%B4%EA%B8%B0</link>
            <guid>https://velog.io/@mini_y/instamg-JsonResponse-2%EA%B0%9C%EC%9D%B4%EC%83%81-%EB%B3%B4%EB%82%B4%EA%B8%B0</guid>
            <pubDate>Mon, 08 Mar 2021 11:30:18 GMT</pubDate>
            <description><![CDATA[<p>기존코드에서는 댓글, 대댓글 작성시 성공메시지만 전달했다.
프론트단에서 댓글생성과 동시에 바로 댓글 삭제 또는 수정을 하고 싶다면?
작성한 commnet의 정보(id, comment작성한 user account등)가 필요하다.</p>
<p>댓글생성이 성공하면 success message와 comment data를 함께준다면 가능하지 않을까..?</p>
<pre><code class="language-python">from django.http  import JsonResponse, HttpResponse

.
.
.
class CommentView(View):
    @login_check
    def post(self, request, post_id):
        try:
            data = json.loads(request.body)

            if data.get(&#39;comment_id&#39;) is not None:
                comment_id = Comment.objects.get(id=data.get(&#39;comment_id&#39;))
            else:
                comment_id = None

            Comment(
                post_id    = Post.objects.get(id=post_id),
                user_id    = request.user,
                content    = data[&#39;content&#39;],
                comment_id = comment_id
            ).save() #생성된 댓글 저장

            comment_last = Comment.objects.last() #생성된 댓글 object
            comment_data = {
                &#39;comment_id&#39;      : comment_last.id,
                &#39;comment_content&#39; : data[&#39;content&#39;],
                &#39;user_account&#39;    : request.user.account,
                &#39;user_profile&#39;    : &quot;media/&quot;+ str(request.user.thumbnail_path) if str(request.user.thumbnail_path) else None
            } #생성된 댓글 정보
            return HttpResponse(json.dumps({&#39;message&#39;: &#39;SUCCESS&#39;, &#39;comment_data&#39;: comment_data}))
        except KeyError:
            return JsonResponse({&#39;message&#39;:&#39;KEY_ERROR&#39;}, status=400)
        except Post.DoesNotExist :
            return JsonResponse({&#39;message&#39;:&#39;POST_DOES_NOT_EXIST&#39;}, status=400)</code></pre>
<blockquote>
<p>return HttpResponse(json.dumps({&#39;message&#39;: &#39;SUCCESS&#39;, &#39;comment_data&#39;: comment_data}))</p>
</blockquote>
<p>HttpResponse로 보내줄 데이터들을 json.dumps에 싸서 보내주기???</p>
<pre><code class="language-JSON">{
    &quot;message&quot;: &quot;SUCCESS&quot;,
    &quot;comment_data&quot;: {
        &quot;comment_id&quot;: 913,
        &quot;comment_content&quot;: &quot;댓글달기 http다시도전 이번엔 제발&quot;,
        &quot;user_account&quot;: &quot;burgerqueen&quot;,
        &quot;user_profile&quot;: &quot;media/thumbnail/slackselfi_zPwpVQY.jpeg&quot;
    }
}</code></pre>
<p><a href="https://stackoverflow.com/questions/18152126/return-multiple-items-from-a-django-view-ajax">참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[select_related와 prefetch_related]]></title>
            <link>https://velog.io/@mini_y/selectrelated%EC%99%80-prefetchrelated</link>
            <guid>https://velog.io/@mini_y/selectrelated%EC%99%80-prefetchrelated</guid>
            <pubDate>Mon, 08 Mar 2021 08:40:28 GMT</pubDate>
            <description><![CDATA[<p>Django ORM 사용할 때 Query 개수를 줄일 수 있는 방법에는 select_related와 prefetch_related가 있다.</p>
<p>select_related 와 prefetch_related 는 하나의 QuerySet을 가져올 때, 미리 related objects들까지 다 불러와주는 함수이다.<br>그렇게 불러온 data들은 모두 cache에 남아있게 되므로 DB에 다시 접근해야 하는 수고를 덜어줄 수 있다. 이렇게 두 함수 모두 DB에 접근하는 수를 줄여, performance를 향상시켜준다는 측면에서는 공통점이 있지만, 그 방식에는 차이점이 있다.</p>
<h2 id="예시모델">예시모델</h2>
<pre><code class="language-python">from django.db import models

class Publisher(models.Model):
    name = models.CharField(max_length=255)

    class Meta:
        db_table = &#39;publishers&#39;

    def __str__(self):
        return self.name


class Book(models.Model):
    name = models.CharField(max_length=255)
    price = models.IntegerField(default=0)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)

    class Meta:
        db_table = &#39;books&#39;

    def __str__(self):
        return self.name


class Store(models.Model):
    name = models.CharField(max_length=255)
    books = models.ManyToManyField(Book)

    class Meta:
        db_table = &#39;stores&#39;

    def __str__(self):
        return self.name</code></pre>
<h1 id="select_related">select_related</h1>
<p>select_related는 쿼리셋을 반환할 때 foreign-key, OneToOne관계인 모델을 가져오는 ORM이다. (SQL의 JOIN을 사용함)</p>
<h3 id="정참조onetomany관계에서-db-hit-줄이는-방법">정참조(OneToMany관계)에서 DB hit 줄이는 방법</h3>
<p>views.py(select_related 적용 전)</p>
<pre><code class="language-python">class BooksWithAllMethodView(View):
    def get(self, request):
        queryset = Book.objects.all()

        books = []
        for book in queryset:  # queryset 평가
            books.append({
                &#39;id&#39;: book.id,
                &#39;name&#39;: book.name,
                &#39;publisher&#39;: book.publisher.name # book.publisher에 접근, 캐싱되지 않은 데이터이므로 query 발생
                }
            )
        return JsonResponse({&#39;books_with_all_method&#39; : books }, status=200)</code></pre>
<p>실행된 쿼리문</p>
<pre><code class="language-sql">SELECT books.id, books.name, books.price, books.publisher_id
FROM books 

SELECT publishers.id, publishers.name
FROM publishers
WHERE publishers.id = 1

SELECT publishers.id, publishers.name
FROM publishers
WHERE publishers.id = 1

SELECT publishers.id, publishers.name
FROM publishers
WHERE publishers.id = 1

...</code></pre>
<p>Book 모델에서 data를 가져오는 쿼리 1개
Book모델에 걸려있는 publishers모델을 가져오는 쿼리(book의 개수만큼 쿼리 발생)</p>
<hr>
<p>views.py(select_related 적용 후)</p>
<pre><code class="language-python">class BooksWithSelectRelatedView(View):
    def get(self, request):
        queryset = Book.objects.select_related(&quot;publisher&quot;).all()

        books = []
        for book in queryset:  # queryset 평가
            books.append({
                &#39;id&#39;: book.id,
                &#39;name&#39;: book.name,
                &#39;publisher&#39;: book.publisher.name
                }
            )
        return JsonResponse({&#39;books_with_all_method&#39; : books }, status=200)</code></pre>
<p>실행된 쿼리문</p>
<pre><code class="language-sql">SELECT books.id, books.name, books.price, books.publisher_id, publishers.id, publishers.name 
FROM books 
INNER JOIN publishers 
ON books.publisher_id = publishers.id</code></pre>
<p>select_related는 INNER JOIN으로 data를 가져오기 때문에 쿼리문 하나로 해결가능!</p>
<h1 id="prefetch_related">prefetch_related</h1>
<p>prefetch_related은 foreign-key, OneToOne뿐만 아니라 ManyToMany, ManyToOne등 모든 관계에서 쓰일 수 있다.</p>
<h3 id="역참조many-to-many-관계-상황에서-db-hit-줄이는-방법">역참조(Many-To-Many 관계) 상황에서 DB hit 줄이는 방법</h3>
<p>views.py(prefetch_related 적용 전)</p>
<pre><code class="language-python">class StoresWithAllMethodView(View):
    def get(self, request):
        queryset = Store.objects.all()

        stores = []
        for store in queryset:  # 쿼리셋 평가
            books = [book.name for book in store.books.all()]  # 각 store마다 books로 접근, 쿼리 발생
            stores.append({
                &#39;id&#39;: store.id,
                &#39;name&#39;: store.name,
                &#39;books&#39;: books
                }
            )
        return JsonResponse({&#39;stores_with_all_method&#39; : stores }, status=200)</code></pre>
<p>실행된 쿼리문</p>
<pre><code class="language-sql">SELECT stores.id, stores.name
FROM stores;

SELECT books.id, books.name, books.price, books.publisher_id
FROM books 
INNER JOIN stores_books 
ON books.id = stores_books.book_id 
WHERE stores_books.store_id = 1

SELECT books.id, books.name, books.price, books.publisher_id
FROM books 
INNER JOIN stores_books 
ON books.id = stores_books.book_id 
WHERE stores_books.store_id = 1

SELECT books.id, books.name, books.price, books.publisher_id
FROM books 
INNER JOIN stores_books 
ON books.id = stores_books.book_id 
WHERE stores_books.store_id = 1

SELECT books.id, books.name, books.price, books.publisher_id
FROM books 
INNER JOIN stores_books 
ON books.id = stores_books.book_id 
WHERE stores_books.store_id = 1

...</code></pre>
<hr>
<p>views.py(prefetch_related 적용 후)</p>
<pre><code class="language-python">class StoresWithPrefetchRelatedView(View):
    def get(self, request):
        queryset = Store.objects.prefetch_related(&quot;books&quot;).all()

        stores = []
        for store in queryset:
            books = [book.name for book in store.books.all()]
            stores.append({&#39;id&#39;: store.id, &#39;name&#39;: store.name, &#39;books&#39;: books})

        return JsonResponse({&#39;stores_with_prefetch_related&#39; : stores }, status=200)</code></pre>
<p>실행된 쿼리문</p>
<pre><code class="language-sql">SELECT stores.id, stores.name
FROM stores;

SELECT stores_books.store_id, books.id, books.name, books.publisher_id 
FROM books 
INNER JOIN 
stores_books 
ON books.id = stores_books.book_id 
WHERE stores_books.store_id IN (1, 2, 3, 4, 10, 50, ....) ;</code></pre>
<p>Store.objects.all() 이라는 쿼리가 실행되면서 동시에 store에 걸려있는 book의 data들이 cache에 저장된다. 
store의 수 만큼 book이 실행되더라도 DB에 접근하지 않고 cache에서 찾아서 쓰게 된다.</p>
<h1 id="정리">정리</h1>
<p>prefetch_related은 원래의 main query가 실행된 후 별도의 query를 따로 실행하고 select_related은 하나의 query만으로 related objects들을 다 가져온다.</p>
<p>MTM MTO의 관계에서는 prefetch_related를 사용해야 하지만 foreign_key, OTO과 같은 single-valued 관계가 있는 곳에서는 최대한 select_related를 사용하여 query수를 줄여주는 것이 효과적!</p>
<p><a href="https://docs.djangoproject.com/en/2.2/topics/db/queries/#caching-and-querysets">Caching and QuerySets</a>
<a href="https://jay-ji.tistory.com/35">참고</a>
<a href="https://jupiny.tistory.com/entry/selectrelated%EC%99%80-prefetchrelated">참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django> Datetime]]></title>
            <link>https://velog.io/@mini_y/Django-Datetime</link>
            <guid>https://velog.io/@mini_y/Django-Datetime</guid>
            <pubDate>Sun, 07 Mar 2021 09:06:30 GMT</pubDate>
            <description><![CDATA[<h2 id="pytz">pytz</h2>
<p><code>pytz</code>라이브러리는 <code>datetime</code>을 쉽게 다룰 수 있도록 도와준다.</p>
<blockquote>
<p>pytz가 제공하는 시간대 식별자 확인하기</p>
</blockquote>
<pre><code class="language-python">import pytz

for tz in pytz.all_timezones:
    print(tz)
   # 출력결과
   Africa/Abidjan
   Africa/Accra
   Africa/Addis_Ababa
   Africa/Algiers
   Africa/Asmara
   Africa/Asmera
   Africa/Bamako
   Africa/Bangui
   Africa/Banjul
   Africa/Bissau
   .
   .
</code></pre>
<blockquote>
<p>settings.py에서 설정한 시간 출력</p>
</blockquote>
<pre><code class="language-python">from datetime     import datetime
from pytz         import utc

now = datetime.now()
print(now) 
//2021-02-27 14:20:08.235438</code></pre>
<h2 id="datetime">Datetime</h2>
<p>python에서 날짜와 시간은 표현하는 아주 유용한 패키지이다.</p>
<p>데이터베이스의 시간은 utc로 저장하고 데이커를 가지고올 때 timezone을 localtime으로 바꿔주는 것이 좋다.</p>
<p>django에서 프로젝트 settings.py에서 <code>USE_TZ=true TIME_ZONE=&#39;Asia/Seoul&#39;</code>로 설정하기</p>
<h3 id="naive-vs-aware">Naive VS Aware</h3>
<ul>
<li>Naive
tzinfo 속성이 설정되지 않은 datetime 객체 (TIME_ZONE 변수로 지정한 시간대 사용)</li>
<li>Aware
tzinfo 속성이 설정된 datetime 객체 (명확하게 기준이 되는 시간대를 tzinfo 속성으로 지정)</li>
</ul>
<h3 id="use_tz-설정에-따른-차이">USE_TZ 설정에 따른 차이</h3>
<p>장고는 내부적으로 Aware datetime 객체를 사용한다. 즉 장고 개발 시 Aware datetime 객체를 사용해야 한다. (기준 시간대를 명확히 지정해줘야 함)</p>
<p>폼에 입력된 날짜 값은 Current time zone으로 해석한 뒤 Aware datetime 객체로 변환되고, 템플릿에 렌더링할 때는 Aware datetime 객체를 Current time zone으로 변환한다.</p>
<p>※ 만약 Naive datetime 객체를 사용하면 어떻게 될까?</p>
<p>하위호환성을 위해 Naive datetime 객체를 사용해도 문제가 없도록 설계가 되어 있다. 데이터베이스에 저장될 때 Naive datetime 객체를 Aware datetime 객체로 변환하고, 경고만 띄워준다. 하지만 그것도 DST 등으로 인한 문제가 발생할 여지는 있기 때문에, 항상 Aware datetime 객체를 사용하는 게 안전하다.</p>
<h2 id="utc---localtime으로-바꿔서-보여주기">UTC -&gt; localtime으로 바꿔서 보여주기</h2>
<pre><code class="language-python">from datetime import datetime
from pytz import timezone

now_utc = datetime.now(timezone(&#39;UTC&#39;)) # 현재시간을 utc로 표시
now_kst = now_utc.astimezone(timezone(&#39;Asia/Seoul&#39;)) # utc를 kst로 변경</code></pre>
<h2 id="데이터베이스에-저장된-시간은-utc로-설정하고-데이터를-보여줄-때-한국시간으로-보여주기">데이터베이스에 저장된 시간은 utc로 설정하고 데이터를 보여줄 때 한국시간으로 보여주기</h2>
<pre><code class="language-python"># 기존코드 ,db에 저장된 그대로 나옴(utc)
&quot;created_at&quot;     : post.created_at
# 결과 
//“created_at”: “2021-03-06T07:55:02.563Z”,


# 한국시간으로 보여주기
&quot;created_at&quot;     : post.created_at.astimezone(timezone(&#39;Asia/Seoul&#39;))
# 결과
//“local_created_at”: “2021-03-06T16:55:02.563+09:00&quot;,</code></pre>
<p><a href="https://spoqa.github.io/2019/02/15/python-timezone.html">참고</a>
<a href="https://show-me-the-money.tistory.com/entry/Python%EC%97%90%EC%84%9C-Datetime%EA%B3%BC-Timezone-%EB%8B%A4%EB%A3%A8%EA%B8%B0">참고</a>
<a href="https://8percent.github.io/2017-05-31/django-timezone-problem/">참고</a>
<a href="https://it-eldorado.tistory.com/13">참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mysql DB 백업과 복원]]></title>
            <link>https://velog.io/@mini_y/mysql-DB-%EB%B0%B1%EC%97%85%EA%B3%BC-%EB%B3%B5%EC%9B%90</link>
            <guid>https://velog.io/@mini_y/mysql-DB-%EB%B0%B1%EC%97%85%EA%B3%BC-%EB%B3%B5%EC%9B%90</guid>
            <pubDate>Fri, 05 Mar 2021 03:45:36 GMT</pubDate>
            <description><![CDATA[<h1 id="백업하기">백업하기</h1>
<h2 id="mysql-db-백업">mysql DB 백업</h2>
<p><code>$ mysqldump -u root -p [DB명] &gt; [생성할 백업DB명].sql</code>
<code>$ mysqldump -u root -p [패스워드] [DB명] &gt; [생성할 백업DB명].sql</code></p>
<blockquote>
<p>ex <code>$ mysqldump -u root instamg &gt; instamg_v1.sql</code></p>
</blockquote>
<p><strong>화살표 방향에 주의할 것!</strong></p>
<h2 id="mysql-table-백업">mysql table 백업</h2>
<p><code>$ mysqldump -u root -p [DB명] [백업할 table명] &gt; [생성할 백업table명].sql</code></p>
<blockquote>
<p>ex <code>$ mysqldump -u root -p instamg users &gt; instamg_users.sql</code></p>
</blockquote>
<hr>
<h1 id="복원하기">복원하기</h1>
<h2 id="mysql-db-복원">mysql DB 복원</h2>
<p><code>$ mysql -u root -p [복원할 데이터베이스] &lt; [백업한 데이터베이스].sql</code></p>
<blockquote>
<p>ex  <code>$ mysql -u root -p instamg &lt; instamg_vi.sql</code></p>
</blockquote>
<h2 id="mysql-table-복원">mysql table 복원</h2>
<p><code>$ mysql -u root -p [복원할 데이터베이스] &lt; [백업한 테이블].sql</code></p>
<blockquote>
<p>ex <code>$ mysql -u root -p instamg &lt; instamg_users.sql</code></p>
</blockquote>
<p><strong>앞에 mysql !!!
mysqldump 🚫</strong></p>
<p>복원할 때 해당 경로에 sql파일이 있어야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django> [instamg] UserProfileView]]></title>
            <link>https://velog.io/@mini_y/instamguserprofileview</link>
            <guid>https://velog.io/@mini_y/instamguserprofileview</guid>
            <pubDate>Thu, 25 Feb 2021 14:52:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/mini_y/post/d1b9039c-876c-4d46-a874-c23abbd134b9/2021-03-08%2013;55;00.PNG" alt="">
페이지의 구성은 달라질 예정</p>
<h3 id="유저의-프로필-업데이트">유저의 프로필 업데이트</h3>
<ul>
<li>accout</li>
<li>email</li>
<li>password</li>
<li>phone</li>
<li>profile photo</li>
</ul>
<p>form-data형식으로 데이터 받음</p>
<h2 id="🙅🏻♀️-오류코드">🙅🏻‍♀️ 오류코드</h2>
<pre><code class="language-python">    @login_check
    def post(self, request):
        data  = json.loads(request.POST[&#39;json&#39;])
        user = request.user
        PASSWORD_LENGTH = 8
        EMAIL_VALIDATOR = re.compile(r&quot;[a-z0-9!#$%&amp;&#39;*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&amp;&#39;*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?&quot;)
        new_password = data.get(&#39;new_password&#39;)
        new_account = data.get(&#39;new_account&#39;)
        new_phone = data.get(&#39;new_phone&#39;)
        new_email = data.get(&#39;new_email&#39;)

        # 생략(account 변경)

        # 생략(email 변경)

        # 생략(phone 변경)

        # 생략(password 변경)

        if request.FILES.get(&#39;profile_photo&#39;):
            print(request.FILES.get(&#39;profile_photo&#39;))
            files = request.FILES[&#39;profile_photo&#39;]
            User.objects.filter(id = user.id).update(profile_photo=files, thumbnail_path=files)
            return JsonResponse({&#39;message&#39; : &#39;PROFILE_PHOTO_CHANGE_COMPLETE&#39;})
</code></pre>
<h3 id="error-message">ERROR MESSAGE</h3>
<p><img src="https://images.velog.io/images/mini_y/post/ff5fbf93-0a1f-4080-825a-a48b609d0145/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-02-25%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.55.47.png" alt=""></p>
<p>json형식으로 받는 데이터들은 문제없이 업데이트가 되나 파일을 받는 경우에 다음과 같은 에러메시지 발생함.</p>
<p><code>request.POST[&#39;json&#39;]</code> 대신에 <code>request.POST.get(&#39;json&#39;)</code> 을 사용하여 해결?
<code>[&#39;json&#39;]</code>을 이용하면 <code>‘json’</code>에 해당하는 값을 리턴해 주는데 존재하지 않으면 <code>KeyError</code>를 발생시킨다. </p>
<p>dict에서 특정 <code>key</code>의 값을 얻는 다른 방법으로 <code>.get(&#39;key&#39;)</code> 가 있다.
<code>get</code>을 써서 존재하지 않은 <code>key</code>일 경우 <code>None</code>을 반환해준다. 추가로 <code>.get(&#39;key&#39;, &#39;defaultvalue&#39;)</code> 와 같이 <code>key</code> 값이 없을 경우 <code>default</code> 값을 설정해 줄 수 있다.</p>
<h2 id="🙅🏻♀️-다른오류">🙅🏻‍♀️ 다른오류</h2>
<pre><code class="language-python">    @login_check
    def post(self, request):
        data = json.loads(request.POST.get(&#39;json&#39;)) #.get을 사용
        new_password = data.get(&#39;new_password&#39;)
        new_account = data.get(&#39;new_account&#39;)
        new_phone = data.get(&#39;new_phone&#39;)
        new_email = data.get(&#39;new_email&#39;)
        .
        .
        .</code></pre>
<h3 id="error-message-1">ERROR MESSAGE</h3>
<p><img src="https://images.velog.io/images/mini_y/post/880de9a3-f4d8-49f4-93ef-fb16dd137dc1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-02-25%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.36.15.png" alt=""></p>
<p>다시 생각해보니 당연히 오류가 날 수 밖에...
프로필 사진 업로드 시 <code>&#39;json&#39;</code>에는 값이 없으니 None이 나오지!</p>
<h2 id="🙆🏻♀️-해결">🙆🏻‍♀️ 해결</h2>
<pre><code class="language-python">@login_check
    def post(self, request):
        user = request.user
        PASSWORD_LENGTH = 8
        EMAIL_VALIDATOR = re.compile(r&quot;[a-z0-9!#$%&amp;&#39;*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&amp;&#39;*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?&quot;)

        if request.POST.get(&#39;json&#39;) is not None:
            data = json.loads(request.POST.get(&#39;json&#39;))
            new_password = data.get(&#39;new_password&#39;)
            new_account = data.get(&#39;new_account&#39;)
            new_phone = data.get(&#39;new_phone&#39;)
            new_email = data.get(&#39;new_email&#39;)

            if new_account:
                if User.objects.filter(account=new_account).exists():
                    return JsonResponse({&#39;message&#39; : &#39;ALREADY_IN_USE&#39;}, status=400)
                User.objects.filter(id=user.id).update(account=new_account)
                return JsonResponse({&#39;message&#39; : &#39;ACCOUNT_CHANGE_COMPLETE&#39;}, status=200)
                .
                .
                .
         if request.FILES.get(&#39;profile_photo&#39;):
            print(request.FILES.get(&#39;profile_photo&#39;))
            files = request.FILES[&#39;profile_photo&#39;]
            User.objects.filter(id = user.id).update(profile_photo=files, thumbnail_path=files)
            return JsonResponse({&#39;message&#39; : &#39;PROFILE_PHOTO_CHANGE_COMPLETE&#39;})</code></pre>
<p>조건문을 걸어줘서 None이 아닐경우 값을 변수에 저장해주기!</p>
<p>코드가 지저분?하지만 나중에 리팩토링 하는걸로..</p>
<p><img src="https://images.velog.io/images/mini_y/post/1e443744-95e1-4791-a862-066051e6e180/%E1%84%8B%E1%85%A1%E1%86%B8%E1%84%83%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%A8%E1%84%80%E1%85%A1%E1%86%B7%E1%84%89%E1%85%A1.jpeg" alt="">
같이 해결해준 연우님 준형님 압도적 감사!!ㅋㅋㅋ</p>
<h2 id="🚫-오류-발생">🚫 오류 발생</h2>
<p>폼데이터로 받는 파일데이터(참고로 이번 프로젝트에서 파일은 s3에 저장하는것이 아닌 mediafile로 저장)가 로컬에 저장이 안되는거임...
db를 확인해보면 업데이트한 파일로 변경된것을 확인할 수 있으나 로컬엔 저장이 안되어있고
프론트와 통신해봤을 때 사진도 안뜨고..</p>
<p>filter가 안먹히는 건가해서 save로 바꿔줌</p>
<pre><code class="language-python">if request.FILES.get(&#39;profile_photo&#39;):
                path = request.FILES[&#39;profile_photo&#39;]
                user=User.objects.filter(id = user.id)[0]
                user.profile_photo = path
                user.thumbnail_path = path
                user.save()
                return JsonResponse({&#39;message&#39; : &#39;PROFILE_PHOTO_CHANGE_COMPLETE&#39;})</code></pre>
<p>db에도 저장 잘되어있고 로컬에도 저장 잘되고 프론트와 통신했을 때도 사진이 잘 떴다.</p>
<p>그런데
에러가 안잡힌다....
To be continue....</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django> [instamg] feeddetailpage]]></title>
            <link>https://velog.io/@mini_y/instamg-feeddetailpage</link>
            <guid>https://velog.io/@mini_y/instamg-feeddetailpage</guid>
            <pubDate>Wed, 24 Feb 2021 14:29:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/mini_y/post/7c2b25f7-5fbb-4662-b290-ba71ceb0d034/feed%E1%84%83%E1%85%B5%E1%84%90%E1%85%A6%E1%84%8B%E1%85%B5%E1%86%AF%E1%84%91%E1%85%A6%E1%84%8B%E1%85%B5%E1%84%8C%E1%85%B5.png" alt="">
인스타 피드에서 게시물을 누를 때 나오는 페이지</p>
<blockquote>
<ul>
<li>필요한 데이터
post, user_pofile, user_account, post_content, 로그인한 유저가 계정주와 팔로우 한 사이인지TF, 댓글, 대댓글....</li>
</ul>
</blockquote>
<pre><code class="language-python">&#39;comments&#39;       : [{
                        &#39;comment_id&#39;    : comment.id,
                        &#39;user_id&#39;       : comment.user_id_id,
                        &#39;account&#39;       : comment.user_id.account,
                        &#39;profile_photo&#39; : comment.user_id.profile_photo,#&quot;/media/&quot;+ str(comment.user_id.thumbnail_path) if str(comment.user_id.thumbnail_path) else None,
                        &#39;content&#39;       : comment.content,
                        &#39;created_at&#39;    : comment.created_at,
                        &#39;like_count&#39;    : Like.objects.filter(comment_id_id=comment.id).count()
                    }for comment in post.comments.filter(comment_id_id=None)],
&#39;recomments&#39;     : [{
                        &#39;recomment_id&#39;  : comment.id,
                        &#39;comment_id&#39;    : comment.comment_id_id,
                        &#39;user_id&#39;       : comment.user_id_id,
                        &#39;account&#39;       : comment.user_id.account,
                        &#39;profile_photo&#39; : comment.user_id.profile_photo,#&quot;/media/&quot;+ str(comment.user_id.thumbnail_path) if str(comment.user_id.thumbnail_path) else None,
                        &#39;content&#39;       : comment.content,
                        &#39;created_at&#39;    : comment.created_at,
                        &#39;like_count&#39;    : Like.objects.filter(comment_id_id=comment.id).count(),
                    }for comment in post.comments.exclude(comment_id_id=None)],
 &#39;file&#39;          :[{
                                    &#39;file_type&#39;      : post_attach_file.file_type,
                                    &quot;view_count&quot;     : post_attach_file.view_count,
                                    &#39;path&#39;           : &quot;/media/&quot;+str(post_attach_file.path),
                                    &#39;thumbnail_path&#39; : &quot;/media/&quot;+str(post_attach_file.thumbnail_path)
                    }for post_attach_file in post.post_attach_files.all()]
            }
            return JsonResponse({&#39;post&#39;:post}, status=200)
        except ValueError:
            return JsonResponse({&#39;message&#39;:&#39;VALUE_ERROR&#39;}, status=400)
        except IndexError:
            return JsonResponse({&#39;message&#39;:&#39;POST_DOES_NOT_EXIST&#39;}, status=400)</code></pre>
<p>처음에 썼던 방법
비효율 적인 느낌👎</p>
<pre><code class="language-python">&#39;comments&#39;           : [{
                                        &#39;comment_id&#39;                 : comment.id,
                                        &#39;content&#39;                    : comment.content,
                                        &#39;comment_user_id&#39;            : comment.user_id.id,
                                        &#39;comment_user_account&#39;       : comment.user_id.account,
                                        &#39;comment_user_profile_photo&#39; : &quot;/media/&quot;+ str(comment.user_id.thumbnail_path) if str(comment.user_id.thumbnail_path) else None,
                                        &#39;created_at&#39;                 : comment.created_at,
                                        &#39;like_count&#39;                 : Like.objects.filter(id=comment.id).count(),
                                        &#39;is_liked&#39;                   : comment.likes.exists(),
      &#39;recomment&#39;                  :[{
                                            &#39;comment_id&#39;                   : comment.id,
                                            &#39;recomment_id&#39;                 : recomment.id,
                                            &#39;content&#39;                      : recomment.content,
                                            &#39;recomment_user_id&#39;            : recomment.user_id.id,
                                            &#39;recomment_user_account&#39;       : recomment.user_id.account,
                                            &#39;recomment_user_profile_photo&#39; : &quot;/media/&quot;+ str(recomment.user_id.thumbnail_path) if str(recomment.user_id.thumbnail_path) else None,
                                            &#39;created_at&#39;                   : recomment.created_at,
                                            &#39;like_count&#39;                   : Like.objects.filter(comment_id_id=comment.id).count(),
                                            &#39;is_liked&#39;                     : comment.likes.exists()
                } for recomment in Comment.objects.filter(comment_id=comment.id)]
            } for comment in post.comments.filter(comment_id=None)],</code></pre>
<p>수정한 코드
comments 라는 배열안에 키값이 recomments인 배열을 가지고있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django> [instamg] profilestory ]]></title>
            <link>https://velog.io/@mini_y/instamgprofilestory</link>
            <guid>https://velog.io/@mini_y/instamgprofilestory</guid>
            <pubDate>Sat, 20 Feb 2021 18:29:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/mini_y/post/819fbd8f-664e-42c9-babe-740a109398d5/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-02-21%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%203.18.46.png" alt=""></p>
<h3 id="인스타그램-개인-프로필에서-저장된-스토리-데이터-보여주기">인스타그램 개인 프로필에서 저장된 스토리 데이터 보여주기</h3>
<pre><code class="language-python">class ProfileStoryView(View):
    def get(self, request, user_id):
        try:
            user    = User.objects.get(id = user_id)
            stories = []
            for story in user.story_set.all():
                if story.story_profile==1:
                    for files in story.storyattachfiles_set.all():
                        stories.append({
                            &quot;story_id&quot;  : story.id,
                            &quot;title&quot;     : story.title,
                            &quot;file&quot;      : files.path, # 나중에 thumbnail_path로 바꾸기
                            &quot;file_type&quot; : files.file_type
                        })
            return JsonResponse({&quot;profile_story&quot; : stories}, status = 200)
        except KeyError:
            return JsonResponse({&quot;message&quot; : &quot;KEY_ERROR&quot;}, status=400)</code></pre>
<p>유저가 스토리를 생성하고나서 그 스토리를 본인의 프로필에 저장할 것인지 결정할 수 있다.
스토리 테이블에 <code>story_profile</code>컬럼을 만들어 저장안할 경우(불린값0) 저장할 경우 1</p>
<p>저장할 경우(불린값1)의 스토리만 보여주기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<git> rebase flow]]></title>
            <link>https://velog.io/@mini_y/git-rebase-flow</link>
            <guid>https://velog.io/@mini_y/git-rebase-flow</guid>
            <pubDate>Sat, 20 Feb 2021 17:28:26 GMT</pubDate>
            <description><![CDATA[<h3 id="git-rebase-commit-history-정리하기">git rebase (commit history 정리하기)</h3>
<p><code>feature/기능</code> branch에서 기능구현 완료 후 (커밋필수!)</p>
<ol>
<li><code>$ git checkoust main</code> 메인 브랜치로 이동하여 <code>main pull</code>받기</li>
<li>다시 작업했던 브랜치로 돌아와서<code>$ git rebase -i main</code> 하기</li>
<li><code>vim</code>에서 오래된 <code>commit</code>은  <code>pick</code>하고  나머지는<code>s(squash)</code>하기</li>
<li><code>vim</code>에서 커밋 메시지 작성</li>
</ol>
<blockquote>
<p>rebase중 충돌 있을 경우? 해당 코드로 가서 수정 후 <code>git add .</code> <code>git rebase --continue</code>반복하기
해결 안되면 <code>git rebase --abort</code></p>
</blockquote>
<ol start="5">
<li>rebase를 마쳤다면 push하기(작업중인 브랜치에서) <code>$ git push origin &lt;작업한 브랜치명&gt; -f</code>
(git은 history가 다른 branch의 push를 허용하지 않는다. -f를 이용하여 강제로 푸쉬)</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[위코드 기업협업 프로젝트 <instamg> 모델링]]></title>
            <link>https://velog.io/@mini_y/%EC%9C%84%EC%BD%94%EB%93%9C-%EA%B8%B0%EC%97%85%ED%98%91%EC%97%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-instamg-%EB%AA%A8%EB%8D%B8%EB%A7%81</link>
            <guid>https://velog.io/@mini_y/%EC%9C%84%EC%BD%94%EB%93%9C-%EA%B8%B0%EC%97%85%ED%98%91%EC%97%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-instamg-%EB%AA%A8%EB%8D%B8%EB%A7%81</guid>
            <pubDate>Sat, 20 Feb 2021 17:11:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/mini_y/post/99217b38-6a36-419e-82fb-272a5f5e9dff/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-02-21%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.47.18.png" alt=""></p>
<h3 id="요약">요약</h3>
<ul>
<li>users, posts, stories direct messages 앱으로 구성</li>
</ul>
<h5 id="user를-product로-생각하면">user를 product로 생각하면</h5>
<ul>
<li>리스트 : 팔로우한 계정 게시물</li>
<li>상세 페이지 : 개인 프로필(개인 인스타 계정)</li>
<li>디테일 페이지 : 개인 프로필에서 게시물 상세 조회</li>
</ul>
<h3 id="맡은-부분">맡은 부분</h3>
<ul>
<li>좋아요 </li>
<li>개인피드 조회</li>
<li>개인피드 게시물 상세 조회</li>
<li>개인피드 태그된 게시물 조회</li>
<li>스토리 조회</li>
</ul>
<h3 id="생각-할-부분">생각 할 부분</h3>
<ul>
<li>개인프로필 조회 시 데이터를 한번에 다 보내줘야 하는가?
👉🏻 프로필부분, 프로필에 저장한 스토리, 피드 데이터 따로 보내주는 게 RESTful함. </li>
<li>게시물 상세 조회에서 대댓글은 어떤 형태로 보여주지?</li>
<li>유저가 로그인 후 유저의 프로필인지, 유저가 팔로우한 프로필인지 아닌지 구분하기</li>
<li>스토리 조회 시 현재시간 기준으로 24시간내 생성된 게시물만 보여주기</li>
<li>SSE(Server Sent Event)이용하여 실시간으로 좋아요, 팔로워, 팔로잉 수 업데이트하기</li>
<li>WebSocket</li>
</ul>
<h3 id="새로운-부분">새로운 부분</h3>
<ul>
<li>s3이용하여 이미지 저장하는 것이 아닌 로컬에서 저장하기</li>
<li>이미지 업로드 시 썸네일도 같이 업로드, 이미지크기변환</li>
<li>실시간 기능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[위코드 2차 프로젝트 <bearbnb>]]></title>
            <link>https://velog.io/@mini_y/%EC%9C%84%EC%BD%94%EB%93%9C-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-bearbnb</link>
            <guid>https://velog.io/@mini_y/%EC%9C%84%EC%BD%94%EB%93%9C-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-bearbnb</guid>
            <pubDate>Mon, 08 Feb 2021 18:14:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/mini_y/post/27b3b56d-e033-4ae6-8d90-850c9de98c11/bearbnb%E1%84%90%E1%85%B5%E1%86%B7_%E1%84%8B%E1%85%A6%E1%84%8B%E1%85%A5%E1%84%87%E1%85%B5%E1%84%8B%E1%85%A2%E1%86%AB%E1%84%87%E1%85%B5_%E1%84%8F%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A9%E1%86%AB_%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%8C%E1%85%A6%E1%86%A8%E1%84%90%E1%85%B3_%E1%84%86%E1%85%A6%E1%84%8B%E1%85%B5%E1%86%AB.gif" alt=""></p>
<h1 id="🐻-bearbnb-team-project-🐻">🐻 BearBnB Team Project 🐻</h1>
<h2 id="💁🏻♀️-bearbnb-github">💁🏻‍♀️ Bearbnb Github</h2>
<p><a href="https://github.com/wecode-bootcamp-korea/16-2nd-bearbnb-frontend">프론트엔드</a></p>
<p><a href="https://github.com/wecode-bootcamp-korea/16-2nd-bearbnb-backend">백엔드</a></p>
<h2 id="💁🏻♀️-team-member">💁🏻‍♀️ Team Member</h2>
<h4 id="frontend--정민선pm-태성현-김승완">Frontend : 정민선(PM), 태성현, 김승완</h4>
<h4 id="backend--윤정민-김건희-홍연우">Backend : 윤정민, 김건희, 홍연우</h4>
<h2 id="💁🏻♀️-technologies">💁🏻‍♀️ Technologies</h2>
<p>MySQL</p>
<p>Python</p>
<p>Django</p>
<p>Git</p>
<p>AWS : EC2, RDS, S3</p>
<p>Docker</p>
<p>JWT, Bcrpyt</p>
<p>googlemaps</p>
<p>Kakao API</p>
<p>django-seed</p>
<h2 id="💁🏻♀️-modeling">💁🏻‍♀️ Modeling</h2>
<p><img src="https://images.velog.io/images/mini_y/post/180b391f-b833-4558-8dfe-cb9f386edb31/bearbnb_modeling.png" alt=""></p>
<h2 id="💁🏻♀️-functions">💁🏻‍♀️ Functions</h2>
<p>회원가입, 비밀번호 암호화(bcrypt)
로그인, token 발행(JWT)
소셜 로그인, kakao API
전체 리스트, pagination, 필터링
location 위도 경도 변환
상세 페이지 뷰
예약하기, SMTP 사용
호스팅
Project Result
BearBnB 결과물</p>
<h2 id="💁🏻♀️-데모영상">💁🏻‍♀️ 데모영상</h2>
<p><a href="https://www.youtube.com/watch?v=RFja9gWiVzs&amp;feature=emb_title">youtube 영상</a></p>
<h2 id="💁🏻♀️-reference">💁🏻‍♀️ Reference</h2>
<p>이 프로젝트는 airbnb 사이트를 참조하여 학습목적으로 만들었습니다.
실무수준의 프로젝트이지만 학습용으로 만들었기 때문에 이 코드를 활용하여 이득을 취하거나 무단 배포할 경우 법적으로 문제될 수 있습니다.
이 프로젝트에서 사용하고 있는 사진 대부분은 위코드에서 구매한 것이므로 해당 프로젝트 외부인이 사용할 수 없습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<AWS>s3 파일업로드]]></title>
            <link>https://velog.io/@mini_y/AWSs3-%ED%8C%8C%EC%9D%BC%EC%97%85%EB%A1%9C%EB%93%9C</link>
            <guid>https://velog.io/@mini_y/AWSs3-%ED%8C%8C%EC%9D%BC%EC%97%85%EB%A1%9C%EB%93%9C</guid>
            <pubDate>Sun, 31 Jan 2021 15:18:38 GMT</pubDate>
            <description><![CDATA[<p>앞부분다시 정리하기!
<a href="https://community.wecode.co.kr/t/django-s3/1163/2">s3 setting</a></p>
<p>&quot;CORS configuration&quot; 버튼을 눌러서 CORS 설정을 해주도록 하자.
위에 설명은 XML형식이므로 JSON형식으로 바꿔서 설정해주기</p>
<pre><code class="language-python">[
    {
        &quot;AllowedHeaders&quot;: [
            &quot;*&quot;
        ],
        &quot;AllowedMethods&quot;: [
            &quot;GET&quot;
        ],
        &quot;AllowedOrigins&quot;: [
            &quot;*&quot;
        ],
        &quot;ExposeHeaders&quot;: [
            &quot;ETag&quot;
        ],
        &quot;MaxAgeSeconds&quot;: 3000
    }
]</code></pre>
<h2 id="django에서-boto3를-사용하여-s3와-연결-및-파일-업로드">Django에서 boto3를 사용하여 S3와 연결 및 파일 업로드</h2>
<p>터미널에서 boto3설치하기
<code>$ pip install boto3</code></p>
<pre><code class="language-python"># django 앱 views.py
import boto3
from django.http import HttpResponse</code></pre>
<p>import해주기!</p>
<pre><code class="language-python">class FileView(View):
    s3_client = boto3.client(
        &#39;s3&#39;,
        aws_access_key_id=&quot;s3세팅 시 설치한new_user_credentials에 있는 키ID&quot;,
        aws_secret_access_key=&quot;s3세팅 시 설치한new_user_credentials에 있는 키&quot;
    )

    def post(self, request):
        file = request.FILES[&#39;filename&#39;] #[&#39;키값&#39;]

        self.s3_client.upload_fileobj(
            file, 
            &quot;wecode16&quot;, #버킷이름
            file.name,
            ExtraArgs={
                &quot;ContentType&quot;: file.content_type
            }
        ) 

        return HttpResponse(status= 200)</code></pre>
<h4 id="url-설정">url 설정</h4>
<p><code>from users.views import FileView</code>
<code>from &lt;앱이름&gt;.views import FileView</code></p>
<pre><code class="language-python">urlpatterns = [
    path(&quot;/file&quot;, FileView.as_view()),
]
</code></pre>
<h3 id="postman-이용-시">postman 이용 시</h3>
<p><img src="https://images.velog.io/images/mini_y/post/9137563c-1443-45a2-890b-12e88b330d7d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-01-26%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.01.58.png" alt=""></p>
<h3 id="httpie-이용-시">HTTPie 이용 시</h3>
<p><code>http -f POST 127.0.0.1:8000/user/file filename@~/Desktop/database.png</code>
<code>http -f POST &lt;ip 주소&gt;/&lt;url&gt; &lt;키값&gt;@~/&lt;업로드할 파일경로와 이름&gt;</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[위코드 1차 프로젝트 <enter cloud>]]></title>
            <link>https://velog.io/@mini_y/%EC%9C%84%EC%BD%94%EB%93%9C-1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-enter-cloud</link>
            <guid>https://velog.io/@mini_y/%EC%9C%84%EC%BD%94%EB%93%9C-1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-enter-cloud</guid>
            <pubDate>Sun, 24 Jan 2021 12:10:44 GMT</pubDate>
            <description><![CDATA[<p><img src="https://j.gifs.com/71ENKj.gif" alt=""></p>
<h1 id="💁-enter-cloud-github">💁 enter cloud github</h1>
<p><a href="https://github.com/wecode-bootcamp-korea/enter_cloud-frontend">프론트엔드</a></p>
<p><a href="https://github.com/wecode-bootcamp-korea/enter_cloud-backend">백엔드</a></p>
<h1 id="🚀-프로젝트-소개">🚀 프로젝트 소개</h1>
<p>1차 프로젝트 <a href="https://www.spacecloud.kr/">스페이스 클라우드</a>를 모티브로 프로젝트를 진행했습니다.</p>
<p>2주라는 짧을 기간 제약이 있어 기획이 최소한으로 들어가는 사이트 클론 프로젝트를 선정했습니다. 
http method와 uri을 활용하여 restful 한 프로젝트를 지향합니다. </p>
<h1 id="🚀-개발-방향">🚀 개발 방향</h1>
<p>개발기간 : 2021/1/11 ~ 2020/1/22 ( 2주간 진행 )</p>
<p>매일 데일리 미팅과 Sprint기간을 1주일로 잡고 매주 한번씩 Sprint 미팅을 통해 에자일하게 프로젝트를 진행했습니다. </p>
<h1 id="🚀-팀원">🚀 팀원</h1>
<h3 id="front_end">Front_end</h3>
<ul>
<li><p><a href="https://github.com/wan-seung">김승완</a></p>
</li>
<li><p><a href="https://github.com/moonkyuchan">문규찬</a></p>
</li>
</ul>
<h3 id="back_end">Back_end</h3>
<ul>
<li><p><a href="https://github.com/ddalkigum">김준형(PM)</a></p>
</li>
<li><p><a href="https://github.com/jeongmin14">윤정민</a></p>
</li>
</ul>
<h1 id="🚀-적용-기술-및-구현-기능">🚀 적용 기술 및 구현 기능</h1>
<h3 id="공통">공통</h3>
<ul>
<li>AWS EC2, RDS 를 이용하여 배포를 진행했고, nohup으로 백그라운드로 돌아가도록 설정 </li>
<li>Aquery Tool을 이용한 모델링 </li>
</ul>
<h3 id="백엔드">백엔드</h3>
<ul>
<li>Python</li>
<li>Django Framework</li>
<li>Bcrypt를 이용한 단방향 해쉬 암호 저장, JWT Token을 이용한 유저 확인 </li>
<li>RDBS인 Mysql을 사용 </li>
<li>Django-seed를 활용한 커맨드를 만들어 가짜데이터로 통신을 주고 받음  </li>
</ul>
<h1 id="🚀-페이지별-구현-항목">🚀 페이지별 구현 항목</h1>
<h3 id="signin-signup">/signin, /signup</h3>
<ul>
<li>Bcrypt, JWT를 활용한 로그인, 회원가입 기능 구현 </li>
</ul>
<h3 id="spaces">/spaces</h3>
<ul>
<li>메인페이지와 리스트 페이지 등... 여러 곳에서 필요한 공간 카드는 따로 View를 만들어 관리 </li>
</ul>
<h3 id="likes">/likes</h3>
<ul>
<li>찜하기 기능, 찜한 공간 리스트 보여주기 </li>
</ul>
<h3 id="데모-영상">데모 영상</h3>
<p><a href="https://www.youtube.com/watch?v=s8qKmTRccPw">youtube 영상</a></p>
<h2 id="reference">Reference</h2>
<ul>
<li>이 프로젝트는 스페이스 클라우드 (<a href="https://www.spacecloud.kr/">https://www.spacecloud.kr/</a>) 사이트를 참조하여 학습목적으로 만들었습니다.</li>
<li>실무수준의 프로젝트이지만 학습용으로 만들었기 때문에 이 코드를 활용하여 이득을 취하거나 무단 배포할 경우 법적으로 문제될 수 있습니다.</li>
<li>이 프로젝트에서 사용하고 있는 사진 대부분은 위코드에서 구매한 것이므로 해당 프로젝트 외부인이 사용할 수 없습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django>QuerySet]]></title>
            <link>https://velog.io/@mini_y/DjangoQuerySet</link>
            <guid>https://velog.io/@mini_y/DjangoQuerySet</guid>
            <pubDate>Wed, 20 Jan 2021 00:57:50 GMT</pubDate>
            <description><![CDATA[<h2 id="210119-til">210119 TIL</h2>
<h3 id="queryset-심화">QuerySet 심화</h3>
<h4 id="1-select_related">1. select_related()</h4>
<h4 id="2-prefetch_related">2. prefetch_related()</h4>
<h3 id="field-lookups">Field lookups</h3>
<h3 id="many-to-many-orm">many to many ORM</h3>
<h3 id="python-삼항연산자">python 삼항연산자</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Django> spaces/views.py]]></title>
            <link>https://velog.io/@mini_y/django-spaces.views.py</link>
            <guid>https://velog.io/@mini_y/django-spaces.views.py</guid>
            <pubDate>Mon, 18 Jan 2021 15:35:42 GMT</pubDate>
            <description><![CDATA[<h2 id="210119-til">210119 TIL</h2>
<h3 id="1-메인-페이지-space-카드뷰-데이터-불러오기">1. 메인 페이지 space 카드뷰 데이터 불러오기</h3>
<p> <code>spaces=Space.objects.all()</code>
 many to many 또는 one to many로 연결된 데이터 가지고 오기</p>
<p> views.py에서 <code>print(dir(space))</code> 입력해서 space객체가 쓸 수 있는 메소드 확인하기</p>
<p><code>&lt;class명&gt;_set</code> 이용하여 many to many 또는 one to many로 연결된 데이터 가지고 오기</p>
<pre><code class="language-python"># 공간의 태그 가져오기
spaces = Space.objects.all()
for space in spaces:
    print(space.spacetag_set.all())
    for tag in space.spacetag_set.all():
    print(tag.tag.name)

# 공간의 타입 가져오기
for i in spaces:
    print(i.name) # 공간의 이름
    for space_type in i.types.all(): #공간과 mtm로 연결된 타입을 모두 호출
    print(space_type.name) #공간의 타입의 이름</code></pre>
<p><code>python shell</code>에서 연습해서 데이터를 불러올 수 있으면 views.py에 코드를 작성할 차례!</p>
<p>many to many 또는 one to many로 연결된 데이터를 가지고 오기 위해서는 for문이 두 번 돌아야 함
이럴 경우 <strong>list comprehension</strong> 이용하여 코드작성하기</p>
<blockquote>
<p><strong>list comprehension</strong>
[(변수를 활용할 값) for (사용할 변수 이름) in (순회할 수 있는 값)]</p>
</blockquote>
<pre><code class="language-python"># spaces/views.py
spaces=Space.objects.all()
data=[]
for space in spaces:
    data.append(
    {
&quot;tag&quot;   = [tag.tag.name for tag in space.spacetag_set.all()]

&quot;types&quot; = [space_type.name for space_type in space.types.all()]
    }
)
return JsonResponse({&quot;data&quot;:data}, status = 201)</code></pre>
<h3 id="list-comprehension사용하여-코드-리팩토링">list comprehension사용하여 코드 리팩토링</h3>
<p><code>data</code>라는 빈 리스트를 선언하여 데이터 요소들을 <code>append</code> 하여 리스트에 추가하는 방법으로 작성한 코드를 list comprehension을 사용하여 코드를 줄일 수 있다.</p>
<pre><code class="language-python"># spaces/views.py
spaces=Space.objects.all()
data = [
    {
        &quot;tag&quot;   = [tag.tag.name for tag in space.spacetag_set.all()]
        &quot;types&quot; = [space_type.name for space_type in space.types.all()]
    }
    for space in spaces
    ]
return JsonResponse({&quot;data&quot;:data}, status = 201)</code></pre>
<h3 id="2-aggregate">2. aggregate</h3>
<p><a href="https://docs.djangoproject.com/en/3.1/topics/db/aggregation/">aggregation 공식문서</a>
django database abstraction API</p>
<p>사용하기 전에 반드시
<code>from django.db.models import Max,Avg</code> import 할 것!</p>
<p>space와 one to many로 연결되어 있는 detailspace의 속성 &quot;max_people&quot;의 최대값을 구하기
<code>&lt;class명&gt;.objects.all.aggregate(Max(&quot;속성&quot;))</code> 최대값 구하기
<code>&lt;class명&gt;.objects.all.aggregate(Avg(&quot;속성&quot;))</code> 평균값 구하기</p>
<pre><code class="language-python">spaces.views.py

spaces = Space.objects.all()
results =  [
    {
        &quot;max_people&quot; = space.detailspace_set.all().aggregate(Max(&quot;max_people&quot;))[&quot;max_people__max&quot;]
    }
    for space in spaces:
    ]

# [&quot;max_people__max&quot;] 프론트에 데이터 보내줄 때 조금더 깔끔하게?보내주기위해, 딕셔너리형태?</code></pre>
]]></description>
        </item>
    </channel>
</rss>