Facts

  • 프로젝트의 리팩토링을 전반적으로 맡으면서 여러 아이디어나 최적화 방법을 볼 수 있엇다.

  • 이번에 주어진 조건들을 만족시키기 위해 여러 prisma의 문법이나 메서드를 배울 수 있었다.

  • 코드를 팀원분들에게 설명을 해주며 프로젝트에 대한 이해도가 올라갔다.

Feelings

  • 팀원분들과의 소통이 원활하여 프로젝트 진행이 순조로웠고, 그래서 기분좋게 마무리를 할 수 있었다.

  • 리팩토링을 해가며 코드들을 이해하고 새로운 알고리즘을 짜는 기분이여서 뿌듯했다.

Finding

  • 기획/발표와 관련된 일을 대부분 팀장님께 일임이라는 형태로 업무를 많이 드린 것 같아 죄송하다.

  • 이번 경험을 통해 커뮤니케이션이 일의 효율에 얼마나 관여하는지를 다시금 깨닫게되었다.

Future

  • 기록 습관을 들일 수 있도록 블로그 정리를 열심히 해야겠다.

오늘의 Troubleshooting

쁘띠 TFT 프로젝트

챔피언 뽑기 리팩토링

  • 챔피언을 뽑았을 때, 이를 가지고 있는 챔피언인지 확인하고, 없으면 생성하는 로직에 upsert()라는 메서드를 적용하려 했다

=> upsert()는 where 에서 unique 속성이 있는 컬럼을 이용해 찾아보고 없으면 생성해주는 메서드다.

  • 그러나.. myAgents 테이블엔 myAgentKey 라는 @id 빼곤 unique한 키가 없어 효율적으로 검색할 방법이 없다!

  • 이로인해 깨닫게 된게 @id(PRIMARY KEY) 를 정할 때, N:M 테이블은 복합키를 이용해야 편하다는 것이었다..

=> 후에 합의하여 테이블을 바꿔 수정해주었다

// 기존의 테이블
model MyAgents {
  myAgentKey Int      @id  @default(autoincrement()) @map("my_agent_key")
  userKey    Int      @map("user_key")
  agentKey   Int      @map("agent_key")
  name       String   @map("name")
  level      Int      @default(1) @map("level")
  class      Int      @default(0) @map("class")
  count      Int      @default(1) @map("count")
  obtainedAt DateTime @updatedAt @map("obtained_at")
  agent      Agents   @relation(fields: [agentKey], references: [agentKey], onDelete: Cascade)
  user       Users    @relation(fields: [userKey], references: [userKey], onDelete: Cascade)

  @@map("MyAgents")
}
// 수정된 테이블
model MyAgents {
  userKey    Int      @map("user_key")
  agentKey   Int      @map("agent_key")
  name       String   @map("name")
  level      Int      @default(1) @map("level")
  class      Int      @default(0) @map("class")
  count      Int      @default(1) @map("count")
  obtainedAt DateTime @updatedAt @map("obtained_at")
  agent      Agents   @relation(fields: [agentKey], references: [agentKey], onDelete: Cascade)
  user       Users    @relation(fields: [userKey], references: [userKey], onDelete: Cascade)

  @@id([userKey, agentKey])
  @@map("MyAgents")
}
  • 기존의 myAgent 생성/업데이트 코드
const existingAgent = await tx.myAgents.findFirst({
    where: {
        userKey: +userKey,
        agentKey,
        },
    });
if (existingAgent) {
    await tx.myAgents.update({
        where: { myAgentKey: existingAgent.myAgentKey },
        data: {
            count: { increment: 1 },
        },
    });
} else {
    await tx.myAgents.create({
        data: {
            userKey: +userKey,
            agentKey,
            count: 1,
            level: 1,
            class: 0,
            name,
        },
    });
}
  • 리팩토링한 코드
await tx.myAgents.upsert({
    where: { userKey_agentKey : {userKey, agentKey}},
    update: {
    count: { increment: 1 }
    },
    create:{
    userKey: +userKey,
    agentKey,
    count: 1,
    level: 1,
    class: 0,
    name,
    }
})
  • 추가로 myAgentKey 가 사라져서 바꾸게 된 곳
// 관계성을 이용해 upadte를 동시에 작업하는 곳에서의 이전 버전
myAgent: {
    update: {
        where: { myAgentKey: myAgent.myAgentKey },
        data: {
        count: { decrement: +count },
        },
    },
}
// 수정 버전
myAgent: {
    update: {
        where: { userKey_agentKey: { agentKey: agent.agentKey, userKey: user.userKey } },
        data: {
        count: { decrement: +count },
        },
    },
}

프론트엔드 픽업 캐릭터 설정오류

  • 프론트엔드에서 픽업 캐릭터를 가렌(S)으로 설정하였음에도, 세주아니(S) 가 더 자주 나오는 오류가 있었다!

  • 그래서 챔피언 뽑기쪽 API를 확인 해보니 맨 아래에 무조건 적으로 마지막 S급을 반환하는 코드가 있었음

function getRandomAgent(agents, pickup = null) {
    if (!pickup) return agents[Math.trunc(Math.random() * agents.length)];
    // 기본 균등 확률

    // 가중치 설정 prob1 = pickup 확률 prob2 = 나머지 s급 확률
    const prob1 = 55;
    const prob2 = Math.trunc((100 - prob1) / (agents.length - 1));
    // 확률 계산
    const weights = agents.map((agent) => agent.agentKey === pickup ? prob1 : prob2); 
    const random = Math.trunc(Math.random() * 100) 

    let cumulative = 0; // 이제 가중치를 모아서 어느수에 걸치는지 파악하기 위한 변수.

    for (let i = 0; i < agents.length; i++) {
        cumulative += weights[i]; //가중치를 모은다.
        if (random <= cumulative) {
        // 가중치가 랜덤에 걸렸을때.
        
        return agents[i]; //그 가중치의 선수.
        }
    }

    return agents[agents.length - 1]; //이건 만약 오류나면 그냥 마지막꺼 내놓음. << 이게 작동한거 같은데..
}
  • 그래서 for문이 제대로 동작 안하는 것 같아 console.log를 사용해 random 값과 cumulative 값을 비교해 보았음!

비교 사진

for (let i = 0; i < agents.length; i++) {
    cumulative += weights[i]; //가중치를 모은다.
    if (random <= cumulative) {
    // 가중치가 랜덤에 걸렸을때.
    console.log(i, cumulative, random, prob2, agents.length)
    return agents[i]; //그 가중치의 선수.
    }
}
console.log(agent.agentKey === pickup, agent.agentKey, pickup,"비동작")
return agents[agents.length - 1]; 
  • 확인을 해보니 확률을 조절하는 weights 에서 확률을 합친 값이 100이 안된 거 같다!

=> 그래서 더 살펴보니 agent.agentKey와 pickup가 똑같지 않다고 하는거다!(pickup이 아마 문자열로 들어온듯)

  • 그렇게 pickup에 +를 붙여줘 INT 형식으로 바꿔 디버깅을 마무리하였다

개선점 분석

  • 프로젝트 마무리 단계에서 아이디어를 내어 작업시간을 늘리지 말고,
    중간 회의나 초반에 소통을 통해 아이디어나 의견을 주고 받을 수 있도록 노력해야겠다.

Reference

스파르타코딩클럽