<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Hazel의 개발일기</title>
        <link>https://velog.io/</link>
        <description>코드 한 줄로, 세상의 가치를 만들자🌟</description>
        <lastBuildDate>Sun, 25 Oct 2020 11:40:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Hazel의 개발일기</title>
            <url>https://images.velog.io/images/hyunju-song/profile/a2318500-5288-4d30-b5f4-78c0422fa994/KakaoTalk_Photo_2020-09-19-18-17-35.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Hazel의 개발일기. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyunju-song" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[node-express 서버로 필요한 데이터 메일로 보내기]]></title>
            <link>https://velog.io/@hyunju-song/node-express-%EC%84%9C%EB%B2%84%EB%A1%9C-%ED%95%84%EC%9A%94%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%A9%94%EC%9D%BC%EB%A1%9C-%EB%B3%B4%EB%82%B4%EA%B8%B0</link>
            <guid>https://velog.io/@hyunju-song/node-express-%EC%84%9C%EB%B2%84%EB%A1%9C-%ED%95%84%EC%9A%94%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%A9%94%EC%9D%BC%EB%A1%9C-%EB%B3%B4%EB%82%B4%EA%B8%B0</guid>
            <pubDate>Sun, 25 Oct 2020 11:40:44 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하며, 비밀번호 찾기 기능을 구현하기로 하였고,
