[TIL] 211218

Lee SyongΒ·2021λ…„ 12μ›” 18일
0

TIL

λͺ©λ‘ 보기
122/204
post-thumbnail

πŸ“ 였늘 ν•œ 것

  1. λŒ“κΈ€ μ‚­μ œν•˜κΈ° - ν”„λ‘ νŠΈμ—”λ“œμ™€ DBμ—μ„œ λͺ¨λ‘ μ‚­μ œν•˜κΈ°

  2. μ„œλ²„ & ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œ λΉŒλ“œ / μ„œλ²„ μ‹œμž‘

  3. Heroku에 μ•± λ°°ν¬ν•˜κΈ° - Heroku Git / mongoDB Atlas / Heroku에 ν™˜κ²½ λ³€μˆ˜ μΆ”κ°€

  4. μ—λŸ¬ ν•΄κ²° - PORT μ—°κ²° / github 둜그인


πŸ“š 배운 것

1. comment section

1) λŒ“κΈ€ μ‚­μ œν•˜κΈ°

μ½”λ“œ μ±Œλ¦°μ§€

πŸ’‘ ν”„λ‘ νŠΈμ—”λ“œ - fetch - apiRouter - λ°±μ—”λ“œ(컨트둀러) - ν”„λ‘ νŠΈμ—”λ“œ

(1) pug와 js νŒŒμΌμ—μ„œ μ‚­μ œ λ²„νŠΌ μΆ”κ°€

μ‚­μ œ λ²„νŠΌμ€ λŒ“κΈ€ μž‘μ„±μžλ§Œ λ³Ό 수 μžˆμ–΄μ•Ό ν•œλ‹€.

//- watch.pug
div.video-comments
  ul
    each comment in video.comments.reverse()
      li.video-comment
        i.fas.fa-comment
        span  #{comment.text}
        if String(comment.owner._id) === loggedInUser._id
          span  ❌
// commentSection.js
const addComment = (text) => {
  const videoComments = document.querySelector(".video-comments ul");
  const newComment = document.createElement("li");
  newComment.className = "video-comment";
  const icon = document.createElement("i");
  icon.className = "fas fa-comment";
  const textSpan = document.createElement("span");
  textSpan.textContent = ` ${text}`;
  const delSpan = document.createElement("span");
  delSpan.textContent = " ❌";
  newComment.appendChild(icon);
  newComment.appendChild(textSpan);
  newComment.appendChild(delSpan);
  videoComments.prepend(newComment);
};

(2) apiRouter / fetch

fetchλ₯Ό μ΄μš©ν•΄ μš”μ²­μ„ 보낼 routeλ₯Ό λ§Œλ“ λ‹€.
delete μš”μ²­μ„ 보낼 것이기 λ•Œλ¬Έμ— routerμ—μ„œλ„ deleteλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

// apiRouter.js
import { deleteComment } from "../controllers/videoController";

apiRouter.delete("/comments/:id([0-9a-f]{24})", deleteComment);

이벀트 μœ„μž„μ„ μ΄μš©ν•΄ μ‚­μ œ λ²„νŠΌ 클릭 μ‹œ λŒ“κΈ€μ΄ μ‚­μ œλ˜λ„λ‘ ν–ˆλ‹€.
μ‚­μ œ λ²„νŠΌκ³Ό λŒ“κΈ€μ„ λ¬ΆκΈ° μœ„ν•΄ data 속성을 μ΄μš©ν–ˆλ‹€.

watch.pug νŒŒμΌμ—μ„œ μ‚­μ œ λ²„νŠΌκ³Ό λŒ“κΈ€μ— 같은 data-comment-idλ₯Ό μΆ”κ°€ν–ˆλ‹€.

//- watch.pug
div.video-comments
  ul
    each comment in video.comments.reverse()
      li.video-comment(data-comment-id=comment._id)
        i.fas.fa-comment
        span  #{comment.text}
        if String(comment.owner._id) === loggedInUser._id
          button.delBtn(data-comment-id=comment._id)  ❌

μ΄λ•Œ data μ†μ„±μ˜ 이름은 HTMLμ—μ„œλŠ” -(ν•˜μ΄ν”ˆ)을 μ‚¬μš©ν•˜μ§€λ§Œ JSμ—μ„œλŠ” 카멜 ν‘œκΈ°λ²•μœΌλ‘œ λ°”κΏ” μž‘μ„±ν•΄μ•Ό ν•œλ‹€.
ex) data-comment-id β†’ data-commentId

