나만의 작은 도서관
[TIL] 240607 캠프 54일차: prisma: findFirst, findUnique, 그리고 유일성 조건에 따른 오류 본문
[TIL] 240607 캠프 54일차: prisma: findFirst, findUnique, 그리고 유일성 조건에 따른 오류
pledge24 2024. 6. 7. 21:50오늘 배운 내용
prisma. findFirst메소드는 유일성을 검사하지 않는다.
findFirst메소드는 조건을 만족하는 첫번째 레코드를 반환하며 유일성을 검사하지 않는다. 따라서, 조건을 만족하는 레코드가 여러 개 존재해도 오류를 반환하지 않는다. (findMany결과값에서 [0]을 반환하는 경우와 같다고 보면 된다.)만약 조건을 만족하는 레코드가 하나도 존재하지 않는다면 null값이 반환된다.
const data = await prisma_gamedata.items.findFirst({
where:{
itemCode: 0 // itemCode가 0인 경우는 없다
}
});
console.log(data); // null
prisma. findUnique에서 발생할 수 있는 유일성 오류
findUnique메서드는 id 또는 @unique로 지정된 컬럼을 통해 유일한 레코드를 반환한다. (findFirst와 같이 해당 조건을 만족하는 레코드가 존재하지 않는다면 null을 반환)그렇기 때문에 항상 반환값은 하나이다. findUnique는 findFirst와 달리 유일한 레코드를 반환해야한다는 규칙이 존재하기 때문에 where절에는 필수적으로 들어가야하는 컬럼이 존재하게 된다. 만약, 필수 컬럼을 넣지 않으면 다음과 같은 오류가 발생하게 된다.
// itemHealth가 0인 item을 items테이블에서 가져옵니다.(items의 기본키는 itemCode)
const data = await prisma_gamedata.items.findUnique({
where:{
itemHealth: 0
}
});
Argument `where` of type ItemsWhereUniqueInput needs at least one of `itemCode` arguments.
Available options are listed in green.
만약 키가 복합키인 경우, where절에 해당 키를 그냥 사용하면 오류가 난다. 오류는 다음과 같다.
// 0번 캐릭터가 0번 itemCode를 아이템을 장착했는 지 여부를 테이블에서 가져옵니다.(오류 발생 예)
// (복합키: [characterId, itemCode])
const data = await prisma.CharactersEquipment.findUnique({
where:{
characterId : 0,
itemCode: 0
}
});
Argument `where` of type CharactersEquipmentWhereUniqueInput needs at least one of `characterId_itemCode` arguments.
Available options are listed in green.
오류가 발생하는 원인은 복합키를 사용한 테이블은 복합키 속성으로 따로 묶어줘야 하기 때문이다. 위의 오류가 말해주듯, 복합키: [characterId, itemCode]를 사용하고 있다면 characterId_itemCode로 한 번 더 묶어줘야 한다. 올바른 코드는 다음과 같다.
// 0번 캐릭터가 0번 itemCode를 아이템을 장착했는 지 여부를 테이블에서 가져옵니다.(올바른 예)
const data = await prisma.CharactersEquipment.findUnique({
where:{
characterId_itemCode:{
characterId : 0,
itemCode: 0
}
}
});
추가로, 비단 findUnique가 아니더라도 유일한 레코드에 접근하려면 복합키를 사용한 테이블은 전부 위와 같이 한 번 더 묶어줘야한다.( 예: update메소드 )
오늘의 Trouble Shooting
Problem 1. 마이너스 랭크 점수
랭크 매치를 통해 승패 결과에 따라 랭크 점수를 조정하는 로직을 짜고 있었는데 존재해서는 안될 마이너스 랭크 점수가 발견되었다... 랭크 점수가 마이너스인 경우는 게임 상 오류로 취급하므로 정상적으로 게임을 진행했다면 절대 나와서는 안된다.
Solve. update메소드 decrement조건 변경
워낙 랭크 매치 API의 코드 길이가 길어서 하나하나 console.log()를 찍어가며 찾는데 꽤 시간이 걸렸다. 문제는 졌을 때 무조건 랭크 점수가 100 하락하도록 update()메소드를 사용했기 때문이였다. 따라서, 랭크 점수를 100 하락하기 전, 마이너스 점수가 발생하는 지 확인하고 마이너스가 발생하면 0으로 고정하는 삼항 연산자를 추가하였다.
// 변경 전.
decrement: 100,
// 변경 후.
decrement: opponentUserInfo.rank_score >= 100 ? 100 : 0,
Problem 2. 구단이 존재하는 상대를 매칭했지만 구단이 비어서 반환되는 문제
분명 적절한 레이팅 범위 내에 상대방을 찾았지만 상대방이 구단을 가지고 있지 않는 문제가 발생했다. find_opponent라는 함수는 구단이 존재하지 않는 상대방 구단 정보를 찾지 않으므로 (where문 조건으로 구단을 가지고 있는지 판단하는 have_club이 true인 조건을 넣어서 판단) 도무지 갈피가 잡히지 않았다. 이전 버전에서는 잘 돌아갔기 때문에 버그를 찾는 데 꽤 오랜 시간이 걸렸다
Solve. 구단에 선수 추가/제거 API에서 누락된 have_club변경 로직 추가
구단에 선수를 추가하거나 제거할 때 각 조건에 따른(추가: 현재 선수를 추가해서 구단이 꽉차는 경우, 제거: 선수 제거 API실행시 항상)have_club값을 true/false로 변경되는 로직을 짰었는데, 어째서인지 해당 로직이 코드에서 누락되어 있었다. 즉, have_club이 true인데 구단이 존재하지 않는 유저 정보가 있다는 것이다. 이 때문에 have_club이라는 변수의 값만으로 상대가 구단을 가지고 있는지 판단하는 find_opponent함수는 구단을 가지고 있지 않는 유저또한 상대 리스트에 추가되면서 구단이 존재하지 않는 상대방 구단 정보를 찾지 않는 로직이 제대로 동작하지 않았던 것이다.
뒤늦게 깨닫고 각 API에 해당 코드를 추가하였다.
// 구단에서 선수 제거 API 내부
await tx.user_info.update({
where:{
account_id
},
data:{
have_club: false
}
})
.
.
.
// 구단에서 선수 추가 API 내부
// 선수를 추가함으로써 완벽한 구단(3명)이 만들어지면 have_club을 true로 변경합니다.
if (clubPlayerList.length == 2) {
await tx.user_info.update({
data: {
have_club: true,
},
where: {
account_id,
},
});
}
오늘 한 일
- futsalOnline 팀 프로젝트 최종 버그 픽스
- futsalOnline 팀 프로젝트 최종 제출
'Today I Learn' 카테고리의 다른 글
[TIL] 240614 캠프 61일차: docker를 사용하는 이유, redis가 좋은 이유 (0) | 2024.06.14 |
---|---|
[TIL] 240610 캠프 57일차: 인터페이스, SOLID, 아키텍처 패턴, jest 사용 오류해결 (0) | 2024.06.10 |
[TIL] 240605 캠프 52일차: Prisma 배열과 객체의 반환 값 판정, Prisma NOT, gte, lte사용법 (0) | 2024.06.05 |
[TIL] 240604 캠프 51일차: prisma client와 연결된 DB간의 관계. 협업에서 발생할 수 있는 문제에 대해서... (0) | 2024.06.04 |
[TIL] 240603 캠프 50일차: 소켓, 쓰레드, I/O (1) | 2024.06.04 |