오늘의 삽질
RogueLike TextGame
서버 연결
- node.js 강의에 있던 MongoDB를 이용하여 무기들이나, 점수 랭킹 등을 저장하고 불러오는 기능을 구현해볼 것이다!
-
MongoDb_Atlas(Cloud Service) 가입
-
구글/깃허브를 통해 로그인!
-
New project를 눌러 M0(free)를 선택하고, Region(나라) 는 서울로 설정한다음 이름을 정해 저장해준다!
-
Database Access 에서 ADD NEW DATABASE USER 로 이름과 비밀번호(나중에 사용할 예정이니 쉽게 해두기!)를 설정하고 만들어준다!
(Built-in Role 은 Atlas admin 으로!)
=> 마지막으로 Network Access 에 들어가 모든 사용자가 접속할 수 있도록 IP ADDRESS는 0.0.0.0 으로 설정해준다!
-
-
mongoose Package 설치
- yarn(npm) 을 이용하여 mongoose 를 프로젝트에 설치해준다! (VS Code 터미널)
yarn add mongoose
- 내 MongoDB와 연결하기 위한 함수를 만들어준다!
import mongoose from 'mongoose'; const connect = () => { mongoose .connect( 'mongodb+srv://<db_username>:<db_password>@express-mongo.p1x5z.mongodb.net/?retryWrites=true&w=majority&appName=express-mongo', { dbName: 'roguelike', // 데이터베이스명 입력!(맘대루) }, ) //에러 탐지1! .catch((err) => console.log(err)) .then(() => console.log('데이터 불러오기 성공')); }; //에러 탐지2! mongoose.connection.on('error', (err) => { console.error('데이터 불러오기 실패', err); }); export default connect;
=> 여기서 mongooseconnect() 의 첫번째 인수를 얻기 위해 아래 사진과 같이 Overview 에서 Connect를 누르고 Drivers를 선택한다!
=> Driver는 현재 프로젝트에 쓰고있는 Node.js를 선택하고 버전도 맞춰 아래에 있는 코드를 복사하여
에 유저 생성 때 쓴 비밀번호를 입력해주고 넣으면 된다! (`<>` <= 이것도 지워요..) -
데이터 저장!
-
mongoose를 이용해서 데이터 저장 형식을 지정해준다!
import mongoose from "mongoose"; const weaponSchema = new mongoose.Schema({ // 이름 name: { type: String, required: true, uinque: true, }, //기본 데미지 damage: { type: Number, required: true, }, //회복량 heal: { type: Number, required: true, }, //무기 스탯 영향 0: 이해력 / 1: d-day / 2: 최대 정신력 / 3: -이해력 type: { type: Number, required: true, }, //등급 rating: { type: String, required: true, }) export default mongoose.model("WeaponSch", weaponSchema)
- 여기서 type은 들어올 값을 지정해주는 것이고, required는 이 값이 무조건 들어와야하는지 여부를, uinque는 중복값이 허용되지 않게 하는 설정이다!
=> 추가로 default를 이용해 값이 없을 시 기본 값을 설정해 줄 수 있다!
-
mongoDB 서버에 연결 후, 데이터를 올려 볼 것이다!
//서버 연결 함수(위에) import connect from './storage/index.js'; //올릴 데이터 형식(바로 위) import WeaponSch from './storage/weapon-schemas.js'; //올릴 데이터를 저장한 곳 import Weapons from './storage/weapons.js'; //서버 연결! connect(); //데이터를 모두 돌면서 WeaponSch 형식으로 인스턴스를 만든다 Weapons.forEach(async (val) => { const newWeapon = new WeaponSch({ name: val.name, damage: val.damage, heal: val.heal, type: val.type, rating: val.rating, }) // 만든 WeaponSch 인스턴스를 서버에 저장! await newWeapon.save() })
-
값 확인하기
- Atlas 사이트(MongoDB Cloud Service) 에서 Clusters 에 들어가 Browse Collections 를 누른다!
=> 여기서 보고 싶은 database 명을 누르고 데이터 형식을 찾아 값을 확인한다!
=> 잘 들어온 걸 확인할 수 있었다!
-
-
데이터 불러오기!
- 저장은 한번만 해두면 DB에서 계속해서 꺼내오면 된다! 그렇다면 어떻게 꺼내오면 될까?
-
mongoose 메서드 모델명(형식).find()를 통해 전체 값을 가져오기!
Model.find(conditions, [projection], [options], [callback]) // conditions = 문서와 일치시키기 위한 조건 // [] = 선택사항 // projection = 제외목록 지정 // options = 정렬 또는 제한 조건
=> 데이터를
-
모델명(형식).findone() 으로 데이터 하나만 가져오기!
Model.findOne(query, [projection], [options], [callback]) // query = 조건 // [] = 선택사항 // projection = 제외목록 지정 // options = 정렬 또는 제한 조건
=> 이런 방식으로 처음 화면에서 weapons를 가져와 쓸 수 있게 하면 되게따!
//weapons.js 무기들 가져오기 import Weapon from '../class/weapon.js'; import connect from './connect.js'; import weaponSch from './weapon-schemas.js'; connect() const Weapons = []; const temp = await weaponSch.find().exec(); temp.forEach((val) => { Weapons.push(new Weapon( val.name, val.damage, val.heal, val.rating )) })
관리자 모드 만들기
- 무기들을 새로 만들거나 모든 무기를 보려면, 이 데이터들을 읽고/쓰기 위한 코드들을 관리자인 나만 쓸 수 있게 만들려 한다!
-
admin 파일 만들기 + DB에 password Collection 연동
- 관리자 모드를 이벤트 파일에 만들어주고, 이동 시, 비밀번호를 물어봐 데이터베이스에서 가져온 값과 비교해 틀릴경우 메인화면으로 돌아가게 설계를 하였다!
const admin = async (password) => { //데이터 저장공간에서 가져오기 const passwordCk = await mongoose.model('password', PSWDSchema).findOne({ name: "admin" }).exec() // 값이 맞는지 확인 후 아닐경우 return start로 메인화면 이동 if (passwordCk.password !== password) { console.log(chalk.redBright('오류! 비밀번호가 틀렸습니다!!')) console.log(chalk.yellowBright('메인화면으로 이동합니다!')); readlineSync.question('Back<<') return start() } }
- 관리자 모드에서 할 수 있는 일로 무기 추가하기 / 무기 전체 확인 / 이름 찾아 확인 기능을 넣을 예정이다!
-
무기 List 확인
- 등급별로 정렬해서 보이게 하기 위해 ratingToNum 함수를 따로 만들었다!
//무기 전부 조회 const WeaponList = (logs) => { Weapons.sort((a, b) => { const x = ratingToNum(a) const y = ratingToNum(b) return y - x }).forEach((val, idx) => { if (idx === 0) { logs.push( chalk.magentaBright( `=========== ${val.rating}급 ===========`, ), ); } else if (val.rating !== Weapons[(idx - 1)].rating) { logs.push( chalk.magentaBright( `=========== ${val.rating}급 ===========`, ), ); } logs.push( chalk.yellowBright( `| 등급 : ${val.rating} | 몰입도 : ${String(val.damage).padStart(3, " ")} Page | 수면효과 : ${String(val.heal).padStart(3, " ")} | 이름 : ${val.name}`, ), ); }) } // 정렬용 비교 함수 const ratingToNum = (x) => { switch (x.rating) { case "H": return 7 case "S": return 6 case "A": return 5 case "B": return 4 case "C": return 3 case "D": return 2 case "E": return 1 } }
-
무기 이름을 받아 List에서 똑같은 이름을 찾아 반환해주는 함수를 만들어야겠다!
- 간단하게 금방 만들었는데… 작동이 안됀다?!
=> 콘솔에 직접 값을 찍어봤는데 입력받은 한글이 깨져서 나온 것이다!
//무기 하나 조회 const SearchingWeapon = (name, logs) => { const val = Weapons.find((e) => { return e.name === name }) if (val) { logs.push( chalk.yellowBright( `| 등급 : ${val.rating} | 몰입도 : ${String(val.damage).padStart(3, " ")} Page | 수면효과 : ${String(val.heal).padStart(3, " ")} | 이름 : ${val.name}`, ), ); } else { logs.push(chalk.redBright('입력하신 이름을 가진 무기를 찾을 수 없습니다!')); } }
- 한글 글자 깨짐 현상 고치기
=> 알아본 바
chcp 65001
라는 터미널 명령어를 통해 코드 페이지를 UTF-8로 변경해서 유니코드 문자(한글 포함)를 지원하게 만들어 준다고 한다!(1회용)=> 명령어를 게임 시작전마다 실행하게 만들어 주기 위해,
터미널 명령어를 코딩으로 입력시키는 함수를 찾게 되었다!import { execSync } from 'child_process'; //터미널에 직접 실행 execSync('chcp 65001')
=> 게임 시작부터 실행하게 만들어 주니, 생각했던 기능들이 잘 작동한다!
-
무기 추가기능 넣기!
- 위에 데이터를 저장하는 기능을 이용해 새로운 무기를 만들 수 있도록 할 것이다!
console.log(chalk.yellowBright( `기입 사항들을 (,) 기준으로 차례대로 입력해주세요!`, )) newWp = readlineSync.question('무기 정보 기입::').split(","); console.log(chalk.yellowBright( `입력된 정보 ${newWp}`, )) const newWpData = new WeaponSch({ name: newWp[0], damage: Number(newWp[1]), heal: Number(newWp[2]), type: Number(newWp[3]), rating: newWp[4], }) await newWpData.save() .catch((err) => console.log(err)) .then(logs.push(chalk.greenBright('정상적으로 저장되었습니다!')))
=> 잘 작동한다!
-
메인 화면에 연결하기
- 메인 화면에 선택지를 만들어 주고, 거기에 간단하게 연결하였다!
case '3': // 무기 정보 확인 및 추가 및 삭제 console.log(chalk.redBright('관리자 모드 진입을 위해 비밀번호를 입력해주세요')); //관리자 모드 함수 admin(readlineSync.question('비밀번호:')); break;
-
단일로 검색한 무기를 수정하거나 삭제하는 기능도 추가하자!
- 삭제 기능 구현! (+ 무기값 update 기능도 구현)
await WeaponSch.deleteOne({ name: name }) .then(async () => { logs.push( chalk.redBright( `무기를 삭제했습니다!`, ), ); await update() }) //무기 업데이트 함수 const update = async () => { //초기화 Weapons.splice(0, Weapons.length); const temp = await WeaponSch.find().exec() //업데이트 temp.forEach((val) => { Weapons.push(new Weapon( val.name, val.damage, val.heal, val.rating, val.type, )) }) }
- 무기 수정 구현
const valueName = readlineSync.question('바꿀 정보를 영어로 입력해주세요! '); console.log(chalk.yellowBright(`${valueName}을 선택하셨습니다.`)) const value = readlineSync.question('값을 입력해주세요!'); console.log(chalk.yellowBright(`${value}를 입력 하셨습니다.`)) const weaponFound = await WeaponSch.findOne({ name: name }).exec() switch (valueName) { case "name": weaponFound.name = value break; case "rating": weaponFound.rating = value break; case "damage": weaponFound.damage = Number(value) break; case "heal": weaponFound.heal = Number(value) break; case "type": weaponFound.type = Number(value) break; default: console.log("오류입니다") break; } //저장 await weaponFound.save(); await update()
개선점 분석
-
내일 할 일을 적어 두어서 다음 날 확인해가며 시작하는게 많이 편했다!
-
내일은 플레이어 랭킹 시스템 구현 + 리팩토링/밸런싱 을 마지막으로 이번 과제를 마무리할 것 같다!