// commentSection.js
const commentsContainer = document.querySelector(".video-comments");

const handleCommentsContainerClick = async (event) => {
  const commentId = event.target.dataset.commentId;
  if (commentId && event.target.className === "delBtn") {
    const response = await fetch(`/api/comments/${commentId}`, {
      method: "DELETE",
    });
    if (response.status === 200) {
      // λ°±μ—”λ“œμ—μ„œ 처리λ₯Ό 마치고(DBμ—μ„œ λŒ“κΈ€ μ‚­μ œ) 200 응닡을 λ°›μœΌλ©΄ ν”„λ‘ νŠΈμ—”λ“œμ—μ„œλ„ λŒ“κΈ€μ„ μ‚­μ œν•œλ‹€...
    }
  }
};

commentsContainer.addEventListener("click", handleCommentsContainerClick);

(3) response.json()

DBμ—μ„œ λŒ“κΈ€μ„ μ‚­μ œν•˜λŠ” μ½”λ“œλ₯Ό κ΅¬ν˜„ν•˜κΈ° 전에 κ³ λ €ν•΄μ•Ό ν•  것 쀑 ν•˜λ‚˜λŠ” μ‹€μ‹œκ°„μœΌλ‘œ μΆ”κ°€ν•œ λŒ“κΈ€λ„ μƒˆλ‘œκ³ μΉ¨ 없이 λ°”λ‘œ μ‚­μ œν•  수 μžˆλ„λ‘ ν•΄μ•Ό ν•œλ‹€λŠ” 것이닀.

JS둜 μ‹€μ‹œκ°„μœΌλ‘œ 보이도둝 λ§Œλ“  λŒ“κΈ€μ€ data-idλ₯Ό 가지지 μ•ŠκΈ° λ•Œλ¬Έμ— μ‚­μ œ λ²„νŠΌμ„ 클릭해도 event.target.dataset.commentIdκ°€ undefined 값이 λ˜μ–΄ λŒ“κΈ€μ΄ μ‚­μ œλ˜μ§€ μ•ŠλŠ”λ‹€.

λ”°λΌμ„œ, js둜 μ‹€μ‹œκ°„μœΌλ‘œ 보이도둝 λ§Œλ“  λŒ“κΈ€λ„ comment._id 값을 가지도둝 ν•΄μ•Ό ν•œλ‹€.
즉, λ°±μ—”λ“œμ—μ„œ comment._id의 값을 ν”„λ‘ νŠΈμ—”λ“œλ‘œ 보내야 ν•œλ‹€.

[ λ°±μ—”λ“œμ—μ„œ 데이터 보내기 ]

createComment ν•¨μˆ˜μ—μ„œ λŒ“κΈ€μ„ λ§Œλ“  ν›„ μƒνƒœ μ½”λ“œ 201을 λ³΄λ‚΄λ©΄μ„œ μš”μ²­μ„ 끝낼 λ•Œ, comment._id의 값을 json() ν˜•νƒœλ‘œ ν•¨κ»˜ 보내도둝 μˆ˜μ •ν•œλ‹€.

// videoController.js
const createComent = asyn (req, res) => {
  const {
    session: { user },
    params: { id },
    body: { text },
  } = req;
  const video = await Video.findById(id);
  if (!video) {
    return res.sendStatus(404);
  }
  const comment = await Comment.create({
    text,
    owner: user._id,
    video: id,
  });
  video.comments.push(comment._id);
  await video.save();
  return res.status(201).json({ newCommentId: comment._id }); // μˆ˜μ • ❗
};

[ ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ 데이터 κ°€μ Έμ˜€κΈ° ]

λ°±μ—”λ“œμ—μ„œ λ³΄λ‚΄λŠ” λ°μ΄ν„°λŠ”, fetch ν•¨μˆ˜κ°€ λ°˜ν™˜ν•˜λŠ” responseλ₯Ό 톡해 response.json().key의 ν˜•νƒœλ‘œ ν”„λ‘ νŠΈμ—”λ“œλ‘œ κ°€μ Έμ˜¬ 수 μžˆλ‹€.

// commentSection.js
const addComment = (text, id) => {
  // μ€‘λž΅
  const newComment = document.createElement("li");
  newComment.dataset.commentId = id;
  // μ€‘λž΅
};

