<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>esjw_.log</title>
        <link>https://velog.io/</link>
        <description>노력형 인간</description>
        <lastBuildDate>Wed, 03 Jul 2024 00:32:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. esjw_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/esjw_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[취약점 / AWS]]></title>
            <link>https://velog.io/@esjw_/%EC%B7%A8%EC%95%BD%EC%A0%90-AWS</link>
            <guid>https://velog.io/@esjw_/%EC%B7%A8%EC%95%BD%EC%A0%90-AWS</guid>
            <pubDate>Wed, 03 Jul 2024 00:32:55 GMT</pubDate>
            <description><![CDATA[<p>검색어 처리</p>
<p>req.session.user는 deserialize (읽어오는거)
passport-local을 쓸 이유가 없음
매번 해야하는데 성능만 낮아짐
-&gt; 도메인 들어오기 전에 자동으로 호출됨
-&gt; 가독성↑, 생산성↑
-&gt; 보이지 않게 해둘 수 있지만 속도가 5배~10배정도 차이남</p>
<p>regExp로 검색 가능</p>
<hr>
<p>사용자에게 에러 발생에 대한 자세한 정보를 주면 안됨
그 정보를 바탕으로 여러가지 시도를 하게됨
-&gt; 해킹</p>
<hr>
<ol>
<li>file upload 취약점 강화 - security</li>
<li>이체 transaction</li>
<li>AWS EC2 deploy</li>
</ol>
<p>-&gt; react(front server 분리)
-&gt; back server
4. https</p>
<hr>
<h2 id="aws">AWS</h2>
<ul>
<li><code>docker 초간단 초스피드 익숙해지기</code> 노션 참조</li>
</ul>
<p>❗ 포트 설정 잘 해주기
❗ volum 연결 잘 해주기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[인증 / CSRF]]></title>
            <link>https://velog.io/@esjw_/%EC%9D%B8%EC%A6%9D-CSRF</link>
            <guid>https://velog.io/@esjw_/%EC%9D%B8%EC%A6%9D-CSRF</guid>
            <pubDate>Mon, 01 Jul 2024 16:19:41 GMT</pubDate>
            <description><![CDATA[<h2 id="재인증">재인증</h2>
<blockquote>
<p>클라이언트 인증 시 필요한 보안 조치를 추가해야 함
-&gt; 재인증</p>
</blockquote>
<ul>
<li>아무리 인증되고 권한이 있더라도 무분별한 수행은 불가능</li>
<li>적절한 절차와 함께 이루어져야 함</li>
<li>바로 수행되지 않고 다시 한번 생각할 기회를 주어야 함</li>
<li>재인증 프로세스 중 어떤것을 선택할지 사전에 보안 설계를 철저히 하여 결정해야 함</li>
<li>구현 단계 이전에서 방어에 대한 설계가 이미 완성되어 개발자에게 전달되어 있어야 함</li>
</ul>
<h2 id="csrf">CSRF</h2>
<blockquote>
<p>CSRF(Cross Site Request Forgery)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/esjw_/post/12856583-296f-4101-aff8-05311dc69f65/image.png" alt=""></p>
<p><a href="https://devscb.tistory.com/123">CSRF란?</a></p>
<ul>
<li>로그인을 하면 게시판에 글 쓰는 권한이 생김</li>
<li>해커가 회원가입/로그인을 하고 admin이 글을 쓴것처럼 조작할 수 있음</li>
<li>해커가 자기 글에 js를 추가</li>
<li>제목을 클릭 시 해커가 원하는 글이 작성되게 할 수 있음</li>
</ul>
<p>ex)</p>
<pre><code>글제목 : 관리자님 억울해요
글내용 : 

&lt;form id=&quot;hongForm&quot; style=&quot;display:none&quot; method=&quot;post&quot; action=&quot;/post/save&quot; &gt;
  &lt;input type=&quot;hidden&quot; name=&quot;title&quot; value=&quot;회비 계좌 확인 요합니다&quot;&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;admin&quot;&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;content&quot; value=&quot;이번 모임의 회비 납부 안내입니다&lt;br&gt;국민은행 01-0123-1234 홍길동&quot;&gt;
  &lt;input type=&quot;submit&quot;&gt;
&lt;/form&gt;

&lt;script&gt;
  document.getElementById(&#39;hongForm&#39;).submit();
&lt;/script&gt;</code></pre><p>admin이 이 글을 보기 위해 클릭하면 관리자 인증과 권한으로 글이 작성됨</p>
<p>글쓰기 화면에서 작성한 글과 위 js 같은 글로 들어온 쓰기 요청을 구별해야 함</p>
<h3 id="csrf-token">CSRF token</h3>
<p>글쓰기 화면에 선행되어야 하는 요청을 ejs에 보냄
이때 csrf 토큰 발부(긴 문장으로 이루어짐)</p>
<p>submit 버튼을 클릭했을 때 casrf 토큰이 같이 들어오게 함</p>
<p>해커가 csrf 토큰을 스니핑하여 토큰까지 넘어가게 js 작성할 수도 있음
-&gt; 토큰은 일회용으로 사용 후 파기해야 함</p>
<pre><code class="language-js">...

const crypto = require(&#39;crypto&#39;);


...

//로그인 된 사용자만 글쓰기 화면 보여주기
router.get(&quot;/post/enter&quot;, function (req, res) {
  if (req.session.user) {
    req.session.csrf_token=crypto.randomBytes(32).toString(&#39;hex&#39;);
    res.render(&quot;post/enter.ejs&quot;, { data: { id: req.session.user.userid }, csrf_token:req.session.csrf_token });    
    // csrf 토큰을 보내서 render
  } else {
    res.render(&quot;index.ejs&quot;, { data: { alertMsg: &quot;로그인 먼저 해주세요&quot; } });
  }
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[라우터 분리 / 게시판 만들기]]></title>
            <link>https://velog.io/@esjw_/node.js%EC%97%90%EC%84%9C-MongoDB%EC%97%B0%EB%8F%99%ED%95%98%EC%97%AC-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@esjw_/node.js%EC%97%90%EC%84%9C-MongoDB%EC%97%B0%EB%8F%99%ED%95%98%EC%97%AC-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 27 Jun 2024 15:29:37 GMT</pubDate>
            <description><![CDATA[<h2 id="세션">세션</h2>
<p><img src="https://velog.velcdn.com/images/esjw_/post/797cc8f9-c049-45ab-a5e7-13d4600a7aa0/image.png" alt="">
<img src="https://velog.velcdn.com/images/esjw_/post/9bddb96b-1baa-41e8-a258-16ced10dfe61/image.png" alt=""></p>
<p>세션을 생성하여 user객체를 가리키게 만들어 req, res는 garbage가 되지만 user객체(정보)는 그대로 남아있음
-&gt; db 엑세스를 막고 메모리 엑세스를 사용하여 성능 향상</p>
<p>세션을 접근하기 위해서 세션id는 게속 유지시켜주어야함
-&gt; 쿠키에 담아두고 그것을 db에 저장해둠</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9eb2360c-a862-4ff0-8ad9-aef53b9e50e0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/f2f6960c-f8b5-4349-833c-006cde311067/image.png" alt=""></p>
<p>위는 모두 SSR 기반</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9eb396e1-b9ef-4d67-8c1d-82eae108df76/image.png" alt=""></p>
<p> <img src="https://velog.velcdn.com/images/esjw_/post/863c3150-aea4-425f-bcc8-7df6a6d94da3/image.png" alt=""></p>
<ul>
<li><p>p.545 전략 설정</p>
</li>
<li><p>b2b여서 client는 일반적인 사용자가 아니라 facebook-server(개발자)의 관계에서의 server(개발자)를 말함 (facebook이 server 역할)</p>
</li>
<li><p>p.557 serialize
<img src="https://velog.velcdn.com/images/esjw_/post/be42ec61-efc9-4283-a77e-779f4919592d/image.png" alt=""></p>
</li>
</ul>
<hr>
<h2 id="라우터-분리">라우터 분리</h2>
<p>server.js에 mysql 연동되어 있고 account.js에서 mysql을 사용하려고 하면 server.js에서 account.js로 exports하고 다시 server.js로 라우터를 import해야됨
-&gt; db를 따로 빼서 연결을 한번만 하자</p>
<pre><code>const { MongoClient } = require(&#39;mongodb&#39;);
const mongoClient = require(&#39;mongodb&#39;).MongoClient;</code></pre><hr>
<h1 id="nodejs에서-mongodb연동하여-게시판-만들기">node.js에서 MongoDB연동하여 게시판 만들기</h1>
<pre><code class="language-js">const { MongoClient } = require(&#39;mongodb&#39;);
const mysql = require(&#39;mysql&#39;);

let mongodb;
let mysqldb;

const setup = () =&gt; {
    const mongoDbUrl = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`
    const mongoConn = MongoClient.connect(mongoDbUrl, 
        { 
            usenewUrlParser: true,
            useUnifiedTopology: true
        }); 

};</code></pre>
<pre><code class="language-js">const { MongoClient } = require(&#39;mongodb&#39;);
const mysql = require(&#39;mysql&#39;);

let mongodb;
let mysqldb;

const setup = async () =&gt; {
    const mongoDbUrl = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`
    const mongoConn = await MongoClient.connect(mongoDbUrl, 
        { 
            usenewUrlParser: true,
            useUnifiedTopology: true
        }); 
    mongodb = mongoConn.db(&#39;myboard&#39;);
    console.log(&#39;몽고db 접속 성공&#39;);
};</code></pre>
<p>mongoclient.connect는 비동기라 연결될때까지 기다리도록 추가</p>
<hr>
<pre><code class="language-js">const { MongoClient } = require(&#39;mongodb&#39;);
const mysql = require(&#39;mysql&#39;);

let mongodb;
let mysqldb;

const setup = async () =&gt; {
    const mongoDbUrl = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`
    const mongoConn = await MongoClient.connect(mongoDbUrl, 
        { 
            usenewUrlParser: true,
            useUnifiedTopology: true
        }); 
    mongodb = mongoConn.db(&#39;myboard&#39;);
    console.log(&#39;몽고db 접속 성공&#39;);

    mysqldb = mysql.createConnection({
        host: &#39;localhost&#39;,
        user: &#39;jwbook&#39;,
        password: &#39;1234&#39;,
        database: &#39;myboard&#39;
    });

};</code></pre>
<p>mysql.connect는 알아서 설정 안해도 완료될때까지 기다렸다가 실행됨 -&gt; 동기</p>
<hr>
<pre><code class="language-js">const { MongoClient } = require(&#39;mongodb&#39;);
const mysql = require(&#39;mysql&#39;);

let mongodb;
let mysqldb;

const setup = async () =&gt; {
    if(mongodb &amp;&amp; mysqldb){
        return { mongodb, mysqldb };
    }

    try{
        const mongoDbUrl = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`
        const mongoConn = await MongoClient.connect(mongoDbUrl, 
            { 
                usenewUrlParser: true,
                useUnifiedTopology: true
            }); 
        mongodb = mongoConn.db(&#39;myboard&#39;);
        console.log(&#39;몽고db 접속 성공&#39;);

        mysqldb = mysql.createConnection({
            host: &#39;localhost&#39;,
            user: &#39;jwbook&#39;,
            password: &#39;1234&#39;,
            database: &#39;myboard&#39;
        });
        mysqldb.connect();
        console.log(&quot;MySQL 접속 성공&quot;);

        return { mongodb, mysqldb };
    }catch(err) {
        console.error(&quot;DB 접속 실패&quot;, err);
        throw err;    // 서버 가동 끝내게 만들기 위함
    }
};</code></pre>
<p>mongodb와 mysqldb에 무언가 값이 있을 경우 (연결되었을 경우)와 없는 경우로 나누기</p>
<hr>
<p>최종 db_setup.js</p>
<pre><code class="language-js">const { MongoClient } = require(&#39;mongodb&#39;);
const mysql = require(&#39;mysql&#39;);

let mongodb;
let mysqldb;

const setup = async () =&gt; {
    if(mongodb &amp;&amp; mysqldb){
        return { mongodb, mysqldb };
    }

    try{
        const mongoDbUrl = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`
        const mongoConn = await MongoClient.connect(mongoDbUrl, 
            { 
                usenewUrlParser: true,
                useUnifiedTopology: true
            }); 
        mongodb = mongoConn.db(&#39;myboard&#39;);
        console.log(&#39;몽고db 접속 성공&#39;);

        mysqldb = mysql.createConnection({
            host: &#39;localhost&#39;,
            user: &#39;jwbook&#39;,
            password: &#39;1234&#39;,
            database: &#39;myboard&#39;
        });
        mysqldb.connect();
        console.log(&quot;MySQL 접속 성공&quot;);

        return { mongodb, mysqldb };
    }catch(err) {
        console.error(&quot;DB 접속 실패&quot;, err);
        throw err;
    }
};

module.exports = setup;</code></pre>
<hr>
<p>server.js</p>
<pre><code class="language-js">const setup = require(&quot;./db_setup&quot;);
const express = require(&quot;express&quot;);

const app = express();

app.listen(8080, () =&gt; {
    console.log(&quot;8080 서버가 준비되었습니다...&quot;);
});</code></pre>
<hr>
<pre><code class="language-js">const setup = require(&quot;./db_setup&quot;);
const express = require(&quot;express&quot;);

const app = express();

app.listen(8080, async () =&gt; {
    await setup();
    console.log(&quot;8080 서버가 준비되었습니다...&quot;);
});</code></pre>
<p>db구축을 위한 setup 호출
비동기라서 await</p>
<hr>
<h2 id="라우터-분리-1">라우터 분리</h2>
<p>routes 폴더에 account.js</p>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();

router.get(&#39;/&#39;, (req, res) =&gt; {
    res.send(&#39;홈&#39;);
});</code></pre>
<hr>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

router.get(&#39;/&#39;, (req, res) =&gt; {
    setup();
    res.send(&#39;홈&#39;);
});</code></pre>
<hr>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

router.get(&#39;/&#39;, (req, res) =&gt; {
    const { mongodb, mysqldb } = setup();
  // db_setup.js 의 return 값 받기
    res.send(&#39;홈&#39;);
});</code></pre>
<hr>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

router.get(&#39;/&#39;, async (req, res) =&gt; {
    const { mongodb, mysqldb } = await setup();
  // db setup이 다 될때까지 기다려야해서 awit 추가
    res.send(&#39;홈 : db 사용 가능&#39;);
});</code></pre>
<p>db setup이 다 될때까지 기다려야해서 awit 추가</p>
<hr>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

router.get(&#39;/&#39;, async (req, res) =&gt; {
  // db 연결 오류 처리 추가
    try{
        const { mongodb, mysqldb } = await setup();
        res.send(&#39;홈 : db 사용 가능&#39;);
    }catch(err){
        res.status(500).send(&#39;db fail&#39;);
    }
});</code></pre>
<p>db 연결 오류 처리 추가</p>
<hr>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

router.get(&#39;/&#39;, async (req, res) =&gt; {
    console.log(&quot;GET / 처리 시작 &quot;);
    try{
        const { mongodb, mysqldb } = await setup();
        res.send(&#39;홈 : db 사용 가능&#39;);
    }catch(err){
        res.status(500).send(&#39;db fail&#39;);
    }
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});</code></pre>
<p>router를 만들때마다 db 연결이 되지 않도록 하기위하여 하는 작업임 -&gt; console.log로 확인가능</p>
<hr>
<p>최종 account.js</p>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

router.get(&#39;/&#39;, async (req, res) =&gt; {
    try{
        const { mongodb, mysqldb } = await setup();
        res.send(&#39;홈 : db 사용 가능&#39;);
    }catch(err){
        res.status(500).send(&#39;db fail&#39;);
    }
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});

module.exports = router;</code></pre>
<hr>
<p>server.js에 연결 추가</p>
<pre><code class="language-js">const setup = require(&quot;./db_setup&quot;);
const express = require(&quot;express&quot;);

const app = express();

//server.js에 연결 추가
app.use(&#39;/&#39;, require(&#39;./routes/account.js&#39;));

app.listen(8080, async () =&gt; {
    await setup();
    console.log(&quot;8080 서버가 준비되었습니다...&quot;);
});</code></pre>
<hr>
<p>account.js에서 server.js로 옮김</p>
<pre><code class="language-js">const setup = require(&quot;./db_setup&quot;);
const express = require(&quot;express&quot;);

const app = express();

app.get(&#39;/&#39;, async (req, res) =&gt; {
    console.log(&quot;GET / 처리 시작 &quot;);
    try{
        const { mongodb, mysqldb } = await setup();
        res.send(&#39;홈 : db 사용 가능&#39;);
    }catch(err){
        res.status(500).send(&#39;db fail&#39;);
    }
});

app.use(&#39;/&#39;, require(&#39;./routes/account.js&#39;));

app.listen(8080, async () =&gt; {
    await setup();
    console.log(&quot;8080 서버가 준비되었습니다...&quot;);
});</code></pre>
<p>index.ejs로 render하도록 수정</p>
<pre><code class="language-js">const setup = require(&quot;./db_setup&quot;);
const express = require(&quot;express&quot;);

const app = express();

app.get(&#39;/&#39;, (req, res) =&gt; {
    res.render(&#39;index.ejs&#39;);
});

app.use(&#39;/&#39;, require(&#39;./routes/account.js&#39;));

app.listen(8080, async () =&gt; {
    await setup();
    console.log(&quot;8080 서버가 준비되었습니다...&quot;);
});</code></pre>
<hr>
<p>account.js 수정</p>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

/////// 추가
router.get(&#39;/account/enter&#39;, (req, res) =&gt; {
    res.render(&#39;enter.ejs&#39;);
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});

module.exports = router;</code></pre>
<hr>
<h2 id="회원가입">회원가입</h2>
<p>server.js에 bodyParser 추가</p>
<pre><code class="language-js">const setup = require(&quot;./db_setup&quot;);
const express = require(&quot;express&quot;);

const app = express();

const bodyParser = require(&#39;body-parser&#39;);
app.use(bodyParser.urlencoded({extended:true}));
// 여러 개체 중복 가능

app.get(&#39;/&#39;, (req, res) =&gt; {
    res.render(&#39;index.ejs&#39;);
});

app.use(&#39;/&#39;, require(&#39;./routes/account.js&#39;));

app.listen(8080, async () =&gt; {
    await setup();
    console.log(&quot;8080 서버가 준비되었습니다...&quot;);
});</code></pre>
<hr>
<p>account.js에 추가</p>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

// 회원가입 화면 보기
router.get(&#39;/account/enter&#39;, (req, res) =&gt; {
    res.render(&#39;enter.ejs&#39;);
});

// 회원가입 처리
router.post(&quot;/account/save&quot;, (req, res) =&gt; {
    console.log(req.body);
    res.send(&quot;save ok&quot;);
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});

module.exports = router;</code></pre>
<hr>
<p>account.js</p>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

const sha = require(&#39;sha256&#39;);

// 회원가입 화면 보기
router.get(&#39;/account/enter&#39;, (req, res) =&gt; {
    res.render(&#39;enter.ejs&#39;);
});

// 회원가입 처리
router.post(&quot;/account/save&quot;, async (req, res) =&gt; {
    //console.log(req.body);
    // db 객체가 있어야 저장
    const { mongodb, mysqldb } = await setup();    // 비동기

    res.send(&quot;save ok&quot;);
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});

module.exports = router;</code></pre>
<hr>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

const sha = require(&#39;sha256&#39;);

// 회원가입 화면 보기
router.get(&#39;/account/enter&#39;, (req, res) =&gt; {
    res.render(&#39;enter.ejs&#39;);
});

// 회원가입 처리
router.post(&quot;/account/save&quot;, async (req, res) =&gt; {
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({userid: req.body.userid})
        .then(result =&gt; {
            if(result){// 중복 상태
                res.render(&#39;enter.ejs&#39;, {data: {msg: &#39;ID가 중복되었습니다.&#39;}});
            } else {
                const generateSalt = (length = 16) =&gt; {
                    const crypto = require(&#39;crypto&#39;);
                    return crypto.randomBytes(length).toString(&#39;hex&#39;);  // 버퍼를 16진수의 string으로 바꿔 반환
                };
            }
        })
        .catch();
    res.send(&quot;save ok&quot;);
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});

module.exports = router;</code></pre>
<p>id가 db에 있는지에 따라 작업 설정</p>
<hr>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

const sha = require(&#39;sha256&#39;);

// 회원가입 화면 보기
router.get(&#39;/account/enter&#39;, (req, res) =&gt; {
    res.render(&#39;enter.ejs&#39;);
});

// 회원가입 처리
router.post(&quot;/account/save&quot;, async (req, res) =&gt; {
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({userid: req.body.userid})
        .then(result =&gt; {
            if(result){// 중복 상태
                res.render(&#39;enter.ejs&#39;, {data: {msg: &#39;ID가 중복되었습니다.&#39;}});
            } else {
                const generateSalt = (length = 16) =&gt; {
                    const crypto = require(&#39;crypto&#39;);
                    return crypto.randomBytes(length).toString(&#39;hex&#39;);  // 버퍼를 16진수의 string으로 바꿔 반환
                };

                const salt = generateSalt();
                console.log(req.body);
                req.body.userpw = sha(req.body.userpw + salt);
                mongodb.collection(&#39;account&#39;)
                    .insertOne()
                    .then()
                    .catch();
            }
        })
        .catch();
    res.send(&quot;save ok&quot;);
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});

module.exports = router;</code></pre>
<p>아래처럼 추가</p>
<pre><code class="language-js">const salt = generateSalt();
                console.log(req.body);
                req.body.userpw = sha(req.body.userpw + salt);
                mongodb.collection(&#39;account&#39;)
                    .insertOne(req.body)
                    .then( result =&gt; {
                        if(result){
                            console.log(&#39;회원가입 성공&#39;);
                            mysqldb.query(sql, [], ()=&gt;{{}})
                        }
                    })
                    .catch();</code></pre>
<hr>
<p>mysql에 salt 저장</p>
<pre><code class="language-js">const salt = generateSalt();
console.log(req.body);
req.body.userpw = sha(req.body.userpw + salt);
mongodb.collection(&#39;account&#39;)
    .insertOne()
    .then( result =&gt; {
        if(result){
            console.log(&#39;회원가입 성공&#39;);
            const sql = `insert into usersalt(userid, salt) values (?, ?)`
            mysqldb.query(sql, [req.body.userid, salt], (err, rows, fields)=&gt;{
                if (err){
                    console.log(err);
                } else {
                    console.log(&#39;salt 저장 성공&#39;);
                }
            });
            res.redirect(&#39;/&#39;);
        } else{
            console.log(&#39;회원가입 fail&#39;);
            res.render(&#39;enter.ejs&#39;, {data: {alertMsg: &#39;회원가입 실패&#39;}});    // 페이지 그대로 머무는것 처럼 보이게
        }
    })
    .catch();</code></pre>
<hr>
<p>(ID 중복확인 후 비밀번호 암호화)</p>
<pre><code class="language-js">const router = require(&#39;express&#39;).Router();
const setup = require(&#39;../db_setup&#39;);

const sha = require(&#39;sha256&#39;);

// 회원가입 화면 보기
router.get(&#39;/account/enter&#39;, (req, res) =&gt; {
    res.render(&#39;enter.ejs&#39;);
});

// 회원가입 처리
router.post(&quot;/account/save&quot;, async (req, res) =&gt; {
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({userid: req.body.userid})
        .then(result =&gt; {
            if(result){// 중복 상태
                res.render(&#39;enter.ejs&#39;, {data: {msg: &#39;ID가 중복되었습니다.&#39;}});
            } else {
                const generateSalt = (length = 16) =&gt; {
                    const crypto = require(&#39;crypto&#39;);
                    return crypto.randomBytes(length).toString(&#39;hex&#39;);  // 버퍼를 16진수의 string으로 바꿔 반환
                };

                const salt = generateSalt();
                console.log(req.body);
                req.body.userpw = sha(req.body.userpw + salt);
                mongodb.collection(&#39;account&#39;)
                    .insertOne(req.body)
                    .then( result =&gt; {
                        if(result){
                            console.log(&#39;회원가입 성공&#39;);
                            const sql = `insert into usersalt(userid, salt) values (?, ?)`
                            mysqldb.query(sql, [req.body.userid, salt], (err, rows, fields)=&gt;{
                                if (err){
                                    console.log(err);
                                } else {
                                    console.log(&#39;salt 저장 성공&#39;);
                                }
                            });
                            res.redirect(&#39;/&#39;);
                        } else{
                            console.log(&#39;회원가입 fail&#39;);
                            res.render(&#39;enter.ejs&#39;, {data: {alertMsg: &#39;회원가입 실패&#39;}});    // 페이지 그대로 머무는것 처럼 보이게
                        }
                    })
                    .catch(err =&gt; {
                        console.log(err);
                        res.status(500).send(); 
                    });
            }
        })
        .catch(err =&gt; {
            console.log(err);
            res.status(500).send(); 
        });
});

router.get(&#39;/login&#39;, async (req, res) =&gt; {
    console.log(&quot;GET /login 처리 시작 &quot;);
    try {
        const { mongodb, mysqldb } = await setup();
        res.send(&quot;홈화면 : 데이터베이스 사용 가능&quot;);
    } catch (err) {
        res.status(500).send(&quot;데이터베이스 연결 실패&quot;);
    }
});

module.exports = router;</code></pre>
<p>enter.ejs</p>
<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;utf-8&quot;&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
        &lt;title&gt;Home&lt;/title&gt;
        &lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65&quot; crossorigin=&quot;anonymous&quot;&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;%- include(&#39;menu.html&#39;) %&gt;

        &lt;% if(typeof data != &#39;undefined&#39; &amp;&amp; data.alertMsg){ %&gt;
            &lt;script&gt;
                alert(`&lt;%= data.alertMsg %&gt;`);
            &lt;/script&gt;
        &lt;% } %&gt;

        &lt;div class = &quot;container mt-4&quot;&gt;
            &lt;h1&gt;회원가입&lt;/h1&gt;
            &lt;form action = &quot;/account/save&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; name = &quot;userid&quot; class = &quot;form-control&quot;&gt;
                &lt;% if (typeof data != &#39;undefined&#39; &amp;&amp; data.msg){ %&gt;
                    &lt;span class = &#39;text-danger&#39;&gt;&lt;%= data.msg %&gt;&lt;/span&gt;
                &lt;% } %&gt;
                &lt;/div&gt;&lt;p&gt;&lt;/p&gt;

                &lt;div class=&quot;form-group&quot;&gt;
                &lt;label&gt;비밀번호&lt;/label&gt;
                &lt;input type=&quot;password&quot;  class=&quot;form-control&quot; name =&quot;userpw&quot;&gt;
                &lt;/div&gt;&lt;p&gt;&lt;/p&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;username&quot;&gt;
                &lt;/div&gt;&lt;p&gt;&lt;/p&gt; 

                &lt;button type = &quot;submit&quot; class=&quot;btn btn-warning&quot; style=&quot;float:right&quot;&gt;회원가입&lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
        &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<hr>
<h2 id="환경변수">환경변수</h2>
<p><code>npm i dotenv</code></p>
<p>db_setup.js 맨 위에 추가</p>
<pre><code class="language-js">const dotenv = require(&#39;dotenv&#39;).config();</code></pre>
<hr>
<p>.env 파일 생성</p>
<pre><code>MONGODB_URL = &#39;mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0&#39;
MONGODB_DB = &#39;myboard&#39;

MYSQL_HOST = &#39;localhost&#39;
MYSQL_USER = &#39;jwbook&#39;
MYSQL_PASSWORD = &#39;1234&#39;
MYSQL_DB = &#39;myboard&#39;</code></pre><p>db_setup.js에 수정</p>
<pre><code class="language-js">const dotenv = require(&#39;dotenv&#39;).config();
const { MongoClient } = require(&#39;mongodb&#39;);
const mysql = require(&#39;mysql&#39;);

let mongodb;
let mysqldb;

const setup = async () =&gt; {
    if(mongodb &amp;&amp; mysqldb){
        return { mongodb, mysqldb };
    }

    try{
        const mongoDbUrl = process.env.MONGODB_URL;
        const mongoConn = await MongoClient.connect(mongoDbUrl, 
            { 
                usenewUrlParser: true,
                useUnifiedTopology: true
            }); 
        mongodb = mongoConn.db(process.env.MONGODB_DB);
        console.log(&#39;몽고db 접속 성공&#39;);

        mysqldb = mysql.createConnection({
            host: process.env.MYSQL_HOST,
            user: process.env.MYSQL_USER,
            password: process.env.MYSQL_PASSWORD,
            database: process.env.MYSQL_DB
        });
        mysqldb.connect();
        console.log(&quot;MySQL 접속 성공&quot;);

        return { mongodb, mysqldb };
    }catch(err) {
        console.error(&quot;DB 접속 실패&quot;, err);
        throw err;
    }
};

module.exports = setup;</code></pre>
<hr>
<h2 id="로그인-처리">로그인 처리</h2>
<p>menu.html</p>
<pre><code class="language-html">&lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65&quot; crossorigin=&quot;anonymous&quot;&gt;


&lt;nav class=&quot;navbar navbar-expand-lg bg-info&quot;&gt;
    &lt;div class=&quot;container-fluid&quot;&gt;
      &lt;!--  ///////// 추가 ///////   --&gt;
        &lt;span id=&quot;loginSpan&quot;&gt;
            &lt;form action=&quot;/account/login&quot; method=&quot;post&quot;&gt;
                ID &lt;input size=&quot;3&quot; name=&quot;userid&quot;&gt; 
                PW &lt;input size=&quot;3&quot; name=&quot;userpw&quot;&gt; 
                &lt;input type=&quot;submit&quot; value=&quot;login&quot; class=&quot;btn btn-outline-warning&quot;&gt;
            &lt;/form&gt;
        &lt;/span&gt;
      &lt;!--     ///////////////  --&gt;
        &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#navbarNav&quot; aria-controls=&quot;navbarNav&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&gt;
            &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
        &lt;/button&gt;
        &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarNav&quot;&gt;
            &lt;ul class=&quot;navbar-nav&quot;&gt;
                &lt;li class=&quot;nav-item&quot;&gt;
                    &lt;a class=&quot;nav-link active&quot; aria-current=&quot;page&quot; href=&quot;/&quot;&gt;Home&lt;/a&gt;
                &lt;/li&gt;
                &lt;li class=&quot;nav-item&quot;&gt;
                    &lt;a class=&quot;nav-link&quot; href=&quot;/post/list&quot;&gt;게시글목록&lt;/a&gt;
                &lt;/li&gt;
                &lt;li class=&quot;nav-item&quot;&gt;
                    &lt;a class=&quot;nav-link&quot; href=&quot;/account/enter&quot;&gt;회원가입&lt;/a&gt;
                &lt;/li&gt;
                &lt;li class=&quot;nav-item&quot;&gt;
                    &lt;a class=&quot;nav-link disabled&quot;&gt;Disabled&lt;/a&gt;
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/nav&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/df900ab8-09f5-49d1-9db5-9db5c6d6231d/image.png" alt=""></p>
<p>nav bar에 로그인 추가</p>
<hr>
<p>account.js에서 post방식으로 수정 후 데이터 넘어오는지 확인</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
});</code></pre>
<hr>
<p>로그인 되면 다시 index 페이지로 이동하게 만들기</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    // login ok 경우
    res.render(&#39;index.ejs&#39;);
});</code></pre>
<p>index.ejs는 menu.html을 include하고 있음
menu.html에 script 추가</p>
<pre><code class="language-html">&lt;script&gt;
    alert();
&lt;/script&gt;</code></pre>
<p>이렇게 script를 추가해두면 nav bar가 포함된 어떤 페이지를 들어가도 이게 선행되어 수행됨
본문을 랜더링하기 전에 먼저 수행</p>
<p>메뉴가 있을때마다 웹브라우저 메모리에 쿠키 확인
쿠키가 있으면 로그아웃 버튼 보이게</p>
<pre><code class="language-html">&lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;
    // 웹 브라우저 메모리에 uid 쿠키가 있는지 확인
    if(uid){
        $(&#39;#loginSpan&#39;).html(`&lt;button&gt;logout&lt;/button&gt;`); // 로그아웃 버튼
    }

&lt;/script&gt;</code></pre>
<p>cookie 값 가져오기 위한 jquery 추가</p>
<pre><code class="language-html">&lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;
    // 웹 브라우저 메모리에 uid 쿠키가 있는지 확인
    const uid = $.cookie(&#39;uid&#39;);    // 없으면 null로 들어감
    if(uid){
        $(&#39;#loginSpan&#39;).html(`&lt;button&gt;logout&lt;/button&gt;`);
    }

&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/ccb05775-1713-47e6-b683-8e055697ba5c/image.png" alt=""></p>
<p>직접 쿠키 추가하면 logout으로 바뀜</p>
<p>버튼 앞에 아이디 추가</p>
<pre><code class="language-html">&lt;script&gt;
    // 웹 브라우저 메모리에 uid 쿠키가 있는지 확인
    const uid = $.cookie(&#39;uid&#39;);    // 없으면 null로 들어감
    if(uid){
        $(&#39;#loginSpan&#39;).html(`${uid} &lt;button&gt;logout&lt;/button&gt;`);
    }
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/eca3d3a2-5aa9-4f30-a819-f1c3438cf8cd/image.png" alt=""></p>
<hr>
<pre><code class="language-html">&lt;script&gt;
    // 웹 브라우저 메모리에 uid 쿠키가 있는지 확인
    const uid = $.cookie(&#39;uid&#39;);    // 없으면 null로 들어감
    if(uid){
        $(&#39;#loginSpan&#39;).html(`${uid} &lt;button onclick = &#39;logout()&#39;&gt;logout&lt;/button&gt;`);
    }

    function logout(){
        $.removeCookie(&#39;uid&#39;, { path: &#39;/&#39; });
        location.reload();
    }
&lt;/script&gt;</code></pre>
<p>logout 버튼 클릭 시 cookie가 삭제됨
다시 login 입력이 보이게됨</p>
<hr>
<p>클라이언트에서만 reload하면 서버쪽에서 세션30분이 그대로 남겨짐</p>
<pre><code class="language-html">&lt;script&gt;
    // 웹 브라우저 메모리에 uid 쿠키가 있는지 확인
    const uid = $.cookie(&#39;uid&#39;);    // 없으면 null로 들어감
    if(uid){
        $(&#39;#loginSpan&#39;).html(`${uid} &lt;button onclick = &#39;logout()&#39;&gt;logout&lt;/button&gt;`);
    }

    function logout(){
        $.removeCookie(&#39;uid&#39;, { path: &#39;/&#39; });
        location.href = &#39;/account/logout&#39;;
    }
&lt;/script&gt;</code></pre>
<p>서버쪽에 보내지도록 href로 작성
account.js에 라우터 추가</p>
<pre><code class="language-js">router.get(&#39;/account/logout&#39;, (req, res) =&gt; {
    req.session.destroy();
});</code></pre>
<p>herf는 다 get방식으로 넘어옴</p>
<hr>
<p>session 사용을 위해 모듈 설치</p>
<p><code>npm i express-session cookie-parser</code></p>
<p>server.js에 session과 cookie 설정</p>
<pre><code class="language-js">const setup = require(&quot;./db_setup&quot;);
const express = require(&quot;express&quot;);

const session = require(&quot;express-session&quot;);
app.use(
    session({
        secret: &quot;암호화키&quot;,
        resave: false,
        saveUninitialized: false,
    })
);

const cookieParser = require(&quot;cookie-parser&quot;);
app.use(cookieParser());

const app = express();

const bodyParser = require(&#39;body-parser&#39;);
app.use(bodyParser.urlencoded({extended:true}));

app.get(&#39;/&#39;, (req, res) =&gt; {
    res.render(&#39;index.ejs&#39;);
});

app.use(&#39;/&#39;, require(&#39;./routes/account.js&#39;));

app.listen(process.env.WEB_PORT, async () =&gt; {
    await setup();
    console.log(&quot;8080 서버가 준비되었습니다...&quot;);
});</code></pre>
<hr>
<p>account.js에서 로그인 처리부분 설정</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({ userid: req.body.userid })
        .then(result =&gt; {
            if (result) {
                const sql = `SELECT salt FROM UserSalt 
                            WHERE userid=?`
                mysqldb.query(sql, [req.body.userid], () =&gt; {});
            } else {

            }
        })
        .catch();

});</code></pre>
<hr>
<p>mysql에서 가져온 usersalt와 password 합쳐서 암호화</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({ userid: req.body.userid })
        .then(result =&gt; {
            if (result) {
                const sql = `SELECT salt FROM UserSalt 
                            WHERE userid=?`
                mysqldb.query(sql, [req.body.userid], (err, rows, fields) =&gt; {
                    const salt = rows[0].salt;
                    const hashPw = sha(req.body.userpw + salt);
                });
            } else {

            }
        })
        .catch();

});</code></pre>
<hr>
<p>로그인 성공/실패 작업 추가</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({ userid: req.body.userid })
        .then(result =&gt; {
            if (result) {
                const sql = `SELECT salt FROM UserSalt 
                            WHERE userid=?`
                mysqldb.query(sql, [req.body.userid], (err, rows, fields) =&gt; {
                    const salt = rows[0].salt;
                    const hashPw = sha(req.body.userpw + salt);
                    if(result.userpw == hashPw){
                        // login ok
                        res.render(&#39;index.ejs&#39;);
                    } else {
                        // pw fail
                        res.render(&#39;login.ejs&#39;);
                    }
                });
            } else {

            }
        })
        .catch();

});</code></pre>
<hr>
<p>세션, 쿠키 추가</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({ userid: req.body.userid })
        .then(result =&gt; {
            if (result) {
                const sql = `SELECT salt FROM UserSalt 
                            WHERE userid=?`
                mysqldb.query(sql, [req.body.userid], (err, rows, fields) =&gt; {
                    const salt = rows[0].salt;
                    const hashPw = sha(req.body.userpw + salt);
                    if(result.userpw == hashPw){
                        // login ok
                        req.session.user = req.body;
                        res.cookie(&#39;uid&#39;, req.body.userid);
                        res.render(&#39;index.ejs&#39;);
                    } else {
                        // pw fail
                        res.render(&#39;login.ejs&#39;);
                    }
                });
            } else {

            }
        })
        .catch();

});</code></pre>
<hr>
<p>세션에 req.body를 넣을 때 비밀번호가 평서문으로 작성되어 있으므로 해시로 변경</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({ userid: req.body.userid })
        .then(result =&gt; {
            if (result) {
                const sql = `SELECT salt FROM UserSalt 
                            WHERE userid=?`
                mysqldb.query(sql, [req.body.userid], (err, rows, fields) =&gt; {
                    const salt = rows[0].salt;
                    const hashPw = sha(req.body.userpw + salt);
                    if(result.userpw == hashPw){
                        // login ok
                        req.body.userpw = hashPw;
                        req.session.user = req.body;
                        res.cookie(&#39;uid&#39;, req.body.userid);
                        res.render(&#39;index.ejs&#39;);
                    } else {
                        // pw fail
                        res.render(&#39;login.ejs&#39;);
                    }
                });
            } else {

            }
        })
        .catch();

});</code></pre>
<hr>
<p>오류처리</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({ userid: req.body.userid })
        .then(result =&gt; {
            if (result) {
                const sql = `SELECT salt FROM UserSalt 
                            WHERE userid=?`
                mysqldb.query(sql, [req.body.userid], (err, rows, fields) =&gt; {
                    const salt = rows[0].salt;
                    const hashPw = sha(req.body.userpw + salt);
                    if(result.userpw == hashPw){
                        // login ok
                        req.body.userpw = hashPw;
                        req.session.user = req.body;
                        res.cookie(&#39;uid&#39;, req.body.userid);
                        res.render(&#39;index.ejs&#39;);
                    } else {
                        // pw fail
                        res.render(&#39;login.ejs&#39;);
                    }
                });
            } else {
                // login fail
                res.render(&#39;login.ejs&#39;);
            }
        })
        .catch(err =&gt; {
            // login fail
            res.render(&#39;login.ejs&#39;);
        });

});</code></pre>
<hr>
<p>session 처리가 완료되었으니 logout시에 삭제 후 index.ejs로 foward</p>
<pre><code class="language-js">router.get(&#39;/account/logout&#39;, (req, res) =&gt; {
    req.session.destroy();
    res.render(&#39;index.ejs&#39;);
})</code></pre>
<hr>
<p>로그인 실패 시 alert이 뜨도록 수정</p>
<pre><code class="language-js">// 로그인 처리
router.post(&#39;/account/login&#39;, async (req, res) =&gt; {
    console.log(req.body);
    // db연결
    const { mongodb, mysqldb } = await setup();
    mongodb.collection(&#39;account&#39;)
        .findOne({ userid: req.body.userid })
        .then(result =&gt; {
            if (result) {
                const sql = `SELECT salt FROM UserSalt 
                            WHERE userid=?`
                mysqldb.query(sql, [req.body.userid], (err, rows, fields) =&gt; {
                    const salt = rows[0].salt;
                    const hashPw = sha(req.body.userpw + salt);
                    if(result.userpw == hashPw){
                        // login ok
                        req.body.userpw = hashPw;
                        req.session.user = req.body;
                        res.cookie(&#39;uid&#39;, req.body.userid);
                        res.render(&#39;index.ejs&#39;);
                    } else {
                        // pw fail
                        res.render(&#39;login.ejs&#39;, {data: {alertMsg: &#39;다시 로그인 해주세요&#39;}});
                    }
                });
            } else {
                // login fail
                res.render(&quot;index.ejs&quot;, {data:{alertMsg:&#39;다시 로그인 해주세요&#39;}});
            }
        })
        .catch(err =&gt; {
            // login fail
            res.render(&quot;index.ejs&quot;, {data:{alertMsg:&#39;다시 로그인 해주세요&#39;}});
        });

});</code></pre>
<hr>
<p>inde.ejs에 alertMsg가 전달되었을 때 추가
bootstrap 추가하는 href와 script는 menu.html에 넣어놔서 지우기</p>
<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;utf-8&quot;&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
        &lt;title&gt;Home&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;%- include(&#39;menu.html&#39;) %&gt;

        &lt;% if(typeof data != &#39;undefined&#39; &amp;&amp; data.alertMsg){ %&gt;
            &lt;script&gt;
                alert(`&lt;%= data.alertMsg %&gt;`) ;
            &lt;/script&gt;            
        &lt;% } %&gt;

        &lt;h1&gt;홈입니다.&lt;/h1&gt;

    &lt;/body&gt;
&lt;/html&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[passport]]></title>
            <link>https://velog.io/@esjw_/passport</link>
            <guid>https://velog.io/@esjw_/passport</guid>
            <pubDate>Wed, 26 Jun 2024 15:56:05 GMT</pubDate>
            <description><![CDATA[<h2 id="비밀번호-암호화">비밀번호 암호화</h2>
<h3 id="salt-추가-hashing">salt 추가 hashing</h3>
<p>그냥 값을 그대로 해싱하면 브루투포스 방법으로 알아낼 수도 있음
-&gt; 랜덤한 salt를 뒤에 추가하여 암호화하면 알아내기 힘듦
-&gt; 사용자마다 다른 랜덤한 salt 사용해야 의미가 있음!
-&gt; 사용자마다의 발생된 salt를 db에 저장해두기(비밀번호와 물리적으로 다른 db에 저장)</p>
<h3 id="salt-작업">salt 작업</h3>
<p>mysql에 salt를 저장할 테이블 생성</p>
<pre><code class="language-sql">create table UserSalt(
userid varchar(20) primary key,
salt varchar(500) not null
);</code></pre>
<pre><code class="language-js">//////// mysql 연결
const mysql = require(&quot;mysql&quot;);
mysqlconn = mysql.createConnection({
  host: &quot;localhost&quot;,
  user: &quot;test&quot;,
  password: &quot;1234&quot;,
  database: &quot;myboard&quot;,
});
mysqlconn.connect();
console.log(&quot;mysqlconn ok &quot;);

...

///// 회원가입 처리
const sha = require(&#39;sha256&#39;);

app.post(&quot;/signup&quot;, (req, res)=&gt;{
  console.log(req.body);

  ////// salt 생성 위한 부분 ///////////////
  const crypto = require(&#39;crypto&#39;);
  const generateSalt = (length = 16) =&gt; {
    return crypto.randomBytes(length).toString(&#39;hex&#39;);
  };// 16바이트의 hex 스타일로 랜덤 string 만들어기
  const salt = generateSalt();
  console.log(`Generated salt : ${salt}`);
  ////////////////////////////////////////

  console.log(`salt 없는 pw hash: `, sha(req.body.userpw));
  req.body.userpw = sha(req.body.userpw + salt);
  console.log(`salt 있는 pw hash: `, req.body.userpw);

  mydb.collection(&#39;account&#39;)
    .insertOne(req.body)
    .then(result =&gt; {
      console.log(&#39;회원가입 성공&#39;);

      //mysql에 salt 저장
      const sql = `insert into UserSalt (userid, salt)
                    values (?, ?)`;
      mysqlconn.query(sql, [req.body.userid, salt], (err, result2)=&gt;{
        if (err){
          console.log(err);
        }else{
          console.log(&#39;salt 저장 성공&#39;);
        }
      });
    })
    .catch(err=&gt;{
      console.log(err);
    });
  res.redirect(&#39;/&#39;);
});</code></pre>
<ul>
<li>req.body로 입력 데이터가 넘어옴</li>
<li>salt를 문자열 형태로 생성</li>
<li><blockquote>
<p>내장모듈 중 <code>crypto</code>를 사용함</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/esjw_/post/955dcd61-467d-461b-96dc-841d227ec82b/image.png" alt=""></p>
<pre><code class="language-js">app.post(&#39;/login&#39;, (req, res) =&gt;{
  //console.log(req.body);
  mydb.collection(&#39;account&#39;)
    .findOne({userid:req.body.userid})
    .then(result =&gt; {
      //console.log(result);
      let salt;
      const sql = `select salt from UserSalt
                        where userid=?`;
      mysqlconn.query(sql, [req.body.userid], (err, rows, fields)=&gt;{
        console.log(rows);
        salt = rows[0].salt;
      });
      // 입력한 pw를 hash로 변경
      const hashPw = sha(req.body.userpw + salt);
      if(result != null &amp;&amp; result.userpw == hashPw){
        //req.session.userid = req.body.userid;
        req.body.userpw = hashPw;
        req.session.user = req.body;
        //console.log(req.session);
        console.log(&#39;새로운 로그인&#39;);
        //res.send(`${req.session.user.userid}님 환영합니다.`);
        res.render(&#39;index.ejs&#39;, {user:req.session.user});
      }else{
        //res.send(&#39;login fail&#39;);
        res.render(&#39;login.ejs&#39;);
      }
    })
    .catch(err=&gt;{
      console.log(err);
      res.status(500).send();
    });
});</code></pre>
<p>hashPw 설정하는 위치가 잘못됨!
sql 설정 안으로 넣어주어야 함</p>
<pre><code class="language-js">app.post(&#39;/login&#39;, (req, res) =&gt;{
  //console.log(req.body);
  mydb.collection(&#39;account&#39;)
    .findOne({userid:req.body.userid})
    .then(result =&gt; {
      //console.log(result);
      let salt;
      const sql = `select salt from UserSalt
                        where userid=?`;
      mysqlconn.query(sql, [req.body.userid], (err, rows, fields)=&gt;{
        console.log(rows);
        salt = rows[0].salt;
        // 입력한 pw를 hash로 변경
        const hashPw = sha(req.body.userpw + salt);
        if(result != null &amp;&amp; result.userpw == hashPw){
          //req.session.userid = req.body.userid;
          req.body.userpw = hashPw;
          req.session.user = req.body;
          //console.log(req.session);
          console.log(&#39;새로운 로그인&#39;);
          //res.send(`${req.session.user.userid}님 환영합니다.`);
          res.render(&#39;index.ejs&#39;, {user:req.session.user});
        }else{
          //res.send(&#39;login fail&#39;);
          res.render(&#39;login.ejs&#39;);
        }
      });
    })
    .catch(err=&gt;{
      console.log(err);
      res.status(500).send();
    });
});</code></pre>
<hr>
<h2 id="패스포트">패스포트</h2>
<p><code>npm i passport passport-local passport-facebook</code></p>
<p>server.js를 복사하여 server_local.js로 이름 변경</p>
<p>server_local.js</p>
<pre><code class="language-js">const passport = require(&#39;passport&#39;);
const localStragegy = require(&#39;passport-local&#39;).Strategy;
app.use(passport.initialize());    
// req.session이 있는지 확인하고 존재하면 req.session.passport.user 추가
app.use(passport.session());</code></pre>
<p>로그인해서 나온 객체를 세션에 할당해줌</p>
<pre><code class="language-js">app.post(&#39;/login&#39;,
  passport.authenticate(&#39;local&#39;,{failureRedirect: &#39;/fail&#39;}),
  (req,res)=&gt;{
    console.log(req.session);
    console.log(req.session.passport);
    res.render(&#39;index.ejs&#39;, {user:req.session.passport})
});

passport.use(new localStragegy(
  {
    usernameField: &#39;userid&#39;,
    passwordField: &#39;userpw&#39;,
    session: true,
    passReqToCallback: false,
  },
  // 전략 수행 함수
  function(inputid, inputpw, done){
    mydb.collection(&#39;account&#39;)
        .findOne({userid: inputid})
        .then((result)=&gt;{
          if(result.userpw == inputpw){ // db == 입력값
            console.log(&#39;새로운 로그인&#39;);
            done(null, result);
          }else{
            done(null, false, {message: &#39;비밀번호 틀렸어요&#39;})
          }
        })
        .catch();
  }
))</code></pre>
<p>~ 11:17</p>
<p>실행 후 로그인 시 에러 발생</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/c04bab50-15d2-4493-854a-0d965697e1d6/image.png" alt=""></p>
<p>serialize: 정보 객체가 저장되는 것</p>
<pre><code class="language-js">////////// passport local 사용 로그인
app.post(&#39;/login&#39;,
  passport.authenticate(&#39;local&#39;,{
    //successRedirect: &#39;/&#39;,
    //failureRedirect: &#39;/fail&#39;
  }),
    // 위 두 줄이 없어야 콜백함수로 들어갈 수도 있음
  (req,res)=&gt;{
    console.log(req.session);
    console.log(req.session.passport);
    res.render(&#39;index.ejs&#39;, {user:req.session.passport})
});

...

passport.serializeUser(function(user, done){
  console.log(&#39;serializeUser&#39;);
  console.log(user);
  done(null, user.userid);
});

passport.deserializeUser(function(puserid, done){
  console.log(&#39;deserializeUser&#39;);
  console.log(puserid);

  mydb.collection(&#39;account&#39;)
    .findOne({userid: puserid})
    .then((result)=&gt;{
      console.log(result);
      done(null, result);
    })
    .catch();
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/d46a6d5f-4ef6-4e4a-b37f-fdd9d9a41936/image.png" alt=""></p>
<p>위 결과 설명 11:33~
done 끝나고 이 콜백? - post /login의 콜백</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/c6a4c18b-a043-4817-ba42-3bf1509c064b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/b4e68c97-e8ed-4ef0-adc9-c1cb1fff0985/image.png" alt=""></p>
<p>user 정보가 안뜸</p>
<hr>
<h2 id="https">https</h2>
<p>c:/program files/openssl-win64/bin/
위 주소(openssl.cfg 있는 위치) 복사해서 환경변수 설정</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/11c001f6-71fd-4387-835d-94154197815a/image.png" alt=""></p>
<p>접속 후 login 버튼 누르면
<img src="https://velog.velcdn.com/images/esjw_/post/4e23b9b9-a6f8-4129-b24b-ac0cad156b5a/image.png" alt=""></p>
<hr>
<h2 id="실습-과제">실습 과제</h2>
<ul>
<li>06_26_pw_hashing 파일이용</li>
<li>userid 중복 검사 후 회원가입 되도록</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[session & 암호화]]></title>
            <link>https://velog.io/@esjw_/session</link>
            <guid>https://velog.io/@esjw_/session</guid>
            <pubDate>Tue, 25 Jun 2024 16:24:05 GMT</pubDate>
            <description><![CDATA[<h2 id="session">session</h2>
<h3 id="세션-할당-원리">세션 할당 원리</h3>
<p>어플리케이션</p>
<ul>
<li>서버 서블릿이 가동</li>
</ul>
<p>일반 메모리 영역</p>
<ul>
<li>서블릿 객체가 일반 메모리 영역에 하나 생성됨
상속되는 모든 데이터도 함께 생성</li>
</ul>
<p>application scope</p>
<ul>
<li>서버가 가동될 때 객체가 하나 생성됨(하나만)</li>
<li>그 객체는 정보를 갖게 됨
: param 테이블
: attribute 테이블
: 테이블이라서 key-value 형태로 저장 가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/esjw_/post/2058983f-7aed-4efb-8d11-2732ed6173f2/image.png" alt=""></p>
<p>은수, 길동, 톰 모두 웹 서버로 /index.html을 가져감
화면에 id, pw 입력 후 request 날림
서블릿에 req 도착</p>
<p>최초로 넘어가는 경우엔 쿠키가 없음
-&gt; id, pw는 보통 post방식으로 넘김
-&gt; url의 body에 데이터 채워짐</p>
<p>요청이 넘어가면 request queue에 쌓임
꺼내지면서 request 객체가 생성(쓰레드 객체)
이 객체들의 우선순위는 모두 동일하게 0
어떤게 먼저 작업할 지 모름</p>
<p>작업이 시작되면 메모리의 스택 영역의 맨 밑에 있는 메인 스택에 런 메소드 스펙 생성됨
여기에 request 객체와 response 객체의 주소를 가리킴</p>
<ul>
<li>쓰레드는 스택 영역 내에서 작업을 수행하며, 다른 쓰레드가 CPU 사용 권한을 소모하면 대기 상태로 넘어감</li>
<li>cpu 사용 권한의 시간은 아주 짧음</li>
<li>이 과정이 반복되며, 쓰레드마다 작업 순서가 정해지지 않고 일정 순서대로 변경될 수 있음</li>
<li>여러 쓰레드들이 조금씩 왔다갔다 하면서 작업 수행됨</li>
</ul>
<p>request 정보들은 db에 가서 확인하는 작업이 일어남
확인이 완료되면 user객체가 생성되고 이 안에는 그 회원의 정보가 들어감
이 객체를 어딘가에 저장해두기 위해 세션 사용함</p>
<p><code>req.session</code>: 세션 할당
세션에는 테이블 형태의 값이 저장됨
ex) user @100 / user라는 이름으로 100번지
-&gt; 100번지를 가리키게 됨</p>
<p>쿠키(JSID)가 생성되고 response 헤더에 들어감
서블릿 객체로 인해 무언가가 일어나고 response가 web server를 거쳐서 응답되어짐 -&gt; 커넥션 종료 -&gt; 스택영역 삭제 -&gt; 쿠키 삭제
하지만 user 정보는 남아있음 (30분)
이때 유지시간은 설정할 수 있지만 길게 설정하면 용량 문제가 생김</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/f3dd14f7-165e-4329-82de-4b00f11f7192/image.png" alt=""></p>
<p>세션이 할당된 상태에서 웹 브라우저가 종료되어도 서버에서는 종료되었는지 모름 -&gt; 30분간 유지 (비효율)
-&gt; 모든 페이지에서 로그아웃 버튼이 보이게해서 로그아웃 하도록 함</p>
<p><code>saveUninitialized : true</code>로 설정하면 어떤 사용자가 로그인하지 않아도 세션을 할당해줌
로그인하고 정보가 확인되어야 그 정보를 가리키는 세션이 생기도록 하기 위해 false로 하는 것이 좋다</p>
<pre><code>로그인 버튼 클릭 시 url의 마지막에 서블릿에 도착하기 위한 서블릿 이름이 와야 함
url 뒤에 확장명이 정확하게 없거나 .do로 되어있는 경우면 서블릿일 수 있음
-&gt; ❗ 서블릿 이름은 자신의 기술과 구조를 노출시키면 안돼서 별명 사용
-&gt; 스프링 프레임워크에서 대표 서블릿을 .do라고 하는 예제를 많은 사람이 따라해서 .do를 보면 java로 만든것을 알게되어버림
-&gt; .do가 아닌 다른 별명 사용하기</code></pre><p><img src="https://velog.velcdn.com/images/esjw_/post/24709a63-9244-4996-8286-113843ced9f2/image.png" alt=""></p>
<hr>
<p>개발자모드에서 세션 id 지우면 같은 세션으로는 접속 못함
지우고 새로고침해도 세션을 생성할 생각이 없는데 세션이 생성됨
새로고침 할때마다 새로운 세션 생성
세션 계속 추가되면 DOS 공격이 될 수 있음</p>
<p>로그인 될 때만 세션 부여하기 위해 
saveUninitialized: false로 지정</p>
<pre><code class="language-js">app.use(session({
  secret : &#39;adsfasdaf&#39;,
  resave : false,
  saveUninitialized : false 
}));

app.post(&#39;/login&#39;, (req, res) =&gt;{
  console.log(req.body);
  mydb.collection(&#39;account&#39;)
    .findOne({userid:req.body.userid})
    .then(result =&gt; {
      //console.log(result);
      if(result != null &amp;&amp; result.userpw == req.body.userpw){
        req.session.userid = req.body.userid;
        console.log(req.session);
        res.send(&#39;login ok&#39;);
      }else{
        res.send(&#39;login fail&#39;);
      }
    })
    .catch(err=&gt;{
      console.log(err);
      res.status(500).send();
    });
});
</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/876a8a18-a6e4-44d2-9054-4c451203fbe7/image.png" alt=""></p>
<p>로그인 성공 시 세션이 생성됨
세션에 userid가 아니라 req.body로 객체를 넣어줄 수도 있음</p>
<pre><code class="language-js">app.post(&#39;/login&#39;, (req, res) =&gt;{
  console.log(req.body);
  mydb.collection(&#39;account&#39;)
    .findOne({userid:req.body.userid})
    .then(result =&gt; {
      //console.log(result);
      if(result != null &amp;&amp; result.userpw == req.body.userpw){
        //req.session.userid = req.body.userid;
        req.session.user = req.body;
        console.log(req.session);
        res.send(&#39;login ok&#39;);
      }else{
        res.send(&#39;login fail&#39;);
      }
    })
    .catch(err=&gt;{
      console.log(err);
      res.status(500).send();
    });
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/db719cfd-8275-4ce9-bc56-923f4784dafd/image.png" alt=""></p>
<pre><code class="language-js">res.send(`${req.session.user.userid}님 환영합니다.`);</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/f4d51e3f-d9f7-4b81-8a43-2c9d572de8ab/image.png" alt=""></p>
<pre><code class="language-js">app.get(&#39;/bank&#39;, (req, res)=&gt;{
  // 로그인되면 /bank 접속 가능
  if(req.session.user.userid){  // 세션에 userid가 있으면 로그인된 상태
    res.send(`${req.session.user.userid}님 자산 현황`);
  }else{
    res.send(&#39;로그인부터 해주세요&#39;);
  }
});</code></pre>
<p>로그인 안된 상태일 때 user 세션을 찾을 수 없어서 에러 발생
❗ 어제 실습때 발생했던 에러</p>
<pre><code class="language-js">app.get(&#39;/bank&#39;, (req, res)=&gt;{
  // 로그인되면 /bank 접속 가능
  if(typeof req.session.user != &#39;undefined&#39;){  // 세션에 userid가 있으면 로그인된 상태
    res.send(`${req.session.user.userid}님 자산 현황`);
  }else{
    res.send(&#39;로그인부터 해주세요&#39;);
  }
});</code></pre>
<p>undefined 상태일 경우 조건으로 수정하여 해결!</p>
<hr>
<p>세션이 있으면 로그인 상태 유지</p>
<pre><code class="language-js">app.get(&#39;/login&#39;, (req, res) =&gt;{
  if(req.session.user){
    res.send(&#39;이미 로그인 되어있습니다.&#39;);
  }else{
    res.render(&#39;login&#39;);
  }
});</code></pre>
<hr>
<h3 id="로그아웃">로그아웃</h3>
<pre><code class="language-html">if(user){
  반갑습니다. ..님 로그아웃
}else{
  로그인 해주세요. 로그인
}

&lt;% if(user){ %&gt;
  &lt;h3&gt;반갑습니다. &lt;%= user.userid %&gt;님&lt;/h3&gt;
  &lt;a href=&quot;/logout&quot;&gt;로그아웃&lt;/a&gt; 
&lt;% }else{ %&gt;
  &lt;h3&gt;로그인 해주세요.&lt;/h3&gt;
  &lt;p&gt;&lt;p&gt;
  &lt;button class=&quot;w-100 btn btn-warning login&quot;&gt;&lt;b&gt;로그인&lt;/b&gt;&lt;/button&gt;
&lt;% } %&gt;</code></pre>
<p>res.render index 페이지로 넘어가면 else로 들어갈 수 있지만
다이렉트로 접속하면 user가 undefined되었다는 오류 발생
-&gt; if에 조건 추가</p>
<pre><code class="language-html">&lt;% if(typeof user !==&#39;undefined&#39; &amp;&amp; user){ %&gt;
      &lt;h3&gt;반갑습니다. &lt;%= user.userid %&gt;님&lt;/h3&gt;
      &lt;a href=&quot;/logout&quot;&gt;로그아웃&lt;/a&gt; 
    &lt;% }else{ %&gt;
      &lt;h3&gt;로그인 해주세요.&lt;/h3&gt;
      &lt;p&gt;&lt;p&gt;
      &lt;a href=&quot;/login&quot; class=&quot;w-100 btn btn-warning login&quot;&gt;&lt;b&gt;로그인&lt;/b&gt;&lt;/a&gt;
    &lt;% } %&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/42bb7fb3-c81a-4a24-beba-1fa40e172aca/image.png" alt=""></p>
<p>로그인 후 세션이 있는 상태
여기에서 index페이지를 직접 요청하면 foward가 아니라서 user가 없음 -&gt; 로그인해주세요 (else문) 실행됨</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/b37a94b7-4a34-4bd4-94df-f539e49e381c/image.png" alt=""></p>
<pre><code class="language-js">app.get(&quot;/&quot;, function (req, res) {
  if(req.session.user){
    //res.send(&#39;이미 로그인 되어있습니다.&#39;);
    res.render(&#39;index.ejs&#39;, {user:req.session.user});
  }else{
    res.render(&#39;index.ejs&#39;, {user:null});
  }
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/79db6bef-8490-4ac3-bfcf-acd31b48b882/image.png" alt=""></p>
<pre><code class="language-js">app.get(&quot;/&quot;, function (req, res) {
  if(typeof req.session.user !== &#39;undefined&#39; &amp;&amp; req.session.user){
    //res.send(&#39;이미 로그인 되어있습니다.&#39;);
    res.render(&#39;index.ejs&#39;, {user:req.session.user});
  }else{
    res.render(&#39;index.ejs&#39;, {user:null});
  }
});</code></pre>
<p>이렇게 수정해도 안됨
❗❗ session 설정한 내용 위에 작성했어서 난 오류였다
session 설정 아래로 내려주니 잘 작동 함</p>
<p>여기에서는 <code>typeof req.session.user !== &#39;undefined&#39; &amp;&amp;</code> 추가 안해줘도 됨!</p>
<hr>
<p>보안 상 모든 페이지에서 로그인 여부가 보여야 함
-&gt; 모든 페이지에 있는 nav 영역에서 로그인 여부를 인식이 가능하도록 코딩해야 함</p>
<p>성능을 고려하면 위에 했던 작업은 좋지 않음
nav 페이지를 갈때마다 user 데이터를 들고 가야함
html로 만들었었는데 동적 처리를 위해 menu.ejs로 바꾸고 데이터를 들고 가게 해야하는데 이는 좋지 않음
이유: ssr이어서 client에서 요청할 때마다 동적처리해서 nav를 완성한 상태를 넘겨야 함 -&gt; 모든 요청을 ssr해야하여 서버 부하</p>
<p>세션 id는 클라이언트의 cookie 형태로 있으니 세션이 존재하면 js로 html에 포함하여 로그인 상태로 보여주고 그렇지 않으면 로그인 내용을 안보여주게 처리 (서버 부하 없앨 수 있음)
문제는 세션 id는 js로 핸들링될 수 없는 보안쿠키임</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/0613ef07-dba6-40d2-81db-9c6912dedb33/image.png" alt=""></p>
<ul>
<li>HttpOnly는 js로 사용 불가능한 보안 쿠키</li>
<li>node.js가 아닌 html 안에서 사용하는 바닐라 js를 말함</li>
</ul>
<hr>
<h3 id="회원가입">회원가입</h3>
<p>signup.ejs</p>
<pre><code class="language-html">&lt;div class = &quot;container mt-4&quot;&gt;
  &lt;form action = &quot;/signup&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; name = &quot;userid&quot; class = &quot;form-control&quot;&gt;
    &lt;/div&gt;&lt;p&gt;&lt;/p&gt;
    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label&gt;비밀번호&lt;/label&gt;
      &lt;input type=&quot;text&quot; name = &quot;userpw&quot; class = &quot;form-control&quot;&gt;
    &lt;/div&gt;&lt;p&gt;&lt;/p&gt;   
    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label&gt;소속&lt;/label&gt;
      &lt;input type=&quot;text&quot; name = &quot;usergroup&quot; class = &quot;form-control&quot;&gt;
    &lt;/div&gt;&lt;p&gt;&lt;/p&gt;   
    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label&gt;이메일&lt;/label&gt;
      &lt;input type=&quot;text&quot; name = &quot;useremail&quot; class = &quot;form-control&quot;&gt;
    &lt;/div&gt;&lt;p&gt;&lt;/p&gt;             
    &lt;button type = &quot;submit&quot; class=&quot;btn btn-primary&quot; style=&quot;float:right&quot;&gt;가입&lt;/button&gt;
  &lt;/form&gt;
&lt;/div&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/a2a7975a-3e86-4106-bd6e-e2ade1d0fa3a/image.png" alt=""></p>
<pre><code class="language-js">app.get(&quot;/signup&quot;, (req, res)=&gt;{
  res.render(&#39;signup&#39;);
});

app.post(&quot;/signup&quot;, (req, res)=&gt;{
  console.log(req.body);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/e1027b6a-fa92-4a01-a62a-27084ba59a0f/image.png" alt=""></p>
<pre><code class="language-js">app.post(&quot;/signup&quot;, (req, res)=&gt;{
  console.log(req.body);
  mydb.collection(&#39;account&#39;)
    .insertOne(req.body)
    .then(result =&gt; {
      console.log(&#39;회원가입 성공&#39;);
    })
    .catch(err=&gt;{
      console.log(err);
    });
  res.redirect(&#39;/&#39;);
});</code></pre>
<hr>
<h2 id="암호화">암호화</h2>
<h3 id="비밀번호-암호화">비밀번호 암호화</h3>
<pre><code class="language-html">input type=&#39;password&#39;</code></pre>
<p>암호화를 위해 비밀번호 타입으로 변경</p>
<p>사용자는 입력을 평문으로 하고 저장되어 있는 것은 해쉬로 암호화됨</p>
<p>똑같은 해쉬 알고리즘으로 암호화하여 암호화되어 있던 값과 비교</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[foward & cookie]]></title>
            <link>https://velog.io/@esjw_/foward-cookie</link>
            <guid>https://velog.io/@esjw_/foward-cookie</guid>
            <pubDate>Mon, 24 Jun 2024 16:40:06 GMT</pubDate>
            <description><![CDATA[<h2 id="foward">foward</h2>
<p>서버에는 data가 넘어오지만 DB에는 반영되지 않게
edit으로 post요청
보통 화면 보기는 get방식
post로 하는 이유
: content에서 edit으로 넘길 데이터가 많아서</p>
<blockquote>
<ul>
<li>제목을 클릭하면 나오는 상세페이지를 모달로 띄움 </li>
</ul>
</blockquote>
<ul>
<li>모달을 이용하여 바로 수정이 가능하도록 변경</li>
<li>수정된 내용은 바로 목록에 반영하여 보여지도록 함</li>
<li>_id에 따라 상세페이지 띄우는 /content와 수정을 위한 /edit, edit.ejs가 필요없어짐
(modal에 <span id="hiddenSpan"></span>를 추가하여 id를 받아 사용)</li>
</ul>
<ul>
<li>input 태그에 readonly 속성추가하면 편집이 안됨</li>
</ul>
<p><code>updateOne(조건, 변경항목)</code></p>
<pre><code class="language-js">app.post(&#39;/update&#39;, (req, res) =&gt;{
  console.log(req.body);
  mydb.collection(&#39;post&#39;)
    .updateOne({_id:new ObjId(req.body._id)},
    {$set: {title:req.body.title, content:req.body.content, date:req.body.someDate} })
    .then(result =&gt; {
      //res.redirect(&#39;/list&#39;);
      list(req,res);
    })
    .catch(err =&gt; {
      console.log(err);
      res.status(500).send();
    });
});</code></pre>
<p>list.ejs</p>
<pre><code class="language-html">&lt;!-- The Modal --&gt;
&lt;form action=&quot;/update&quot; method=&quot;post&quot;&gt;
  &lt;span id=&quot;hiddenSpan&quot;&gt;&lt;/span&gt;
  &lt;!-- db update를 위한 _id 값을 담기 위함(숨김처리) --&gt;  
  &lt;div class=&quot;modal&quot; id=&quot;myModal&quot;&gt;
    &lt;div class=&quot;modal-dialog&quot;&gt;
      &lt;div class=&quot;modal-content&quot;&gt;

        &lt;!-- Modal Header --&gt;
        &lt;div class=&quot;modal-header&quot;&gt;
          제목 : &lt;div&gt;&lt;h4 class=&quot;modal-title&quot; id=&quot;postTitle&quot;&gt;&lt;/h4&gt;&lt;/div&gt;        
          &lt;button type=&quot;button&quot; class=&quot;btn-close&quot; data-bs-dismiss=&quot;modal&quot;&gt;&lt;/button&gt;
        &lt;/div&gt;

        &lt;!-- Modal body --&gt;
        &lt;div class=&quot;modal-body&quot; &gt;
          &lt;div&gt;
            내용 : &lt;span id=&quot;postContent&quot;&gt;&lt;/span&gt;
          &lt;/div&gt;
          &lt;p&gt;&lt;/p&gt;
          &lt;div&gt;
            작성일 : &lt;span id=&quot;postDate&quot;&gt;&lt;/span&gt;
          &lt;/div&gt;

        &lt;/div&gt;

        &lt;!-- Modal footer --&gt;
        &lt;div class=&quot;modal-footer&quot;&gt;  
          &lt;span id=&quot;updateSpan&quot;&gt;
            &lt;button type=&quot;button&quot; class=&quot;btn btn-info&quot; onclick=&quot;editDisplay()&quot;&gt;수정하러가기&lt;/button&gt;
          &lt;/span&gt;      

          &lt;button type=&quot;button&quot; class=&quot;btn btn-danger&quot; data-bs-dismiss=&quot;modal&quot;&gt;Close&lt;/button&gt;
        &lt;/div&gt;

      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/form&gt;

...

&lt;tr&gt;    
  &lt;!-- 타이틀 부분을 클릭하면 모달 띄워지게
       이때 모달창에 data가 전달됨 --&gt;
  &lt;td data-bs-toggle=&quot;modal&quot; data-bs-target=&quot;#myModal&quot;&gt;
    &lt;a href=&quot;#&quot; onclick=&quot;modal_content(`&lt;%= data[i]._id %&gt;`,`&lt;%= data[i].title %&gt;`,`&lt;%= data[i].content %&gt;`, `&lt;%= data[i].date %&gt;`)&quot;&gt;&lt;%= data[i].title %&gt;&lt;/a&gt;&lt;/td&gt;
  &lt;td&gt;&lt;%= data[i].date %&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button class = &#39;delete btn btn-outline-danger&#39; data-id = &#39;&lt;%= data[i]._id %&gt;&#39;&gt;삭제&lt;/button&gt;&lt;/td&gt;
&lt;/tr&gt;

...

&lt;script&gt;
  let modalData;

  // 모달창에 보이게할 내용 설정 함수
  function modal_content(_id, title, content, date){                
    modalData={_id, title, content, date};
    $(&#39;#postTitle&#39;).text(`${modalData.title}`);                
    $(&#39;#postContent&#39;).text(`${modalData.content}`);
    $(&#39;#postDate&#39;).text(`${modalData.date}`);
    $(&#39;#updateSpan&#39;).html(`&lt;button type=&quot;button&quot; class=&quot;btn btn-info&quot; onclick=&#39;editDisplay()&#39;&gt;수정하러가기&lt;/button&gt;`);
    // 마지막 줄이 추가되어 있지 않으면 &#39;수정하러가기&#39;가 아닌 &#39;수정&#39; 버튼이 뜰 수 있음 (동적이라서 생기는 문제)
  }

  // 수정하기 버튼 클릭 시 수정 가능하게 바꿔주는 함수
  function editDisplay() {
    //console.log(modalData._id, modalData.title, modalData.content, modalData.date);
    $(&#39;#hiddenSpan&#39;).html(`&lt;input  type=&#39;hidden&#39; value=&#39;${modalData._id}&#39; name=&#39;_id&#39;&gt;`);  
    $(&#39;#postTitle&#39;).html(`&lt;input value=&#39;${modalData.title}&#39; name=&#39;title&#39;&gt;`);                
    $(&#39;#postContent&#39;).html(`&lt;textarea name=&#39;content&#39;&gt;${modalData.content}&lt;/textarea&gt;`);
    $(&#39;#postDate&#39;).html(`&lt;input type=&#39;date&#39; value=&#39;${modalData.date}&#39; name=&#39;someDate&#39;&gt;`);
    $(&#39;#updateSpan&#39;).html(`&lt;button type=&quot;submit&quot; class=&quot;btn btn-warning&quot; &gt;수정&lt;/button&gt;`);
  }
  // 모달 안에서 사용되어 submit 타입의 버튼 클릭 시 모달 맨 처음 부분에 설정되어 있는 action=&quot;/update&quot;로 인해 /update로 데이터가 넘어감
&lt;/script&gt;</code></pre>
<hr>
<p>목표: 어디에서 렌더링할것이냐
CCR/SCR가 아니라 성능이 목표
-&gt; DB까지 가서 가져오는것 보다 Server에 있는 것을 가져오는 것이 성능이 좋음
-&gt; update를 DB에 적용하고 그 결과를 redirect로 가져오면 db에서 다시 데이터를 가져옴
결과를 foward
server에서 update 후 list에 적용하는게 forward</p>
<p>보안 상에도 redirect는 안좋음
중간에 해커가 요청을 변조할 수 있음</p>
<p><a href="https://velog.io/@bongf/learned-redirect-forward">https://velog.io/@bongf/learned-redirect-forward</a>
<a href="https://nesoy.github.io/articles/2018-04/Redirect-Forward">https://nesoy.github.io/articles/2018-04/Redirect-Forward</a></p>
<pre><code>URL의 변화여부가 필요하다면 Redirect를 사용하는 것이 좋습니다.
객체를 재사용하거나 공유해야한다면 Forward를 사용하는 것이 좋습니다.</code></pre><hr>
<h2 id="쿠키">쿠키</h2>
<pre><code class="language-js">app.get(&#39;/cookie&#39;, (req,res)=&gt;{
  res.cookie(&#39;milk&#39;, `1000원`);
  res.send(&#39;쿠키 설정 완료&#39;);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/07da1883-1a84-41e9-9e94-ec444e24718c/image.png" alt=""></p>
<pre><code class="language-js">app.get(&#39;/cookie&#39;, (req,res)=&gt;{
  let milk = parseInt(req.cookies.milk) + 1000;
  // 맨 처음에는 milk라는 쿠키가 존재하지 않아서 NaN이 나옴
  if(isNaN(milk)){
    milk = 0;
  }
  res.cookie(&#39;milk&#39;, milk);
  res.cookie(&#39;name&#39;, &#39;이지우&#39;);
  //res.send(&#39;쿠키 설정 완료&#39;);
  res.send(&#39;product : &#39; + req.cookies.milk + &quot; / name : &quot; + req.cookies.name);
  // send는 두번 실행 안됨
  // send를 작성하지 않아도 브라우저 헤더를 통해 쿠키가 넘어감
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/14d141da-7792-4c37-b7a1-2bc69c1de4d7/image.png" alt=""></p>
<h3 id="수명-설정">수명 설정</h3>
<pre><code class="language-js">res.cookie(&#39;milk&#39;, milk, {maxAge : 1000});</code></pre>
<p>1초 전에 새로고침하면 1000씩 증가되지만
1초 후에 새로고침하면 다시 1000이 됨 (화면에는 0)</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/aa0e00b2-a2be-41ca-b8c8-aeef1a7c168f/image.png" alt=""></p>
<p>값부분에 쓰여있는 것은 암호화가 아니라 인코딩된 것</p>
<hr>
<h3 id="쿠키-암호화">쿠키 암호화</h3>
<pre><code class="language-js">const cookieParser = require(&#39;cookie-parser&#39;);
app.use(cookieParser(&#39;암호화키&#39;));  // 암호화키 부분에는 아무거나 들어가도 됨
app.get(&#39;/cookie&#39;, (req,res)=&gt;{
  let milk = parseInt(req.signedCookies.milk) + 1000; // 암호화된 쿠키값
  if(isNaN(milk)){
    milk = 0;
  }
  res.cookie(&#39;milk&#39;, milk, {signed:true});  // 암호화하여 보이게
  res.cookie(&#39;name&#39;, &#39;이지우&#39;);
  //res.send(&#39;쿠키 설정 완료&#39;);
  res.send(&#39;product : &#39; + req.signedCookies.milk + &quot; / name : &quot; + req.cookies.name);
  // send는 두번 실행 안됨
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/4862ed46-aa16-4c3a-98e4-0bb11b058857/image.png" alt=""></p>
<hr>
<h2 id="세션">세션</h2>
<blockquote>
<p>저장할 때는 주로 텍스트 형태로 저장
객체 형태 그대로 저장도 가능</p>
</blockquote>
<ul>
<li>보안 문제 때문에 서버쪽에 저장</li>
</ul>
<h3 id="로그인-처리-과정">로그인 처리 과정</h3>
<ol>
<li>웹 서버에서 사용자의 아이디, 패스워드를 입력하고 로그인 버튼을 클릭함</li>
<li>입력된 정보가 존재하는 사용자의 정보를 DB에서 확인하고, 그 정보에 세션을 할당함</li>
<li>❗ 세션은 미들웨어를 통해 공간을 할당받아서, 이 공간에 접속할 수 있는 번호(id)를 생성함</li>
<li>생성된 세션 아이디를 웹 서버 엔진에 의해 리스폰스 헤더에 쿠키로 세팅함</li>
<li>클라이언트 측에서 생성된 쿠키 정보를 저장하여 다음 요청시 사용함</li>
</ol>
<h3 id="session-생성">session 생성</h3>
<pre><code class="language-js">let session = require(&quot;express-session&quot;);
app.use(
  session({
  secret : &#39;asdfasdfasf&#39;,
  resave : false,   // 접속할 때마다 새로운 세션id 발급 여부
  saveUninitialized: true,  // 세션 사용하기 전까지 세션id 발급 안함
  })
);

app.get(&quot;/session&quot;, function (req, res) {
  console.log(req.session.milk);
  if(isNaN(req.session.milk)){
    req.session.milk = 0;
  }
  req.session.milk = req.session.milk + 1000;
  res.send(&quot;session : &quot; + req.session.milk + &quot;원&quot;);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/b98b136a-ca89-42f9-8d54-c1cb5c7d708e/image.png" alt=""></p>
<p>connect.sid라는 이름의 키값이 저장됨</p>
<hr>
<h3 id="cookie와-session-비교">cookie와 session 비교</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/21a53f06-8eca-4a5b-924b-077f87ada724/image.png" alt=""></p>
<hr>
<h2 id="실습">실습</h2>
<h3 id="로그인-페이지-생성">로그인 페이지 생성</h3>
<p>login.ejs (enter.ejs 수정하여 사용)</p>
<pre><code class="language-html">&lt;div class = &quot;container mt-4&quot;&gt;
        &lt;form action = &quot;/login&quot; method=&quot;post&quot;&gt;    &lt;!-- post 방식으로 넘김 --&gt;
            &lt;div class = &quot;form-group&quot;&gt;
            &lt;label&gt;아이디&lt;/label&gt;
            &lt;input type=&quot;text&quot; name = &quot;userid&quot; class = &quot;form-control&quot;&gt;
            &lt;/div&gt;&lt;p&gt;&lt;/p&gt;
            &lt;div class=&quot;form-group&quot;&gt;
            &lt;label&gt;비밀번호&lt;/label&gt;
            &lt;input type=&quot;text&quot; name =&quot;userpw&quot; class=&quot;form-control&quot;&gt;
            &lt;/div&gt;&lt;p&gt;&lt;/p&gt;                
            &lt;button type = &quot;submit&quot; class=&quot;btn btn-warning&quot; style=&quot;float:right&quot;&gt;로그인&lt;/button&gt;
        &lt;/form&gt;
    &lt;/div&gt;</code></pre>
<p>server.js (get, post 라우터 모두 설정)</p>
<pre><code class="language-js">app.get(&quot;/login&quot;, (req, res) =&gt;{
  console.log(&#39;로그인 페이지&#39;);
  res.render(&#39;login.ejs&#39;);
});

app.post(&#39;/login&#39;, (req, res) =&gt;{
  console.log(&#39;아이디 : &#39;+ req.body.userid);
  console.log(&#39;비밀번호 : &#39;+ req.body.userpw);
  res.send(&#39;로그인 되었습니다.&#39;);
});</code></pre>
<hr>
<h3 id="정보-일치-여부-확인">정보 일치 여부 확인</h3>
<p>server.js</p>
<pre><code class="language-js">app.post(&#39;/login&#39;, (req, res) =&gt;{
  console.log(&#39;아이디 : &#39;+ req.body.userid);
  console.log(&#39;비밀번호 : &#39;+ req.body.userpw);

  mydb
    .collection(&#39;account&#39;)
    .findOne({userid : req.body.userid})
    .then(result =&gt; {
      if(result.userpw == req.body.userpw){
        res.send(&#39;로그인 되었습니다.&#39;);
      }else{
        res.send(&#39;비밀번호가 틀렸습니다.&#39;);
      }
    });
});</code></pre>
<p>findOne으로 db에 저장된 userid와 입력된 userid가 같은 값의 데이터 가져오기(result)
이 데이터의 userpw가 입력된 userpw와 같으면 로그인 되도록 로직 추가</p>
<p>❗ 여기에서 입력된 userid  값이 db에 없으면 userpw를 가져올 수 없어 에러 발생함</p>
<hr>
<h3 id="세션-적용">세션 적용</h3>
<pre><code class="language-js">app.get(&quot;/login&quot;, (req, res) =&gt;{
  console.log(req.session);
  if(req.session.user){
    // 로그인 시 req.session.user 세션 데이터가 존재하면 로그인 상태인 것으로 간주함
    console.log(&#39;세션 유지&#39;);
    res.send(&#39;로그인 되었습니다.&#39;);
  }else{
    res.render(&#39;login.ejs&#39;);
  }
});

app.post(&#39;/login&#39;, (req, res) =&gt;{
  console.log(&#39;아이디 : &#39;+ req.body.userid);
  console.log(&#39;비밀번호 : &#39;+ req.body.userpw);

  mydb
    .collection(&#39;account&#39;)
    .findOne({userid : req.body.userid})
    .then(result =&gt; {
      if(result.userpw == req.body.userpw){
        req.session.user = req.body;
        console.log(&#39;새로운 로그인&#39;);
        res.send(&#39;로그인 되었습니다.&#39;);
      }else{
        res.send(&#39;비밀번호가 틀렸습니다.&#39;);
      }
    });
});</code></pre>
<p>db의 데이터와 입력한 id, pw가 일치하면 req.body에 있는 데이터(입력한 id, pw)가 req.session.user 세션에 추가됨</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/7c55bc3f-a488-4213-8a6b-ee9bf48ca1aa/image.png" alt=""></p>
<p>로그인 성공한 뒤 브라우저를 닫고 다시 /login에 접속하면 바로 로그인 성공 페이지가 띄워짐</p>
<hr>
<h3 id="로그아웃-세션-삭제">로그아웃 (세션 삭제)</h3>
<pre><code class="language-js">app.get(&#39;/logout&#39;, (req, res) =&gt; {
  console.log(&#39;로그아웃&#39;);
  req.session.destroy();  // 세션 삭제
  res.redirect(&#39;/&#39;);
});</code></pre>
<p>로그인 후 세션이 있는 상태에서 /logout에 접속하면 홈화면으로 이동하면서 세션이 삭제됨</p>
<hr>
<h3 id="회원가입-기능-링크">회원가입 기능 링크</h3>
<pre><code class="language-js">app.post(&#39;/login&#39;, (req, res) =&gt;{
  console.log(&#39;아이디 : &#39;+ req.body.userid);
  console.log(&#39;비밀번호 : &#39;+ req.body.userpw);

  mydb
    .collection(&#39;account&#39;)
    .findOne({userid : req.body.userid})
    .then(result =&gt; {
      if(result.userpw == req.body.userpw){
        req.session.user = req.body;
        console.log(&#39;새로운 로그인&#39;);
        res.render(&#39;index.ejs&#39;, {user:req.session.user});
      }else{
        res.render(&#39;login.ejs&#39;);
      }
    });
});

app.get(&#39;/logout&#39;, (req, res) =&gt; {
  console.log(&#39;로그아웃&#39;);
  req.session.destroy();  // 세션 삭제
  res.render(&#39;index.ejs&#39;, {user:null});
});</code></pre>
<ul>
<li>/login 요청 시 이미 사용자 세션이 있으면 user 데이터와 함께 index.ejs로 이동</li>
<li>처음 로그인 시에 성공하면 user 데이터와 함께 index.ejs로 이동</li>
<li>로그인 실패 시 login.ejs로 이동</li>
<li>로그아웃 성공 시 user 데이터는 null 값으로 넘겨주면서 index.ejs로 이동 </li>
</ul>
<hr>
<h3 id="로그인-성공-화면-구성">로그인 성공 화면 구성</h3>
<p>index.ejs</p>
<pre><code class="language-html">&lt;h1&gt;홈입니다.&lt;/h1&gt;

&lt;% if(user){ %&gt;
&lt;h3&gt;반갑습니다. &lt;%= user.userid %&gt;님.&lt;/h3&gt;
&lt;a href=&quot;/logout&quot;&gt;로그아웃&lt;/a&gt;
&lt;% }else{%&gt;
&lt;h3&gt;로그인 해주세요.&lt;/h3&gt;
&lt;p&gt;&lt;p&gt;
&lt;button class=&quot;w-100 btn btn-warning login&quot;&gt;&lt;b&gt;로그인&lt;/b&gt;&lt;/button&gt;
&lt;% }%&gt;

&lt;script&gt;
  $(&#39;.login&#39;).click(function(e){
    location.href=&#39;/login&#39;;
  })
&lt;/script&gt;</code></pre>
<hr>
<h3 id="에러-발생-부분">에러 발생 부분</h3>
<ul>
<li>로그아웃 후 나오는 페이지에서 로그인 버튼 클릭해도 작동 안함</li>
<li><code>localhost:8080/</code>에 접속하면 <pre><code>ReferenceError: D:\study\JavaScript\01_WEB\06_24_login\views\index.ejs:14
  12|     &lt;h1&gt;홈입니다.&lt;/h1&gt;
  13|
&gt;&gt; 14|     &lt;% if(user){ %&gt;
  15|     &lt;h3&gt;반갑습니다. &lt;%= user.userid %&gt;님.&lt;/h3&gt;
  16|     &lt;a href=&quot;/logout&quot;&gt;로그아웃&lt;/a&gt;
  17|     &lt;% }else{%&gt;
</code></pre></li>
</ul>
<p>user is not defined</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Mini Project(부동산 매물 등록)]]></title>
            <link>https://velog.io/@esjw_/Mini-Project%EB%B6%80%EB%8F%99%EC%82%B0-%EB%A7%A4%EB%AC%BC-%EB%93%B1%EB%A1%9D</link>
            <guid>https://velog.io/@esjw_/Mini-Project%EB%B6%80%EB%8F%99%EC%82%B0-%EB%A7%A4%EB%AC%BC-%EB%93%B1%EB%A1%9D</guid>
            <pubDate>Sun, 23 Jun 2024 14:50:06 GMT</pubDate>
            <description><![CDATA[<h2 id="소개">소개</h2>
<h3 id="목표">목표</h3>
<p>지금까지 배운 JavaScript를 이용하여 부동산 매물 등록/목록 페이지 구현하기</p>
<ol>
<li>등록 페이지에서 작성한 내용은 DB에 저장</li>
<li>DB에서 내용을 가져와 목록에 보여주기</li>
<li>저장된 내용 수정/삭제 기능 구현</li>
</ol>
<h3 id="팀">팀</h3>
<ul>
<li>김현호</li>
<li>이지우</li>
</ul>
<h3 id="구현-결과">구현 결과</h3>
<ol>
<li>등록 페이지에서 작성한 내용은 DB에 저장</li>
</ol>
<p>-&gt; mongoDB로 연동 완료
-&gt; 저장 후 목록 페이지에 바로 추가된 내용 보이도록 함</p>
<ol start="2">
<li>DB에서 내용을 가져와 목록에 보여주기</li>
</ol>
<p>-&gt; 저장된 내용 중 필요한 부분을 넘겨주어 보여주도록 함</p>
<ol start="3">
<li>저장된 내용 수정/삭제 기능 구현</li>
</ol>
<p>-&gt; 해당 db의 id로 연동하여 form 태그에 작성된 내용을 가져와 수정되도록 함
-&gt; 버튼 클릭 시 해당 데이터의 id를 가져와 삭제되도록 함</p>
<p>목표했던 큰 틀 3가지는 모두 구현 완료</p>
<hr>
<h2 id="구조">구조</h2>
<h3 id="데이터베이스">데이터베이스</h3>
<p>데이터의 유연성을 위하여 mongoDB 사용</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/58425076-82d2-461b-a45e-31d29b0c2868/image.png" alt=""></p>
<ul>
<li><code>_id</code>: 자동 생성됨</li>
<li><code>title</code>: 글 제목</li>
<li><code>content</code>: 글 내용</li>
<li><code>wirter</code>: 작성자</li>
<li><code>region</code>: 지역 (선택항목으로 지정함)</br>

</li>
</ul>
<h3 id="서버">서버</h3>
<p>server.js</p>
<pre><code class="language-js">const express = require(&#39;express&#39;);
const app = express();

const MongoClient = require(&#39;mongodb&#39;).MongoClient;
const ObjectId = require(&#39;mongodb&#39;).ObjectId;

const bodyParser = require(&#39;body-parser&#39;);

const path = require(&#39;path&#39;);

let db;

app.use(express.static(&#39;public&#39;));
app.use(bodyParser.urlencoded({ extended: true }));
app.set(&#39;view engine&#39;, &#39;ejs&#39;);

// mongoDB 연결
const mongoURI = &#39;&lt;&lt;url&gt;&gt;&#39; ;
MongoClient.connect(mongoURI)
    .then(client =&gt; {
        console.log(&#39;Connected to Database&#39;);
        db = client.db(&#39;&lt;&lt;db이름&gt;&gt;&#39;);
        // server 8080포트로 열기
        app.listen(8080, () =&gt; {
            console.log(&#39;Server running on port 8080&#39;);
        });
    })
    .catch(err =&gt; {
           console.log(err)
    });


// 목록 페이지 라우터
app.get(&#39;/list&#39;, (req, res) =&gt; {
  // DB를 Array형식으로(posts) data로 넘겨 list.ejs에서 data 변수로 사용 가능
    db.collection(&#39;post&#39;).find().toArray()
        .then(posts =&gt; {
            res.render(&#39;list.ejs&#39;, { data: posts });
        })
        .catch(error =&gt; console.error(error));
});

// 추가 페이지 라우터
app.get(&#39;/enter&#39;, (req, res) =&gt; {
    res.render(&#39;enter&#39;);    // .ejs 생략 가능
});

// id별로 수정 페이지 라우터
app.get(&#39;/edit/:id&#39;, (req, res) =&gt; {
    const postId = new ObjectId(req.params.id);
    // db에서 _id를 objectid로 생성하여 해당 내용을 edit.ejs로 넘김
    db.collection(&#39;post&#39;).findOne({ _id: postId })
        .then(post =&gt; {
            res.render(&#39;edit&#39;, { data: post });
        })
        .catch(error =&gt; console.error(error));
});

// 수정 페이지 라우터(db에 수정내용 업데이트)
app.post(&#39;/edit&#39;, (req, res) =&gt; {
    const postId = new ObjectId(req.body.id);
    const updatedPost = {
        title: req.body.title,
        content: req.body.content,
        date: req.body.someDate,
        writer: req.body.writer, 
        region: req.body.region 
    };

    db.collection(&#39;post&#39;).updateOne({ _id: postId }, { $set: updatedPost })
        .then(result =&gt; {
            console.log(&#39;Post updated successfully&#39;);
            res.redirect(&#39;/list&#39;);
        })
        .catch(error =&gt; console.error(error));
});

// 삭제
app.post(&#39;/delete&#39;, (req, res) =&gt; {
    const postId = new ObjectId(req.body._id);
    db.collection(&#39;post&#39;).deleteOne({ _id: postId })
        .then(result =&gt; {
            console.log(&#39;Post deleted successfully&#39;);
            res.status(200).send();
        })
        .catch(error =&gt; {
            console.error(&#39;Error deleting post:&#39;, error);
            res.status(500).send();
        });
});

// db에 내용 추가
app.post(&#39;/save&#39;, (req, res) =&gt; {
    const newPost = {
        title: req.body.title,
        content: req.body.content,
        date: req.body.date,
        writer: req.body.writer, 
        region: req.body.region 
    };

    db.collection(&#39;post&#39;).insertOne(newPost)
        .then(result =&gt; {
            console.log(&#39;New post added successfully&#39;);
            res.redirect(&#39;/list&#39;);    // 목록 페이지가 보이도록 함
        })
        .catch(error =&gt; console.error(error));
});</code></pre>
<hr>
<h3 id="클라이언트">클라이언트</h3>
<h4 id="목록-페이지list">목록 페이지(/list)</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/3e654860-f880-4bc4-8254-7870533176a3/image.png" alt=""></p>
<h4 id="삭제-시-alert">삭제 시 alert</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/1f0818b7-56eb-4ed6-80ae-6188f9e64973/image.png" alt="">
<img src="https://velog.velcdn.com/images/esjw_/post/23a01975-58b8-434e-813f-619a9fb29837/image.png" alt=""></p>
<p>list.ejs</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Post List&lt;/title&gt;
    &lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH&quot; crossorigin=&quot;anonymous&quot;&gt;
    &lt;style&gt;
        .card-body {
            padding: 1.5rem;
            background-color: #f8f9fa;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            transition: box-shadow 0.3s ease;
        }
        .card-body:hover {
            box-shadow: 0 0 20px rgba(0,0,0,0.2);
        }
        .btn-edit {
            margin-right: 0.5rem;
        }
        .sidebar {
            background-color: #f0f0f0;
            padding: 20px;
            border-radius: 10px;
            margin-bottom: 20px;
        }
        .sidebar h4 {
            margin-bottom: 20px;
        }
        .sidebar .btn-write {
            width: 100%;
            margin-bottom: 10px;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;!-- Navbar --&gt;
    &lt;nav class=&quot;navbar navbar-expand-lg navbar-dark bg-primary&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;a class=&quot;navbar-brand&quot; href=&quot;#&quot;&gt;Post List&lt;/a&gt;
            &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#navbarNav&quot; aria-controls=&quot;navbarNav&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&gt;
                &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarNav&quot;&gt;
                &lt;ul class=&quot;navbar-nav&quot;&gt;
                    &lt;li class=&quot;nav-item&quot;&gt;
                        &lt;a class=&quot;nav-link&quot; href=&quot;/enter&quot;&gt;Write&lt;/a&gt;
                    &lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/nav&gt;

    &lt;div class=&quot;container mt-4&quot;&gt;
        &lt;div class=&quot;row&quot;&gt;
            &lt;div class=&quot;col-md-3&quot;&gt;
                &lt;div class=&quot;sidebar&quot;&gt;
                    &lt;h4&gt;Options&lt;/h4&gt;
                    &lt;!-- Write Button --&gt;
                    &lt;a href=&quot;/enter&quot; class=&quot;btn btn-primary btn-write&quot;&gt;Write&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;col-md-9&quot;&gt;
                &lt;h2 class=&quot;mb-4&quot;&gt;Post List&lt;/h2&gt;
                &lt;div class=&quot;row row-cols-1 row-cols-md-2 g-4&quot;&gt;
                    &lt;% data.forEach(post =&gt; { %&gt;
                        &lt;div class=&quot;col&quot;&gt;
                            &lt;div class=&quot;region-background region-&lt;%= post.region %&gt;&quot;&gt;
                                &lt;div class=&quot;card h-100&quot;&gt;
                                    &lt;div class=&quot;card-body&quot;&gt;
                                        &lt;h5 class=&quot;card-title&quot;&gt;&lt;%= post.title %&gt;&lt;/h5&gt;
                                        &lt;p class=&quot;card-text&quot;&gt;&lt;%= post.content %&gt;&lt;/p&gt;
                                        &lt;p class=&quot;card-text&quot;&gt;&lt;strong&gt;Region:&lt;/strong&gt; &lt;%= post.region %&gt;&lt;/p&gt;
                                        &lt;p class=&quot;card-text&quot;&gt;&lt;strong&gt;Writer:&lt;/strong&gt; &lt;%= post.writer %&gt;&lt;/p&gt;
                                        &lt;p class=&quot;card-text&quot;&gt;&lt;strong&gt;Date:&lt;/strong&gt; &lt;%= post.date %&gt;&lt;/p&gt;
                                        &lt;a href=&quot;/edit/&lt;%= post._id %&gt;&quot; class=&quot;btn btn-warning btn-edit&quot;&gt;Edit&lt;/a&gt;
                                        &lt;button class=&quot;btn btn-danger btn-delete&quot; data-id=&quot;&lt;%= post._id %&gt;&quot;&gt;Delete&lt;/button&gt;
                                    &lt;/div&gt;
                                &lt;/div&gt;
                            &lt;/div&gt;
                        &lt;/div&gt;
                    &lt;% }); %&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;https://code.jquery.com/jquery-3.6.0.min.js&quot;&gt;&lt;/script&gt;
    &lt;script&gt;
        $(document).ready(function() {
            $(&#39;.btn-delete&#39;).click(function(e) {
                e.preventDefault();
                var postId = $(this).attr(&#39;data-id&#39;);
                if (confirm(&#39;Are you sure you want to delete this post?&#39;)) {
                    $.ajax({
                        type: &#39;POST&#39;,
                        url: &#39;/delete&#39;,
                        data: { _id: postId },
                        success: function(response) {
                            alert(&#39;Post deleted successfully&#39;);
                            window.location.href = &#39;/list&#39;;
                        },
                        error: function(xhr, textStatus, errorThrown) {
                            console.log(&#39;Error deleting post:&#39;, textStatus);
                        }
                    });
                }
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<hr>
<h4 id="추가-페이지enter">추가 페이지(/enter)</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/6fc02592-a8c2-4307-856c-efd1efdc230b/image.png" alt=""></p>
<p>enter.ejs</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Edit Post&lt;/title&gt;
    &lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH&quot; crossorigin=&quot;anonymous&quot;&gt;
    &lt;style&gt;
        .form-container {
            max-width: 600px;
            margin: auto;
            background-color: #f8f9fa;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            transition: box-shadow 0.3s ease;
        }
        .form-container:hover {
            box-shadow: 0 0 20px rgba(0,0,0,0.2);
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;nav class=&quot;navbar navbar-expand-lg navbar-dark bg-primary&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;a class=&quot;navbar-brand&quot; href=&quot;/list&quot;&gt;Post List&lt;/a&gt;
            &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#navbarNav&quot; aria-controls=&quot;navbarNav&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&gt;
                &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarNav&quot;&gt;
                &lt;ul class=&quot;navbar-nav&quot;&gt;
                    &lt;li class=&quot;nav-item&quot;&gt;
                        &lt;a class=&quot;nav-link&quot; href=&quot;/enter&quot;&gt;Write&lt;/a&gt;
                    &lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/nav&gt;

    &lt;div class=&quot;container mt-4 form-container&quot;&gt;
        &lt;h2 class=&quot;mb-4&quot;&gt;Edit Post&lt;/h2&gt;
        &lt;form action=&quot;/edit&quot; method=&quot;POST&quot;&gt;
            &lt;input type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;&lt;%= data._id %&gt;&quot;&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;title&quot; class=&quot;form-label&quot;&gt;Title&lt;/label&gt;
                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;title&quot; name=&quot;title&quot; value=&quot;&lt;%= data.title %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;content&quot; class=&quot;form-label&quot;&gt;Content&lt;/label&gt;
                &lt;textarea class=&quot;form-control&quot; id=&quot;content&quot; name=&quot;content&quot; rows=&quot;5&quot; required&gt;&lt;%= data.content %&gt;&lt;/textarea&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;someDate&quot; class=&quot;form-label&quot;&gt;Date&lt;/label&gt;
                &lt;input type=&quot;date&quot; class=&quot;form-control&quot; id=&quot;someDate&quot; name=&quot;someDate&quot; value=&quot;&lt;%= data.date %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;region&quot; class=&quot;form-label&quot;&gt;Region&lt;/label&gt;
                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;region&quot; name=&quot;region&quot; value=&quot;&lt;%= data.region %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;writer&quot; class=&quot;form-label&quot;&gt;Writer&lt;/label&gt;
                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;writer&quot; name=&quot;writer&quot; value=&quot;&lt;%= data.writer %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;button type=&quot;submit&quot; class=&quot;btn btn-warning&quot;&gt;Update&lt;/button&gt;
        &lt;/form&gt;
    &lt;/div&gt;

    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<hr>
<h4 id="업데이트-페이지edit_id">업데이트 페이지(/edit:_id)</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/7e36bcbd-e89e-48d5-9ad5-9063d6ea7fe9/image.png" alt=""></p>
<p>edit.ejs</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Edit Post&lt;/title&gt;
    &lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH&quot; crossorigin=&quot;anonymous&quot;&gt;
    &lt;style&gt;
        .form-container {
            max-width: 600px;
            margin: auto;
            background-color: #f8f9fa;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            transition: box-shadow 0.3s ease;
        }
        .form-container:hover {
            box-shadow: 0 0 20px rgba(0,0,0,0.2);
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

    &lt;nav class=&quot;navbar navbar-expand-lg navbar-dark bg-primary&quot;&gt;
        &lt;div class=&quot;container&quot;&gt;
            &lt;a class=&quot;navbar-brand&quot; href=&quot;/list&quot;&gt;Post List&lt;/a&gt;
            &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#navbarNav&quot; aria-controls=&quot;navbarNav&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&gt;
                &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
            &lt;/button&gt;
            &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarNav&quot;&gt;
                &lt;ul class=&quot;navbar-nav&quot;&gt;
                    &lt;li class=&quot;nav-item&quot;&gt;
                        &lt;a class=&quot;nav-link&quot; href=&quot;/enter&quot;&gt;Write&lt;/a&gt;
                    &lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/nav&gt;
    &lt;div class=&quot;container mt-4 form-container&quot;&gt;
        &lt;h2 class=&quot;mb-4&quot;&gt;Edit Post&lt;/h2&gt;
        &lt;form action=&quot;/edit&quot; method=&quot;POST&quot;&gt;
            &lt;input type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;&lt;%= data._id %&gt;&quot;&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;title&quot; class=&quot;form-label&quot;&gt;Title&lt;/label&gt;
                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;title&quot; name=&quot;title&quot; value=&quot;&lt;%= data.title %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;content&quot; class=&quot;form-label&quot;&gt;Content&lt;/label&gt;
                &lt;textarea class=&quot;form-control&quot; id=&quot;content&quot; name=&quot;content&quot; rows=&quot;5&quot; required&gt;&lt;%= data.content %&gt;&lt;/textarea&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;someDate&quot; class=&quot;form-label&quot;&gt;Date&lt;/label&gt;
                &lt;input type=&quot;date&quot; class=&quot;form-control&quot; id=&quot;someDate&quot; name=&quot;someDate&quot; value=&quot;&lt;%= data.date %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;region&quot; class=&quot;form-label&quot;&gt;Region&lt;/label&gt;
                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;region&quot; name=&quot;region&quot; value=&quot;&lt;%= data.region %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;div class=&quot;mb-3&quot;&gt;
                &lt;label for=&quot;writer&quot; class=&quot;form-label&quot;&gt;Writer&lt;/label&gt;
                &lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;writer&quot; name=&quot;writer&quot; value=&quot;&lt;%= data.writer %&gt;&quot; required&gt;
            &lt;/div&gt;
            &lt;button type=&quot;submit&quot; class=&quot;btn btn-warning&quot;&gt;Update&lt;/button&gt;
        &lt;/form&gt;
    &lt;/div&gt;

    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><hr>
<h2 id="소감">소감</h2>
<ul>
<li>김현호<pre><code>몽고DB, 노드JS, 서버와의 연동이 이렇게 부드럽게 잘 연결됨에 놀라웠다. 거기에 덧붙혀 오류의 많은 부분을 챗GPT의 도움을 받으면서 챗GPT의 Assistant 능력에 또한 놀라게 되었다.)</code></pre></li>
<li>이지우<pre><code>이번 기회에 처음부터 설계, 구현해보면서 지금까지 배운 내용을 어떤 부분에 어떻게 적용할지 고민하면서 내것으로 만드는 시간이 되었던것 같습니다.
</code></pre></li>
</ul>
<p>```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mongoDB & jQuery]]></title>
            <link>https://velog.io/@esjw_/mongoDB-jQuery</link>
            <guid>https://velog.io/@esjw_/mongoDB-jQuery</guid>
            <pubDate>Thu, 20 Jun 2024 16:15:44 GMT</pubDate>
            <description><![CDATA[<h3 id="날짜-추가">날짜 추가</h3>
<p>enter.html</p>
<pre><code class="language-html">&lt;div class=&quot;mb-3&quot;&gt;
  &lt;label class=&quot;form-label&quot;&gt;작성일&lt;/label&gt;
  &lt;input type=&quot;date&quot; class=&quot;form-control&quot; name=&quot;someDate&quot;&gt;
&lt;/div&gt;&lt;p&gt;&lt;/p&gt;</code></pre>
<p>server.js</p>
<pre><code class="language-js">mydb.collection(&#39;post&#39;).insertOne({
  title:req.body.title,
  content:req.body.content,
  date:req.body.someDate, // date 추가
})</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/654e3088-c887-4330-8b7e-9bb29ded64e5/image.png" alt=""></p>
<hr>
<h3 id="enterejs로-변경">enter.ejs로 변경</h3>
<pre><code class="language-js">app.get(&#39;/enter&#39;, function(req, res){
    res.render(&#39;enter.ejs&#39;);
});</code></pre>
<hr>
<h3 id="list-페이지-수정">list 페이지 수정</h3>
<pre><code class="language-html">&lt;thead&gt;
  &lt;tr&gt;         
    &lt;th&gt;제목&lt;/th&gt;
    &lt;th&gt;작성일&lt;/th&gt;
    &lt;th&gt;삭제&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
  &lt;% for(let i=0; i &lt; data.length; i++){ %&gt;
  &lt;tr&gt;         
    &lt;td&gt;&lt;%= data[i].title %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;%= data[i].date %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;button class = &#39;delete btn btn-outline-danger&#39; &gt;삭제&lt;/button&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;% } %&gt;
&lt;/tbody&gt;    </code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/88445a7c-98c4-48ef-a0cd-35ff9207a774/image.png" alt=""></p>
<hr>
<h2 id="jquery">jQuery</h2>
<pre><code class="language-html">&lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;
    $(document).ready(function(){
        alert();
    });
&lt;/script&gt;</code></pre>
<p><code>ready()</code>를 이용하여 문서가 모두 뜨면 콜백함수 실행</p>
<pre><code>&lt;head&gt;
&lt;script&gt;
    $(document).ready(function(){
        btn.addEventListener(&#39;click&#39;, function(){
            alert();
        });
    });
&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;
    hi
    &lt;button id=&quot;btn&quot;&gt;전송&lt;/button&gt;
&lt;/body&gt;</code></pre><p>이를 jQuery 사용하도록 변경</p>
<pre><code class="language-html">&lt;script&gt;
    $(document).ready(function(){
        // btn.addEventListener(&#39;click&#39;, function(){
        //    alert();
        // }); 
        $(&#39;button&#39;).click(function(){
            alert();
        });
    });
&lt;/script&gt;</code></pre>
<p>버튼이 하나만 있을 경우에는 문제 없음</p>
<p>버튼이 여러개가 있을 경우에 id나 class 사용</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/36e89e95-4ed5-45fe-b010-460d79cd07fc/image.png" alt=""></p>
<pre><code>&lt;head&gt;
&lt;script&gt;
    $(document).ready(function(){
        // btn.addEventListener(&#39;click&#39;, function(){
        //    alert();
        // }); 
        $(&#39;.a&#39;).click(function(){
            alert();
        });
    });
&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;
    hi
    &lt;button id=&quot;btn&quot; class=&quot;a&quot;&gt;전송&lt;/button&gt;
    &lt;button id=&quot;btn&quot; class=&quot;a&quot;&gt;전송&lt;/button&gt;
    &lt;button id=&quot;btn&quot; class=&quot;b&quot;&gt;전송&lt;/button&gt;
&lt;/body&gt;</code></pre><hr>
<h2 id="삭제-기능-추가">삭제 기능 추가</h2>
<p>server.js</p>
<pre><code class="language-js">app.post(&#39;/delete&#39;, (req, res) =&gt; {
    console.log(req.body, &#39;삭제 완료&#39;);
});</code></pre>
<p>list.ejs</p>
<pre><code class="language-ejs">&lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
        $(&#39;.delete&#39;).click(function(){
            $.ajax({
                type:&#39;post&#39;,
                url:&#39;/delete&#39;,
                data:{_id:&#39;66731326c95262081745adf7&#39;}
            })
            .done(function(result){
                console.log(result);
            })
            .fail(function(xhr, textstatus, err){
                console.log(err);
            })
        });
    &lt;/script&gt;</code></pre>
<p>콘솔에 삭제 id 값이 잘 넘어갔는지 확인
DB에는 아직 적용 안됨</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/64ef6f21-07a5-4103-80e6-284df6454d7a/image.png" alt=""></p>
<h3 id="원하는-id-가져오기">원하는 id 가져오기</h3>
<p>데이터셋 속성으로 </p>
<pre><code class="language-html">&lt;tr&gt;         
    &lt;td&gt;&lt;%= data[i].title %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;%= data[i].date %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;button data-id = &quot;&lt;%=data[i]._id%&gt;&quot; class = &#39;delete btn btn-outline-danger&#39; &gt;삭제&lt;/button&gt;&lt;/td&gt;
&lt;/tr&gt;

...
&lt;script&gt;
$(&#39;.delete&#39;).click(function(e){
    let sid = e.target.dataset.id;
    $.ajax({
        type:&#39;post&#39;,
        url:&#39;/delete&#39;,
        data:{_id:sid}
    })
  ...
&lt;/script&gt;</code></pre>
<hr>
<h3 id="mongodb-delete-추가">mongoDB delete 추가</h3>
<p>server.js</p>
<pre><code class="language-js">app.post(&#39;/delete&#39;, (req, res) =&gt; {
    // console.log(req.body, &#39;삭제 완료&#39;);
    mydb.collection(&#39;post&#39;)
        .deleteOne(req.body)
        .then(result =&gt; {
            console.log(&#39;삭제 완료&#39;);
            res.status(200).send();
        })
        .catch(err =&gt; {
            console.log(err);
        });
});</code></pre>
<p>아까 받아온 id로 delete 해도 적용이 안됨
ObjectId 형식으로 바꾸어 적용해주어야 함</p>
<p><code>npm i objectid</code></p>
<pre><code class="language-js">const ObjId = require(&#39;mongodb&#39;).ObjectId;

...

app.post(&#39;/delete&#39;, (req, res) =&gt; {
    console.log(req.body._id);  // 문자열
    req.body._id = new ObjId(req.body._id);
    console.log(req.body._id);  // 객체

    mydb.collection(&#39;post&#39;)
        .deleteOne(req.body)
        .then(result =&gt; {
            console.log(&#39;삭제 완료&#39;);
            res.status(200).send();
        })
        .catch(err =&gt; {
            console.log(err);
        });
});</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/32165644-ba08-4160-a9a0-b6cca6db119e/image.png" alt=""></p>
<p>이제 db에서 확인해 보면 삭제되는 것이 확인됨</p>
<p>/list 페이지에서 확인하기 위해서는 새로고침 해야 함</p>
<hr>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9ef830d4-f871-47a4-8779-92716bbfc935/image.png" alt=""></p>
<p>같은 것을 가리키는 것의 변수명은 모두 같게 맞추기 권장</p>
<hr>
<h3 id="상태코드-이용">상태코드 이용</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/a4dab532-835b-4d64-979f-2d12e571da6e/image.png" alt=""></p>
<p>200 코드를 반환되면 list.ejs의 script에서 .done이 실행됨</p>
<pre><code class="language-js">.done(function(result){
    location.reload();  // 현재 주소 다시 요청
})</code></pre>
<h4 id="새로고침-방법">새로고침 방법</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/17489d0b-6020-4aad-ba5d-d0c5231d0f4e/image.png" alt=""></p>
<p>이제 삭제시 바로 목록에서 사라짐
/list 요청을 서버쪽에 다시 보내고 받아오는 방식임
요청 넘기는건 xhr로 비동기적으로 작업중인데 화면 갱신은 리로드 방식(동기)
-&gt; 이것은 의도와 맞지 않음</p>
<hr>
<h3 id="비동기로-수정하기">비동기로 수정하기</h3>
<p>삭제될 <code>&lt;tr&gt;</code> 부분을 찾아서 삭제되어야 함</p>
<h4 id="dom-tree">DOM Tree</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/ca98e08c-f61d-4fa5-9846-6efced1249dd/image.png" alt=""></p>
<p>button이 눌린 이벤트가 발생한 td의 tr을 찾음(parent)</p>
<pre><code class="language-js">$(&#39;.delete&#39;).click(function(e){
    console.log(this);           
});</code></pre>
<p>삭제버튼 클릭 시 발생하는 이벤트의 this를 출력해보면 button이 나옴</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/8f57e62d-035d-480f-b1ae-dd25696553ce/image.png" alt=""></p>
<pre><code class="language-js">$(&#39;.delete&#39;).click(function(e){
    let _id = e.target.dataset._id;
    let item = $(this);
    $.ajax({
        type:&#39;post&#39;,
        url:&#39;/delete&#39;,
          data:{_id}  // _id:_id를 함축적으로 표현
    })
    .done(function(result){
        // location.reload();  // 현재 주소 다시 요청
        item.parent(&#39;td&#39;).parent(&#39;tr&#39;).remove();
    })
    .fail(function(xhr, textstatus, err){
        console.log(err);
    })
});</code></pre>
<hr>
<h3 id="삭제-실패-예외-처리">삭제 실패 예외 처리</h3>
<p>server.js</p>
<pre><code class="language-js">.catch(err =&gt; {
    console.log(err);
    res.status(500).send();
});</code></pre>
<p>list.ejs</p>
<pre><code class="language-js">.fail(function(xhr, textstatus, err){
    console.log(xhr, textstatus, err);
})</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/afaaf9ed-a694-4356-8b09-91152e3f4c29/image.png" alt=""></p>
<p>server.js에서 collection 이름을 잘못 주는 방식으로 에러 발생시키기
-&gt; 버튼 클릭 시 db는 삭제가 안되는데 tr은 삭제됨
-&gt; catch가 수행되지 않음
-&gt; <code>throw err;</code>로 작성해도 똑같음
-&gt; <strong>없는 collection 이름을 넣으면 새로 생성해버림!</strong></p>
<p>collection 이름을 빼버리면 에러 발생함
<img src="https://velog.velcdn.com/images/esjw_/post/e5a1f73f-fdf5-49d1-bf80-5c464f616a52/image.png" alt=""></p>
<p>log로 내용을 찍지 않고 <code>alert(err)</code> 실행 시</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/a21ed590-d90d-4bfc-a191-defcd99ff2ad/image.png" alt=""></p>
<hr>
<h3 id="내용-페이지-생성">내용 페이지 생성</h3>
<p>list.ejs 복사해서 content.ejs 생성 후 수정</p>
<pre><code class="language-html">&lt;h1&gt;상세내용&lt;/h1&gt;
&lt;h2&gt;제목 : ...&lt;/h2&gt;
&lt;h3&gt;내용 : ...&lt;/h3&gt;
&lt;h4&gt;작성일 : 년월일&lt;/h4&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/093328b9-f342-4224-bdfa-77621859d8fb/image.png" alt=""></p>
<hr>
<h3 id="시멘틱-url-방식">시멘틱 url 방식</h3>
<p>글마다 글번호를 url 뒤에 붙여서 페이지를 각각 나눠주기</p>
<p>req 객체</p>
<ul>
<li>header/body/params 형태</li>
<li>네트워크 상에서는 아니고 객체로 만들어지면</li>
</ul>
<pre><code class="language-js">app.get(&#39;/content/:_id&#39;, (req, res) =&gt; {
    console.log(req.params._id);
    res.render(&#39;content.ejs&#39;);
});</code></pre>
<hr>
<h3 id="db-파라미터-검색">db 파라미터 검색</h3>
<pre><code class="language-js">app.get(&#39;/content/:_id&#39;, (req, res) =&gt; {
    mydb.collection(&#39;post&#39;)
        .findOne({_id:new ObjId(req.params._id)})
        .then(result =&gt; {
            res.render(&#39;content.ejs&#39;);
        })
        .catch(err =&gt; {
            res.status(500).send();
        });
});</code></pre>
<p>fineOne에서 deleteOne과 다르게 body가 아닌 객체로 넘기는 이유</p>
<ul>
<li>get 방식에서는 body가 비어있음</li>
<li>id 값은 body가 아닌 params에 들어 있음</li>
</ul>
<hr>
<h3 id="리스트에-링크">리스트에 링크</h3>
<p><code>&lt;a&gt;</code>는 무조건 get방식</p>
<p>list.ejs</p>
<pre><code class="language-ejs">&lt;tbody&gt;
    &lt;% for(let i=0; i &lt; data.length; i++){ %&gt;
    &lt;tr&gt;         
        &lt;td&gt;&lt;a href = &#39;/content/&lt;%= data[i]._id %&gt;&#39;&gt;&lt;%= data[i].title %&gt;&lt;/td&gt;
        &lt;td&gt;&lt;%= data[i].date %&gt;&lt;/td&gt;
        &lt;td&gt;&lt;button data-a = &quot;b&quot; data-_id = &quot;&lt;%=data[i]._id%&gt;&quot; class = &#39;delete btn btn-outline-danger&#39; &gt;삭제&lt;/button&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;% } %&gt;
&lt;/tbody&gt; </code></pre>
<p>제목 부분에 <code>&lt;a&gt;</code> 추가</p>
<hr>
<h3 id="리스트에-db-나타내기">리스트에 db 나타내기</h3>
<p>server.js</p>
<pre><code class="language-js">app.get(&#39;/content/:_id&#39;, (req, res) =&gt; {
    mydb.collection(&#39;post&#39;)
        .findOne({_id:new ObjId(req.params._id)})
        .then(result =&gt; {
            res.render(&#39;content.ejs&#39;, {data:result});
        })
        .catch(err =&gt; {
            res.status(500).send();
        });
});</code></pre>
<p>render 시 data로 result(_id) 넘기기</p>
<p>content.ejs</p>
<pre><code class="language-html">&lt;h1&gt;상세내용&lt;/h1&gt;
&lt;h2&gt;제목 : &lt;%= data.title %&gt;&lt;/h2&gt;
&lt;h3&gt;내용 : &lt;%= data.content %&gt;&lt;/h3&gt;
&lt;h4&gt;작성일 : &lt;%= data.date %&gt;&lt;/h4&gt;</code></pre>
<hr>
<h3 id="content-페이지-수정">content 페이지 수정</h3>
<p>content.ejs</p>
<pre><code>&lt;div class = &quot;container mt-4&quot;&gt;
    &lt;div class=&quot;card&quot; style=&quot;width: 100%;&quot;&gt;
        &lt;div class=&quot;card-header&quot;&gt;
            &lt;h4 class=&quot;card-title&quot;&gt;&lt;%= data.title %&gt;&lt;/h4&gt;
        &lt;/div&gt;
        &lt;div class=&quot;card-body&quot;&gt;
            &lt;p class=&quot;card-text&quot;&gt;&lt;%= data.content %&gt;&lt;/p&gt;
            &lt;p class=&quot;card-subtitle mb-2&quot;&gt;작성일 : &lt;%= data.date %&gt;&lt;/p&gt;
            &lt;a href=&quot;#&quot; class=&quot;card-link&quot;&gt;수정하기&lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</code></pre><p>부트스트랩 적용 &amp; 수정하기 추가</p>
<hr>
<h3 id="수정기능-추가">수정기능 추가</h3>
<p>라우터 추가</p>
<pre><code class="language-js">app.get(&#39;/edit&#39;, (req,res)=&gt;{
    res.render(&#39;edit.ejs&#39;);
})</code></pre>
<p>수정하기 클릭 시 넘어가도록 content.ejs에 수정
<code>&lt;a href=&quot;/edit&quot; class=&quot;card-link&quot;&gt;수정하기&lt;/a&gt;</code></p>
<h4 id="작성되어-있던-데이터-전달">작성되어 있던 데이터 전달</h4>
<p>시멘틱 url 방식으로 전달</p>
<pre><code class="language-js">app.get(&#39;/edit/:_id&#39;, (req,res)=&gt;{
    console.log(req.params._id);
    mydb.collection(&#39;post&#39;)
        .findOne({_id:new ObjId(req.params._id)})
        .then(result =&gt; {
            res.render(&#39;edit.ejs&#39;, {data:result});
        })
        .catch(err =&gt; {
            res.status(500).send();
        });
})</code></pre>
<p>위에서 작성했던 content 부분과 render 파일명만 다름</p>
<p>edit.ejs 수정</p>
<pre><code>&lt;div class=&quot;mb-3&quot;&gt;
    &lt;label for=&quot;exampleFormControlInput1&quot; class=&quot;form-label&quot;&gt;제목&lt;/label&gt;
    &lt;input value=&quot;&lt;%= data.title %&gt;&quot; name=&quot;title&quot; type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;exampleFormControlInput1&quot;&gt;
&lt;/div&gt;

&lt;div class=&quot;mb-3&quot;&gt;
    &lt;label for=&quot;exampleFormControlTextarea1&quot; class=&quot;form-label&quot;&gt;내용&lt;/label&gt;
    &lt;textarea name=&quot;content&quot; class=&quot;form-control&quot; id=&quot;exampleFormControlTextarea1&quot; rows=&quot;3&quot;&gt;
        &lt;%= data.content %&gt;
    &lt;/textarea&gt;
&lt;/div&gt;

&lt;div class=&quot;mb-3&quot;&gt;
    &lt;label class=&quot;form-label&quot;&gt;작성일&lt;/label&gt;
    &lt;input value=&quot;&lt;%= data.date %&gt;&quot; type=&quot;date&quot; class=&quot;form-control&quot; name=&quot;someDate&quot;&gt;
&lt;/div&gt;&lt;p&gt;&lt;/p&gt;</code></pre><p>input 태그에는 value 속성으로 값 넣어주고 textarea는 태그 사이에 작성</p>
<p>content.ejs</p>
<pre><code>&lt;a href=&quot;/edit/&lt;%= data._id %&gt;&quot; class=&quot;card-link&quot;&gt;수정하기&lt;/a&gt;</code></pre><h4 id="수정-저장">수정 저장</h4>
<p>edit.ejs</p>
<pre><code class="language-html">&lt;form action=&quot;/edit&quot; method=&quot;post&quot;&gt;</code></pre>
<p>post 방식으로 라우터 추가</p>
<p>server.js</p>
<pre><code class="language-js">app.post(&#39;/edit&#39;, (req, res) =&gt; {
    console.log(req.body);
})</code></pre>
<p>몽고db에 update 반영
<code>updateOne({조건}, {변경항목})</code></p>
<pre><code class="language-js">app.post(&#39;/edit&#39;, (req, res) =&gt; {
    console.log(req.body);
    mydb.collection(&#39;post&#39;)
        .updateOne(
            {_id:new ObjId(req.body._id)},
            {$set:{title:req.body.title, content:req.body.content, date:req.body.someDate } }
        )
        .then()
        .catch(err =&gt; {
            res.status(500).send();
        });
});</code></pre>
<p>_id는 edit.ejs 안넘어옴
form 태그 안에 name:_id로 주어진 값이 없어서
-&gt; 히든 타입으로 생성</p>
<pre><code>&lt;input type=&quot;hidden&quot; name=&quot;_id&quot; value=&quot;&lt;%= data._id %&gt;&quot;&gt;

&lt;input type=&quot;text&quot; name=&quot;_id&quot; value=&quot;&lt;%= data._id %&gt;&quot; style=&quot;display:none&quot;&gt;
이렇게도 가능</code></pre><p>.then에 redirect로 /list 불러오게</p>
<pre><code class="language-js">app.post(&#39;/edit&#39;, (req, res) =&gt; {
    console.log(req.body);
    mydb.collection(&#39;post&#39;)
        .updateOne(
            {_id:new ObjId(req.body._id)},
            {$set:{title:req.body.title, content:req.body.content, date:req.body.someDate } }
        )
        .then(result =&gt; {
            console.log(&#39;수정완료&#39;);
            res.redirect(&#39;/list&#39;);
        })
        .catch(err =&gt; {
            res.status(500).send();
        });
});</code></pre>
<h4 id="redirect-대신-render를-해도-되는가">redirect 대신 render를 해도 되는가?</h4>
<p>render를 하려면 list.ejs에 넘길 data: result의 result에 수정된 data가 넘어가야해서 find()를 다시 해서 넘겨야 해서 복잡
-&gt; 위에 작성했던 get방식으로 요청하여 redirect로 불러오기</p>
<hr>
<h3 id="성능-향상">성능 향상</h3>
<p>get방식의 /edit</p>
<p>content에는 이미 데이터가 다 있는 상태
수정하기 버튼을 눌렀을 때 _id만 가지고 옴
db에 그 값을 가지고 가서 edit.ejs에 필요한 것을 불러와서 사용함</p>
<p>수정하기 화면에 작성되어 있던 데이터 그대로 띄우는 것이니 db에 갔다오지 않고 화면에 있던 데이터를 바로 req에 있는 data를 edit.ejs에 넘겨 렌더링 하게 하면 db에 가서 find()가 생략됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mongoDB]]></title>
            <link>https://velog.io/@esjw_/mongoDB</link>
            <guid>https://velog.io/@esjw_/mongoDB</guid>
            <pubDate>Wed, 19 Jun 2024 14:14:07 GMT</pubDate>
            <description><![CDATA[<h2 id="mongodb">mongoDB</h2>
<p><img src="https://velog.velcdn.com/images/esjw_/post/df8dad15-2160-4d0b-9b08-5c0e23b50571/image.png" alt=""></p>
<p><code>데이터베이스 - 컬렉션 - 도큐먼트</code></p>
<p><code>컬렉션</code>: RDB의 테이블과 비슷한 개념
<code>도큐먼트</code>: RDB의 rows와 비슷한 개념</p>
<p>mongoDB 웹 사용</p>
<hr>
<h3 id="생성">생성</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/4ef6ea88-b397-4503-908d-0b45c49ad3cf/image.png" alt=""></p>
<p>Databas Access - Add New Database User</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/0d345f92-f126-4b17-90b0-87eb5ed7e0d2/image.png" alt=""></p>
<p>DEPLOYMENT/Database - build a cluster</p>
<p>생성 후 DEPLOYMENT/Database - Browse Colletions</p>
<h4 id="데이터베이스-추가">데이터베이스 추가</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/1b13c0e6-420b-4ea7-8582-747631995494/image.png" alt=""></p>
<h4 id="컬렉션-추가">컬렉션 추가</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/c2193824-d75f-4769-9d2a-99411b09f2c1/image.png" alt=""></p>
<h4 id="insert-documnet">Insert Documnet</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9ad96285-71db-4681-9643-8914b9ba246d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/49a2bf37-b9cb-465d-92f2-dead893c1dbf/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/d5ff0e57-32f5-4e9f-a763-5143cc87a885/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/7fc8247b-3e2a-41b0-80c9-b595205b5049/image.png" alt=""></p>
<p>여기에서 UPDATE, DELETE도 가능</p>
<hr>
<h3 id="연결">연결</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/620212ce-bba0-4074-8682-0a6f25d5e77a/image.png" alt=""></p>
<p>Connect에서 Drivers 선택</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/c82fad5e-f8b4-4b9a-871f-382cda3a2914/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/69fef942-0375-4894-97be-5a19c59a8b15/image.png" alt=""></p>
<pre><code>npm install mongodb

mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0</code></pre><p>vscode에서 폴더 생성 후</p>
<pre><code>npm init
npm install mongodb</code></pre><p>mongodb 드라이버에서 mongodb 객체 생성 하여 전달받은 url로 클라우드의 mongodb와 내 장치를 연결함
-&gt; mongodb 상의 web은 이제 필요 없음</p>
<pre><code class="language-js">const mongoclient = require(&#39;mongodb&#39;).MongoClient;
const url = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`; // 연결 url

mongoclient.connect(url)
    .then(client =&gt; {
        console.log(&#39;몽고DB 접속 성공&#39;);
    });
</code></pre>
<hr>
<h3 id="데이터-읽어오기">데이터 읽어오기</h3>
<pre><code class="language-js">const mongoclient = require(&#39;mongodb&#39;).MongoClient;
const url = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`; // 연결 url

let mydb;

mongoclient.connect(url)
    .then(client =&gt; {
        console.log(&#39;몽고DB 접속 성공&#39;);
        mydb = client.db(&#39;myboard&#39;);
        mydb.collection(&#39;post&#39;).find().toArray().then(result =&gt;{
            console.log(result);
        })
    });
</code></pre>
<p><code>mydb.collection(&#39;post&#39;).find().toArray()</code>
collection이 된 객체에서 find로 값을 가져와 배열로 받아옴 -&gt; Promise로 받아와짐</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/291ffc59-10d6-4e8b-b83c-97eb4c3e2b60/image.png" alt=""></p>
<hr>
<h3 id="서버에서-접근하기">서버에서 접근하기</h3>
<pre><code class="language-js">const mongoclient = require(&#39;mongodb&#39;).MongoClient;
const url = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`; // 연결 url

let mydb;

mongoclient.connect(url)
    .then(client =&gt; {
        console.log(&#39;몽고DB 접속 성공&#39;);
        mydb = client.db(&#39;myboard&#39;);
        mydb.collection(&#39;post&#39;).find().toArray()
            .then(result =&gt;{
                console.log(result);
            });
    });

const express = require(&#39;express&#39;);
const app = express();

app.listen(8080, function(){
    console.log(&#39;8080 server ready...&#39;);
})</code></pre>
<p>위처럼 작성 시 mongo에 접근하는 것과 서버 부분이 다른 구간으로 작성되어 있어서 db에 접근하지 않은 상태로 서버로 넘어가거나 할 수 있음
-&gt; .then 안에서 db로 사용할 내용을 작성해주어야 함</p>
<pre><code class="language-js">const mongoclient = require(&#39;mongodb&#39;).MongoClient;
const url = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`; // 연결 url

let mydb;
const express = require(&#39;express&#39;);
const app = express();

mongoclient.connect(url)
    .then(client =&gt; {
        console.log(&#39;몽고DB 접속 성공&#39;);
        mydb = client.db(&#39;myboard&#39;);
        mydb.collection(&#39;post&#39;)
            .find()
            .toArray()
            .then(result =&gt;{
                console.log(result);
            });
        app.listen(8080, function(){
            console.log(&#39;8080 server ready...&#39;);
        });
    });</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/0cb48604-dac7-4646-b602-ab07fb024292/image.png" alt=""></p>
<h3 id="입력-페이지-생성">입력 페이지 생성</h3>
<pre><code class="language-js">const mongoclient = require(&#39;mongodb&#39;).MongoClient;
const url = `mongodb+srv://admin:1234@cluster0.gnyth4u.mongodb.net/?retryWrites=true&amp;w=majority&amp;appName=Cluster0`; // 연결 url

let mydb;
const express = require(&#39;express&#39;);
const app = express();

mongoclient.connect(url)
    .then(client =&gt; {
        console.log(&#39;몽고DB 접속 성공&#39;);
        mydb = client.db(&#39;myboard&#39;);

        app.listen(8080, function(){
            console.log(&#39;8080 server ready...&#39;);
        });
    })
    .catch((err) =&gt; {
        console.log(err);
    });

app.get(&#39;/list&#39;, function(req, res){
    mydb.collection(&#39;post&#39;)
        .find()
        .toArray()
        .then(result =&gt;{
            console.log(result);
            res.send(result);
    });
})</code></pre>
<pre><code class="language-js">app.get(&#39;/enter&#39;, function(req, res){
    res.sendFile(__dirname + &#39;/enter.html&#39;);
});</code></pre>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;utf-8&quot; /&gt;
        &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot; /&gt;
        &lt;meta name=&quot;description&quot; content=&quot;&quot; /&gt;
        &lt;meta name=&quot;author&quot; content=&quot;&quot; /&gt;
        &lt;title&gt;404 Error - SB Admin&lt;/title&gt;
        &lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH&quot; crossorigin=&quot;anonymous&quot;&gt;
        &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/styles.css&quot;/&gt;
        &lt;script src=&quot;https://use.fontawesome.com/releases/v6.3.0/js/all.js&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
        &lt;style&gt;
            body {
                background-color: #f8f9fa;
                font-family: &#39;Segoe UI&#39;, Tahoma, Geneva, Verdana, sans-serif;
            }
            .form-container {
                max-width: 500px;
                margin: 50px auto;
                padding: 30px;
                background-color: #ffffff;
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
                border-radius: 10px;
            }
            .form-label {
                font-weight: bold;
                color: #333;
            }
            .form-control {
                border-radius: 5px;
            }
            .btn-custom {
                background-color: #007bff;
                color: white;
                border: none;
                border-radius: 5px;
                padding: 10px 20px;
                cursor: pointer;
                transition: background-color 0.3s ease;
            }
            .btn-custom:hover {
                background-color: #0056b3;
            }
        &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;

        &lt;form action=&quot;/save&quot; method=&quot;post&quot;&gt;
            &lt;div class=&quot;form-container&quot;&gt;
                &lt;div class=&quot;mb-3&quot;&gt;
                    &lt;label for=&quot;exampleFormControlInput1&quot; class=&quot;form-label&quot;&gt;제목&lt;/label&gt;
                    &lt;input name=&quot;title&quot; type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;exampleFormControlInput1&quot; &gt;
                &lt;/div&gt;
                &lt;div class=&quot;mb-3&quot;&gt;
                    &lt;label for=&quot;exampleFormControlTextarea1&quot; class=&quot;form-label&quot;&gt;내용&lt;/label&gt;
                    &lt;textarea name=&quot;content&quot; class=&quot;form-control&quot; id=&quot;exampleFormControlTextarea1&quot; rows=&quot;3&quot;&gt;&lt;/textarea&gt;
                &lt;/div&gt;
                &lt;button class=&quot;btn btn-custom&quot; type=&quot;submit&quot;&gt;저장&lt;/button&gt;
            &lt;/div&gt;
        &lt;/form&gt;


        &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9e4cd75e-3a30-428a-8d0e-240344f65937/image.png" alt=""></p>
<hr>
<h3 id="입력한-데이터-서버로-보내기">입력한 데이터 서버로 보내기</h3>
<h4 id="get-방식">get 방식</h4>
<pre><code class="language-html">&lt;form action=&quot;/save&quot; method=&quot;get&quot;&gt;
  &lt;div class=&quot;form-container&quot;&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
      &lt;label for=&quot;exampleFormControlInput1&quot; class=&quot;form-label&quot;&gt;제목&lt;/label&gt;
      &lt;input type=&quot;email&quot; class=&quot;form-control&quot; id=&quot;exampleFormControlInput1&quot; placeholder=&quot;name@example.com&quot;&gt;
    &lt;/div&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
      &lt;label for=&quot;exampleFormControlTextarea1&quot; class=&quot;form-label&quot;&gt;내용&lt;/label&gt;
      &lt;textarea class=&quot;form-control&quot; id=&quot;exampleFormControlTextarea1&quot; rows=&quot;3&quot;&gt;&lt;/textarea&gt;
    &lt;/div&gt;
    &lt;button class=&quot;btn btn-custom&quot; type=&quot;submit&quot;&gt;저장&lt;/button&gt;
  &lt;/div&gt;
&lt;/form&gt;</code></pre>
<h4 id="post-방식">post 방식</h4>
<pre><code class="language-html">&lt;form action=&quot;/save&quot; method=&quot;post&quot;&gt;
  &lt;div class=&quot;form-container&quot;&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
      &lt;label for=&quot;exampleFormControlInput1&quot; class=&quot;form-label&quot;&gt;제목&lt;/label&gt;
      &lt;input name=&quot;title&quot; class=&quot;form-control&quot; id=&quot;exampleFormControlInput1&quot;&gt;
    &lt;/div&gt;
    &lt;div class=&quot;mb-3&quot;&gt;
      &lt;label for=&quot;exampleFormControlTextarea1&quot; class=&quot;form-label&quot;&gt;내용&lt;/label&gt;
      &lt;textarea name=&quot;content&quot; class=&quot;form-control&quot; id=&quot;exampleFormControlTextarea1&quot; rows=&quot;3&quot;&gt;&lt;/textarea&gt;
    &lt;/div&gt;
    &lt;button class=&quot;btn btn-custom&quot; type=&quot;submit&quot;&gt;저장&lt;/button&gt;
  &lt;/div&gt;
&lt;/form&gt;</code></pre>
<pre><code class="language-js">app.post(&#39;/save&#39;, function(req, res){
  console.log(&#39;저장완료&#39;);
})</code></pre>
<hr>
<h3 id="서버에서-데이터-읽어오기">서버에서 데이터 읽어오기</h3>
<p>실행 시 브라우저는 계속 로딩 중
-&gt; 값을 가져오지 못하고 있음</p>
<p>콜백 함수의 req에 데이터가 포함되어 있음
post 방식의 데이터는 <code>body-parser</code> 통해서 읽어 와야 함</p>
<p><code>npm install body-parser</code></p>
<pre><code class="language-js">const bodyParser = require(&#39;body-parser&#39;);
app.use(bodyParser.urlencoded({extended:true}));

app.post(&#39;/save&#39;, function(req, res){
  console.log(&#39;저장완료&#39;);
  console.log(req);
})</code></pre>
<hr>
<h3 id="입력-값-db에-저장">입력 값 DB에 저장</h3>
<pre><code class="language-js">app.post(&#39;/save&#39;, function(req, res){
  //console.log(&#39;저장완료&#39;);
  //console.log(req);

  // 몽고DB에 저장
  mydb.collection(&#39;post&#39;).insertOne(
    {title : req.body.title,
     content : req.body.content})
      .then(result =&gt; {
        console.log(result);
        console.log(&#39;데이터 추가 성공&#39;);
  });
  res.send(&#39;데이터 추가 성공&#39;);
});</code></pre>
<pre><code class="language-js">단일 도큐먼트 또는 다수의 도큐먼트 모두 삽입
collection(&#39;컬렉션 이름&#39;).insert()    

단일 도큐먼트 삽입
collection(&#39;컬렉션 이름&#39;).insertOne()

다수의 도큐먼트 삽입
collection(&#39;컬렉션 이름&#39;).insertMany()

하나의 단위는 { }
다수의 경우는 { } 여러 개를 [ ]로 묶어서</code></pre>
<hr>
<h3 id="목록창-생성">목록창 생성</h3>
<p>list.html</p>
<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
    &lt;title&gt;Home&lt;/title&gt;
    &lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65&quot; crossorigin=&quot;anonymous&quot;&gt;
  &lt;/head&gt;

  &lt;body&gt;
    &lt;nav class=&quot;navbar navbar-expand-lg bg-light&quot;&gt;
      &lt;div class=&quot;container-fluid&quot;&gt;
        &lt;a class=&quot;navbar-brand&quot; href=&quot;#&quot;&gt;Navbar&lt;/a&gt;
        &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#navbarNav&quot; aria-controls=&quot;navbarNav&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&gt;
          &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
        &lt;/button&gt;
        &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarNav&quot;&gt;
          &lt;ul class=&quot;navbar-nav&quot;&gt;
            &lt;li class=&quot;nav-item&quot;&gt;
              &lt;a class=&quot;nav-link active&quot; aria-current=&quot;page&quot; href=&quot;#&quot;&gt;Home&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class=&quot;nav-item&quot;&gt;
              &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Features&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class=&quot;nav-item&quot;&gt;
              &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Pricing&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class=&quot;nav-item&quot;&gt;
              &lt;a class=&quot;nav-link disabled&quot;&gt;Disabled&lt;/a&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/nav&gt;

    &lt;table class = &quot;table table-hover table-striped text-center container mt-4&quot; style = &quot;border: 1px solid;&quot;&gt;
      &lt;thead&gt;
        &lt;tr&gt;         
          &lt;th&gt;제목&lt;/th&gt;
          &lt;th&gt;작성일&lt;/th&gt;
          &lt;th&gt;삭제&lt;/th&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;

      &lt;tbody&gt;
        &lt;tr&gt;         
          &lt;td&gt;...&lt;/td&gt;
          &lt;td&gt;...&lt;/td&gt;
          &lt;td&gt;&lt;button class = &#39;delete btn btn-outline-danger&#39; &gt;삭제&lt;/button&gt;&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;            
    &lt;/table&gt;

    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-js">app.get(&#39;/list&#39;, function(req, res){
    mydb.collection(&#39;post&#39;)
        .find()
        .toArray()
        .then(result=&gt;{
            console.log(result);
            res.sendFile(__dirname + &#39;/list.html&#39;);
        });
});</code></pre>
<hr>
<h3 id="템플릿-엔진">템플릿 엔진</h3>
<blockquote>
<p>동적인 결과를 정적인 파일에 담음</p>
</blockquote>
<p><code>res.sendFile(__dirname + &#39;/list.html&#39;);</code>로는 동적인 페이지가 나올 수 없음</p>
<p><code>npm i ejs</code></p>
<pre><code class="language-js">app.set(&#39;view engin&#39;, &#39;ejs&#39;);    // 템플릿 엔진 설정

...

// res.sendFile(__dirname + &#39;/list.html&#39;);
res.render(&#39;list.ejs&#39;);</code></pre>
<p>views 폴더가 기본 작업 환경
views 폴더 안에 .ejs 파일 생성</p>
<p><code>render()</code>함수로
반복되는 부분을 ejs 문법으로 for문으로 작성하고 이것을 html로 변환시켜줌</p>
<h4 id="렌더링할-데이터-전달">렌더링할 데이터 전달</h4>
<p><code>render(렌더링할 파일, 파일에 전달할 데이터)</code></p>
<pre><code class="language-js">// res.sendFile(__dirname + &#39;/list.html&#39;);
res.render(&#39;list.ejs&#39;, {data : result});</code></pre>
<h4 id="ejs-문법">ejs 문법</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/0920b106-9470-4530-810d-124226b5f8b5/image.png" alt=""></p>
<p>&lt;% %&gt; 스크립트릿
&lt;%= %&gt; 익스프레션</p>
<pre><code class="language-ejs">&lt;table class = &quot;table table-hover table-striped text-center container mt-4&quot; style = &quot;border: 1px solid;&quot;&gt;
        &lt;thead&gt;
            &lt;tr&gt;         
                &lt;th&gt;제목&lt;/th&gt;
                &lt;th&gt;내용&lt;/th&gt;
                &lt;th&gt;삭제&lt;/th&gt;
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            &lt;% for(let i=0; i &lt; data.length; i++){ %&gt;
            &lt;tr&gt;         
                &lt;td&gt;&lt;%= data[i].title %&gt;&lt;/td&gt;
                &lt;td&gt;&lt;%= data[i].content %&gt;&lt;/td&gt;
                &lt;td&gt;&lt;button class = &#39;delete btn btn-outline-danger&#39; &gt;삭제&lt;/button&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;% } %&gt;
        &lt;/tbody&gt;            
    &lt;/table&gt;</code></pre>
<p>ejs엔진 (png, jsp, asp, php)
server side render</p>
<p>html
client side render</p>
<hr>
<p><a href="https://velog.io/@jhyun_k/%EC%84%9C%EB%B2%84%EC%82%AC%EC%9D%B4%EB%93%9C%EB%A0%8C%EB%8D%94%EB%A7%81-vs-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%82%AC%EC%9D%B4%EB%93%9C%EB%A0%8C%EB%8D%94%EB%A7%81-SSR%EA%B3%BC-CSR">SSR/CSR</a></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/8f82afab-8de7-4548-85c8-4c4cdfd462a2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/1b0596b3-a282-478b-9086-ff3317da2a73/image.png" alt=""></p>
<p>뚫려있는 부분 뒤쪽에 있는것 앞으로 밀면서 채움</p>
<p>major G/C 자주 일어나면 퍼포먼스 ↓</p>
<p>minor G/C</p>
<p><a href="https://blog.bespinglobal.com/post/garbage-collection-1%EB%B6%80/">참고</a></p>
<p>eden에서 서바이브로 넘어가는 객체가 적게 만들어야함
-&gt; 분석, 설계가 잘 된 후에 코드 작성하는 것이 중요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MySQL 연동]]></title>
            <link>https://velog.io/@esjw_/sql-%EC%97%B0%EB%8F%99</link>
            <guid>https://velog.io/@esjw_/sql-%EC%97%B0%EB%8F%99</guid>
            <pubDate>Tue, 18 Jun 2024 15:42:01 GMT</pubDate>
            <description><![CDATA[<pre><code># npm init
# npm i express

# npm i node-mysql

# npm i mysql2</code></pre><p>mysql 접근권한 에러 발생 시 mysql2 드라이버 설치해보기</p>
<hr>
<h2 id="서버">서버</h2>
<p>server.js</p>
<pre><code class="language-js">const mysql = require(&#39;mysql&#39;);
const conn = mysql.createConnection({
    host: &#39;localhost&#39;,
    user: &#39;jwbook&#39;,
    password:&#39;1234&#39;,
    database: &#39;myboard&#39;,
}); // 연결 설정
conn.connect(); // MySQL 연결 완료

//require: import 같은 의미 (라이브러리 가져오기)
const express=require(&#39;express&#39;);
const app=express();

app.use(express.static(&#39;public&#39;));  // 미들웨어 설정 static resource를 다이렉트로 찾음

app.listen(8080, function(){
    console.log(&quot;8080 server ready...&quot;);
});

// app.get(&quot;/&quot;, function (req, res) {
//     res.sendFile(__dirname + &quot;/public/index.html&quot;);
// });  // 미들웨어 설정하면 라우터가 필요 없어짐

app.get(&#39;/list&#39;, function(req,res) {
    const rows=conn.query(&#39;select * from post&#39;, function(err, rows, fields){ // err 있으면 1 없으면 rows 가져옴
        if(err) {
            console.log(err);
        } else {
            console.log(rows); // rows를 블럭 안에서만 사용하도록 해야됨
            res.send(rows);
        }
    });   

    // res.send(rows); // 이렇게 받아오면 안됨
});</code></pre>
<hr>
<h2 id="클라이언트">클라이언트</h2>
<p>my.js</p>
<pre><code class="language-js">testBtn.addEventListener(&#39;click&#39;, async function(){ // await을 사용하기 위해 async
    // 직접 사용 코드
    // var xhttp = new XMLHttpRequest();
    // xhttp.onreadystatechange = function() {
    //     if (this.readyState == 4 &amp;&amp; this.status == 200) {
    //     // Typical action to be performed when the document is ready:
    //     //document.getElementById(&quot;demo&quot;).innerHTML = xhttp.responseText;
    //     console.log(xhttp.responseText);
    //     }
    // };
    // xhttp.open(&quot;GET&quot;, &quot;/list&quot;, true);
    // xhttp.send();

    // 간접 사용
    let resObj = await fetch(&#39;/list&#39;);
    let data = await resObj.json();
    //console.log(data);
    let displayData = 
    `&lt;thead&gt;
        &lt;td&gt;ID&lt;/td&gt;
        &lt;td&gt;Title&lt;/td&gt;
        &lt;td&gt;writer&lt;/td&gt;
        &lt;td&gt;created&lt;/td&gt;
    &lt;/tead&gt;
    &lt;tbody&gt;`;
    data.forEach((item, index) =&gt; { // item이 있을 때 까지 반복 (동기함수)
        displayData +=
        `&lt;tr&gt;
            &lt;td&gt;${item.id}&lt;/td&gt;
            &lt;td&gt;${item.title}&lt;/td&gt;
            &lt;td&gt;${item.profile_id}&lt;/td&gt;
            &lt;td&gt;${item.created}&lt;/td&gt;
        &lt;/tr&gt;`
    });

    displayData += `&lt;/tbody&gt;`;

    document.getElementById(&quot;datatablesSimple&quot;).innerHTML = displayData;    // displayData를 datatablesSimple에 HTML로 넣어줌 (innerText는 Text로 넣어줌)
});</code></pre>
<p><a href="https://blog.naver.com/shdlsdo/220590134013">XMLHttpRequest 사용 시 readyState == 4 의미</a></p>
<p>fetch: 내장되어 있는 XHR을 사용할 수 있게 해주는 내장 함수(동기함수)
제대로 수행 됐을 때 단계적으로 .then 실행</p>
<p>.then을 여러개 쓰는 것 보다 await 써서 fetch 수행되어 나온 결과를 변수에 담을 때 까지 기다림
반드시 await는 async 블럭 안에서만 사용 가능</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/dafcffbb-2f43-4573-aea8-948e22898134/image.png" alt=""></p>
<p>아직 결과 값이 안담김 (비동기) -&gt; await 사용해야됨</p>
<h3 id="과제">과제</h3>
<p>동적인 내용에 modal 띄우려면 script 사용하는게 적당함
클릭되었을 때 실행될 function 작성</p>
<p>id를 기준으로 modal이 뜨기 전에 fetch 호출로 id에 대한 content 값을 가져오기
route 안에서 <code>select content from post where id=1</code></p>
<hr>
<h2 id="에러-처리">에러 처리</h2>
<pre><code class="language-js">function(err, rows){
  try{

  }catch(err){

  }
}</code></pre>
<p>SQL로 db에 에러 발생 시</p>
<ol>
<li>err를 send(err)로 보내기</li>
<li>err로 다시 err를 발생(throw err)시켜 웹 서버가 에러코드를 브라우저에 보냄</li>
</ol>
<p><a href="https://velog.io/@nemo/throw">throw 문으로 에러 지정</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OOSD & Mysql]]></title>
            <link>https://velog.io/@esjw_/Mysql</link>
            <guid>https://velog.io/@esjw_/Mysql</guid>
            <pubDate>Mon, 17 Jun 2024 15:32:40 GMT</pubDate>
            <description><![CDATA[<h2 id="oosd-process">OOSD Process</h2>
<blockquote>
<p>OOSD Process; Object-Oriented Software Development Process
객체 지향 소프트웨어 개발 과정</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/esjw_/post/55648561-15c6-4915-8fd5-c6e11564b504/image.png" alt=""></p>
<p>workflows: 해야할 일
activities: woker가 구체적으로 할 일
artifacts: 산출물</p>
<p>산출물을 어떤 형태로 만들지에 대한 툴이 필요함</p>
<hr>
<h3 id="workflow">workflow</h3>
<ol>
<li>요구사항 수집</li>
<li>요구사항 분석</li>
<li>아키텍쳐 수립</li>
<li>설계</li>
<li>구현</li>
<li>테스트</li>
<li>배치</li>
</ol>
<hr>
<h3 id="장점">장점</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/c1f56453-acd0-431f-ae79-db0566bd078b/image.png" alt=""></p>
<p>조직이 하는 일과 소프트웨어는 같다고 볼 수 있음</p>
<ul>
<li>조직에서 팀별로 분리하여 작업할 경우 각 작업은 독립성을 갖지 않고 서로 유기적인 관계를 가져야 함
= 소프트웨어에서 어떻게 협력 구조를 가지고 만들 것인가?</li>
</ul>
<p><code>다형성</code>
: 속도를 위해서 만들어진 처리 기법
: if를 줄이기 위함</p>
<hr>
<h3 id="project-stakeholders">project stakeholders</h3>
<p>프로젝트의 모든 참가자</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/750e09c0-e79c-49b2-8019-5fd2befff085/image.png" alt=""></p>
<p>Deployment specialist
: 소프트웨어 비용을 줄이기 위한 전략을 짜는 사람</p>
<hr>
<h3 id="모델링">모델링</h3>
<ul>
<li>단순화 시켜서 가시화를 위함</li>
<li>여러 각도에서 봐야 함</li>
<li><code>목표: 소통</code></li>
<li>언어 방식은 휘발성이기 때문에 가시화하기 위하여 문서화, 모델링 함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/esjw_/post/242d27a4-79b7-4fa0-bea4-dd6c76670d96/image.png" alt=""></p>
<ol>
<li>각자 가지고 있는 mental model에서 요구사항 모델을 끄집어내어 문서화</li>
<li>어마어마한 문서 중 기능적 요구사항/비기능적 요구사항에 대해 설계</li>
<li>두 가지를 합하여 솔루션 모델 제작</li>
<li>코드화</li>
</ol>
<hr>
<h3 id="uml">UML</h3>
<blockquote>
<p>시각 정보를 사용한 저장 매체를 표준화 한 도구
소통의 방법을 시각화하는 툴</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/esjw_/post/674d8646-3a8a-4055-99b1-d41fc896533a/image.png" alt=""></p>
<hr>
<p><img src="https://velog.velcdn.com/images/esjw_/post/82b4b220-2169-4759-9d33-620dcd635de0/image.png" alt=""></p>
<hr>
<h2 id="mysql">MySQL</h2>
<ul>
<li>DBMS</li>
<li>ctrl+Enter = 커서 라인 실행</li>
</ul>
<h3 id="생성">생성</h3>
<pre><code class="language-sql">create database myboard default character set utf8;

use myboard;

create table post(
id int(11) not null auto_increment,
title varchar(100) not null,
content text null,
created datetime not null,
writer varchar(100) null,
email varchar(100) null,
primary key(id)
);
// NULL은 생략 가능

desc post;    // 설정한 필드 이름, 타입 옵션 표시</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/5c79da0e-55ce-4dc7-b5d7-658a8e2677d3/image.png" alt=""></p>
<h3 id="삽입">삽입</h3>
<pre><code class="language-sql">insert into post(title, content, created, writer, email)
values(&#39;삶은&#39;, &#39;계란이다&#39;, NOW(), &#39;lee&#39;, &#39;lee@naver.com&#39;);

insert into post(title, content, created, writer, email)
values(&#39;위대하다&#39;, &#39;밥을 많이 먹어서&#39;, NOW(), &#39;lee&#39;, &#39;lee@naver.com&#39;);

insert into post(title, content, created, writer, email)
values(&#39;나의 성격유형&#39;, &#39;infj&#39;, NOW(), &#39;kim&#39;, &#39;kim@naver.com&#39;);

insert into post(title, content, created, writer, email)
values(&#39;가을바람&#39;, &#39;가을은 쓸쓸하다&#39;, NOW(), &#39;park&#39;, &#39;park@naver.com&#39;);

insert into post(title, content, created, writer, email)
values(&#39;언젠가부터&#39;, &#39;사람들과 이해관계가 힘들어지는 것 같다&#39;, NOW(), &#39;lee&#39;, &#39;lee@naver.com&#39;);
</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/bf337c80-aa9b-4025-bed0-b784714208e7/image.png" alt=""></p>
<h3 id="조회">조회</h3>
<pre><code class="language-sql">select id, title, content from post;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/e54d2e3b-66b1-4be6-869f-6741e611110f/image.png" alt=""></p>
<pre><code class="language-sql">select * from post where id&gt;2;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/685cde24-b238-400d-ae21-619721a28a25/image.png" alt=""></p>
<pre><code class="language-sql">select * from post where writer=&#39;lee&#39;;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/07ec0f8d-b76a-413d-b885-94b2ea0f3db1/image.png" alt=""></p>
<pre><code class="language-sql">select * from post order by id desc;// 내림차순
select * from post order by id asc;    // 오름차순

select * from post limit 2;    // 정렬된 내용 중 위에서 2건</code></pre>
<h3 id="수정">수정</h3>
<pre><code class="language-sql">update post set content = &#39;성격 파탄자&#39; where id = 3;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/a5b214b2-9a12-44b5-ae97-fa91eaeb660b/image.png" alt=""></p>
<h3 id="삭제">삭제</h3>
<pre><code class="language-sql">delete from post where id=2;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/ab011146-7d4a-43fe-b0a4-b22dada452af/image.png" alt=""></p>
<h3 id="테이블-분리">테이블 분리</h3>
<pre><code class="language-sql">rename table post to post_bk;    // 테이블명 변경

create table post(
id int(11) not null auto_increment,
title varchar(100) not null,
content text null,
created datetime not null,
profile_id int(11) default null,
primary key(id)
);

create table profile(
id int(11) not null auto_increment,
writer varchar(20) not null,
email varchar(100) default null,
primary key(id)
);</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9143e2a9-e2a2-4de2-8be8-04bc1fc42c33/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9c8c8979-5509-4615-95d0-084e258b968f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/388ed3a8-e3c6-4568-8b35-5b8e43ff5443/image.png" alt=""></p>
<p>위 보이는 테이블에서 더블 클릭 후 내용 작성도 가능
id는 자동 할당 됨</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/79a3f7bc-ba32-4a00-8453-3a75a8e52a6f/image.png" alt=""></p>
<pre><code class="language-sql">insert into post(title, content, created, profile_id)
values(&#39;삶은&#39;, &#39;계란이다&#39;, NOW(), 1);

insert into post(title, content, created, profile_id)
values(&#39;위대하다&#39;, &#39;밥을 많이 먹어서&#39;, NOW(), 1);

insert into post(title, content, created, profile_id)
values(&#39;나의 성격유형&#39;, &#39;infj&#39;, NOW(), 2);

insert into post(title, content, created, profile_id)
values(&#39;가을바람&#39;, &#39;가을은 쓸쓸하다&#39;, NOW(), 3);

insert into post(title, content, created, profile_id)
values(&#39;언젠가부터&#39;, &#39;사람들과 이해관계가 힘들어지는 것 같다&#39;, NOW(), 1);</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/358b1fb3-7b42-4e65-a1a9-51a57d87b60f/image.png" alt=""></p>
<hr>
<h3 id="join">join</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/8d8fcc47-9e99-47b9-9dde-1848a38fa669/image.png" alt=""></p>
<p><a href="https://www.w3schools.com/sql/sql_join.asp">[참고내용]</a></p>
<pre><code class="language-sql">select *
from post
left join profile
on post.profile_id = profile.id;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/d78dd5ad-b8ee-4960-b8e9-fdbeffc7ee33/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/7611edc8-7eb6-435d-afa9-1dc4d6ba76c9/image.png" alt=""></p>
<p>모든 칼럼이 다 나와서 가독성이 떨어짐</p>
<pre><code class="language-sql">select id, title, content, created, writer, email
from post
left join profile
on post.profile_id = profile.id;</code></pre>
<p>실행 시 오류 발생
select의 id가 어떤 테이블의 id인지 모호함</p>
<pre><code class="language-sql">select post.id, title, content, created, writer, email
from post
left join profile
on post.profile_id = profile.id;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/84767b74-6872-4108-b1f2-af07f240a81c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트(3) & Node.js(1)]]></title>
            <link>https://velog.io/@esjw_/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B83-Node.js1</link>
            <guid>https://velog.io/@esjw_/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B83-Node.js1</guid>
            <pubDate>Fri, 14 Jun 2024 15:57:42 GMT</pubDate>
            <description><![CDATA[<h2 id="객체">객체</h2>
<h3 id="생성-방법">생성 방법</h3>
<pre><code class="language-js">// 방법1
let dic = new Object();    

dic.boy = &#39;소년&#39;;
dic.girl = &#39;소녀&#39;;
dic.friend = &#39;친구&#39;;


// 방법 2
let dic = {
    boy: &#39;소년&#39;,
    girl: &#39;소녀&#39;,
    friend: &#39;친구&#39;
};


// 자바와 달리 멤버 추가/삭제/수정이 자유로움
dic.apple = &#39;사과&#39;;
dic.ten = 10;

delete dic.girl;

dic.boy = &#39;청년&#39;;

console.log(dic.boy);
console.log(dic[&#39;boy&#39;]);    // 이렇게도 가능
// !&#39;&#39;로 씌워주어야 함
// 프로퍼티에 띄어쓰기 넣기 가능</code></pre>
<h3 id="객체-데이터-관리-방법">객체 데이터 관리 방법</h3>
<blockquote>
<p>const 키워드로 선언한 객체의 프로퍼티 값을 추가하거나 삭제, 변경이 가능함</p>
</blockquote>
<ul>
<li>객체 자체의 주소 값은 const 형식으로 재할당이 불가능</li>
<li>하지만 객체의 데이터는 다른 주소에 저장되어있는 값이기 때문에 객체 주소에는 영향을 미치지 않으므로 추가, 변경, 삭제가 가능함
<img src="https://velog.velcdn.com/images/esjw_/post/3d8f4930-abae-413f-a2db-1ac50362d66e/image.png" alt=""></li>
</ul>
<hr>
<h3 id="여러-객체-생성-방식">여러 객체 생성 방식</h3>
<pre><code class="language-js">const unit = {
    attack: function (weapon){
        return `${weapon}으로 공격한다.`;
    }
  /*attack (weapon){
        return `${weapon}으로 공격한다.`;
    }*/
  // 이렇게 표현도 가능
};

console.log(unit);  // 객체
console.log(unit.attack);   // 객체 프로퍼티
console.log(unit.attack(&#39;총&#39;)); // 객체 메서드 호출 결과값</code></pre>
<p>객체 생성할 때 프로퍼티 키를 대괄호로 표시할 경우</p>
<pre><code class="language-js">rl.question(&#39;무조건 천원! 상품 입력? &#39;, function(obj){
    let basket = {
        [obj] : &quot;1000원&quot;,
    }
    console.log(basket[obj]);
    // 무조건 1000원 출력

    rl.close();
})</code></pre>
<p>프로퍼티 키와 값의 이름이 같아도 됨</p>
<pre><code class="language-js">let id = &#39;jamsu&#39;;
let pw = &#39;1111&#39;;

let user = {
    id : id,
    pw : pw
  // 이때 값 부분 생략 가능
  /*id,
      pw*/
}

console.log(user.id);
console.log(user.pw);

// 하나씩 꺼내서 출력하기
for(let info in user){
    console.log(`${info} : ${user[info]}`);
}</code></pre>
<hr>
<h2 id="화살표-함수">화살표 함수</h2>
<blockquote>
<p>수행 컨텍스트가 달라질 때 화살표 함수 사용으로 편하게 사용 가능</p>
</blockquote>
<pre><code class="language-js">
function plus(a, b){
  return a + b;
}

// 이름 없는 function을 변수에 할당
let plus = (a, b) =&gt;{
    return a + b;
}

// 매개변수 하나일 경우 괄호 생략 가능
let plus = a =&gt; a+1;    

// 매개변수가 없는 경우 빈괄호 표시
let plus = () =&gt; 2+1;    </code></pre>
<pre><code class="language-js">let obj = {
    myVar: &#39;foo&#39;,

    myFunc: function(){
        let self = this;    // object의 주소가 self에 할당됨
        console.log(this.myVar);    // 수행 컨텍스트가 obj

        setTimeout(function(){
            console.log(this.myVar);// 수행 컨텍스트가 window
        }, 1000);

          setTimeout(function(){
            console.log(self.myVar);// 수행 컨텍스트가 obj
        }, 1000);

      // self 변수에 주소 할당하여 사용하는것을 함수로 만든 것이 bind(this)
          setTimeout(function(){
            console.log(this.myVar);
        }.bind(this), 1000);        // 수행 컨텍스트가 obj

      // 수행 컨텍스트가 달라질 때 화살표 함수 사용으로 편하게 사용 가능
          setTimeout(() =&gt; {  // 화살표 함수, function() 객체가 만들어질 때 this를 바인딩
            console.log(this.myVar);// 수행 컨텍스트가 obj
        }, 1000);
    }
}
obj.myFunc();   // foo   undefined   foo</code></pre>
<p>어떤 객체에 귀속되는 함수(메소드) 마들 때는 프로퍼티에 할당되는 함수를 function 키워드로 만들기</p>
<p>나머지는 콜백함수는 화살표 함수로 만드는게 베스트</p>
<pre><code class="language-js">let obj = {
    myVar: &#39;foo&#39;,

    myFunc: function(){
        console.log(this.myVar);    // 수행 컨텍스트가 obj

          setTimeout(() =&gt; {  
            console.log(this.myVar);// 수행 컨텍스트가 obj
        }, 1000);
    }
}

obj.myFunc(); </code></pre>
<hr>
<h2 id="비구조화-할당">비구조화 할당</h2>
<blockquote>
<p>배열의 요소나 프로퍼티를 변수에 할당하여 사용</p>
</blockquote>
<pre><code class="language-js">const color=[&#39;red&#39;, &#39;green&#39;, &#39;blue&#39;];
let [r, g, b] = color;
// [r, g, b]=[&#39;red&#39;, &#39;green&#39;, &#39;blue&#39;];

console.log(r);
console.log(g);
console.log(b);

// 변수값 변경
[b, g, r] = [r, g, b]</code></pre>
<p>배열에서 <code>...</code>을 붙인 매개변수에 요소 여러개 넣기 가능</p>
<pre><code class="language-js">const [a, b, ...rest] 
= [&#39;C#&#39;, &#39;javascript&#39;, &#39;python&#39;, &#39;react&#39;, &#39;C++&#39;];
console.log(a);
console.log(b);
console.log(rest.length);    // 3
console.log(rest[0]);
console.log(rest[1]);
console.log(rest[2]);
console.log(rest);    // [ &#39;python&#39;, &#39;react&#39;, &#39;C++&#39; ]</code></pre>
<pre><code class="language-js">// 배열 결합
const arr1 = [&#39;C#&#39;, &#39;javascript&#39;];
const arr2 = [&#39;python&#39;, &#39;react&#39;, &#39;C++&#39;];
const arr3 = [...arr1, ...arr2];

console.log(arr3);    
// [ &#39;python&#39;, &#39;react&#39;, &#39;C++&#39; ]

console.log([arr1, arr2]); 
/*  [ [ &#39;C#&#39;, &#39;javascript&#39; ],
      [ &#39;python&#39;, &#39;react&#39;, &#39;C++&#39; ] ]*/</code></pre>
<p>객체의 프로퍼티</p>
<pre><code class="language-js">let {id, pw, name} 
    = {id: &#39;a&#39;, pw: &#39;b&#39;, name: &#39;c&#39;, age: 30};
// 이때 age는 사용할 수 없음</code></pre>
<hr>
<h2 id="프로미스">프로미스</h2>
<pre><code class="language-js">function c(){
    console.log(&#39;c&#39;);
}
function b(){
    console.log(&#39;b&#39;);
}
function a(){
    console.log(&#39;a&#39;);
}

setTimeout(a, 3000);    // 3초 뒤에 a 실행
setTimeout(b, 2000);    // 2초 뒤에 b 실행
setTimeout(c, 1000);    // 1초 뒤에 c 실행
// 예상: 3초 뒤 a 출력, 2초 뒤 b 출력, 1초 뒤 c 출력(동기)
// 실제 실행 결과: c b a 1초 간격으로 출력 (비동기)

setTimeout(function(){
    console.log(&#39;a&#39;);
}, 3000);
setTimeout(function(){
    console.log(&#39;b&#39;);
}, 2000);
setTimeout(function(){
    console.log(&#39;c&#39;);
}, 1000);
// 같은 결과</code></pre>
<p>-&gt; a b c 순서로 나오게 하려면?</p>
<pre><code class="language-js">setTimeout(function(){
    console.log(&#39;a&#39;);
    setTimeout(function(){
        console.log(&#39;b&#39;);
        setTimeout(function(){
            console.log(&#39;c&#39;);
        }, 1000);
    }, 2000);
}, 3000);</code></pre>
<p>콜백이 너무 많고 가독성 떨어짐</p>
<blockquote>
<p>프로미스를 사용하여 의도하는 순서대로 실행 가능</p>
</blockquote>
<pre><code class="language-js">var pro1 = new Promise(function(resolve, reject){   
  // resolve: 정상, reject: 비정상
    if(true)    // 서버에 갔다온 시점 / 결과 올때까지 기다리고 결과가 있으면 then으로 넘어감
        resolve(1); // then 안의 함수 실행됨
    else    reject();
});

pro1
    .then(function(value){ // true일 때
        console.log(value);     // 후속작업
    })
    .catch(function(){  // false일 때
        console.log(2);
    });</code></pre>
<p>flag 변수로 매개변수에 따라 resolve/reject결과 출력</p>
<pre><code class="language-js">function f(flag){
    return new Promise((resolve, reject) =&gt; {   
        if(flag){
          resolve(1); 
        }else{
          reject(&#39;처리 오류&#39;);
        }
    });
}

const prom = f(false);    

prom
    .then(function(value){ 
        console.log(value);     
    })
    .catch(function(errMsg){  
        console.log(errMsg);
    });</code></pre>
<p>프로미스를 이용하여 a b c 순서로 나오도록</p>
<pre><code class="language-js">function f(flag, time){
    return new Promise((resolve, reject) =&gt; {   
        if(flag){
            setTimeout(resolve, time);
        }else{
            reject(&#39;처리 오류&#39;);
        }
    });
}

f(true, 3000)
    .then(function(){ 
        console.log(&#39;a&#39;);     
        return f(true, 2000);   // then을 한번 더 쓰기 위함
    })
    .then(function(){
        console.log(&#39;b&#39;);
        return f(true, 1000);
    })
    .then(function(){
        console.log(&#39;c&#39;);
    })
    .catch(function(errMsg){  
        console.log(errMsg);
    });</code></pre>
<hr>
<h1 id="nodejs">Node.js</h1>
<ul>
<li>v8 엔진 사용</li>
<li>비동기 이벤트 기반의 런타임</li>
</ul>
<pre><code class="language-js">const http = require(&#39;http&#39;);   // core 라이브러리에 있는 http 모듈 가져옴

const server = http.createServer((req, res)=&gt;{
    res.statusCode = 200;   // 정상
    res.setHeader(&#39;Content-Type&#39;, &#39;text/html&#39;);    // html 사용 위함
    res.end(&#39;&lt;h1&gt;Hello World&lt;h1&gt;&#39;);
});

server.listen(3000, &quot;127.0.0.1&quot;, () =&gt; {
    console.log(&quot;server ready...&quot;);
});</code></pre>
<ul>
<li><p>express 모듈을 깔아두어서 위 코드를 작성하지 호출하여 사용</p>
</li>
<li><p>라이브러리 매니저가 있으면 패키지를 가져오기 편함</p>
</li>
<li><blockquote>
<p>npm로 install</p>
</blockquote>
</li>
<li><p>코드가 업데이트 될때마다 서버를 다시 켜줘야하는데 nodemon을 사용하면 코드 변경이 있을 때 자동으로 적용해줌</p>
</li>
</ul>
<pre><code class="language-js">const express = require(&#39;express&#39;);
const app = express();

app.listen(3000, function(){
    console.log(&#39;server ready...&#39;);
});</code></pre>
<p>get 요청 처리</p>
<pre><code class="language-js">app.get(&#39;/book&#39;, function(req,res){
    res.send(&#39;도서 목록 관련 페이지입니다.&#39;)
});</code></pre>
<hr>
<h2 id="실습">실습</h2>
<h3 id="서버-코드">서버 코드</h3>
<pre><code class="language-js">const express = require(&#39;express&#39;);
const app = express();

app.listen(3000, function(){
    console.log(&#39;server ready...&#39;);
});

app.get(&#39;/&#39;, function(req,res){
    res.sendFile(__dirname + &#39;/index.html&#39;);
});

app.get(&#39;/inputUser&#39;, function(req,res){
    res.sendFile(__dirname + &#39;/inputUser.html&#39;);
});

app.get(&#39;/userInfo&#39;, function(req,res){
    res.sendFile(__dirname + &#39;/userInfo.html&#39;);
});</code></pre>
<p>3개의 페이지 구성</p>
<h3 id="root">root</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/1affd49c-e809-42a5-bc0f-83d7771200fa/image.png" alt=""></p>
<p>Navbar의 <code>Home</code>, <code>회원 정보 등록</code> 클릭 시 페이지 이동이 가능하도록 <code>href=&quot;/&quot;</code>, <code>href=&quot;/inputUser&quot;</code> 속성 추가</p>
<ul>
<li>bootstrap에서 가져온 코드에는 <code>href=&quot;#&quot;</code>로 작성되어 있었음</li>
<li>클릭 시 실행내용은 없지만 페이지 최상단으로 이동한다는 의미</li>
<li>최상단으로도 이동하지 않게 하려면 <code>href=&quot;#none&quot;</code> 또는 <code>href=&quot;#;&quot;</code>로 작성</li>
</ul>
<h3 id="inputuser">inputUser</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/184cc324-c56a-4857-9c1d-5faa9035a1b1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/6d02658c-1912-477d-999e-db827107d477/image.png" alt=""></p>
<ul>
<li>입력값에 대한 유효성 검사는 넣지 않음</li>
<li>가입하기 버튼 클릭 시 등록 알림창 띄워짐</li>
<li>다시 작성 버튼 클릭 시 입력 내용 초기화</li>
</ul>
<pre><code class="language-html">&lt;form action=&quot;&quot;&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;개인정보 입력&lt;/legend&gt;
    이름:&lt;br&gt;
        &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;br&gt;&lt;br&gt;
    비밀번호:&lt;br&gt;
        &lt;input type=&quot;password&quot; name=&quot;pw&quot;&gt;&lt;br&gt;&lt;br&gt;
    E-mail:&lt;br&gt;
        &lt;input type=&quot;text&quot; name=&quot;email&quot;&gt;&lt;br&gt;&lt;br&gt;
    연락처:&lt;br&gt;
        &lt;select name=&quot;ntype&quot;&gt;
        &lt;option&gt;KT&lt;/option&gt;
        &lt;option&gt;SK&lt;/option&gt;
        &lt;option&gt;LGU+&lt;/option&gt;
        &lt;option&gt;자급제&lt;/option&gt;
        &lt;/select&gt; &lt;input type=&quot;text&quot; name=&quot;n1&quot; size=&quot;3&quot;&gt; - &lt;input type=&quot;text&quot; name=&quot;n2&quot; size=&quot;4&quot;&gt; - &lt;input type=&quot;text&quot; name=&quot;n3&quot; size=&quot;4&quot;&gt;&lt;br&gt;
    &lt;br&gt;
    성별 : &lt;input type=&quot;radio&quot; name=&quot;gender&quot; value=&quot;남성&quot;&gt; 남 &lt;input type=&quot;radio&quot; name=&quot;gender&quot; value=&quot;여성&quot;&gt; 여&lt;br&gt;
    &lt;br&gt;
    취미 : &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;운동&quot;&gt;운동 &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;독서&quot;&gt;독서
    &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;여행&quot;&gt;여행 &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;음악감상&quot;&gt;음악감상&lt;br&gt;
    &lt;br&gt;
    본인 소개:&lt;br&gt;
    &lt;textarea name=&quot;mes&quot; cols=&quot;100&quot; rows=&quot;3&quot;&gt;&lt;/textarea&gt;
    &lt;hr&gt;
    &lt;div style=&quot;text-align: center;&quot;&gt;
    &lt;input type=&quot;button&quot; id=&quot;submit&quot; value=&quot;가입하기&quot;&gt;

    &lt;input type=&quot;reset&quot; value=&quot;다시작성&quot;&gt;
    &lt;/div&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;
&lt;script&gt;
  function sub(){
    alert(&#39;등록되었습니다.&#39;);
    document.location = &quot;userInfo&quot;;
  }

  submit.addEventListener(&#39;click&#39;,sub);
&lt;/script&gt;</code></pre>
<ul>
<li><p>form 태그 안에 작성 할 경우 fieldset으로 내용 필드를 생성할 수 있음</p>
</li>
<li><p>이때 legend로 이 필드의 제목 작성 가능</p>
</li>
<li><p>radio: 단일선택</p>
</li>
<li><p>checkbox: 다중선택</p>
</li>
<li><p>textarea: 긴 내용 작성 가능한 칸</p>
</li>
<li><p>div 안에 style을 <code>text-align: center;</code>로 지정하여 가운데 정렬</p>
</li>
<li><p>form 태그 안에서 input type을 <code>reset</code>으로 지정하면 적은 내용 삭제 버튼 생성됨</p>
</li>
<li><p>가입하기 버튼 클릭 시 &#39;등록되었습니다.&#39; 문구가 띄워지고 userInfo 페이지로 넘어가도록 함수 작성</p>
</li>
<li><p>document.location으로 현재 페이지를 userInfo로 변경 가능</p>
</li>
</ul>
<h3 id="userinfo">userInfo</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/76bbe740-d00c-4c75-8bb7-39b24f239d55/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트(2)]]></title>
            <link>https://velog.io/@esjw_/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B82</link>
            <guid>https://velog.io/@esjw_/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B82</guid>
            <pubDate>Thu, 13 Jun 2024 14:52:50 GMT</pubDate>
            <description><![CDATA[<h2 id="문자열">문자열</h2>
<pre><code class="language-js">function account(userId){
    let savedUser=&#39;이은성&#39;;
    if(userId==savedUser){
        console.log(&#39;반갑습니다 &#39;, savedUser, &#39;님&#39;);
    } else{
        console.log(&#39;fail&#39;);
    }
}
account(&#39;이은성&#39;);</code></pre>
<ul>
<li>여기에서 userId와 savedUser는 완전히 같은 내용</li>
<li>문자열은 따로 객체를 생성하고 그 주소를 가리키도록 하게됨</li>
<li>String Literal Pool에 있는 c-value 엔진으로 같은 내용이 있는지 확인</li>
<li>같은 내용의 객체를 중복으로 생성하지 않고 저장공간 효율적으로 사용 가능</li>
<li>String Literal Pool은 Method area(구조)에 존재하여 프로그램이 종료될 때까지 상주하게 됨</li>
<li><blockquote>
<p>많이 사용되면 안된다 (static도)</p>
</blockquote>
</li>
<li>edan은 Instance atrea에 존재하여 지워질 수 있음</li>
</ul>
<pre><code class="language-js">String s1=&#39;java&#39;;    // String Literal Pool에 생성

String s2 = new String(&#39;java&#39;);    // edan에 생성

s1+=&#39;study&#39;    // edan에 새로 생성</code></pre>
<hr>
<h2 id="이벤트">이벤트</h2>
<h4 id="1-onclick-사용">1. onclick 사용</h4>
<pre><code class="language-html">&lt;body&gt;
     &lt;script&gt;
        function msg(no){
            alert(no);
        }
    &lt;/script&gt;

    &lt;button onclick=&quot;msg(&#39;이지우&#39;)&quot;&gt;전송&lt;/button&gt;
&lt;/body&gt;</code></pre>
<ul>
<li><code>button onclick=&quot;msg(&#39;이지우&#39;)&quot;&gt;전송&lt;/button&gt;</code>에서 msg()는 호출이 아니라 연결</li>
<li>호출은 버튼이 눌렸을 때 발생</li>
<li>이벤트 하나만 적용 가능</li>
</ul>
<h4 id="2-자바-스크립트로-onclick-속성-추가">2. 자바 스크립트로 onclick 속성 추가</h4>
<pre><code class="language-html">&lt;body&gt;
     &lt;script&gt;
        btn.onclick=    // btn이 정의되지 않아서 실행 안됨
        function msg(no){
            alert(no);
        }
    &lt;/script&gt;    

    &lt;button id=&quot;btn&quot;&gt;전송&lt;/button&gt; &lt;!-- 이 라인이 script 위에 있어야 함 --&gt;
&lt;/body&gt;</code></pre>
<h4 id="3-addeventlistener-사용">3. addEventListener 사용</h4>
<pre><code class="language-html">&lt;body&gt;
    &lt;button id=&quot;btn&quot;&gt;전송&lt;/button&gt;

    &lt;script&gt;
        function first(){
            alert(&#39;첫 번째 메세지&#39;);
        }
          // 이벤트 이름, 이벤트 핸들러 연결
        btn.addEventListener(&#39;click&#39;, first);
        btn.addEventListener(&#39;click&#39;, function(){
            alert(&#39;두 번째 메세지&#39;);
        });
    &lt;/script&gt;
&lt;/body&gt;</code></pre>
<pre><code class="language-html">&lt;input type=&quot;button&quot; id=&quot;btn&quot; value=&quot;전송&quot;&gt;
&lt;!-- input으로 작성하기 --&gt;
&lt;script&gt;
  btn.addEventListener(&#39;click&#39;, function(){
  alert(this.value); // this로 value 가져오기 가능
  });

&lt;/script&gt;</code></pre>
<ul>
<li>이벤트가 발생(호출)했을 때 이벤트는 엔진으로 실행되고 그때 정보를 매개변수로 전달해줌</li>
</ul>
<hr>
<h2 id="콜백-함수">콜백 함수</h2>
<pre><code class="language-js">function order(callback){ // callback은 function code
  callback();    // 나중에 호출됨
}

const coffe = function(){
    console.log(&#39;주문하신 아메리카노 나왔습니다&#39;);
};
setTimeout(coffe, 5000);    // 5초 뒤 실행</code></pre>
<p>setTimeout 형태</p>
<pre><code class="language-js">function setTime(f, time){
    time delay
    f();    // call back
}</code></pre>
<p>html과 연결하기</p>
<p>html</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;input type=&quot;button&quot; id=&quot;btn&quot; value=&quot;주문&quot;&gt;

    &lt;script src = &quot;evtcallback.js&quot;&gt;

    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>js</p>
<pre><code class="language-js">const coffe = function(){
    alert(&#39;주문하신 아메리카노 나왔습니다&#39;);
};

function order(){
    setTimeout(coffe, 5000);
}

btn.addEventListener(&#39;click&#39;, order);</code></pre>
<pre><code class="language-js">// 콜백 안에 콜백 넣기 가능
btn.addEventListener(&#39;click&#39;, function (){
    setTimeout(function(){
        alert(&#39;주문하신 아메리카노 나왔습니다&#39;);
    }, 5000);
});</code></pre>
<hr>
<h2 id="백틱">백틱(`)</h2>
<pre><code class="language-js">const name = &#39;이지우&#39;;
const age = &#39;25&#39;;
const height = 178.5;

// 템플릿 문자열
console.log(`My name is ${name}, My age is ${age}, My height is ${height}`);

// 백틱 안에서 엔터로 줄바꿈 가능
console.log(`My name is ${name}
My age is ${age}
My height is ${height}`);</code></pre>
<hr>
<h2 id="실습">실습</h2>
<p>test.js</p>
<pre><code class="language-js">let q1Answer = &#39;ISTJ&#39;;

function checkQ1(){
    if(a1.value == q1Answer)
        alert(&#39;정답입니다.&#39;);
    else
        alert(`틀렸습니다. 정답은 &#39;${q1Answer}&#39;입니다.`);
}</code></pre>
<p>test.html</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;p&gt;이지우님의 MBTI는?&lt;/p&gt;
            &lt;input type=&quot;text&quot; id=&quot;a1&quot; value=&quot;&quot;&gt;
            &lt;button onclick=&quot;checkQ1()&quot;&gt;제출&lt;/button&gt;
    &lt;script src=&quot;test.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[html5 & 자바스크립트]]></title>
            <link>https://velog.io/@esjw_/html5-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@esjw_/html5-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Wed, 12 Jun 2024 08:46:13 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Semantic
: 따로 스타일이나 디자인이 없음
: 자리 지정만 해주는 것
: 보이게 하기위한 태그가 아님</li>
</ul>
<hr>
<p>context = application</p>
<p><code>http://ip:port/context/resource</code></p>
<p>java sublet의 코드 한줄이 text
sublet 객체가 돌아가려면 보조 객체가 있어야하고 이 코드들은 눈에 보이지 않음
-&gt; container가 text 작성하여 run-time때 작성함
이때 작업을 context라고 함</p>
<hr>
<h2 id="자바스크립트">자바스크립트</h2>
<h3 id="객체-생성">객체 생성</h3>
<ol>
<li><p>객체리터럴 이용</p>
<pre><code class="language-javascript">var p1={mame:&quot;전은수&quot;}</code></pre>
</li>
<li><p>Object 생성자 이용</p>
<pre><code class="language-js">var p2=new Object();
p2.name=&quot;전은수&quot;;</code></pre>
</li>
<li><p>사용자 지정 생성자 이용</p>
<pre><code class="language-js">function Person(name){
this.name=name;
}
var p3=new Person(&quot;전은수&quot;);
</code></pre>
</li>
</ol>
<p>var Person=function(name){ 
  //이름이 없는 function(익명 함수)을 Person 변수에 담음
  this.name=name;
}
var p4=new Person(&quot;전은수&quot;);</p>
<pre><code>---

function

method : 소속


![](https://velog.velcdn.com/images/esjw_/post/22a7e98a-66a4-45b8-9f45-99a1b000461d/image.png)


- window
data: document, frames, history...
method: open(), close(), moveTo()...

- document
data: anchors, forms, images, links, location...
method: write()

- console
method: log()

```js
window.document.write() // 이렇게 써야하지만 
document.write() // js에서는 이렇게 써도 됨</code></pre><p><img src="https://velog.velcdn.com/images/esjw_/post/da22cc4f-e05f-43a2-bf51-4d1098e8c3e1/image.png" alt=""></p>
<p>script 안의 데이터는 모두 window의 data 영역에 올라가짐 <strong>(stack X)</strong></p>
<hr>
<pre><code class="language-html">&lt;script&gt;
  console.log(window.v1); 
  // 선언하지 않으면 undefined로 나옴
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt;
  v1=10; // var 선언 없으면 무조건 window것
  console.log(window.v1); //10
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt;
  v1; //var 연산자 없을 때는 할당 안되면 선언도 안됨
  console.log(window.v1);  // ReferenceError
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt; // js engine이 해석함 (최소한의 정렬 하고 해석)
  console.log(window.v1); 
  var v1=10; // 선언문을 맨 위로 올림
&lt;/script&gt;

&lt;script&gt;
  var v1; // 선언문을 맨 위로 올림
  console.log(window.v1); // undefined
  v1=10; 
  console.log(window.v1); // 10
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/esjw_/post/42d41645-e851-4712-9e9e-ad3c8659ca55/image.png" alt=""></p>
<pre><code class="language-html">&lt;script&gt;
  var v1=new Object();
  console.log(window.v1); //[object Object]
  console.log(v1.__proto__===Object.prototype); 
  // v1의 __proto__와 Object의 prototype이 완전히 같은가? -&gt; true
&lt;/script&gt;</code></pre>
<p>Object(): function 객체</p>
<pre><code class="language-html">&lt;script&gt;
  var v1=new Object();
  v1.name=&#39;aaa&#39;;
  console.log(v1.name);    // aaa
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt;
  function Person(){
  }
  var p1=new Person();
  console.log(p1);    // [object Object]
&lt;/script&gt;

원형/상속

</code></pre>
<pre><code class="language-js">객체 생성 후에 상위 형식으로 형변환하면 하위는 shadow 처리됨(shadow effect)

B a = new A(); // A로 생성하고 B로 지정함 (자동 형변환)</code></pre>
<pre><code class="language-js">function Person(name){
  this.name=name;
}
var p1=new Person(&#39;bbb&#39;);
console.log(p1.name);    // bbb

var p2=Person(&#39;bbb&#39;);    
// new 없이 function 이름으로 호출 -&gt; function 객체 생성, 리턴 값이 없음(할당X)

console.log(p2);    // undefined
console.log(p2.name);    //TypeError
console.log(window.name);    // bbb</code></pre>
<pre><code class="language-js">function Person(){
  this.eyes=2;
  this.nose=1;
}
var p1=new Person();
var p2=new Persion();
console.log(p1.eyes+&#39;:&#39;+p1.nose);    // 2:1
console.log(&#39;&lt;br&gt;&#39;);
console.log(p2.eyes+&#39;:&#39;+p2.nose);    // 2:1


p1, p2의 데이터를 공유하고 싶은 경우
function Person(){
}
Person.prototype.eyes=2;
Person.prototype.nose=1;
var p1=new Person();
var p2=new Persion();
console.log(p1.eyes+&#39;:&#39;+p1.nose);    // 2:1
console.log(&#39;&lt;br&gt;&#39;);
console.log(p2.eyes+&#39;:&#39;+p2.nose);    // 2:1

// 이때 p1의 eyes를 1로 바꿈
p1.eyes=1;
console.log(p1.eyes+&#39;:&#39;+p1.nose);    // 1:1
// 개별로 eyes data값이  생김
console.log(&#39;&lt;br&gt;&#39;);
console.log(p2.eyes+&#39;:&#39;+p2.nose);    // 2:1

Person.prototype.eyes=1 // 이 형태로 변경해야 함</code></pre>
<pre><code class="language-js">var p1={
  eyes: 3,
  nose: 4
}
console,log(p1.__proto__===Object.prototype);    // true
console.log(Object===p1.constructe);    // true
console.log(p1.hasOwnProperty(&#39;eyes&#39;));    // true</code></pre>
<pre><code class="language-js">// function에 hoisting 발생

fn(); // error: fn is not a function
var fn = function() {alert(&quot;test!&quot;);}    // function  코드를 실행하는 것
// var fn 부분만 위로 올라감

아래 방법이 오류 잘 발생 안함
fun2(); // ok
function fn2() {alert(&#39;test!&#39;);}
// 전체가 위로 올라감

</code></pre>
<hr>
<h3 id="유효성-검사">유효성 검사</h3>
<pre><code class="language-js">// id가 null이 아니고 unfined도 아닌 유효한 값이면 true 그렇지 않으면 null 반환
var result = id ?? null 
? &#39;로그인되었습니다.&#39;:&#39;아이디 또는 비번이 틀렸습니다.&#39;;</code></pre>
<hr>
<h3 id="입력-받아서-처리하기">입력 받아서 처리하기</h3>
<pre><code class="language-js">const readline = require(&#39;readline&#39;);

const rl=readline.createInterface({
    input : process.stdin,
    output: process.stdout 
});
rl.question(&#39;프로그래밍 언어 이름을 입력하시오,: &#39;,function(data){
    console.log(&#39;가장 좋아하는 프로그래밍 언어는 &#39; + data + &#39;입니다.&#39;);
      rl.close();
});
// question(메시지, 호출해서 어떤 작업을 할것인지)
// function은 호출된게 아니라 정의만 해둔 상태
// 어떤 조건을 만족하면 하게될 작업 (call back function)</code></pre>
<pre><code class="language-js">const readline = require(&#39;readline&#39;);

const rl=readline.createInterface({
    input : process.stdin,
    output: process.stdout 
});

rl.question(&quot;정수를 입력하시오,:&quot;, function(num){
    num=num%2;
    if(num){
        console.log(&quot;홀수 입니다&quot;);
    } else{
        console.log(&quot;짝수 입니다&quot;);
    }
    rl.close();
});</code></pre>
<hr>
<h3 id="자습내용-정리">자습내용 정리</h3>
<p><strong>HTML의 값 변경하기</strong></p>
<pre><code class="language-js">document.getElementById(&#39;demo&#39;).innerHTML=&#39;Hello JavaScript&#39;;
// demo라는 id의 객체 값을 변경한다

document.getElementById(&#39;myImage&#39;).src=&#39;pic_bulbon.gif&#39;;
// myImage라는 id의 src라는 속성값 변경

document.getElementById(&quot;demo&quot;).style.fontSize = &quot;35px&quot;;
// demo라는 id 객체의 폰트 사이즈 변경

document.getElementById(&quot;demo&quot;).style.display = &quot;none&quot;;
document.getElementById(&quot;demo&quot;).style.display = &quot;block&quot;;
// demo라는 id 객체의 display 변경 
// none: 안보이게, block: 보이게</code></pre>
<hr>
<p><strong>JS 위치에 따른 HTML에서 읽는 법</strong></p>
<pre><code class="language-html">&lt;script&gt;
document.getElementById(&quot;demo&quot;).innerHTML = &quot;My First JavaScript&quot;;
&lt;/script&gt;
// html 사이에 js 넣을 수 있음
// &lt;body&gt;, &lt;head&gt; 아무데나 넣어도 됨
// &lt;body&gt; 맨 하단에 작성하면 display speed가 향상됨

&lt;script src=&quot;myScript.js&quot;&gt;&lt;/script&gt;  
// 외부 .js 파일 삽입 </code></pre>
<p>외부에 JS 파일을 두었을 때 장점</p>
<ol>
<li>HTML과 분리해둘 수 있음</li>
<li>HTML과 JS의 가독성 향상</li>
<li>Cache로 저장된 JS 파일을 불러와 페이지 로드 속도가 빨라짐</li>
</ol>
<p>외부 참조 방법</p>
<ol>
<li>전체 URL 포함</li>
<li>파일 경로</li>
<li>경로 사용 X</li>
</ol>
<hr>
<p><strong>JS 출력</strong></p>
<ol>
<li><code>innerHTML</code>로 HTML 요소에 작성</li>
<li><code>document.write()</code>로 HTML 출력에 작성</li>
<li><code>window.alert()</code>로 경고 상자에 작성</li>
<li><code>console.log()</code>로 브라우저 콘솔에 작성</li>
</ol>
<pre><code class="language-html">&lt;p id=&quot;demo&quot;&gt;&lt;/p&gt;    // &lt;p id=&quot;demo&quot;&gt; 11 &lt;/p&gt;이 됨

&lt;script&gt;
document.getElementById(&quot;demo&quot;).innerHTML = 5 + 6;
&lt;/script&gt;

---

&lt;script&gt;
document.write(5 + 6);
&lt;/script&gt;

&lt;button type=&quot;button&quot; 
        onclick=&quot;document.write(5 + 6)&quot;&gt;
  Try it
&lt;/button&gt;
// 이때 버튼을 클릭하면 기존의 HTML이 모두 삭제됨
// test용으로만 사용하기

---

&lt;script&gt;
window.alert(5 + 6);
&lt;/script&gt;
// 경고 박스로 띄우기
// window 없이 alert(5+6);로 작성해도 됨

---

&lt;script&gt;
console.log(5 + 6);
&lt;/script&gt;

---

&lt;button onclick=&quot;window.print()&quot;&gt;Print this page&lt;/button&gt;
// 현재 창 출력하기
</code></pre>
<hr>
<p><strong>키워드</strong> </p>
<ul>
<li>var: 변수 선언</li>
<li>let: 블록 변수 선언</li>
<li>const: 블록 상수 선언</li>
<li>if: 조건에 따라 실행될 명령문 블록 표시</li>
<li>switch: 여러 경우에 따른 명령문 블록 표시</li>
<li>for: 반복될 블록 표시</li>
<li>function: 함수 선언</li>
<li>return: 함수 종료</li>
<li>try: 명령 블록에 대한 오류 처리 구현 </li>
</ul>
<hr>
<p><strong>JS 변수</strong></p>
<blockquote>
<p>변수에 숫자 넣었다가 문자열로 변경하기 가능</p>
</blockquote>
<p>선언 방법</p>
<ol>
<li>자동 선언</li>
<li>var</li>
<li>let</li>
<li>const</li>
</ol>
<pre><code class="language-js">// 자동 선언
x=5;
y=6;
z=x+y;


// var
var x=5;
var y=6;
var z=x+y;


// let
let x=5;
let y=6;
let z=x+y;


// const
const x=5;
const y=6;
const z=x+y;
// const로 선언 시 값/형 변경 불가능</code></pre>
<ul>
<li>변경되면 안되는 변수는 <code>const</code> 사용</li>
<li><code>const</code> 사용이 안될때 <code>let</code> 사용</li>
<li><code>var</code>은 오래된 브라우저도 지원</li>
</ul>
<pre><code class="language-js">+ 로 문자열 합치기

let x = 5+2+3; // 8
let y = &#39;John&#39; + &#39; &#39; + &#39;Doe&#39;; // John Doe

// 숫자를 하나라도 따옴표로 묶으면 그 뒤 숫자는 문자열로 처리됨
let z = &#39;5&#39;+2+3 // 523
let z = 2+3+&#39;5&#39; // 55</code></pre>
<hr>
<p><strong>let</strong></p>
<ul>
<li>블록 범위가 지정됨</li>
<li>사용하기 전에 미리 선언되어야 함</li>
<li>다시 선언할 수 없음</li>
</ul>
<pre><code class="language-js">{
  let x = 2;
  var y = 1;
}
// 여기에서 x 사용 못함
// var로 선언한 y는 전역변수로 사용 가능


let a = &#39;abc&#39;;
let a = 0;    // 재선언 불가능

var b = &#39;abc&#39;;
var b = 0;    // 가능</code></pre>
<table>
<thead>
<tr>
<th></th>
<th align="center">범위 지정</th>
<th align="center">재선언</th>
<th align="center">재할당</th>
<th align="center">hoisted</th>
<th align="center">binds this</th>
</tr>
</thead>
<tbody><tr>
<td>var</td>
<td align="center">N</td>
<td align="center">Y</td>
<td align="center">Y</td>
<td align="center">Y</td>
<td align="center">Y</td>
</tr>
<tr>
<td>let</td>
<td align="center">Y</td>
<td align="center">N</td>
<td align="center">Y</td>
<td align="center">N</td>
<td align="center">N</td>
</tr>
<tr>
<td>const</td>
<td align="center">Y</td>
<td align="center">N</td>
<td align="center">N</td>
<td align="center">N</td>
<td align="center">N</td>
</tr>
</tbody></table>
<hr>
<p><code>binds this</code>
: 객체 메서드를 콜백으로 사용할 때 this가 사라지는 문제
: 객체 메서드가 객체 내부가 아니라 다른 곳에서 전달되어 호출되면 this가 사라짐
: window에 객체가 없어서 undefined가 출력됨</p>
<pre><code class="language-js">let user = {
  firstName: &quot;John&quot;,
  sayHi() {
    alert(`Hello, ${this.firstName}!`);
  }
};

setTimeout(user.sayHi, 1000); // Hello, undefined!

setTimeout(function() {
  user.sayHi(); // Hello, John!
}, 1000);
</code></pre>
<hr>
<p><strong>const</strong></p>
<ul>
<li>재할당 불가</li>
<li>선언과 함께 할당해야됨</li>
</ul>
<pre><code class="language-js">배열에 사용 시
const cars = [&quot;Saab&quot;, &quot;Volvo&quot;, &quot;BMW&quot;];

// 값 변경 가능
cars[0] = &quot;Toyota&quot;;

// 값 추가 가능
cars.push(&quot;Audi&quot;);

// 재할당 불가능
cars = [&quot;Toyota&quot;, &quot;Volvo&quot;, &quot;Audi&quot;];    // ERROR</code></pre>
<hr>
<p><strong>데이터 형식</strong></p>
<pre><code class="language-js">// Numbers:
let length = 16;
let weight = 7.5;
let x = 34.00;    // 34
let y = 123e-5;   // 0.00123
let z = BigInt(&quot;123456789012345678901234567890&quot;);

// Strings:
let color = &quot;Yellow&quot;;
let lastName = &quot;Johnson&quot;;
let car = &quot;&quot;;

// Booleans
let x = true;
let y = false;

// Object:
const person = {firstName:&quot;John&quot;, lastName:&quot;Doe&quot;};

// Array object:
const cars = [&quot;Saab&quot;, &quot;Volvo&quot;, &quot;BMW&quot;];

// Date object:
const date = new Date(&quot;2022-03-25&quot;);</code></pre>
<hr>
<p><strong>function</strong></p>
<p>함수 호출 시 () 없이 함수명만 작성하면 함수가 반환됨</p>
<pre><code class="language-html">&lt;p id=&quot;demo&quot;&gt;&lt;/p&gt;

&lt;script&gt;
function toCelsius(f) {
  return (5/9) * (f-32);
}

let value = toCelsius;
document.getElementById(&quot;demo&quot;).innerHTML = value;
// function toCelsius(f) { return (5/9) * (f-32); }
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[클라이언트-서버 구조]]></title>
            <link>https://velog.io/@esjw_/%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@esjw_/%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Tue, 11 Jun 2024 15:52:08 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/esjw_/post/70314ec8-a9db-430a-8613-57ed36d520e7/image.jpg" alt=""></p>
<p><code>새로고침</code> - 요청을 다시 서버에 보내라는 의도
해당 주소의 리소스를 다시 요청</p>
<p><code>캐싱</code> - 이미 갖고왔던 정보는 새로고침 안함
똑같은 내용을 다시 가져오는 불편함을 해소하기 위함</p>
<p>강제 새로고침 - ctrl+새로고침</p>
<hr>
<h2 id="환경마다-다르게-보이게-하는-법">환경마다 다르게 보이게 하는 법</h2>
<ul>
<li>서버/도메인 네임 다르게 설정</li>
<li>도메인 같고 컨텍스트명에 따라 요청 구별</li>
<li>컨텍스트는 하나만 사용하고 컨트롤러 두기
컨트롤러 - 브라우저가 어디인지 확인하고 그거에 맞는 자원 리턴</li>
</ul>
<hr>
<h2 id="web1">Web1</h2>
<p>정적(static) 페이지 - HTML, CSS, js
<code>static 리소스</code>
: 한번 요청을 받으면 클라이언트쪽에 저장함
: 저장된 내용으로 화면에 그려내는 작업을 함
: 이미 저장된 내용과 다른 자원 요청이 발생하면 새로 받아와야 함 
-&gt; 이미 있던 내용 삭제됨</p>
<ul>
<li>다 지우고 새로 작성하기 때문에 시간이 걸려 화면이 정체되어 보일 수 있음(깜빡이는 현상)</li>
<li>네트워크 밴드위스(대역폭)
: 요청했을 때 얼마의 양이 가느냐
: 화면이 고정되어있는데 아주 작은 부분만 수정될 경우</li>
<li><blockquote>
<p>밴드위스 비용이 많이 듦</p>
</blockquote>
</li>
<li><blockquote>
<p>해결하기 위해 <code>XHR</code> 사용</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="web2">Web2</h2>
<p><code>XHR</code>
: XML HTTP Request
: js에서 요청 중 적은 데이터만 가져오도록 할 수 있음
: 1) 처음부터 다시 그리지 않기 위함
: 2) 네트워크 비용 절감을 위함
: request는 같지만 url에 마지막에 들어가는 리소스가 동적인 url이어야 함</p>
<pre><code>정적 : .html, .css, .js
동적 : getData(확장명X)</code></pre><p>일반적으로는 Web Server에서 해석 못함 (404)
-&gt; 컨트롤러를 만들어두고 컨트롤러로 넘길 수 있게해야 함
-&gt; 컨트롤러에는 if-else if문이 엄청 길게 코딩되어 있음(<code>컨트롤링</code>)</p>
<p><code>비즈니스</code>: DB에 접근해라 같은 명령</p>
<p>MVC 사용하지 않으면 불편함
why?
한 코드에 컨트롤링, model, view가 모아져 있어서 가독성이 낮아짐 -&gt; 유지보수 힘듦
-&gt; 비즈니스 컴포넌트 따로 두기</p>
<p>요청에따라 일하는 모델 클래스 분리
리퀘스트 객체에따라 컴팩트한 데이터만 제공
view 데이터가 text 기반으로 조그마한 데이터 제공 (xml)</p>
<p>XHR 객체로 요청 받아오는 동안에 화면 동작 안함 -&gt; 비동기로 처리하기
비동기 : 다른 화면대로 동작하고 XHR 객체 처리</p>
<hr>
<blockquote>
<p>Web1: 정적 site
Web2: XHR 비동적 처리
Web3: 블록체인</p>
</blockquote>
<hr>
<h2 id="web3">Web3</h2>
<p>웹서버에 클라이언트가 집중됨 (중앙집중)
-&gt; 사용자의 정보가 모두 서버에 저장됨
-&gt; 서버에서 권한을 가지고 클라이언트 제어
-&gt; 클라이언트가 종속적 입장</p>
<p>클라이언트(peer)끼리 데이터 사용 가능 (p2p)
최초 p2p 프로토콜 - <code>비트코인</code></p>
<p>보상이 없으면 네트워크에 참여 안함
보상 체계 구성 - 네트워크상에 데이터 공유 
-&gt; 데이터 저장소 비용 지불해야함
-&gt; 이 비용보다 보상이 많아야 됨
-&gt; 저장하는 사람에 대해서 보상을 해주자
-&gt; &quot;비트코인&quot;</p>
<p>시간 순서대로 데이터를 특정 시간만큼 모아서 데이터 작성 후 압축(해싱)
해싱 정보를 데이터에 포함시켜서 다시 해싱
다음 단위 시간동안 모아진 데이터를 포함시켜서 다시 해싱
-&gt; 데이터 묶음이 계속 늘어남
-&gt; <code>체인</code></p>
<h3 id="rdb와-블록체인-차이">RDB와 블록체인 차이</h3>
<p><strong>RDB</strong></p>
<ul>
<li>데이터 셰도 가능</li>
<li>변경 가능</li>
<li>중간에 추가 가능</li>
</ul>
<p><strong>블록체인</strong></p>
<ul>
<li>복호화가 안되는 압축 기술</li>
<li>연결된 데이터 중간에 삽입하거나 변경 불가능</li>
<li>항상 뒤에만 붙어야 함</li>
<li>변경하기 위해서는 변경사항 정보 추가해야함</li>
<li>삭제하기 위해서는 삭제되었다는 정보 추가</li>
<li>모든 사항을 insert로 수행</li>
<li>원본 데이터는 항상 남겨진 상태</li>
<li>이러한 정보(해시값)를 통해 해킹 상황 알아낼 수 있음</li>
<li>데이터의 무결성 보장</li>
</ul>
<hr>
<p>사용자들끼리 네트워크에 참여하여 데이터를 서로 제공하면서 무결성 확보하자
-&gt; 정보가 저장된 컴퓨터가 사라지면 데이터가 날아감
-&gt; 정보를 가지고 있는 사람끼리 결탁하여 조작할 수 있음
-&gt; 기밀성이 유지되지 않음
-&gt; 기밀성이 요구되어지는 정보는 p2p 네트워크에 참여하지 않아야 함</p>
<p>b2b를 사용해야 한다
: 전송 프로토콜만 http 사용
: 메시지 프로토콜은 soap</p>
<h3 id="b2b에-블록체인-활용">b2b에 블록체인 활용</h3>
<ul>
<li>이체를 농협에 하면 블록으로 남음</li>
<li>블록을 남길 머신을 고사양으로 구축</li>
<li>private/consortium 블록체인</li>
<li>하이퍼레저 패브릭</li>
<li>사용자들을 제한하기 위한 프로토콜을 만들거나 기존 프로토콜 활용하자</li>
<li><blockquote>
<p>Web3는 블록체인을 쉽게 할 수 있는 기술이 내장된 브라우저를 사용하는 시점 (아직 없음)</p>
</blockquote>
</li>
</ul>
<hr>
<p>클라이언트가 네이버 지도상에 직접적으로 데이터 가져오면 네이버 서버 비용 증가 -&gt; 다수 이용 목적이더라도 너무 많은 비용이 들어가기 때문에 손해 &amp; 공격받을 수 있음
-&gt; 파트너한테만 key 발급하여 사용 가능하게 하기</p>
<p>개발자 key가 있어도 완전 무료는 아니고 과금
request 건수에 따라 과금 + 비이상적 요청에 방어</p>
<hr>
<h2 id="architecture">Architecture</h2>
<p><img src="https://velog.velcdn.com/images/esjw_/post/bb3a2d6a-fe6b-47af-81da-f7af38550148/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IT 기술의 변천과 Web 기술의 발전]]></title>
            <link>https://velog.io/@esjw_/IT-%EA%B8%B0%EC%88%A0%EC%9D%98-%EB%B3%80%EC%B2%9C%EA%B3%BC-Web-%EA%B8%B0%EC%88%A0%EC%9D%98-%EB%B0%9C%EC%A0%84</link>
            <guid>https://velog.io/@esjw_/IT-%EA%B8%B0%EC%88%A0%EC%9D%98-%EB%B3%80%EC%B2%9C%EA%B3%BC-Web-%EA%B8%B0%EC%88%A0%EC%9D%98-%EB%B0%9C%EC%A0%84</guid>
            <pubDate>Tue, 11 Jun 2024 00:47:43 GMT</pubDate>
            <description><![CDATA[<h2 id="program-기법-측면">program 기법 측면</h2>
<p><img src="https://velog.velcdn.com/images/esjw_/post/15f11490-5a3f-4054-8a00-1da3baec8864/image.png" alt=""></p>
<hr>
<h2 id="구조적-측면">구조적 측면</h2>
<p><img src="https://velog.velcdn.com/images/esjw_/post/d4ac520f-673e-4bc5-aa26-44c95e913277/image.png" alt=""></p>
<p>Main Frame에 연결되어 있는것을 완전히 분리
-&gt; data/program 내 컴퓨터에  (데스크탑 시대)
size에 따라 내 컴퓨터 사양에 따라 문제 생길 수 있음
-&gt; 다른 컴퓨터를 만들어 놓고 program을 올려놓고 내 컴퓨터에서 불러오고 랜더링하여 사용 (네트워크 시대)
DBMS로 data를 요청하고 사용</p>
<p><img src="https://velog.velcdn.com/images/esjw_/post/86b53efb-c812-41fa-af7b-ba9948dc9b6e/image.png" alt=""></p>
<p>클라이언트 머신에 따라 프로그램을 다르게 배포/유지보수 해야돼서 서버 개발자 입장에서 너무 힘듬</p>
<p>Web Server - Web browser
웹서버는 개발자가 아니라 단체에서 제공 받음
<a href="http://ip:80(port)/Context%EB%AA%85(%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EB%AA%85)/a.html">http://ip:80(port)/Context명(애플리케이션명)/a.html</a>
ip 부분은 도메인명으로 바꿔서 씀
Context는 여러개 있을 수 있음</p>
<p>client에서 해당 애플리케이션에 맞는 엔진으로 요청 처리
http는 일회성(소켓통신이라서 서버쪽에 소켓이 계속 생겨버림 -&gt; 서버측에서 관리하기도 힘들고 자원이 많이 사용됨 -&gt; 많은 클라이언트 동시 수용 못함)</p>
<p><code>http</code>가 텍스트 기반이라서 많이 쓰는 프로토콜임
컴퓨터 입장에서는 텍스트 기반이 힘들고 사람 입장에서는 편함
멀티 유저를 수용하기위해 등장</p>
<p>xml : 텍스트 기반
json : 요즘 쓰는 텍스트 기반 </p>
<p>웹서버 위에서 구동되는 애플리케이션 개발(개발자)
여러 언어로 시도(서버 위에서 돌아가는 언어 통칭 CGI;Common Gateway Interface)</p>
<p>*<em>심각한 문제 *</em></p>
<ul>
<li><p>프로세스식 처리
: request마다 프로세스 하나가 돌아감
: 메모리 공유 못함</p>
</li>
<li><blockquote>
<p>쓰레드로 돌아가게 함(OS와 HW에도 연결되어야 함)</p>
</blockquote>
</li>
<li><p>스케일 아웃 (서버 증설)
: 쓰레드 하나당 감당할 수 있는 수준을 넘어서면 증설해야함
: 상황마다 예전에 만들어진 프로그램을 올릴 수 없을 수 있음 </p>
</li>
<li><blockquote>
<p>플랫폼 독립성 구성해야함(Sun에서 관심 있었음)
어떤 플랫폼에서든 구동할 수 있게 하는 언어 - Oak 개발</p>
</blockquote>
</li>
<li><blockquote>
<p>잘 안됐고 Web에서 CGI 언어로 Java 개발됨</p>
</blockquote>
</li>
</ul>
<h3 id="web-application-아키텍처">Web Application 아키텍처</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/aa6c5e9d-8a07-4ba7-be7e-4ab39fb2f4a3/image.png" alt=""></p>
<p>가장 엔터프라이즈 좋은게 java
가장 편리한게 Node.js</p>
<hr>
<h3 id="모바일">모바일</h3>
<p><img src="https://velog.velcdn.com/images/esjw_/post/478fa5fc-197d-4acd-a214-4bf4db86196b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3-3 082 DML [A]]]></title>
            <link>https://velog.io/@esjw_/3-3-082-DML</link>
            <guid>https://velog.io/@esjw_/3-3-082-DML</guid>
            <pubDate>Fri, 10 May 2024 17:46:24 GMT</pubDate>
            <description><![CDATA[<h2 id="dml데이터-조작어">DML(데이터 조작어)</h2>
<blockquote>
<p>데이터베이스 사용자가 응용 프로그램이나 질의어를 통해 저장된 데이터를 실질적으로 관리하는데 사용되는 언어</p>
</blockquote>
<p>데이터베이스 사용자와 데이터베이스 관리 시스템 간의 인터페이스 제공</p>
<ul>
<li>SELECT: 튜플 검색</li>
<li>INSERT: 튜플 삽입</li>
<li>DELETE: 튜플 삭제</li>
<li>UPDATE: 튜플 갱신</li>
</ul>
<hr>
<h3 id="삽입문insert-into">삽입문(INSERT INTO~)</h3>
<blockquote>
<p>기본 테이블에 새로운 튜플 삽입</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/esjw_/post/6857ea0a-334e-4b2c-b4ca-e62403dd3ff3/image.png" alt=""></p>
<ul>
<li>대응하는 속성과 데이터는 <strong><code>개수</code>와 <code>데이터 유형</code>이 일치해야 함</strong></li>
<li>기본 테이블의 모든 속성을 사용할 때는 속성명 생략 가능</li>
<li><strong>SELECT문</strong>을 사용하여 <strong>다른 테이블의 검색 결과</strong> 삽입 가능</li>
</ul>
<p>ex) 
INSERT INTO 사원 (이름, 부서)
VALUES (&#39;홍승현&#39;, &#39;인터넷&#39;);</p>
<p>INSERT INTO 사원
VALUES (&#39;장보고&#39;, &#39;기획&#39;, #05/03/73#, &#39;홍제동&#39;, 90);</p>
<p><code>날짜 데이터는 &#39;&#39; 또는 ##으로 묶어줌</code></p>
<hr>
<h3 id="삭제문delete-from">삭제문(DELETE FROM~)</h3>
<blockquote>
<p>기본 테이블에 있는 튜플 중에서 특정 튜플 삭제</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/esjw_/post/cd3b72e7-f12a-4dd5-bf68-d85696be395a/image.png" alt=""></p>
<ul>
<li>모든 레코드 삭제할 때는 WHERE절 생략</li>
<li>모든 레코드를 삭제하더라도 테이블 구조는 남아있어서 DROP과 다름</li>
</ul>
<hr>
<h3 id="갱신문update-set">갱신문(UPDATE~ SET~)</h3>
<blockquote>
<p>기본 테이블에 있는 튜플 중 특정 튜플 내용 변경</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/esjw_/post/9922cc68-ea57-4425-94fd-eabcc274184a/image.png" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[3-3 081 DCL [B]]]></title>
            <link>https://velog.io/@esjw_/3-3-081-DCL-B</link>
            <guid>https://velog.io/@esjw_/3-3-081-DCL-B</guid>
            <pubDate>Fri, 10 May 2024 17:25:11 GMT</pubDate>
            <description><![CDATA[<h2 id="dcl데이터-제어어">DCL(데이터 제어어)</h2>
<blockquote>
<p>데이터의 보안, 무결성, 회복, 병행 제어 등을 정의</p>
</blockquote>
<ul>
<li>GRANT : 권한 부여</li>
<li>REVOKE : 권한 취소</li>
<li>COMMIT : 완료</li>
<li>ROLLBACK : 복구</li>
<li>SAVEPOINT : 저장점</li>
</ul>
<hr>
<h3 id="grant--revoke">GRANT / REVOKE</h3>
<p>*<em>GRANT : 권한 부여
REVOKE : 권한 취소
*</em></p>
<h4 id="사용자-등급-지정-및-해제">사용자 등급 지정 및 해제</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/acb3f06f-77bd-4ead-924b-8e5ff956d76e/image.png" alt=""></p>
<p>ex) GRANT CONNECT TO STAR</p>
<h4 id="사용자-등급">사용자 등급</h4>
<ul>
<li><strong>DBA</strong>: 데이터베이스 관리자</li>
<li><strong>RESOURCE</strong>: 데이터베이스 및 테이블 생성 가능자</li>
<li><strong>CONNECT</strong>: 단순 사용자</li>
</ul>
<h4 id="테이블-및-속성에-대한-권한-부여-및-취소">테이블 및 속성에 대한 권한 부여 및 취소</h4>
<p><img src="https://velog.velcdn.com/images/esjw_/post/322409c2-b7b8-45ba-b9d7-621cb5ed5bbd/image.png" alt=""></p>
<p>ex1) GRANT ALL ON 고객 TO NABI WITH GRANT OPTION 
ex2) REVOKE GRANT OPTION FOR UPDATE ON 고객 FROM STAR</p>
<ul>
<li><p>권한 종류: ALL, SELECT, INSERT, DELETE, UPDATE, ALTER</p>
</li>
<li><p>WITH GRANT OPTION
: 부여받은 권한을 다른 사용자에게 다시 부여할 수 있는 권한 부여</p>
</li>
<li><p>GRANT OPTION FOR
: 다른 사용자에게 권한을 부여할 수 있는 권한 취소</p>
</li>
<li><p>CASCADE
: 권한 취소 시 권한을 부여받았던 사용자가 다른 사용자에게 부여한 권한도 연쇄적으로 취소</p>
</li>
</ul>
<hr>
<h3 id="commit">COMMIT</h3>
<blockquote>
<p>트랜잭션이 성공적으로 끝나면 데이터베이스가 새로운 일관성 상태를 가지기 위해 변경된 모든 내용을 데이터베이스에 반영하는 명령어</p>
</blockquote>
<ul>
<li>COMMIT 명령을 실행하지 않아도 성공적으로 완료되면 자동으로 COMMIT됨</li>
<li>DML이 실패하면 자동으로 ROLLBACK이 되도록 Auto Commit 기능 설정 가능</li>
</ul>
<hr>
<h3 id="rollback">ROLLBACK</h3>
<blockquote>
<p>아직 COMMIT되지 않은 변경된 모든 내용 취소하고 이전 상태로 되돌리는 명령어</p>
</blockquote>
<ul>
<li>트랜잭션 전체가 성공적으로 끝나지 못하면 일부 변경된 내용만 반영되는 비일관성인 상태 발생</li>
<li>일부분만 완료된 트랜잭션은 롤백해야 함</li>
<li>이미 완료된 COMMIT 전으로는 롤백 안됨</li>
</ul>
<hr>
<h3 id="savepoint">SAVEPOINT</h3>
<blockquote>
<p>ROLLBACK할 위치(저장점) 지정하는 명령어</p>
</blockquote>
<ul>
<li>이름을 부여하여 저장점 지정</li>
<li>ROLLBACK 시 지정된 저장점까지의 트랜잭션 처리 내용 취소됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[3-3 080 DDL [A]]]></title>
            <link>https://velog.io/@esjw_/3-3-080-DDL-A</link>
            <guid>https://velog.io/@esjw_/3-3-080-DDL-A</guid>
            <pubDate>Fri, 10 May 2024 13:09:55 GMT</pubDate>
            <description><![CDATA[<h2 id="ddl">DDL</h2>
<blockquote>
<p>DB 구조, 데이터 형식, 접근 방식 등 DB를 구축하거나 수정할 목적으로 사용</p>
</blockquote>
<ul>
<li>번역한 결과가 데이터 사전(Data Dictionary)에 여러 개의 테이블로서 저장됨</li>
</ul>
<p><strong>CREATE</strong></p>
<ul>
<li><p>CREATE SCHEMA</p>
</li>
<li><p>CREATE DOMAIN</p>
</li>
<li><p>CREATE TABLE
<img src="https://velog.velcdn.com/images/esjw_/post/1c30576b-5165-4e27-880b-57779765b50d/image.png" alt=""></p>
</li>
<li><p>CREATE VIEW</p>
</li>
<li><p>CREATE INDEX</p>
</li>
</ul>
<p><strong>ALTER TABLE</strong></p>
<p><strong>DROP</strong></p>
<p><code>CASCADE</code> 제거하거나 변경할 요소를 참조하는 다른 모든 개체 함께 작업함
<code>RESTRICT</code> 참조중일 때는 제거 취소</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3-2 073 파티션 설계 [B]]]></title>
            <link>https://velog.io/@esjw_/3-2-073-%ED%8C%8C%ED%8B%B0%EC%85%98-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@esjw_/3-2-073-%ED%8C%8C%ED%8B%B0%EC%85%98-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Fri, 10 May 2024 13:01:51 GMT</pubDate>
            <description><![CDATA[<h2 id="파티션의-종류">파티션의 종류</h2>
<p><img src="https://velog.velcdn.com/images/esjw_/post/00532e78-479c-4010-8c38-440f0955c60d/image.png" alt=""></p>
<h3 id="범위-분할">범위 분할</h3>
<p><strong>Range Partitioning</strong></p>
<p>지정한 열의 값을 기준으로 분할
ex) 일별, 월별, 분기별</p>
<h3 id="해시-분할">해시 분할</h3>
<p><strong>Hash Partitioning</strong></p>
<ul>
<li>해시 함수를 적용한 결과 값에 따라 데이터 분할</li>
<li>특정 파티션에 데이터가 집중되는 범위 분할의 단점 보완</li>
<li>데이터를 고르게 분산할 때 유용</li>
<li>특정 데이터가 어디에 있는지 판단할 수 없음</li>
<li>고객번호, 주민번호 등과 같이 데이터가 고른 컬럼에 효과적</li>
</ul>
<h3 id="조합-분할">조합 분할</h3>
<p><strong>Composite Partitioning</strong></p>
<ul>
<li>범위 분할로 분할한 다음 해시 함수를 적용하여 다시 분할</li>
<li>범위 분할한 파티션이 너무 커서 관리가 어려울 때 유용</li>
</ul>
<h3 id="목록-분할">목록 분할</h3>
<ul>
<li>지정한 열 값에 대한 목록을 기준으로 분할
ex) &#39;국가&#39;라는 열에 &#39;한국&#39;, &#39;미국&#39;, &#39;일본&#39;이 있는 경우 &#39;미국&#39;을 제외할 목적으로 &#39;아시아&#39;라는 목록을 만들어 분할</li>
</ul>
<h3 id="라운드-로빈-분할">라운드 로빈 분할</h3>
<ul>
<li>레코드를 균일하게 분배하는 방식</li>
<li>각 레코드가 순차적으로 분배됨</li>
<li>기본키가 필요없음
<img src="https://velog.velcdn.com/images/esjw_/post/e956782f-d0ed-478e-9a7b-56f137c7c954/image.png" alt=""></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>