λκΈ μμ νκΈ° - νλ‘ νΈμλμ DBμμ λͺ¨λ μμ νκΈ°
μλ² & ν΄λΌμ΄μΈνΈ μ½λ λΉλ / μλ² μμ
Herokuμ μ± λ°°ν¬νκΈ° - Heroku Git / mongoDB Atlas / Herokuμ νκ²½ λ³μ μΆκ°
μλ¬ ν΄κ²° - PORT μ°κ²° / github λ‘κ·ΈμΈ
μ½λ μ±λ¦°μ§
π‘ νλ‘ νΈμλ - fetch - apiRouter - λ°±μλ(컨νΈλ‘€λ¬) - νλ‘ νΈμλ
μμ λ²νΌμ λκΈ μμ±μλ§ λ³Ό μ μμ΄μΌ νλ€.
//- 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);
};
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);
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()
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);
};
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);
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μ μΆκ°νλ€.
μ΄μ 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);
μλ²λΏ μλλΌ 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κ° μλ μλ²λ‘ μ΄λνκΈ° λλ¬Έμ μλ¬κ° μκΈ΄λ€.
μ΄λ λ€μμ ν΄κ²°ν΄λ³Ό κ²μ΄λ€.
Herokuλ₯Ό μ΄μ©νλ©΄ μλ²λ₯Ό λ§€μ° λΉ λ₯΄κ² λ°°ν¬ν μ μλ€.
μΌλ¨ Herokuμ μ±μ λ§λ€μ΄μΌ νλ€.
κ³μ μμ± ν μ¬κΈ°μ create new app ν΄λ¦νλ€.
μ± μ΄λ¦μ μμ±νκ³ μ§μμ λ―Έκ΅μΌλ‘ μ€μ ν ν create app λ²νΌ ν΄λ¦νλ©΄, Herokuμ μ±μ΄ λ§λ€μ΄μ§λ€.
Herokuμ λ°±μλ μλ²λ₯Ό μ
λ‘λ νλ λ°λ 2κ°μ§ λ°©λ²μ΄ μλ€.
λ¨Όμ 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 νμΌμ λ μ μλ€.