const handleSubmit = () => {
  event.preventDefault();
  const text = textarea.value;
  if (!text) {
    return;
  }
  const response = fetch(`/api/videos/${videoId}/comment`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ text });
  });
  if (response.status === 201) {
    textarea.value = "";
    const { newCommentId } = response.json(); // μΆ”κ°€ ❗
    addComment(text, newCommentId); // μˆ˜μ • ❗
  }
};

πŸ”₯ 데이터 κ°€μ Έμ˜€κΈ°

  • λ°±μ—”λ“œμ—μ„œ pug(HTML)μ—μ„œ ν”„λ‘ νŠΈμ—”λ“œλ‘œ β†’ data 속성
  • λ°±μ—”λ“œμ—μ„œ ν”„λ‘ νŠΈμ—”λ“œλ‘œ β†’ json()

(4) μ‚­μ œ λ²„νŠΌ 클릭 μ‹œ DBμ—μ„œ λŒ“κΈ€ μ‚­μ œ

fetch ν•¨μˆ˜κ°€ Delete Requestλ₯Ό 보내면 deleteComment μ»¨νŠΈλ‘€λŸ¬λŠ” comments DB와 videos DBμ—μ„œ λŒ“κΈ€μ„ μ‚­μ œν•œλ‹€.

videoμ—μ„œ ν•΄λ‹Ή λŒ“κΈ€μ„ μ‚­μ œν•˜κ³ , videoλ₯Ό μ—…λ°μ΄νŠΈ ν•˜κ³ (빼먹으면 μ•ˆλ¨), λ§ˆμ§€λ§‰μœΌλ‘œ comments DBμ—μ„œ ν•΄λ‹Ή λŒ“κΈ€μ„ μ‚­μ œν•΄μ•Ό ν•œλ‹€.

// videoController.js
const deleteComment = async (req, res) => {
  const {
    session: { user },
    params: { id },
  } = req;
  const comment = await Comment.findById(id).populate("video"); // ν•΄λ‹Ή λŒ“κΈ€ κ°€μ Έμ˜€κΈ°
  const videoComments = comment.video.comments; // ν•΄λ‹Ή λŒ“κΈ€μ΄ 달렀 μžˆλŠ” video의 λͺ¨λ“  λŒ“κΈ€(λ°°μ—΄) κ°€μ Έμ˜€κΈ°
  if (!comment) {
    return res.sendStatus(404);
  }
  if (String(comment.owner._id) !== user._id) {
    return res.sendStatus(403);
  }
  videoComments.splice(videoComments.indexOf(id), 1); // λŒ“κΈ€μ΄ 달렀 있던 videoμ—μ„œ ν•΄λ‹Ή λŒ“κΈ€ μ‚­μ œ ❗
  await comment.video.save(); // video μ—…λ°μ΄νŠΈ ❗❗
  await Comment.findByIdAndDelete(id); // λ§ˆμ§€λ§‰μœΌλ‘œ comments DBμ—μ„œ ν•΄λ‹Ή λŒ“κΈ€ μ‚­μ œ ❗
  return res.sendStatus(200);
};

(5) ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ λŒ“κΈ€ μ‚­μ œ

data 속성을 'μ„ νƒμž'둜 μ‚¬μš©ν•  λ•ŒλŠ” JSμ—μ„œ μ‚¬μš©ν•˜λ”λΌλ„ -(ν•˜μ΄ν”ˆ) ν˜•νƒœ κ·ΈλŒ€λ‘œ 써야 ν•œλ‹€.

// commentSection.js
const commentsContainer = document.querySelector(".video-comments");

const handleCommentsContainerClick = async (event) => {
  const commentId = event.target.dataset.commentId;
  if (commentId && event.target.className === "delBtn") {
    const response = await fetch(`/api/comments/${commentId}`, {
      method: "DELETE",
    });
    if (response.status === 200) {
      // ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ delBtnκ³Ό 같은 'data-comment-id'λ₯Ό κ°€μ§€λŠ” λŒ“κΈ€(li.video-comment) μ‚­μ œ ❗
      const toBeDeleted = commentsContainer.querySelector(
        `.video-comment[data-comment-id="${commentId}"]`
      );
      toBeDeleted.remove();
    }
  }
};

commentsContainer.addEventListener("click", handleCommentsContainerClick);

2. 배포

1) μ„œλ²„ λΉŒλ“œ