우선은 기본적으로 비밀번호 찾기 화면에서 주어진 정보가 있다면, 
해당하는 비밀번호를 고객 데이터로 저장된 메일로 보내주는 것을 구현하고자 했다.</p>
<ul>
<li>우선 주어진 데이터를 바탕으로 비밀번호 찾는 쿼리문은, 아이디찾기 기능에서도 활용된 부분이고 자주 사용하는 sequelize 쿼리문이므로 메일 보내는 기능부터 본격적으로 정리하고자 한다.</li>
</ul>
<h3 id="비밀번호-찾고-메일-보내는-로직">비밀번호 찾고 메일 보내는 로직</h3>
<ol>
<li>프론트에서 비밀번호 찾기 화면으로 이동 후에, 필요한 정보
(이땐 이메일과, 깃헙아이디)를 post 메소드로 서버로 보낸다.</li>
<li>서버에서는 주어진 정보 기준으로 데이터가 있으면, 데이터를 반환해주고,
그 데이터에서 비번과 메일을 추출한다. 없으면 에러메시지 반환</li>
</ol>
<h2 id="비밀번호-찾은-후에-메일로-전송해주기">비밀번호 찾은 후에 메일로 전송해주기</h2>
<h3 id="nodemailer-모듈-설치하기">NODEMAILER 모듈 설치하기</h3>
<p><a href="https://community.nodemailer.com/">nodemailer 관련 참고글</a></p>
<h3 id="비밀번호-찾기-구현한-서버-코드">비밀번호 찾기 구현한 서버 코드</h3>
<pre><code class="language-js">const { users } = require(&#39;../../models&#39;);
//우리가 사용자의 데이터를 저장해두고 찾는 테이블
const nodemailer = require(&#39;nodemailer&#39;);
//nodemailer 모듈 설치

const user = process.env.findpw_mail;
const password_send = process.env.findpw_pw;
//이건 저희 개별 계정으로 연습?하거나 흠 각자의 서버에 저장된 개인 메일이나 비번을 활용하는게 좋을거 같아서 환경변수 설정함.

module.exports = {
  post: (req, res, next) =&gt; {
    const { useremail, githubId } = req.body;
    users
      .findOne({
        where: {
          email: useremail,
          githubId: githubId,
        },
      })
      .then((data) =&gt; {
        if (!data) {
          res.status(400).send(&#39;informations are not matched or have to sign up &#39;);
        } else {
          const useremail = data.email;
          const password = data.password;
          //이건 보내는 사람의 정보를 입력하는 것(즉 서비스 제공자)
          let transporter = nodemailer.createTransport({
            service: &#39;gmail&#39;,
            //지메일의 경우, 보내는 사용자의 계정이 보안수준이 낮은 접근 허용불가로 되어있으면 에러가 나는 경우가 있으므로 참고
            auth: {
              user: user,
              pass: password_send,
            },
          });
          //메일 보내는 실질적은 내용 구성
          let mailOptions = {
            from: user, //보내는 사람 메일주소
            to: useremail, //받는 사람 메일주소
            subject: &#39;S*FU Password 찾기&#39;,
            html: `&lt;p&gt;${useremail} 계정의 임시 비밀번호는 &lt;strong&gt;${password}&lt;strong&gt;입니다.&lt;/p&gt;`, // 메일내용
          };
          transporter.sendMail(mailOptions, function (error, info) {
            if (error) {
              console.log(error);
              next(error);
            } else {
              console.log(&#39;Email sent: &#39; + info.response);
              return res.status(200).json({ success: true });
            }
          });
        }
      })
      .catch((err) =&gt; {
        res.status(500).send(&#39;err&#39;);
      });
  },
};</code></pre>
<p>우와...엄청 복잡한 기능이라 생각했는데 진짜 간단했다 ㅠㅠ
모듈 만세...ㅠㅠ
이거 활용해서 엄청나게 많은 것들을 할 수 있을거 같아</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 문제풀이 13_자료구조 stack&queue]]></title>
            <link>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-13%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-stackqueue</link>
            <guid>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-13%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-stackqueue</guid>
            <pubDate>Sun, 25 Oct 2020 10:05:16 GMT</pubDate>
            <description><![CDATA[<p>자료구조 stack과 queue의 기본 성질을 구현하는 각 메소드를 알고리즘으로 풀어보는 문제였다. 
이 문제는 알고리즘보다는 자료구조에 대한 이해를 위한 문제라고 이해하면 될 듯하다.</p>
<pre><code class="language-js">/**
 * Stack Class
 */
var Stack = function () {
  this.storage = {};
  this.top = 0;
  // top을 이해할때는 스택 저장소의 길이로 이해하면 좋을 듯 하다.
  this.push = function (value) {
    this.storage[this.top] = value;
    this.top++;
  };

  // remove an item from the top of the stack
  this.pop = function () {
    let topValue = this.storage[this.top - 1];
    //실제로 가장 위에 있는 값으 인덱스는 저장소의 길이보다 1개 작다.
    delete topValue;
    this.top--;
  };

  // return the number of items in the stack
  this.size = function () {
    return this.top;
  };
};

/**
 * Queue Class
 */
var Queue = function () {
  var queue = new Stack();
  //스택과 비슷한 성격을 지녔으므로 기본 스택의 형태를 가지고 와서 만들수 있다.
  this.front = queue.top;
  this.storage = queue.storage;
  this.rear = 0;
  //스택과는 다르게 큐에서는 앞과 뒤의 개념이 있으므로 새로 정의해준다.
  //큐에서는 rear이 현재 저장소 안에 있는 요소의 수로 이해하면 된다.
  // called to add an item to the `queue`
  this.enqueue = function (value) {
    if (this.rear === 0) {
      this.storage[this.front] = value;
      this.storage[this.rear] = value;
      this.rear++;
    } else {
      this.storage[this.rear] = value;
      this.rear++;
    }
  };

  // called to remove an item from the `queue`
  this.dequeue = function () {
    if (this.rear === 0) {
      return;
    } else {
      let frontValue = this.storage[this.front];
      delete frontValue;
      this.front++;
      return frontValue;
    }
  };

  // should return the number of items in the queue
  this.size = function () {
    if (this.rear &lt; this.front) {
      return 0;
    } else {
      return this.rear - this.front;
    }
  };
};</code></pre>
<p><em>큐를 스택을 두번사용해서 구현해보기</em></p>
<pre><code class="language-js">Stack을 이용한 Queue는 enqueue는 둘다 뒤에 넣어주는 것이므로 동일한 개념이다(단, 값을 &#39;넣어&#39;주는 거니까 inbox에 push해준다.)
그런데 dequeu가 서로 방법이 다르므로 조금 복잡하다. 
inbox   outbox
| c |   |   |
| b |   |   | 
|_a_|   |_ _|
일 때 dequeue시 a가 inbox에서 제거되어야 하므로
1. inbox.pop()을 a만 남아있을 때까지 반복해주고, (while (inbox.size() !== 1))
inbox   outbox
|   |   |   |
|   |   | b | 
|_a_|   |_c_|
2. a를 pop한 다음 (이 역시 값을 return해주어야지 테스트케이스 line 161, 162의 조건을 만족)(let item = inbox.pop())
inbox   outbox
|   |   |   |
|   |   | b | 
|_ _|   |_c_|
3. 다시 outbox에 있는 것들을 outbox에 아무것도 없을 때까지(while (outbox.size() !== 0)) outbox pop &amp; inbox push
inbox   outbox
|   |   |   |
| c |   |   | 
|_b_|   |_ _|
4. 그리고 dequeue한 값인 a를 return 한다. (return item)
어짜피 이미 구현된 stack을 활용하는 것이므로 Queue 구현 과정에서 this.front나 this.end로 index를 고려해줄 필요는 없다. 
*/
var Queue = function () {
  // Use two `stack` instances to implement your `queue` Class
  var inbox = new Stack();
  var outbox = new Stack();

  // called to add an item to the `queue`
  this.enqueue = function (value) {
    // TODO: implement `enqueue`
    inbox.push(value);
  };

  // called to remove an item from the `queue`
  this.dequeue = function () {
    // TODO: implement `dequeue`
    //inbox안에 요소가 하나만 남을때까지
    while (inbox.size() !== 1) {
      let top = inbox.pop();
      outbox.push(top);
    }
    //그리고 맨 마지막 하나남은 요소를 꺼내주면 일종의 queue에서 pop을 구현한 것이다.
    let item = inbox.pop();
    //그리고 outbox에서 요소가 하나도 남지않을때까지 다시 inbox에서 넣어준다. 위의 그림 참고
    while (outbox.size() !== 0) {
      let value = outbox.pop();
      inbox.push(value);
    }
    return item;
  };

  // should return the number of items in the queue
  this.size = function () {
    // TODO: implement `size`
    return inbox.size();
  };
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 문제풀이 12]]></title>
            <link>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-12</link>
            <guid>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-12</guid>
            <pubDate>Sun, 25 Oct 2020 09:58:26 GMT</pubDate>
            <description><![CDATA[<p>문자열이 주어진다면, 각 문자에 해당하는 수들로 환산하고
누적해서 더해준다.
가령 LC가 주어진다면, L에 해당하는 수와 C에 해당하는 수를 누적해서 더해준다. 
그런데 만약에 LCS이렇게 수가 주어졌을때, L, C에 해당하는 수는 더해주지만, C에 해당하는 수가 S에 해당하는 수보다 작은 경우에는, S에서 C를 뺀수를 더해주어야 한다.
해당 조건때문에 새로운 조건을 추가하고 문제를 처음부터 다시 되짚었던 기억이 난다.</p>
<pre><code class="language-js">const DIGIT_VALUES = {
  I: 1,
  V: 5,
  X: 10,
  L: 50,
  C: 100,
  D: 500,
  M: 1000
};

var translateRomanNumeral = function(romanNumeral){
  if(typeof romanNumeral === &#39;string&#39;){
    if(romanNumeral.length === 1){
      return DIGIT_VALUES[romanNumeral];
    } else if(romanNumeral.length &gt;1){
      let result = DIGIT_VALUES[romanNumeral[0]];
      //일단 첫번째 글자에 해당하는 수를 먼저 선언해둠 그리고 하단에서 반복문을 돌면서 누적해서 더해줄 예정
      for(let i=0; i&lt;romanNumeral.length-1; i++){
        if(DIGIT_VALUES[romanNumeral[i]] &lt; DIGIT_VALUES[romanNumeral[i+1]]){
          let sub = DIGIT_VALUES[romanNumeral[i+1]] - DIGIT_VALUES[romanNumeral[i]];
          result = result - DIGIT_VALUES[romanNumeral[i]] + sub;
          //이렇게 해준 이유는, 가령 누적해오면서 &quot;10[0번째인덱스 수]+5[1번째 인덱스수]=result&quot; 이렇게일때 2번째 인덱스에 20이 온다면, 
          //result 값에 단순히 2번째 인덱스 수에서 1번째 인덱스 수를 뺀값을 더해준다면, 1번째 인덱스 수는 2번 계산된셈이다. 따라서 일단 빼주고
          //새로 계산한 수를 더해주는 것
        } else {
          result = result + DIGIT_VALUES[romanNumeral[i+1]];
        }
      }
    return result;
    } else {
      return 0;
    }
  } else {
    return null;
  }
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 문제풀이 11]]></title>
            <link>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-11</link>
            <guid>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-11</guid>
            <pubDate>Sun, 25 Oct 2020 09:39:52 GMT</pubDate>
            <description><![CDATA[<p>자료구조의 가장 기본인 queue와 stack을 활용한 알고리즘 문제이다.</p>
<p>첫번째 문제는 stack 자료구조에서 단골 손님이라 할 수 있는<br>괄호의 쌍이 맞는지를 점검하는 문제이다.</p>
<pre><code class="language-js">var balancedParens = function(input){
    //() , {}, [] : 인풋값중에서 괄호가 이렇게 쌍으로 이루어져 있는지를 탐색
    //input값을 우선 spilt 해준다.
    //그리고 괄호 부분만 발라내서 새로운 배열에 넣어준다.
    let bracket = [&quot;(&quot;,&quot;)&quot;,&quot;{&quot;,&quot;}&quot;,&quot;[&quot;,&quot;]&quot;];
    let inputArr = input.split(&#39;&#39;);
    let res = [];
    for(let i=0; i&lt;inputArr.length; i++){
        if(bracket.includes(inputArr[i])){
            res.push(inputArr[i]);
        }
        if(((res[res.length-2]===&quot;(&quot;) &amp;&amp; (res[res.length-1]===&quot;)&quot;))
        || ((res[res.length-2]===&quot;{&quot;) &amp;&amp; (res[res.length-1]===&quot;}&quot;))
        || ((res[res.length-2]===&quot;[&quot;) &amp;&amp; (res[res.length-1]===&quot;]&quot;))){
            res.pop();
            res.pop()
        }
    }
    if(res.length &gt;0){
        return false;
    }
    return true;
};</code></pre>
<p>어떻게 보면 괄호에 해당하는지를 확인하고 결과값에 넣을때마다 
결과값 배열에 짝이있는지 없는지 점검하고, 있으면 빼주는 형태로 계산한 것이다. FIFO 즉 먼저 들어온것이 먼저 나가는 queue의 자료구조를 활용하였다고 보면 될 것 같다. </p>
<p><em>그러나 pop을 한 번만 쓰도록 해줄수있지 않을까?
그러니까 처음에 무조건 전부다 push를 하지 말고, 하나씩 번갈아서 점검하면서 pop하고 push를 해주지 말기</em></p>
<pre><code class="language-js">var balancedParens = function (input) {
  let stack = [];

  for (let i = 0; i &lt; input.length; i++) {
    var curr = input[i];
    if (curr === &#39;(&#39; || curr === &#39;{&#39; || curr === &#39;[&#39;) {
      stack.push(curr);
    } else if (curr === &#39;)&#39; || curr === &#39;}&#39; || curr === &#39;]&#39;) {
      let top = stack[stack.length - 1];//즉 가장 마지막 요소를 top이란 변수에 선언
      if (top === undefined) {
        // 이 말인 즉슨 집어넣어주려는 요소가 닫히는 괄호인데 stack에 넣어놓은 괄호가 없을때 즉, 열린 괄호보다 닫힌괄호가 먼저 앞인 경우에 false를 반환 
        // 열린 괄호보다 닫힌 괄호가 더 많을 때, ex) should return false for )
        return false;
      }
      // 넣어놓은 배열에서 가장 마지막 요소가 열린 괄호이고, input에서 막 집어넣으려는 요소가 이에 대응하는 닫힌 괄호인 경우
      if (top === &#39;(&#39; &amp;&amp; input[i] === &#39;)&#39;) {
        stack.pop();
      } else if (top === &#39;{&#39; &amp;&amp; input[i] === &#39;}&#39;) {
        stack.pop();
      } else if (top === &#39;[&#39; &amp;&amp; input[i] === &#39;]&#39;) {
        stack.pop();
      }
    }
  }
  if (stack.length !== 0) {
    //닫힌 괄호보다 열린 괄호가 더 많을 때, ex) should return false for (
    return false;
  }
  return true;
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Node.js 에러(listen EADDRINUSE :) 해결하기]]></title>
            <link>https://velog.io/@hyunju-song/Node.js-%EC%97%90%EB%9F%AClisten-EADDRINUSE-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyunju-song/Node.js-%EC%97%90%EB%9F%AClisten-EADDRINUSE-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 24 Oct 2020 07:56:13 GMT</pubDate>
            <description><![CDATA[<p>서버코드를 작성하다가 아무런 에러도 없었는데 갑자기
서버 실행이 안되더니 뜨는 에러</p>
<pre><code>listen EADDRINUSE :::4000</code></pre><p>으잉???
모지하면서 바로 구글링을 해보니</p>
<p>기존 서버가 제대로 종료가 되지 않는 상태에서 실행하려 해서 발생하는 에러이다.</p>
<p>따라서 이런 경우에는 해당 포트의 서버를 강제종료하면 된다.</p>
<ol>
<li>일단 프로세스의 pid를 찾기<pre><code>$ ps -ef | grep index.js</code></pre></li>
</ol>
<p>나는 아래의 메시지가 떴다</p>
<pre><code>501 34445 34444   0  4:36PM ttys000    0:00.24 node /Users/songhyeonju/Desktop/202010_PR_1/SAFU-server/node_modules/.bin/nodemon index.js
501 34447 34445   0  4:36PM ttys000    0:00.66 /Users/songhyeonju/.nvm/versions/node/v10.13.0/bin/node index.js
501 37103 13889   0  4:53PM ttys000    0:00.00 grep index.js</code></pre><p>오호 추측상으론 4:53 이게 서버를 실행한 시간 같다. 흠 그러면
501은 pid가 아닌듯 하고, 34445부터 차례대로 다 죽여보기
<em>내가 구글에서 나온 메시지랑 약간 다르잖아!!! 일단 시도해보기</em></p>
<ol start="2">
<li>우선 34445부터 죽여봤다.</li>
</ol>
<pre><code>$ kill -9 34445</code></pre><p>오호 잘 삭제되다가 37103은 no such process 가 뜬다..!
흠 일단 다시 서버를 시작해볼까?</p>
<p>오 잘 실행된다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS로 배포하기_RDS(DB)]]></title>
            <link>https://velog.io/@hyunju-song/AWS%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0RDSDB</link>
            <guid>https://velog.io/@hyunju-song/AWS%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0RDSDB</guid>
            <pubDate>Thu, 22 Oct 2020 14:47:33 GMT</pubDate>
            <description><![CDATA[<h2 id="rds로-db-배포하기">RDS로 DB 배포하기</h2>
<ul>
<li><p>데이터베이스 생성을 진행
<img src="https://images.velog.io/images/hyunju-song/post/ce36859e-533f-4d7b-9bec-c0c6bd5877e8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.09.24.png" alt=""></p>
</li>
<li><p>내가 데이터베이스를 진행하는 프로그램에 맞는 환경을 설정하고,
과금되지 않게 꼭 프리티어로 설정해준다.
<img src="https://images.velog.io/images/hyunju-song/post/96289f2e-1786-4c93-9c42-b3eab45f39bf/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.11.47.png" alt="">
<img src="https://images.velog.io/images/hyunju-song/post/d55d509c-d06a-482e-a7e5-0e105497cf92/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.12.09.png" alt=""></p>
</li>
</ul>
<ul>
<li><p>DB 인스턴스 이름 및 사용자 이름과 암호 설정
<img src="https://images.velog.io/images/hyunju-song/post/30d79c41-f266-4ec5-bbc3-92c6f6c4a7ab/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.13.10.png" alt=""></p>
</li>
<li><p>그리고 스크롤을 내려서 하단의 &quot;연결&quot;부분을 퍼블릭하게 수정
<img src="https://images.velog.io/images/hyunju-song/post/1facf97b-6b97-4750-9d26-124c134c6ef7/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.25.42.png" alt=""></p>
</li>
<li><p>다른 기본적인 세팅은 디폴트값으로 두기</p>
</li>
<li><p>DB 셋업은 시간이 꽤 걸리고 셋업이 될 때까지 기다리기</p>
</li>
<li><blockquote>
<p>셋업완료후에 연결하고자 하는 데이터베이스 식별자 이름을 클릭</p>
</blockquote>
</li>
</ul>
<p><img src="https://images.velog.io/images/hyunju-song/post/7990e70b-0046-4fb3-aa20-a4013a4f8d7c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.39.49.png" alt=""></p>
<ul>
<li>mysql 접속하는 방법은 터미널을 통해서 접속 가능하다.</li>
</ul>
<ol>
<li>mysql start</li>
<li>mysql -u &quot;내가 설정한 사용자 이름&quot; -host &quot;화면에서 보이는 앤드포인트 복붙&quot; -P &quot;포트번호&quot; -p &quot;패스워드&quot;</li>
</ol>
<p>-&gt; 위의 터미널 명령어로 AWS의 데이터베이스 접속 가능</p>
<ul>
<li>squel pro 통해서 데이터베이스를 GUI로 확인 가능</li>
</ul>
<h4 id="그렇다면-내가-만들어-둔-데이터베이스를-어떻게-aws-rds에-옮길-수-있을까">그렇다면 내가 만들어 둔 데이터베이스를 어떻게 AWS RDS에 옮길 수 있을까?</h4>
<p>이는 간단하다 즉, db를 만든 local에서 연결된 데이터베이스를 RDS에서 설정한 데이터베이스 이름으로 설정하면 된다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS로 배포하기_S3(client)]]></title>
            <link>https://velog.io/@hyunju-song/AWS%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0S3clientRDSDB</link>
            <guid>https://velog.io/@hyunju-song/AWS%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0S3clientRDSDB</guid>
            <pubDate>Thu, 22 Oct 2020 14:02:22 GMT</pubDate>
            <description><![CDATA[<h2 id="s3로-client-배포하기">S3로 client 배포하기</h2>
<ul>
<li>우선 local에서 client코드를 build하기<pre><code>yarn build</code></pre></li>
<li>AWS S3에서 버킷 만들기</li>
<li><blockquote>
<p>S3화면으로 클릭해서 들어가면 하단의 창이 보이고, 버킷만들기를 클릭한다.
<img src="https://images.velog.io/images/hyunju-song/post/39e089c8-79c6-4ba2-a4fd-c44d1106e2ba/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.49.49.png" alt=""></p>
</blockquote>
</li>
</ul>
<p>-&gt; 그러면 하단의 창이 뜬다. 버킷 이름을 만들고 생성 버튼 클릭
<img src="https://images.velog.io/images/hyunju-song/post/d5b3104e-feb5-4fd6-849d-8f76b78749fc/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.39.11.png" alt=""></p>
<ul>
<li><p>생성을 한 후, 내가 만든 버킷의 이름을 클릭해서 설정을 진행해주어야 한다.</p>
</li>
<li><blockquote>
<p>우선 하단의 창이 뜨면 속성탭으로 들어간다.
<img src="https://images.velog.io/images/hyunju-song/post/4ad474d7-f6c8-496b-84ba-61b1da977065/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.40.47.png" alt=""></p>
</blockquote>
</li>
<li><p>속성탭에서 정적 웹사이트 호스팅 카드를 눌러서 필요한 설정을 해주어야 한다.
<img src="https://images.velog.io/images/hyunju-song/post/29982122-ba3a-4665-aa93-c053ad25df2a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.45.40.png" alt=""></p>
</li>
</ul>
<p>-&gt; 정적 웹사이트 호스팅을 사용하도록 속성 변경을 해준다.
 : 인덱스 문서는 유저가 페이지를 들어갔을 때 가장 먼저 보이는 엔트리 페이지이다.
 : 에러페이지는 에러가 났을 경우, 보여지는 에러페이지이다.
 (리액트에서는 별도로 에러페이지 생성기능이 없으므로 엔트리페이지와 동일하게 설정)
 -&gt; 아래의 앤드포인트 링크로 들어가면 아직은 에러가 뜬다.
 <img src="https://images.velog.io/images/hyunju-song/post/23bf2c0d-a9d4-4f8e-bd6a-42ed2a9022a1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.48.42.png" alt=""></p>
<ul>
<li><p>그 다음으로는 &quot;권한&quot; 탭으로 들어가서 권한 설정을 해주어야 한다.</p>
</li>
<li><blockquote>
<p>아래 화면으로 들어가면 모든액세스차단이 체크되어있는데, 해제해준다.
(모든 퍼블릭 액세스를 허용해주는 것)
<img src="https://images.velog.io/images/hyunju-song/post/f717b966-ca47-4872-b23f-df0125f7aafd/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.50.25.png" alt=""></p>
</blockquote>
</li>
<li><p>버킷정책 탭으로 들어가서, 정책을 입력해 주어야 한다. 
이때, 입력하는 정책을 생성해주는 기능 또한 AWS에서 제공해준다.
아래 화면에서 하단의 정책 생성기를 클릭해보자
<img src="https://images.velog.io/images/hyunju-song/post/7c44a682-9fbc-465a-8ef0-ed7caf7a07b2/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.52.41.png" alt=""></p>
</li>
</ul>
<p>-&gt; 그러면 하단과 같은 창이 뜨고, 아래의 설정을 참고하여 칸을 채워준다.
a. action의 경우, get Object 하나만 설정해 두면 되고
b. ARN에는 arn:aws:s3:::<bucket_name>/<key_name> 이러한 양식으로 기재해준다. 이 때 key name에는 * 로 모든 키를 허용하도록 한다.</p>
<p><img src="https://images.velog.io/images/hyunju-song/post/ce60960c-0d9c-4e2a-acb4-0bb52d60821e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.58.38.png" alt=""></p>
<p>-&gt; 생성하면 하단의 JSON 타입의 정책이 생성된다. 이를 복붙하여 앞의 버킷정책 관리의 빈화면에 복붙해준다. 그리고 저장해준다.
<img src="https://images.velog.io/images/hyunju-song/post/42727542-926d-40c1-9dbf-3d059b86f81d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.59.51.png" alt=""></p>
<p><img src="https://images.velog.io/images/hyunju-song/post/24990584-d226-4026-9bea-283af7ae59f8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.00.22.png" alt=""></p>
<p>그러면 해당 버킷에 대한 퍼블릭 액세스 권한이 있지만 아직 코드파일이 없으므로 에러가 뜬다.</p>
<ul>
<li>따라서 개요 탭으로 가서, 가장 첫 단계에서 만들어 둔 client build 파일을 드래그앤 드랍으로 AWS S3에 옮겨둔다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[소셜로그인기능구현(2)_Github Social Login 구현]]></title>
            <link>https://velog.io/@hyunju-song/%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%842Github-Social-Login-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@hyunju-song/%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%842Github-Social-Login-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 20 Oct 2020 13:13:58 GMT</pubDate>
            <description><![CDATA[<p>소셜로그인 기능을 직접 구현해보기에 앞서서, 이 기능의 원리와 절차에 대해서 먼저 정리해보았다.
<a href="https://velog.io/@hyunju-song/%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8%EA%B8%B0%EB%8A%A51OAuth">OAuth에 대한 설명글</a></p>
<h2 id="github-social-login-구현하기">Github Social Login 구현하기</h2>
<p><em>프로젝트를 같이 진행하는 팀원분이 정리한 블로그 글을 참고했습니다</em>
<a href="https://libertegrace.tistory.com/entry/41-Authentication-Github-Social-Login-1?category=869766">github social login 구현에 대한 설명 블록,</a></p>
<h3 id="github에-등록하기">Github에 등록하기.</h3>
<p>github의 소셜로그인 서비스를 이용하기 위해서는 github의 OAuth를 사용해야하고 따라서 등록 과정을 거쳐야 한다. </p>
<ul>
<li>보통 검색창에 바로 사용하고자 하는 소셜서비스와 OAuth 를 검색하면 해당 페이지로 바로 들어갈 수 있다.</li>
<li>github의 경우, OAuth 등록을 위해서는 
: 로그인 -&gt; my page의 setting -&gt; developer setting -&gt; OAuth Apps 클릭</li>
<li>그러면 하단의 화면이 나온다.
<img src="https://images.velog.io/images/hyunju-song/post/ee33722e-dcee-40a5-88fe-ea82bf5e920f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-20%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.54.00.png" alt="">
a. Client ID: 내가 구현 할 어플리케이션인 Client를 식별 할 수 있는 ID
b. Client Secret: 내가 구현 할 어플리케이션인 Client를 식별 할 수 있는 PW (절대, 코드에 노출되면 안되는 정보, 보안 이슈)
c. Authorized Redirect URL: Resource Server가 권한을 부여하는 과정에서 Authorized Code를 전달해 줄 경로, (&#39; Authorized rediret URIs로 Authorized Code 전달해줘~&#39;)</li>
</ul>
<p>해당 정보가 Github와 내가 만든 서비스 사이에서 공유한다. 
이 정보들은 외부에 노출되면 안된다! 
또 중요한 것은 </p>
<ul>
<li>Homepage URL: 내 서비스의 URL</li>
<li>Authorizatiton callback URL: 사용자가 Github 로그인을 하면 redirect될 페이지의 주소. </li>
</ul>
<h3 id="user가-github에-내가-만든-서비스의-접근을-승인">user가 github에 내가 만든 서비스의 접근을 승인</h3>
<p>[프론트] 깃헙 로그인 버튼 코드를 구현한다. 해당 코드는 a 태그로 되어있고 href는 개발한 서버의 auth api의 주소가 담겨있다.
아마 아래와 같은 href 주소를 버튼을 프론트에서 작성해야 한다.</p>
<pre><code>&lt;body&gt;
    &lt;a
      href=&quot;https://github.com/login/oauth/authorize?client_id=[위에서 받은 client id]&amp;redirect_uri=[위에 나와있는 redirect url]&quot;
      &gt;GitHub 아이디로 로그인&lt;/a&gt;
  &lt;/body&gt;</code></pre><p>사용자가 깃헙 로그인 버튼을 누르면 위의 링크로 가게되고, 보이는 화면은 
github을 통해 해당 서비스에 로그인하는 것을 허용할 것인지에 대한 화면이다.
<img src="https://images.velog.io/images/hyunju-song/post/b5e15189-20d1-4801-9df3-d5bb92e089fd/image.png" alt="">
-&gt; Authorize 버튼을 누르면 내가 사용자들을 redirect 시키고자 github OAuth에 등록한 링크로 이동하게 된다.</p>
<h3 id="github에게서-access-token-받기">Github에게서 Access Token 받기</h3>
<p>사용자가 승인을 하게 되면서 등록된 url로 redirect 될 때 전달되는
response의 header에 &#39;https://[redirect URL인 callbackURL]?code=[Authorization Code]&#39; 형식으로 Authorization Code를 준다. </p>
<p>이 code를 client_id와 client_secret과 함께 Github API auth api에 Post하면 access_token을 리턴 할 것이다. </p>
<p>서버에서는 해당 Access token을 요청하는 fetch 메소드를 작성해야한다.
이는 즉, 서비스의 server는 서비스의 client 입장에서는 서버의 위치이므로 client의 버튼 이벤트를 받는 get 이벤트를 동작 시키지만, 동시에 소셜 서비스에 인증을 요청하는 post 메소드를 보내는 client 역할을 하기도 한다.
따라서 하단의 코드를 보면 get 메소드 안에 axio모듈을 통해, post를 보내고 있다.</p>
<pre><code class="language-js">// GitHub에 OAuth 앱을 등록한 후, 발급받은 client id 및 secret을 입력한다. 
const clientID = process.env.GITHUB_CLIENT_ID
const clientSecret = process.env.GITHUB_CLIENT_SECRET

const app = express()

app.get(&#39;/callback&#39;, (req, res) =&gt; {
  //&#39;/callback&#39;: 인증 정보를 바탕으로 access token을 받아올 수 있도록 도와주는 라우터이다.
  const requestToken = req.query.code //이 req.query.code가 위의 &#39;code=[Authorization Code]&#39; 에 해당한다.
  axios({
    method: &#39;post&#39;,
    url: `https://github.com/login/oauth/access_token?client_id=${clientID}&amp;client_secret=${clientSecret}&amp;code=${requestToken}`,
    headers: {
      accept: &#39;application/json&#39;,
    },
  }).then((response) =&gt; {
    const accessToken = response.data.access_token //Github가 access_token을 응답으로 줄 것이다. 
    res.redirect(`/welcome.html?access_token=${accessToken}`) //그리고 이렇게 accessToken을 받은 사용자에 한해서만 welcome 페이지로 리다이렉트 된다. 
    //그리고 welcome 페이지를 구성하는 client에서 get fetch를 통해 token및 데이터를 받아오게 된다.
  }).catch((err) =&gt; {
      console.error(err)
  })
})</code></pre>
<p>더 간단하게 작성하면</p>
<pre><code class="language-js">const response = await axios.post(
    &#39;https://github.com/login/oauth/access_token&#39;,
    {
      code,
      client_id, // SAFU application의 정보
      client_secret, // SAFU application의 정보
    },
    {
      headers: {
        accept: &#39;application/json&#39;,
      },
    },
  );

   //Github가 access_token을 응답으로 줄 것이다. 
  const token = response.data.access_token;</code></pre>
<p>이렇게 post 메소들를 통해서 알게된 데이터를 
<strong>서버에서는 ORM 함수를 통해 DB에 저장해주고,
client에서는 하단의 코드를 통해서 로그인에 성공한 페이지에서 get fetch를 통해서 access token등, 로그인에 성공했을 때 받을 수 있는 정보를 받아온다</strong></p>
<pre><code class="language-js">// GitHub API를 통해 사용자 정보를 받아올 수 있다. 
  fetch(&#39;//api.github.com/user&#39;, {
    headers: {
      // 이와 같이 Authorization 헤더에 `token ${token}`과 같이
      // 인증 코드를 전송하는 형태를 가리켜 Bearer Token 인증이라고 한다.
      Authorization: &#39;token &#39; + token
    }
  })
    .then(res =&gt; res.json())
    .then(res =&gt; { 
      // 이 응답에 대한 문서는 GitHub 공식 문서를 참조하세요
      // https://developer.github.com/v3/users/#get-the-authenticated-user

      document.body.innerText = `${res.name}님 환영합니다!`
    })</code></pre>
<p>더 간단하게 작성하면</p>
<pre><code class="language-js">const { data } = await axios.get(&#39;https://api.github.com/user&#39;, {
    headers: {
      Authorization: `token ${token}`,
    },
  });</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[소셜로그인기능 구현(1)_그 원리와 절차에 대해]]></title>
            <link>https://velog.io/@hyunju-song/%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8%EA%B8%B0%EB%8A%A51OAuth</link>
            <guid>https://velog.io/@hyunju-song/%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8%EA%B8%B0%EB%8A%A51OAuth</guid>
            <pubDate>Tue, 20 Oct 2020 11:55:52 GMT</pubDate>
            <description><![CDATA[<p>소셜로그인을 구현하기에 앞서, 해당 기능이 client와 연동되어 작업이 되어야 할거 같기도 하고/ 아직 소셜로그인에 대해 잘 이해가 되지 않아서, 
정리를 먼저 해보고자 한다.</p>
<p>이번 프로젝트에서는 물론이고 앞으로도 유용하게 쓰일 수 있는 기능이라 꼼꼼하게 정리하고 가면 좋다고 생각했다.
세부적인 공부는 깃헙 소셜 로그인 구현을 기반으로 정리해보았다.</p>
<h2 id="소셜로그인-구현의-원리">소셜로그인 구현의 원리</h2>
<p><a href="https://velog.io/@parkoon/OAuth-%EC%99%84%EB%B2%BD%ED%95%98%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0">소셜로그인 참고 문서</a>
소셜로그인 기능에는 3명의 주인공이 등장하게 된다.</p>
<ul>
<li>client : 소셜로그인 기능을 사용하는 주체, 즉 서비스를 만드는 &quot;나&quot;이다.</li>
<li>resource owner : 소셜로그인 기능을 제공하는 서비스를 사용하는 유저이다. 어떻게 보면 진짜 고객 인것!</li>
<li>resource server : 소셜로그인 기능을 제공하는 곳, 그리고 그 기능을 제공하기 위한 데이터를 가지고 있는 진짜 서버이다. 일종의 우리가 만드는 서비스의 서버 기능을 하는 것. google, facebook, twitter</li>
</ul>
<p>우선 소셜로그인 자체에 대한 이해를 위해서 소셜로그인이 무엇인지 설명해보자면, </p>
<blockquote>
<ol>
<li>사용자가 소셜로그인 버튼을 누르면, 로그인하고자 하는 소셜의(카카오나 구글) 로그인페이지로 가게된다. </li>
<li>이 때 이 로그인 페이지로 가게 하기 위해, 서비스제공자와 소셜 사이에서의 모종의 상호작용이 일어나게 된다. 이 상호작용을 위해 서비스 제공자는 미리 OAuth라는 서비스를 사용하게 된다. (이에 대해서는 이후에 추가적인 설명을 할 예정)</li>
<li>로그인을 성공하면, 소셜은 사용자의 페이지가 기존에 사용하던 서비스 페이지로 redirect 되도록 해준다.</li>
</ol>
</blockquote>
<p>즉 소셜로그인은, 구글이나 카카오에서 &quot;나&quot;라는 제공자와 &quot;사용자&quot; 사이에서 로그인을 중개해주는 역할을 하는 것이다. 
이 중개자의 역할을 가능하도록 해주는 서비스가 OAuth 이다. </p>
<p>사용자가 소셜로그인에 로그인 했을 때, 그 아이디와 비번을 서비스 제공자에게 주는 것이 아니라, OAuth를 거쳐서 소셜에서는 &quot;나&quot;에게 Access Token을 제공하고, &quot;나&quot;는 이 토큰을 통해서 소셜에 접근할 수 있게 되고, 사용자에게 로그인 페이지를 제공할 수 있는 것이다.</p>
<h3 id="구체적인-절차">구체적인 절차</h3>
<p><em>부트캠프 동기분이 정리한 블로그 글의 도움을 많이 받았습니다💖</em>
<a href="https://libertegrace.tistory.com/entry/40-Authentication-OAuth-20?category=869766">OAuth의 절차에 대해 정리한 참고 블로그</a></p>
<ol>
<li>소셜 서비스에 접근하기 위해 등록하기</li>
</ol>
<ul>
<li>이 때, &quot;나&quot;와 소셜서비스 사이에서 3가지의 정보를 공유한다.
a. client Id : 내가 구현할 서비스의 식별 ID, 
b. client secret : 실제로 소셜서비스가 &quot;나&quot;라는 서비스가 맞는지, 옳은 서비스가 접근하여 로그인을 하려고 하는지 식별하는 pw(보안이슈로 절대절대 노출되면 안된다.)
c. Authorized Redirect URL : 소셜 서비스가 인증이 가능하도록 권한을 부여하는 과정에서 그 인증코드(Authorized Code)를 전달해줄 경로</li>
</ul>
<ol start="2">
<li>등록 절차 이후, 실제로 서비스 사용자가 소셜로그인 버튼을 누른다면 어떤일이 발생 할까? 
우선 로그인 하고자 하는 사용자의 승인을 받아야 한다.</li>
</ol>
<ul>
<li>우선 소셜 서비스에 로그인 진행
a. 소셜 서비스에 사용자가 이미 로그인 되어있는 경우 
: 소셜서비스는 사용자가 타고 들어온 서비스의 사이트에 담겨져 있는 client ID을 점검한다. 
b. 사용자가 로그인되어있지 않아서, 해야하는 경우
: 로그인 화면 보여주어서 로그인 시키기</li>
<li>로그인 완료 후, 사용자가 타고 들어온 서비스에 담겨진 redirect url을 비교
a. 소셜서비스에서 해당 url을 가지고 있지 않다면 종료
b. 같은 url을 가지고 있다면, 사용자에게 서비스 제공자에게 로그인을 허용하는지에 대한 메세지를 띄운다. </li>
<li>허용한다고 답한 경우에, 그 응답이 서비스 제공자에게 전달. </li>
<li>서비스 제공자는 그 응답에 담긴, 사용자의 소셜 로그인에 대한 데이터를 소셜서비스로 부터 받는다. </li>
<li><blockquote>
<p>user id : [링크에 담겨있던 client id 값], scope:[그 링크에 담겨있던 scope]</p>
</blockquote>
</li>
</ul>
<ol start="3">
<li>사용자의 승인을 받았으니 이제 소셜서비스로부터 승인을 받아야 한다.</li>
</ol>
<ul>
<li>Authorization Code를 소셜서비스가 서비스 사용자에게 제공하는 응답의 header에 location: https://[redirect URL]?code=[Authorization Code]&#39;이라는 값을 주어 redirect하도록 한다. </li>
<li>location으로 인해, 서비스 사용자는 해당 주소로 redirect 되는 것이다.</li>
<li>따라서 서비스 제공자인 &quot;나&quot;는 redirect로 넘어온 URL뒤의 params형태로 담긴 authorization code를 알게된다. 그리고 아래 그림과 같은 형식의 주소로 서비스 제공자는 소셜서비스로 접속한다.</li>
</ul>
<p><img src="https://images.velog.io/images/hyunju-song/post/5f25f492-f37d-4069-bc3d-0aef4fd9bccd/image.png" alt=""></p>
<ul>
<li>이 때 서비스제공자인 &quot;나&quot;가 소셜서비스로 접속할때 가지고 간 주소링크에서 Authorizaion code와 clientID, client secret, redirect URL이 모두 일치하는지를 확인한다.</li>
<li>일치하면 드디어 Access Token을 발급한다.</li>
</ul>
<ol start="4">
<li>Acess Token을 발급받을 때</li>
</ol>
<ul>
<li>이제 초반 인증과정을 거쳤으므로 Authorizaion code를 지운다. 그리고 소셜서비스는 서비스제공자인 &quot;나&quot;에게 Access Token을 제공하고 나는 이것을 저장한다. </li>
<li>이후에는 소셜서비스는 내가 가지고 있는 Access Token을 통해 &#39;user id가 ~~인 사용자의 정보에 대해서 scope ~, ~ .. 을 허용&#39;한다.</li>
</ul>
<p>그렇다면 다음글에서 깃헙 소셜로그인 사례를 통해 실제 소셜로그인을 구현하는 과정을 정리해보고자 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[202010 프로젝트_8일차&9일차 회고]]></title>
            <link>https://velog.io/@hyunju-song/202010-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B889</link>
            <guid>https://velog.io/@hyunju-song/202010-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B889</guid>
            <pubDate>Mon, 19 Oct 2020 12:16:57 GMT</pubDate>
            <description><![CDATA[<h2 id="8일차">8일차</h2>
<h3 id="메인화면-get-메소드-구현">메인화면 get 메소드 구현</h3>
<p>메인화면에서 get메소드를 통해 모든 데이터 불러오기를 구현하고자 했다.
처음에는 매우 간단하다고 생각했는데, foreign key가 두 개나 있는 테이블에서 데이터를 불러와야 했으므로, 공부하고 이해하느라 생각보다 오래 걸렸다.</p>
<p>하지만 구현 완료 하였고, foreign key가 있는 경우, 데이터를 불러오는 것에 대해선 별도로 정리해두었으니 하단의 링크를 참고하면 될 듯 하다.
<a href="https://velog.io/@hyunju-song/sequelize%EC%97%90%EC%84%9C-foreign-key%EB%A1%9C-%EC%84%A4%EC%A0%95%EB%90%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%93%A4%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B0%80%EC%A0%B8%EC%98%AC-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C">foreign key 있을 경우, sequelize query문 작성하기</a></p>
<p><em>프로젝트를 하면서 느낀 점은, 단순한 폴더 구조라던가, 변수명 같이 소소하다고 생각한 부분에서 충돌이 많다는 점이다. 어떻게 보면 처음에 셋팅할 때는 별거 아니지만, 코드가 쌓이다보면 엄청나게 큰일이 될것이고..
매우 중요한 작업이라는 생각이 들었다.</em></p>
<p>앞으로 사이드 프로젝트든, 회사를 들어가서든 프로젝트든 일반 업무에서도 협업을 주로 하게 될텐데 이번 프로젝트에서 느꼈던 부분들을 잊지말고 많이 활용해야겠다.</p>
<h3 id="로그인-구현하기">로그인 구현하기</h3>
<p>일반로그인과 소셜로그인을 구현해야 했다.
우선은 일반로그인부터 구현하려했다. 
참고로 로그인의 경우, POST 메소드이므로, 서버 쪽에서 데이터가 오가는 상황이 보이질 않는다. 
이런 경우 postman 프로그램을 활용하면 된다!</p>
<p>포스트메소드로 전달 온 데이터를 따로 추가해주는 것이 아니라, 확인하는 작업만 해주면 되므로, sequelize의 findOne쿼리문을 사용해준다.</p>
<p><a href="https://velog.io/@hyunju-song/sequelize%EC%97%90%EC%84%9C-foreign-key%EB%A1%9C-%EC%84%A4%EC%A0%95%EB%90%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%93%A4%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B0%80%EC%A0%B8%EC%98%AC-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C">외래키 있는경우에 get Method 구현하기</a></p>
<p>또한 로그인에서 핵심은 로그인이 성공하면, session값에 새로운 키값을 만들어서 넣어주는 것이다. </p>
<h2 id="9일차">9일차</h2>
<h3 id="프로젝트-회고5f">프로젝트 회고(5F)</h3>
<h4 id="fact-사실--주요-사건에서-내가-취한-행동을-객관적으로-서술한다">Fact (사실) : 주요 사건에서 내가 취한 행동을 객관적으로 서술한다</h4>
<ul>
<li>서버를 구현하기 위해 필요한 모듈들을 설치한 후, 셋팅작업을 진행했다. 그리고 DB를 구축한 후
가장 메인 페이지에서 모든 데이터를 받아올 수 있는 메소드를 구현했다.</li>
</ul>
<h4 id="feeling-느낌--그때-느꼈던-기분을-간략히-정리한다">Feeling (느낌) : 그때 느꼈던 기분을 간략히 정리한다</h4>
<ul>
<li>초기 세팅이 진짜 너무 매우 중요하구나를 느꼈다. 그리고 생각보다 나는 더디고 속도는 느리니까 조급함을 느끼게 되었다.</li>
</ul>
<h4 id="finding-교훈--사건에서-얻은-교훈을-적는다">Finding (교훈) : 사건에서 얻은 교훈을 적는다</h4>
<ul>
<li>실제로 완성한 코드는 적지만 교훈은 많이 얻었다....</li>
</ul>
<ol>
<li>초기 세팅이 매우 중요하다...
메인화면에서 받아오는 데이터는 두개의 foreign key(외래키)를 가지고 있는 테이블이라 단순하게 get을 해오면 되는 것이 아니었다. 외래키를 제대로 셋팅하고, 그리고 진행을 했었어야 했는데, 그래 이거면 되겠지하고 넘어갔다가 메인에서 get 메소드를 작성하다가 DB를 잘못 셋팅했음을 깨닫고 부랴부랴 고쳤다. 이거 때문에 뒷단의 다른 서버 작업들이 진행이 늦어진 점에 너무 아쉽다 ㅠㅠ</li>
<li>나는 생각한 것보다 더디고 느리지만 조급해하지말고 하나하나 꼼꼼히 해야함을 느꼈다. 일단 구현하면 되지! 라는 생각을 버리고, 현재 구현해야 하는 기능과 필요한 것들을 공부하고 이해하면서 코드를 작성해야 이후에 에러 발생에도 대처할 수 있고 코드가 더 복잡해지더라도 헷갈리지 않음을 느꼈다.</li>
</ol>
<h4 id="future-action행동--그래서-내가-앞으로-취할-행동을-미래형으로-적는다">Future action(행동) : 그래서 내가 앞으로 취할 행동을 미래형으로 적는다</h4>
<ul>
<li>조금 느리지만 꼼꼼하게 코드를 작성해서 최대한 되돌아가서 코드를 수정하는 일이 없도록!!</li>
</ul>
<h3 id="소셜로그인은-추가-공부">소셜로그인은 추가 공부</h3>
<p>소셜로그인은 client와 server가 강하게 연계된 작업이기도하고,
아직 이해가 잘 되지 않아서 다같이 작업해보기로 했다.
따라서 오늘은 추가 코드 작성보다는 소셜로그인에 대해 공부해보았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 문제풀이 10]]></title>
            <link>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-10</link>
            <guid>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-10</guid>
            <pubDate>Sun, 18 Oct 2020 11:30:10 GMT</pubDate>
            <description><![CDATA[<p>주어진 수가 소수인지를 점검하는 알고리즘문제이다.
소수이면 true, 소수가 아니면 false를 반환한다.
해당 알고리즘에서 핵심은 소수를 찾는 것도 있지만, 
Math.sqrt() 를 활용하는 것이다.</p>
<p><a href="https://libertegrace.tistory.com/entry/%EC%A0%95%EC%88%98%EB%A1%A0-%EC%86%8C%EC%88%98">정수론에 대한 참고 블로그</a>
<strong>소수에 대한 알고리즘 문제는 크게 두 가지이다</strong></p>
<ol>
<li>소수인지 아닌지?</li>
<li>주어진 숫자 범위내에서 소수 골라내기</li>
</ol>
<p><strong>왜 주어진 수의 제곱근보다 작은 수 중에서 주어진 수의 약수가 없으면 왜 소수일까???</strong></p>
<blockquote>
<p>이것은 주어진 수의 약수리스트가 제곱근들보다 모두 클수가 없기 때문이다.
무슨 말일까?  모든 수는 n = a* b 즉, 두수의 곱으로 표현될 수 있다.
숫자 64를 예를 들어보자.
64의 제곱근은 8이므로 8* 8로 표현될 수 있다.
그런데 8보다 큰 수로만 64를 표현할 수없다. 
9* 9 라던가, 10* 9 라던가 다 64보다 크다. 
즉, 주어진 수 = 제곱근보다 작은 수  * 제곱근보다 큰수 로 구성되어있으므로, 제곱근보다 작은 수들을 약수로 가지는지 안가지는 지만 확인하면 되는 것!</p>
</blockquote>
<ul>
<li>참고 1 : <a href="https://stackoverflow.com/questions/40200089/number-prime-test-in-javascript">https://stackoverflow.com/questions/40200089/number-prime-test-in-javascript</a></li>
<li>참고 2 : <a href="https://www.thepolyglotdeveloper.com/2015/04/determine-if-a-number-is-prime-using-javascript/">https://www.thepolyglotdeveloper.com/2015/04/determine-if-a-number-is-prime-using-javascript/</a></li>
</ul>
<pre><code class="language-js">var primeTester = function (n) {
  if (typeof n !== &#39;number&#39; || n &lt;= 1 || !Number.isInteger(n)) {
    // n 이 숫자가 아니거나 n이 1보다 작거나 or n이 정수가 아닌 경우
    //n%1!==0 처럼 소수인지 정수인지를 점검하는 건 메소드를 써준다
    return false;
  } else if (n === 2) {
    return true; //즉 n이 1이거나 2일 때 소수이다
    //1은 소수가 아니라서  n&lt;=2가 아니라 n===2여야 한다.
  } else if (n &gt; 2 &amp;&amp; n % 2 === 0) {
    return false; //n이 2보다 크고 짝수이면 무조건 소수가 아니다
  } else {
    //? 숫자의 제곱근까지 루프를 실행하면 알고리즘의 복잡성을 O (n)에서 O (sqrt (n))로 줄일 수도 있다.
    //제곱근 활용하지 않고 그냥 숫자를 반복문을 돌다보니 테스트를 통과하지 못하고 시간복잡도가 너무 오래 걸림.

    let num = parseInt(Math.sqrt(n)); // n의 제곱근에서 소수자리 버려주고 정수화

    for (let i = 3; i &lt;= num; i = i + 2) {
      //소수인지 알아보려 해당 조건까지 내려온 수는 1보다 큰 홀수일 것이므로, 약수인지 비교하는 수들에서 짝수는 건너뛰어도 된다.
      if (n % i === 0) {
        //약수가 하나라도 있으면 바로 false 반환
        return false;
      }
    }
    return true;
  }
};

/* Extra credit: Write a function that generates a list of all prime numbers
 * in a user-specified range (inclusive). If you&#39;re not quite sure where to start,
 * check out the Sieve of Eratosthenes on Wikipedia. (And if you&#39;re feeling
 * saucy, check out the Sieve of Atkin.)
 */

//해당 부분은 &quot;에라토스테네의 체&quot;활용해서 구현하기
//var primeSieve = function (start, end) {
//  let arr = [];
//  for (let i = start; i &lt;= end; i++) {
//    if (primeTester(i) === true) {
//      arr.push(i);
//    }
//  }
//  return arr;
//};</code></pre>
<h4 id="에라토스테네의-체에-대한-이해가-핵심">에라토스테네의 체에 대한 이해가 핵심</h4>
<p>개념에 대한 이해는 완료되었고, 코드 구현이 중요했다.
해당 알고리즘 코드는 같이 공부하는 동기분의 코드가 리뷰 후에도 가장
효율적이라는 결론이 나와서, 참고하기로 하여 가지고 온 것이다.</p>
<p>구체적인 설명은 상단의 링크를 참고하면 된다!
(현정님의 블로그는 항상 최고다)</p>
<pre><code class="language-js">//소수문제풀 때 항상 조심해야하는 두 수: 1은 소수가 아니다. 2는 소수이다.
//다 소수(true)라고 치고, 소수가 아닌 것(false)을 제외시켜 나간다.

var primeSieve = function (start, end) {
  let Era = []; //start부터 end까지의 수가 차례대로 들어가있는 배열
  for (let i = start; i &lt;= end; i++) {
    Era.push(i);
  } //끝까지 Era 배열에 남아있는수가 소수들이다.
  if (start === 1) {
    Era.splice(0, 1);
  } //1은 소수가 아니고 모두가 다 1의 배수이므로 처음부터 제외하고 시작한다.
  //에라토스테네스의 체 시작

  let inx = 0;
  let len = Era.length;
  for (let i = 0; i &lt; len; i++) {
    let curr = Era[inx];
    if (curr === undefined) {
      return Era;
    }
    //console.log(Era, curr)
    if (primeTester(curr)) {
      //소수이면 그것만 놔두고
      for (let mul = 2 * curr; mul &lt;= end; mul += curr) {
        //그것의 배수는 다 제거
        if (Era.includes(mul)) {
          Era.splice(Era.indexOf(mul), 1);
        }
      }
      inx++;
    } else {
      //소수 아니면 제거
      Era.splice(Era.indexOf(curr), 1);
    }
  }
  //남은게 소수들로 구성된 배열
  return Era;
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 문제풀이 9]]></title>
            <link>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B49</link>
            <guid>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B49</guid>
            <pubDate>Sun, 18 Oct 2020 11:03:51 GMT</pubDate>
            <description><![CDATA[<p>주어진 소수점을 분수의 형태로 바꾸는 알고리즘 문제였다.
여기서 가장 유용하게 사용된, 메소드는 </p>
<ul>
<li>Math.pow(a,b) : a를 b만큼 곱하는 제곱메소드</li>
<li>Math.floor() : 주어진 수를 소수점 이하로 버림한 수를 반환</li>
<li><blockquote>
<p>Math.floor(10.1) = 10</p>
</blockquote>
</li>
<li><blockquote>
<p>Math.floor(10.9) = 10</p>
</blockquote>
</li>
<li>Match.ceil() : 주어진 수를 소수점 이하로 버리고 올림한 수를 반환</li>
<li><blockquote>
<p>Math.floor(10.1) = 11</p>
</blockquote>
</li>
<li><blockquote>
<p>Math.floor(10.9) = 11</p>
</blockquote>
</li>
<li>Math.round() : 소수점 이하로 버리고 반올림한 수</li>
<li><blockquote>
<p>Math.round(10.1) = 10</p>
</blockquote>
</li>
<li><blockquote>
<p>Math.round(10.9) = 11</p>
</blockquote>
</li>
<li>parseFloat() : 주어진 수를 실수로 바꾸기(소수점 나오게)</li>
<li>pareInt(): 주어진 수를 정수로 바꾸기</li>
<li>Math.max(a,b) : a,b 중에서 큰 수</li>
<li>Math.min(a,b) : a,b 중에서 작은 수</li>
</ul>
<pre><code class="language-js">var toFraction = function (number) {
  let strNum = number.toString();
  //let numLength = strNum.length - 1; //왜냐면 소수점부분을 빼야 진짜 숫자의 갯수이므로 -1을 해준 것.
  let inputNum = number;
  if (strNum[0] === &#39;-&#39;) {
    //주어진 인자가 음수라면
    //numLength = strNum.length - 2; //실제 숫자의 갯수는 소수점과, 마이너스 기호를 뺀 갯수이므로 -2 이다.
    //let minus = strNum.slice(1, strNum.length); //마이너스 기호를 뺀 숫자 부분을 발라냄(소수점자체는 포함)
    strNum = strNum.slice(1, strNum.length);
    inputNum = parseFloat(minus);
  }
  //위의 조건문 자체는 음수가 주어졌을때, 마이너스라는 기호를 배제하고 우선 수를 변환시키기 위한 작업을 위해 필요.

  //let ten = Math.pow(10, numLength - 1);
  //위의 변수는 소수점을 분수로 만들기 위해, 필요한 10의 제곱 수를 구하기 위함이다.
  //가령 2.5인 경우에는 10^1을 곱하고 그 분모로 들어가게 된다.

  //-&gt;이 부분은 미스였다. 10.2 같이 소수점 위의 정수 부분이 두자리 이상인 경우를 인지하지 못했다.
  let float = strNum.slice(strNum.indexOf(&#39;.&#39;)+1, strNum.length);
  //slice메소드는 앞에 위치한 인덱스(포함) 끝의 인덱스-1의 값만을 뽑아내서 새로운 배열을 만들어준다.
  let floatLength = float.length;

  let ten = Math.pow(10, floatLength);

  // 소수점이 0인 경우
  if (inputNum - Math.floor(inputNum) === 0) {
    return `${Math.floor(inputNum)}/1`;
  } else {
    // 소수점이 0이 아닌 경우 + 5로 나눠지는 경우

    let newNum = parseInt(inputNum * ten);
    // 주어진 수를 분수로 만들기 위해 10의 제곱 수 중 하나를 곱하고 나서 parseInt를 써주지 않으면, 소수점 이하의 0까지 같이 불린다.

    //두수의 최대공약수 구하기(소수점이 0이 아닌 수에 필요하다)
    let max = 1;
    // 우선 최대공약수는 최초로는 1로 선언. 공약수가 없는 두수가 있을 수도 있으므로
    //가령 number 0.88 일경우, 최대공약수 구해야 하는 수는 88과 100이된다. 두 수의 공약수 구할때는 비교하는 두수 중에
    //작은 수 까지만 비교하면 되므로, 88까지 반복문 돌린다
    // 아래의 경우에서 분자가 분모보다 큰 경우를 고려한 조건문을 넣어줘야한다.
    let small = Math.min(newNum, ten)

    for (let i = 1; i &lt;= small; i++) {
      if (newNum % i === 0 &amp;&amp; ten % i === 0) {
        max = i;
      }
    }
    if (number &lt; 0) {
      return `-${newNum / max}/${ten / max}`;
    }
    return `${newNum / max}/${ten / max}`;
  }
};</code></pre>
<h4 id="해당-문제의-핵심은-최대공약수와-최소공배수에-대해-이해하고-알고리즘을-구현하는-것이-아닐까하는-생각이-들었다">해당 문제의 핵심은 최대공약수와 최소공배수에 대해 이해하고 알고리즘을 구현하는 것이 아닐까하는 생각이 들었다.</h4>
<p><a href="https://libertegrace.tistory.com/entry/%EC%A0%95%EC%88%98%EB%A1%A0?category=855348">유클리드 호제법(최대공약수, 최소공배수 구하기)을 구현하는 알고리즘</a></p>
<ul>
<li>최대공약수를 구하는 알고리즘 코드<pre><code class="language-js">let originA = A;
let originB = B;
let GCD;
</code></pre>
</li>
</ul>
<p>while (1) {
    let r = A % B;
    if (r === 0) {
      GCD = B;
      break;
    }
    A = B;
    B = r;
}</p>
<p>```</p>
<ul>
<li>최소공배수를 구하는 알고리즘 코드</li>
<li><blockquote>
<p>이는 참고 블로그를 보면 알수있겠지만 구현하기 매우 쉽다.
(한 10몇년 전에 학교에서 배운듯한 최소공배수의 원리는 결국
최소공배수 = (A*B)/최대공약수이다.</p>
</blockquote>
</li>
<li><blockquote>
<p>따라서 위의 알고리즘 코드로 최대공약수를 구하면 쉽게 최소공배수도 구할 수 있다.</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2010 프로젝트 초기세팅_7일차]]></title>
            <link>https://velog.io/@hyunju-song/2010-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B4%88%EA%B8%B0%EC%84%B8%ED%8C%857%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@hyunju-song/2010-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%B4%88%EA%B8%B0%EC%84%B8%ED%8C%857%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sun, 18 Oct 2020 10:20:43 GMT</pubDate>
            <description><![CDATA[<p>확실히 첫 프로젝트다보니 초기세팅에서부터 헤매게 되는 것같다.</p>
<ul>
<li><p>초반에 upstream에서 필요한 모듈들을 다 설치했는데,
생각해보니 server와 client를 폴더를 나누어서 각각 필요한 모듈을 따로 셋팅했어야 하지 않았나하는 생각이 들었다. </p>
</li>
<li><p>그리고 가장 중요한 건, 초반에 코드 작성 전에, git pull upstream 하는것 중요!</p>
</li>
<li><p>branch 이름만들때 실수를 많이 할 것 같아서, branch이름 수정하는 건 꼭 외워두자!</p>
</li>
</ul>
<p><strong>branch 이름 변경하기</strong></p>
<blockquote>
<p>git branch -m의 &quot;변경전_branch_name&quot; &quot;새로운_branch_name&quot;</p>
</blockquote>
<ul>
<li>내 작업 깃레포에 푸쉬할때 main브랜치가 아닌 작업한 브랜치에 푸쉬하는 거 익숙해지기!</li>
</ul>
<h3 id="기본적인-서버-세팅부터-진행">기본적인 서버 세팅부터 진행.</h3>
<ul>
<li>cors 모듈관련 이야기.</li>
<li>server와 client를 위한 폴더 나누기 =&gt; 모듈 설치(차후 프로젝트 진행헤 필요)</li>
<li>배포 진행 필요</li>
</ul>
<h3 id="기본-db-세팅mac-os-기준">기본 DB 세팅(mac OS 기준)</h3>
<ul>
<li>mysql 실행(터미널 명령어는 정말 외우기 힘들다...)</li>
</ul>
<ol>
<li>Homebrew를 이용한 설치
$ brew install mysql</li>
<li>MySQL 서비스 시작
설치 후에 MySQL 서비스를 실행해야 MySQL을 사용할 수 있다.
macOS
$ brew services start mysql</li>
<li>MySQL 접속
mysql -u(계정 접근) [계정명] -p(비밀번호 입력)
$ mysql -u root -p</li>
<li>sql 문으로 필요한 데이터를 담을 테이블을 만든다</li>
<li>그리고 sequelize를 통해 각 table을 만든다.</li>
</ol>
<p>-&gt; 이때 config에 담길 데이터에 대해, database이름은 내가 만든 데이터 베이스를 입력해서 연결해야한다.
<a href="https://sequelize.org/master/manual/migrations.html">sequelize 공식문서</a>
-&gt; 참고로 다른 공식문서는 모르겠는데 sequelize 공식문서는 엄청 도움이 된다.
6. sequelize의 핵심은 외래키(forein key를 어떻게 연결할 것이냐? 였다)</p>
<h3 id="sequelize로-foriegn-key-생성하기">sequelize로 foriegn key 생성하기</h3>
<p><a href="https://velog.io/@cadenzah/sequelize-document-4">sequelize foreign key 설정에 대한 설명</a></p>
<p>프라이머리 키는 사실 굳이 sequelize에서 설정하지 않아도 되지만, 
외래키는 의외로 mysql만으로 데이터 베이스를 정리할 때보다 복잡하다.
위의 블로그는 그 외래키 설정에 대한 설명이다. </p>
<p>우선 외래키를 설정 코드를 작성해야 하는 곳은, model의 테이블 파일들이다.
associate 함수 내에 작성해주면된다.</p>
<p><strong>외래키 설정에 대해서 더 공부</strong>
<a href="https://velog.io/@hyunju-song/sequelize%EC%97%90%EC%84%9C-foreign-key-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0">블로그에 외래키에 대한 추가설명글</a></p>
<h3 id="서버코드-aws-ec2로-배포하기">서버코드 AWS EC2로 배포하기</h3>
<p><a href="https://velog.io/@hyunju-song/AWS%EB%A1%9C-node.js-%EC%84%9C%EB%B2%84%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0">AWS로 node.js 서버 배포하기</a>
우리 서버의 퍼블릭 주소는 <strong><a href="http://3.34.136.75:%ED%8F%AC%ED%8A%B8%EB%B2%88%ED%98%B8/">http://3.34.136.75:포트번호/</a></strong>이다.</p>
<p>client와 DB는 차차 배포하기로 하였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[sequelize에서 foreign key로 설정된 데이터들을 어떻게 가져올 수 있을까?]]></title>
            <link>https://velog.io/@hyunju-song/sequelize%EC%97%90%EC%84%9C-foreign-key%EB%A1%9C-%EC%84%A4%EC%A0%95%EB%90%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%93%A4%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B0%80%EC%A0%B8%EC%98%AC-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C</link>
            <guid>https://velog.io/@hyunju-song/sequelize%EC%97%90%EC%84%9C-foreign-key%EB%A1%9C-%EC%84%A4%EC%A0%95%EB%90%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%93%A4%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B0%80%EC%A0%B8%EC%98%AC-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C</guid>
            <pubDate>Sun, 18 Oct 2020 08:21:31 GMT</pubDate>
            <description><![CDATA[<p>의외로 foreign key가 이론적으로는 쉽지만 실질적으로 활요용하고자 할 때, 
복잡한 개념이란 사실을 깨닫게 되었다.</p>
<p>foreign key를 데이터에 설정하는 법에 대해서는 이미 정리했고, 
이번에는 실제로 서버에서 물러올 때 어떻게 코드를 작성하면 좋을 지 정리해보고자 한다.
특히나, 이번에 내가 진행하는 프로젝트에서 구현하려는 기능 중 하나가,
foreign key를 2개나 지닌 데이터에서 한꺼번에 데이터를 불러오는 것이기 때분에 더 정리해보고자 한다.</p>
<p><a href="https://sequelize.org/master/manual/eager-loading.html">sequelize 공식문서</a></p>
<p>위의 공식문서를 차근차근 따라가며 테스트 해보았다. </p>
<ul>
<li>우선 외래키로 연결된 데이터를 가져올 때, id가 같은 경우, 그니까 외래키의 조건을 추가해줘야 하는줄 알았는데, 이미 연결되어 있으므로, 별도로 조건을 주지 않아도 되었다!</li>
<li><blockquote>
<p>이걸로 계속 서버 에러가 발생하다가 발견한 것이라 잘 기억해 두면 좋을 듯.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-js">reviews
    .findAll({
        where: {
          active: true,
        },
        include: {
          model: users,
          as: &#39;useremail&#39;,
          where: {
            active: true,
          },
          attributes: [&#39;email&#39;],
        },</code></pre>
<ul>
<li>reviews와 users 모델의 관계는 N:1의 관계이다. 
따라서 reviews 내에 users_id 라는 키값이 users의 id값을 받고 있다.</li>
<li>위에서 설명한 바와 같이, 외래키로 연결된 데이터는 별도로 where 조건으르 주지 않아도 연결되어 데이터를 제공한다.</li>
<li>위의 쿼리는 reviews의 모든 데이터를 get하는데, 그 중에, 외래키로 연결된 테이블인 users에서 email 값만 받아오려 한다.</li>
<li><blockquote>
<p>여기서, reviews와 users 데이터 값이 true인 값만 받아와야 하므로 where 조건에 해당 조건들을 추가한 것이다.</p>
</blockquote>
</li>
</ul>
<h3 id="그렇다면-한-테이블-안에-외래키가-2개-이상인-경우는">그렇다면 한 테이블 안에 외래키가 2개 이상인 경우는?</h3>
<p>내가 구현하고자 하는 데이터는 최종적으로는 두개의 테이블과 외래키로 연결되어 있다.
<img src="https://images.velog.io/images/hyunju-song/post/7fd7e312-1514-40d1-8b50-9b5111659531/db%20schema.png" alt=""></p>
<p>위의 코드를 활용하여, include 조건을 추가했지만, 2개의 연결된 데이터가 다 받아와지지 않고, 하나만 받아와졌다.
=&gt; 해결책은 간단했다.</p>
<pre><code class="language-js">include: [
          {
            model: users,
            as: &#39;useremail&#39;,
            where: {
              active: true,
            },
            attributes: [&#39;email&#39;],
          },
          {
            model: bootcamp_list,
            as: &#39;bootcampname&#39;,
            attributes: [&#39;name&#39;],
          },
        ],</code></pre>
<ul>
<li>위와 같이, include할 테이블이 여러개면, 이 데이터들으르 전체적으로
배열형태로 묶고, 그 내에서 객체 형태로 조건을 지정하는 것이다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[sequelize에서 foreign key 설정하기]]></title>
            <link>https://velog.io/@hyunju-song/sequelize%EC%97%90%EC%84%9C-foreign-key-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyunju-song/sequelize%EC%97%90%EC%84%9C-foreign-key-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 17 Oct 2020 13:06:17 GMT</pubDate>
            <description><![CDATA[<p>데이터들은 실제로 단발적이고 독립적으로 구성되어 있는 것이 아니라, 
서로서로가 연결되어 있다.</p>
<p>따라서 DB구성 시에, 특히 sequelize로 작업할 때, 외래키(foreign key)를 어떻게 설정하는 지에 대해서 정리해 보고자 한다.</p>
<p><a href="https://sequelize.org/master/manual/advanced-many-to-many.html">foreign key 설정에 대한 설명이 담긴 공식문서</a>
<a href="https://velog.io/@cadenzah/sequelize-document-4">foreign key 설정에 대한 추가 블로그</a></p>
<p>외래키를 설정하고자 여기저기 구글링을 해보았고, 공식문서 혹은 공식문서에 기반한 위의 게시글들을 발견하게 되었다.</p>
<p>위의 글들은 migration을 통해 table을 만들고, 그에 따라 만들어진
각 모델들에 특정 코드를 추가하라고 안내되어 있었다.</p>
<p>가령, users 테이블의 id 값과 bootcamp_lists 테이블의 id 값을 N:N의 관계로 설정하고자 하면, 각각의 model 파일에</p>
<pre><code class="language-js">//bootcamp_list의 id는 users 테이블과 다대다 관계이므로 가상의 users_bootcamp라는 테이블을 통해 연결되어있음을 명시
static associate(models) {
      // define association here
      bootcamp_list.belongsToMany(models.users, {through: &#39;users_bootcamp&#39;, targetKey:&#39;id&#39;, foreignKey: &#39;bootcamp_id&#39;})
    }

static associate(models) {
      // define association here
      users.belongsToMany(models.bootcamp_list, {through: &#39;users_bootcamp&#39;, targetKey:&#39;id&#39;, foreignKey:&#39;users_id&#39;})
    }</code></pre>
<p>처음에는 연결해주는 역할을 하는 users_bootcamp 테이블까지 별도로 만들어준 다음에, 위에서 설명한 대로 belongsToMany 메소드를 추가해주었다.</p>
<p><strong>결과는 아무런 일도 일어나지 않았다....
(왠지 그럴거 같았다.)</strong></p>
<p>따라서 여기저기 구글링을 꼼꼼하게 해본 결과, stackoverflow에서 답을 찾았다.(역시...)
<a href="https://stackoverflow.com/questions/29904939/writing-migrations-with-foreign-keys-using-sequelizejs">관련한 스택오버플로우 링크</a></p>
<h3 id="sequelize로-foreign-key-설정해주기">sequelize로 foreign key 설정해주기</h3>
<p>내가 진행했던 과정을 차근차근 정리해보고자 한다.</p>
<ul>
<li><p>우선 다대다 관계의 경우 가상의 테이블이 하나더 생기게된다.
이 테이블은 마이그레이션으로 생성해주면 안된다.</p>
</li>
<li><p>users 테이블을 마이그레이션을 통해 만들어주고, </p>
</li>
<li><p><em>npx sequelize-cli migration:generate --name users_fk_edit*</em>
해당 터미널 명령어를 통해, users 테이블을 수정하기 위한 마이그레이션 파일을 하나 더 만들어 주었다. </p>
</li>
<li><p>그리고 기존 users의 기본 구성에 then으로 외래키를 추가해주는 함수를 넣어주었다.</p>
<pre><code class="language-js">module.exports = {
up: async (queryInterface, Sequelize) =&gt; {
  await queryInterface.createTable(&#39;users&#39;, {
    id: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: Sequelize.INTEGER
    },
    email: {
      type: Sequelize.STRING
    },
    password: {
      type: Sequelize.STRING
    },
    githubId: {
      type: Sequelize.STRING
    },
    active: {
      type: Sequelize.BOOLEAN
    },
    createdAt: {
      allowNull: false,
      type: Sequelize.DATE
    },
    updatedAt: {
      allowNull: false,
      type: Sequelize.DATE
    }
  }).then(function(){
    queryInterface.createTable(&#39;users_bootcamp&#39;, {
      users_id:{
        type: Sequelize.INTEGER,
        references:{model: &#39;users&#39;, key: &#39;id&#39;}
      }
    })
  })
},</code></pre>
<p>즉, users의 id값을 레퍼런스로 가지는 users_bootcamp 테이블을 만들어 주고, 그 테이블의 연결된 외래키로 users_id를 생성!</p>
</li>
<li><blockquote>
<p>mysql로 확인하니까 잘 만들어진 것을 확인했다.</p>
</blockquote>
</li>
<li><p>다대다 관계이므로 또 다른 테이블 또한 users와 비슷한 터미널 명령어로 수정버전의 마이그레이션을 만들어 준다.</p>
<pre><code class="language-js">module.exports = {
up: async (queryInterface, Sequelize) =&gt; {
  await queryInterface.createTable(&#39;bootcamp_lists&#39;, {
    id: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: Sequelize.INTEGER
    },
    name: {
      type: Sequelize.STRING
    },
    createdAt: {
      allowNull: false,
      type: Sequelize.DATE
    },
    updatedAt: {
      allowNull: false,
      type: Sequelize.DATE
    }
  }).then(function(){
    queryInterface.addColumn(&#39;users_bootcamp&#39;,&#39;bootcamp_id&#39;,{
        type: Sequelize.INTEGER,
        references:{model: &#39;bootcamp_lists&#39;, key: &#39;id&#39;}
    })
  })
},</code></pre>
</li>
<li><blockquote>
<p>users와 다른점이 있다면, 이번에는 테이블을 만들어주는 것이 아니라, 
만들어진 테이블에 칼럼을 추가 하는 것이므로 addColumn 메소드를 사용해준다.</p>
</blockquote>
</li>
</ul>
<p>이렇게 만들고 나서 sequel Pro와 mysql을 통해 확인해보니 다대다 연결 테이블인 user_bootcamp가 잘 만들어져 있었다!</p>
<h3 id="fakedata-넣을-때-seed-작업">fakedata 넣을 때 seed 작업</h3>
<p>외래키가 있을 경우에는 seed 작업 또한 기존과는 다르게 작업해 주어야했다.
<a href="https://stackoverflow.com/questions/48732223/sequelize-seed-with-associations">foreign key있을 경우, seed 파일 기재에 대한 참고 링크</a></p>
<pre><code class="language-js">module.exports = {
  up: async (queryInterface, Sequelize) =&gt; {
    await queryInterface.bulkInsert(&#39;users&#39;, [
      {
        email: &#39;thdguswn93@naver.com&#39;,
        password: &#39;1234&#39;,
        githubId: &#39;hyunju-song&#39;,
        active: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ]);

    await queryInterface.bulkInsert(&#39;bootcamp_lists&#39;, [
      {
        name: &#39;codestates&#39;,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ]);

    const users = await queryInterface.sequelize.query(`SELECT id FROM users;`);
    const bootcamp = await queryInterface.sequelize.query(`SELECT id FROM bootcamp_lists`);
    const usersRows = users[0];
    const bootcampRows = bootcamp[0];

    await queryInterface.bulkInsert(&#39;users_bootcamp&#39;, [
      {
        users_id: usersRows[0].id,
        bootcamp_id: bootcampRows[0].id,
      },
    ]);

    return await queryInterface.bulkInsert(&#39;reviews&#39;, [
      {
        users_id: usersRows[0].id,
        bootcamp_id: bootcampRows[0].id,
        githublink: &#39;https://github.com/codestates/SAFU-server.git&#39;,
        price: &#39;비쌈&#39;,
        level: &#39;어려움&#39;,
        recommend: &#39;추천&#39;,
        curriculum: &#39;어려움&#39;,
        comment: &#39;자기주도 학습!!!!중심이다&#39;,
        active: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ]);
  },

  down: async (queryInterface, Sequelize) =&gt; {
    await queryInterface.bulkDelete(&#39;users&#39;, null, {});
    await queryInterface.bulkDelete(&#39;bootcamp_lists&#39;, null, {});
    await queryInterface.bulkDelete(&#39;users_bootcamp&#39;, null, {});
    await queryInterface.bulkDelete(&#39;reviews&#39;, null, {});
  },
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS로 node.js 서버배포하기]]></title>
            <link>https://velog.io/@hyunju-song/AWS%EB%A1%9C-node.js-%EC%84%9C%EB%B2%84%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyunju-song/AWS%EB%A1%9C-node.js-%EC%84%9C%EB%B2%84%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 17 Oct 2020 08:47:38 GMT</pubDate>
            <description><![CDATA[<p>AWS의 세계란 쉬운 듯 복잡한 듯하다...
분명 제대로 익힌 듯 하지만 막상 하려니 헷갈려서 이렇게 정리해보고자 한다.</p>
<ul>
<li><p>우선 내 local 컴퓨터에 서버를 만든다. 그리고 배포를 위해서 git에 해당 서버코드를 업로드 한다. 이때 git에 업로드 하는 방법은 평소 git 저장소에 저장하는 방법과 동일하다(add -&gt; commit -&gt; push)</p>
</li>
<li><p>aws ec2에 들어가서 인스턴스를 새로 만들자!
(인스턴스 시작을 누르면 된다.)
<img src="https://images.velog.io/images/hyunju-song/post/84a17de2-3432-4ea9-9aaf-52970ca2b093/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.12.10.png" alt=""></p>
</li>
<li><p>일반적으로는 우분투환경으로 서버를 만들게 된다. </p>
</li>
<li><blockquote>
<p>과금되지 않기 위해 프리티어 기능이 되는 것으로 골라서 셋팅하자! 
<img src="https://images.velog.io/images/hyunju-song/post/840ec9b2-ef63-44ac-a83c-8671e4a53d37/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.12.38.png" alt=""></p>
</blockquote>
</li>
</ul>
<p><img src="https://images.velog.io/images/hyunju-song/post/9cfd0470-b7ec-4328-9a49-7b74e8bb9d4f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.13.24.png" alt=""></p>
<ul>
<li><p>위의 화면에서 검토및 시작을 누르면 뜨는 화면에서
하단의 시작하기를 누르면 아래와 같이 키페어 생성 혹은 선택 창이 뜬다.</p>
</li>
<li><blockquote>
<p>이 때 설치되는 pem 키를 통해서 AWS 서버에 접속하는 것이므로,
관리를 잘 해야한다.</p>
</blockquote>
</li>
<li><blockquote>
<p>하단의 창에서 키를 생성하거나 만든 후에, 인스턴스 시작을 누른 후,
팝업창이 닫히고 인스턴스 보기를 누르면, 초반에 인스턴스 시작을 눌렀던 화면이 뜨게된다.</p>
</blockquote>
</li>
<li><blockquote>
<p>방금 내가 만든 인스턴스에는 이름이 없으므로, 설정해주는 것이 좋다.
<img src="https://images.velog.io/images/hyunju-song/post/52c57576-972d-4f01-b21e-e493ed58cd52/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.14.40.png" alt=""></p>
</blockquote>
</li>
<li><p>그리고 키페어를 새로 만든 다음에, AWS EC2에 연결하기 위해서, 펨키를 통해 local에서 작업해주어야 하는 것이 있다.
a. 바탕화면에 .ssh 폴더 만들고 키페어 이동하기</p>
</li>
<li><blockquote>
<p>이때 mkdir .ssh/로 폴더를 만들고 (.ssh/해주는 이유는 해당 폴더가 숨겨지기 위해서다.)
b. mv 명령어를 통해서 키페어를 옮기자!</p>
</blockquote>
</li>
<li><p>그리고 아래의 화면에서 작업-&gt; 연결을 누른다
<img src="https://images.velog.io/images/hyunju-song/post/11715561-aa46-4149-9618-6556fb2b6730/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.32.01.png" alt=""></p>
</li>
<li><p>그리고 아래와 같이 SSH 클라이언트탭으로 들어가면 아래와 같은 창이 뜬다
a. 설명되어 있는 바와 같이 터미널에서 SSH폴더에 들어간다. 
b. 그리고 chmod &quot;펨키명&quot; 으로 펨키를 숨김처리 해준다.
c. 그다음에 터미널 명령어에 </p>
</li>
<li><p><em>ssh -i ~/.ssh/&quot;펨키명&quot; ubuntu@public ipv4 주소*</em>
입력하면 서버 컴퓨터로 접속하게 된다.
<img src="https://images.velog.io/images/hyunju-song/post/6fa31be7-6eec-4f3d-939f-2c00d264be03/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.34.13.png" alt=""></p>
</li>
<li><p>서버컴퓨터에 접속하고 서버실행을 위해 해야할 일
a. 서버 컴퓨터에 sudo apt update해서 필요한거 설치
b. git clone으로 서버 레포주소를 서버컴퓨터에 설치
c. npm install 해주고 서버 시작 확인하기</p>
</li>
<li><p>퍼블릭하게 하려면 보안그룹에서 인바운드 탭 지정해줘야한다.
a. 인스턴스들이 모여있는 화면에서 내가 보안설정을 해주려는 서버를 클릭하면 아래와 같이 하단 창이 보인다.
<img src="https://images.velog.io/images/hyunju-song/post/602270dc-6bae-485d-aae3-3f6a97c2ec37/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.44.09.png" alt="">
b. 하단 창의 보안탭에서 보안 그룹 링크를 누르면 인바운드 규칙을 설정할 수 있는 창이 뜬다.
c. 아래의 인바운드 규칙 예시를 참고로 하여, 보안 그룹 설정하면 된다!</p>
</li>
</ul>
<p><strong>프로젝트 진행을 위해 만든 인바운드 규칙 예시!</strong>
<img src="https://images.velog.io/images/hyunju-song/post/f0d0cef2-04a7-41ee-8f25-b550184b2923/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-10-17%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.35.49.png" alt=""></p>
<p>-&gt; ipv4 퍼블릭 주소와 포트 번호 조합으로 서버 배포 가능!</p>
<p><strong>내가 헷갈렸던 배포에 대한 개념</strong>
사람들이 들어오는 도메인은 S3에서 만든 버켓 주소로 들어오게 된다.
즉 클라이언트 주소로 사용자들과 소통한다.
그 주소를 도메인을 사게 되면 된다.</p>
<p>즉 사용자들은 클라이언트와 연동된 코드만 받는것이고, 실제 서버 코드는
통신을 위해 퍼블릭하게 배포해 두는 것이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[sequelize로 DB셋팅할 때, 환경변수 파일 설정 및 사용하기]]></title>
            <link>https://velog.io/@hyunju-song/sequelize%EB%A1%9C-DB%EC%85%8B%ED%8C%85%ED%95%A0-%EB%95%8C-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95-%EB%B0%8F-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyunju-song/sequelize%EB%A1%9C-DB%EC%85%8B%ED%8C%85%ED%95%A0-%EB%95%8C-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95-%EB%B0%8F-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 17 Oct 2020 04:21:54 GMT</pubDate>
            <description><![CDATA[<p>sequelize 뿐 아니라, 데이터베이스를 관리하거나 기타 시스템을 구축할 때, 패스워드와 같이 유출되어서는 안되는 정보를 다루어야 하는 경우가 있다.
이런 경우는 바로 코드에 작성해서 git에 올리면 매우 위험하므로, 
별도로 환경 설정 파일인 .env를 만들어서 여기에 저장해두고, 이 환경설정 파일을 gitignore에 추가하여, git commit 되지 않도록 막아둔다.</p>
<h2 id="dotenv-모듈-사용하기">dotenv 모듈 사용하기</h2>
<p>바로 이런 환경 변수 파일을 설정하고 사용하기 위해 필요한 node의 모듈이 dotenv이다.
우선 npm install dotenv --save-dev 로 해당 모듈을 설치하는 것은 간단하다! 
그리고 require(&#39;dotenv).config() 로 모듈을 불러와서 환경 설정 파일을 불러오면 되는 것 까지.
이론적으론 이해가 되는데 실제로 사용하려고 하니까 너무 헷갈렸다.</p>
<p>현재 프로젝트를 위해 데이터베이스를 셋팅 중이었고, sequelize를 통해 데이터 베이스를 다루고자 했던 나는, sequelize를 설치하며 다운 받아진 config.json 파일을 통해 password나 host 등등을 관리하는 것은 알았다.
그러나 바로 이 파일에 패스워드를 입력하는 것은 불가능!
어떻게 환경 변수 파일을 만들어서 패스워드를 관리하는 지에 대해서 공부했고, 계속해서 사용될 기능이라 정리해두고자 했다.</p>
<h2 id="sequelize에서-환경변수파일-설정하고-사용하기">sequelize에서 환경변수파일 설정하고 사용하기</h2>
<p>우선 sequelize의 공식문서에 따라, 해당 모듈을 설치하고, 실행하면 
config 폴더와 config.json 파일과 함께 기본적인 model과 migration 폴더들이 생성된다.</p>
<h3 id="configjson-수정하기">config.json 수정하기</h3>
<p>해당 파일이 우리가 데이터베이스를 사용하는데 필요한 정보들을 저장해두는 곳이다.
하지만 json 파일은 모듈으르 불러오고 사용하는 것이 불가능하다. 
따라서, dotenv 모듈을 불러와서 사용하려면 해당 파일을 config.js로 바꾸고, 내부를 수정해야 한다.</p>
<p><strong>기존 config.json</strong></p>
<pre><code class="language-js">{
  &quot;development&quot;: {
    &quot;username&quot;: &quot;root&quot;,
    &quot;password&quot;: null,
    &quot;database&quot;: &quot;database_development&quot;,
    &quot;host&quot;: &quot;127.0.0.1&quot;,
    &quot;dialect&quot;: &quot;mysql&quot;
  },
  &quot;test&quot;: {
    &quot;username&quot;: &quot;root&quot;,
    &quot;password&quot;: null,
    &quot;database&quot;: &quot;database_test&quot;,
    &quot;host&quot;: &quot;127.0.0.1&quot;,
    &quot;dialect&quot;: &quot;mysql&quot;
  },
  &quot;production&quot;: {
    &quot;username&quot;: &quot;root&quot;,
    &quot;password&quot;: null,
    &quot;database&quot;: &quot;database_production&quot;,
    &quot;host&quot;: &quot;127.0.0.1&quot;,
    &quot;dialect&quot;: &quot;mysql&quot;
  }
}</code></pre>
<p><strong>config.js로 변경 후, 파일 재작성</strong></p>
<pre><code class="language-js">require(&#39;dotenv&#39;).config();
const env = process.env;

const development = {
  username: env.MYSQL_USERNAME,
  //env.MYSQL_USERNAME은 불러오고자 하는 데이터의 키값이므로 자유롭게 이름설정이 가능하다.
  password: env.MYSQL_PASSWORD,
  database: env.MYSQL_DATABASE,
  host: env.MYSQL_HOST,
  dialect: &quot;mysql&quot;,
  //port: env.MYSQL_PORT
};

const production = {
  username: env.MYSQL_USERNAME,
  password: env.MYSQL_PASSWORD,
  database: env.MYSQL_DATABASE,
  host: env.MYSQL_HOST,
  dialect: &quot;mysql&quot;,
  //port: env.MYSQL_PORT
};

const test = {
  username: env.MYSQL_USERNAME,
  password: env.MYSQL_PASSWORD,
  database: env.MYSQL_DATABASE_TEST,
  host: env.MYSQL_HOST,
  dialect: &quot;mysql&quot;,
  //port: env.MYSQL_PORT
};

module.exports = { development, production, test };</code></pre>
<h3 id="modelindexjs-파일-수정하기">model/index.js 파일 수정하기</h3>
<p>model/index.js는 서버에서 DB를 실행했을 때, 어떤 경로를 통해 어떤 파일을 불러와서 실행하는지가
기재되어있다.
우리는 dotenv 모듈을 사용하기 위해, config폴더 내의 파일을 변경했으므로, 당연히
model/index.js 도 수정해주어야 한다.</p>
<p><strong>변경 전</strong></p>
<pre><code class="language-js">const config = require(__dirname + &#39;/../config/config.json&#39;)[env];</code></pre>
<p><strong>변경 후</strong></p>
<pre><code class="language-js">const config= require(__dirname + &#39;/../config/config.js&#39;)[env]</code></pre>
<p><em>간단하다! 결국 불러오는 파일명만 바꿔주면 되는것!</em></p>
<h3 id="env-파일-만들어주기">env 파일 만들어주기</h3>
<p>나는 초반에 이것도 헷갈렸다. root폴더에 만들어주라길래 처음에는 
가장 초반(그러니까 server폴더보다도 더 상위에) 위치에 만들어 주었다.
하지만 실행이 되지 않았다.</p>
<p>결국 server폴더 내에서, 즉 해당 서버가 실행돼서 DB를 불러오는 그 위치가 env파일을 만들어줘야할
root 폴더인 것이다. </p>
<p><strong>.env 파일</strong></p>
<pre><code class="language-js">MYSQL_USERNAME= (db 사용자이름)
MYSQL_PASSWORD= (비번)
MYSQL_DATABASE= (사용하고자 하는 데이터베이스 이름)
MYSQL_HOST=127.0.0.1</code></pre>
<p>.env 파일은 꼭 gitignore에 추가 해야하며, 
해당 파일 내에서 값들을 저장하는 각 키값들은 (MYSQL_USERNAME 같은거) 원하는 대로 임의 설정 가능하다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[body-parser의 urlencoded는 도대체 어떤 역할을 하는 걸까?]]></title>
            <link>https://velog.io/@hyunju-song/body-parser%EC%9D%98-urlencoded%EB%8A%94-%EB%8F%84%EB%8C%80%EC%B2%B4-%EC%96%B4%EB%96%A4-%EC%97%AD%ED%95%A0%EC%9D%84-%ED%95%98%EB%8A%94-%EA%B1%B8%EA%B9%8C</link>
            <guid>https://velog.io/@hyunju-song/body-parser%EC%9D%98-urlencoded%EB%8A%94-%EB%8F%84%EB%8C%80%EC%B2%B4-%EC%96%B4%EB%96%A4-%EC%97%AD%ED%95%A0%EC%9D%84-%ED%95%98%EB%8A%94-%EA%B1%B8%EA%B9%8C</guid>
            <pubDate>Fri, 16 Oct 2020 16:06:11 GMT</pubDate>
            <description><![CDATA[<p>node express를 사용할 때, 뭐랄가 기본 세트처럼 딸려오는 모듈들 중에서,
데이터(client에서 post로 보내준 데이터)들을 자동으로 파싱해주어서, 진짜로 필요한 부분인 body부분을 쉽게 추출해주는 body-parser 모듈이 있다.</p>
<p>그런데 해당 모듈을 사용할 때마다 항상 같이 사용하던 셋팅에</p>
<pre><code class="language-js">app.use(express.urlencoded({extended: false}))</code></pre>
<p>이것도 있는데, 이건 정확히 무슨 의미를 지니고 있을까?</p>
<p>일단 body-parser 모듈을 사용할 때 아무 옵션을 주지 않는 다면 
<strong>body-parser deprecated undefined extended: provide extended option</strong> 같은 문구가 뜬다.</p>
<p>그리고 해당 모듈을 사용하고자 한다면 위에서 기재한 urlncoded 옵션을 사용해야 한다고 한다. </p>
<p>urlencoded({extended : })는 어떤 옵션일까? </p>
<p>이를 이야기 하기 전에 우선 자바스크립트에서 데이터를 주고받는 형식에 대해서 잠깐 이야기하고 가면 좋을 듯하다. </p>
<p>자바스크립트에서 데이터를 주고받고 읽을 땐느 객체 형태를 띄고, 실제로 데이터를 주고 받을 때도 객체 형태를 선호한다. 
(따라서 여러 과정을 거쳐 파싱을 거쳐야 하는 것이다. JSON을 사용하는 것처럼)</p>
<p>extended 옵션의 경우, true일 경우, 객체 형태로 전달된 데이터내에서 또다른 중첩된 객체를 허용한다는 말이며, false인 경우에는 허용하지 않는 다는의비니다. </p>
<p>일반적으로는 false로 셋팅하고 사용하기는 하는 것을 express 셋팅 관련해서 예시를 볼 때 많이 볼 수 있었다. 그렇다면 true와 false로 셋팅했을 때 차이가 무엇일까?</p>
<p>*<em>bodyParser 미들웨어의 여러 옵션 중에 하나로 false 값일 시 node.js에 기본으로 내장된 queryString, true 값일 시 따로 설치가 필요한 npm qs 라이브러리를 사용한다.
*</em></p>
<p>queryString 과 qs 라이브러리 둘 다 url 쿼리 스트링을 파싱해주는 같은 맥락에 있으나 qs가 추가적인 보안이 가능한 말 그대로 extended 확장된 형태이다.
기본이 true 값이니 qs 모듈을 설치하지 않는다면 아래와 같이 false 값으로 따로 설정을 해주어야 한다.</p>
<p>즉 qs는 추가적인 보안기능이 있는 모듈로서 필요하다면 모듈 설치후에 사용하면 된다. </p>
<p>아무래도 중첩된 객체를 허용한다는 말은 추가적인 보안기능이 있는 것에 대한 일부 사용에 대한 설명인 것으로 추측된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2020프로젝트 준비_5일차]]></title>
            <link>https://velog.io/@hyunju-song/2020%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%A4%80%EB%B9%845%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@hyunju-song/2020%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%A4%80%EB%B9%845%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 14 Oct 2020 13:02:00 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 시작한 지 5일차가 되었고, 이제 오늘 최종적으로 기본적인 셋팅을 완료하고, 내일부터는 코드 작성을 시작하여야 한다.
초기 준비단계에 시간을 많이 할애해 둔 이유가 있었다. 이전 4일차까지 진행했던 다양한 기획단계들과 팀룰 정하기 또한 시간이 꽤 걸렸기 때문.</p>
<p>오늘은 프로젝트 초기의 git셋팅과 모듈 설치 그리고 역할분담을 하기로 했다.</p>
<h2 id="git-기초-셋팅">git 기초 셋팅</h2>
<p>기본적인 git flow는 이미 정리해둔 글을 참고해서 진행했다.
<a href="https://velog.io/@hyunju-song/202010-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8git-workflow">project git workflow 정리</a></p>
<h3 id="upstream에서-초기-작업">upstream에서 초기 작업</h3>
<ol>
<li><p>master branch에서 초기 모듈 셋팅</p>
<ul>
<li>client  : react, react-dom, react-router-dom, axios</li>
<li>server : express(관련 모듈들 설치, cors 등등) , mysql, sequelize, nodemon</li>
<li><blockquote>
<p>모듈 설치 할 때, npm install &quot;모듈&quot; --save-dev 해야한다,</p>
</blockquote>
</li>
</ul>
<p>** --save-dev 를 해야하는 이유는 무엇일까?**
: 이는 협업을 위해서 필수이다. 저 명령어를 하지 않으면 본인의 package.json에서만 설치된다. 따라서 팀원이 내 코드를 받아가면 모든 모듈을 처음부터 다시 설치해야 한다. 하지만 해당 명령어를 사용해서 최초에 모듈 설치를 해주면, 팀원은 npm install만 해도 모든 모듈 설치가 가능하다.</p>
<ol start="2">
<li>lint 설치
npm install eslint --save-dev [lint 설치]
npx eslint --init [lint 실행]</li>
</ol>
<p>그리고 세부적인 조건들을 설정해줬다.
(방식을 common JS 방식, node 환경 등등...)</p>
<p>그리고 lint rule은 세미콜론과 따옴표 정도를 vsc의 prettier 규칙과 동일하게 적용해 주었다.</p>
<ol start="3">
<li>node modules를 gitignore 파일 만들어서 추가</li>
</ol>
</li>
</ol>
<h3 id="dev작업-branch-만들기-중요">dev(작업) branch 만들기 (중요!)</h3>
<p>여기서 생각도 못한 난관에 봉착.
초반에 git flow를 공부했을땐, master에서 기초 셋팅해주고 dev 브랜치를 만들어준다고 했는데, 실제로 작업을 해보니까 
<strong>master에서 작업한게 dev 브랜치로 그대로 넘어가질 않았다.</strong>
허거덩...분명히 작업브랜치 만들고, upstream에 push 하라고 했는데....</p>
<p>팀원들과 고민을 해보다가</p>
<ol>
<li>master에서 작업한 걸 upstream master에 push 해주고, </li>
<li>그리고 local에서 dev branch를 만들고, dev 에서 upstream master을 pull해오기</li>
<li>그리고 dev branch에서 upstream dev push 해주기!!!</li>
</ol>
<h3 id="이-후에-작업하고-pr할-때-주의-사항">이 후에 작업하고 PR할 때 주의 사항</h3>
<ul>
<li>무조건 upstream의 dev에 PR해주기! </li>
<li>upstream의 dev pull해올때, local의 dev branch에서 해주기</li>
<li>merge 전 코드리뷰는 매우 중요하다!!!! 단순히 올바른 코드작성을 보기위함이 아니라, 팀원 간 충돌발생 가능성이 있으므로!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 문제풀이 8]]></title>
            <link>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-8</link>
            <guid>https://velog.io/@hyunju-song/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-8</guid>
            <pubDate>Tue, 13 Oct 2020 13:49:55 GMT</pubDate>
            <description><![CDATA[<p>이진탐색트리와 관련된 알고리즘 문제다.
주어진 배열 내에서 원하는 요소의 유무를 파악하고 있으면 그 요소의 인덱스값을 리턴해주는 문제를 해결해야하는 알고리즘 문제였다.
보통 요소의 인덱스 값을 찾으면 indexOf 메소드를 활용해준다. 이는 주어진 배열의 수를 한 번 순회해주는 것으로 시간 복잡도가 O(N)이다. 하지만 문제에서 요구한 점은 시간복잡도 O(logN)이었다. </p>
<h3 id="이진탐색트리란-무엇일까">이진탐색트리란 무엇일까?</h3>
<p>자료구조를 공부하다보면 배우게될 트리구조가 있다. 일종의 나무를 뒤집어 놓은 모양대로 자료구조가 짜여져 있는 것이다. 보통의 트리 구조는 하나의 뿌리(root)가 있으면 거기서 가지가 얼마든지 만들어진다.(자식 노드) 그러나 이진트리는 한 뿌리(혹은 부모노드당) 자식 노드를 단 두개만 가질 수 있다.</p>
<p>그리고 여기서 이진탐색트리는 추가적인 규칙을 지니고 있다.</p>
<ol>
<li>왼쪽의 자식 노드는 부모노드보다 무조건 작다.</li>
<li>오른쪽의 자식 노드는 부모노드보다 무조건 크다.</li>
</ol>
<p>어떻게 보면 정해진 요소를 탐색하고자 할 때, 이진탐색트리를 활용하게 된다면, 찾으려는 수가
해당 자료구조의 root수보다 작냐 크냐에 따라서 배열의 일부만 탐색하게 된다. </p>
<p>따라서 이진탐색트리를 활용한다면 원하는 요소를 찾고 탐색하는 문제를 시간복잡도 O(logN)으로 해결가능하다.</p>
<ul>
<li>처음에는 임의의 중간수를 정의하여 풀어주었는데, 생각해보니 그렇게 하면 재귀함수가 진행될수록 인덱스값이 자꾸 바뀌었다. 따라서 인덱스 값들을 고정시켜준채로 재귀해야했다.</li>
<li>주어진 배열에서 이진탐색트리의 논리로 요소를 찾는 분기?(if문 조건 나누기)를 어떻게 해야할 지 이해가 가지 않았다. 왜냐면 주어진 배열은 크기순으로 정렬된 배열이 아닐 확률이 높기 때문이다. 그렇다고 정렬을 해주고 계산해주자니, 기존의 인덱스 구성이 다 깨져 버린다.
=&gt; 이건 문제를 다시 읽어보니 &quot;정렬되어 있는 배열 중 일부를 왼쪽 혹은 오른쪽으로 회전시킨 배열이 주어졌을때&quot; 라고 되어있다. 즉 특정 요소를 중심으로 양쪽은 제대로 정렬되어있다.</li>
</ul>
<pre><code class="language-js">/*
 * 정렬되어 있는 배열 중 일부를 왼쪽 혹은 오른쪽으로 회전시킨 배열이 주어졌을때,
 * 어떻게 특정 element를 효율적으로 찾을 수 있을까요?
 *
 * 작성한 함수는 target의 index값을 return하고, 없으면 null을 return해야 합니다.
 *
 * 예시 :
 * rotatedArraySearch([7 ,8, 9,10, 0, &quot;1&quot;, 2, 3, 4, 5, 6 ], 2) === 5
 *
 * rotatedArraySearch([4, 5, 6, 0, 1, 2, 3], 100) === null
 *
 * 시간 복잡도가 O(log(array.length))이 되도록 도전해 보세요!
 */
const rotatedArraySearch = function (rotated, target) {
  //우선 주어진 배열의 첫번째와 마지막 인덱스를 정의
  let firstidx = 0;
  let lastidx = rotated.length - 1;

  function find(first, last) {
    //첫번째 인덱스 값으로 오는 first가 last보다 크면 안된다.
    if (first &gt; last) {
      return null;
    }
    //임의로 중간 인덱스를 정의
    let mididx = Math.floor((first + last) / 2);
    // 1. 임의의 인덱스 값과 타겟값이 같으면 해당 인덱스값 리턴
    if (target === rotated[mididx]) {
      return mididx;
    }
   //요소가 하나인 경우에도 재귀를 돌수있다. 숫자와 undefined 의 비교는 false를 주긴 한다!(이건 자바스크립트에서만)
    //그런데 다른 언어에서는 위와 같은 부분에서 에러가 발생할 수 있다.
    //따라서 첫 인덱스와 마지막 인덱스가 같으면 이라는 조건을 걸어두는 것이 좋다.
    if(first === last){
      if(rotated[first] === target){
        return first
      } else {
        return null
      }
    }
    //결론적으로 아래의 조건이 없어도, 문제 없다.
    // 2. 비교하는 가상의 배열의 길이가 3개이하일때를 말한다.
    //if (mididx &lt;= 1) {
     // if (target === rotated[first]) {
    //    return first;
    //  } else {
        //이미 위의 조건문에서 mididx에 해당하는 값은 일치하지 않는다고 나왔으므로, 재귀를 돌릴때 mididx는 고려X
    //    return find(mididx + 1, last);
    //  }
   // }
    //해당 중간값을 기준으로 양쪽이 제대로 정렬되어있는지 점검
    //우선 기준점의 앞쪽부터 점검
    if (rotated[first] &lt; rotated[mididx - 1]) {
      //앞쪽이 제대로 정렬되어있다면, 찾고자 하는 수가 앞쪽에 있는지 확인
      if (target &gt;= rotated[first] &amp;&amp; target &lt;= rotated[mididx - 1]) {
        //앞쪽에 있는 경우에만 재귀돌려주기
        return find(first, mididx - 1);
      } else {
        return find(mididx + 1, last);
      }
    } else {
      //앞쪽이 제대로 정렬되어있지 않은 경우 ex) [7,8,9,10,0]
      //아래의 코드는 내가 생각한 예시의 코드에만 해당되어 버린다.
      //따라서 적절한 조건은, 위에서 앞에 정렬되어있는지를 본다음, 아니면
      //뒤에 내가 찾고자 하는것이 있는지 점검하고, 없으면 다시 앞쪽으로 돌아간다로 하는것이 맞다
      if(target &gt;= rotated[mididx+1] &amp;&amp; target &lt;= rotated[last]){
        //맨끝요소를 제외한 가상의 배열에 타겟이 있는 경우만 재귀를 넣어주고
        return find(mididx+1, last);
      } else {
        return find(first, mididx-1);
      }
      //if (target === rotated[mididx - 1]) {
        //앞쪽의 가장 끝요소 즉 중간인덱스에서 -1한 인덱스의 요소가 타겟과 같은지 검사
      //  return mididx - 1;
     // } else if (target &gt;= rotated[first] &amp;&amp; target &lt;= rotated[mididx - 2]) {
        //맨끝요소를 제외한 가상의 배열에 타겟이 있는 경우만 재귀를 넣어주고
      //  return find(first, mididx - 2);
     // } else {
     //   return find(mididx + 1, last);
     // }
   // }
  }
  return find(firstidx, lastidx);
};</code></pre>
<p>위의 문제를 해결하니, 정렬된 배열에 대한 이진탐색트리는 좀 더 쉽게 풀렸다.</p>
<pre><code class="language-js">/*
 * 정렬된 배열이 주어졌을때, 이진 탐색 알고리즘을 이용하여 특정 요소의 인덱스값을 return하는 함수를 작성하세요.
 *
 * 예시 :
 *
 * let index = binarySearch([1, 2, 3, 4, 5], 4);
 * console.log(index); // [3]
 *
 * 참고 : https://qph.fs.quoracdn.net/main-qimg-742d049387316193be2d097fe7a499de
 */

const binarySearch = function (array, target) {
  //임의의 난수 만들어서 그 난수 인덱스에 위치한 랜덤한 기준값뽑아내기
  //여기서 임의의 수를 나는 중간값으로 할 것이다
  //첫번째 코드 짤때는 배열 내의 값으로 비교를 하니까 나중에 결국 값의 인덱스를 찾기위해 indexOf 메소드를 사용해야했다.
  //따라서 index값을 나중에 구할수있도록 index값들끼리 비교해야한다.
  function findIndex(first, last) {
    if (first &gt; last) {
      return null;
    }
    //요소가 하나인 경우에도 재귀를 돌수있다. 숫자와 undefined 의 비교는 false를 주긴 한다!(이건 자바스크립트에서만)
    //그런데 다른 언어에서는 위와 같은 부분에서 에러가 발생할 수 있다.
    //따라서 첫 인덱스와 마지막 인덱스가 같으면 이라는 조건을 걸어두는 것이 좋다.
    if(first === last){
      if(rotated[first] === target){
        return first
      } else {
        return null
      }
    }
    let midKey = Math.floor((first + last) / 2);
    if (target === array[midKey]) {
      return midKey;
    }
    //if (last - first &lt;= 2) {
     // if (target === array[first]) {
     //   return first;
     // } else {
     //   return findIndex(midKey + 1, last);
     // }
   // }
    if (array[midKey] &gt; target) {
      return findIndex(0, midKey - 1);
    } else if (array[midKey] &lt; target) {
      return findIndex(midKey + 1, array.length - 1);
    }
  }
  return findIndex(0, array.length - 1);
};</code></pre>
]]></description>
        </item>
    </channel>
</rss>