일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- JavaScript
- 그리디
- PROJECT
- localstorage
- router
- 백준 32029번
- Next
- 지금 자면 꿈을 꾸지만
- 더 흔한 색칠 타일 문제
- 게임 서버 아키텍처
- insomnia
- Prisma
- Express.js
- HTTP
- ccw 알고리즘
- MySQL
- pm2
- ucpc 2023 예선 i번
- html5
- Github
- 백준 32028번
- map
- branch
- 백준 28303번
- 자바스크립트
- ERD
- string
- ucpc 2023 예선 d번
- MongoDB
- ucpc 2024 예선 e번
- Today
- Total
dh_0e
[Project] Node.js와 express를 활용한 게임 아이템 시뮬레이터 서비스 (개인 과제) 본문
내배캠 4번째 프로젝트부터 본격적으로 백엔드와 게임 서버반으로 분반되어 진행되었다.
Node.js와 express를 사용하여 캐릭터, 아이템의 CRUD와 상호작용을 구하는 개인 프로젝트를 진행하였다.
필수 요구 사항은 다음과 같다.
🚩 필수 요구 사항
0️⃣ 필수 요구 사항: 프로젝트 관리
- .env 파일을 이용해서 민감한 정보(DB 계정 정보, API Key 등)를 관리합니다.
- .gitignore 파일을 생성하여 .env 파일과 node_modules 폴더가 Github에 올라가지 않도록 설정합니다.
- .prettierrc 파일을 생성하여 일정한 코드 포맷팅을 유지할 수 있도록 설정합니다.
1️⃣ 필수 요구 사항: AWS EC2 배포
- 여러분의 완성된 프로젝트를 **AWS EC2**에 배포해주세요!
- 배포된 IP 주소를 제출해주세요!
2️⃣ 필수 요구 사항: API 구현하기
- 캐릭터 생성 API
- 엄밀히 말하면 게임 내의 캐릭터를 불러와야 하지만 저희는 아직 게임을 만들지 않았으니 캐릭터 생성을 하는 API가 필요합니다.
- 캐릭터 명을 request에서 전달 받기
- 이 때, 이미 존재하는 캐릭터 명으로 캐릭터 생성을 하려고 하면 생성을 못하게 해주세요!
- 사실은 캐릭터 명 말고도 캐릭터에 대한 여러가지 정보를 받아야하지만 여기선 간소화 할게요!
- 캐릭터는 생성할 때 character_id를 순차적으로 부여해주세요.
- 최초 캐릭터 생성 시 캐릭터 ID는 1로 부여하고 그 뒤에 캐릭터 생성하면 캐릭터 ID는 2로 부여
- MongoDB에서는 인덱스로 ObjectId를 쓰는 것이 기본이지만 여기선 따로 주는 것으로 할게요!
- 캐릭터의 스탯을 다음과 같이 설정해주세요.
- health: 500
- power: 100
- 캐릭터 스탯은 정말 다양하게 있지만 역시나 최소한으로 간소화하여 관리하도록 하겠습니다!
- 즉, 우리가 만드는 캐릭터는 HP와 힘 능력만 있는 상태라고 생각하시면 됩니다!
- MongoDB에는 이런식으로 캐릭터 데이터가 저장이 되어있을 것입니다.
- { _id: ObjectId("649b85a52009a26bc6b4f6e9") character_id: 321, name: "호호아줌마" health: 500, power: 100 }
- 생성 성공 시 캐릭터의 ID를 반드시 response로 꼭 전달해주세요!
- 캐릭터 삭제 API
- 삭제할 캐릭터의 ID는 URI의 parameter로 전달 받기
- 예시: DELETE /api/characters/**321** ← 321번 캐릭터 삭제! 굵은 글씨가 parameter에요!
- 삭제할 캐릭터의 ID는 URI의 parameter로 전달 받기
- 캐릭터 상세 조회 API
- 조회할 캐릭터의 ID는 URI의 parameter로 전달 받기
- 캐릭터 이름, HP, 힘 스탯 및 아이템 목록을 전달해주세요!
- response 예시
- { "name": "호호아줌마", "health": 500, "power": 100 }
- 아이템 생성 API
- 아이템 코드, 아이템 명, 아이템 능력을 request에서 전달 받기
- 이 때, 아이템 능력은 JSON 포맷으로 전달해주시면 됩니다.
- request의 body 예시
- 위의 예시대로 아이템 생성을 하면 “파멸의 반지”라는 아이템이 생성이 되어야 합니다.
- 해당 아이템은 캐릭터의 HP를 20 올려주고 힘 능력을 2를 올려주는 것이에요.
- 위의 예시대로 아이템 생성을 하면 “파멸의 반지”라는 아이템이 생성이 되어야 합니다.
- { "item_code": 3, "item_name": "파멸의 반지", "item_stat": { "health": 20, "power": 2 } }
- 아이템 코드, 아이템 명, 아이템 능력을 request에서 전달 받기
- 아이템 수정 API
- 아이템 코드는 URI의 parameter로 전달 받기
- 아이템 명, 아이템 능력을 request에서 전달 받기
- 아이템 생성과 마찬가지로 전달을 해주시면 됩니다.
- request의 body 예시
- { "item_name": "파멸의 반지_리뉴얼", "item_stat": { "health": 30 } }
- 위의 예시대로 아이템 수정을 하면 파멸의 반지 → 파멸의 반지_리뉴얼로 이름이 바뀌었고 HP를 20에서 30으로 올려줍니다. 다만, 기존에 힘 능력을 2를 올려줬던 능력은 사라졌네요.
- 아이템 목록 조회 API
- 아이템 코드, 아이템 명 내용만 조회
- 아이템 생성 API를 통해 생성된 모든 아이템들이 목록으로 조회가 될 수 있어야 합니다.
- response 예시
- [ { "item_code": 1, "item_name": "막대기", }, { "item_code": 2, "item_name": "너덜너덜한 고무신", }, { "item_code": 3, "item_name": "파멸의 반지_리뉴얼", } ]
- 아이템 상세 조회 API
- 아이템 코드를 URI의 parameter로 전달 받아 아이템 코드, 아이템 명, 아이템 능력을 조회
- response 예시
- {"item_code": 3,"item_name": "파멸의 반지","item_stat": { "health": 20, "power": 2 }}
선택 요구 사항으로 아이템 장착 및 탈착 기능까지 모두 구현했다.
🔥 도전 요구 사항: 캐릭터에 아이템 실제로 탈/장착해보기!
- 필수 요구 사항의 API를 전부 구현하면 우리는 다음과 같은 것들을 확인할 수 있어요.
- 캐릭터 생성 및 삭제가 가능합니다.
- 해당 캐릭터의 스탯 조회도 가능합니다.
- 우리가 원하는대로 아이템 도감에 아이템을 추가할 수 있습니다.
- 이미 추가된 아이템의 정보를 수정하는 것도 가능합니다!
- 하지만, 뭔가 허전하다고 생각드지 않으시나요? 생성된 캐릭터에 아이템을 장착하거나 탈착할 수 있어야 진짜죠! 필수 요구 사항을 구현하신 분들을 위해 추가 미션을 드리겠습니다.
🤔 Q) 튜터님, 아이템 삭제 기능은 왜 넣지 않으셨나요? A) 실제로 게임에서 아이템을 삭제하면 정말 어마어마한 후유증을 야기할 수 있기 때문이에요. 게임에서 아이템은 게임 경제를 구성하는 중요한 요소이기 때문이죠!
특정 게임 아이템이 너무 OP라고 생각이 들면 아이템 수정 API를 통해 밸런스 패치를 해주면 문제가 없어지니 삭제 기능은 굳이 필요가 없습니다!
- 캐릭터의 아이템 정보를 저장할 수 있는 컬렉션 준비하기
- MongoDB에서 캐릭터의 정보와 아이템의 정보를 각각 저장하는 컬렉션은 이미 있을 겁니다.
- 하지만, 어떤 캐릭터가 어떤 아이템을 장착하고 있는지에 대한 정보를 관리할 수 있어야 아이템 탈/장착이 가능하겠죠?
- 캐릭터가 장착한 아이템 목록 조회 API
- 장착된 아이템 목록을 조회할 캐릭터의 ID를 URI의 parameter로 전달 받기
- response 예시
- [ { "item_code": 1, "item_name": "막대기", }, { "item_code": 3, "item_name": "파멸의 반지", } ]
- 아이템 장착 API
- 아이템을 장착할 캐릭터의 ID를 URI의 parameter로 전달 받기
- 장착할 아이템 코드를 request에서 전달 받기
- 이 때, 이미 장착한 아이템(아이템 코드 기반으로 구분할 수 있겠죠)을 또 장착하려고 하면 이미 장착된 아이템이라고 장착이 거부되어야 합니다!
- 매우 중요: 아이템 장착을 하게 되면 캐릭터의 스탯이 올라가야 합니다!
- 아이템 장착에 성공하면 기존 캐릭터의 스탯을 직접적으로 변경해주도록 해요.
- 예시
- BEFORE.
- 캐릭터 스탯: { health: 500, power: 100 }
- “파멸의 반지”를 장착!
- AFTER.
- 캐릭터 스탯: { health: 520, power: 102 }
- BEFORE.
- 캐릭터 조회 API를 사용할 때 변경된 캐릭터 스탯으로 나타나야겠죠?
- 또한, 1번 항목에서 만든 캐릭터-아이템 컬렉션에서 해당 아이템 정보를 추가해야 됩니다.
- 정상적으로 추가가 되었다면 캐릭터가 장착한 아이템 목록 조회 API에서 추가된 것이 보일겁니다.
- 아이템 탈착 API
- 아이템을 장착할 캐릭터의 ID를 URI의 parameter로 전달 받기
- 탈착할 아이템 코드를 request에서 전달 받기
- 이 때, 장착 되지 않은 아이템을 탈착하려고 하면 장착 되어있지 않은 아이템이라고 탈착이 거부되어야 합니다!
- 매우 중요: 아이템 탈착을 하게 되면 캐릭터의 스탯이 떨어져야 합니다!
- 아이템 탈착에 성공하면 기존 캐릭터의 스탯을 직접적으로 변경해주도록 해요.
- 예시
- BEFORE.
- 캐릭터 스탯: { health: 520, power: 102 }
- “파멸의 반지”를 탈착!
- AFTER.
- 캐릭터 스탯: { health: 500, power: 100 }
- BEFORE.
- 캐릭터 조회 API를 사용할 때 변경된 캐릭터 스탯으로 나타나야겠죠?
- 또한, 1번 항목에서 만든 캐릭터-아이템 컬렉션에서 해당 아이템 정보를 삭제해야 됩니다.
- 정상적으로 삭제가 되었다면 캐릭터가 장착한 아이템 목록 조회 API에서 없어진 것이 보일겁니다.
Code
app.js: index.js 실행, 라우터 charactersRouter, itemsRouter, mountingRouter를 미들웨어로 연결(URL이 /api로 입력되었을 때), 포트를 3000으로 설정하여 서버를 열어줌
import express from "express";
import connect from "./schemas/index.js";
import charactersRouter from "./routes/characters.router.js";
import itemsRouter from "./routes/items.router.js";
import mountingRouter from "./routes/mounting.router.js";
const app = express();
const PORT = 3000;
connect(); // index.js 실행
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const router = express.Router();
router.get("/", (req, res) => {
return res.json({ message: "Hello, welcome to Item simulator!" });
});
app.use("/api", [router, charactersRouter, itemsRouter, mountingRouter]); // use는 미들웨러를 사용해주게 함 /api 경로로 접근하는 경우에만 json 미들웨어를 거친 뒤 router로 연결되게 함
app.listen(PORT, () => {
console.log(PORT, "포트로 서버가 열렸어요");
});
index.js: MongoDB 개인이 대여한 ID, PW, 주소를 입력하여 MongoDB와 서버를 연결해줌. .env에 저장한 ID, PW를 가져오며, .env 파일은 개인 정보이므로 외부에 유출되지 않게끔 함.
import mongoose from "mongoose";
import dotenv from "dotenv";
const connect = () => {
dotenv.config();
const id = process.env.ID;
const pw = process.env.PW;
mongoose
.connect(
`mongodb+srv://${id}:${pw}@express-mongo.8eld6sd.mongodb.net/?retryWrites=true&w=majority&appName=express-mongo`,
{
dbName: "game-manager",
}
)
.then(() => console.log("MongoDB 연결에 성공하였습니다."))
.catch((err) => console.log(`MongoDB 연결에 실패하였습니다. ${err}`));
};
mongoose.connection.on("error", (err) => {
console.error("MongoDB 연결 에러", err);
});
export default connect;
character.schema.js: 캐릭터 컬렉션의 구조 및 문서의 데이터 타입(스키마)를 짜놓은 파일
import mongoose from "mongoose";
const characterSchema = new mongoose.Schema({
character_id: {
type: Number,
required: true,
},
name: {
type: String,
required: true,
},
health: {
type: Number,
required: true,
},
power: {
type: Number,
required: true,
},
});
export default mongoose.model("Characters", characterSchema);
character.router.js: 캐릭터 생성, 조회(전체/상세), 삭제 api가 작성된 라우터 구현 (mountedItems이 빈 마운팅도 생성)
import express from "express";
import Characters from "../schemas/characters.schema.js";
import Mountings from "../schemas/mounting.schema.js";
const router = express.Router();
/* 캐릭터 생성 api */
router.post("/character", async (req, res, next) => {
const { name } = req.body;
const sameName = await Characters.findOne({ name }).exec(); // name과 동일한 이름의 character가 있는지 찾음
if (sameName) {
// 있다면 에러 메시지 전송
return res.status(400).json({ errorMessage: "동일한 이름이 존재합니다" });
}
const characterId = await Characters.findOne().sort("-character_id").exec(); // 가장 큰 character_id의 객체를 가져옴
const id = characterId ? characterId.character_id + 1 : 1; // 비어있다면 1, 아니라면 가장 큰 character_id+1을 id값으로 설정
const newCharacter = new Characters({
// 자동으로 health, power 스탯 초기화
character_id: id,
name,
health: 500,
power: 100,
});
await newCharacter.save();
// Mountings에도 캐릭터 정보 추가
const newMounting = new Mountings({
character_id: id,
mountedItems: [],
});
await newMounting.save();
return res.status(201).json({ newCharacter });
});
/* 캐릭터 전체 조회 api */
router.get("/character/", async (req, res, next) => {
const charactersList = await Characters.find().sort("-character_id").exec();
return res.status(200).json({ charactersList });
});
/* 캐릭터 상세 조회 api */
router.get("/character/:characterId", async (req, res, next) => {
const characterId = req.params.characterId; // parameter 가져오기
const character = await Characters.findOne({
// character_id가 같은 객체 찾기
character_id: characterId,
}).exec();
if (!character) {
// 없으면 에러 메시지
return res.status(404).json({ errorMessage: "조회할 캐릭터가 없습니다." });
}
const { name, health, power } = character; // 출력할 정보들 구조 분해 할당
return res.status(200).json({ name, health, power });
});
/* 캐릭터 삭제 api */
router.delete("/character/:characterId", async (req, res, next) => {
const characterId = req.params.characterId; // parameter 가져오기
const character = await Characters.findOne({
// character_id가 같은 객체 찾기
character_id: characterId,
}).exec();
if (!character) {
// 없으면 에러 메시지
return res.status(404).json({ errorMessage: "삭제할 캐릭터가 없습니다." });
}
const deleteId = character.character_id; // 객체의 id 불러와서
await Characters.deleteOne({ character_id: deleteId }); // character_id가 같은 객체 삭제
// Mountings에도 캐릭터 정보 삭제
await Mountings.deleteMany({ character_id: deleteId });
return res.status(200).json({ completeMessage: "삭제가 완료되었습니다." });
});
export default router;
item.schema.js: 아이템 컬렉션의 구조 및 문서의 데이터 타입(스키마)를 짜놓은 파일
import mongoose from "mongoose";
const itemSchema = new mongoose.Schema({
item_code: {
type: Number,
required: true,
},
item_name: {
type: String,
required: true,
},
item_stat: {
health: {
type: Number,
required: false,
},
power: {
type: Number,
required: false,
},
},
});
export default mongoose.model("Items", itemSchema);
items.router.js: 아이템 생성, 조회(전체/상세), 수정 api가 작성된 라우터 구현
import express from "express";
import Items from "../schemas/items.schema.js";
import Mountings from "../schemas/mounting.schema.js";
const router = express.Router();
/* 아이템 생성 api */
router.post("/item", async (req, res, next) => {
const { item_name, item_code, item_stat } = req.body;
const sameName = await Items.findOne({ item_name }).exec(); // item_name과 동일한 이름의 item이 있는지 찾음
if (sameName) {
// 있다면 에러 메시지 전송
return res.status(400).json({ errorMessage: "동일한 이름이 존재합니다" });
}
const newItem = new Items({
item_code,
item_name,
item_stat,
});
await newItem.save();
return res.status(201).json({ newItem });
});
/* 아이템 목록 조회 api */
router.get("/item/", async (req, res, next) => {
const itemsList = await Items.find().sort("item_code").exec(); // item_code로 내림차순 정렬
const arr = [];
itemsList.forEach((item) => {
// 배열에 code, name만 담기
const { item_code, item_name } = item;
arr.push({ item_code, item_name });
});
return res.status(200).json(arr);
});
/* 아이템 상세 조회 api */
router.get("/item/:itemCode", async (req, res, next) => {
const itemCode = req.params.itemCode; // parameter 가져오기
const item = await Items.findOne({
// item_code가 같은 객체 찾기
item_code: itemCode,
}).exec();
if (!item) {
// 없으면 에러 메시지
return res.status(404).json({ errorMessage: "조회할 아이템이 없습니다." });
}
const { item_code, item_name, item_stat } = item; // 출력할 정보들 구조 분해 할당
return res.status(200).json({ item_code, item_name, item_stat });
});
/* 아이템 수정 api */
router.patch("/item/:itemCode", async (req, res, next) => {
const { item_name, item_stat } = req.body; // 수정할 정보 갸져오기
const itemCode = req.params.itemCode; // parameter(바꿀 아이템 code) 가져오기
const item = await Items.findOne({
// item_code가 같은 객체 찾기
item_code: itemCode,
}).exec();
if (!item) {
// 없으면 에러 메시지
return res.status(404).json({ errorMessage: "수정할 아이템이 없습니다." });
}
// 장착하고 있는 캐릭터가 있다면 에러 메시지 출력
let mounting = false;
const mount = await Mountings.find().exec();
mount.forEach((obj) => {
const mounted = obj.mountedItems.find(function (arr) {
// 장착되어있는 item_code 아이템 불러오기
return arr.item_code == itemCode;
});
if (mounted) {
mounting = true;
return false;
}
});
if (mounting) {
return res
.status(400)
.json({ errorMessage: "아이템이 장착되어 있어 수정할 수 없습니다." });
}
item.item_name = item_name;
item.item_stat = item_stat;
await item.save();
return res.status(200).json({ completeMessage: "수정이 완료되었습니다." });
});
export default router;
mounting.schema.js: 마운팅(장착) 컬렉션의 구조 및 문서의 데이터 타입(스키마)를 짜놓은 파일
import mongoose from "mongoose";
const mountingSchema = new mongoose.Schema({
character_id: {
type: Number,
required: true,
},
mountedItems: {
type: Array,
required: true,
},
});
export default mongoose.model("mountings", mountingSchema);
mounting.router.js: 마운팅 조회(목록/상세), 장착, 탈착 api가 작성된 라우터 구현
import express from "express";
import Mountings from "../schemas/mounting.schema.js";
import Items from "../schemas/items.schema.js";
import Characters from "../schemas/characters.schema.js";
const router = express.Router();
/* 장착된 아이템 목록 조회 api */
router.get("/mounting/", async (req, res, next) => {
const mountingList = await Mountings.find().sort("+character_id").exec();
return res.status(200).json({ mountingList });
});
/* 장착된 아이템 상세 조회 api */
router.get("/mounting/:characterId", async (req, res, next) => {
const characterId = req.params.characterId; // parameter 가져오기
const mounting = await Mountings.findOne({
// character_id가 같은 객체 찾기
character_id: characterId,
}).exec();
if (!mounting) {
// 없으면 에러 메시지
return res.status(404).json({ errorMessage: "조회할 캐릭터가 없습니다." });
}
const { mountedItems } = mounting; // 출력할 정보들 구조 분해 할당
mountedItems.sort((a, b) => {
// item_code로 정렬
if (a.item_code > b.item_code) return 1;
else if (a.item_code < b.item_code) return -1;
return 0;
});
return res.status(200).json({ mountedItems });
});
/* 아이템 장착 api */
router.patch("/mounting/:characterId", async (req, res, next) => {
const characterId = req.params.characterId; // parameter 가져오기
const mounting = await Mountings.findOne({
// character_id가 같은 객체 찾기 (Mountings 모듈에서)
character_id: characterId,
}).exec();
if (!mounting) {
// 없으면 에러 메시지
return res
.status(404)
.json({ errorMessage: "아이템을 장착할 캐릭터가 없습니다." });
}
const { item_code } = req.body; // 장착할 아이템 갸져오기
const item = await Items.findOne({
// item_code가 같은 객체 찾기
item_code: item_code,
}).exec();
if (!item) {
// 없으면 에러 메시지
return res
.status(404)
.json({ errorMessage: "장착할 아이템이 존재하지 않습니다." });
}
const mountedItems = mounting.mountedItems;
const sameItem = mountedItems.find(function (obj) {
return obj.item_code == item_code;
});
if (sameItem) {
return res
.status(400)
.json({ errorMessage: "아이템이 이미 장착되어 있습니다." });
}
const { item_name, item_stat } = item;
mounting.mountedItems.push({ item_code, item_name }); // 아이템 장착하기
await mounting.save();
const character = await Characters.findOne({
// character_id가 같은 객체 찾기 (캐릭터 모듈에서)
character_id: characterId,
}).exec();
if (item_stat.health) character.health += item_stat.health;
if (item_stat.power) character.power += item_stat.power;
character.save(); // 캐릭터에 아이템을 장착하면서 스탯이 올라감
return res
.status(200)
.json({ completeMessage: "아이템 장착이 완료되었습니다." });
});
/* 아이템 탈착 api */
router.patch("/detachable/:characterId", async (req, res, next) => {
const characterId = req.params.characterId; // parameter 가져오기
const mounting = await Mountings.findOne({
// character_id가 같은 객체 찾기
character_id: characterId,
}).exec();
if (!mounting) {
// 없으면 에러 메시지
return res
.status(404)
.json({ errorMessage: "아이템을 탈착할 캐릭터가 없습니다." });
}
const { item_code } = req.body; // 장착할 아이템 갸져오기
const item = await Items.findOne({
// item_code가 같은 객체 찾기
item_code: item_code,
}).exec();
if (!item) {
// 없으면 에러 메시지
return res
.status(404)
.json({ errorMessage: "탈착할 아이템이 존재하지 않습니다." });
}
const mountedItems = mounting.mountedItems;
const sameItem = mountedItems.find(function (obj) {
// 장착되어있는 item_code 아이템 불러오기
return obj.item_code == item_code;
});
if (!sameItem) {
// item_code와 일치하는 item이 없을 때
const { item_name } = item;
return res
.status(400)
.json({ errorMessage: `캐릭터에게 "${item_name}"이 존재하지 않습니다.` });
}
mountedItems.forEach((item, index) => {
// item_code와 일치하는 아이템 삭제
if (item.item_code === item_code) {
mountedItems.splice(index, 1);
return false;
}
});
mounting.mountedItems = mountedItems;
await mounting.save();
const character = await Characters.findOne({
// character_id가 같은 객체 찾기 (캐릭터 모듈에서)
character_id: characterId,
}).exec();
const { item_stat } = item;
if (item_stat.health) character.health -= item_stat.health;
if (item_stat.power) character.power -= item_stat.power;
character.save(); // 캐릭터에 아이템을 탈착하면서 스탯이 내려감
return res
.status(200)
.json({ completeMessage: "아이템 탈착이 완료되었습니다." });
});
export default router;
막혔던 부분
id는 자동으로 생성되는 건 알았지만 __v는 뭐지?
.env 사용법
1. yarn add dotenv
2. dotenv.config();
3. process.env.(key명) 으로 .env에서 값을 불러올 수 있음
- .gitignore에 추가하여 git엔 안 올라가게 해야함
git clone 해서 서버 열 때 .env 파일은?
- env에 key로만 구성된 .env.example을 만들어 함께 업로드하여 clone한 뒤 .env로 수정하여 사용
pm2 서버 여는 법
1. git Bash에서 SSH로 접속하기
- ssh -i /c/Users/STORY/Downloads/(SSH이름) ubuntu@(public ip 주소)로 들어가 private ip 주소와 일치하는지 확인
2. git clone 해서 프로젝트 가져오고 프로젝트 폴더로 이동
3. .env, node_modules 등 필요한 것들 생성
- .env.example 파일 수정
- sudo nmp install -g yarn: 전역으로 yarn 설치
- yarn: yarn.lock
4. pm2로 서버 열기
아래 GitHub repository의 readme에서 프로젝트 API 명세와 테스트 결과를 볼 수 있음
Project Github Link: https://github.com/znfnfns0365/Game-Manager-with-node.js
'내일배움캠프 > Project' 카테고리의 다른 글
[Project] Insomnia에서 팀원들과 협업하기 (0) | 2024.06.05 |
---|---|
[Project] 협업 시 유용한 사이트 및 정보 (0) | 2024.05.31 |
[Project] 고도화된 게임 아이템 시뮬레이터 서비스 (개인 과제) (1) | 2024.05.29 |
[Project] 영화 검색 사이트 심화 (팀 프로젝트) (0) | 2024.05.12 |
[Project] 영화 검색 사이트 제작 (개인 과제) (0) | 2024.05.02 |