babel-nodeλŠ” 개발 λ‹¨κ³„μ—μ„œ μœ μš©ν•œ λͺ¨λ“ˆμΌ 뿐, μ„œλΉ„μŠ€ λ‹¨κ³„μ—μ„œ μ‚¬μš©ν•˜κΈ°μ—” 속도가 λŠλ¦¬λ‹€λŠ” 단점이 μžˆλ‹€.
λ”°λΌμ„œ, μ‹€μ œλ‘œ 앱을 λ°°ν¬ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ‹€μ‹œ μ˜›λ‚  μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œλ‘œ λ°”κΏ”μ•Ό ν•œλ‹€.
babel cliλ₯Ό μ΄μš©ν•΄ μ΅œμ‹  μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œλ₯Ό μ˜›λ‚  μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œλ‘œ λ°”κΏ” λ°±μ—”λ“œλ₯Ό λΉŒλ“œν•˜κ³  이λ₯Ό μ €μž₯ν•  수 μžˆλ‹€.

$ npm install --save-dev @babel/core @babel/cli

package.json νŒŒμΌμ— scriptλ₯Ό μž‘μ„±ν•œλ‹€.

scripts: {
  "build:server": "babel src -d build",
}

μœ„ scriptλ₯Ό μ‹€ν–‰ν•˜λ©΄, babel cliλŠ” src 폴더λ₯Ό λΉŒλ“œν•΄μ„œ 이λ₯Ό build 폴더에 μ €μž₯ν•œλ‹€.

$ npm run build:server

ν•΄λ‹Ή 폴더가 github에 μ˜¬λΌκ°€μ§€ μ•Šλ„λ‘ .gitignore에 μΆ”κ°€ν•œλ‹€.

2) μ„œλ²„ μ‹œμž‘

이제 build ν΄λ”μ˜ init νŒŒμΌμ„ μ‹€ν–‰ν•΄μ•Ό ν•œλ‹€.
μ—­μ‹œλ‚˜ package.json에 scriptλ₯Ό μž‘μ„±ν•œλ‹€.
(build ν΄λ”μ˜ init νŒŒμΌμ€ μ˜›λ‚  μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œμ΄λ―€λ‘œ babel-nodeλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„λ„ nodeκ°€ 이해할 수 μžˆλ‹€.)

scripts: {
  "start": "node build/init.js",
}

μœ„ scriptλ₯Ό μ‹€ν–‰ν•œλ‹€.

$ npm run start

κ·ΈλŸ¬λ‚˜ μ½˜μ†” 창에 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.
regeneratorRuntime이 μ •μ˜λ˜μ–΄ μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€.

문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ src ν΄λ”μ˜ init.js νŒŒμΌμ— regeneratorRuntime을 import ν•œλ‹€.
λ‹€μ‹œ μ„œλ²„λ₯Ό λΉŒλ“œν•˜κ³  μ„œλ²„λ₯Ό μ—΄λ©΄ μ΄μ œλŠ” μ—λŸ¬κ°€ λœ¨μ§€ μ•ŠλŠ”λ‹€.

μ΄μ œλŠ” node.jsκ°€ λͺ¨λ“  μ½”λ“œλ₯Ό μ΄ν•΄ν•˜κΈ° λ•Œλ¬Έμ— babel-nodeκ°€ μ‹€ν–‰λ˜μ§€ μ•Šμ•„λ„ node.js만으둜 μ„œλ²„κ°€ λŒμ•„κ°„λ‹€.

πŸ’‘ μ—¬κΈ°μ„œ 잠깐 !

  • babel cliλŠ” src ν΄λ”λ§Œ λΉŒλ“œν•˜κ³  views 폴더(pug νŒŒμΌλ“€)λŠ” λΉŒλ“œν•˜μ§€ μ•Šμ•˜μŒμ—λ„ μ„œλ²„κ°€ 문제 없이 λŒμ•„κ°„λ‹€. κ·Έ μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

server.js νŒŒμΌμ—μ„œ viewsλ₯Ό μ„€μ •ν•  λ•Œ λ‹€μŒκ³Ό 같이 μž‘μ„±ν–ˆλ‹€.

// server.js
app.set("views", process.cwd() + "/src/views");

