<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>miracle-21.log</title>
        <link>https://velog.io/</link>
        <description>backend developer 🐌</description>
        <lastBuildDate>Tue, 17 Jan 2023 08:15:29 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>miracle-21.log</title>
            <url>https://velog.velcdn.com/images/miracle-21/profile/b5acce6b-20ec-46ce-8a8c-77e9128c99b1/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. miracle-21.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/miracle-21" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[배포 단계에서 PM2, NPM 에러]]></title>
            <link>https://velog.io/@miracle-21/%EB%B0%B0%ED%8F%AC-%EB%8B%A8%EA%B3%84%EC%97%90%EC%84%9C-PM2-NPM-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@miracle-21/%EB%B0%B0%ED%8F%AC-%EB%8B%A8%EA%B3%84%EC%97%90%EC%84%9C-PM2-NPM-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Tue, 17 Jan 2023 08:15:29 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ol>
<li><p><code>npm install</code> 후 <code>npm start</code> 에서 문제 발생.</p>
<pre><code class="language-bash">ReferenceError: TextEncoder is not defined</code></pre>
</li>
<li><p><code>pm2 start server.js</code> 에서 문제 발생.</p>
<pre><code class="language-bash">pm2 start error : too many unstable restarts (16). Stopped. &quot;errored&quot;</code></pre>
</li>
</ol>
<h2 id="해결">해결</h2>
<p>Stack Overflow도 다 소용없다가 <strong>node 버전 문제</strong>일 수 있다는 글을 봤다. 거기서 제시한 해결책은 소용 없었고, <a href="https://velog.io/@miracle-21/JestSyntaxError-Unexpected-token--914j81de">이전에 올렸던 글</a>을 참고해서 해결했다.</p>
<h2 id="결론">결론</h2>
<p>NPM 캐시 지우고 Node.js 최신버전 설치.</p>
<pre><code class="language-bash">sudo npm cache clean -f
 sudo npm install -g n
sudo n stable</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Mongoose]CastError: 스키마 타입]]></title>
            <link>https://velog.io/@miracle-21/MongooseCastError-%EC%8A%A4%ED%82%A4%EB%A7%88-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@miracle-21/MongooseCastError-%EC%8A%A4%ED%82%A4%EB%A7%88-%ED%83%80%EC%9E%85</guid>
            <pubDate>Sat, 14 Jan 2023 17:14:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/miracle-21/post/936c6a66-be89-476a-9de2-69e84c2e780f/image.png" alt=""></p>
