오늘의 Troubleshooting
아이템 시뮬레이터 과제
아이템 판매 기능 구현
-
구매 기능을 구현했기에 판매는 그렇게 어렵지 않았다.
-
인벤토리에서 아이템이 수량만큼 존재하는지 확인해주는 기능을 추가로 구현하였다.
// 판매 할 아이템이 없을 시
const { charId } = req.params;
const perchase = req.body;
const item = await prisma.itemTable.findFirst({ where: { itemCode: +perchase.item_code } })
const items = await prisma.inventory.findFirst({ where: { charId: +charId } })
if (!items.items || items.items.find((val) => val.code === +perchase.item_code).amount < +perchase.count) return res
.status(400)
.json({ errorMessage: "인벤토리에 판매할 아이템이 부족합니다" })
- 돈을 지급하기위해 decrement 부분을 increment로 변경하며 지급될 돈을 다르게 계산해준다! (원래 가격의 60%)
//트랜잭션 내부이기에 prisma 대신 tx 를 사용하였다!
const amount = Math.round(item.price * 0.6) * +perchase.count;
const updateCharacter = await tx.characters.update({
data: {
money: { increment: amount }
},
where: { charId: +charId }
})
- 아이템을 삭제하는 기능을 구현하였다
const json = items.items.filter((val) => {
if (val.code === item.itemCode) {
val.amount -= +perchase.count
// 만약 수량이 0이되면 아이템정보를 삭제
if (val.amount <= 0) return false
}
return true
})
// 최종 저장
const inventory = await tx.inventory.update({
data: {
items: json
},
where: { charId: +charId }
})
-
다중 구매에 총합을 구하는 도중 reduce 함수에서 return res 가 다르게 작동되는오류가 발견되었다!!
=> 원래는 return res 를 통해 유효성 검사에 실패하면 API를 마치게 설계
=> 그러나 return으로 reduce만 빠져나와 전체 API가 멈추지 않아 구문에 오류가 생겨 서버다운!
-
현재는 try catch를 이용해 서버자체는 다운되지 않게 하였으나..
=> 나중엔(리팩토링 과정) 미들웨어를 이용하여 유효성 검사를 실행하게 만들어야 겠다고 생각하게 되었다.
인벤토리 조회
- 인벤토리의 아이템코드와 수량을 가지고 item들을 검색하여 출력값으로 바꿔주기 위해 map 메서드를 이용했다!
// 출력값 조회
const resJson = items.items.map( async (val) => {
const item = await prisma.itemTable.findFirst({ where : {itemCode: +val.code}})
return ({
"item_code" : item.itemCode,
"item_name" : item.name,
"count": +val.amount
})
})
console.log(reJson)
=> 근데 JSON 형태의 파일이 제대로 출력이 안됀다!
- console.log를 이용해 확인해보니 리턴값은 제대로 나오는데 resJson에 return 될 때 다른 작용이 일어나는 것 같다!
- 위의 포스팅을 참고로 요약을 하자면..
-
map의 인수 비동기 콜백함수(async function)로 인해 Promise 자체가 계속해서 반환되는데..
-
보통의 Promise들은 변수에 대입하는 과정에서 await으로 실행이 되지만 map/reduce의 경우 콜백함수가 반복되며 반환된 값을 모아 최종 반환하기에.. 1차 반환된 Promise 들이 실행되지 않은 채 최종 반환에 모이는 것 같다.
-
비동기 콜백함수의 return 값은 반환된 Promise가 실행되었을 때 출력되기에 각각의 Promise들을 실행시켜(fulfill) 줘야 한다!
=> await Promise.all 을 이용해 반환된 map의 모든 Promise 를 실행하도록 수정하여 해결!
const resJson = await Promise.all(items.items.map( async (val) => {
const item = await prisma.itemTable.findFirst({ where : {itemCode: +val.code}})
let json = {}
//보기 좋게 리팩토링
json["item_code"] = item.itemCode
json["item_name"] = item.name
json["count"] = +val.amount
return json
}))
=> 추가로 아이템 구매 API의 총비용 확인의 reduce도 수정 해주었다!
// 비동기 콜백함수를 실행 시켜주기위해 await 사용
const sum = await perchase.reduce(async (acc, cur) => {
// 아이템 코드 미입력(+숫자가 아닐 시) 시,
if (!(cur.item_code && Number.isInteger(+cur.item_code))) return res
.status(400)
.json({ errorMessage:"아이템코드 <item_code> 를 숫자로 입력해주세요" })
// 아이템이 없을 시
const item = await prisma.itemTable.findFirst({ where: {itemCode: +cur.item_code}})
if (!item) return res
.status(404)
.json({ errorMessage: `<item_code> ${+cur.item_code}번의 아이템이 존재하지 않습니다` })
// 수량 미 기입 시
if (!(cur.count && Number.isInteger(+cur.count))) return res
.status(400)
.json({ errorMessage: "구매할 수량 <count> 를 숫자로 입력해주세요" })
// return 되는 acc가 promise 형식이기에 await을 이용하여 실행시킨 값과 합침
return await acc + item.price * +cur.count
},0)
장비 장착/탈착
- 아이템 장착/탈착 시 인벤토리와 장비창을 새롭게 업데이트 시키기 위해 Json 객체 변수를 만들어 값을 조절했다!
const {charId} = req.params
const equipItem = req.body;
// 선택한 아이템
const item = await prisma.itemTable.findFirst({ where: { itemCode: +equipItem.item_code } })
// 인벤토리
const items = await prisma.inventory.findFirst({ where: { charId: +charId } })
// 장비창
const equipItems = await prisma.equipment.findFirst({ where: { charId: +charId } })
// 아이템 삭제 (기존 값에서 제외)
const json = items.items.filter((val) => {
if (val.code === item.itemCode) {
val.amount -= 1
if (val.amount <= 0) return false
}
return true
})
let equip = {};
// 장착한 장비가 없을 시,
if (!equipItems.items) {
equip = [{ code: item.itemCode }];
// 장착한 장비가 있을 때
} else {
equip = [...equipItems.items, { code: item.itemCode }]
}
- 이를 update를 이용해 직접 장비창과 인벤토리의 items(Type: Json)에 저장해주었다
// 인벤토리 삭제 적용
const inventory = await tx.inventory.update({
data: {
items: json
},
where: { charId: +charId }
})
// 장비창 아이템 추가 적용
const equipment = await tx.equipment.update({
data: {
items: equip
},
where: { charId: +charId }
})
- 추가로 캐릭터 스탯은 increment와 decrement를 사용했다.
// 캐릭터 스탯적용
const updateCharacter = await tx.characters.update({
data: {
health: { increment: item.health },
power: { increment: item.power }
},
where: { charId: +charId }
})
돈 획득
-
기존의 로직들 중 필요한 부분만 선택하여 금방 만들었다
-
돈 획득에 랜덤성을 조금 부여하고 싶어서 Math.random()을 사용했다
const amonut = Math.round(Math.random() * 10) * 100
const updateCharacter = await prisma.characters.update({
data: {
money: {increment: amonut}
},
where: { charId: +charId }
})
개선점 분석
-
커리어 코칭 면담으로 앞으로 어떤 회사를 목표로 할지, 회사에 대해 알아보고 여러 게임들도 경험해봐야 한다는걸 배웠다.
-
오늘 완성한 과제들을 리팩토링하는 과정에서 유효성 평가/오류 처리 미들웨어를 만들어볼 예정이다.
-
리팩토링을 최대한 마치고 README 작성해 두어야 겠다.
-
과제를 마친 이후 또는 저녁식사 이후 TIL을 작성해가며 챌린지 반 자료를 봐야겠다.
지식창고
알고리즘 코드 카타
새로운 지식
- 알고리즘 코드 카타 시에 프로그램 최적화를 위해(시간 초과 실패) 약수를 효율적으로 구하는데 이러한 법칙을 사용할 수 있음을 알게 되었다
-
N의 약수 중 하나가 m이라고 했을 때, 다른 약수는 N/m이 되므로 하나의 약수를 알면 다른 하나의 존재가 보장이 된다.
-
약수를 √N(N의 제곱근)까지 구하면 총 절반의 개수를 구할 수 있다
-
약수를 √N까지 구한 뒤 N의 약수를 2번 카운트 해주고, 약수가 √N(제곱근) 인 경우에만 1개로 카운트 해주면 된다.
기사단원의 무기
- 문제
숫자나라 기사단의 각 기사에게는 1번부터 number까지 번호가 지정되어 있습니다.
기사들은 무기점에서 무기를 구매하려고 합니다.
각 기사는 자신의 기사 번호의 약수 개수에 해당하는 공격력을 가진 무기를 구매하려 합니다. 단, 이웃나라와의 협약에 의해 공격력의 제한수치를 정하고, 제한수치보다 큰 공격력을 가진 무기를 구매해야 하는 기사는 협약기관에서 정한 공격력을 가지는 무기를 구매해야 합니다.
무기를 만들 때, 무기의 공격력 1당 1kg의 철이 필요합니다.
그래서 무기점에서 무기를 모두 만들기 위해 필요한 철의 무게를 미리 계산하려 합니다.
기사단원의 수를 나타내는 정수 number와 이웃나라와 협약으로 정해진 공격력의 제한수치를 나타내는 정수 limit와 제한수치를 초과한 기사가 사용할 무기의 공격력을 나타내는 정수 power가 주어졌을 때,
무기점의 주인이 무기를 모두 만들기 위해 필요한 철의 무게를 return 하는 solution 함수를 완성하시오.
-
조건
-
1 ≤ number ≤ 100,000
-
2 ≤ limit ≤ 100
-
1 ≤ power ≤ limit
-
function solution(number, limit, power) {
let answer = 0;
let weapon = [];
for (let i = 1;i <= number;i++) {
// 제곱근 내에서만 약수를 구한 뒤, 약수로 수를 나누면 또다른 약수가 나온다
for (let j = 1;j <= Math.sqrt(i);j++) {
if (i % j === 0) {
if (!weapon[i-1]) weapon.push(2)
else if (i / j === j) weapon[i-1] += 1;
else weapon[i-1] += 2;
}
if (weapon[i-1] > limit) break
}
}
// -1을 해준 이유는 1의 경우 약수가 자신 혼자 뿐임에도 초기값 2가 들어가기 때문
answer = weapon.reduce((acc, cur) => {
if (cur > limit) return acc += power
else return acc += cur
},0) - 1
return answer;
}
- 다른 코드 분석
function solution(number, limit, power) {
var answer = 0;
for (let n = 1; n <= number; n++)
{
// 하나의 기사(number)마다 초기화
let count = 0;
// [N의 제곱근] 대신 [N의 약수 J의 제곱]을 이용해 계산을 보기쉽게 만들었다.
// [ j <= Math.sprt(n) ] >>> [j * j <= n ]
for (let j = 1; j * j <= n; j++)
{
// n의 제곱근의 경우 한 번만 카운트하게 설정
if (j * j == n) count++;
else if (n % j == 0) count += 2;
}
// answer 에 들어갈 값 계산 후 더하기
if (count > limit) count = power;
answer += count;
}
return answer;
}