<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>index.html</title>
    <link>https://andwinter.tistory.com/</link>
    <description>html/css/js 공부중..</description>
    <language>ko</language>
    <pubDate>Tue, 11 Aug 2026 23:46:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>빛장</managingEditor>
    <item>
      <title>1. 회원가입, 로그인, 로그아웃 구현</title>
      <link>https://andwinter.tistory.com/356</link>
      <description>&lt;p&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Frontend next.js, Redux, Redux-saga를 사용하였고&lt;/p&gt;
&lt;p&gt;Backend는 mongoDB, express 를 이용해 구현했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;왜 Next.js 인가?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;기존 CRA(create react application) 은 클라이언트 사이드 렌더링방식으로 초기 렌더링시에 HTML을 받고 리소스를 받아 렌더링 하기에 맨처음 로딩까지의 시간이 SSR보다 느리다. 또한 빈 HTML에 JS로 VIEW를 구성하기에 SEO부분에서도 검색엔진이 데이터를 수집하는데에 문제가 있다. 때문에 CRA에서 SSR 구성을 어찌어찌 해야하는데, 반면 Next.js 는 기본적으로 서버사이드 렌더링을 제공하며, 코드스플리팅을 자동화 해주고(여기서 코드스플리팅이란 하나의 작은 번들을 잘개 쪼개어 사용자가 필요한 코드만 로드하여 성능을 향상시켜주는 것을 말한다) SSR이기에 SEO에서도 최적화되어있고 초기 로딩속도또한 빠르다고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@rjs1197/SSR%EA%B3%BC-CSR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot;&gt;https://velog.io/@rjs1197/SSR%EA%B3%BC-CSR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1590406506330&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;SPA에서의 SSR과 CSR&quot; data-og-description=&quot;이 글은 SPA(Single Page Application)에서의 SSR과 CSR을 비교하기 위해 작성되었다(편의를 위해 반말 사용). - 최근 수정: 2019. 09. 22 초기 View 로딩 속도 먼저 전통적인 SSR과 CSR의 가장 큰 차이 중 하나인 '�&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@rjs1197/SSR%EA%B3%BC-CSR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; data-og-url=&quot;https://velog.io/@rjs1197/SSR과-CSR의-차이를-알아보자&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dDT4ZN/hyGbpc0jbZ/Gz9bWeZewQSfnY1c6yb8q0/img.png?width=800&amp;amp;height=300&amp;amp;face=0_0_800_300,https://scrap.kakaocdn.net/dn/ZomTJ/hyF9WwJRXG/VfoJG8nBAEKctPMhPypiu0/img.png?width=800&amp;amp;height=300&amp;amp;face=0_0_800_300,https://scrap.kakaocdn.net/dn/bpBhZH/hyGbuFnOpd/Bx9yX8X9Xe2hYRlYfn7Rk1/img.png?width=800&amp;amp;height=300&amp;amp;face=0_0_800_300&quot;&gt;&lt;a href=&quot;https://velog.io/@rjs1197/SSR%EA%B3%BC-CSR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@rjs1197/SSR%EA%B3%BC-CSR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dDT4ZN/hyGbpc0jbZ/Gz9bWeZewQSfnY1c6yb8q0/img.png?width=800&amp;amp;height=300&amp;amp;face=0_0_800_300,https://scrap.kakaocdn.net/dn/ZomTJ/hyF9WwJRXG/VfoJG8nBAEKctPMhPypiu0/img.png?width=800&amp;amp;height=300&amp;amp;face=0_0_800_300,https://scrap.kakaocdn.net/dn/bpBhZH/hyGbuFnOpd/Bx9yX8X9Xe2hYRlYfn7Rk1/img.png?width=800&amp;amp;height=300&amp;amp;face=0_0_800_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;SPA에서의 SSR과 CSR&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;이 글은 SPA(Single Page Application)에서의 SSR과 CSR을 비교하기 위해 작성되었다(편의를 위해 반말 사용). - 최근 수정: 2019. 09. 22 초기 View 로딩 속도 먼저 전통적인 SSR과 CSR의 가장 큰 차이 중 하나인 '�&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;어쨌든 만들껀?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;간단하게 매일 주제를 던져주고 그에 맞는 사진을 사용자가&lt;/p&gt;
&lt;p&gt;업로드하는 갤러리형 사이트를 만든다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;i&gt;1. Front&lt;/i&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLmfS3/btqEghCrcit/zqKZjYIlZRg5NWMxLx5Oz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLmfS3/btqEghCrcit/zqKZjYIlZRg5NWMxLx5Oz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLmfS3/btqEghCrcit/zqKZjYIlZRg5NWMxLx5Oz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLmfS3%2FbtqEghCrcit%2FzqKZjYIlZRg5NWMxLx5Oz0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;프론트에서 미리 회원가입 폼을 작성해 놓았다.&lt;/p&gt;
&lt;p&gt;이는 리덕스와 리덕스 사가로 더미데이터를 넣어&lt;/p&gt;
&lt;p&gt;가상으로 회원가입 후 자동 로그인되는 방식으로 구현해 놓았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589813109236&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; const signUp = (e) =&amp;gt; { //onSubmit 시 동작하는 함수
    e.preventDefault();
    if (userPassword !== userPasswordCheck) { //미리 password 의 일치여부를 파악한다.
      return setPasswordCheckError(true);
    }
    return dispatch({
      type: SIGN_UP_REQUEST,
      data: {
        email: userEmail,
        password: userPassword,
        name: userName,
      },
    });
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;SignUp 페이지에서 폼데이터가 onSubmit 되는 순간&lt;/p&gt;
&lt;p&gt;리듀서 중 SIGN_UP_REQUEST 가 dispatch 되어 지고 action data 로 폼 안의 email, password, userName 이 전달된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589813003820&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function signUpAPI(signUpData) {
  return axios.post('http://localhost:5000/signup', signUpData, { //signUpAPI로 요청을 보낸다
    withCredentials: true,//cors 쿠키 교환을 위한것. back에서 cors 를 사용해 credentials: true 한번더 처리해줘야한다
  });
}

function* signUp(action) {
  try {
    const result = yield call(signUpAPI, action.data); //signUpAPI 를 호출하고 돌아오는 데이터도 받는다.
    yield put({
      type: SIGN_UP_SUCCESS,
      data: result.data,
    });
  } catch (e) {
    console.log(e);
    yield put({
      type: SIGN_UP_FAILURE,
    });
  }
}

function* watchSignUp() {
  yield takeEvery(SIGN_UP_REQUEST, signUp); //리듀서 감지
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그럼 동시에 redux-saga 에서 비동기로&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SIGN_UP_REQUEST 를 감지하고 넘어온 action data 를 backend 서버로 전달한다.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;&lt;i&gt;1. Back&lt;/i&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1589813412786&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import mongoose from 'mongoose';
import passportLocalMongoose from 'passport-local-mongoose';
const UserSchema = new mongoose.Schema({
  name: String,
  writer: String,
  email: String,
  userInfo: String,
  likeList: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Photo',
    },
  ],
});

UserSchema.plugin(passportLocalMongoose, { usernameField: 'email' });

const model = mongoose.model('User', UserSchema);