μ΄λ•Œ process.cwd()λž€ ν˜„μž¬ working directory둜써 nodeλ₯Ό μ‹€ν–‰ν•œ 폴더λ₯Ό λ§ν•œλ‹€.
즉, package.json 파일이 μžˆλŠ” 폴더λ₯Ό λ§ν•˜λ©° μ΄λŠ” root 폴더이닀.
root ν΄λ”λŠ” build 폴더와 src 폴더λ₯Ό λͺ¨λ‘ ν¬ν•¨ν•˜λŠ” ν΄λ”μ΄λ―€λ‘œ /src/views 폴더에 μ ‘κ·Όν•˜λŠ” 데 아무 λ¬Έμ œκ°€ μ—†κΈ° λ•Œλ¬Έμ— μ„œλ²„λŠ” λ¬Έμ œμ—†μ΄ λŒμ•„κ°€κ²Œ λœλ‹€.

  • ν•œνŽΈ, λΉŒλ“œ μ„œλ²„λŠ” ν™˜κ²½ λ³€μˆ˜μ—λ„ 문제 없이 μ ‘κ·Όν•  수 μžˆλ‹€.
_mongoose["default"].connect(process.env.DB_URL);

3) ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œ λΉŒλ“œ

μ„œλ²„λΏ μ•„λ‹ˆλΌ assets λ˜ν•œ λΉŒλ“œν•΄μ•Ό ν•œλ‹€.
webpack은 κ°œλ°œν•  λ•ŒλŠ” development λͺ¨λ“œμ˜€μ§€λ§Œ 배포할 λ•ŒλŠ” production λͺ¨λ“œλ‘œ λ°”κΏ” μ½”λ“œλ₯Ό μ••μΆ•ν•΄μ•Ό ν•œλ‹€.

webpack.config.json νŒŒμΌμ—μ„œ mode와 watch 뢀뢄을 μ§€μš°κ³ , webpack을 μ‹€μ œλ‘œ μ‹€ν–‰ν•  λ•Œ 상황에 맞게 싀행될 수 μžˆλ„λ‘ package.json νŒŒμΌμ—μ„œ μ˜΅μ…˜μœΌλ‘œ 넣어쀬닀.
μΆ”κ°€λ‘œ μ„œλ²„μ™€ assets을 λΉŒλ“œν•˜λŠ” λͺ…λ Ήμ–΄λ₯Ό ν•œλ²ˆμ— λͺ¨μ•„ build scriptλ₯Ό μž‘μ„±ν–ˆλ‹€.

// package.json
scripts: {
  "start": "node build/init.js",
  "build": "npm run build:server && npm run build:assets",
  "build:server": "babel src -d build",
  "build:assets": "webpack --mode=production",
  "dev:server": "nodemon -L",
  "dev:assets": "webpack --mode=development -w",
}

이제 npm run build만 ν•˜λ©΄, μ„œλ²„μ™€ assetsκ°€ λͺ¨λ‘ λΉŒλ“œλœλ‹€.
λ‹€λ§Œ 아직은 npm run start(npm start)λ₯Ό ν•˜λ©΄ DBκ°€ μ—†λŠ” μ„œλ²„λ‘œ μ΄λ™ν•˜κΈ° λ•Œλ¬Έμ— μ—λŸ¬κ°€ 생긴닀.
μ΄λŠ” λ’€μ—μ„œ ν•΄κ²°ν•΄λ³Ό 것이닀.

4) Heroku에 배포

Herokuλ₯Ό μ΄μš©ν•˜λ©΄ μ„œλ²„λ₯Ό 맀우 λΉ λ₯΄κ²Œ 배포할 수 μžˆλ‹€.

일단 Heroku에 앱을 λ§Œλ“€μ–΄μ•Ό ν•œλ‹€.
계정 생성 ν›„ μ—¬κΈ°μ„œ create new app ν΄λ¦­ν•œλ‹€.
μ•± 이름을 μž‘μ„±ν•˜κ³  지역을 미ꡭ으둜 μ„€μ •ν•œ ν›„ create app λ²„νŠΌ ν΄λ¦­ν•˜λ©΄, Heroku에 앱이 λ§Œλ“€μ–΄μ§„λ‹€.

Heroku에 λ°±μ—”λ“œ μ„œλ²„λ₯Ό μ—…λ‘œλ“œ ν•˜λŠ” λ°λŠ” 2가지 방법이 μžˆλ‹€.
λ¨Όμ € Heroku Git을 μ΄μš©ν•˜λŠ” 방법을 μ‚΄νŽ΄λ³Ό 것이닀.

(1) Heroku Git 이용

πŸ’‘ μ•± 배포

Heroku - Deployment method μ°Έκ³ 

Heroku cliλ₯Ό μ„€μΉ˜ν•œλ‹€. (WSL에 Heroku cli μ„€μΉ˜ν•˜κΈ°)
μ„€μΉ˜κ°€ λλ‚˜λ©΄ 터미널에 heroku login을 μž…λ ₯ν•΄ λ‘œκ·ΈμΈν•œλ‹€.