<blockquote>
<p><strong>CastError: Cast to Number failed for value &quot;6375ce0cfa8e8a3ad6f43be8&quot; (type string) at path &quot;user&quot; for model &quot;Post&quot;</strong></p>
</blockquote>
<h2 id="문제">문제</h2>
<p>포스팅한 <code>유저 _id</code>를 받아오지 못함.</p>
<pre><code class="language-js">//models.post.js
const mongoose = require(&#39;mongoose&#39;);

const postSchema = new mongoose.Schema({
    _id: {
          type: Number
    },
    user: {
        type: String,
        required: true
    },
       ...
},  { versionKey : false } )

const Post = mongoose.model(&#39;Post&#39;, postSchema);

module.exports = Post;</code></pre>
<pre><code class="language-js">//routes.controller.js
const postModel = require(&#39;../models/post&#39;);

postModel.find({user:req.user._id});</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/c02fd79d-e288-410e-8823-621ed4b6f76f/image.png" alt=""></p>
<h2 id="해결">해결</h2>
<ol>
<li><p><code>models.post.js</code> 파일에서 <code>_id</code> 필드는 정의하지 않는다고 한다. 그래서 삭제.</p>
</li>
<li><p><code>_id</code> 필드의 타입은 <code>ObjectId</code> 타입이다. <code>user</code> 필드의 타입을 <code>String</code>이 아닌 <code>ObjectId</code> 타입으로 바꿔야 한다.</p>
<pre><code class="language-js">//models.post.js
const mongoose = require(&#39;mongoose&#39;);
</code></pre>
</li>
</ol>
<p>const postSchema = new mongoose.Schema({
    user: {
        type: mongoose.Schema.Types.ObjectId,
        required: true
    },
      ...
},  { versionKey : false } )</p>
<p>const Post = mongoose.model(&#39;Post&#39;, postSchema);</p>
<p>module.exports = Post;</p>
<p>```</p>
<h2 id="요약">요약</h2>
<p>mongoose schema 필드 타입을 잘못 정의해서 발생한 문제.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JEST]Cannot log after tests are done. Did you forget to wait for something async in your test?]]></title>
            <link>https://velog.io/@miracle-21/JESTCannot-log-after-tests-are-done.-Did-you-forget-to-wait-for-something-async-in-your-test</link>
            <guid>https://velog.io/@miracle-21/JESTCannot-log-after-tests-are-done.-Did-you-forget-to-wait-for-something-async-in-your-test</guid>
            <pubDate>Thu, 12 Jan 2023 09:10:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/miracle-21/post/cf882155-fa73-4a96-8cd8-9c097a82b135/image.png" alt=""></p>
<pre><code class="language-js">//server.js
...
module.exports = app;</code></pre>
<p>app.listen에 function 삭제했더니 해결</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Jest]SyntaxError: Unexpected token .]]></title>
            <link>https://velog.io/@miracle-21/JestSyntaxError-Unexpected-token--914j81de</link>
            <guid>https://velog.io/@miracle-21/JestSyntaxError-Unexpected-token--914j81de</guid>
            <pubDate>Tue, 10 Jan 2023 12:33:16 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/miracle-21/post/7744d981-2e13-4f48-998a-d0f8c5512f44/image.png" alt=""></p>
<p>정적파일인 css파일 때문에 발생한 에러라면서 <code>__mocks__/styleMock.js</code> 파일을 만들고 <code>jest.config.js</code>파일을 만들어서 뭘 어떻게 하라는데 하나도 해결이 안되다가,</p>
<h2 id="해결">해결</h2>
<p><a href="https://stackoverflow.com/questions/73715494/error-while-jest-setup-for-snapshots-in-if-error-stack">https://stackoverflow.com/questions/73715494/error-while-jest-setup-for-snapshots-in-if-error-stack</a></p>
<p>Node.js 버전이 낮은게 원인이었다.</p>
<ol>
<li>NPM의 캐시 지우기<pre><code class="language-bash">sudo npm cache clean -f</code></pre>
</li>
<li>&#39;n&#39;이라는 Node.js version management 설치<pre><code class="language-bash">sudo npm install -g n</code></pre>
</li>
<li>안정적인 최신 Node.js 버전 설치<pre><code class="language-bash">sudo n stable</code></pre>
업데이트 후에도 반영이 안되는 경우에는,
<img src="https://velog.velcdn.com/images/miracle-21/post/38615526-8c54-41d7-885e-3f704e492bc5/image.png" alt=""><pre><code class="language-bash">hash -r</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[git pull 에러]]></title>
            <link>https://velog.io/@miracle-21/git-pull-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@miracle-21/git-pull-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Fri, 06 Jan 2023 17:17:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/miracle-21/post/ccce0ed8-73b8-42aa-9412-fa79948bd753/image.png" alt=""></p>
<h3 id="해결">해결</h3>
<pre><code class="language-bash">git stash &amp;&amp; git pull origin master &amp;&amp; git stash pop</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Node.js로 개발하기]]></title>
            <link>https://velog.io/@miracle-21/Node.js%EB%A1%9C-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@miracle-21/Node.js%EB%A1%9C-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 02 Jan 2023 08:20:20 GMT</pubDate>
            <description><![CDATA[<p>github: <a href="https://github.com/miracle-21/community-service">https://github.com/miracle-21/community-service</a></p>
<hr>
<h1 id="✅-nodejs">✅ NodeJS?</h1>
<p>Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임이다. 즉, JS 실행창(실행환경)이다.</p>
<h2 id="✔-왜-사용하나">✔ 왜 사용하나</h2>
<h3 id="1-non-blocking-io">1. Non-blocking I/O</h3>
<p>요청이 들어오면 다 받아두고 처리속도가 빠른 것부터 처리한다. SNS, 채팅서비스 등 요청이 많은 경우 유리하다. 무거운 요청들도 멈추거나 요청 대기시간이 발생하지 않는다. 일반 서버는(python) 서버스케일링(서버 인스턴스 추가) 또는 멀티쓰레딩으로 이 문제를 해결할 수 있긴 하다. 하지만 node.js를 쓰는게 더 간편하다.</p>
<h3 id="2-빠른-개발">2. 빠른 개발</h3>
<p>짧은 코드로 개발 가능하다. 프로토타입 개발에 쓰기 좋다. 다른 서비스로 돌리기도 쉽다(pivot). 웹서비스 만들 때 주로 사용한다.</p>
<h1 id="✅-사용할-라이브러리와-패키지도구">✅ 사용할 라이브러리와 패키지도구</h1>
<h2 id="✔-expressjs">✔ Express.js</h2>
<p>Node.js를 위한 웹 어플리케이션 프레임워크. Python의 Django 역할이다.</p>
<h2 id="✔-npm">✔ NPM</h2>
<pre><code class="language-bash"># 어떤 라이브러리를 사용했는지 &#39;package.json&#39; 파일로 기록해준다.
npm init</code></pre>
<p>라이브러리를 설치하기 위한 도구다. 예를 들어 <code>npm install express</code>이라는 명령어로 express 라이브러리를 설치하면 설치 기록을 남겨준다.</p>
<p>node_modules 라는 파일이 만들어지는데, 여긴 라이브러리에 관련된 파일들이 설치되어있다.</p>
<h3 id="1-ejs-라이브러리">1. EJS 라이브러리</h3>
<pre><code class="language-bash">npm install ejs</code></pre>
<p>HTML을 쉽게 사용할 수 있게 하는 템플릿 엔진이다. 템플릿 엔진은 서버 자원을 이용해서 동적으로 html을 만들어준다. 데이터들은 예쁜 HTML로 보여준다. 사용하기 쉽다.</p>
<p>ejs 문법을 사용해 서버데이터를 삽입한다. ejs 파일은 항상 views 라는 이름의 파일 안에 위치해야 정상작동한다.</p>
<p>Vue, React, Angular를 사용해도 데이터 바인딩이 가능하지만 프론트엔드 프레임워크 및 라이브러리라 백엔드가 이것까지 사용하기엔 쉽지 않다.</p>
<h3 id="2-서버-재실행-자동화">2. 서버 재실행 자동화</h3>
<pre><code class="language-bash">npm install -g nodemon</code></pre>
<p><code>node &lt;js파일명&gt;</code>명렁어로 서버를 실행 할 수 있지만 파일을 수정하면 다시 껐다가 켜야 하는 불편함이 있다. <code>nodemon</code>을 설치하면 서버를 자동으로 재실행시킬 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/daf0903b-f990-4aba-b7e3-af90313a9262/image.png" alt=""></p>
<p>내 경우에는 에러가 발생했는데 현재 사용자에게 node_modules에 액세스할 수 있는 권한을 부여하면 해결된다.</p>
<pre><code class="language-bash">sudo chown -R $USER /usr/local/lib/node_modules</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/bec504f8-81cf-4b46-9f91-3d296f2250b3/image.png" alt=""></p>
<h3 id="3-환경변수">3. 환경변수</h3>
<pre><code class="language-bash">npm install dotenv</code></pre>
<p>환경변수를 한 곳에 모아서 관리하기 위한 라이브러리다.</p>
<pre><code class="language-jsx">require(&#39;dotenv&#39;).config()</code></pre>
<h2 id="✔-bootstrap">✔ Bootstrap</h2>
<p>HTML/CSS 라이브러리. HTML을 직접 짜지 않고 UI를 만들고 API 개발만 신경쓸 수 있다.</p>
<h1 id="✅-javascript-관련">✅ JavaScript 관련</h1>
<h2 id="✔-콜백함수">✔ 콜백함수</h2>
<pre><code class="language-jsx">.get(경로, function(요청내용,응답방법){});
// ES6 신문법
.get(경로, (요청내용,응답방법)=&gt;{});</code></pre>
<p>함수 안에 함수를 콜백함수라고 한다. 위 코드처럼 함수 파라미터 자리에 함수를 넣는다. 첫 번째 파라미터 실행 후 함수가 실행된다. 콜백 함수는 순차적 실행을 보장하기 때문에 유용하게 쓰인다.</p>
<h1 id="✅-mongodb-호스팅-mongodb-atlas">✅ MongoDB 호스팅: MongoDB Atlas</h1>
<p>MongoDB Atlas는 클라우드 데이터베이스로, 컴퓨터에 설치하지 않아도 돼서 빠르고, 용량도 크고, 간편하다.</p>
<pre><code>mongodb+srv://minha:&lt;password&gt;@cluster0.m9rm4ml.mongodb.net/?retryWrites=true&amp;w=majority</code></pre><p>위는 DB를 내 API와 연동하기 위한 커넥션 url이다.</p>
<pre><code class="language-bash">npm install mongodb</code></pre>
<p>mongodb 라이브러리를 설치하면 되는데</p>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/c0dab32d-d26c-4567-afeb-87ee8e7592a5/image.png" alt=""></p>
<p>내 경우에는 버전 에러가 발생한것 같아 따로 버전 지정을 했다.</p>
<pre><code class="language-bash">npm install mongodb@3.6.4</code></pre>
<h2 id="✔-collection-문법">✔ collection 문법</h2>
<ul>
<li>collection().findOne() : 하나 찾기</li>
<li>collection().find().toArray() : 많이 찾기</li>
<li>collection().updateOne() : 하나 수정하기</li>
</ul>
<h2 id="✔-binary-search">✔ Binary Search</h2>
<p>검색 할 때 반씩 쪼개서 찾는 db검색방법. 인덱스를 만들어서 사용한다.</p>
<h2 id="✔-operator">✔ Operator</h2>
<p>데이터를 수정할 때는 operator를 써야한다.</p>
<p>operator는 연산자로, 중괄호 안의 중괄호로 사용한다.</p>
<ul>
<li>{$set : {totalPost : 바꿀 값} }</li>
<li>{$inc : {totalPost : 더할 값} }</li>
</ul>
<pre><code class="language-jsx">db.collection(&#39;counter&#39;).updateOne({name:&#39;count&#39;},{$inc : {totalPost:1}}, function(error, result){
                if(error){return console.log(error)}
            });</code></pre>
<ul>
<li>예시
<img src="https://velog.velcdn.com/images/miracle-21/post/4e323869-8465-4c05-b776-4d35170a6842/image.png" alt=""></li>
</ul>
<h1 id="✅-crud">✅ CRUD</h1>
<h2 id="✔-get--post">✔ GET &amp; POST</h2>
<p>Express는 GET과 POST 메서드를 기본적으로 제공한다.</p>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/dcf8385a-6fc1-45ab-ab9c-b48de7bc75c1/image.png" alt=""></p>
<h3 id="1-body-parser">1. body-parser</h3>
<pre><code class="language-bash">npm install body-parser</code></pre>
<p>body-parser는 요청데이터(body)해석을 도와준다. 즉, POST, PUT 메소드의 request.body를 읽어준다.</p>
<p>express에서 미들웨어 없이 request.body 에 접근하면 요청 데이터 값이 undefined이 나온다.</p>
<pre><code class="language-jsx">urlencoded({ extended: false });</code></pre>
<p>bodyParser의 옵션 중 하나. false 값일 시 node.js에 기본으로 내장된 queryString, true 값일 시 npm qs 라이브러리(설치 필요)를 사용한다.</p>
<p>&lt; 내용출처: <a href="https://velog.io/@yejinh/express-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-bodyParser-%EB%AA%A8%EB%93%88">https://velog.io/@yejinh/express-미들웨어-bodyParser-모듈</a> &gt;</p>
<h3 id="2-form-데이터의-경우-input에-name-쓰기">2. form 데이터의 경우 input에 name 쓰기</h3>
<pre><code class="language-html">&lt;!-- name을 title로 --&gt;
&lt;form action=&quot;/add&quot; method=&quot;POST&quot;&gt;
    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label&gt;오늘의 할일&lt;/label&gt;
      &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;title&quot;&gt;
    &lt;/div&gt;
&lt;form&gt;</code></pre>
<p>서버에서 input을 구분 할 수 있도록 이름을 지정한다.</p>
<h2 id="✔-put">✔ PUT</h2>
<p>method-override 라이브러리를 사용하면 form에서 PUT/DELETE 요청을 쓸 수 있다.</p>
<pre><code class="language-bash">npm install method-override</code></pre>
<pre><code class="language-jsx">// JS 서버 파일에 추가
const methodOverride = require(&#39;method-override&#39;);
app.use(methodOverride(&#39;_method&#39;))</code></pre>
<p>수정할 게시물 id를 알아야 한다. 이 경우 form 전송 시 같이 보내는 방법이 있다.</p>
<pre><code class="language-jsx">&lt;input value=&quot;&lt;%= result._id %&gt;&quot; type=&quot;text&quot; name=&quot;id&quot; style=&quot;display: none;&quot;&gt;</code></pre>
<p><code>display: none;</code>으로 보내면 사용자 눈에 보이지 않으면서 데이터를 보낼 수 있다.</p>
<h2 id="✔-delete">✔ DELETE</h2>
<h3 id="1-method-override-라이브러리">1. method-override 라이브러리</h3>
<p>동기방식으로 삭제 기능을 구현할 수 있다. 삭제 후 창이 새로고침된다.</p>
<ul>
<li>예시:
<img src="https://velog.velcdn.com/images/miracle-21/post/cd6ab5e1-3f55-4727-a95f-e62250fbcaea/image.png" alt=""></li>
</ul>
<h3 id="2-javascript-ajax">2. JavaScript AJAX</h3>
<p>AJAX는 새로고침 없이 서버에 요청을 하는 비동기 방식으로 처리할 수 있다.</p>
<p>JS문법으로 하면 코드가 복잡해서 jquery 문법을 쓴다.</p>
<pre><code class="language-html">&lt;!-- https://code.jquery.com 에서 jquery를 빌려쓴다. CDN 방식 jquary 설치 코드. --&gt;
&lt;script src=&quot;https://code.jquery.com/jquery-3.3.1.min.js&quot;&gt;&lt;/script&gt;</code></pre>
<p>AJAX를 사용할 때에는 성공/실패 여부를 알아야 하기 때문에 응답을 꼭 보내야 한다.</p>
<pre><code class="language-jsx">response.status(200).send({message : &#39;삭제완료&#39;});</code></pre>
<ul>
<li>예시:
<img src="https://velog.velcdn.com/images/miracle-21/post/d0e2fad2-58a8-4771-8c49-66f998ab248b/image.png" alt=""></li>
</ul>
<h1 id="✅-expressjs-관련">✅ Express.js 관련</h1>
<h2 id="✔-redirect">✔ redirect</h2>
<p>지정하는 경로로 이동시킨다. 예를 들어 HTTP 메서드에 대한 응답으로 <code>res.redirect(&#39;/home&#39;)</code> 처리하면 home 디렉토리로 이동한다.</p>
<p>express를 사용하면 redirect를 편하게 할 수 있다.</p>
<p>redirect가 제대로 동작을 안해서 당황했는데</p>
<blockquote>
<p><a href="https://bobbyhadz.com/blog/javascript-error-cannot-set-headers-after-they-are-sent-to-client">https://bobbyhadz.com/blog/javascript-error-cannot-set-headers-after-they-are-sent-to-client</a></p>
</blockquote>
<p>이 사이트를 참고해서 감을 잡았다. <strong><code>response.send()</code></strong> 를 같이 쓰니까 안됐던 것. 중복사용은 안된다.</p>
<h2 id="✔-미들웨어">✔ 미들웨어</h2>
<p><code>app.use(미들웨어)</code> 식으로 사용한다. 미들웨어란 요청과 응답 중간에 코드를 실행시켜준다.</p>
<h1 id="✅-session-방식-로그인-구현">✅ Session 방식 로그인 구현</h1>
<pre><code class="language-bash">npm install passport passport-local express-session</code></pre>
<pre><code class="language-jsx">const passport = require(&#39;passport&#39;);
const LocalStrategy = require(&#39;passport-local&#39;);
const session = require(&#39;express-session&#39;);
app.use(session({secret: &#39;비밀코드&#39;, resave: true, saveUninitialized: false}));
app.use(passport.initialize());
app.use(passport.session());</code></pre>
<p>세 개의 라이브러리를 설치하고 첨부까지 마친다.</p>
<p>passport 라이브러리는 요청을 인증하기 위해 웹 애플리케이션 내에서 미들웨어로 사용된다. nodejs에서 로그인을 쉽게 구현할 수 있게한다.</p>
<pre><code class="language-jsx">//local 방식으로 인증
app.post(&#39;/login&#39;, passport.authenticate(&#39;local&#39;, {failureRedirect : &#39;/fail&#39;}) ,function(request, response){
    response.redirect(&#39;/&#39;)
})</code></pre>
<p>Strategy로 요청을 인증한다.</p>
<blockquote>
<p>참고사이트: <a href="https://www.passportjs.org/packages/passport-local/">https://www.passportjs.org/packages/passport-local/</a></p>
</blockquote>
<p>express-session은 세션이 많아질수록 메모리 부하가 생기는 단점이 있다. connect-mongo 등의 라이브러리로 DB에 세션데이터를 저장해서 보완할 수 있다고 한다.</p>
<ul>
<li>쿠키 확인
<img src="https://velog.velcdn.com/images/miracle-21/post/6a976feb-1c46-4c93-a3e6-e00b941f86fb/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠버네티스(Kubernetes)]]></title>
            <link>https://velog.io/@miracle-21/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4Kubernetes</link>
            <guid>https://velog.io/@miracle-21/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4Kubernetes</guid>
            <pubDate>Tue, 08 Nov 2022 08:37:26 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-쿠버네티스">✅ 쿠버네티스</h2>
<p>&amp;nbsp 쿠버네티스는 Google이 설계한 <strong>오픈소스 컨테이너 오케스트레이션(Container Orchestration)</strong> 도구다. 컨테이너 오케스트레이션은 컨테이너의 <strong>대규모 운영, 배포, 관리, 확장, 네트워킹을 자동화</strong>하는 체계를 의미한다. 대표적인 컨테이너 오케스트레이션 툴은 <code>쿠버네티스(Kubernetes)</code>, <code>도커 스웜(Docker Swarm)</code>, <code>아파치 메소스(Apache Mesos)</code> 가 있다.</p>
<h2 id="✅-쿠버네티스-컴포넌트">✅ 쿠버네티스 컴포넌트</h2>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/0b6329d8-0cc2-4649-8d2c-dbb841cf88a8/image.png" alt=""></p>
<p>&amp;nbsp 쿠버네티스 컴포넌트는 쿠버네티스의 구성요소를 의미한다. 쿠버네티스를 배포하면 클러스터(cluster)를 얻는다. 모든 클러스터는 최소 한 개의 노드를 가지고, 노드는 애플리케이션의 구성요소인 파드를 호스트한다. </p>
<ul>
<li><strong>클러스터(cluster)</strong>: 쿠버네티스의 리소스 제어단위</li>
<li><strong>노드(node)</strong>: 파드가 실행되는 서버</li>
<li><strong>파드(pod)</strong>: 1개 이상의 컨테이너 집합체</li>
</ul>
<p>&amp;nbsp 쿠버네티스의 컴포넌트는 쿠버네티스의 기능제어를 담당하는 <strong>컨트롤 플레인(Control Plane) 컴포넌트</strong>와, 컨트롤 플레인 컴포넌트의 요청을 받아 각 노드에 동작시키는 <strong>노드(Node) 컴포넌트</strong>가 있다.</p>
<h3 id="1-컨트롤-플레인control-plane-컴포넌트">1. 컨트롤 플레인(Control Plane) 컴포넌트</h3>
<ul>
<li><p><strong>kube-apiserver</strong>
쿠버네티스 클러스터에 kubectl로 명령을 전달하면 api-server가 요청을 처리한다.
예) 파드의 실행, 종료 등에 대한 명령을 노드로 전달할 수 있다.</p>
</li>
<li><p><strong>etcd(엣시디)</strong>
key-value 형태로 클러스터 및 리소스의 구성정보, 상태정보 및 명세정보를 분산 저장하는 저장소다.</p>
</li>
<li><p><strong>kube-scheduler</strong>
파드(pod)를 어떤 노드로 배치할지 결정한다.</p>
</li>
<li><p><strong>kube-controller-manager</strong>
다양한 컨트롤러 프로세스를 실행하고 클러스트의 상태를 관리한다.</p>
<ul>
<li><strong>노드 컨트롤러</strong>: 노드가 다운됐을 때 대처한다.</li>
<li><strong>레플리케이션 컨트롤러</strong>: 적절한 파드 수를 배치한다.</li>
<li><strong>엔드포인트 컨트롤러</strong>: 서비스와 파드를 연결한다.</li>
<li><strong>서비스어카운트 &amp; 토큰컨트롤러</strong>: 새로운 네임스페이스에 대한 기본계정과 API접근토큰을 생성한다.</li>
</ul>
</li>
</ul>
<h3 id="2-노드node-컴포넌트">2. 노드(Node) 컴포넌트</h3>
<ul>
<li><p><strong>kubelet(쿠블릿)</strong>
kube-apiserver 에서 받은 명령을 노드에서 실행하여 파드 생성, 중지 등의 관리를 담당한다.</p>
</li>
<li><p><strong>container runtime</strong>
파드에 포함된 컨테이너를 실행한다.</p>
</li>
<li><p><strong>kube-proxy</strong>
네트워크 연결을 관리하고 요청을 전달한다.</p>
</li>
</ul>
<h2 id="✅-kubernetes의-특징">✅ Kubernetes의 특징</h2>
<h4 id="✔-무중단fault-tolerance-ft-서비스">✔ 무중단(Fault tolerance-FT) 서비스</h4>
<p>&amp;nbsp 서비스를 중단하지 않고도 업데이트할 수 있다.</p>
<h4 id="✔-서비스-디스커버리service-discovery">✔ 서비스 디스커버리(Service Discovery)</h4>
<p>&amp;nbsp 서로 다른 서비스들의 IP와 Port 정보에 대해서 저장하고 관리하는 것을 서비스 디스커버리라고 한다. 쿠버네티스는 DNS 이름을 사용하거나 자체 IP 주소를 사용하여 컨테이너를 노출할 수 있다. </p>
<h4 id="✔-로드-밸런싱load-balancing">✔ 로드 밸런싱(Load balancing)</h4>
<p>&amp;nbsp 컨테이너에 대한 트래픽이 많으면, 쿠버네티스는 네트워크 트래픽을 로드밸런싱(트래픽에 따라 컨테이너 수를 자동 조절)하고 배포하여 배포가 안정적으로 이루어질 수 있다.</p>
<h4 id="✔-스토리지-오케스트레이션storage-orchestration">✔ 스토리지 오케스트레이션(Storage Orchestration)</h4>
<p>&amp;nbsp 저장소 시스템을 자동으로 탑재한다.</p>
<h4 id="✔-자동화된-롤아웃과-롤백rollout-and-rollback">✔ 자동화된 롤아웃과 롤백(Rollout and Rollback)</h4>
<p>배포된 컨테이너의 현재 상태를 원하는 상태로 변경할 수 있다. 예를 들어 쿠버네티스를 자동화해서 배포용 새 컨테이너를 만들고, 기존 컨테이너를 제거하고, 모든 리소스를 새 컨테이너에 적용할 수 있다.</p>
<h4 id="✔-자동화된-빈-패킹bin-packing">✔ 자동화된 빈 패킹(bin packing)</h4>
<p>컨테이너화된 작업을 실행하는데 사용할 수 있는 쿠버네티스 클러스터 노드를 제공한다. 각 컨테이너가 필요로 하는 CPU와 메모리(RAM)를 쿠버네티스에게 지시한다. 쿠버네티스는 컨테이너를 노드에 맞추어서 리소스를 가장 잘 사용할 수 있도록 해준다.</p>
<h4 id="✔-자동화된-복구self-healing">✔ 자동화된 복구(self-healing)</h4>
<p>쿠버네티스는 응답하지 않는 컨테이너를 자동으로 죽이고 재시작하며, 이 과정은 클라이언트에 보여주지 않는다.</p>
<h4 id="✔-시크릿과-구성-관리">✔ 시크릿과 구성 관리</h4>
<p>쿠버네티스를 사용하면 암호, OAuth 토큰 및 SSH 키와 같은 중요한 정보를 저장하고 관리 할 수 있다. 컨테이너 이미지를 재구성하지 않고 스택 구성에 시크릿을 노출하지 않고도 시크릿 및 애플리케이션 구성을 배포 및 업데이트 할 수 있다.</p>
<hr>
<p>[참고 사이트]</p>
<p><a href="https://wooono.tistory.com/109">도커와 쿠버네티스 간단 비교</a>
<a href="https://kubernetes.io/ko/docs/concepts/overview/what-is-kubernetes/">kubernetes 공식문서</a>
<a href="https://seongjin.me/tag/kubernetes/">kubernetes | seongjin.me</a>
<a href="https://www.samsungsds.com/kr/insights/220222_kubernetes1.html">쿠버네티스 알아보기 1편: 쿠버네티스와 컨테이너, 도커에 대한 기본 개념</a>
<a href="https://hoing.io/archives/131#i-3">쿠버네티스(kubernetes) (2) - 기본 용어 - 개념 - 컴포넌트 - 클러스터 구성</a>
<a href="https://coding-start.tistory.com/308">Kubernetes란? (클러스터,노드,파드(pod), 리플리카셋, 디플로이먼트)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스]다리를 지나는 트럭]]></title>
            <link>https://velog.io/@miracle-21/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD</link>
            <guid>https://velog.io/@miracle-21/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD</guid>
            <pubDate>Wed, 26 Oct 2022 10:58:18 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-문제">✅ <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42583">문제</a></h2>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/aabecc55-0a7d-4d14-85f7-2234b931ef25/image.png" alt=""></p>
<h2 id="✅-코드">✅ 코드</h2>
<h3 id="1-정답률-286">1. 정답률 28.6</h3>
<pre><code class="language-py">from collections import deque

def solution(bridge_length, weight, truck_weights):
    dq = deque(truck_weights)
    length = 1
    time = 1
    item = dq.popleft()
    while dq:
        if item + dq[0] &lt;= weight and length &lt;= bridge_length:
            item += dq.popleft()
            length += 1
            time += 1
        else:
            item = dq.popleft()
            length = 1
            time += bridge_length
    return time + bridge_length</code></pre>
<h3 id="2-다른-사람의-코드">2. 다른 사람의 코드</h3>
<pre><code class="language-py">def solution(bridge_length, weight, truck_weights):
    time = 0
    q = [0] * bridge_length
    while q:
        q.pop(0)
        time += 1
        if truck_weights:
            if sum(q) + truck_weights[0] &lt;= weight:
                q.append(truck_weights.pop(0))
            else:
                q.append(0)
    return time</code></pre>
<h2 id="☑️-해설">☑️ 해설</h2>
<p>&amp;nbsp 구글링 결과 모든 사람들이 1초씩 계산해야 풀린다고 한다... 애초에 1초 아니면 다리 길이만큼의 시간이 지나게 풀었으니 계속 틀렸던 것. 하긴 다리 위의 무게가 최대 무게보다 가벼워지는 순간 다시 트럭이 지나갈 수 있으니 1초씩 계산하는게 맞긴하다.</p>
<pre><code class="language-python">q = [0] * bridge_length</code></pre>
<p>다리 길이 만큼의 0을 가진 리스트를 만든다. 리스트의 0의 개수는 다리의 길이다.</p>
<pre><code class="language-py">while q:
    q.pop(0)
    time += 1
    if truck_weights:
        if sum(q) + truck_weights[0] &lt;= weight:
            q.append(truck_weights.pop(0))
        else:
            q.append(0)</code></pre>
<p>리스트의 0번 인덱스가 1개 빠질 떄마다 1초 증가한다. 빠진 자리에는 <strong>트럭이 추가되거나 0이 추가된다</strong>.</p>
<ol>
<li>트럭이 추가되는 경우
다리 위의 무게(<code>sum(q) + truck_weights[0]</code>)가 최대 무게(<code>weight</code>)보다 가볍거나 같다.</li>
<li>0이 추가되는 경우
다리 위의 무게(<code>sum(q) + truck_weights[0]</code>)가 최대 무게(<code>weight</code>)보다 무겁다.</li>
</ol>
<p>만약 <code>weight = 10</code>, <code>bridge_length = 2</code>, <code>truck_weights = [7,4,5,6]</code> 인 경우에 <code>트럭[7]</code>이 지나갈 때까지 다음 트럭이 리스트 안에 들어갈 수 없다</p>
<pre><code>0 0 (time = 0)
0 7 (1s)
7 0 (2s)
0 4 (3s)
4 5 (4s)
5 0 (5s)
0 6 (6s)
6   (7s)
    (8s)
break</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스]구명보트]]></title>
            <link>https://velog.io/@miracle-21/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B5%AC%EB%AA%85%EB%B3%B4%ED%8A%B8</link>
            <guid>https://velog.io/@miracle-21/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B5%AC%EB%AA%85%EB%B3%B4%ED%8A%B8</guid>
            <pubDate>Fri, 21 Oct 2022 09:26:03 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-문제">✅ <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42885#">문제</a></h2>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/ce4a264f-04c2-4a23-af8e-21b3e0c86fb1/image.png" alt=""></p>
<h2 id="✅-코드">✅ 코드</h2>
<h3 id="1-오답">1. 오답</h3>
<pre><code class="language-py">answer = 0
    people.sort(reverse=True)
    while len(people) &gt; 1:
        if limit - people[0] &lt; people[-1]:
            del people[0]
            answer +=1
        elif people[0] == people[-1]:
            if len(people) == 2:
                break
            else:
                del people[-1]
                del people[0]
                answer += 1
        else:
            for i in range(1, len(people)):
                if limit - people[0] &gt;= people[i]:
                    del people[i]
                    del people[0]
                    answer += 1
    answer += 1
    return answer</code></pre>
<h3 id="2-다른-사람의-코드1">2. 다른 사람의 코드1</h3>
<pre><code class="language-py">from collections import deque

def solution(people, limit):
    answer = 0
    deq = deque(sorted(people))
    while len(deq):
        if len(deq) == 1:
            answer += 1
            break
        if deq[0] + deq[-1] &lt;= limit:
            deq.pop()
            deq.popleft()
        else:
            deq.pop()
        answer += 1
    return answer</code></pre>
<h3 id="3-다른-사람의-코드2">3. 다른 사람의 코드2</h3>
<pre><code class="language-py">def solution(people, limit) :
    answer = 0
    people.sort()

    a = 0
    b = len(people) - 1
    while a &lt; b :
        if people[b] + people[a] &lt;= limit :
            a += 1
            answer += 1
        b -= 1
    return len(people) - answer</code></pre>
<h2 id="☑️-핵심코드">☑️ 핵심코드</h2>
<h4 id="1-deque데크-양방향-큐">1. deque(데크): 양방향 큐</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DOCKER]Docker-compose 명령어]]></title>
            <link>https://velog.io/@miracle-21/DOCKERDocker-compose-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@miracle-21/DOCKERDocker-compose-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Wed, 19 Oct 2022 08:02:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://docs.docker.com/compose/reference/">docker docs - docker compose</a></p>
</blockquote>
<hr>
<h3 id="✔-yaml-vs-json">✔ YAML vs JSON</h3>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/c8782a40-8986-4925-bf67-dde8768ae628/image.png" alt=""></p>
<ul>
<li>두칸씩 띄어쓴다 .</li>
<li><code>-</code> 는 JSON에서 <code>list</code>를 의미한다.</li>
</ul>
<h3 id="✔-yaml-문법">✔ YAML 문법</h3>
<ul>
<li><p><code>version</code> : docker-compose 파일 버전</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/ad018399-7664-43ef-8799-5d1df7f6b86f/image.png" alt=""></li>
</ul>
</li>
<li><p><code>services</code> : 이하 하나 이상의 컨테이너 설정</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/9d8afb3f-268a-49f3-b596-354518569ffd/image.png" alt=""> db라는 컨테이너 설정.</li>
</ul>
</li>
<li><p><code>volumes: -&lt;host_dir&gt;:&lt;container_dir&gt;</code> : host 폴더를 컨테이너 폴더와 연결</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/a50a82e0-0adc-42c3-adbe-6d4f7f4c24b1/image.png" alt=""></li>
</ul>
</li>
<li><p><code>restart</code> : 서비스가 중지된 경우 항상 재시작</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/8315d57a-6f4c-4d23-b040-23d57a86d430/image.png" alt=""></li>
</ul>
</li>
<li><p><code>environment</code> : 컨테이너 환경변수 설정</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/8d5da76f-a4e8-434a-88be-22e0f03eff6c/image.png" alt=""></li>
</ul>
</li>
<li><p><code>env_file</code> : 외부 파일에서 환경 변수 전달</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/943db46f-2020-4642-ab3e-394c0c3191df/image.png" alt=""></li>
</ul>
</li>
<li><p><code>posts</code> : 외부포트와 내부포트 연결</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/a5ce8340-52f1-4ffe-9824-3a6a3a7ada45/image.png" alt=""></li>
</ul>
</li>
<li><p><code>depends_on</code> : 컨테이너 실행 전에 먼저 실행되어야 할 다른 컨테이너</p>
<ul>
<li><img src="https://velog.velcdn.com/images/miracle-21/post/94884ee2-b0ae-43e9-9808-4f8ebb22c773/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="✔-docker-compose-명령어">✔ Docker Compose 명령어</h3>
<ul>
<li><code>docker-compose --version</code> : 버전 확인</li>
<li><code>docker-compose up &lt;options&gt;</code> : 컨테이너 실행<ul>
<li><code>-d</code> : 컨테이너를 백그라운드에서 실행</li>
</ul>
</li>
<li><code>docker-compose down</code> : 컨테이너 정지</li>
<li><code>docker-compose ps</code> : 컨테이너 상태 확인</li>
<li><code>docker-compose logs</code> : 컨테이너 이력 확인</li>
<li><code>docker-compose logs -f</code> : 실시간으로 이력 확인</li>
<li><code>docker-compose start / stop / pause / unpause / restart [서비스명]</code> : 시작 / 정지 / 일시정지 / 재시작</li>
<li><code>docker-compose rm</code> : 컨테이너 삭제</li>
<li><code>docker-compose kill</code> : 컨테이너 강제 정지</li>
<li><code>docker-compose config</code> : yml 파일 내용 확인</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DOCKER]Dockerfile 명령어]]></title>
            <link>https://velog.io/@miracle-21/DOCKERDockerfile</link>
            <guid>https://velog.io/@miracle-21/DOCKERDockerfile</guid>
            <pubDate>Tue, 18 Oct 2022 10:37:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&amp;nbsp Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.</p>
<p>&amp;nbsp Docker는 Dockerfile에서 지시사항을 읽어 이미지를 자동으로 작성할 수 있다. Dockerfile은 사용자가 명령행에서 호출하여 이미지를 조립할 수 있는 모든 명령을 포함하는 텍스트 문서이다.</p>
<p>출처: <a href="https://docs.docker.com/engine/reference/builder/">dockerfile reference</a></p>
</blockquote>
<hr>
<h2 id="✅-dockerfile">✅ Dockerfile</h2>
<ul>
<li><code>FROM &lt;이미지_이름&gt;[:태그]</code> : 기본 이미지 설정<ul>
<li>ex)<code>FROM ubuntu:latest</code></li>
</ul>
</li>
<li><code>LABEL &lt;key&gt;=&lt;value&gt; [&lt;key&gt;=&lt;value&gt; ...]</code> : 정보 라벨링<ul>
<li>ex)<code>LABEL version=&quot;1.0&quot;</code></li>
</ul>
</li>
<li><code>COPY &lt;복사할_파일&gt; &lt;복사할_위치&gt;</code> : 호스트에 위치한 파일을 컨테이너로 복사<ul>
<li>제외하고 싶은 파일/폴더는 <code>.dockerignore</code> 파일에 작성하면 제외된다.</li>
<li>ex)<code>COPY home.txt /mydir/</code></li>
</ul>
</li>
<li><code>CMD [&quot;executable&quot;,&quot;param1&quot;,&quot;param2&quot;]</code> : 컨테이너 실행시 실행되는 명령어. 보통 실행과 동시에 서버를 열어둘 때 사용<ul>
<li><code>Docker run ~</code> 명령어 마지막에 CMD 명령을 수정할 수 있다.</li>
<li>ex) <code>CMD [&quot;python3&quot;, &quot;-u&quot;, &quot;-m&quot;, &quot;http.server&quot;]</code></li>
</ul>
</li>
<li><code>ENTRYPOINT [&quot;executable&quot;, &quot;param1&quot;, &quot;param2&quot;]</code> : 컨테이너 실행시 실행되는 명령어. CMD와 달리 <code>Docker run</code> 시에 추가한 인자로 수정 불가.</li>
<li><code>RUN &lt;command&gt;</code> : 명령어 실행. 보통 패키지 설치 시에 사용<ul>
<li>한 줄로 작성해야 하나의 이미지로 생성된다.</li>
<li>ex) <code>RUN apt-get update &amp;&amp; apt-get install -y python3</code><ul>
<li><code>-y</code> : 자동으로 yes 선택</li>
</ul>
</li>
</ul>
</li>
<li><code>ENV &lt;key&gt;=&lt;value&gt; ...</code> : 컨테이너 환경변수 설정<ul>
<li>ex) <code>ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}</code></li>
</ul>
</li>
<li><code>WORKDIR &lt;경로&gt;</code> : 명령이 실행될 디렉토리 설정<ul>
<li>ex) <code>WORKDIR /usr/src/app</code></li>
</ul>
</li>
</ul>
<h2 id="✅-docker-command-line">✅ Docker command line</h2>
<ul>
<li><code>docker build</code> : Dockerfile을 바탕으로 이미지 생성. 보통 끝에 온점<code>.</code>을 찍는다.<ul>
<li><code>-t</code>(tag) : 이름 지정. <code>name:tag</code> 형식으로 사용</li>
<li><code>-f</code>(file) : 사용할 Dockerfile 이름(기본값 = Dockerfile)</li>
<li><code>--pull</code> : 최신 버전의 이미지로 생성</li>
<li><a href="https://docs.docker.com/engine/reference/commandline/build/">공식문서에서 더 많은 태그 확인</a></li>
</ul>
</li>
</ul>
<p>--
[참고 사이트]</p>
<p><a href="https://nirsa.tistory.com/m/69?category=868315">https://nirsa.tistory.com/m/69?category=868315</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[GIT]변경사항 취소(커밋 전, add 전)]]></title>
            <link>https://velog.io/@miracle-21/GIT%EB%B3%80%EA%B2%BD%EC%82%AC%ED%95%AD-%EC%B7%A8%EC%86%8C%EC%BB%A4%EB%B0%8B-%EC%A0%84-add-%EC%A0%84</link>
            <guid>https://velog.io/@miracle-21/GIT%EB%B3%80%EA%B2%BD%EC%82%AC%ED%95%AD-%EC%B7%A8%EC%86%8C%EC%BB%A4%EB%B0%8B-%EC%A0%84-add-%EC%A0%84</guid>
            <pubDate>Fri, 30 Sep 2022 09:44:01 GMT</pubDate>
            <description><![CDATA[<h3 id="✔-작업-사항을-모두-commit-시점으로-되돌리고-싶을-때">✔ 작업 사항을 모두 commit 시점으로 되돌리고 싶을 때.</h3>
<pre><code class="language-bash">$ git reset
$ git checkout .
$ git clean -fdx</code></pre>
<ol>
<li>staged 파일을 unstage로 돌리기.</li>
<li>모든 변경 사항 취소.</li>
<li>추적하지 않는 파일 제거.</li>
</ol>
<blockquote>
<p><strong>※주의※</strong>
github에 올라가지 않은 파일도 같이 제거된다. <code>my_settings.py</code>가 날아가서 데이터를 날려먹은 경험이 있다...</p>
</blockquote>
<hr>
<p>[참고 사이트]</p>
<p><a href="https://extbrain.tistory.com/83">https://extbrain.tistory.com/83</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DRF]Authentication & Permission]]></title>
            <link>https://velog.io/@miracle-21/DRFAuthentication-Permission</link>
            <guid>https://velog.io/@miracle-21/DRFAuthentication-Permission</guid>
            <pubDate>Fri, 30 Sep 2022 04:25:46 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-authentication인증">✅ Authentication(인증)</h2>
<blockquote>
<p><a href="https://www.django-rest-framework.org/api-guide/authentication/">Django REST framework - Authentication 공식문서</a></p>
</blockquote>
<ol>
<li><p><strong>BasicAuthentication</strong>
&amp;nbsp 일반인증. username 및 password에 대해 서명된 HTTP 기본 인증을 사용한다. 매번 유저 정보(이름,비번)를 넘겨야하기 때문에 보안상 위험하다. 실제 서비스에 사용하기 보다는 테스트용으로 적절하다.</p>
</li>
<li><p><strong>TokenAuthentication</strong>
&amp;nbsp 토큰인증. 간단한 토큰 기반 HTTP 인증 체계를 사용한다. 기본 데스크톱 및 모바일 클라이언트와 같은 client-server 관계에 적합하다.</p>
</li>
<li><p><strong>SessionAuthentication</strong>
&amp;nbsp 세션 인증. Session에 저장되는 정보를 통해 인증한다. 외부 서비스서는 사용할 수 없고, 웹 사이트와 동일한 session context에서 실행 중인 AJAX 클라이언트에 적합하다.</p>
</li>
<li><p><strong>RemoteAuthentication</strong>
&amp;nbsp 존재하지 않는 사용자에 대한 정보를 관리한다. user 정보가 다른 서비스에서 관리하는데 사용된다.</p>
</li>
<li><p><strong>Custom Authentication</strong>
&amp;nbsp Authentication을 직접 커스텀한다.</p>
</li>
</ol>
<h2 id="✅-permission허가">✅ Permission(허가)</h2>
<blockquote>
<p><a href="https://www.django-rest-framework.org/api-guide/permissions/#allowany">Django REST framework - Permissions 공식문서</a></p>
</blockquote>
<ol>
<li><p><strong>AllowAny</strong>
&amp;nbsp 기본값. 인증, 비인증 상관 없이 API에 액세스할 수 있다.</p>
</li>
<li><p><strong>IsAuthenticated</strong>
&amp;nbsp 등록된 사용자만 API에 액세스할 수 있다.</p>
</li>
<li><p><strong>IsAdminUser</strong>
&amp;nbsp 관리자 중 일부만 API에 액세스할 수 있다(<code>User.is_staff = True</code>).</p>
</li>
<li><p><strong>IsAuthenticatedOrReadOnly</strong>
&amp;nbsp 인증된 유저에게만 쓰기 권한을 허용한다. 비인증 유저는 읽기 권한만 허용한다.</p>
</li>
<li><p><strong>DjangoModelPermissions</strong>
&amp;nbsp 인증된 유저가 관련 모델에 권한이 할당된 경우에만 허용한다. Django <code>.queryset</code> 속성 또는 <code>get_queryset()</code> 메서드가 있는 뷰에만 적용된다.</p>
</li>
<li><p><strong>DjangoModelPermissionOrAnonReadonly</strong>
&amp;nbsp DjangoModelPermissions와 유사하지만 비인증 유저가 읽기 권한을 가질 수 있다.</p>
</li>
<li><p><strong>DjangoObjectPermissions</strong>
&amp;nbsp 모델에 대한 권한이 할당된 경우 허용된다.</p>
</li>
<li><p><strong>Custom Permission</strong>
&amp;nbsp Permission을 직접 커스텀한다.</p>
</li>
</ol>
<hr>
<p>[참고 사이트]</p>
<p><a href="https://wisdom-990629.tistory.com/entry/DRF-%EA%B2%8C%EC%8B%9C%ED%8C%90-Authentication%EA%B3%BC-Permission-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0">[D.R.F] 게시판에 Authentication과 Permission 적용하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DRF]다대다: string 필드값 가져오기]]></title>
            <link>https://velog.io/@miracle-21/DRF%EB%8B%A4%EB%8C%80%EB%8B%A4-%ED%95%84%EB%93%9C%EA%B0%92-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@miracle-21/DRF%EB%8B%A4%EB%8C%80%EB%8B%A4-%ED%95%84%EB%93%9C%EA%B0%92-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Thu, 29 Sep 2022 03:01:05 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-기본-정보">✅ 기본 정보</h2>
<ul>
<li>메인페이지에 작성된 게시물을 내보낸다.</li>
<li>게시물 작성 시 태그를 필수적으로 달아야 한다.</li>
<li>이 태그가 id값으로 반환되는데, 태그 name을 반환하고 싶다.</li>
</ul>
<h3 id="✔-코드">✔ 코드</h3>
<pre><code class="language-py"># serializers.py
class PostListSerializer(serializers.ModelSerializer):
    like = serializers.IntegerField(default=0, read_only=True)
    view = serializers.IntegerField(default=0, read_only=True)

    class Meta:
        model = Post
        fields = &#39;__all__&#39;</code></pre>
<h3 id="✔-posts">✔ /posts/</h3>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/735101c4-062c-4582-a5a4-91fa73348123/image.png" alt=""></p>
<h2 id="✅-수정-코드">✅ 수정 코드</h2>
<h3 id="✔-방법1-tag--serializerscharfieldsourcetagname-추가">✔ 방법1. tag = serializers.CharField(source=&#39;tag.name&#39;) 추가</h3>
<pre><code class="language-py"># serializers.py
class PostListSerializer(serializers.ModelSerializer):
    like = serializers.IntegerField(default=0, read_only=True)
    view = serializers.IntegerField(default=0, read_only=True)
    tag  = serializers.CharField(source=&#39;tag.name&#39;)

    class Meta:
        model = Post
        fields = &#39;__all__&#39;</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/b2b33acc-80d4-4ed1-bb66-58fdeff16432/image.png" alt=""></p>
<p>&amp;nbsp 결과값이 null로 나온다. 왜 이런가 싶어서 shell로 <code>tag.name</code>을 돌려보니 null 값이 나왔다. 어떻게 해야 name이 제대로 나올까 해보니,
<img src="https://velog.velcdn.com/images/miracle-21/post/aaf2fd3e-24c8-4825-959d-442792682f49/image.png" alt=""></p>
<p>&amp;nbsp 그래서 얘를 어떻게 활용하나? </p>
<h3 id="✔-방법2-tag--serializersstringrelatedfieldmanytrue-추가">✔ 방법2. tag = serializers.StringRelatedField(many=True) 추가</h3>
<pre><code class="language-py"># serializers.py
class PostListSerializer(serializers.ModelSerializer):
    like = serializers.IntegerField(default=0, read_only=True)
    view = serializers.IntegerField(default=0, read_only=True)
    tag  = serializers.StringRelatedField(many=True)

    class Meta:
        model = Post
        fields = &#39;__all__&#39;</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/49e68ddf-eb5c-4ef4-9d8b-c7192290e97a/image.png" alt=""></p>
<p>&amp;nbsp 애초에 잘못된 필드를 사용중이었다. 아무튼 해결.</p>
<hr>
<p>[공식 문서 참고]
<a href="https://www.django-rest-framework.org/api-guide/relations/#stringrelatedfield">https://www.django-rest-framework.org/api-guide/relations/#stringrelatedfield</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DRF]ViewSet]]></title>
            <link>https://velog.io/@miracle-21/DRFViewSet</link>
            <guid>https://velog.io/@miracle-21/DRFViewSet</guid>
            <pubDate>Tue, 27 Sep 2022 07:58:54 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-목적">✅ 목적</h2>
<p>&amp;nbsp GenericAPIView로 만든 API를 ViewSet으로 리펙토링한다.</p>
<h3 id="✔-urlspy">✔ urls.py</h3>
<h4 id="수정-전">수정 전</h4>
<pre><code class="language-py"># urls.py
from django.urls import path

from api2 import views

urlpatterns = [
    path(&#39;post/&#39;, views.PostListAPIView.as_view(), name=&#39;post-list&#39;), #url 이름
    path(&#39;post/&lt;int:pk&gt;&#39;, views.PostRetrieveListAPIView.as_view(), name=&#39;post-detail&#39;),
    path(&#39;comment/&#39;, views.CommentCreateAPIView.as_view(), name=&#39;comment-list&#39;),
    path(&#39;post/&lt;int:pk&gt;/like/&#39;, views.PostLikeAPIView.as_view(), name=&#39;post-like&#39;),
    path(&#39;catetag/&#39;, views.CateTagAPIView.as_view(), name=&#39;catetag&#39;),
]</code></pre>
<h4 id="수정-후">수정 후</h4>
<pre><code class="language-py"># urls.py
from django.urls import path

from api2 import views

urlpatterns = [
    # PostViewSet
    path(&#39;post/&#39;, views.PostViewSet.as_view(actions={&#39;get&#39;: &#39;list&#39;}), name=&#39;post-list&#39;),
    path(&#39;post/&lt;int:pk&gt;/like/&#39;, views.PostViewSet.as_view(actions={&#39;get&#39;: &#39;like&#39;}), name=&#39;post-like&#39;),
    # serializer_class가 달라서 그런지 위의 url과 묶여지지 않아서 pass함.
    path(&#39;post/&lt;int:pk&gt;&#39;, views.PostRetrieveListAPIView.as_view(), name=&#39;post-detail&#39;),
    # CommentViewSet
    path(&#39;comment/&#39;, views.CommentViewSet.as_view(actions={&#39;post&#39;: &#39;create&#39;}), name=&#39;comment-list&#39;),
    # category, tag 테이블을 다뤄야 하니 건들지 않는다.
    path(&#39;catetag/&#39;, views.CateTagAPIView.as_view(), name=&#39;catetag&#39;),
]</code></pre>
<p>&amp;nbsp ViewSet은 actions 인자가 필수다. Router를 사용한다면 자동으로 actions 인자를 채워주지만 router를 사용하지 않을거니 추가해준다.</p>
<h3 id="✔-viewspy">✔ views.py</h3>
<h4 id="수정-전-1">수정 전</h4>
<pre><code class="language-py"># views.py    
class PostListAPIView(ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostListSerializer
    pagination_class = PostPageNumberPagination

    def get_serializer_context(self):
        return {
            &#39;request&#39;: None,
            &#39;format&#39;: self.format_kwarg,
            &#39;view&#39;: self
        }

class PostLikeAPIView(GenericAPIView):
    queryset = Post.objects.all()

    def get(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.like += 1
        instance.save()

        return Response(instance.like)

class CommentCreateAPIView(CreateAPIView):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer</code></pre>
<h4 id="수정-후-1">수정 후</h4>
<pre><code class="language-py"># views.py
class PostViewSet(ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostListSerializer
    pagination_class = PostPageNumberPagination

    def get_serializer_context(self):
        return {
            &#39;request&#39;: None,
            &#39;format&#39;: self.format_kwarg,
            &#39;view&#39;: self
        }

    # ViewSet에서는 get 메서드를 사용하지 않아서 like로 변경
    def like(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.like += 1
        instance.save()

        return Response(instance.like)

class CommentViewSet(ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DRF]Serializer relations]]></title>
            <link>https://velog.io/@miracle-21/DRFSerializer-relations</link>
            <guid>https://velog.io/@miracle-21/DRFSerializer-relations</guid>
            <pubDate>Tue, 27 Sep 2022 05:54:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/miracle-21/post/4bfadfe9-6cbe-4f72-b5eb-16781c83555f/image.png" alt=""></p>
<blockquote>
<p>&amp;nbsp<strong>Relational fields are used to represent model relationships. They can be applied to <code>ForeignKey</code>, <code>ManyToManyField</code> and <code>OneToOneField</code> relationships, as well as to reverse relationships, and custom relationships such as <code>GenericForeignKey</code>.</strong></p>
<p>&amp;nbsp<strong>관계 필드는 모델 관계를 나타내는 데 사용됩니다. <code>외부 키</code>, <code>다대다 필드</code> 및 <code>일대일 필드</code> 관계뿐만 아니라 역관계 및 사용자 지정 관계와 같은 <code>일반 외부 키</code>에도 적용할 수 있습니다.</strong></p>
<p>&amp;nbsp<strong>ㅡ django-rest-framework</strong></p>
</blockquote>
<h2 id="✅-serializer-relations">✅ Serializer relations?</h2>
<p>&amp;nbsp 관계형 필드를 사용하는 경우, serializer에서는 위와 같은 field를 사용할 수 있다. 주로 <code>PrimaryKeyRelatedField</code>와 <code>StringRelatedField</code>를 많이 사용한다.</p>
<h3 id="1-primarykeyrelatedfield">1. PrimaryKeyRelatedField</h3>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/64575b9e-1711-47c9-b0a6-5d844496e597/image.png" alt=""></p>
<p>&amp;nbsp PK로 표현된다.</p>
<h3 id="2-stringrelatedfield">2. StringRelatedField</h3>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/dd68e761-de43-440e-829d-baafaf823c63/image.png" alt=""></p>
<p>&amp;nbsp String으로 표현된다. </p>
<h2 id="✅-목표">✅ 목표</h2>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/63604339-a018-4f33-b8cb-db1b5d20db4b/image.png" alt=""></p>
<ul>
<li>category 와 tags 필드를 추가한다.</li>
<li>이미지 url은 경로 부분만 표시한다.</li>
</ul>
<h2 id="✅-기존-코드">✅ 기존 코드</h2>
<p>&amp;nbsp 이전 포스팅 &#39;<a href="https://velog.io/write?id=a7d8ce1c-6038-4c5a-b4f4-250cf4bd0e77">Detail Page 구현</a>&#39;을 참고하면 코드에 대한 상세 내용을 알 수 있다.</p>
<pre><code class="language-py"># views.py
def get_prev_next(instance):
    try:
        prev = instance.get_previous_by_update_dt() # Django 기본 메서드
    except instance.DoesNotExist:
        prev = None

    try:
        next_ = instance.get_next_by_update_dt() # Django 기본 메서드
    except instance.DoesNotExist:
        next_ = None

    return prev, next_


class PostRetrieveListAPIView(RetrieveAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializerDetail

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        prevInstance, nextInstance = get_prev_next(instance)
        commentList = instance.comment_set.all()
        data = {
            &#39;post&#39;: instance,
            &#39;prevPost&#39;: prevInstance,
            &#39;nextPost&#39;: nextInstance,
            &#39;commentList&#39; : commentList,

        }
        serializer = self.get_serializer(instance = data)
        return Response(serializer.data)</code></pre>
<pre><code class="language-py"># serializers.py
class PostRetrieveSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        exclude = [&#39;create_dt&#39;] # &#39;create_dt&#39; 외 모든 데이터 전송</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/ee614b34-b415-43db-9391-26f84a3cca69/image.png" alt=""></p>
<h3 id="✔-stringrelatedfield-추가">✔ StringRelatedField 추가</h3>
<pre><code class="language-py"># serializers.py
class PostRetrieveSerializer(serializers.ModelSerializer):
    category = serializers.StringRelatedField()
    tags = serializers.StringRelatedField(many=True)

    class Meta:
        model = Post
        exclude = [&#39;create_dt&#39;] # &#39;create_dt&#39; 외 모든 데이터 전송</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/e5eb72af-a473-4637-aa74-11e0a752a842/image.png" alt=""></p>
<h3 id="✔-이미지-url-경로-부분만-표시">✔ 이미지 url: 경로 부분만 표시</h3>
<p>&amp;nbsp 이 부분은 이전에 &#39;Pagination 구현&#39; 포스팅 <code>4. 이미지 url: 경로 부분만 표시</code> 에서 구현한 코드를 붙여넣었다. </p>
<pre><code class="language-py"># views.py
class PostRetrieveListAPIView(RetrieveAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializerDetail

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        prevInstance, nextInstance = get_prev_next(instance)
        commentList = instance.comment_set.all()
        data = {
            &#39;post&#39;: instance,
            &#39;prevPost&#39;: prevInstance,
            &#39;nextPost&#39;: nextInstance,
            &#39;commentList&#39; : commentList,

        }
        serializer = self.get_serializer(instance = data)
        return Response(serializer.data)

    def get_serializer_context(self):
        return {
            &#39;request&#39;: None,
            &#39;format&#39;: self.format_kwarg,
            &#39;view&#39;: self
        }</code></pre>
<h3 id="✔-결과">✔ 결과</h3>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/63604339-a018-4f33-b8cb-db1b5d20db4b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DRF]Detail Page 구현]]></title>
            <link>https://velog.io/@miracle-21/DRFDetail-Page-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@miracle-21/DRFDetail-Page-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 27 Sep 2022 05:04:28 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-목표">✅ 목표</h2>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/99c43e76-1090-4d36-9454-7d31e8ca3517/image.png" alt=""></p>
<ul>
<li>현재 게시글, 이전 게시글, 다음 게시글, 댓글로 detail page 구현</li>
</ul>
<h2 id="✅-기존-코드">✅ 기존 코드</h2>
<pre><code class="language-py"># views.py
class PostRetrieveListAPIView(RetrieveAPIView):
    queryset = Post.objects.all()
    serializer_class = PostRetrieveSerializer </code></pre>
<pre><code class="language-py"># serializers.py
class PostRetrieveSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        exclude = [&#39;create_dt&#39;] # &#39;create_dt&#39; 외 모든 데이터 전송</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/56880b04-e84f-42f8-aa29-ea2c0ea64a44/image.png" alt=""></p>
<h3 id="✔-현재-게시글-이전-게시글-다음-게시글-댓글-key-추가">✔ 현재 게시글, 이전 게시글, 다음 게시글, 댓글 key 추가</h3>
<pre><code class="language-py"># serializers.py
class PostSerializerSub(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = [&#39;id&#39;, &#39;title&#39;]

class CommentSerializerSub(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = [&#39;id&#39;, &#39;content&#39;, &#39;update_dt&#39;]

class PostSerializerDetail(serializers.Serializer):
    post = PostRetrieveSerializer()
    prevPost = PostSerializerSub()
    nextPost = PostSerializerSub()
    commentList = CommentSerializerSub(many=True)</code></pre>
<pre><code class="language-py"># views.py
def get_prev_next(instance):
    try:
        prev = instance.get_previous_by_update_dt() # Django 기본 메서드
    except instance.DoesNotExist:
        prev = None

    try:
        next_ = instance.get_next_by_update_dt() # Django 기본 메서드
    except instance.DoesNotExist:
        next_ = None

    return prev, next_


class PostRetrieveListAPIView(RetrieveAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializerDetail

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        prevInstance, nextInstance = get_prev_next(instance)
        commentList = instance.comment_set.all()
        data = {
            &#39;post&#39;: instance,
            &#39;prevPost&#39;: prevInstance,
            &#39;nextPost&#39;: nextInstance,
            &#39;commentList&#39; : commentList,

        }
        serializer = self.get_serializer(instance = data)
        return Response(serializer.data)</code></pre>
<blockquote>
<h4 id="postretrievelistapiview">PostRetrieveListAPIView</h4>
<p><code>RetrieveAPIView</code> &gt; <code>RetrieveModelMixin</code> 참고.</p>
</blockquote>
<pre><code class="language-py">class RetrieveModelMixin:
    &quot;&quot;&quot;
    Retrieve a model instance.
    &quot;&quot;&quot;
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)</code></pre>
<blockquote>
<h4 id="get_previous_by_update_dt-get_next_by_update_dt">get_previous_by_update_dt(), get_next_by_update_dt()</h4>
<p>두 메서드를 사용할 때는 exception 처리를 해야하기 때문에 별도로 <code>get_prev_next</code> 함수를 만들었다. <a href="https://docs.djangoproject.com/en/4.1/ref/models/instances/">공식문서</a> 참고.
<img src="https://velog.velcdn.com/images/miracle-21/post/79955d5c-a14a-41bb-908b-b0b0c2099c82/image.png" alt=""></p>
</blockquote>
<h3 id="✔-결과">✔ 결과</h3>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/0796f0a9-457b-43d9-a2e9-f2fc5118e39b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DRF]Pagination 구현]]></title>
            <link>https://velog.io/@miracle-21/DRFPagination</link>
            <guid>https://velog.io/@miracle-21/DRFPagination</guid>
            <pubDate>Tue, 27 Sep 2022 02:17:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/miracle-21/post/520e0772-44c5-4585-a803-90dd8ecbaae8/image.png" alt=""></p>
<blockquote>
<p>&amp;nbsp<strong>Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links.</strong></p>
<p>&amp;nbsp<strong>Django는 페이지화된 데이터, 즉 &quot;이전/다음&quot; 링크를 사용하여 여러 페이지로 분할된 데이터를 관리하는 데 도움이 되는 몇 가지 클래스를 제공합니다.</strong></p>
<p>&amp;nbsp<strong>— Django documentation</strong></p>
</blockquote>
<h2 id="✅-목표">✅ 목표</h2>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/9f4dece1-b7e7-44cf-a43c-d7b363d46231/image.png" alt=""></p>
<ul>
<li>한 페이지 당 게시물 3개씩 묶기</li>
<li>결과, 총 페이지 수, 현재 페이지 순으로 정렬</li>
<li>category 필드 값을 name으로 수정</li>
<li>이미지 url은 경로 부분만 표시</li>
</ul>
<h2 id="✅-기존-코드">✅ 기존 코드</h2>
<pre><code class="language-py"># views.py
class PostListAPIView(ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostListSerializer</code></pre>
<pre><code class="language-py"># serializers.py
class PostListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = [&#39;id&#39;, &#39;title&#39;, &#39;image&#39;, &#39;like&#39;,&#39;category&#39;]</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/4b100406-679d-48ba-9d49-783a92197f2f/image.png" alt=""></p>
<h3 id="1-한-페이지-당-게시물-3개씩-묶기">1. 한 페이지 당 게시물 3개씩 묶기</h3>
<pre><code class="language-py"># views.py
from rest_framework.pagination import PageNumberPagination

class PostPageNumberPagination(PageNumberPagination):
    page_size = 3

class PostListAPIView(ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostListSerializer
    pagination_class = PostPageNumberPagination</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/edc010bc-71b2-47ec-80ce-2bfbc4eddc53/image.png" alt=""></p>
<h3 id="2-결과-총-페이지-수-현재-페이지-순으로-정렬">2. 결과, 총 페이지 수, 현재 페이지 순으로 정렬</h3>
<pre><code class="language-py">from collections import OrderedDict

class PostPageNumberPagination(PageNumberPagination):
    page_size = 3

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            (&#39;postList&#39;, data),
            (&#39;pageCnt&#39;, self.page.paginator.num_pages),
            (&#39;curPage&#39;, self.page.number),
        ]))</code></pre>
<p>&amp;nbsp <code>page.paginator.num_pages</code>, <code>page.number</code> 코드에 대한 부분은 <a href="https://docs.djangoproject.com/en/4.1/ref/paginator/">Django 공식문서</a> 참고.</p>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/0711eccd-d407-4bae-a802-c2fed7800013/image.png" alt=""></p>
<h3 id="3-category-필드-값을-name으로-수정">3. category 필드 값을 name으로 수정</h3>
<pre><code class="language-py">class PostListSerializer(serializers.ModelSerializer):
    category = serializers.CharField(source=&#39;category.name&#39;)

    class Meta:
        model = Post
        fields = [&#39;id&#39;, &#39;title&#39;, &#39;image&#39;, &#39;like&#39;,&#39;category&#39;]</code></pre>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/0f6a8572-4bd0-435d-b664-77c5ad246b6f/image.png" alt=""></p>
<h3 id="4-이미지-url-경로-부분만-표시">4. 이미지 url: 경로 부분만 표시</h3>
<pre><code class="language-py">class PostListAPIView(ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostListSerializer
    pagination_class = PostPageNumberPagination

    def get_serializer_context(self):
        return {
            &#39;request&#39;: None, # None이 아닌 경우에 full url 표시
            &#39;format&#39;: self.format_kwarg,
            &#39;view&#39;: self
        }</code></pre>
<blockquote>
<p>참고</p>
</blockquote>
<ul>
<li><code>FileField(Field): def to_representation(self, value)</code></li>
<li><code>class GenericAPIView(views.APIView): def get_serializer_context(self)</code>  </li>
</ul>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/ee328a3a-c698-4d35-a99d-3c5e888a0d9c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB]트랜잭션(Transaction)]]></title>
            <link>https://velog.io/@miracle-21/DB%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98Transaction</link>
            <guid>https://velog.io/@miracle-21/DB%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98Transaction</guid>
            <pubDate>Mon, 26 Sep 2022 06:54:06 GMT</pubDate>
            <description><![CDATA[<h3 id="트랜잭션의-필요성">트랜잭션의 필요성?</h3>
<p>&amp;nbsp 트랜잭션은 쪼갤수 없는 최소 업무 처리 단위를 말한다. <strong>데이터베이스에서의 트랜젝션이란 하나의 작업단위를 나타낸다.</strong></p>
<p><img src="https://velog.velcdn.com/images/miracle-21/post/b7386634-cd99-48b7-a0f7-942b213cd9b8/image.png" alt=""></p>
<p>&amp;nbsp 트랜잭션의 중요성은 데이터 동시 접근의 문제에서 발견할 수 있다. 위 사진을 예를 들어 A지점과 B지점에서 동시에 계좌 잔액 500원을 확인하고 100원을 인출했다고 하자. 두 지점 모두 남은 잔액을 400원으로 저장해서 최종 잔액은 300원이 아닌 400원으로 저장됐다. 이는 한 작업이 다른 작업에 영향을 받은 결과이다.</p>
<p>&amp;nbsp 위와 같은 일을 방지하기 위해 데이터베이스는 작업의 단위를 트랜잭션으로 정의한다.</p>
<h3 id="트랜잭션의-특징">트랜잭션의 특징</h3>
<p>&amp;nbsp 트랜잭션은 다수의 연산으로 구성된 트랜잭션이 사용자에게 단일작업처럼 다뤄지도록 ACID 특징을 준수한다.</p>
<h4 id="1-원자성atomicity">1. <strong>원자성(atomicity)</strong></h4>
<ul>
<li>하나의 트랜잭션에 포함된 모든 연산은 완전히 수행되거나 전혀 수행되지 않는다.</li>
<li>All or Noting<blockquote>
<p>ex) 출금 과정에서 하나라도 문제가 된다면 송금이 이루어지지 않는다.
만약 원자성이 지켜지지 않는다면 돈은 빠져나가고 통장 잔고는 그대로인 상황이 발생할 수 있다.</p>
</blockquote>
</li>
</ul>
<h4 id="2-일관성consistency">2. <strong>일관성(consistency)</strong></h4>
<ul>
<li>특정 트랜잭션이 수행되기 전과 후에 데이터베이스가 일관된 상태를 유지한다.<blockquote>
<p>ex) A계좌에서 B계좌로 이체를 할 때, 두 계좌 잔액의 합은 이체 전 후와 같아야 한다. 
위의 사진처럼 500원에서 200원을 출금했는데 400원이 되어버린 상황은 일관성이 무너진 상황이다.</p>
</blockquote>
</li>
</ul>
<h4 id="3-고립성isolation">3. <strong>고립성(isolation)</strong></h4>
<ul>
<li>특정 트랜잭션이 데이터베이스를 갱신하는 동안 다른 트랜잭션에 의해 방해받지 않는다.<blockquote>
<p>ex) 하나의 트랜잭션이 A계좌에서 작업중이라면 다른 트랙잭션은 이를 방해하지 못하고 대기해야 한다.</p>
</blockquote>
</li>
</ul>
<h4 id="4-지속성durability">4. <strong>지속성(durability)</strong></h4>
<ul>
<li>완료된 트랜잭션의 결과는 어떠한 시스템의 장애에도 데이터베이스에 반영되어있어야 한다.</li>
<li>주기억장치 내용이 날아가도 보조기억장치에 기록된 로그로 복원이 가능해야 한다.</li>
</ul>
<hr>
<p>[참고 사이트]</p>
<p><a href="https://limkydev.tistory.com/100">[DataBase] 트랜잭션이란? (Transaction)</a>
<a href="https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=cs0911ke&amp;logNo=60075367600">트랜잭션 개요</a>
<a href="https://m.blog.naver.com/ljh0326s/221305748423">트랜잭션 처리</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS]동기(Synchronous) & 비동기(Asynchronous)]]></title>
            <link>https://velog.io/@miracle-21/%EB%8F%99%EA%B8%B0Synchronous-%EB%B9%84%EB%8F%99%EA%B8%B0Asynchronous</link>
            <guid>https://velog.io/@miracle-21/%EB%8F%99%EA%B8%B0Synchronous-%EB%B9%84%EB%8F%99%EA%B8%B0Asynchronous</guid>
            <pubDate>Mon, 26 Sep 2022 06:53:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/miracle-21/post/b09db237-8095-4b14-b74d-2e3c89c87c42/image.png" alt=""></p>
<h2 id="✅-동기-방식synchronous">✅ 동기 방식(Synchronous)</h2>
<ul>
<li>요청과 응답이 동시에 일어난다.</li>
<li>추구하는 목적이 동일하다.</li>
<li>하나의 작업이 끝나야 다음 작업을 수행할 수 있다.</li>
<li>노드간의 작업 처리 단위(트랜잭션)을 동시에 맞춘다.<blockquote>
<p>ex) 은행. 계좌이체를 할 때 A계좌에서 돈이 빠져나가고 B계좌로 입금되는 작업이 동시에 이루어져야 한다.</p>
</blockquote>
</li>
</ul>
<h3 id="✔-동기-방식의-장단점">✔ 동기 방식의 장단점</h3>
<ul>
<li>장점: 설계가 간단하고 직관적이다.</li>
<li>단점: 작업이 끝날 때까지 다른 작업은 계속 대기해야 한다.</li>
</ul>
<h3 id="✔-예시">✔ 예시</h3>
<ul>
<li>사용자의 입력을 받는 함수 등 일반적으로 사용되는 함수는 대부분 동기식이다.</li>
</ul>
<h2 id="✅-비동기-방식asynchronous">✅ 비동기 방식(Asynchronous)</h2>
<ul>
<li>요청에 대한 응답이 동시에 일어나지 않는다.</li>
<li>추구하는 목적이 다를 수 있다.</li>
<li>요청 결과와 상관 없이 다음 작업을 수행할 수 있다.</li>
<li>노드간의 작업 처리 단위(트랜잭션)을 동시에 맞추지 않아도 된다.<blockquote>
<p>ex) 페이지 접속 시 특정 데이터에 대한 응답이 없더라도 나머지 데이터는 그에 영향을 받지 않는다. 이미지 하나를 불러오지 못한다고 페이지 자체가 열리지 않는 상황을 방지한다.</p>
</blockquote>
</li>
</ul>
<h3 id="✔-비동기-방식의-장단점">✔ 비동기 방식의 장단점</h3>
<ul>
<li>단점<ul>
<li>동기 방식보다 복잡하다.</li>
</ul>
</li>
<li>장점<ul>
<li>작업의 병렬처리가 가능해서 효율적이다.</li>
</ul>
</li>
</ul>
<h3 id="✔-예시-1">✔ 예시</h3>
<ol>
<li>Ajax</li>
</ol>
<ul>
<li>JS 라이브러리 중 하나.</li>
<li>비동기식으로 서버에 데이터를 요청해 전체 페이지를 새로고침하지 않아도 필요한 부분만 가져올 수 있다.</li>
<li>화면의 전환 없이 요청과 응답이 이뤄진다.</li>
<li>페이지 이동이 없는 통신으로 인해 보안상의 문제가 생길 수 있다.</li>
<li>연속으로 데이터 요청 시 서버 부하가 발생할 수 있다.</li>
<li>다른 도메인과는 통신이 불가능하다(Cross-Domain문제).</li>
</ul>
<ol start="2">
<li>setTimeout()</li>
</ol>
<ul>
<li><p>지정한 시간만큼 기다렸다가 실행하는 JS 함수.</p>
</li>
<li><p>아래 코드의 출력 순서는 <code>2, 1</code>이 아닌 <code>1, 2</code>이 된다.</p>
<pre><code class="language-javascript">setTimeout(boo, 3000);

function boo(){
   console.log(&quot;2&quot;);
}
console.log(&quot;1&quot;);

&gt;&gt;&gt; 1
&gt;&gt;&gt; 2</code></pre>
</li>
</ul>
<h3 id="✔-콜백callback-함수">✔ 콜백(callback) 함수?</h3>
<ul>
<li>비동기 방식에서 어떤 작업이 완료됐을 때 수행되는 함수다.</li>
<li>비동기 방식에서는 함수를 호출한 쪽에서 직접 수행결과를 처리하지 않고 콜백 함수를 통해 처리한다(위 코드에서 <code>boo()</code>함수가 <code>setTimeout()</code>함수의 콜백(callback)함수다).</li>
<li>단, 콜백 지옥에 빠지지 않게 주의해야한다.</li>
</ul>
<hr>
<p>[참고 사이트]</p>
<p><a href="https://velog.io/@hyeonyohwan/%EB%8F%99%EA%B8%B0-synchronous-vs-%EB%B9%84%EB%8F%99%EA%B8%B0-asynchronous">동기 ( synchronous ) vs 비동기 ( asynchronous )</a>
<a href="https://sudo-minz.tistory.com/21">비동기 통신과 AJAX</a></p>
]]></description>
        </item>
    </channel>
</rss>