export default model;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;User 모델을 만들어 놓았다. likeList는 좋아요를 구성하기 위함이다.&lt;/p&gt;
&lt;p&gt;일전에 로그인구현하는 예제에서 사용한 그대로 썼다.&lt;/p&gt;
&lt;p&gt;편하게 하기위하여 passport-local-mongoose 사용도 잊지 않았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589813606369&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;globalRouter.post(routes.signup, signup, login); //routes.signup = /signup&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;회원가입 후 자동으로 로그인되는 순서다.&lt;/p&gt;
&lt;pre id=&quot;code_1589813700794&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const signup = async (req, res, next) =&amp;gt; {
  const { email, name, password } = req.body;
  try {
    const user = await User({
      name,
      email,
      userInfo: '',
      writer: '',
    });
    await User.register(user, password);
    next();
  } catch (e) {
    console.log(e);
    res.send('가입실패');
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아까 front 에서 데이터를 넘겨주면 back 에서는 그 데이터를&lt;/p&gt;
&lt;p&gt;req.body 안에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;가입방법은 예전에 쓴 그 글을 참고하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://andwinter.tistory.com/344&quot;&gt;2020/03/23 - [Study/script.js] - [Node.js] 회원가입 구현(로그인,로그아웃,회원가입,passport, Mongo Store)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1589813804737&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Node.js] 회원가입 구현(로그인,로그아웃,회원가입,passport, Mongo Store)&quot; data-og-description=&quot;Passport 로컬 로그인 뿐만아니라 소셜 로그인에서까지 쉽게 인증이 가능한 미들웨어이다. http://www.passportjs.org/ Passport.js Simple, unobtrusive authentication for Node.js www.passportjs.org ** mongo..&quot; data-og-host=&quot;andwinter.tistory.com&quot; data-og-source-url=&quot;https://andwinter.tistory.com/344&quot; data-og-url=&quot;https://andwinter.tistory.com/344&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/G4lR8/hyF4Y9cnZD/dk9mK4NUMScr4q90FOAxs0/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/qXYdS/hyF6p5b0s4/UDMrzFcjqH6rrb2f67Kmj1/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/dYullT/hyF4VklXfS/2SI180p66c6CrWVWyEA0z0/img.png?width=825&amp;amp;height=394&amp;amp;face=0_0_825_394&quot;&gt;&lt;a href=&quot;https://andwinter.tistory.com/344&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://andwinter.tistory.com/344&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/G4lR8/hyF4Y9cnZD/dk9mK4NUMScr4q90FOAxs0/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/qXYdS/hyF6p5b0s4/UDMrzFcjqH6rrb2f67Kmj1/img.png?width=800&amp;amp;height=382&amp;amp;face=0_0_800_382,https://scrap.kakaocdn.net/dn/dYullT/hyF4VklXfS/2SI180p66c6CrWVWyEA0z0/img.png?width=825&amp;amp;height=394&amp;amp;face=0_0_825_394');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[Node.js] 회원가입 구현(로그인,로그아웃,회원가입,passport, Mongo Store)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Passport 로컬 로그인 뿐만아니라 소셜 로그인에서까지 쉽게 인증이 가능한 미들웨어이다. http://www.passportjs.org/ Passport.js Simple, unobtrusive authentication for Node.js www.passportjs.org ** mongo..&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;andwinter.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;근데 왜 userInfo와 writer 을 남겨놓았냐면&lt;/p&gt;
&lt;p&gt;회원가입이 완료되면 모달창이 뜨고 그 모달에서 작가명과 자기소개를&lt;/p&gt;
&lt;p&gt;작성할 수 있는 기능을 원했기 때문이다.&lt;/p&gt;
&lt;p&gt;쓸대없어 보이겠지만 그냥 그걸 원했다.&lt;/p&gt;
&lt;p&gt;그리고 이 모달창은 이후에 editProfile 에서도 동일하게 이용할것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아무튼 회원가입을 누르게되면 로그인으로 가야하니 로그인까지 구현한다.&lt;/p&gt;
&lt;pre id=&quot;code_1589813998441&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const login = (req, res, next) =&amp;gt; {
  passport.authenticate('local', (err, user, info) =&amp;gt; {
    if (err) {
      console.log(err);
      return next(err);
    }
    if (info) {
      return res.status(401).send(info);
    }
    req.login(user, (loginErr) =&amp;gt; {
      if (loginErr) {
        return next(loginErr);
      }
      const userData = Object.assign({}, user.toJSON());
      delete userData.password; //객체안의 password 정보를 지운다.
      return res.json(userData);
    });
  })(req, res, next);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;예전에 작성했던 회원가입글과 다른점이 이거 하나다.&lt;/p&gt;
&lt;p&gt;여기서는 custom callback 를 이용했다는점.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DzCry/btqEd8GIHGF/KkPDI1KZgaWXvZBvRk92R1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DzCry/btqEd8GIHGF/KkPDI1KZgaWXvZBvRk92R1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DzCry/btqEd8GIHGF/KkPDI1KZgaWXvZBvRk92R1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDzCry%2FbtqEd8GIHGF%2FKkPDI1KZgaWXvZBvRk92R1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이는 프론트와 백이 나누어져있고 프론트는 next.js 로 이루어져있기때문에&lt;/p&gt;
&lt;p&gt;redirect 가 필요하지 않았다. 해서 custom callback 를 이용해서&lt;/p&gt;
&lt;p&gt;로그인이 성공하면 해당 유저 데이터를 프론트서버로 다시 돌려보내주게&lt;/p&gt;
&lt;p&gt;만들었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 signUp 이 완료되고 next() 로 login 으로와서 로그인까지 된 상태가 되면&lt;/p&gt;
&lt;p&gt;res.json(userData) 가 실행되어 프론트로 로그인된 사용자 정보가 넘어가게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;2. 다시 Front&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p&gt;프론트에선 그 json 데이터를&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589815143371&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    const result = yield call(signUpAPI, action.data);
    yield put({
      type: SIGN_UP_SUCCESS,
      data: result.data,
    });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아까 try catch 문의 try 에서 result 로 받게 되고&lt;/p&gt;
&lt;p&gt;그 데이터를 put(dispatch 와 동일) 을통해 SIGN_UP_SUCCESS 를 호출하여 데이터를 넘긴다.&lt;/p&gt;
&lt;pre id=&quot;code_1589816061718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    case SIGN_UP_SUCCESS: {
      return {
        ...state,
        isUserLoadding: false,
        me: action.data,
        editing: true,
      };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;리덕스에서는 미리 설정해놓은 action에 따라서 그 유저데이터를 me 라는 state 에 저장하게된다.&lt;/p&gt;
&lt;p&gt;또, editing 이라는 state 의 값을 true 로 바꾸어 모달창을 띄운다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jxoif/btqEfqtbset/KROGIO4NvZd14zZceF1GAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jxoif/btqEfqtbset/KROGIO4NvZd14zZceF1GAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jxoif/btqEfqtbset/KROGIO4NvZd14zZceF1GAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJxoif%2FbtqEfqtbset%2FKROGIO4NvZd14zZceF1GAk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그럼 이러한 모달창이 뜨게된다.&lt;/p&gt;
&lt;p&gt;여기서 아까 말했듯, 이름과 별개로 작가명, 작가소개를 작성할 수 있다.&lt;/p&gt;
&lt;p&gt;여기서 작성되는 데이터는 마찬가지로 onSubmit 되어지면&lt;/p&gt;
&lt;pre id=&quot;code_1589815276963&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; const handleSubmit = (e) =&amp;gt; {
    e.preventDefault();
    const writerData = {
      userInfo: info.userInfo,
      writer: info.name,
    };
    dispatch({
      type: EDITING_PROFILE_REQUEST,
      data: { me, writerData },
    });
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 함수가 실행된다.&lt;/p&gt;
&lt;p&gt;회원가입할때와 비슷한 구조로 dispatch 되고&lt;/p&gt;
&lt;pre id=&quot;code_1589815341247&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function editingAPI(userID) {
  return axios.post('http://localhost:5000/editing', userID, {
    withCredentials: true,
  });
}

function* editing(action) {
  try {
    const result = yield call(editingAPI, action.data);
    const userData = JSON.parse(result.config.data);
    const { me, writerData } = userData;
    yield put({
      type: EDITING_PROFILE_SUCCESS,
      data: { ...me, ...writerData },
    });
  } catch (e) {
    console.log(e);
    yield put({
      type: EDITING_PROFILE_FAILURE,
    });
  }
}

function* watchEditing() {
  yield takeEvery(EDITING_PROFILE_REQUEST, editing);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;redux-saga 에서는 이러한 과정으로 비동기요청을 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;3. 다시 back&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1589816255282&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const editing = async (req, res) =&amp;gt; {
  const {
    me: { _id, email, name },
    writerData: { userInfo, writer },
  } = req.body;
  try {
    await User.findByIdAndUpdate(_id, {
      name,
      email,
      userInfo,
      writer,
    });
    const user = req.user;
    return res.json(user);
  } catch (e) {
    console.log(e);
    res.send('프로필 업데이트 실패');
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;back 에서는 그 데이터를 받아서 mongoose 의 findByIdAndUpdate 를 통하여&lt;/p&gt;
&lt;p&gt;아까 비어있던 userInfo 와 writer 을 채우게 된다.&lt;/p&gt;
&lt;p&gt;그리고 다시 프론트로 정보를 넘겨주고&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;4. 다시다시 Front&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p&gt;프론트에서는 그 정보를 받아서 사용자 정보를 업데이트 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1589816638126&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    case EDITING_PROFILE_SUCCESS: {
      return {
        ...state,
        isLoggedIn: true,
        editing: false,
        isUserLoadding: false,
        me: action.data,
      };
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 회원가입은 끝나게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이미 위에 back서버의 로그인은 만들어져있기때문에 그 api를 이용하는&lt;/p&gt;
&lt;p&gt;프론트 서버의 무언가를 만들어주기만 하면된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kYYoE/btqEg2rhSM4/Da7oIgGmO5IuyRthIHHXm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kYYoE/btqEg2rhSM4/Da7oIgGmO5IuyRthIHHXm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kYYoE/btqEg2rhSM4/Da7oIgGmO5IuyRthIHHXm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkYYoE%2FbtqEg2rhSM4%2FDa7oIgGmO5IuyRthIHHXm0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;요런 로그인창을 이용할 것이고,&lt;/p&gt;
&lt;p&gt;onsubmit 되어지면 회원가입과 똑같은 패턴으로 back 서버의 loginapi 를 이용하여 로그인시키면 된다.&lt;/p&gt;
&lt;p&gt;대신 로그인으로 넘길때에는 email과 password 라는 이름으로 넘겨야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;5. 여기서 문제는? 로그인이 풀리는것!&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;여기까진 반쪽짜리 회원가입, 로그인이다.&lt;/b&gt; 왜냐하면&lt;/p&gt;
&lt;p&gt;새로고침하면 로그인이 풀려버리기 때문이다.&lt;/p&gt;
&lt;p&gt;이는 프론트에서 만들어진 쿠키를 백서버로 서로 교환하지 못하기 때문인데,&lt;/p&gt;
&lt;p&gt;위에서 적어줬던 axios 의&lt;/p&gt;
&lt;pre id=&quot;code_1589818921405&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;withCredentials: true,&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 옵션과&lt;/p&gt;
&lt;pre id=&quot;code_1589818942617&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.use(
  cors({
    origin: true,
    credentials: true,
  }),
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;백의 app.js 에서 cors 를 이용하여 이 옵션들을 설정해주면 쿠키가 올바르게&lt;/p&gt;
&lt;p&gt;생성되는걸 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;6. 하지만 그래도 로그인이 풀리는걸?&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p&gt;하지만 쿠키생성만으로는 로그인이 풀리는걸 막을순 없다.&lt;/p&gt;
&lt;p&gt;쿠키는 단지 사용자의 정보 일부분을 가지고있을뿐이다.&lt;/p&gt;
&lt;p&gt;때문에 이 쿠키를 back으로 보내고 express session과 passport의 &lt;span&gt;deserializeUser를 통해 &lt;/span&gt;이 쿠키를 해석하고 req.user에 로그인된 유저의 정보가 담기면 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그 정보를 프론트로 다시 보내주는 작업을 해야 로그인이 풀려버리는걸 막을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;(아 맞아 미리 세션은 다 설정해놓았다. 설정하는 방법은 저번 글(위에글) 참고 )&lt;/p&gt;
&lt;p&gt;즉 모든 페이지에서 쿠키를 이용해서 로그인된 사용자 정보를 매번 불러와주는것이 필요한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이를위해 모든 페이지의 상위에있는 Layout 에다가 작업을 해줄 것이다.&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;useEffect 를 사용하여 페이지가 로드되는 맨처음에(componentDidMount&lt;span&gt;() 와 같음)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;LOAD_USER_REQUEST 를 호출하는 작업을 해줄것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1589819038877&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    dispatch({
      type: LOAD_USER_REQUEST,
    });
  }, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;layout 에 위와같은 코드를 적어주었고, saga에서는&lt;/p&gt;
&lt;pre id=&quot;code_1589819286374&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function loadUserAPI() {
  return axios.get('http://localhost:5000/load', {
    withCredentials: true,
  });
}
function* loadUser() {
  try {
    const result = yield call(loadUserAPI);
    yield put({
      type: LOAD_USER_SUCCESS,
      data: result.data,
    });
  } catch (e) {
    console.log(e);
    yield put({
      type: LOAD_USER_FAILURE,
    });
  }
}
function* watchloadUser() {
  yield takeEvery(LOAD_USER_REQUEST, loadUser);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 get 요청을 보낼것이다.&lt;/p&gt;
&lt;p&gt;이미 사전작업으로 인해 쿠키가 존재하기때문에 프론트에서 뭔가를 넘겨줄 필요가 없고 단지 로그인된 유저의 정보만을 불러오면 되기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;7. 마지막으로 Back&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p&gt;백에서는&lt;/p&gt;
&lt;pre id=&quot;code_1589819366511&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const loadUser = (req, res) =&amp;gt; {
  if (!req.user) {
    return res.status(401).send('로그인 되어있지 않음.');
  }
  return res.json(req.user);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 req.user 를 넘겨주기만 하면 끝이다. 간단하쥬?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;8. 진짜 마지막으로 로그아웃&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p&gt;로그아웃은 정말 간단하다.&lt;/p&gt;
&lt;p&gt;버튼하나 만들고&lt;/p&gt;
&lt;pre id=&quot;code_1589821511247&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const handleLogout = () =&amp;gt; {
    dispatch({
      type: LOG_OUT_REQUEST,
    });
  };&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1589821604282&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 로그아웃
function logOutAPI() {
  return axios.post(
    'http://localhost:5000/logout',
    {},
    {
      withCredentials: true,
    },
  );
}
function* logOut() {
  try {
    yield call(logOutAPI);
    yield put({
      type: LOG_OUT_SUCCESS,
    });
  } catch (e) {
    console.log(e);
    yield put({
      type: LOG_OUT_FAILURE,
    });
  }
}
function* watchLogOut() {
  yield takeEvery(LOG_OUT_REQUEST, logOut);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 해주고(매번똑같다)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;백에서는&lt;/p&gt;
&lt;pre id=&quot;code_1589821634177&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const logout = (req, res) =&amp;gt; {
  req.logout();
  req.session.destroy(() =&amp;gt; {
    res.clearCookie('connect.sid');
    res.send('로그아웃 성공');
  });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요렇게 세션을 파괴하고 쿠키를 지워주면 끝이다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/356</guid>
      <comments>https://andwinter.tistory.com/356#entry356comment</comments>
      <pubDate>Tue, 19 May 2020 01:15:53 +0900</pubDate>
    </item>
    <item>
      <title>[express Error]Route.get() requires a callback function but got a [object Undefined]</title>
      <link>https://andwinter.tistory.com/355</link>
      <description>&lt;pre id=&quot;code_1589303834207&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Route.get() requires a callback function but got a [object Undefined]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;뒤에 컨트롤러가 존재하지 않을때 생기는 오류&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589303898662&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get(routes.getpost, getpost)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 라우터와 컨트롤러를 따로 import 해와서 쓸때&lt;/p&gt;
&lt;p&gt;콜백인 getpost 가 정의되어있지 않으면 생기는 오류임.&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/355</guid>
      <comments>https://andwinter.tistory.com/355#entry355comment</comments>
      <pubDate>Wed, 13 May 2020 02:18:55 +0900</pubDate>
    </item>
    <item>
      <title>[express Error]UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client</title>
      <link>https://andwinter.tistory.com/354</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brAtDO/btqD4VfI0Ug/D3zMOMjipzTQrGGWxPiXek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brAtDO/btqD4VfI0Ug/D3zMOMjipzTQrGGWxPiXek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brAtDO/btqD4VfI0Ug/D3zMOMjipzTQrGGWxPiXek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrAtDO%2FbtqD4VfI0Ug%2FD3zMOMjipzTQrGGWxPiXek%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;header 를 설정할 수 없다는 의미심장한 에러가 떴다.&lt;/p&gt;
&lt;p&gt;이게 뭘까 뭘까 생각해보다가 검색해보니&lt;/p&gt;
&lt;p&gt;express controller 에서 try catch 문을 써준뒤 마지막에 리턴을 안붙여서 생기는 오류였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5ldbQ/btqD6lSh6kW/R6k22O2ZYPA3ULTI6VPBrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5ldbQ/btqD6lSh6kW/R6k22O2ZYPA3ULTI6VPBrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5ldbQ/btqD6lSh6kW/R6k22O2ZYPA3ULTI6VPBrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5ldbQ%2FbtqD6lSh6kW%2FR6k22O2ZYPA3ULTI6VPBrk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;리턴만 잘 붙여주면 끝&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/354</guid>
      <comments>https://andwinter.tistory.com/354#entry354comment</comments>
      <pubDate>Tue, 12 May 2020 00:42:42 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] 프로그래머스 코딩테스트연습 (2016년) Lv.1</title>
      <link>https://andwinter.tistory.com/353</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;문제 설명&lt;/p&gt;
&lt;p&gt;2016년 1월 1일은 금요일입니다. 2016년 a월 b일은 무슨 요일일까요? 두 수 a ,b를 입력받아 2016년 a월 b일이 무슨 요일인지 리턴하는 함수, solution을 완성하세요. 요일의 이름은 일요일부터 토요일까지 각각&lt;span&gt;&amp;nbsp;&lt;/span&gt;SUN,MON,TUE,WED,THU,FRI,SAT&lt;/p&gt;
&lt;p&gt;입니다. 예를 들어 a=5, b=24라면 5월 24일은 화요일이므로 문자열&lt;span&gt;&amp;nbsp;&lt;/span&gt;TUE를 반환하세요.&lt;/p&gt;
&lt;p&gt;제한 조건&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2016년은 윤년입니다.&lt;/li&gt;
&lt;li&gt;2016년 a월 b일은 실제로 있는 날입니다. (13월 26일이나 2월 45일같은 날짜는 주어지지 않습니다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;입출력 예&lt;/p&gt;
&lt;p&gt;a&amp;nbsp; &amp;nbsp; b&amp;nbsp; &amp;nbsp; result&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 30px;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;24&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;TUE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;과정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1588527027909&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(a, b) {
    var answer = new Date(2016, (a-1), b);
    return String(answer).slice(0,3).toUpperCase();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1. 변수 answer 에 new Date 를 생성한다. 문제였던 2016년을 넣고 a-1 를 한 이유는 0 부터 &lt;span&gt;January 이기 때문이다. 들어오는 값은 실제 달이므로 -1을 해서 맞춰준다.(day 의 경우 0은 이전달 마지막일이 된다. 1은 그냥 1일임)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;2. answer 은 문자열이 아니므로 String 을 통해 문자열로 바꿔준다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;3. 그결과로 나온 문자열(&lt;span&gt;'Tue May 24 2016 00:00:00 GMT+0000 (Asia)') 중 0부터 3번째 자리까지를 slice 를 통해 잘라준다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;4. toUpperCase()를 통해 최종적으로 답에서 원한 대문자로 만들어주면 끝.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r040E/btqDQTpCKL3/CuxGlSMTeiKtq6F2mQtPMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r040E/btqDQTpCKL3/CuxGlSMTeiKtq6F2mQtPMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r040E/btqDQTpCKL3/CuxGlSMTeiKtq6F2mQtPMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr040E%2FbtqDQTpCKL3%2FCuxGlSMTeiKtq6F2mQtPMk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Study/Algorithmus</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/353</guid>
      <comments>https://andwinter.tistory.com/353#entry353comment</comments>
      <pubDate>Mon, 4 May 2020 02:40:21 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] 프로그래머스 코딩테스트연습 (가운데 글자 가져오기) Lv.1</title>
      <link>https://andwinter.tistory.com/352</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;단어 s의 가운데 글자를 반환하는 함수, solution을 만들어 보세요. 단어의 길이가 짝수라면 가운데 두글자를 반환하면 됩니다.&lt;/p&gt;
&lt;p&gt;재한사항&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;s는 길이가 1 이상, 100이하인 스트링입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;입출력 예&lt;/p&gt;
&lt;p&gt;s&amp;nbsp; &amp;nbsp; &amp;nbsp;return&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&quot;abcde&quot;&lt;/td&gt;
&lt;td&gt;&quot;c&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;qwer&quot;&lt;/td&gt;
&lt;td&gt;&quot;we&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;과정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1588268070022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(s) {
    let answer = ''
    const middleSNumber = s.length/2
    middleSNumber%1 !== 0 
    ? answer = s[Math.floor(middleSNumber)] 
    : answer = `${s[middleSNumber - 1]}${s[middleSNumber]}`
    return answer
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1.&amp;nbsp; middleSNumber 를 입력되는 s의 길이를 2로 나누어 저장한다.&lt;/p&gt;
&lt;p&gt;이때 홀수면 소숫점 .5 로 저장될테고 짝수면 소숫점이 없을것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 만약 % 1 을해서 0으로 떨어지지 않는다면 소숫점이 있다는 소리이므로 s의 가운데 자리 인덱스번호는&lt;span style=&quot;color: #333333;&quot;&gt;middleSNumber의 반내림 수가 될것이고 그놈을 찾아서 answer에 담는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;그게 아니라면 정수라는 소리니까 리터럴템플릿을 이용해서 &lt;span style=&quot;color: #333333;&quot;&gt;s의 길이를 &lt;/span&gt;2로 나눈 수와 -1한 수를 s의 인덱스번호로 찾아서 answer에 담는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGR6cv/btqDPMDMimP/JrE9azeU7aSIf19pHhNkX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGR6cv/btqDPMDMimP/JrE9azeU7aSIf19pHhNkX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGR6cv/btqDPMDMimP/JrE9azeU7aSIf19pHhNkX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGR6cv%2FbtqDPMDMimP%2FJrE9azeU7aSIf19pHhNkX0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Study/Algorithmus</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/352</guid>
      <comments>https://andwinter.tistory.com/352#entry352comment</comments>
      <pubDate>Fri, 1 May 2020 02:49:17 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] 프로그래머스 코딩테스트연습 (나누어 떨어지는 숫자 배열) Lv.1</title>
      <link>https://andwinter.tistory.com/351</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;문제 설명&lt;/p&gt;
&lt;p&gt;array의 각 element 중 divisor로 나누어 떨어지는 값을 오름차순으로 정렬한 배열을 반환하는 함수, solution을 작성해주세요.&lt;br /&gt;divisor로 나누어 떨어지는 element가 하나도 없다면 배열에 -1을 담아 반환하세요.&lt;/p&gt;
&lt;p&gt;제한사항&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;arr은 자연수를 담은 배열입니다.&lt;/li&gt;
&lt;li&gt;정수 i, j에 대해 i &amp;ne; j 이면 arr[i] &amp;ne; arr[j] 입니다.&lt;/li&gt;
&lt;li&gt;divisor는 자연수입니다.&lt;/li&gt;
&lt;li&gt;array는 길이 1 이상인 배열입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;입출력 예&lt;/p&gt;
&lt;p&gt;arrdivisorreturn&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[5, 9, 7, 10]&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;[5, 10]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2, 36, 1, 3]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;[1, 2, 3, 36]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[3,2,6]&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;[-1]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;입출력 예 설명&lt;/p&gt;
&lt;p&gt;입출력 예#1&lt;br /&gt;arr의 원소 중 5로 나누어 떨어지는 원소는 5와 10입니다. 따라서 [5, 10]을 리턴합니다.&lt;/p&gt;
&lt;p&gt;입출력 예#2&lt;br /&gt;arr의 모든 원소는 1으로 나누어 떨어집니다. 원소를 오름차순으로 정렬해 [1, 2, 3, 36]을 리턴합니다.&lt;/p&gt;
&lt;p&gt;입출력 예#3&lt;br /&gt;3, 2, 6은 10으로 나누어 떨어지지 않습니다. 나누어 떨어지는 원소가 없으므로 [-1]을 리턴합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;과정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1588263373000&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(arr, divisor) {
  var result = [];
    
  arr.forEach((item) =&amp;gt; {
    const div = item % divisor;
    if (div === 0) {
      result.push(item);
    }
  });
    result.length === 0 ? result.push(-1) :  result.sort((a, b) =&amp;gt; a - b)
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1. result 배열을 생성&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 자연수를 담은 배열 arr 을 돌며 각 인자를 나눌값(divisor) 로 나눈 나머지 값(%)을 div에 담는다.&lt;/p&gt;
&lt;p&gt;만약&amp;nbsp; div 가 0이라면 result 배열에 해당 인자를 담는다.(divisor로 나누어 딱 떨어지는 값이니까)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. 만약 나누어 떨어진 값이 없다면 (result 배열에 담긴 값이 없다면) answer 에 -1 을 담고,&lt;/p&gt;
&lt;p&gt;result 에 값이 담겨 있다면 오름차순으로 정렬한뒤 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctCNoi/btqDPYjDKqC/H775ezGf5N8iIw2tpbgtak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctCNoi/btqDPYjDKqC/H775ezGf5N8iIw2tpbgtak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctCNoi/btqDPYjDKqC/H775ezGf5N8iIw2tpbgtak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctCNoi%2FbtqDPYjDKqC%2FH775ezGf5N8iIw2tpbgtak%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Study/Algorithmus</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/351</guid>
      <comments>https://andwinter.tistory.com/351#entry351comment</comments>
      <pubDate>Fri, 1 May 2020 01:31:36 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘]프로그래머스 코딩테스트연습 (완주하지 못한 선수) Lv.1</title>
      <link>https://andwinter.tistory.com/350</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.&lt;/p&gt;
&lt;p&gt;마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p&gt;제한사항&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.&lt;/li&gt;
&lt;li&gt;completion의 길이는 participant의 길이보다 1 작습니다.&lt;/li&gt;
&lt;li&gt;참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.&lt;/li&gt;
&lt;li&gt;참가자 중에는 동명이인이 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;입출력 예&lt;/p&gt;
&lt;p&gt;participant&amp;nbsp; &amp;nbsp; completion&amp;nbsp; &amp;nbsp; return&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[leo,&lt;span&gt;&amp;nbsp;&lt;/span&gt;kiki,&lt;span&gt;&amp;nbsp;&lt;/span&gt;eden]&lt;/td&gt;
&lt;td&gt;[eden,&lt;span&gt;&amp;nbsp;&lt;/span&gt;kiki]&lt;/td&gt;
&lt;td&gt;leo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[marina,&lt;span&gt;&amp;nbsp;&lt;/span&gt;josipa,&lt;span&gt;&amp;nbsp;&lt;/span&gt;nikola,&lt;span&gt;&amp;nbsp;&lt;/span&gt;vinko,&lt;span&gt;&amp;nbsp;&lt;/span&gt;filipa]&lt;/td&gt;
&lt;td&gt;[josipa,&lt;span&gt;&amp;nbsp;&lt;/span&gt;filipa,&lt;span&gt;&amp;nbsp;&lt;/span&gt;marina,&lt;span&gt;&amp;nbsp;&lt;/span&gt;nikola]&lt;/td&gt;
&lt;td&gt;vinko&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[mislav,&lt;span&gt;&amp;nbsp;&lt;/span&gt;stanko,&lt;span&gt;&amp;nbsp;&lt;/span&gt;mislav,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ana]&lt;/td&gt;
&lt;td&gt;[stanko,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ana,&lt;span&gt;&amp;nbsp;&lt;/span&gt;mislav]&lt;/td&gt;
&lt;td&gt;mislav&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;입출력 예 설명&lt;/p&gt;
&lt;p&gt;예제 #1&lt;br /&gt;leo는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.&lt;/p&gt;
&lt;p&gt;예제 #2&lt;br /&gt;vinko는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.&lt;/p&gt;
&lt;p&gt;예제 #3&lt;br /&gt;mislav는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://hsin.hr/coci/archive/2014_2015/contest2_tasks.pdf&quot;&gt;출처&lt;/a&gt;&lt;/p&gt;
&lt;figure class=&quot;og-loading&quot; contenteditable=&quot;false&quot;&gt;불러오는 중입니다...&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;과정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1588261036771&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(participant, completion) {
    var answer = '';
    
    let myMap = new Map();
    
    for(const person of participant){
        if(!myMap.get(person)){
            myMap.set(person, 1)
        }else{
            myMap.set(person, myMap.get(person) + 1)
        }
    }
    for(const person of completion){
            myMap.set(person, myMap.get(person) - 1)
    }
    for(const person of participant){
        if(myMap.get(person)){
           return person
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1. 새로운 맵을 생성한다.( new Map(); )&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. for of 문을 이용하여 참가자 명단을 돌며 인자를 가지고 맵을 생성하는데&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;만약 내가 생성한 맵에 해당인자(person)가 없다면 map 에 person 의 key 를 가진 value 는 1의 프로퍼티를 생성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;하고,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;만약 해당값이 이미 존재한다면(이는 동명이인이 있다는 것을 뜻함)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이미 있는 프로퍼티의 value 에 +1 을 함.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. 이번엔 for of 문으로 완주자명단을 돌며&lt;/p&gt;
&lt;p&gt;해당 인자(person)와 생성된 맵에서 key값이 동일한 프로퍼티의 value 값에서 -1을함.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;4. 마지막으로 참가자명단을 한번더 돌며 참가자들의 이름을 map 에 대입하여 value 가 0 이 아닌 대상을 리턴함.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chX4ou/btqDQc2X7S2/ZHZd6lkJKEztuBX8ZZWgDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chX4ou/btqDQc2X7S2/ZHZd6lkJKEztuBX8ZZWgDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chX4ou/btqDQc2X7S2/ZHZd6lkJKEztuBX8ZZWgDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchX4ou%2FbtqDQc2X7S2%2FZHZd6lkJKEztuBX8ZZWgDK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Algorithmus</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/350</guid>
      <comments>https://andwinter.tistory.com/350#entry350comment</comments>
      <pubDate>Fri, 1 May 2020 01:04:17 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] 프로그래머스 코딩테스트연습 (체육복) Lv.1</title>
      <link>https://andwinter.tistory.com/349</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.&lt;/p&gt;
&lt;p&gt;전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p&gt;제한사항&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전체 학생의 수는 2명 이상 30명 이하입니다.&lt;/li&gt;
&lt;li&gt;체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.&lt;/li&gt;
&lt;li&gt;여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.&lt;/li&gt;
&lt;li&gt;여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.&lt;/li&gt;
&lt;li&gt;여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;입출력 예&lt;/p&gt;
&lt;p&gt;n&amp;nbsp; lost&amp;nbsp; reserve&amp;nbsp; return&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;[2, 4]&lt;/td&gt;
&lt;td&gt;[1, 3, 5]&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;[2, 4]&lt;/td&gt;
&lt;td&gt;[3]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;[3]&lt;/td&gt;
&lt;td&gt;[1]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;입출력 예 설명&lt;/p&gt;
&lt;p&gt;예제 #1&lt;br /&gt;1번 학생이 2번 학생에게 체육복을 빌려주고, 3번 학생이나 5번 학생이 4번 학생에게 체육복을 빌려주면 학생 5명이 체육수업을 들을 수 있습니다.&lt;/p&gt;
&lt;p&gt;예제 #2&lt;br /&gt;3번 학생이 2번 학생이나 4번 학생에게 체육복을 빌려주면 학생 4명이 체육수업을 들을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://hsin.hr/coci/archive/2009_2010/contest6_tasks.pdf&quot;&gt;출처&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;과정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1588259947826&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(n, lost, reserve) {
    var answer = 0;
    let result = [];
  for (let i = 0; i &amp;lt; n; i++) {
    result.push(1);
  }
  lost.forEach((element) =&amp;gt; {
    result[element - 1] = 0;
  });
  reserve.forEach((element) =&amp;gt; {
    result[element - 1] = result[element - 1] + 1;
  });
    reserve.forEach((element) =&amp;gt; {
    if (element !== 1 &amp;amp;&amp;amp; result[element - 2] === 0 &amp;amp;&amp;amp; result[element - 1] &amp;gt; 1) {
      result[element - 1] = result[element - 1] - 1;
      result[element - 2] = result[element - 2] + 1;
    } else if (element !== 1 &amp;amp;&amp;amp; result[element] === 0 &amp;amp;&amp;amp; result[element - 1] &amp;gt; 1) {
      result[element - 1] = result[element - 1] - 1;
      result[element] = result[element] + 1;
    } else if (element === 1 &amp;amp;&amp;amp; result[element] === 0 &amp;amp;&amp;amp; result[element] &amp;gt; 1) {
      result[element - 1] = result[element - 1] - 1;
      result[element] = result[element] + 1;
    } else if (element === n &amp;amp;&amp;amp; result[element-2] === 0 &amp;amp;&amp;amp; result[element-1] &amp;gt; 1){
      result[element-1] = result[element-1] - 1;
      result[element-2] = result[element-2] + 1;
    }
  });
      result.forEach(element =&amp;gt; {
      if(element &amp;gt; 0){
          answer++
      }
  })
return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1. n 만큼의 길이를 가지고 각각 요소가 1인 배열 result를 만듦(init)&lt;/p&gt;
&lt;p&gt;2. result 안에서 lost 의 index 번호를 찾아서 해당 학생의 값을 -1 함(도난)&lt;/p&gt;
&lt;p&gt;3. result 배열 중 reserve 의 index 번호에 있는 값에 + 1을 함(여분)&lt;/p&gt;
&lt;p&gt;4. reserve 배열(여분) 을 반복하여 돌면서&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;만약 여분을 가진 학생의 인덱스가 1이 아니고(첫번째가 아니고) 자신의 앞의 학생이 도난을 당했으며, 자기 자신이 1보다 크다면(이는 여분을 가지고 있었다가 도난 당한 경우를 대비함)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;자기자신의 값에서 1을 빼고 앞에 학생에게 1을 더함.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;만약 위의 경우가 아니고(앞학생이 도난당하지 않았고) 자신의 인덱스가 1이 아니며, 뒤의 학생이 도난당한경우&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;자신에서 -1을하고 뒤에 학생에게 +1을 해줌.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;만약 내가 첫번째 학생이고 뒤에 학생이 도난 당하였으며 나 자신이 여분을 가지고 있다면&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;뒤에 학생에게 +1 을 하고 자신에서 -1을 함.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;만약 내가 맨 마지막 학생이고 앞의 학생이 도난을 당했으며 내가 여분을 가지고 있다면&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;앞의 학생에게 +1, 나자신은 -1을 함.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;5. 끝으로 result 를 돌며 요소의 값이 0이 아닌경우 answer 에 +1 씩 더해주며 인원수를 세고 종료.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B7kNp/btqDPK6Ytsp/k4hxiEQ6ZiQbqXj0wG5eW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B7kNp/btqDPK6Ytsp/k4hxiEQ6ZiQbqXj0wG5eW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B7kNp/btqDPK6Ytsp/k4hxiEQ6ZiQbqXj0wG5eW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB7kNp%2FbtqDPK6Ytsp%2Fk4hxiEQ6ZiQbqXj0wG5eW0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Algorithmus</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/349</guid>
      <comments>https://andwinter.tistory.com/349#entry349comment</comments>
      <pubDate>Thu, 30 Apr 2020 21:54:47 +0900</pubDate>
    </item>
    <item>
      <title>[styled component] 가상선택자와 참조</title>
      <link>https://andwinter.tistory.com/347</link>
      <description>&lt;pre id=&quot;code_1587826921018&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.parent {
width:300px;
height:300px;
background:#eee;
}

.child {
width:50px;
height:50px;
background:#fff;
}


.parent:hover .child{
background:#000
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;부모태그(parent)에게 hover 시 자식태그(child) 의 background color 가 바뀌는 css&lt;/p&gt;
&lt;p&gt;이를 react 의 styled component 에서 사용시&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1587827051129&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Child = styled.div`
  width:50px;
  height:50px;
  background:#fff;
`

const Parent = styled.div`
  width:300px;
  height:300px;
  background:#eee;
  
  &amp;amp;:hover ${Child}{
  background:#000;
  }
`;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SCSS 식으로 작성 가능하다.&lt;/p&gt;
&lt;p&gt;위와같이 다른 styled component 를 참조할 수 있다.&lt;/p&gt;</description>
      <category>Study/react.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/347</guid>
      <comments>https://andwinter.tistory.com/347#entry347comment</comments>
      <pubDate>Sun, 26 Apr 2020 00:07:18 +0900</pubDate>
    </item>
    <item>
      <title>[VSCODE]eslint 쌩 쑈를 해도 안먹던 eslint가..</title>
      <link>https://andwinter.tistory.com/346</link>
      <description>&lt;p&gt;글로벌 설치 후 eslint가 적용이 안되더라.&lt;/p&gt;
&lt;p&gt;무슨 짓을 해도 안됐다.&lt;/p&gt;
&lt;p&gt;경로가 바탕화면의 a 라는 폴더에 front 와 back 을 나누었고&lt;/p&gt;
&lt;p&gt;front 에 npm 을 init 하고 그 안에서 eslint 를 설치했고 글로벌로도 설치했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;바탕화면&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;|&amp;nbsp; &lt;span style=&quot;color: #333333;&quot;&gt;＼&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;|&amp;nbsp; &amp;nbsp; ＼&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;|&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #333333;&quot;&gt;＼&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;|&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #333333;&quot;&gt;＼&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;front&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #333333;&quot;&gt;back&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(npm)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게 되어있었던건데..&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;front 에서 next 가 실행되고 있었고&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;뭘해도 eslint 가 교정을 해주지 않길래... 뭘해야하나 싶었는데&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;setting.json 에서&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1586353635302&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &quot;eslint.workingDirectories&quot;: [

        &quot;front&quot;

    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;이놈으로 경로를 설정해주니까 그제서 되더라.........&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;에러메세지를 잘 봤어야하는데 ㅠ&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/346</guid>
      <comments>https://andwinter.tistory.com/346#entry346comment</comments>
      <pubDate>Wed, 8 Apr 2020 22:47:30 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js]express, mongoDB, mongoose 를 통한 게시판 구현</title>
      <link>https://andwinter.tistory.com/345</link>
      <description>&lt;p&gt;이전에 작성한 회원가입 구현 파일을 그대로 사용하여&lt;/p&gt;
&lt;p&gt;게시판 구현도 해보려 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선 구현할 기능은 정말 간단한 게시판이고 로그인된 상태로만 글쓰기가 가능하게 만들것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선 템플릿의 수정과 생성이 필요하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585556617607&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;									//header.pug

    .header__colume
        a(href=&quot;/notice&quot;) 게시판
       
       
									//mixins/post.pug
 
 mixin post(video = {})
    a(href=`/notice/${video.id}`)=video.title
    if video.description
        p=video.description
       
       
									//notice.pug
 
extends layout/main
include mixins/post

block content
    a(href=&quot;/posting&quot;) 게시물 작성
    ul
        each posting in postList
            li
                +post({
                    title:posting.title,
                    id:posting._id
                })
 
									//postDetail.pug

extends layout/main
include mixins/post

block content
    h1=postList.title
    p=postList.description

									//posting.pug
                                    
extends layout/main
include mixins/post

block content
    form(action=`/posting`, method=&quot;post&quot;)
        input(type=&quot;text&quot;, name=&quot;title&quot;, placeholder=&quot;제목&quot;)
        textarea(name=&quot;description&quot;, placeholder=&quot;Description&quot;)
        input(type=&quot;submit&quot;, name=&quot;submit&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 헤더에 게시판으로 갈 수 있는 a 태그를 만들어 줬다.&lt;/p&gt;
&lt;p&gt;2. 게시판은 notice.pug 템플릿이다.&lt;/p&gt;
&lt;p&gt;3. 게시판에 글은 mixin 으로 post.pug 템플릿을 사용한다.&lt;/p&gt;
&lt;p&gt;4. 해당 게시물을 클릭하면 postDetail.pug 가 렌더된다.&lt;/p&gt;
&lt;p&gt;5. 게시글 작성은 posting.pug 에 있는 form 을 통해 method post 로 submit 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 mixin 을 사용해서 post와 postDetail 을 같이 쓸 수 도 있었는데&lt;/p&gt;
&lt;p&gt;만들고 나서 생각났다...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;템플릿은 준비되었으니 라우터를 만들어 본다.&lt;/p&gt;
&lt;p&gt;기존에 쓰던 globalRouter 가 있어서 따로 만들려 했지만 또 귀찮아서&lt;/p&gt;
&lt;p&gt;그냥 한꺼번에 쓰기로 한다. 이럴꺼면 굳이 왜 이름을 globalRouter로 지었는지..&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585556867099&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;globalRouter.get(&quot;/notice&quot;, getNotice); //게시판 접근시

globalRouter.get(&quot;/postnotice&quot;, getPosting); //게시글 작성 화면에 접근시
globalRouter.post(&quot;/postnotice&quot;, postPosting); //게시글작성 후 submit 되었을시

globalRouter.get(&quot;/notice/:id&quot;, postDetail); //게시글을 클릭시&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 라우터에 맞는 컨트롤러를 만들어보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585557042286&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getNotice = (req, res) =&amp;gt; {};

export const getPosting = (req, res) =&amp;gt; {};
export const postPosting = (req, res) =&amp;gt; {};

export const postDetail = (req, res) =&amp;gt; {};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 필요한건 mongoose를 이용해 DB에 들어갈 모델을 만들어 준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585557219458&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Post.js

import mongoose from &quot;mongoose&quot;;

const PostSchema = new mongoose.Schema({
  title: {
    type: String,
    required: &quot;title is required&quot;
  },
  description: {
    type: String,
    required: &quot;description is required&quot;
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  comments: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: &quot;Comment&quot;
    }
  ],
  creator: {
    type: mongoose.Schema.Types.ObjectId,
    ref: &quot;User&quot;
  }
});

const model = mongoose.model(&quot;Post&quot;, PostSchema);
export default model;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;크게 이렇게 필요할 것 같다. 코멘트를 달 수 있게 만들(예정)것 이고 작성자도 구분할 수 있었으면 좋겠어서&lt;/p&gt;
&lt;p&gt;Schema는 이정도 만들었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;모델도 생겼겠다 이제 다시 컨트롤러 작업을 시작해 본다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선 작성된 게시글이 있을때 게시판에 넘겨주는것부터 시작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1585557374783&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getNotice = async (req, res) =&amp;gt; {
  const postList = await Post.find({});
  res.render(&quot;notice&quot;, { postList });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Post.find 는 Post 안에 있는 모든 정보들을 꺼내줄 것이고 그것을 postList 라는 변수에 담아 게시판 템플릿으로 전달한다. 앞서 템플릿에서는 이미 postList 를 받아서 그릴 준비가 되어있으므로 이름만 맞춰주면된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 게시물 작성부분을 작업해보자.&lt;/p&gt;
&lt;p&gt;우선 게시글 작성 템플릿을 렌더해줄 get 을 먼저 만든다&lt;/p&gt;
&lt;pre id=&quot;code_1585557532299&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getPosting = (req, res) =&amp;gt; {
  res.render(&quot;postnotice&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getPosting 은 그저 게시글 작성 폼을 렌더하는 역할이므로 이게 전부이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 form 에서 submit되었을때 즉 post 되었을때를 작성해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1585558047383&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const postPosting = async (req, res) =&amp;gt; {
  const {
    body: { title, description }
  } = req;

  const newPosting = await Post.create({
    title,
    description,
    creator: req.user.id
  });

  res.redirect(&quot;/notice&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이전에 설치했던 미들웨어인 body-parser 에 의하여 submit 되었을시&lt;/p&gt;
&lt;p&gt;form 에 입력했던 정보들을 쉽게 조회할 수 있는데&lt;/p&gt;
&lt;p&gt;이를 구조분해 할당으로 필요한 정보만 뺀다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 mongoDB에 데이터를 만들 수 있게 이전에 작성했던 모델에 양식 맞춰서&lt;/p&gt;
&lt;p&gt;Post.create 를 이용하여 작성해 준다.&lt;/p&gt;
&lt;p&gt;뭔가 말이 어려운데 그냥 폼에서 넘어온 정보를 미리 지정해놓은 서식에 맞춰서&lt;/p&gt;
&lt;p&gt;꾸민다음에 DB에 넘기는것 쯤으로 생각하면 될것같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이후 res.redirect 를 이용해 게시판 처음화면으로 이동하도록 해준다.&lt;/p&gt;
&lt;p&gt;작성한 게시물로 이동하게 하려면&lt;/p&gt;
&lt;pre id=&quot;code_1585558561254&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;res.redirect(`/notice/${newPosting.id}`);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;redirect 부분을 요런식으로 바꿔주기만 하면된다. 쉽다 쉬워&lt;/p&gt;
&lt;p&gt;(라우터를 만들때 /notice/:id 로 만들어놨기때문에 해당 게시물의 id가 무엇이든 이렇게 연결되는것)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;근데 이렇게하고 테스트하면 해당 url에 대한 컨트롤러를 아직 만들지 않았기때문에&lt;/p&gt;
&lt;p&gt;무한로딩 혹은 에러가 날것이다.&lt;/p&gt;
&lt;p&gt;얼른 만들어 주자&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585558841388&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const postDetail = async (req, res) =&amp;gt; {
  const {
    params: { id }
  } = req;

  const postList = await Post.findById(id)
  res.render(&quot;postDetail&quot;, { postList });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 params 란 &lt;span style=&quot;color: #333333;&quot;&gt;express route parameter를 이용해 추출 할 수 있는 것인데,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;앞서 라우터를 말할때 &lt;span style=&quot;color: #333333;&quot;&gt;/notice/:id 이것을 예로 들어보면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;express 에서 url 에&lt;b&gt; :id&lt;/b&gt; 라고 작성했다면 &lt;b&gt;콜론 뒤에 id 가 key값&lt;/b&gt;이 되는 것 이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;때문에 req.params 를 콘솔에 출력해보면 { &lt;b&gt;&quot;id&quot;&lt;/b&gt;:&quot;dqwdd12312fqwe&quot; } 이런식으로 출력되어진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;id 가 아니라도 어느 이름이건 상관없이&lt;/b&gt; 작명한대로 불러올 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;내가 작성한게 key값이 되어지고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;실제 그 자리에 들어가는 정보가 value가 되는 것 이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;때문에 실제로는 게시글의 DB id가 그 자리에 들어갈꺼고 그 실제 id 를 가지고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;findById 를 통하여 DB내에서 조회하여 찾아내고 그놈을 변수에 담아&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;템플릿에 전달한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAY9Qn/btqC48G0pTB/18TZY3w2mbnvzl4Zve1Di0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAY9Qn/btqC48G0pTB/18TZY3w2mbnvzl4Zve1Di0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAY9Qn/btqC48G0pTB/18TZY3w2mbnvzl4Zve1Di0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAY9Qn%2FbtqC48G0pTB%2F18TZY3w2mbnvzl4Zve1Di0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그럼 아주 쉽게 이런 게시판을 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 미들웨어를 하나 만들고 로그인한 사람만 게시물을 작성 할 수 있게 만들어본다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;middleware.js 에 아래처럼 하나 만들어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1585559857770&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const private = (req, res, next) =&amp;gt; {
  if (req.user) {
    next();
  } else {
    res.redirect(&quot;/&quot;);
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;passport 에 의하여 현재 로그인한 사용자의 정보를 어디서든 req.user 로 조회할 수 있기 때문에&lt;/p&gt;
&lt;p&gt;가능한 일이다.&lt;/p&gt;
&lt;p&gt;이렇게 만약 현재 사용자가 로그인 되어져있다면 다음 작업을 실행하고 아니라면 홈으로&lt;/p&gt;
&lt;p&gt;redirect 되도록 만들어주고&lt;/p&gt;
&lt;pre id=&quot;code_1585559924965&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;globalRouter.get(&quot;/posting&quot;, private, getPosting);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 가운대에 꽂아주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음은 코멘트기능과 스타일링을 좀 해봐야 겠다.&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/345</guid>
      <comments>https://andwinter.tistory.com/345#entry345comment</comments>
      <pubDate>Mon, 30 Mar 2020 18:14:31 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] 회원가입 구현(로그인,로그아웃,회원가입,passport, Mongo Store)</title>
      <link>https://andwinter.tistory.com/344</link>
      <description>&lt;p&gt;&lt;b&gt;Passport&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;로컬 로그인 뿐만아니라 소셜 로그인에서까지 쉽게 인증이 가능한 미들웨어이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.passportjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://www.passportjs.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1584790077334&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Passport.js&quot; data-og-description=&quot;Simple, unobtrusive authentication for Node.js&quot; data-og-host=&quot;www.passportjs.org&quot; data-og-source-url=&quot;http://www.passportjs.org/&quot; data-og-url=&quot;http://www.passportjs.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/caeMS1/hyFmUyvkdD/p7mIhAlbsuGXmGyHSaSoj0/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260,https://scrap.kakaocdn.net/dn/cLCO0h/hyFmTTSI6h/FcHK3u3g1oPgp4z8DchAAk/img.png?width=3010&amp;amp;height=1612&amp;amp;face=0_0_3010_1612&quot;&gt;&lt;a href=&quot;http://www.passportjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.passportjs.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/caeMS1/hyFmUyvkdD/p7mIhAlbsuGXmGyHSaSoj0/img.png?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260,https://scrap.kakaocdn.net/dn/cLCO0h/hyFmTTSI6h/FcHK3u3g1oPgp4z8DchAAk/img.png?width=3010&amp;amp;height=1612&amp;amp;face=0_0_3010_1612');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Passport.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Simple, unobtrusive authentication for Node.js&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.passportjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;** mongoDB 와 mongoose 를 사용하고 있는 환경에서, 보다 쉽게 passport 를 이용한 사용자 인증이 가능하도록 만들어주는 플러그인인&lt;/p&gt;
&lt;p&gt;passport-local-mongoose 를 사용한다. 이는 기본적인 설정을(패스워드 확인 등) 미리 다 구성해놓았기때문에&lt;/p&gt;
&lt;p&gt;편리하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saintedlama/passport-local-mongoose&quot;&gt;https://github.com/saintedlama/passport-local-mongoose&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1584802989412&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;saintedlama/passport-local-mongoose&quot; data-og-description=&quot;Passport-Local Mongoose is a Mongoose plugin that simplifies building username and password login with Passport - saintedlama/passport-local-mongoose&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/saintedlama/passport-local-mongoose&quot; data-og-url=&quot;https://github.com/saintedlama/passport-local-mongoose&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cGbMsb/hyFmMUYVIq/OXKZk05KjheRKLOvNV8MgK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://github.com/saintedlama/passport-local-mongoose&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/saintedlama/passport-local-mongoose&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cGbMsb/hyFmMUYVIq/OXKZk05KjheRKLOvNV8MgK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;saintedlama/passport-local-mongoose&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Passport-Local Mongoose is a Mongoose plugin that simplifies building username and password login with Passport - saintedlama/passport-local-mongoose&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;회원가입, 로그인 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;준비사항&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 로그인과 조인을 위한 템플릿&lt;/p&gt;
&lt;pre id=&quot;code_1584800461553&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//main 템플릿
    
    Doctype &amp;lt;!DOCTYPE html&amp;gt;
	html(lang=&quot;en&quot;)
    head
        meta(charset=&quot;UTF-8&quot;)
        meta(name=&quot;viewport&quot;, content=&quot;width=device-width, initial-scale=1.0&quot;)
        title Document
    body
        include ../partials/header
        block content
    
    
//home 템플릿
    
	extends layout/main

	block content
	h1 로그인을 해보자
    
    
//join 템플릿
   
   form(action=&quot;/join&quot;, method=&quot;post&quot;)
            input(type=&quot;text&quot;, name=&quot;name&quot;, placeholder=&quot;Full Name&quot;, required=true)
            input(type=&quot;email&quot;, name=&quot;email&quot;, placeholder=&quot;Email&quot;, required=true)
            input(type=&quot;password&quot;, name=&quot;password&quot;, placeholder=&quot;Password&quot;, required=true)
            input(type=&quot;password&quot;, name=&quot;password2&quot;, placeholder=&quot;Verify password&quot;, required=true)
            input(type=&quot;submit&quot;, value=&quot;Join now&quot;)
            
            
//login 템플릿
   
           form(action=&quot;/login&quot;, method=&quot;post&quot;)
            input(type=&quot;email&quot;, name=&quot;email&quot;, placeholder=&quot;Email&quot;, required=true)
            input(type=&quot;password&quot;, name=&quot;password&quot;, placeholder=&quot;password&quot;, required=true)
            input(type=&quot;submit&quot;, value=&quot;Log in&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2. 몽고db, 몽구스가 설치되어있고, db.js 도 구성되어있어야함.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://andwinter.tistory.com/339&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2020/02/29 - [Study/script.js] - [full]mongoDB, mongoose&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1584800621665&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[full]mongoDB, mongoose&quot; data-og-description=&quot;mongoDB = NoSQL Databass mongoose = mongoDB와 자바스크립트를 연결 시켜줌 장점 - 적은 규칙과 절차가 간편하게 작업이 가능한 데이터 베이스 이다. 1. mongoDB 다운로드 https://www.mongodb.com/cloud/atlas/..&quot; data-og-host=&quot;andwinter.tistory.com&quot; data-og-source-url=&quot;https://andwinter.tistory.com/339&quot; data-og-url=&quot;https://andwinter.tistory.com/339&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ci5cCp/hyFmPxoPnb/ZMFxoXESMBKmeYUDt3ReG1/img.png?width=800&amp;amp;height=23&amp;amp;face=0_0_800_23,https://scrap.kakaocdn.net/dn/jhC2c/hyFmKbLZsy/XJhPtm7UtbV5HnHXzEfIS0/img.png?width=800&amp;amp;height=23&amp;amp;face=0_0_800_23,https://scrap.kakaocdn.net/dn/cUfsbl/hyFmHMTflp/hS9CKYNg0q8HGZBaq2Osr0/img.png?width=864&amp;amp;height=239&amp;amp;face=0_0_864_239&quot;&gt;&lt;a href=&quot;https://andwinter.tistory.com/339&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://andwinter.tistory.com/339&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ci5cCp/hyFmPxoPnb/ZMFxoXESMBKmeYUDt3ReG1/img.png?width=800&amp;amp;height=23&amp;amp;face=0_0_800_23,https://scrap.kakaocdn.net/dn/jhC2c/hyFmKbLZsy/XJhPtm7UtbV5HnHXzEfIS0/img.png?width=800&amp;amp;height=23&amp;amp;face=0_0_800_23,https://scrap.kakaocdn.net/dn/cUfsbl/hyFmHMTflp/hS9CKYNg0q8HGZBaq2Osr0/img.png?width=864&amp;amp;height=239&amp;amp;face=0_0_864_239');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[full]mongoDB, mongoose&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;mongoDB = NoSQL Databass mongoose = mongoDB와 자바스크립트를 연결 시켜줌 장점 - 적은 규칙과 절차가 간편하게 작업이 가능한 데이터 베이스 이다. 1. mongoDB 다운로드 https://www.mongodb.com/cloud/atlas/..&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;andwinter.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;3. 라우터와 컨트롤러의 준비&lt;/p&gt;
&lt;pre id=&quot;code_1584800543219&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//라우터

globalRouter.get(&quot;/join&quot;, getJoin);
globalRouter.post(&quot;/join&quot;, postJoin, getLogin);

globalRouter.get(&quot;/login&quot;, getLogin);
globalRouter.post(&quot;/login&quot;, postLogin);

//컨트롤러

export const getJoin = (req, res) =&amp;gt; {
  res.render(&quot;join&quot;);
};
export const postJoin = (req, res, next) =&amp;gt; {};

export const getLogin = (req, res) =&amp;gt; {
  res.render(&quot;login&quot;);
};
export const postLogin = (req, res) =&amp;gt; {};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;당연한거니까..&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;회원가입(Join)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선 User 모델을 만들어준다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이때 passportLocalMongoose 가 쓰이기 때문에 설치해준다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;npm i passport-local-mongoose&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584800716287&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import mongoose from &quot;mongoose&quot;;
import passportLocalMongoose from &quot;passport-local-mongoose&quot;
const UserSchema = new mongoose.Schema({
  name: String,
  email: String
});

UserSchema.plugin(passportLocalMongoose, { usernameField: &quot;email&quot; });

const model = mongoose.model(&quot;User&quot;, UserSchema);

export default model;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 &lt;span style=&quot;color: #333333;&quot;&gt;passportLocalMongoose의 usernameField 는 사용자의 이름이 어떤놈이 될지를 정하는 것이다.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게 세팅해놓으면 알아서 &lt;span style=&quot;color: #333333;&quot;&gt;passportLocalMongoose 가 패스워드 인증이라던지 회원 이름의 변경이라던지 잡다한 일들을 알아서 해줄것이다.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이후에 passport를 설치해준다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;npm&amp;nbsp;i&amp;nbsp;passport&amp;nbsp;passport-local&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;passport 와 passport-local 둘다 설치해준다.&lt;/p&gt;
&lt;p&gt;그리고 passport.js 를 상위 디렉토리에 생성한뒤&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1584801086703&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import passport from &quot;passport&quot;
import User from &quot;./model/User&quot;

passport.use(User.createStrategy())

passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와같이 적는다.&lt;/p&gt;
&lt;p&gt;여기서 createStrategy() 란 &lt;span style=&quot;color: #333333;&quot;&gt;passportLocalMongoose&lt;span&gt; 의 메소드로&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;passport-local 의 LocalStrategy 인스텐스를 만든다(원래는 조금더 귀찮고 복잡한 passport의 과정이지만 &lt;span style=&quot;color: #333333;&quot;&gt;passportLocalMongoose&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt; 에의해 한줄로 구현가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;serializeUser, deserializeUser 또한 마찬가지로 serialization 은 현재 브라우저의 유저에 대하여 어떠한 정보를 쿠키가 가질수 있는가 를 의미(보안상의 이유로 user.id 만을 넘겨주길 권장) 하며(쿠키에 어떤 필드만을 넘겨 저장시킬것인가) deserialization 은 쿠키의 정보를 통하여 어떻게 유저를 찾을것인가 를 의미한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;원래는 직접 설정해 주어야 하지만 이역시 &lt;span style=&quot;color: #333333;&quot;&gt;passportLocalMongoose 에 의하여 간단하게 위처럼 작성 가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;이제 컨트롤러 부분(postJoin)을 수정한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584804373405&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import User from '../model/User';
import passport from 'passport';

export const postJoin = async (req, res, next) =&amp;gt; {
  const {
    body: { name, email, password, password2 }
  } = req;
  //body-parser 에 의하여 request 에서 폼의 데이터를 가져올수 있다.
  
  if (password !== password2) { //입력한 두 패스워드가 일치하지 않다면
    res.render(&quot;join&quot;); //다시 가입화면으로 돌려보낸다.
  } else { //만약 일치하다면
    try {
      const user = await User({ //model 에 등록된 User 스키마에 맞추어 객체 생성
        name,
        email
      });
      await User.register(user, password); //mongoDB에 등록
      next(); //다음으로 넘김(login 화면으로)
    } catch (error) {
      console.log(error);
      res.redirect(&quot;/&quot;); //실패할 경우 홈으로 redirect
    }
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;postJoin 즉 사용자가 가입 양식을 모두 채워 post 했을때&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. body-parser 에 의하여 폼의 데이터가 넘어온다&lt;/p&gt;
&lt;p&gt;2. 사용자가 입력한 폼의 데이터중 password 가 일치한지 검사한다.&lt;/p&gt;
&lt;p&gt;3. 일치하지 않다면 join 화면으로 돌려보내고 일치하다면&lt;/p&gt;
&lt;p&gt;4. mongoose 의 model 안에 있는 User Schema 에 맞추어 object 를 생성한다.&lt;/p&gt;
&lt;p&gt;4-1.여기서 Model() 과 Model.create() 의 차이는 객체만 만드는지 객체를 생성후 db에 저장까지 할 것인가의 차이이다. 여기서 create 를 한다면 register 전에 이미 db에 등록되어버려 이미 사용자가 등록되어있다는 오류가 뜨게 되기 때문에 Model() method 를 사용한다.&lt;/p&gt;
&lt;p&gt;5. User.register를 이용해 db에 사용자를 등록한다.&lt;/p&gt;
&lt;p&gt;6. 이후 next() 를 이용해 다음 컨트롤러가 작동하게 만든다.(postLogin)&lt;/p&gt;
&lt;p&gt;6-1. 만약 error 가 있다면 에러메세지를 출력후 home 으로 이동한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 join 을 통해 회원가입을 진행하면&lt;/p&gt;
&lt;p&gt;mongoDB에서 방금 가입한 회원을 확인 할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;로그인(Login)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;postJoin 에서 회원가입이 이루어진 뒤, next 를 통해 다음으로 넘기게 했었는데,&lt;/p&gt;
&lt;p&gt;라우터에서 그 다음이 무엇인지를 설정해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1584887168846&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;globalRouter.post(&quot;/join&quot;, postJoin, postLogin);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 다음은 postLogin 이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;postJoin 은 next 를 가진 미들웨어로써 자신이 사용한 정보를 다음으로 넘겨줄 수있는데&lt;/p&gt;
&lt;p&gt;그 정보를 postLogin 에서 받아서 사용자를 로그인 시키게 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선 userController 에서 passport 를 import 해준뒤,&lt;/p&gt;
&lt;p&gt;아래처럼 코드를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1584888011421&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import passport from &quot;passport&quot;;

//중략

export const postLogin = passport.authenticate(&quot;local&quot;, {
  failureRedirect: &quot;/login&quot;,
  successRedirect: &quot;/&quot;
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFCZFM/btqCPBqzge5/kdkdZOxGP7o84PJMn26DG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFCZFM/btqCPBqzge5/kdkdZOxGP7o84PJMn26DG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFCZFM/btqCPBqzge5/kdkdZOxGP7o84PJMn26DG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFCZFM%2FbtqCPBqzge5%2FkdkdZOxGP7o84PJMn26DG1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;passport.authenticate() 기본적으로 userName 과 password 를 찾아보도록 설정되어있고,&lt;/p&gt;
&lt;p&gt;passport-local 의 인증을 실행하고 이후 인증이 성공했을때의 redirect 와 실패했을때의 redirect 를 설정 할 수 있다.&lt;/p&gt;
&lt;p&gt;나 같은경우는 인증이 성공했을때 홈으로, 실패했을때는 다시 로그인 화면으로 가게 구성했다.&lt;/p&gt;
&lt;p&gt;그리고 app.js로 가서&lt;/p&gt;
&lt;pre id=&quot;code_1584890115144&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import passport from &quot;passport&quot;;
import &quot;./passport&quot;;


//미들웨어 마지막에
app.use(passport.initialize());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 passport 를 임폴트 하고 미들웨어의 맨 마지막에 passport.initialize() 를 붙여준다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;initialize 는 passport 를 초기화시키고 구동시키는 역할을 한다. 이놈이 없다면 오류가 뜬다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이후 회원가입을 시도해보자.&lt;/p&gt;
&lt;p&gt;그럼 홈화면으로 이동될텐데, 이는 postLogin 에서 인증이 성공했을때 &lt;span&gt;successRedirect&lt;/span&gt;를 이용해 홈으로 가게 했기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;근데좀 뭔가 로그인이 된지 안된지 모르겠고 꺼림칙하니 템플릿과 미들웨어를 추가하여 로그인 되었는지를 확인해 보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선 미들웨어를 작성한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;middleware.js 파일을 만들고(귀찮다면 app.js에 직접 작성해도 된다.)&lt;/p&gt;
&lt;pre id=&quot;code_1584892102888&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const localMiddleware = (req, res, next) =&amp;gt; {
  res.locals.user = req.user || null;
  console.log(req.user);
  next();
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위처럼 작성한다.&lt;/p&gt;
&lt;p&gt;기본적으로 passport 는 인증이 성공하면 req.user 에 인증성공한 사용자의 정보를 어느 라우터에서든 반환하게 된다.&lt;/p&gt;
&lt;p&gt;때문에 console.log(req.user) 을 사용하면 현재 &lt;b&gt;로그인한 사용자의 정보를 조회할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이제 템플릿에서 user 을 사용하여 홈화면에 출력되는 &lt;b&gt;로그인을 해보자!! 를 로그인 되었다!! 로 바꾸도록&lt;/b&gt; 할것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1584892352935&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extends layout/main

block content
    if !user
        h1 로그인을 해보자
    else
        h1 로그인이 되었습니다!!!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;if else 문을 사용하여 미들웨어에서 보내주는 user에 대한 정보가 있다면 로그인이 되었습니다! 를 출력하도록 바꿔준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 후 가입했던 아이디와 비밀번호로 로그인을 해보자.&lt;/p&gt;
&lt;p&gt;홈으로 redirect 되긴 하지만, 메세지가 바뀌질 않는다. 이는 req,user 가 존재하지 않는다는 뜻이다.&lt;/p&gt;
&lt;p&gt;이는 세션에 사용자에 대한 정보가 담긴 쿠키가 없기 때문이다. 그렇기 때문에 passport.session 이라는 놈이 필요하고, 또 express-session 이라는 패키지가 필요하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;npm i express-session&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;으로 패키지를 설치한 뒤, app.js 에서 아래와 같이 세팅해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1584892902981&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import session from &quot;express-session&quot;;

//중략

app.use(
session({
      secret: &quot;dwdasjfu123@#@fc&quot;,
      resave: true,
      saveUninitialized: false
    })
  );
app.use(passport.initialize());
app.use(passport.session());

//생략&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;express-session 의 secret 는 특정 문자열을 이용하여 사용자의 정보를 암호화한 후 쿠키에 저장하게된다.(필수 옵션)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 &lt;b&gt;passport.session()&lt;/b&gt; 은 로그인상태가 유지될 수 있게 만들어 준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게까지 해주면 다른 라우터로 이동하여도 쿠키에 저장된 사용자의 정보를 passport가 매번&lt;/p&gt;
&lt;p&gt;&lt;span&gt;deserialize 하며 로그인 상태를 유지시킬 수 있게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;이제 확인해 보면,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvX8Bu/btqCUxs46Rz/Z50Rm8MY1cjWKt65mkSXHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvX8Bu/btqCUxs46Rz/Z50Rm8MY1cjWKt65mkSXHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvX8Bu/btqCUxs46Rz/Z50Rm8MY1cjWKt65mkSXHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvX8Bu%2FbtqCUxs46Rz%2FZ50Rm8MY1cjWKt65mkSXHk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;쿠키가 저장되고 메세지도 바뀌는걸 볼 수 있다. 즉 로그인 된 상태라는걸 알 수 있다.&lt;/p&gt;
&lt;p&gt;또한 콘솔로그에서&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wr1xz/btqCSgMFG3n/1HjYbBhu9SYxdJdTn47PT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wr1xz/btqCSgMFG3n/1HjYbBhu9SYxdJdTn47PT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wr1xz/btqCSgMFG3n/1HjYbBhu9SYxdJdTn47PT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwr1xz%2FbtqCSgMFG3n%2F1HjYbBhu9SYxdJdTn47PT1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;로그인한 사용자의 정보를 확인할 수 있다(req.user)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;로그아웃(Log out)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;로그아웃은 생각보다 간단하다.&lt;/p&gt;
&lt;p&gt;req.logout() 을 이용하면 된다. 이는 passport 에서 권장하는 방법이란다.&lt;/p&gt;
&lt;p&gt;하지만.. 쿠키가 지워지지 않는다. 또 어디서보니 세션이 완전히 지워지지 않는다는 얘기가 있기때문에&lt;/p&gt;
&lt;p&gt;&lt;b&gt;req.session.destroy&lt;/b&gt; 를 이용하여 세션도 파괴하고 남는 쿠키까지 지워보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1584952387025&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const logout = (req, res) =&amp;gt; {
  req.logout(); //사용자 로그아웃
  req.session.destroy(() =&amp;gt; { //세션 파괴
    res.clearCookie('connect.sid'); //쿠키 삭제
    res.redirect('/'); //홈으로 redirect
});
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Session Store&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 마지막으로 세션의 정보를 서버가 재실행되었을때도 사용자의 로그인정보가 그대로 남아있도록 만들어 보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일단 지금까지 한 상태로 로그인을하고 서버를 재실행 시켜보자.&lt;/p&gt;
&lt;p&gt;로그인이 해제된다. 이는 세션이 날라가버리는것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 해결하기 위하여 &lt;b&gt;connect-mongo&lt;/b&gt; 를 이용할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;세션 스토어 라는 개념은 세션이 데이터를 저장하는 곳으로써 기본값은 memory store 이다.&lt;/p&gt;
&lt;p&gt;memory store 는 기본적으로 휘발성으로서 서버가 재실행되면 세션이 날라가버린다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 Mongo store 로 하여, DB에 세션 저장소를 만들고 아무리 서버가 재실행 되더라도&lt;/p&gt;
&lt;p&gt;로그인정보가 유지되도록 만들어 본다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;필요한 것으로 connect-mongo 가 있다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;npm i&amp;nbsp; connect-mongo&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;로 설치해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;app.js 에서 설치한놈을 import 하고 mongoose 도 import 한다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jdesboeufs/connect-mongo&quot;&gt;https://github.com/jdesboeufs/connect-mongo&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1584954368071&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;jdesboeufs/connect-mongo&quot; data-og-description=&quot;MongoDB session store for Express. Contribute to jdesboeufs/connect-mongo development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jdesboeufs/connect-mongo&quot; data-og-url=&quot;https://github.com/jdesboeufs/connect-mongo&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/o8iLX/hyFn3WDlv3/Nn3EDltFdfaKtGgGZwredK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=141_154_282_308&quot;&gt;&lt;a href=&quot;https://github.com/jdesboeufs/connect-mongo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jdesboeufs/connect-mongo&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/o8iLX/hyFn3WDlv3/Nn3EDltFdfaKtGgGZwredK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=141_154_282_308');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;jdesboeufs/connect-mongo&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;MongoDB session store for Express. Contribute to jdesboeufs/connect-mongo development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;connect-mongo 의 사용법에 따라서 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1584954473434&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import mongoose from &quot;mongoose&quot;
import MongoStore from &quot;connect-mongo&quot;

const Store = MongoStore(session)

app.use(
    session({
      secret: &quot;dwdasjfu123@#@fc&quot;,
      resave: false,
      saveUninitialized: false,
      store:new Store({mongooseConnection:mongoose.connection})
    })
  );&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;session 부분에 store 까지 추가하고나면 이제 서버가 재실행되더라도 로그인상태가 유지된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/344</guid>
      <comments>https://andwinter.tistory.com/344#entry344comment</comments>
      <pubDate>Mon, 23 Mar 2020 18:17:01 +0900</pubDate>
    </item>
    <item>
      <title>[error]node_modules\path-to-regexp 에러(Cannot read property 'length' of undefined)</title>
      <link>https://andwinter.tistory.com/343</link>
      <description>&lt;pre id=&quot;code_1584627413060&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; path = ('^' + path + (strict ? '' : path[path.length - 1] === '/' ? '?' : '/?'))
                                                ^

TypeError: Cannot read property 'length' of undefined
    at pathtoRegexp (D:\wetubeStudy\node_modules\path-to-regexp\index.js:63:49)
    at new Layer (D:\wetubeStudy\node_modules\express\lib\router\layer.js:45:17)
    at Function.route (D:\wetubeStudy\node_modules\express\lib\router\index.js:494:15)
    at Function.get (D:\wetubeStudy\node_modules\express\lib\router\index.js:509:22)
    at Object.&amp;lt;anonymous&amp;gt; (D:\wetubeStudy\routers/apiRouter.js:7:11)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Module._compile (D:\wetubeStudy\node_modules\pirates\lib\index.js:99:24)
    at Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
    at Object.newLoader [as .js] (D:\wetubeStudy\node_modules\pirates\lib\index.js:104:7)
    at Module.load (internal/modules/cjs/loader.js:1002:32)
    at Function.Module._load (internal/modules/cjs/loader.js:901:14)
    at Module.require (internal/modules/cjs/loader.js:1044:19)
    at require (internal/modules/cjs/helpers.js:77:18)
    at Object.&amp;lt;anonymous&amp;gt; (D:\wetubeStudy/app.js:15:1)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Module._compile (D:\wetubeStudy\node_modules\pirates\lib\index.js:99:24)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위같은 에러내용은&lt;/p&gt;
&lt;p&gt;정의되지 않은 경로로 get이나 post 를 할때 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;발생한 과정&lt;/p&gt;
&lt;pre id=&quot;code_1584627511927&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiRouter.get(routes.registerView, postRegisterView);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;이게 맞는데&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1584627525507&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiRouter.get(routes.postRegisterView, postRegisterView);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;이렇게 써서 에러가 났슴.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/343</guid>
      <comments>https://andwinter.tistory.com/343#entry343comment</comments>
      <pubDate>Thu, 19 Mar 2020 23:18:58 +0900</pubDate>
    </item>
    <item>
      <title>[full]mongoDB, mongoose</title>
      <link>https://andwinter.tistory.com/339</link>
      <description>&lt;p&gt;mongoDB = NoSQL Databass&lt;/p&gt;
&lt;p&gt;mongoose = mongoDB와 자바스크립트를 연결 시켜줌&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;장점 - 적은 규칙과 절차가 간편하게 작업이 가능한 데이터 베이스 이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. mongoDB 다운로드&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mongodb.com/cloud/atlas/lp/general/try?utm_source=google&amp;amp;utm_campaign=gs_apac_south%20korea_search_brand_atlas_desktop&amp;amp;utm_term=mongodb&amp;amp;utm_medium=cpc_paid_search&amp;amp;utm_ad=e&amp;amp;gclid=Cj0KCQiAtOjyBRC0ARIsAIpJyGMvYxKQniYgoFDGes0ZPNVUgX9b0MbFRK0-Z3AWieqZ5OFsul7we-gaAvPJEALw_wcB&quot;&gt;https://www.mongodb.com/cloud/atlas/lp/general/try?utm_source=google&amp;amp;utm_campaign=gs_apac_south%20korea_search_brand_atlas_desktop&amp;amp;utm_term=mongodb&amp;amp;utm_medium=cpc_paid_search&amp;amp;utm_ad=e&amp;amp;gclid=Cj0KCQiAtOjyBRC0ARIsAIpJyGMvYxKQniYgoFDGes0ZPNVUgX9b0MbFRK0-Z3AWieqZ5OFsul7we-gaAvPJEALw_wcB&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1582969580642&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;MongoDB&quot; data-og-description=&quot;MongoDB Atlas Cloud-hosted MongoDB service on AWS, Azure, and GCP. Deploy, operate, and scale a MongoDB database in just a few clicks. Trusted by thousands of companies, from startup to enterprise &amp;ldquo;With MongoDB Atlas, time is being spent on development and&quot; data-og-host=&quot;www.mongodb.com&quot; data-og-source-url=&quot;https://www.mongodb.com/cloud/atlas/lp/general/try?utm_source=google&amp;amp;utm_campaign=gs_apac_south%20korea_search_brand_atlas_desktop&amp;amp;utm_term=mongodb&amp;amp;utm_medium=cpc_paid_search&amp;amp;utm_ad=e&amp;amp;gclid=Cj0KCQiAtOjyBRC0ARIsAIpJyGMvYxKQniYgoFDGes0ZPNVUgX9b0MbFRK0-Z3AWieqZ5OFsul7we-gaAvPJEALw_wcB&quot; data-og-url=&quot;https://www.mongodb.com/cloud/atlas/lp/general/try&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bHWZzH/hyE7NZLTjV/4noeNHB7y87V8gXZgHOub1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/lGRg3/hyE6rYkfMf/rfEv45NYeUivlZsIXaLQNk/img.png?width=4054&amp;amp;height=705&amp;amp;face=0_0_4054_705&quot;&gt;&lt;a href=&quot;https://www.mongodb.com/cloud/atlas/lp/general/try&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.mongodb.com/cloud/atlas/lp/general/try?utm_source=google&amp;amp;utm_campaign=gs_apac_south%20korea_search_brand_atlas_desktop&amp;amp;utm_term=mongodb&amp;amp;utm_medium=cpc_paid_search&amp;amp;utm_ad=e&amp;amp;gclid=Cj0KCQiAtOjyBRC0ARIsAIpJyGMvYxKQniYgoFDGes0ZPNVUgX9b0MbFRK0-Z3AWieqZ5OFsul7we-gaAvPJEALw_wcB&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bHWZzH/hyE7NZLTjV/4noeNHB7y87V8gXZgHOub1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/lGRg3/hyE6rYkfMf/rfEv45NYeUivlZsIXaLQNk/img.png?width=4054&amp;amp;height=705&amp;amp;face=0_0_4054_705');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;MongoDB&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;MongoDB Atlas Cloud-hosted MongoDB service on AWS, Azure, and GCP. Deploy, operate, and scale a MongoDB database in just a few clicks. Trusted by thousands of companies, from startup to enterprise &amp;ldquo;With MongoDB Atlas, time is being spent on development and&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.mongodb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 실행해보기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;vsc 터미널에서 mongod =&amp;gt; mongo 실행&lt;/p&gt;
&lt;p&gt;실행이 안된다면 아래 과정 실행&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1. [내컴퓨터] 우클릭 &amp;gt; [고급시스템설정] &amp;gt; [환경변수(N)]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. 시스템변수 리스트 중 'Path'를 선택하고 편집을 선택한 후 'Mongodb의 설치경로￦bin'을 추가&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. vscode 재실행 후 터미널에 'mongod'입력&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. db.js 에 연결시키기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582970181103&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import mongoose from &quot;mongoose&quot;;

mongoose.connect(&quot;mongodb://localhost:포트번호/Databass이름&quot;, {
   useUnifiedTopology: true,
  useNewUrlParser: true
});

const db = mongoose.connection;

const handleOpen = () =&amp;gt; console.log(&quot;Connected to DB&quot;);
const handleError = error =&amp;gt; console.log(`error on DB connection:${error}`);

db.once(&quot;open&quot;, handleOpen);
db.on(&quot;error&quot;, handleError);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;useNewUrlParser, useUnifiedTopology는 이걸 안적고 실행해보면&lt;/p&gt;
&lt;p&gt;mongoose에서 자꾸 에러를 뱉는데(아래같은)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfrgCs/btqCn1V5YT8/9USMsjj1esYZ5lz2oowz3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfrgCs/btqCn1V5YT8/9USMsjj1esYZ5lz2oowz3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfrgCs/btqCn1V5YT8/9USMsjj1esYZ5lz2oowz3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfrgCs%2FbtqCn1V5YT8%2F9USMsjj1esYZ5lz2oowz3k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;즉 이놈은 사용되지 않고, 신버전에서 제거될꺼다. 사용하려면 추가해라. 이런느낌이다.&lt;/p&gt;
&lt;p&gt;경고가 자꾸 뜨니까 추가해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;export 되어질 mongoose.connection 를 만들고 거기에&lt;/p&gt;
&lt;p&gt;서버가 연결되었을때 메세지를 출력하는 함수와 에러가 났을때의 메세지출력 함수를 만들어&lt;/p&gt;
&lt;p&gt;.once와 .on 으로 적어준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 once와 on 은 mongoose 의 Quick start 가이드에 설명되어있다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ENvd7/btqCpResbKH/rBJ4klMz4w3g9kcBqyzvUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ENvd7/btqCpResbKH/rBJ4klMz4w3g9kcBqyzvUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ENvd7/btqCpResbKH/rBJ4klMz4w3g9kcBqyzvUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FENvd7%2FbtqCpResbKH%2FrBJ4klMz4w3g9kcBqyzvUk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;.&lt;a href=&quot;https://mongoosejs.com/docs/index.html&quot;&gt;https://mongoosejs.com/docs/index.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1582978041208&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Mongoose v5.9.2: Getting Started&quot; data-og-description=&quot;Getting Started First be sure you have MongoDB and Node.js installed. Next install Mongoose from the command line using npm: $ npm install mongoose Now say we like fuzzy kittens and want to record every kitten we ever meet in MongoDB. The first thing we ne&quot; data-og-host=&quot;mongoosejs.com&quot; data-og-source-url=&quot;https://mongoosejs.com/docs/index.html&quot; data-og-url=&quot;https://mongoosejs.com/docs/index.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://mongoosejs.com/docs/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mongoosejs.com/docs/index.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Mongoose v5.9.2: Getting Started&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Getting Started First be sure you have MongoDB and Node.js installed. Next install Mongoose from the command line using npm: $ npm install mongoose Now say we like fuzzy kittens and want to record every kitten we ever meet in MongoDB. The first thing we ne&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;mongoosejs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/339</guid>
      <comments>https://andwinter.tistory.com/339#entry339comment</comments>
      <pubDate>Sat, 29 Feb 2020 19:26:26 +0900</pubDate>
    </item>
    <item>
      <title>[string]문자열 정렬(split, sort, reverse)</title>
      <link>https://andwinter.tistory.com/338</link>
      <description>&lt;p&gt;1. split()&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://andwinter.tistory.com/337&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2020/02/26 - [Study/script.js] - [string] split - 문자열 분할&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1582724196640&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[string] split - 문자열 분할&quot; data-og-description=&quot;문자열 분할 메소드 예시 const splitString = (e) =&amp;gt; { const split = e.split(' ') return split } console.log(splitString('가나 다라 마바 사')) // [&amp;quot;가나&amp;quot;, &amp;quot;다라&amp;quot;, &amp;quot;마바&amp;quot;, &amp;quot;사&amp;quot;] split의 인자로 어느 기..&quot; data-og-host=&quot;andwinter.tistory.com&quot; data-og-source-url=&quot;https://andwinter.tistory.com/337&quot; data-og-url=&quot;https://andwinter.tistory.com/337&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cOb3Wg/hyE5lv5DLk/zfKnKk4hFTqdZA9M0hgYCK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bw6wPR/hyE5e4Nwxu/11cMtV4MoXiEtWr8eXZ4yK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://andwinter.tistory.com/337&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://andwinter.tistory.com/337&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cOb3Wg/hyE5lv5DLk/zfKnKk4hFTqdZA9M0hgYCK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bw6wPR/hyE5e4Nwxu/11cMtV4MoXiEtWr8eXZ4yK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[string] split - 문자열 분할&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;문자열 분할 메소드 예시 const splitString = (e) =&amp;gt; { const split = e.split(' ') return split } console.log(splitString('가나 다라 마바 사')) // [&quot;가나&quot;, &quot;다라&quot;, &quot;마바&quot;, &quot;사&quot;] split의 인자로 어느 기..&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;andwinter.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2.sort()&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배열의 정렬이 가능하다.&lt;/p&gt;
&lt;p&gt;기본 오름차순 정렬인데, 아래처럼 쓸 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1582724303257&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = [&quot;c&quot;, &quot;d&quot;, &quot;a&quot;]

console.log(a.sort())

//[&quot;a&quot;,&quot;c&quot;,&quot;d&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3. reverse()&lt;/p&gt;
&lt;p&gt;배열 뒤집기.&lt;/p&gt;
&lt;p&gt;위의 정렬된 배열에 reverse를 쓰면&lt;/p&gt;
&lt;pre id=&quot;code_1582724398270&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = [&quot;c&quot;, &quot;d&quot;, &quot;a&quot;]

console.log(a.sort().reverse())

//[&quot;d&quot;, &quot;c&quot;, &quot;a&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 반전된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;예제&lt;/p&gt;
&lt;p&gt;입력되는 문자열을 내림차순으로 정렬&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582724717747&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arrSort = s =&amp;gt; {
   let a = s.split('').sort().reverse().reduce((a,b) =&amp;gt; a+b)
   console.log(a)
}

arrSort(&quot;ceasgwZ&quot;)



// &quot;wsgecaZ&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1. 들어온 문자열을 쪼개어 배열로 만든다.(split)&lt;/p&gt;
&lt;p&gt;2. 오름차순으로 정렬한다.(sort)&lt;/p&gt;
&lt;p&gt;3. 반전시킨다(reverse)&lt;/p&gt;
&lt;p&gt;4. 문자 하나하나 들어가있는 배열을 합친다.(reduce)&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/338</guid>
      <comments>https://andwinter.tistory.com/338#entry338comment</comments>
      <pubDate>Wed, 26 Feb 2020 22:47:00 +0900</pubDate>
    </item>
    <item>
      <title>[string] split - 문자열 분할</title>
      <link>https://andwinter.tistory.com/337</link>
      <description>&lt;p&gt;문자열 분할 메소드&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예시&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582712689104&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const splitString = (e) =&amp;gt; {
  const split = e.split(' ')
  return split
}

console.log(splitString('가나 다라 마바 사'))

// [&quot;가나&quot;, &quot;다라&quot;, &quot;마바&quot;, &quot;사&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;split의 인자로 어느 기준을 가지고 분할 할 것인지(위에서는 공백=스페이스바)를 첫번째로 받고&lt;/p&gt;
&lt;p&gt;두번째 인자로는 분할 개수를 정한다.&lt;/p&gt;
&lt;p&gt;만약 위 코드를 두번째 인자를 주어 아래처럼 변경하면&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582712801072&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const splitString = (e) =&amp;gt; {
  const split = e.split(' ', 2)
  return split
}

console.log(splitString('가나 다라 마바 사'))

// [&quot;가나&quot;, &quot;다라&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와같은 결과가 나온다.&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/337</guid>
      <comments>https://andwinter.tistory.com/337#entry337comment</comments>
      <pubDate>Wed, 26 Feb 2020 19:26:51 +0900</pubDate>
    </item>
    <item>
      <title>[배열] 배열과 배열을 비교하여 한쪽에 없는값 찾기.</title>
      <link>https://andwinter.tistory.com/336</link>
      <description>&lt;pre id=&quot;code_1582713136858&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arrFunction = (participant, completion) =&amp;gt; {
  var arr = [];
  
  participant.forEach(element =&amp;gt; {
  let name = completion.indexOf(element);
  if(name === -1){
  arr.push(element);
  }
  });
  
  return arr;
  }

  console.log(arrFunction([&quot;ee&quot;, &quot;aa&quot;, &quot;cc&quot;], [&quot;ee&quot;]))
  
  //결과 [&quot;aa&quot;, &quot;cc&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배열 completion 을 전체배열 participant 에 비교하여 가지고 있지 않은 배열만을&lt;/p&gt;
&lt;p&gt;반환하는 문제이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우선 전체배열에서 값을 순서대로 빼와서(forEach) 그 값이 completion 에 있는지&lt;/p&gt;
&lt;p&gt;indexOf 로 검사한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 대상이 없다면 -1 을 반환할 것이고 -1이 반환된 element는 answer 배열에 push 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Algorithmus</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/336</guid>
      <comments>https://andwinter.tistory.com/336#entry336comment</comments>
      <pubDate>Wed, 26 Feb 2020 19:11:30 +0900</pubDate>
    </item>
    <item>
      <title>[express] View - PUG</title>
      <link>https://andwinter.tistory.com/335</link>
      <description>&lt;p&gt;express 로 서버를 만들고 해당 주소로&lt;/p&gt;
&lt;p&gt;request 가 들어왔을때 response 를 주는 방법은 저번 글에서도 작성했듯&lt;/p&gt;
&lt;p&gt;기본형은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1582555022241&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get(&quot;/join&quot;, (req, res) =&amp;gt; res.send(&quot;join&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 응답은 단순히 화면에 join 이라는 문구만 출력되게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리가 어떠한 요청이 들어와서 특정 화면을 그리고싶을때,&lt;/p&gt;
&lt;p&gt;express 에서는 템플릿엔진을 설치하여&lt;/p&gt;
&lt;p&gt;렌더링을 할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://expressjs.com/ko/guide/using-template-engines.html&quot;&gt;http://expressjs.com/ko/guide/using-template-engines.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1582555376246&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Express와 함께 템플리트 엔진 사용&quot; data-og-description=&quot;Express와 함께 템플리트 엔진을 사용 Express가 템플리트를 렌더링하려면 다음과 같은 애플리케이션 설정이 필요합니다. views, 템플리트가 있는 디렉토리. 예: app.set('views', './views') view engine, 사용할 템플리트 엔진. 예: app.set('view engine', 'pug') 이후 그에 맞는 템플리트 엔진 npm 패키지를 다음과 같이 설치하십시오. $ npm install pug --save Express&quot; data-og-host=&quot;expressjs.com&quot; data-og-source-url=&quot;http://expressjs.com/ko/guide/using-template-engines.html&quot; data-og-url=&quot;http://expressjs.com/ko/guide/using-template-engines.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bSvjkJ/hyE3PjzOAT/edboHYHhsRgTH5hhmuWwJ1/img.png?width=435&amp;amp;height=157&amp;amp;face=0_0_435_157&quot;&gt;&lt;a href=&quot;http://expressjs.com/ko/guide/using-template-engines.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://expressjs.com/ko/guide/using-template-engines.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bSvjkJ/hyE3PjzOAT/edboHYHhsRgTH5hhmuWwJ1/img.png?width=435&amp;amp;height=157&amp;amp;face=0_0_435_157');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Express와 함께 템플리트 엔진 사용&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Express와 함께 템플리트 엔진을 사용 Express가 템플리트를 렌더링하려면 다음과 같은 애플리케이션 설정이 필요합니다. views, 템플리트가 있는 디렉토리. 예: app.set('views', './views') view engine, 사용할 템플리트 엔진. 예: app.set('view engine', 'pug') 이후 그에 맞는 템플리트 엔진 npm 패키지를 다음과 같이 설치하십시오. $ npm install pug --save Express&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;expressjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;npm i pug 로 pug 를 설치해 준 뒤,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582555413284&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.set('view engine', 'pug');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 명령을 작성하여 모듈을 불러온다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이다음 기본적으로 view engine 은 views 폴더를 기본 경로로 인식하기때문에&lt;/p&gt;
&lt;p&gt;package.json 이 있는 경로에 views 폴더를 생성후 안쪽에 pug 문서를 작성한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 요청에대한 응답으로 해당템플릿을 렌더하려면&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582555660802&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get(&quot;/join&quot;, (req, res) =&amp;gt; res.render(&quot;join&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 작성하면 된다. 안쪽에 join은 알아서 views 폴더안에서&lt;/p&gt;
&lt;p&gt;동일한 이름의 템플릿을 찾아서 렌더링 해주게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/335</guid>
      <comments>https://andwinter.tistory.com/335#entry335comment</comments>
      <pubDate>Mon, 24 Feb 2020 23:52:40 +0900</pubDate>
    </item>
    <item>
      <title>[express] router</title>
      <link>https://andwinter.tistory.com/334</link>
      <description>&lt;p&gt;express router&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582297391373&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//index.js

import express from &quot;express&quot;;

app.listen(3000);

app.get(&quot;/&quot;, (req, res) =&amp;gt; res.send(&quot;Hi from home&quot;));

// http://localhost:3000 으로 GET 되면 response 로 Hi from home 메세지 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;조각조각 분할하여&lt;/p&gt;
&lt;p&gt;route, router, controller 로 나눌 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582299147099&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// routes.js

const HOME = &quot;/&quot;

const routes = {
home:HOME
}

export default routes&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1582299349112&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// controller.js

export const home = (req, res) =&amp;gt; res.send(&quot;Hi from home&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1582299278716&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// homeRouter.js

import express from &quot;express&quot;
import routes from &quot;routes&quot;
import { home }  from &quot;controller&quot;

const homeRouter = express.Router();

homeRouter.get(routes.home, home)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1582299548571&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//index.js

import express from &quot;express&quot;;
import routes from &quot;routes&quot;;
import homeRouter from &quot;homeRouter&quot;;

app.listen(3000);

app.use(routes.home, homeRouter);
//router 를 쓸때는 app.use 로 사용함
//여기서 homeRouter 로 연결되고 라우터에서 컨트롤러로 연결&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/334</guid>
      <comments>https://andwinter.tistory.com/334#entry334comment</comments>
      <pubDate>Sat, 22 Feb 2020 00:40:44 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] 한글 깨짐문제</title>
      <link>https://andwinter.tistory.com/333</link>
      <description>&lt;h3&gt;&lt;span&gt;&lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #333333;&quot;&gt;문제&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;평범하게 문서를 작성할때&lt;/p&gt;
&lt;p&gt;meta 태그로 UTF-8 을 설정하면&lt;/p&gt;
&lt;p&gt;한글이 깨지지 않고 잘 나온다. 다만,&lt;/p&gt;
&lt;p&gt;이번에 겪은 문제는 현재 문서의 주소를 가져와서&lt;/p&gt;
&lt;p&gt;콘솔에 찍으려 했을때 한글이 깨지는 문제였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://localhost:3000/category#%EB%B7%B0%ED%8B%B0&quot;&gt;http://localhost:3000/category#뷰티&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와같은 주소였고 나는 해쉬가 붙은 저 뷰티라는 부분을&lt;/p&gt;
&lt;p&gt;추출하고 싶었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해서 작성한 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1581689080167&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    const address = document.location.hash;
    console.log(address)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;하지만 이렇게 하고 실행해보면&lt;/p&gt;
&lt;p&gt;한글이 깨진다. ( &lt;span style=&quot;color: #333333;&quot;&gt;#%EB%B7%B0%ED%8B%B0 ) 이렇게.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #333333;&quot;&gt;해결&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이때 아래와 같이 변환해주면 정상적으로 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1581689211881&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(decodeURI(address))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/i&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;escape / unescape&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;encodeURI / decodeURI&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;encodeURIComponent / decodeURIComponent&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/333</guid>
      <comments>https://andwinter.tistory.com/333#entry333comment</comments>
      <pubDate>Fri, 14 Feb 2020 23:08:04 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트]indexOf, findIndex</title>
      <link>https://andwinter.tistory.com/332</link>
      <description>&lt;p&gt;1. indexOf&lt;/p&gt;
&lt;pre id=&quot;code_1581176081563&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let e = [&quot;a&quot;, &quot;b&quot;, &quot;b&quot;, &quot;b&quot;, &quot;c&quot;]
e.indexOf(&quot;b&quot;)
// 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;일반 배열안에서 값 찾아서 인덱스 번호 반환&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. findIndex&lt;/p&gt;
&lt;pre id=&quot;code_1581176095841&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let Arr = [{name:&quot;aa&quot;, id:4},{name:&quot;aa&quot;, id:2},{name:&quot;aa&quot;, id:3},{name:&quot;aa&quot;, id:4}]
Arr.findIndex(i =&amp;gt; i.id == 2)
//1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;배열안의 객체에서 특정값을 조회하여&lt;/p&gt;
&lt;p&gt;인덱스 번호를 반환&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/332</guid>
      <comments>https://andwinter.tistory.com/332#entry332comment</comments>
      <pubDate>Sun, 9 Feb 2020 00:35:01 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] map</title>
      <link>https://andwinter.tistory.com/330</link>
      <description>&lt;p&gt;배열에 담긴놈들을 하나하나 꺼내어 어떠한 작업을 한 뒤 새로운 배열을 반환한다.&lt;/p&gt;
&lt;p&gt;여기서 하나하나 꺼내는기능을 활용할 수 도 있고 실제로 새로운 배열이 필요할때도 쓴다.&lt;/p&gt;
&lt;p&gt;하나하나 꺼내서 쓰는경우는 리액트에서 많이 썼으므로 따로 언급하지 않고,&lt;/p&gt;
&lt;p&gt;새로운 배열을 반환한다는 뜻은&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1580738327317&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let Arr = [{name:&quot;aa&quot;, id:1},{name:&quot;aa&quot;, id:2},{name:&quot;aa&quot;, id:3},{name:&quot;aa&quot;, id:4}]

const newArr = Arr.map((element)=&amp;gt;(element.id))
//newArr 은 [1, 2, 3, 4] 이런 모습의 배열이 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런것이다.&lt;/p&gt;
&lt;p&gt;map으로 각각 객체들을 돌며 그놈들의 id값만 빼내어 새로운 배열을 만들었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위를 보면 알 수 있듯이 새로운 배열을 만들어 냈을뿐, 기존 배열을 건드리진 않는다.&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/330</guid>
      <comments>https://andwinter.tistory.com/330#entry330comment</comments>
      <pubDate>Mon, 3 Feb 2020 23:00:02 +0900</pubDate>
    </item>
    <item>
      <title>[react]조건부 렌더링</title>
      <link>https://andwinter.tistory.com/329</link>
      <description>&lt;p&gt;리턴 부분에&lt;/p&gt;
&lt;pre id=&quot;code_1580366532084&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return(
	{state
		?(
			&amp;lt;&amp;gt;
			&amp;lt;div&amp;gt;true&amp;lt;/div&amp;gt;
			&amp;lt;/&amp;gt;
		)
		:(
			&amp;lt;&amp;gt;
			&amp;lt;div&amp;gt;false&amp;lt;/div&amp;gt;
			&amp;lt;/&amp;gt;
        )
     }
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 삼항연산자를 써서 state 값이 true 냐 false 이냐에 따라서 렌더링하는 방법도 있고&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1580366609687&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const rendering = (state) =&amp;gt; {
    switch(state){
      case 0:{
          return(
            &amp;lt;Text size=&quot;tiny&quot; message style={{ marginTop: &quot;-10px&quot; }}&amp;gt;
                추가할 상품이 존재하지 않습니다.
              &amp;lt;/Text&amp;gt;
          )
      }
      case 1:{
        return(
              &amp;lt;div
                style={{
                  position: &quot;relative&quot;,
                  display: &quot;flex&quot;,
                  flexDirection: &quot;column&quot;,
                  alignItems: &quot;center&quot;
                }}
              &amp;gt;
                &amp;lt;Text size=&quot;tiny&quot; message&amp;gt;
                  상품이 장바구니에 추가되었습니다.
                &amp;lt;/Text&amp;gt;
                &amp;lt;Button subButton&amp;gt;장바구니 바로가기&amp;lt;/Button&amp;gt;
              &amp;lt;/div&amp;gt;
        )
      }
      case 3:{
        return(
          &amp;lt;Text size=&quot;tiny&quot; message style={{ marginTop: &quot;-10px&quot; }}&amp;gt;
              이미 장바구니에 담긴 상품입니다.
            &amp;lt;/Text&amp;gt;
        )
    }
    }
  }
  
  
  -----------------------------
  
  
  return({rendering(state)})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 분리해주는 방법도 있슴.&lt;/p&gt;</description>
      <category>Study/react.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/329</guid>
      <comments>https://andwinter.tistory.com/329#entry329comment</comments>
      <pubDate>Thu, 30 Jan 2020 15:44:20 +0900</pubDate>
    </item>
    <item>
      <title>[배열, react state]배열안에 객체 중복 파악하기</title>
      <link>https://andwinter.tistory.com/328</link>
      <description>&lt;p&gt;장바구니 담기 기능을 구현하던중&lt;/p&gt;
&lt;p&gt;해당 상품이 이미 장바구니에 담겨있는가를 파악해야 했다.&lt;/p&gt;
&lt;pre id=&quot;code_1580235130500&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;장바구니에담길배열.map(element =&amp;gt; {
const id = element.item;
const value = element.value;
비교대상배열.map(elem =&amp;gt; {
if(elem.item === id &amp;amp;&amp;amp; elem.value === value){
return(console.log(elem, &quot;해당상품이 이미담겨있습니다&quot;))
}else{
return(console.log(&quot;unoverLap&quot;))
}})
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;장바구니 담기를 누르면 해당 상품들이 장바구니에 담길 배열에 들어간체로&lt;/p&gt;
&lt;p&gt;비교대상배열과 비교가 이루어져 중복된 객체만을 뽑아낸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 상태에서 문제가 있었다. 뽑아내긴 하는데 리턴부분에 스프레드 연산자로&amp;nbsp;&lt;/p&gt;
&lt;p&gt;상태값을 변경시키는놈을 넣으니 작동이 이상하게 되는것&lt;/p&gt;
&lt;p&gt;(각각 비교해보다가 처음엔 잘 거르고 다음번에 다시 추가를 시키는둥 아무튼 이상함)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1580364726758&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const saveOptions = value =&amp;gt; {
    value.map(element =&amp;gt; {
      const id = element.itemid;
      const valued = element.value;
      let AA = cartValue.findIndex(i =&amp;gt; i.itemid === id &amp;amp;&amp;amp; i.value === valued);
      if (AA === -1) {
        setCartValue([...cartValue, element]);
      }else{
        setState(3)
      }
    });
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그래서 다음번 수정한 코드는 이렇다.&lt;/p&gt;
&lt;p&gt;map으로 장바구니에 추가될 옵션들을 하나씩 뽑아와서 기존 장바구니에 담긴 배열에 있는 객체들과 비교하여 이미 값이 중복되어있다면 거르고(setState(3)) 중복되어있지 않다면 findIndex 가 -1 을 반환해서 if의 true 부분이 실행되게 한것.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 또 문제가 있었다. 하나씩 각각 추가하면 정상적으로 작동하지만, 인자로 들어오는 추가할 옵션(value)이 1개가 아닌경우에는 인자 배열 안의 맨 마지막놈만 추가가 되는것.&lt;/p&gt;
&lt;p&gt;그래서 고민하다가 질문글을 올려보았는데 간단한 문제였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;setCartValue가 비동기적이라 첫번째 map을 돌아서 변경된 상태로 생각해야하는것이 아닌 초기상태 그대로이기 때문..&lt;/p&gt;
&lt;p&gt;그래서 마지막 구문을 돌때만 호출이 되고 마지막놈만 추가가 된 것이었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1580365652620&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const saveOptions = value =&amp;gt; {
    value.map(element =&amp;gt; {
      const id = element.itemid;
      const valued = element.value;
      let AA = cartValue.findIndex(i =&amp;gt; i.itemid === id &amp;amp;&amp;amp; i.value === valued);
      if (AA === -1) {
        setCartValue(cartValue =&amp;gt; [...cartValue, element]);
      }else{
        setState(3)
      }
    });
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그래서 이렇게 인자를 받게 만들어놓으면 이전 state를 인자로 받아서 처리하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;다른 방법&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;map으로 돌며 setState 를 하는 방법은 좋은 방법이 아니라고 한다.&lt;/p&gt;
&lt;p&gt;의도치 못한 결과를 만들어 낼 수 있다고..&lt;/p&gt;
&lt;p&gt;map보다는 forEach를 사용하고 빈 배열을 선언한뒤&lt;/p&gt;
&lt;p&gt;배열안에 push를 하고 반복문이 종료된후 배열을 setState에 넣는방법을&lt;/p&gt;
&lt;p&gt;쓰라고 하셨다.&lt;/p&gt;
&lt;pre id=&quot;code_1580554950890&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const saveOptions = value =&amp;gt; {
    const newCartArr = []
     value.forEach( element =&amp;gt; {
      const id = element.itemid;
      const valued = element.value;
      let ExistenceStatus = customerOrder.findIndex(i =&amp;gt; i.itemid === id &amp;amp;&amp;amp; i.value === valued);
       if (ExistenceStatus === -1) {
        newCartArr.push(element)
        console.log(&quot;장바구니 추가 실행&quot;);
      } else {
        setCartState(3);
      }
    });
    handleUpdateCart(newCartArr)
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 만들었다. 잘되고 이상한 문제들도 다 해결되었다.&lt;/p&gt;
&lt;p&gt;굳굳&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/328</guid>
      <comments>https://andwinter.tistory.com/328#entry328comment</comments>
      <pubDate>Wed, 29 Jan 2020 03:13:01 +0900</pubDate>
    </item>
    <item>
      <title>[context API]기본</title>
      <link>https://andwinter.tistory.com/327</link>
      <description>&lt;p&gt;리덕스와 마찬가지로 글로벌하게 상태관리가 가능하다.&lt;/p&gt;
&lt;p&gt;다만 따로 설치하지 않아도 리액트에서 기본으로&lt;/p&gt;
&lt;p&gt;지원하는 API기 때문에 호출해서 쓰기만 하면된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사용법&lt;/p&gt;
&lt;p&gt;예제) 인풋과 텍스트는 형제 컴포넌트이지만 context api를 이용하여&lt;/p&gt;
&lt;p&gt;인풋에서 작성한 값을 텍스트가 받아 폰트 크기를 변화시킴.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 먼저 글로벌하게 관리할 state를 모아놓을 provider(store)를 만든다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;application_context.js&lt;/p&gt;
&lt;pre id=&quot;code_1580210860825&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import { createContext, useContext, useState } from 'react';

const Context = createContext(); //createContext 선언

export function ApplicationContextProvider({ children, font: init }) {//관리대상들을 감쌀놈
	const [font, setFont] = useState(init); //글로벌하게 관리할 state

	const value = {
		font,
		setFont
	};

	return &amp;lt;Context.Provider value={value}&amp;gt;{children}&amp;lt;/Context.Provider&amp;gt;;
    //children 자리에 감싸질 컴포넌트가 들어올 것
}
export function useApplicationContext() {//형제 컴포넌트에서 context 를 사용할때 쓸놈
	return useContext(Context);
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;app.js&lt;/p&gt;
&lt;pre id=&quot;code_1580211479786&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import Bro from './components/bro';
import Brotwo from './components/brotwo';
import { ApplicationContextProvider } from './application_context'; //provider 를 불러옴
function App() {
	return (
		&amp;lt;ApplicationContextProvider font={20}&amp;gt; //형제들을 감싸줌
			&amp;lt;Bro /&amp;gt;
			&amp;lt;Brotwo /&amp;gt;
		&amp;lt;/ApplicationContextProvider&amp;gt;
	);
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와같이 아까 만든 provider로 글로벌한 상태값이 필요한 컴포넌트를 감싸준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;bro.js&lt;/p&gt;
&lt;pre id=&quot;code_1580211538931&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import Child from './childBro';

function View() {
	return (
		&amp;lt;div&amp;gt;
			&amp;lt;Child /&amp;gt;
		&amp;lt;/div&amp;gt;
	);
}

export default View;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;조금더 꼬아보려고 bro 안에 child 라는 컴포넌트를 넣었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;childbro.js&lt;/p&gt;
&lt;p&gt;(text)&lt;/p&gt;
&lt;pre id=&quot;code_1580211555446&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import { useApplicationContext } from '../application_context';

function Child() {
	const { font } = useApplicationContext();

	return &amp;lt;div style={{ fontSize: `${font}px` }}&amp;gt;Child..&amp;lt;/div&amp;gt;;
}

export default Child;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;그리고 그 안에 child 에선 위와같이 useApplicationContext 를 불러와서 그안의 state 를 가져와서 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;brotwo.js&lt;/p&gt;
&lt;p&gt;&amp;nbsp;(input)&lt;/p&gt;
&lt;pre id=&quot;code_1580211570846&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import { useApplicationContext } from '../application_context';

function Input() {
	const { setFont } = useApplicationContext();

	const handleChange = ({ target: { value } }) =&amp;gt; {
		setFont(value);
	};

	return &amp;lt;input placeholder=&quot;fontSize&quot; onChange={handleChange} /&amp;gt;;
}

export default Input;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;마찬가지로 store에 있는 state 중 setFont 를 불러와서 인풋에서 적히는 값이 state에 전달될 수 있게 만들어준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런식이면 형제간의 state 공유가 가능해 진다.&lt;/p&gt;</description>
      <category>Study/react.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/327</guid>
      <comments>https://andwinter.tistory.com/327#entry327comment</comments>
      <pubDate>Tue, 28 Jan 2020 20:42:26 +0900</pubDate>
    </item>
    <item>
      <title>[warning]Use the `defaultValue` or `value` props on &amp;lt;select&amp;gt; instead of setting `selected` on &amp;lt;option&amp;gt;.</title>
      <link>https://andwinter.tistory.com/326</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1580108425376&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;select
      onChange={handleChange}
      name=&quot;selectBox&quot;
      aria-label=&quot;옵션선택&quot;
      style={{
        border: &quot;1px solid #eee&quot;,
        width: &quot;100%&quot;,
        height: &quot;50px&quot;,
        color: &quot;#ccc&quot;,
        marginBottom: &quot;15px&quot;,
        padding: &quot;16px&quot;
      }}
    &amp;gt;
      &amp;lt;option selected disabled style={{ color: &quot;#ccc&quot; }}&amp;gt;
        ---옵션선택---
      &amp;lt;/option&amp;gt;
      {(optionValue || []).map((options, idx) =&amp;gt; (
        &amp;lt;option key={idx} value={options} style={{ color: &quot;#000&quot; }}&amp;gt;
          {options}
        &amp;lt;/option&amp;gt;
      ))}
    &amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와같이 selected 를 사용했을시 나타나는 경고이다.&lt;/p&gt;
&lt;p&gt;react에서는 selected 를 사용하는 대신 최상위 select 에 defaultValue 를 지정하여&lt;/p&gt;
&lt;p&gt;그놈을 맨처음 보이게 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1580108549086&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;select
      defaultValue=&quot;default&quot;
      onChange={handleChange}
      name=&quot;selectBox&quot;
      aria-label=&quot;옵션선택&quot;
      style={{
        border: &quot;1px solid #eee&quot;,
        width: &quot;100%&quot;,
        height: &quot;50px&quot;,
        color: &quot;#ccc&quot;,
        marginBottom: &quot;15px&quot;,
        padding: &quot;16px&quot;
      }}
    &amp;gt;
      &amp;lt;option value=&quot;default&quot; disabled style={{ color: &quot;#ccc&quot; }}&amp;gt;
        ---옵션선택---
      &amp;lt;/option&amp;gt;
      {(optionValue || []).map((options, idx) =&amp;gt; (
        &amp;lt;option key={idx} value={options} style={{ color: &quot;#000&quot; }}&amp;gt;
          {options}
        &amp;lt;/option&amp;gt;
      ))}
    &amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요런식으로. disabled 는 선택 안되게 막아주려고 넣음&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/forms.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.reactjs.org/docs/forms.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>Study/react.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/326</guid>
      <comments>https://andwinter.tistory.com/326#entry326comment</comments>
      <pubDate>Mon, 27 Jan 2020 16:02:44 +0900</pubDate>
    </item>
    <item>
      <title>[warning]Nested block is redundant  no-lone-blocks</title>
      <link>https://andwinter.tistory.com/325</link>
      <description>&lt;pre id=&quot;code_1580107565254&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const handleCartValue = () =&amp;gt; {
      {selectOption[0] ? setState(true) : setState(false)}
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 과정에서 뜨는 문법 오류(경고)&lt;/p&gt;
&lt;p&gt;정상 작동은하지만 짜증나게 노랗게 떠있으니 여간 거슬리는게 아니다.&lt;/p&gt;
&lt;p&gt;esLint 쓰면 자동으로 교정해주니 좋지만&lt;/p&gt;
&lt;p&gt;이 경우의 오류는 본질적으로 뭘 고치길 원하는 것일까?&lt;/p&gt;
&lt;p&gt;그냥 써있는 그대로다. 중첩되어있다는것이니까 풀어주면 사라진다.&lt;/p&gt;
&lt;pre id=&quot;code_1580107708511&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	const handleCartValue = () =&amp;gt; {
		selectOption[0] ? setState(true) : setState(false);
	};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;이런식이나&lt;/p&gt;
&lt;pre id=&quot;code_1580107828705&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const handleCartValue = () =&amp;gt; selectOption[0] ? setState(true) : setState(false)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;이런식으로.&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/325</guid>
      <comments>https://andwinter.tistory.com/325#entry325comment</comments>
      <pubDate>Mon, 27 Jan 2020 15:50:14 +0900</pubDate>
    </item>
    <item>
      <title>[노드패키지매니져]npm 과 npx 의 차이점</title>
      <link>https://andwinter.tistory.com/324</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;NPM(Node Package Manager)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;Node.js 에서 사용할 수 있는 모듈을 패키지화 하여 모아둔 저장소같은 개념.&lt;/p&gt;
&lt;p&gt;개인이 자신이 만든 패키지를 공개할 수 있으며, 사용또한 쉽다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;보통 npm install -g create-react-app myreact&lt;/p&gt;
&lt;p&gt;이런식으로 쓴다. install 대신 i 라고 써도 무방, 편한대로 쓰면 된다.&lt;/p&gt;
&lt;p&gt;이렇게 리액트를 npm을 통해 설치하게 되면 온갖것들을 다 끌고와서 react를 설치하게 되고, 그놈들은 계속 컴퓨터에 남아있는 상태가 된다. 또한 버전의 업데이트가 이루어졌을시, 스스로 삭제후 재설치를 해주어야 하는 번거로움이 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이때 사용할 수 있는것이&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;NPX(&lt;span&gt;Npm Package Runner)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;이놈을 이용해 create-react-app을 설치할 경우, 무거운 CRA 패키지를 설치하는동안 받아 쓴 후 다시 삭제해준다. 또한 항상 최신의 버전을 이용할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://helloinyong.tistory.com/177&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://helloinyong.tistory.com/177&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Study/react.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/324</guid>
      <comments>https://andwinter.tistory.com/324#entry324comment</comments>
      <pubDate>Mon, 27 Jan 2020 15:36:44 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 es6]구조분해할당(Destructuring)</title>
      <link>https://andwinter.tistory.com/322</link>
      <description>&lt;p&gt;객체나 배열을 풀어 쓸 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배열&lt;/p&gt;
&lt;pre id=&quot;code_1579018642391&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 배열을 각각의 변수에 담아야 한다고 치자.&lt;/p&gt;
&lt;p&gt;원래의 방법이라면&lt;/p&gt;
&lt;pre id=&quot;code_1579018756546&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;];

const first = arr[0];
const second = arr[1];
const third  = arr[2];
const fourth = arr[3];

console.log(first, second, third, fourth)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 써야 하지만&lt;/p&gt;
&lt;p&gt;구조분해할당을 쓰게되면&lt;/p&gt;
&lt;pre id=&quot;code_1579018821773&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;];
const [first, second, third, fourth] = arr

console.log(first, second, third, fourth)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 방법으로 써줄 수 있다.&lt;/p&gt;
&lt;p&gt;왼쪽부터 차례대로 배열의 순서대로 할당된다.&lt;/p&gt;
&lt;pre id=&quot;code_1579019498137&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a, b, c
[a, b, c] = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;];

console.log(a, b, c)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식의 할당도 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;객체&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1579021148480&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const OBJ = {name:&quot;kim&quot;, age:23}

const name = OBJ.name
const age = OBJ.age

console.log(name, age)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기존의 객체에서 값을 빼와서 변수에 담으려면&lt;/p&gt;
&lt;p&gt;이렇게 했지만&lt;/p&gt;
&lt;p&gt;구조분해 할당을 이용하면&lt;/p&gt;
&lt;pre id=&quot;code_1579021259825&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const OBJ = {name:&quot;kim&quot;, age:23}

const {name, age} = OBJ

console.log(name, age)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 쓸 수 있다.&lt;/p&gt;
&lt;p&gt;배열과 다른점은&lt;/p&gt;
&lt;p&gt;변수로 지정한 2번째 열에서 객체의 순서대로 변수에 담는것이아닌,&lt;/p&gt;
&lt;p&gt;객체의 프로퍼티를 빼는 것이다.&lt;/p&gt;
&lt;p&gt;그렇기때문에&lt;/p&gt;
&lt;pre id=&quot;code_1579021392767&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const OBJ = {name:&quot;kim&quot;, age:23}

const {a, b} = OBJ

console.log(a, b)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 쓰면 undefined 를 출력하는것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;중첩된 객체의 경우에는&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1579021868263&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const event = {
  target:{
    name:&quot;kim&quot;,
    age:23
  }
}

const {target : {name}} = event

console.log(name)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 꺼내 줄 수 있다.(필요한 부분만 꺼내는것도 가능하기에 위에서 name만 꺼냈다.)&lt;/p&gt;
&lt;p&gt;만약 중첩이 여러개 겹친다면&lt;/p&gt;
&lt;pre id=&quot;code_1579022105254&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const event = {
  target:{
    name:&quot;kim&quot;,
    age:23,
   another:{
      help:{
        memo:{
          listOne:&quot;힘들다&quot;
        }
      }
    } 
  }
}

const {target : {another:{help:{memo:{listOne}}}}} = event

console.log(listOne)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 경악스러운 모습이 되지만 아무튼 이렇게도 쓸 수 있다.&lt;/p&gt;</description>
      <category>Study/script.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/322</guid>
      <comments>https://andwinter.tistory.com/322#entry322comment</comments>
      <pubDate>Wed, 15 Jan 2020 02:15:43 +0900</pubDate>
    </item>
    <item>
      <title>[react]useRef 사용</title>
      <link>https://andwinter.tistory.com/321</link>
      <description>&lt;p&gt;리액트를 사용하여 해당 돔에 직접 접근해야할때&lt;/p&gt;
&lt;p&gt;useRef 를 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;밑은 과격한 예시이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기존의 200px 검은 정사각형이 버튼을 누르면 300px 빨간색 정사각형으로 바꾸는 예제이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1578990251100&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useRef } from &quot;react&quot;; //1. useRef 를 불러온다

const styled = {
  width: &quot;200px&quot;,
  height: &quot;200px&quot;,
  background: &quot;black&quot;
};

export default function App() {
  const handleChange = () =&amp;gt; {
    colorChange.current.style = &quot;width:300px; height:300px; background:red;&quot;
    //4. 해당 ref 이름과 current 를이용해 직접 조정할 수 있다.
  };

  const colorChange = useRef(); //2. useRef 를 지정해준다.


//3. ref 를 이용해 지정한 useRef를 넣는다
  return (
    &amp;lt;&amp;gt;
      &amp;lt;div ref={colorChange} style={styled} /&amp;gt;
      &amp;lt;button onClick={handleChange}&amp;gt;클릭&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/react.js</category>
      <author>빛장</author>
      <guid isPermaLink="true">https://andwinter.tistory.com/321</guid>
      <comments>https://andwinter.tistory.com/321#entry321comment</comments>
      <pubDate>Tue, 14 Jan 2020 17:27:08 +0900</pubDate>
    </item>
  </channel>
</rss>