$ curl https://cli-assets.heroku.com/install.sh | sh

ν”„λ‘œμ νŠΈ ν΄λ”λ‘œ μ΄λ™ν•œλ‹€.
git repositoryλ₯Ό λ§Œλ“ λ‹€.
git heroku 원격 μ €μž₯μ†Œλ₯Ό λ§Œλ“ λ‹€.

$ cd my-project/
$ git init
$ heroku git:remote -a syongtube
  • ν”„λ‘œμ νŠΈ 폴더에 Heroku 원격 μ €μž₯μ†Œλ₯Ό λ§Œλ“€λ©΄, HerokuλŠ” μ—…λ‘œλ“œλœ μ½”λ“œ 쀑에 package.json νŒŒμΌμ„ 보고 이것이 node.js ν”„λ‘œμ νŠΈλΌλŠ” 것을 μ•Œκ²Œ λœλ‹€.
  • HerokuλŠ” package.json νŒŒμΌμ—μ„œ dependencies와 devDependenciesλ₯Ό μ°Έκ³ ν•΄ ν”„λ‘œμ νŠΈμ— ν•„μš”ν•œ λͺ¨λ“ˆλ“€μ„ μ„€μΉ˜ν•œ ν›„, npm build와 npm startλ₯Ό μžλ™μœΌλ‘œ μ‹€ν–‰ν•œλ‹€.
    (이λ₯Ό μœ„ν•΄ script 이름을 μΌλΆ€λŸ¬ start와 build둜 μ§€μ—ˆλ‹€.)

이제 git add와 git commit은 git에 ν•˜κ³ , git pushλŠ” heroku에 ν•  수 μžˆλ‹€.
λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μž…λ ₯ν•˜μ—¬ 앱을 λ°°ν¬ν•˜κ³  μˆ˜μ •ν•  수 μžˆλ‹€.

$ git push heroku main

배포 전에 μ£Όμ˜ν•  점 ❗❗

HerokuλŠ” 였직 git historyλ§Œμ„ λ³Ό 수 μžˆλ‹€.
HerokuλŠ” git으둜 μž‘λ™ν•˜κΈ° λ•Œλ¬Έμ— 였직 git이 λ³Ό 수 μžˆλŠ” μ½”λ“œλ§Œ HerokuλŠ” μ—…λ‘œλ“œλ₯Ό ν•˜λŠ” 것이닀.
λ§Œμ•½ μ½”λ“œλ₯Ό λ°”κΎΈκ³  commit을 ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, HerokuλŠ” 변경사항을 μ•Œ 수 μ—†λ‹€.
λ”°λΌμ„œ, μ½”λ“œλ₯Ό λ°”κΎΌλ‹€λ©΄ λ°˜λ“œμ‹œ μžŠμ§€ 말고 commit을 ν•΄μ•Ό ν•œλ‹€ ❗❗
λ‹Ήμ—°νžˆ 맨 처음 앱을 λ°°ν¬ν•˜κΈ° 전에도 μ„œλ²„μ™€ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλ₯Ό λΉŒλ“œν•œ ν›„ commit을 ν•΄μ•Όλ§Œ 배포λ₯Ό μ •μƒμ μœΌλ‘œ 진행할 수 μžˆλ‹€.

앱을 λ°°ν¬ν•˜κΈ° 전에 μ„œλ²„λ‚˜ Heroku의 둜그λ₯Ό λ³Ό 수 μžˆλ„λ‘ λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•œλ‹€.
μ΄λŠ” 터미널에 μ‹€μ‹œκ°„μœΌλ‘œ 둜그λ₯Ό 보여쀀닀.

$ heroku logs --tail

πŸ’‘ mongoDB Atlas

κ·ΈλŸ¬λ‚˜, μœ„ 절차λ₯Ό λ”°λ₯΄λ”라도 mongoDB와 κ΄€λ ¨ν•˜μ—¬ λ‹€μŒκ³Ό 같은 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

You must provide either mongoUrl|clientPromise|client in options
Error: Cannot init client. Please provide correct options

[ μ—λŸ¬ 원인 ]

