오늘의 Troubleshooting

아이템 시뮬레이터 과제

API 명세서 작성

  • 조장님이 API 명세서를 작성하며 과제를 진행하는 것이 설명/관리 하기 쉬울 것 같다하셔서 명세서를 작성하게 되었다!

API 명세서

  • 명세서를 작성하는 동안 http 응답코드에 대해 알아보게 되었다.

나무위키 - 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 }
})

개선점 분석

  • 일단 시작을 하는 행동은 좋으나.. 생각을 정리 하며 기록하는 습관을 가지자

Reference

programmers
스파르타코딩클럽