오늘의 Troubleshooting
아이템 시뮬레이터 과제
API 명세서 작성
- 조장님이 API 명세서를 작성하며 과제를 진행하는 것이 설명/관리 하기 쉬울 것 같다하셔서 명세서를 작성하게 되었다!
- 명세서를 작성하는 동안 http 응답코드에 대해 알아보게 되었다.
아이템 생성 API
- 아이템 생성 시, 필요한 인수를 받지 못하면 그에 관한 에러를 반환해주게 설계
//입력값 확인
const {item_code, item_name, item_stat, item_price} = req.body;
//이름 유효성 검사 확인
if (!item_name) return res
.status(400)
.json({ errorMessage: "아이템 이름 <item_name>을 입력해주세요" })
const isExitItem = await prisma.itemTable.findFirst({ where: {name: item_name}})
if (isExitItem) return res
.status(409)
.json({ errorMessage: "이미 존재하는 아이템입니다" })
//능력치 입력 여부 확인
if (!item_stat) {
return res
.status(400)
.json({ errorMessage: "능력치 <item_stat: {\"스탯이름\":값}> 을 입력해주세요" })
}
//코드 중복여부 확인
if (item_code) {
const isExitCode = await prisma.itemTable.findFirst({ where: { itemCode: item_code } })
if (isExitCode) return res
.status(409)
.json({ errorMessage: "아이템 코드가 동일한 아이템이 있습니다" })
}
- 아이템의 능력치 부분은 원하는 능력치만 설정할 수 있도록 설계
//아이템 생성용 변수
let item
// 스탯 확인 및 없을 시 초기화
let { health, power } = item_stat
health = health ? +health : 0
power = power ? +power : 0
// item_code 존재 여부에 따라 기본값[default(autoincrement())] 적용
if (item_code) {
item = await prisma.itemTable.create({
data: {
itemCode: +item_code,
name: item_name,
health: health,
power: power,
price: +item_price
}
})
} else {
item = await prisma.itemTable.create({
data: {
name: item_name,
health: health,
power: power,
price: +item_price
}
})
}
아이템 구매 API
- 아이템 구매 시, 종류별 동시 구매 및 단일 구매 모두 가능하게 하기 위해 입력값이 배열인지 확인하는 과정을 추가했다.
const perchase = req.body;
if (Array.isArray(perchase)) {}
-
추가로 inventory에 아이템을 저장하려는데.. 저장 형식을 {아이템코드: 수량} 으로 하는게 이상적일 것 같아 inventory의 구조를 바꾸게 되었다!
-
모델에서 N:M 관계를 위해 Containier를 생성해 줬다.
(equipment 와 inventory는 동일관계)=> 아닌 것 같아서 json type coulmn을 생성해 주었다.
model Inventory {
inventoryId Int @id @default(autoincrement()) @map("inventoryId")
charId Int @unique @map("charId")
items Json @map("items")
character Characters @relation(fields: [charId], references: [charId], onDelete: Cascade)
@@map("Inventory")
}
model Equipment {
equipmentId Int @id @default(autoincrement()) @map("equipmentId")
charId Int @unique @map("charId")
items Json @map("items")
character Characters @relation(fields: [charId], references: [charId], onDelete: Cascade)
@@map("Equipment")
}
-
아이템 종류별 동시 구매 시, 가지고있는 돈이 부족한지 확인하기 위해 reduce 함수를 이용했다!
-
중간에 조장님의 조언으로 입력값이 숫자인지도 판별하는 기능을 추가하였다!
// 비용 총합 구하기
const sum = 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> 를 숫자로 입력해주세요" })
// 배열마다 값 추가
acc += item.price * +cur.count
},0)
- 트랜잭션을 이용하여 구매 기능의 일관성을 유지했다!
( 여러 데이터베이스를 동시에 업데이트 해줄 때, 일부 데이터베이스만 업데이트 되는걸 방지 )
// 입력값을 이용해 구매할 아이템 찾기
const item = await prisma.itemTable.findFirst({ where: { itemCode: +perchase.item_code } })
// 구매( 돈 지불 + 아이템 인벤토리 이동) 트랜잭션
const { updateCharacter, inventory } = await prisma.$transaction(async (tx) => {
// 가격 지불 (decrement 연산을 이용하여 값을 현재값에서 뺌 <> increment)
const updateCharacter = await tx.characters.update({
data: {
money: { decrement: sum }
},
where: { charId: +charId }
})
// 아이템 저장 (기존 값 유지 시키기)
const items = await prisma.inventory.findFirst({ where: { charId: +charId } })
let json
// 저장된 기존 값이 있을 시
if (items.items) {
let isExist = false;
// 아이템코드가 중복이면 합침 (filter 메서드 사용)
json = items.items.filter((val) => {
if (val.code === item.itemCode) {
val.amount += +perchase.count
isExist = true;
}
return true
})
// 아이템코드가 중복이 아닐 시 추가
if (!isExist) {
json = [...json, { code: item.itemCode, amount: +perchase.count }]
}
// 저장된 기존 값이 없을 시
} else {
json = [{ code: item.itemCode, amount: +perchase.count }];
}
// 최종 json 값 저장
const inventory = await tx.inventory.update({
data: {
items: json
},
where: { charId: +charId }
})
//사용자 출력용 값 저장
resJson = [...resJson, {
"message": `${item.name}(을)를 ${+perchase.count}만큼 구매에 성공하였습니다.`,
"total_amount": sum,
"balance": updateCharacter.money
}]
return { updateCharacter, inventory }
})
개선점 분석
- 일단 시작을 하는 행동은 좋으나.. 생각을 정리 하며 기록하는 습관을 가지자