HerokuλŠ” githubκ°€ λ³΄λŠ” κ²ƒλ§Œ λ³Ό 수 μžˆλ‹€.
그런데 μ•žμ„œ mongoDBλ₯Ό μ—°κ²°ν•  λ•Œ κ·Έ url을 .env νŒŒμΌμ— μž‘μ„±ν•œ ν›„ 이λ₯Ό .gitignore νŒŒμΌμ— 적어 github에 μ˜¬λ¦¬μ§€ μ•Šμ•˜λ‹€.
λ”°λΌμ„œ, HerokuλŠ” commit λ˜μ§€ μ•Šμ€ ν•΄λ‹Ή 파일의 λ‚΄μš©μ„ λ³Ό 수 μ—†κΈ° λ•Œλ¬Έμ— mongoDB에 μ—°κ²°ν•  수 μ—†μ–΄ μ—λŸ¬κ°€ λ°œμƒν•œ 것이닀.

[ μ—λŸ¬ ν•΄κ²° ]

mongoDB Atlas 계정을 μƒμ„±ν•˜κ³  mongoDB에 μ—°κ²°ν•œλ‹€.
( free / ip anywhere / username & password / native / DB url )

μ•žμ„œ .env νŒŒμΌμ— μž‘μ„±ν•œ mongoDB url은 κ·Έμ € localhostλ₯Ό μœ„ν•œ κ²ƒμ΄μ—ˆλ‹€.
mongoDB Atlasλ₯Ό μ΄μš©ν•΄ mongoDB에 μ—°κ²°ν•˜λ©΄ μ‹€μ œ λ°μ΄ν„°λ² μ΄μŠ€μ˜ url을 얻을 수 μžˆλ‹€.

이λ₯Ό Heroku의 Settingsμ—μ„œ λ³€μˆ˜μ˜ κ°’μœΌλ‘œ μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.
db.js νŒŒμΌμ— 적어쀀 DB_URL λ³€μˆ˜λ₯Ό μΆ”κ°€ν•œ ν›„, λ‹€μ‹œ ν„°λ―Έλ„μ—μ„œ 둜그λ₯Ό ν™•μΈν•˜λ©΄ mongoDB에 μ—°κ²°λœ 것을 확인할 수 μžˆλ‹€.

βž• 이외에도 server.js νŒŒμΌμ— sessionκ³Ό κ΄€λ ¨ν•˜μ—¬ 적어쀀 COOKIE_SECRET λ³€μˆ˜λ₯Ό μΆ”κ°€ν•΄μ•Ό ν•œλ‹€. (μž„μ˜μ˜ κ°’ μ„€μ •)


πŸ’‘ Herokuκ°€ μ£ΌλŠ” 랜덀 PORT둜 μ—°κ²°

κ·ΈλŸ¬λ‚˜, μ΄λ ‡κ²Œ 해도 λ°°ν¬ν•œ μ›Ή μ‚¬μ΄νŠΈμ— λ“€μ–΄κ°€λ €κ³  ν•˜λ©΄ μ΄λ²ˆμ—λŠ” λ‹€μŒκ³Ό 같은 μ—λŸ¬κ°€ λœ¬λ‹€.

Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch

[ μ—λŸ¬ 원인 ]

Herokuκ°€ μ£ΌλŠ” 랜덀 PORT둜 연결을 ν•΄μ•Ό ν•˜λŠ”λ° ν˜„μž¬ init.js νŒŒμΌμ—μ„œλŠ” 무쑰건 4000포트둜 μ—°κ²°ν•˜λ„λ‘ λ˜μ–΄ 있기 λ•Œλ¬Έμ— timeout μ—λŸ¬κ°€ λ°œμƒν–ˆλ‹€.

[ μ—λŸ¬ ν•΄κ²° ]

Herokuκ°€ μ£ΌλŠ” 랜덀 Port둜 μ—°κ²°ν•˜κΈ° μœ„ν•΄ init.js νŒŒμΌμ—μ„œ PORT의 값을 μˆ˜μ •ν•œλ‹€.
μ΄λ•Œ Herokuμ—μ„œ μ‹€ν–‰λ˜μ§€ μ•Šμ„ λ•Œ(ex. λ‚΄ 컴퓨터)λŠ” 4000 PORT에 μ—°κ²°λ˜λ„λ‘ λ‘˜ λ‹€ μ μ–΄μ€˜μ•Ό ν•œλ‹€

const PORT = process.env.PORT || 4000;

νŒŒμΌμ„ μˆ˜μ •ν–ˆμœΌλ―€λ‘œ λ°˜λ“œμ‹œ commit을 ν•΄μ•Ό ν•œλ‹€ ❗❗
git commit ν›„ git push heroku main을 μž…λ ₯ν•˜λ©΄ λ‹€μ‹œ λΉŒλ“œλ₯Ό μ‹œμž‘ν•œλ‹€.

