<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>primi-cream.log</title>
        <link>https://velog.io/</link>
        <description>.</description>
        <lastBuildDate>Mon, 05 Jun 2023 00:43:30 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>primi-cream.log</title>
            <url>https://velog.velcdn.com/images/primi-cream/profile/c88abfa6-7f3a-49e0-8eb6-e3a19e5fea34/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. primi-cream.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/primi-cream" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TIL] 개발일지 8일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-8%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-8%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 05 Jun 2023 00:43:30 GMT</pubDate>
            <description><![CDATA[<h2 id="schema">Schema</h2>
<blockquote>
<p>Schema는 자료의 구조를 의미한다. 그리고 그러한 구조는 Validation을 의미하기도 한다.</p>
</blockquote>
<pre><code class="language-javascript">//Video Model
import mongoose from &quot;mongoose&quot;

//Video Data 구조.
const videoSchema = new mongoose.Schema({
    title: { type: String, required: true, trim: true, maxLength: 80 },
    description: { type:String, required: true, trim: true, minLength: 20 },
    createdAt: { type:Date, required: true, default: Date.now }, 
    hashtags: [{type: String, trim: true}],
    meta: {
        views: { type: Number, default: 0, required: true },
        rating: { type: Number, default: 0, required: true },
    },
});

//Middlware - hashtag의 #처리를 위한 formatHashtags 정의.
videoSchema.static(&quot;formatHashtags&quot;, function (hashtags){
    return hashtags
    .split(&quot;,&quot;)
    .map((word) =&gt; (word.startsWith(&quot;#&quot;) ? word : `#${word}`)); // startsWith는 word의 제일 앞부분을 본다.
});


const Video = mongoose.model(&quot;Video&quot;, videoSchema);

export default Video;</code></pre>
<h2 id="video-crud">Video CRUD</h2>
<pre><code class="language-javascript">//Read
export const watch = async (req, res) =&gt; {
    const {id} = req.params;
    // id로 해당하는 data를 DB에서 찾는다.
    // Mongoose 지원 Model.findById
    const video = await Video.findById(id); 
    if(!video){ 
        return res.render(&quot;watch&quot;,{pageTitle: video.title, video});
    }
    return res.render(&quot;watch&quot;, {pageTitle: video.title, video});
};

//Update
//getEdit은 read와 다를바가없어 생략 
export const postEdit = async (req, res) =&gt; {
    const {id} = req.params;
    const {title, description, hashtags} = req.body;
      // true, false로 해당 결과값 가져오기.(DB내에 값이 존재하면 필터된 값을 반환 없다면 null 반환)
    const video = await Video.exists({_id: id}); 
    if(!video){
        return res.render(&quot;404&quot;,{pageTitle: &quot;Video not found.&quot;});
    }
    // 해당 id를 찾아 내용(title, description, hashtags) Update
    // Mongoose 지원 Model.findByIdAndUpdate
    await Video.findByIdAndUpdate(id, {
        title,
        description,
        hashtags: Video.formatHashtags(hashtags), 
    });
    return res.redirect(`/videos/${id}`);
};

//Create
export const postUpload = async (req, res) =&gt; {
    const {title, description, hashtags} = req.body;
    try{
        console.log(title);
        //Mongoose 지원 Model.create
        await Video.create({
            title,
            description,
            hashtags: Video.formatHashtags(hashtags),
        });        
        return res.redirect(&quot;/&quot;);
    }catch(error){
        return res.render(&quot;upload&quot;,{
            pageTitle:&quot;Upload Video&quot;,
            errorMessage: error._message,
        });
    }
}

//Delete
export const deleteVideo = async (req, res) =&gt; {
    const {id} = req.params;
    //Mongoose 지원 Model.findByIdAndDelete
    await Video.findByIdAndDelete(id);
    return res.redirect(&quot;/&quot;);
}
</code></pre>
<blockquote>
<p>Javascript의 특성상 시간이 걸리는(DB조회 및 불러오기) 경우 그 뒤의 내용부터 실행되기 때문에 DB내용이 필요한 경우 async와 await를 사용.</p>
</blockquote>
<h2 id="search">Search</h2>
<pre><code class="language-javascript">export const search = async (req, res) =&gt;{
    const {keyword} = req.query;
    let videos = [];
    if(keyword){
        videos = await Video.find({
            title: {
                // mongoDB 지원 Regular Expression
                // i 는 대소문자 구분을 없애준다.(Welcome welcome)
                $regex: new RegExp(keyword, &quot;i&quot;),
            }
        });
    }
    return res.render(&quot;search&quot;, {pageTitle: &quot;Search&quot;, videos});
}</code></pre>
<h2 id="mongodb-with-docker">mongoDB (with. Docker)</h2>
<div>
  <p>DB Connection</p>  
<img src="https://velog.velcdn.com/images/primi-cream/post/534ed949-84a9-45c4-982e-31f74dc04882/image.png">
</div>
<div>
  <p>DB 전환 및 Collection(videos)으로 내용 검색</p>  
<img src="https://velog.velcdn.com/images/primi-cream/post/64506945-5c10-4086-9b78-341a3210a2c2/image.png">
</div>

]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개발일지 7일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-7%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-7%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sat, 03 Jun 2023 14:29:24 GMT</pubDate>
            <description><![CDATA[<h1 id="docker-compose">Docker-Compose</h1>
<p>이제부터 mongodb를 사용하게 되는데 이 또한 컨테이너로 독립된 구성을 만들어 
--network를 통해 컨테이너간 통신을 진행하여 개발하려 했다. 그러나, 이미 비대해진 명령어를 두번씩(각 컨테이너별로) 실행시키는 것도 불편할 뿐만 아니라 변경점이 있을 때 명령어에서 수정이 이뤄지면서 생기는 오타나 에러 등으로 생기는 시간소모가 아쉬워 여러개의 컨테이너를 파일로 관리할 수 있는 <code>Docker-Compose</code>를 채용하게 되었다.</p>
<pre><code class="language-javascript">//현재 두개의 컨테이너(DB,Node) 사용중
//docker-compose.yml file
services:
  mongodb:
    image: &quot;mongo&quot;
    volumes:
      - data:/data/db
    environment:
      - TZ=Asia/Seoul
  wetube-practice:
    build: ./
    ports:
      - &quot;4000:8000&quot;
    volumes:
      - ./:/app
      - /app/node_modules
    environment:
      - TZ=Asia/Seoul  
    depends_on:
      - mongodb
volumes:
  data:</code></pre>
<hr>

<p>해당 파일이 존재하는 곳에서 <code>docker-compose on</code>으로 실행
<img src="https://velog.velcdn.com/images/primi-cream/post/59402d37-fb3b-4d4a-b99f-81b257449889/image.png" alt="">
제대로 실행되는 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/primi-cream/post/927684de-221f-4494-826c-60abb2744e22/image.png" alt=""></p>
<h3 id="extra">Extra</h3>
<blockquote>
<p>process.env.PORT로 Dockerfile의 환경변수로 PORT 대체.
ex) app.listen(process.env.PORT)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개발일지 6일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-6%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-6%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 02 Jun 2023 11:39:50 GMT</pubDate>
            <description><![CDATA[<h2 id="url-path">URL Path</h2>
<blockquote>
<p>URL은 Absolute와 Relative가 있다.</p>
</blockquote>
<p>videoRouter.js</p>
<pre><code class="language-javascript">videoRouter.get(&quot;/:id(\\d+)/edit&quot;,edit);</code></pre>
<p>watch.pug</p>
<pre><code class="language-javascript">extends base

block contents 
   h1=video.title
   h4 #{video.views} #{video.views === 0 ? &quot;view&quot; : &quot;views&quot;}
   // 아래는 상대경로로 watch.pug 파일이 실행될때 기본적인 경로는 
   // videos/ 가 붙으므로 videos/2/edit과 같은 형태가 된다.
   a(href=`${video.id}/edit`) Edit Video &amp;rarr; 
   // 아래는 절대경로의 예시다. (제일 앞에 / 를 붙여주면 절대경로가된다.)
   // a(href=`/videos/${video.id}/edit`) Edit Video &amp;rarr;    </code></pre>
<h2 id="get">Get</h2>
<p>edit.pug</p>
<pre><code class="language-javascript">extends base

block contents 
    h4 Change Title of video
    //Form을 이용한 Data 전달 방식.
    form(action=&quot;/save-changes&quot;) // Default GET
        input(placeholder=&quot;Video Title&quot;,name=&quot;title&quot;, value=`${video.title}`, required)
        input(value=&quot;Save&quot;, type=&quot;submit&quot;)</code></pre>
<p><code>URL : localhost:4000/save-changes?title=First+Video</code></p>
<p>URL을 보면 save-changes의 뒤에 <code>?title=First+Video(name:vlaue)</code>를 확인할 수 있는데, 이것을 <code>Query String</code>이라고 말하며 <strong>해당 페이지에서 가지고 있는 Parameter 값의 전달</strong>이라고 할수있다. 이것을 통해 서버에서 해당되는 값을 찾기 위한 것으로 Index가 가능한 수준의 Parameter로 서버에 전달하고 결과값을 받아오기 위한 것이다.<strong>(Read)</strong></p>
<blockquote>
<p>GET 예시) 만약 영화를 검색한다고 할 때, 우리는 <code>짱구는 못말려 : 태풍을 부르는 영광의 불고기로드</code> 와 같이 그 제목을 브라우저의 검색창에 입력할 것이다.
<code>?q=짱구는+못말려+태풍을+부르는..</code> 의 형태로 주소창을 확인될 것이며 브라우저는 해당 결과에 대해 띄워줄 것이다.   </p>
</blockquote>
<h2 id="post">POST</h2>
<p>위에서 살펴보았듯 GET은 Query String을 그대로 노출시킨다.
따라서 GET을 사용하는 것은 DB의 형태를 외부에서 관찰할 수 있다는 것을 의미한다. 그러나 POST의 경우 GET과 같이 주소로 노출시키지 않으며, 데이터를 주고받는 방식 또한 달라지게 되는데(Query String이 없으므로) request body를 통해 서버에서 수신하게 된다.(HTTP의 양식 중 body 부분) 따라서 데이터의 양이 GET보다 크다.<strong>(Create, Update, Delete)</strong></p>
<pre><code class="language-javascript">//edit.pug
 form(method=&quot;post&quot;) // Default action /videos/:id(\\d)/edit
        input(placeholder=&quot;Video Title&quot;,name=&quot;title&quot;, value=`${video.title}`, required)
        input(value=&quot;Save&quot;, type=&quot;submit&quot;)

//videoRouter.js
videoRouter.route(&quot;/:id(\\d+)/edit&quot;).get(getEdit).post(postEdit);

//videoController.js
export const getEdit = (req, res) =&gt; {
    const {id} = req.params;
    const video = videos[id-1];
    return res.render(&quot;edit&quot;,{pageTitle:`Editing: ${video.title}`,video});
}

export const postEdit = (req, res) =&gt; {
    const {id} = req.params;
    const {title} = req.body;
    videos[id-1].title = title; //실제 DB를 사용하지 않아 업데이트 해당 방식으로 진행
    return res.redirect(`/videos/${id}`);
}

//app.js
//express에서 지원하는 URL Incoding Data 구문분석 Middleware
//쉽게 말해 HTML에서 보내준 Form을 Javascript Object로 변경해준다.
app.use(express.urlencoded({extended:true})); // 이 후 req.body가 존재.
app.use(&quot;/&quot;, globalRouter);
app.use(&quot;/users&quot;, usersRouter);
app.use(&quot;/videos&quot;, videosRouter);</code></pre>
<hr>

<p><img src="https://velog.velcdn.com/images/primi-cream/post/371d1ded-33c9-4213-855a-e6c51d09457b/image.png" alt=""></p>
<blockquote>
<p>videoRouter.js를 보면 한페이지에서 GET, POST 두가지 상태가 존재함을 알 수 있는데 간단히 생각하면 처음 유저가 요청한 페이지는 GET, 변경사항을 Save 버튼을 눌러 서버에 보내는 것을 POST로 정리할 수 있다.</p>
</blockquote>
<hr>


<blockquote>
<p>PS. terminal에서 폴더조정을 위해 작업하다 rm -rf로 Working Directory를 날려버렸다...하필 Push를 초기에만 한 탓에 git 내용까지 날아가 처음부터 다시 해야했으므로 rm 명령어는 조심하자...동시에 push는 항상 해두자...(with Conflict) </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개발일지 5일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-5%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-5%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 31 May 2023 13:39:22 GMT</pubDate>
            <description><![CDATA[<h2 id="inheritence--partials--block">Inheritence &amp; Partials &amp; Block</h2>
<hr>

<p>PUG가 적용되었지만, 페이지마다 HTML구조를 반복하는 해야한다는 점은 크게 다를바가 없는데 이러한 부분을 해소할 수 있는 방법이 존재한다. </p>
<h3 id="partials">Partials</h3>
<p>페이지마다 고정적으로 반복되는 부분은 Partials로 나누어 적용한다.</p>
<p>home.pug</p>
<pre><code class="language-javascript">    doctype html
    html(lang=&quot;ko&quot;)
    head
        title Home | Wetube
    body 
        Welcome Home! 
    include partials/footer.pug</code></pre>
<p>partials/footer.pug</p>
<pre><code class="language-javascript">    footer &amp;Copy; #{new Date().getFullYear()} Wetube    </code></pre>
<h3 id="inheritence">Inheritence</h3>
<p>전체적인 구조의 기본틀을 공유할 때, Inheritence를 이용한다.</p>
<p>base.pug</p>
<pre><code class="language-javascript">    doctype html
    html(lang=&quot;ko&quot;)
    head
        title Home | Wetube
    body 
        Welcome Home! 
    include partials/footer.pug</code></pre>
<p>home.pug</p>
<pre><code class="language-javascript">    extends base.pug</code></pre>
<h3 id="block">Block</h3>
<p>Contents의 확장을 위해 Block을 사용한다.</p>
<p>base.pug</p>
<pre><code class="language-javascript">    doctype html
    html(lang=&quot;ko&quot;)
    head
        title Wetube
    body 
        block content
    include partials/footer.pug</code></pre>
<p>home.pug</p>
<pre><code class="language-javascript">    extends base.pug

    block content 
        h1 Home!</code></pre>
<h2 id="templates에-값-전달하기">Templates에 값 전달하기</h2>
<p>기존 home의 controller</p>
<pre><code class="language-javascript">export const home = (req, res) =&gt;{
    return res.render(&quot;home&quot;); // home.pug를 렌더링 해주고있다.
};</code></pre>
<p>값의 전달</p>
<pre><code class="language-javascript">export const home = (req, res) =&gt;{
    return res.render(&quot;home&quot;,{pageTitle: &quot;Home&quot;}); // pageTitle이라는 변수명으로 Home String 전달.
};</code></pre>
<p>base.pug</p>
<pre><code class="language-javascript">    doctype html
html(lang=&quot;ko&quot;)
    head
        title #{pageTitle} | Wetube // #{}은 Javascript 문법 pageTitle이라는 변수를 받고있다.
    body 
        block content
    include partials/footer.pug
</code></pre>
<h2 id="mixin">Mixin</h2>
<p>Data를 받을 수 있는 Partials.
home.pug</p>
<pre><code class="language-javascript">extends base.pug
include mixins/video // include 가 필요하다.

block content 
    h1 Home!

    each video in videos 
        +video(video) // +FileName의 형태로 사용할 수 있다. </code></pre>
<p>video.pug</p>
<pre><code class="language-javascript">mixin video(info)
    div 
        h4=info.title 
        ul 
            li #{info.rating}/5.
            li #{info.comments} comments,
            li Posted #{info.createAt},
            li #{info.views} views.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개발일지 4일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-4%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-4%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 30 May 2023 04:41:17 GMT</pubDate>
            <description><![CDATA[<h1 id="router">Router</h1>
<hr>

<blockquote>
</blockquote>
<hr>
웹에서의 <b>라우터</b>는 3가지로 정의된다고 한다.
<ol>
  <li>네트워크 계층에서는 데이터 패킷 방향을 결정하는 네트워크 장치</li>
  <ul><li>목적지까지 안전하고 빠르게 데이터를 보내는 것</li></ul>
  <br>
  <li> 애플리케이션 계층의 Single-page Application의 경우 주어진 URL로 표시되는 웹 페이지를 결정하는 라이브러리.</li>
<ul><li>SPA는 URL을 통해 각기 다른 페이지를 보여주는데 Fragment Identifier(hash)를 통해 문서의 부차적 자원에 참조하므로 문서 내부에서 일어나는 일이기 때문에 화면의 갱신이 일어나지 않는다. Framework가 이러한 방식으로 Routing 한다고 표현하는 듯</li></ul><br>
<li>서비스 계층의 API 구현에서 라우터는 요청을 구문 분석하고 요청을 프로그램 내의 다양한 핸들러로 보내거나 라우팅하는 소프트웨어 구성요소.</li>
    <ul><li>응용 프로그램이 URL(or Path) 및 특정 HTTP 요청 Method인 특정 EndPoint에 대한 클라이언트의 요청에 응답하는 방법을 결정하는 것을 의미하는듯 하다.</li><ul>  
</ol>  <hr>

<p>위의 3가지 정의 중 지금부터 사용 될 Router는 <code>Express</code>에서 사용되는 의미인 3번에 해당되는 것을 알 수 있으며, <strong>이는 사용자가 요청하는 URL(or Path)에 최적으로 도달하기 위한 분배(그룹화)</strong>로 이해할 수 있다.</p>
<h2 id="적용하기">적용하기</h2>
<p>언급한대로 결국 라우터는 URL에 대한 분배(그룹화)이며, 이를 용이하게 하기 위한 중개소같은 것으로 사용된다.
사용해야 할 Path가 많아질수록 가독성 등의 이유로 관리하기 쉽도록 파일을 나누어 적용했다.      </p>
<p>globalRouter.js</p>
<pre><code class="language-javascript">//독립된 파일에 정리하여 나누었기 때문에 express를 import 해주어야한다.
import express from &quot;express&quot;

const globalRouter = express.Router();

const homeController = (req, res) =&gt;{
    return res.send(&quot;&lt;h1&gt;Welcome!!&lt;/h1&gt;&quot;);
}

const homeEditController = (req, res) =&gt; {
    return res.send(&quot;&lt;h1&gt;Edit page&lt;/h1&gt;&quot;);
}

globalRouter.get(&quot;/&quot;,homeController);
globalRouter.get(&quot;/edit&quot;,homeEditController);

//listen하고 있는 app.js에서 사용하기 위해 globalRouter를 디폴트로 내보내고있다.
//디폴트의 경우 globalRouter를 명시했기 때문에 외부사용 시 별도로 지정해주지 않아도 되지만 파일에서 한개만 지정가능하다.
export default globalRouter;</code></pre>
<p>app.js</p>
<pre><code class="language-javascript">import express from &quot;express&quot;
import globalRouter from &quot;./routers/globalRouter&quot;;

const app = express();
const PORT = 8000;

const middleware = (req, res, next) =&gt; {
    console.log(&quot;Middleware!!!!&quot;);
    next();
}

app.use(middleware);
app.use(&quot;/&quot;,globalRouter)


app.listen(PORT,() =&gt; {console.log(`Start Server http://localhost:${PORT}`)});</code></pre>
<h2 id="controller">Controller</h2>
<p>Router와 같은 이유로 Controller 또한 정리. </p>
<p>app.js</p>
<pre><code class="language-javascript">import express from &quot;express&quot;
import globalRouter from &quot;./routers/globalRouter&quot;;
import userRouter from &quot;./routers/userRouter&quot;;
import videoRouter from &quot;./routers/videoRouter&quot;;
import testRouter from &quot;./routers/testRouter&quot;;

const app = express();
const PORT = 8000;

const middleware = (req, res, next) =&gt; {
    console.log(&quot;Middleware!!!!&quot;);
    next();
}

app.use(middleware);
app.use(&quot;/&quot;,globalRouter);
app.use(&quot;/user&quot;,userRouter);
app.use(&quot;/video&quot;,videoRouter);
app.use(&quot;/test&quot;,testRouter);

app.listen(PORT,() =&gt; {console.log(`Start Server http://localhost:${PORT}`)}); </code></pre>
<p>testRouter</p>
<pre><code class="language-javascript">import express from &quot;express&quot;
import {test,pi} from &quot;../controllers/testController&quot;

const testRouter = express.Router();

testRouter.get(&quot;/&quot;,test);
testRouter.get(&quot;/pi&quot;,pi);

export default testRouter;</code></pre>
<p>testController</p>
<pre><code class="language-javascript">export const test = (req, res) =&gt; res.send(&quot;&lt;h1&gt;Test Page&lt;/h1&gt;&quot;);
export const pi = (req, res) =&gt; res.send(&quot;&lt;h1&gt;PI!&lt;/h1&gt;&quot;);</code></pre>
<blockquote>
<p>현 구조를 정리해보면 app.js → router →  countroller 순으로 참조하고 있으며 서버 → 분배 → 페이지(함수) 순임을 알수있다.</p>
</blockquote>
<h2 id="regular-expression--parameters">Regular Expression &amp; Parameters</h2>
<p>testRouter.js</p>
<pre><code class="language-javascript">import express from &quot;express&quot;
import {test,pi} from &quot;../controllers/testController&quot;

const testRouter = express.Router();

//:id는 인자값 전달을 위한 변수명처럼 생각하면 되며 \\d+에서 d는 숫자를, +는 갯수의 관계없이 적용해준다.(ex. /12, /22223, /9999999)
testRouter.get(&quot;/:id(\\d+)&quot;,test);
//:fufu는 명명이 자유로움을 보여준다. pi문자 뒤에 w는 문자를 +는 갯수.(ex. /piajsdhaj, /piaaaaa, /pidks) 
testRouter.get(&quot;/:fufu(pi\\w+)/pi&quot;,pi);

export default testRouter;
</code></pre>
<p>testController.js   </p>
<pre><code class="language-javascript">export const test = (req, res) =&gt; res.send(`&lt;h1&gt;Test Page#${req.params.id}&lt;/h1&gt;`);
export const pi = (req, res) =&gt; res.send(`&lt;h1&gt;PI! #${req.params.fufu}&lt;/h1&gt;`);</code></pre>
<h2 id="pugview-engine">PUG(View Engine)</h2>
<blockquote>
<p>지금까지 HTML을 출력하는 방식은 String으로 모든 DOM 구조를 입력하고 페이지마다 수정해야 한다는 점에서 관리뿐만 아니라 개발함에도 너무 큰 리소스를 낭비하는 구조이다. 따라서 이러한 부분을 해결해줄 PUG라는 View Engine으로 대체함으로 반복적인 소스와 가독성에서의 개선을 기대해볼 수 있다.   </p>
</blockquote>
<pre><code class="language-javascript">//pug를 install하고 Express에게 View Engine이 무엇인지 명시해 주어야한다.(app.js에 추가)
app.set(&quot;view engine&quot;, &quot;pug&quot;);
app.set(&quot;views&quot;, process.cwd() + &quot;/src/views&quot;); // default path값 변경</code></pre>
<blockquote>
<p>Express는 기본적으로 process.cwd() + <code>/views</code>를 기준으로 view파일을 찾는다. 따라서 ./views 폴더에 PUG 파일이 들어가야한다. 단 ./ 의 기준은 package.jon의 위치이므로(서버구동은 package.json의 script를 실행하는 것이므로) 실제 실행해야할 js 파일에 디폴트값을 맞출필요가있다.</p>
</blockquote>
<blockquote>
<p>VSCode의 Prettier Extension을 사용하고 있을 경우 PUG는 별도의 Plug-In을 추가해주어야한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개발일지 3일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-3%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-3%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sat, 27 May 2023 13:15:31 GMT</pubDate>
            <description><![CDATA[<h1 id="docker-적용하기">Docker 적용하기</h1>
<h2 id="dockerizing">Dockerizing</h2>
<p>시작하기에 앞서 당일 공부하여 적용하려 했기 때문에 많은 우여곡절이 있었다.
따라서 기본적인 Docker의 사용에서도 많은 실수를 했으며, 해당 내용을 어느정도 포함할 예정이다.</p>
<h3 id="dockerfile-create">Dockerfile Create</h3>
<pre><code>FROM node:18`// 사용할 Node Version인데 사용하고 있는 babel 적용을 위해 14이상의 버전필요

WORKDIR /wetube-practice // Working directory 

COPY package.json . // [상대경로,절대경로 둘다가능] package 목록 COPY

RUN npm install // 사용중인 package를 설치한다.(Container에 node_modules 생성)

COPY . . // 현 Working space의 모든 내용 COPY

EXPOSE 4000 // Docker 내부 port 개방

CMD [&quot;node&quot;,&quot;app.js&quot;] // node 실행 command line</code></pre><p>아래에서 모든 내용을 복사하는데 <code>package.json</code>을 먼저 복사한 이유는 단순하다.
먼저 Docker image는 스냅샷이라는 점과 그러한 image를 빌드할때 중요한 것은 Dockerfile은 layer 방식으로 실행된다는 점으로 변경된 부분만 감지(Dockerfile을 기준으로 각라인으로 이해하면 될듯)하여 리빌드한다는 것인데 만약 모든 파일을 복사하고 npm install을 한다면 package의 변화가 없어도 매번 install을 하는 상황이 발생할 것이다. 따라서 이러한 리소스의 낭비를 막기위해 위와 같은 형태가 되었다.   </p>
<p>해당 workspace에서(Dockerfile이 있는) <code>docker build .</code> 명령어
<img src="https://velog.velcdn.com/images/primi-cream/post/c0daa535-588d-4133-8749-4c3c2e2e1679/image.png" alt="">
에러의 이유는 docker desktop이 실행 중이 아니어서.</p>
<p>다시 실행하면 dockerfile에서 image를 빌드한다. 해당 image의 id를 확인할수있다.(windows 기준)
<img src="https://velog.velcdn.com/images/primi-cream/post/60b1aefb-79ba-4d5f-a84a-e279dfd539ff/image.png" alt=""></p>
<p>또다시 에러 발생.
<img src="https://velog.velcdn.com/images/primi-cream/post/e13c3075-fcf6-495b-aa23-8eab05d7389d/image.png" alt="">
파일의 Local Path가 src/app.js 이기때문에 못찾아서 생긴 오류.</p>
<p>바꾼후 빌드를 다시 해줬으므로 Image 또한 다시 생성 바뀐 ID값으로 실행했는데 (<code>run -p system-port:container-prot</code>)</p>
<p><img src="https://velog.velcdn.com/images/primi-cream/post/cd155f85-4ff1-437a-90eb-e3dba4e08bf3/image.png" alt="">
내용을 보니 확장자를 mjs로 바꾸거나 type:module을 설정하라고 한다. 확장자를 변경.</p>
<p><img src="https://velog.velcdn.com/images/primi-cream/post/ca5bfdcf-c4b0-44fe-a30d-77eddf2e258c/image.png" alt="">
실행 확인했다.</p>
<p><img src="https://velog.velcdn.com/images/primi-cream/post/7e505e49-a369-471a-a3fd-f01962bbd0be/image.png" alt="">
실행했던 Docker를 종료.</p>
<h2 id="nodemon-적용하기">Nodemon 적용하기</h2>
<p>단순한 Docker 환경은 마련되었지만 매번 바뀔때마다 image를 재생성하고 빌드하며, run을 해야하는 상황으로 이 상태에서는 매우 번거로울 수밖에없다.</p>
<p>따라서 해당 부분을 해결하기 위해 몇가지를 변경하려고 하는데, 먼저 <code>Nodemon</code>이 적용되어 있는 상태이므로 <code>NodeJS</code>는 신경쓸 필요가 없는 상황이지만 실질적으로 위와 같은 문제때문에 의미가 없는 상태이다. 따라서 적용될 수 있도록 부차적인 작업을 해줘야한다.</p>
<p>현재 시스템의 상황은 Docker Desktop이 WSL2를 기준으로 되어있는데 예상되는 문제는 VSCode에서 사용하고 있는 파일들은 윈도우 시스템으므로 두가지를 생각해볼수있다.</p>
<li>구조상 윈도우 - WSL2 - Docker를 거치기 때문에 먼저 윈도우와 WSL2를 mount 해줘야한다.</li>
<li>WSL2와 Docker가 접근가능한 권한문제</li>

<p>나는 파티션을 별도로 나누어 쓰지 않았으므로 디바이스의 상황은 아래와 같다.
<img src="https://velog.velcdn.com/images/primi-cream/post/c0544eb6-2372-4b81-bd32-9965233a7c02/image.png" alt=""></p>
<p>이러한 상태에서 wsl로 mount를 시도한다면 
<img src="https://velog.velcdn.com/images/primi-cream/post/836dcd37-aa20-4938-ac4a-e8931d51bf0b/image.png" alt=""></p>
<p>wsl을 통해 Docker를 사용중이었으므로 해당 Device는 mount 하고있을거라 판단.
Docker의 <code>Bind Mount</code>를 사용하여 변경사항을 바로 적용할 예정이다.
간단하게 <code>Bind Mount</code>에 대해 이야기한다면, 호스트 머신에 매핑 될 컨테이너의 경로를 설정하는 것이다.
즉 컨테이너가 컴퓨터에 저장된 해당 내용을 참조하여 즉각 적용된다고 생각하면 될 듯 하다.
<code>Volume</code>은 그 위치를 알 수 없기 때문에 (Docker가 알아서 관리한다.) <code>Bind Mount</code>를 사용할 수밖에 없는 상황.</p>
<h3 id="그렇다면-어디서-어떻게-적용하는가">그렇다면 어디서 어떻게 적용하는가?</h3>
<p>먼저 Docker의 Image는 스냅샷이라고 했다. (그리고 도커가 호스트를 직접 수정하는 일은 없다.)
이후의 변경점에 대해 논하는 것이므로 <code>Dockerfile</code>을 기반으로 빌드하는 시점까지는 동일한 프로세스일 것이며 그렇다면 빌드까지는 그대로 적용하고 <code>run</code>에서 제어하면 될듯하다.</p>
<pre><code>WORKDIR /app 

EXPOSE 8000 

CMD [&quot;node&quot;,&quot;dev&quot;] </code></pre><p>CMD에 Script를 적용했다.</p>
<p>아래는 발생한 Error들이다.</p>
<p><img src="https://velog.velcdn.com/images/primi-cream/post/4e1a92e3-b2c3-4411-9a6b-5507f311144c/image.png" alt=""></p>
<p><a href="https://learn.microsoft.com/en-us/windows/wsl/filesystems">https://learn.microsoft.com/en-us/windows/wsl/filesystems</a> 에서 wsl로 접근하는 방식을 확인.
<img src="https://velog.velcdn.com/images/primi-cream/post/20ed810d-c47c-4f71-a1b3-d7f0f7e1270d/image.png" alt="">
해결되었음을 확인할 수 있었다.
여기서 NodeJS에 대한 매우 기본적인 실수를 했는데 아래와 같다.</p>
<pre><code>WORKDIR /wetube-practice      // 잘못된 패스설정
CMD [&quot;npm&quot;,&quot;run&quot;,&quot;dev&quot;]     // 언급한 scpript 실행부</code></pre><p>따라서 당연히(WORKDIR 문제로) Path상에서 못찾았던 것.
<img src="https://velog.velcdn.com/images/primi-cream/post/65885ae7-5ce0-46c9-891a-e15d8aa3aca0/image.png" alt="">
이미지를 다시 빌드 후 필요없던 -v wetube:/app/wetube 와 같은 볼륨제거 후 실행하니 제대로 동작하는 것을 확인.
(단, 내가 열어둔 내부 포트는 8000이므로 저부분은 소스내에서 변경이 필요.)
이로써 Docker 환경에서 기본적인 적용이 가능해졌음을 알 수 있었다.</p>
<p>접근권한의 문제는 System에 따라 나누어질 수 있는데, 보편적으로 <code>Docker Desktop</code>의 <code>Setting</code>- <code>Resources</code>-<code>File Sharing</code>에서 설정하는 것 같으나, 나의 경우 <code>File Sharing</code>이 없었으며 WSl로 window에서 직접 관리된다는 메세지를 확인할 수 있었다. 이 부분에서 문제없이 진행 된 것으로 보아서 초기 Docker를 설치하면서 WSL2에 관한 세팅에서 이미 권한 설정이 되었던 것으로 생각된다.</p>
<p>문제가 추가로 발생하여 해결중...<br>실시간으로 적용되지 않고있음.
위에서 경로의 오타를 발견했지만 수정해도 변함없는 것으로 보아 WSL에서 Docker로 전달이 되지 않고 있음을 확인.
<a href="https://www.docker.com/blog/docker-desktop-wsl-2-best-practices/">https://www.docker.com/blog/docker-desktop-wsl-2-best-practices/</a>
에서 추천하는바로 VSCode의 WSL Extension을 이용한 방법을 사용하기로 변경.
<a href="https://docs.docker.com/desktop/windows/wsl/">https://docs.docker.com/desktop/windows/wsl/</a> 에서 마운트의 방식에 대해 /mnt 를 지양.
<a href="https://devblogs.microsoft.com/commandline/access-linux-filesystems-in-windows-and-wsl-2/">https://devblogs.microsoft.com/commandline/access-linux-filesystems-in-windows-and-wsl-2/</a> 단일 파티션에 대해서는 지원되지 않는 상황으로 현재 파티션이 1개 이기때문에 윈도우즈 파일의 마운트는 현재로썬 진행불가.(파티션을 나눌수도 없는 상황.)
결국 위에서 나온바와 같이 WSL의 내부에서 작업하는 방식과 nodemon -L 옵션조차 되지 않는 상황으로 보아 WSL와 Docker 사이에서 제대로 전달이 안되는 듯하다.
<a href="https://stackoverflow.com/questions/39239686/nodemon-doesnt-restart-in-windows-docker-environment?rq=3">https://stackoverflow.com/questions/39239686/nodemon-doesnt-restart-in-windows-docker-environment?rq=3</a> 확인. <strong>2022년까지도 해결되지 않은 문제로 윈도우용 Docker의 문제로 일단락.</strong> 해당 세팅은 맥북을 기준으로 하고 윈도우는 배포된 것을 테스트하는 용으로 당분간 운용해야겠다.</p>
<h2 id="extra">Extra</h2>
<p>Docker 또한 Repository를 제공한다.(무료는 하나뿐이지만...)
따라서 pull, push가 가능한데 가입하여 VSCode내에서도 login완료.
현재 DockerHub의 repository 이름과 image의 이름은 같지만, 태그가 추가되어있어 동일하지 않은 상황.
다시 빌드하긴 싫으므로 <code>docker tag wetube-practice:dev primicream/wetube-practice</code>로 이미지를 복제하여 push하는 방법을 선택했다.(만약 동일하지 않은 상태에서 push한다면 <code>denied: requested access to the resource is denied</code> 메시지를 확인할 수 있을것이다.)
<img src="https://velog.velcdn.com/images/primi-cream/post/8e22dfe0-0e8e-4d9b-9919-e8946b7e0c35/image.png" alt="">
push가 잘이루어졌음을 확인.
이제 본격적으로 wetube 내용을 진행하게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개발일지 2일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-2%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-2%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 26 May 2023 17:18:21 GMT</pubDate>
            <description><![CDATA[<h3 id="nodejs">NodeJS</h3>
<p>Docker를 진행하기 전에 NodeJS의 테스트와 구조부터 만들었다.
<code>npm init</code>
<img src=https://velog.velcdn.com/images/primi-cream/post/5230e6de-a127-4211-908f-09261a637dcd/image.png width=500px height=500px>
<code>package.json</code> 구성
<img src=https://velog.velcdn.com/images/primi-cream/post/72dccbc1-492d-4024-954f-6443ca0e4d13/image.png width=500px height=500px></p>
<p>이게 바로 <code>NodeJS</code>이며 <code>package.json</code>을 보면 떠오르는 것은 <code>maven</code>, <code>gradle</code> 말그대로 패키지 버전 등을 관리해준다는 측면에서 사실상 비슷해보인다. 물론 <code>NodeJS</code>의 경우 V8엔진을 내장하고 있어 <code>Javascript</code>의 <strong>런타임환경을 브라우저 외부에서 제공해준다는 점의 차이</strong>가 있다.
즉, <code>NodeJS</code>란 런타임환경을 제공해주며 패키지에 대한 관리를 해주는 것으로 생각하면 될듯하다.
<code>node app.js</code>로 실행가능.</p>
<h3 id="crlfcrlf">CR/LF/CRLF</h3>
<p><code>git add</code> 시 <code>package.json</code>이 LF에서 CRLF로 git에 올라가는 복사본을 대체한다는 <strong>경고문</strong>을 확인.
해당 관련된 용어는 Carrige Return / Line Feed / CR + LF 로 <strong>개행의 형태</strong>를 말한다.
즉, 줄바꿈의 타입으로 생각하면 될 듯 하며 LF는 Linux, CRLF의 경우 Windows의 기본값이라고 한다.
둘의 <strong>바이트 코드가 다르기 때문에 다른 코드로 인식 할 수 있으므로 LF로 통일하여 사용하는 것을 권장</strong>한다고 한다.</p>
<h2 id="express">Express</h2>
<p>설치하기에 앞서 <code>Express</code>란 무엇일까?
<code>NodeJS Web Application Framworks</code> 라고 소개되어있는데 <code>NodeJS</code> 기반의 HTTP Handler, View의 randering, Middleware 처리 등을 지원해주는 것으로 생각된다.</p>
<h3 id="express-install">Express Install</h3>
<p>이제 express를 설치 해보자 <code>npm i express</code> 
<code>node_modules</code>와 <code>package-lock.json</code>이 생성 <code>node_modules</code>에는 <strong>npm으로 설치한 모든 패키지가 저장</strong>된다. (물론 express가 사용하는-혹은 의존하는-패키지들 또한 같이 설치된다.)</p>
<h4 id="dependencies">dependencies</h4>
<p><code>package.json</code>을 보면 <code>dependencies</code>에 express가 버전과 함께 기입된 것을 볼 수 있는데 만약 <code>node_modules</code>와 <code>package-lock.json</code>이 없는 상태라면 <code>npm i</code> 만으로도 재설치가 가능하다.
이러한 것을 보면 <code>dependencies</code>가 어떠한 역할을 하는지 알수있을것이다.(git으로 이러한 modules의 패키지들을 모두 올리지 않아도 된다는 말이기도함)
<strong>주의해야 할 점</strong>은 <code>package.json</code> 파일이 저장되지 않은 상태에서 <code>npm i</code>를 사용할 경우 충돌이 일어날 수 있다. <code>npm i</code>가 <code>dependencies</code>를 변경하려고 하기 때문에 기존의 변경된 부분이 저장되지 않은 상황에서 충돌이 일어나는것.</p>
<h4 id="dependencies-1">dependencies</h4>
<p>Dependencies는 프로젝트에 필요한 것이라면 devDependencies는 개발자를 위한 Dependencies이다.</p>
<h4 id="package-lockjson">package-lock.json</h4>
<p><code>package-lock.json</code>의 경우 <code>node_modules</code>의 트리나 <code>package.json</code> 파일을 수정하게 되면 자동으로 생성되는 파일인데, 파일이 생성되는 시점의 의존성 트리에 대한 정확한 정보를 가지고 있다고 한다.</p>
<h3 id="babel">babel</h3>
<p><code>babel</code>은 최신 자바스크립트 코드를 하위 버전으로 컴파일하여 호환되게 만들어준다.(nodeJS 버전에 따라 실행 가능한 Javascript 버전이 다르다.)
<strong>Babel Install</strong> 
<code>npm install --save-dev @babel/core</code></p>
<pre><code>babel.config.json생성 
{
  &quot;presets&quot;: [&quot;@babel/preset-env&quot;]
}</code></pre><p><code>npm install @babel/preset-env --save-dev</code></p>
<p>위의 presets 은 babel을 위한 엄청 거대한 플러그인이며 preset-env는 javascript 최신버전.</p>
<p>아래와 같이 babel을 실행할수있다. </p>
<pre><code>require(&quot;@babel/core&quot;).transform(&quot;code&quot;, {
  presets: [&quot;@babel/preset-env&quot;],
});</code></pre><p>번거러우니 script로 처리하기 위해 <code>babel-node</code>를 설치.
<code>npm install @babel/node --save-dev</code>
<code>babel-node app.js</code> 로 실행가능해졌다.</p>
<h3 id="nodemon">Nodemon</h3>
<p><code>nodemon</code>은 수정 시 자동으로 재시작해주는 기능이 있어
<code>npm i nodemon</code> 으로 설치
script 변경 <code>nodemon --exec babel-node app.js</code></p>
<h3 id="gitignore">gitignore</h3>
<p>마지막으로 위에서 언급한 것처럼 <code>node-modules</code>는 <code>npm i</code> 로 생성가능하므로 제외해줬다. </p>
<h2 id="start-server">Start Server</h2>
<pre><code>import express from &quot;express&quot;

const app = express();
const PORT = 4000;

const loggerHandler = () =&gt; {
    console.log(`Start Server http://localhost:${PORT}`);
}

app.listen(PORT,loggerHandler);</code></pre><p>이로서 서버까지 실행하는데 성공했다.
미들웨어로 Page Controll 까지 확인 후 Docker세팅을 시작해야겠다.</p>
<h3 id="http">HTTP</h3>
<p><strong>Hypertext Transfer Protocol</strong> 는 말그대로 Protocol로 비유한다면 <strong>HTML</strong>은 보고서와 같은 일종의 양식(Form)이며 HTTP은 그것을 주고 받는 방식인 것이다.</p>
<h4 id="그렇다면-그러한-http를-사용하는-주체는-무엇일까">그렇다면 그러한 HTTP를 사용하는 주체는 무엇일까?</h4>
<p>일반적으로 <strong>웹사이트의 서버</strong>와 클라이언트 즉, <strong>브라우저</strong>일 것이다.</p>
<h3 id="middleware">Middleware</h3>
<p>사용자가 웹사이트에 들어간다고 가정해보자.
<strong>google.com</strong>으로 접속할 때 브라우저는 사용자의 요청(request)을 받고 그것을 서버에 HTTP request 해준다.
서버는 브라우저에게 요청받고 응답(response)해주며 그 결과물을 브라우저가 출력한다.
그리고 <strong>서버와 브라우저의 요청과 응답 사이에 있는 존재가 Middleware</strong>이다.
조금 더 정확히는 <strong>마지막으로 반환하는 Controller를 제외한 존재</strong>라고 생각하면 될듯하다.
따라서 모든 Middleware는 Controller이지만 모든 Controller는 Middleware가 아닐 수 있다.
아래는 예시 코드이다.</p>
<pre><code>const homeController = (req, res) =&gt; {
    return res.send(`${req.method}${req.url}`);
};

const middleware = (req, res, next) =&gt; {
    const url = req.url;
    if(url === &quot;/protected&quot;){
        return res.send(&quot;&lt;h1&gt;Not Allowed&lt;/h1&gt;&quot;);
    }
    console.log(&quot;Allowed, you may continue.&quot;);
    next();
};

const protect = (req, res) =&gt; {
    return res.send(&quot;Welcome to the private lounge.&quot;);
}

app.use(middleware);
app.get(&quot;/&quot;,homeController);
app.get(&quot;/protected&quot;,protect); // /protected를 차단하고 있으므로 실행되지 않음</code></pre><p>Reference 
LF/CRLF - <a href="https://velog.io/@dev_yong/CRLF%EC%99%80-LF%EC%B0%A8%EC%9D%B4%EC%9D%98-%EC%9D%B4%ED%95%B4">https://velog.io/@dev_yong/CRLF%EC%99%80-LF%EC%B0%A8%EC%9D%B4%EC%9D%98-%EC%9D%B4%ED%95%B4</a>
Package-lock.json - <a href="https://hyunjun19.github.io/2018/03/23/package-lock-why-need/">https://hyunjun19.github.io/2018/03/23/package-lock-why-need/</a>
NomardCoder - <a href="https://nomadcoders.co/wetube">https://nomadcoders.co/wetube</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개발일지 1일차]]></title>
            <link>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@primi-cream/TIL-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%80-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 25 May 2023 10:33:27 GMT</pubDate>
            <description><![CDATA[<p>현재 NomadCoder의 유튜브클론 코딩을 윈도우에서 진행중이었는데</p>
<ul>
<li>NodeJS의 Package 보안문제 Issue</li>
<li>Package 버전 호환성 문제는 Git으로 해결하기 번거로움</li>
<li>노트북은 ARM 맥북인데 데스크탑은 윈도우</li>
</ul>
<p>위의 이유로 인해 Docker를 통해 독립적인 구성으로 개발하는 것이 훨씬 편하며 보안측면에도 관리하기 좋을 듯 했다.</p>
<p>물론 버츄얼 머신을 구성하는 것도 하나의 방법이겠으나, 나는 여러개의 독립된 구성을(연습을 위해) 필요로 하며 그만한 숫자의 버츄얼 머신을 생성하는 것은 리소스적인 낭비가 너무 심하여 이것이 도커를 사용하는 가장 큰 이유인 것 같다.</p>
<h3 id="그렇다면-도커는-어떻게-이러한-차이를-보일-수-있을까">그렇다면 도커는 어떻게 이러한 차이를 보일 수 있을까?</h3>
<p style="center"><img src="https://velog.velcdn.com/images/primi-cream/post/4ce11eb4-2c0b-4ea3-b785-558a8fac397d/image.png" height="400px" width="400px"></p>


<p>위의 이미지를 보면 알 수 있듯 먼저 Docker는 표준이 존재하며 그 위에 Application이 올라간다. 따라서 비대한 OS의 내용들이 중첩되지 않으며 만들어진 표준 위에서 Management 한다고 보는 것이 맞을 것이다. 현재 우리가 사용하는 OS의 용량과 거기에 속하는 많은 도구들이 하나가 늘어날때마다 그만큼의 차이가 생긴다는 것을 생각해본다면 당연한 결과일 것이다.(물론 각 App마다 부분적인 운영체제의 변경점이 존재할 수 있으나 상대적으로 매우 가벼운 수준.)</p>
<h3 id="install">Install</h3>
<p>Docker는 현재 Docker Desktop과 Docker Toolbox로 설치가 가능한데 Docker Desktop가 지원되지 않는 경우에만 Docker Toolbox를 설치하면 된다.
나의 경우엔 Windows Pro 버전으로 Hyper-v와 WSL2에 대한 설치 및 설정으로 기본적인 설치준비는 끝난 상태였으므로 설치하고 실행하는데 지장이 없었다. </p>
<h3 id="how-to-use">How to use</h3>
<p>Docker Desktop을 설치 후 직접 이미지를 생성하고 구성하는 방법도 있지만, 현 상황상 VSCode를 이용하여 개발하는 것이 편의성에서 훨씬 좋으므로 해당 방법을 택하였다.
VSCode의 Extentions에서 Docker를 인스톨하고 사용할 언어가 Javascript 이므로 NodeJS를 통해 진행할 예정이다.</p>
]]></description>
        </item>
    </channel>
</rss>