λ‹€μ‹œ λ°°ν¬ν•œ μ›Ή μ‚¬μ΄νŠΈμ— 듀어가보면 μ œλŒ€λ‘œ λœ¨λŠ” 것을 확인할 수 μžˆλ‹€!!!!! YEAH!!!!!
비둝 DB에 λ°μ΄ν„°λŠ” ν•˜λ‚˜λ„ μ—†μ§€λ§Œ μ›Ή μ‚¬μ΄νŠΈκ°€ μ •μƒμ μœΌλ‘œ λœ¬λ‹€.


πŸ’‘ github 둜그인 μ•ˆ λ˜λŠ” μ—λŸ¬ ν•΄κ²°

β‘  λ‹€λ§Œ, github에 λ‘œκ·ΈμΈν•˜λ €κ³  ν•˜λ©΄ 404 μ—λŸ¬κ°€ λœ¬λ‹€.
userController.js에 github 둜그인과 κ΄€λ ¨ν•˜μ—¬ 적어쀀 GH_CLIENT, GH_SECRET λ³€μˆ˜κ°€ undefined 값이닀.
Herokuκ°€ .env νŒŒμΌμ— 적어쀀 값을 μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

μ—λŸ¬λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ Heroku의 Settingsμ—μ„œ GH_CLIENT, GH_SECRET λ³€μˆ˜λ„ μΆ”κ°€ν–ˆλ‹€.
ν˜„μž¬κΉŒμ§€ μΆ”κ°€ν•œ λ³€μˆ˜λŠ” 총 4개인데 λͺ¨λ‘ 정말 μ€‘μš”ν•˜κΈ° λ•Œλ¬Έμ— Hide λ²„νŠΌμ„ 눌러 μˆ¨κ²¨μ€¬λ‹€.

β‘‘ ν•˜μ§€λ§Œ, μ—¬μ „νžˆ github둜 둜그인 ν•  수 μ—†λ‹€.
localhost:4000~ 으둜 λ˜λŒμ•„κ°€λ„λ‘ μ½”λ“œλ₯Ό μž‘μ„±ν–ˆκΈ° λ•Œλ¬Έμ΄λ‹€.

μ—λŸ¬λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ github Settings - Developer settings - Ouath Appsμ—μ„œ callball URL을 λ³€κ²½ν•΄μ•Ό ν•œλ‹€.
https:// localhost:4000/을 μ›Ή μ‚¬μ΄νŠΈμ˜ home μ£Όμ†Œλ‘œ λ°”κΎΌλ‹€.

β‘’ 이제 github λ‘œκ·ΈμΈμ€ λ¬Έμ œμ—†μ΄ ν•  수 μžˆλ‹€.
κ·ΈλŸ¬λ‚˜, github 둜그인과 κ΄€λ ¨ν•˜μ—¬ ν…ŒμŠ€νŠΈ ν•˜λ €κ³  ν•  λ•Œλ§ˆλ‹€ 이λ₯Ό λ‹€μ‹œ λ°”κΏ”μ€˜μ•Ό ν•˜λŠ” 게 λ²ˆκ±°λ‘­λ‹€.

μ’€ 더 νŽΈν•˜κ²Œ ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ‹€μ œ 배포용과 ν…ŒμŠ€νŠΈμš© μ•± 2개λ₯Ό λ§Œλ“€μ–΄ μ‚¬μš©ν•  수 μžˆλ‹€.
μ‹€μ œ 배포용 μ•±μ—μ„œμ˜ callback URL은 localhost:4000으둜 두고, Client ID와 Client secretsλ₯Ό Heroku μ›Ή μ‚¬μ΄νŠΈμ— λ‘˜ 수 μžˆλ‹€.
ν…ŒμŠ€νŠΈμš© μ•±μ—μ„œμ˜ callback URL은 μ‹€μ œ μ›Ή μ‚¬μ΄νŠΈ url둜 두고, Client ID와 Client secretsλ₯Ό .env νŒŒμΌμ— λ‘˜ 수 μžˆλ‹€.


✨ 내일 ν•  것

  1. githubλ₯Ό μ΄μš©ν•΄ Heroku에 μ•± λ°°ν¬ν•˜κΈ°
profile
λŠ₯λ™μ μœΌλ‘œ μ‚΄μž, ν–‰λ³΅ν•˜κ²ŒπŸ˜

0개의 λŒ“κΈ€