축구팀 관리 프로젝트 38일차 - soft delete된 팀 멤버가 조회되는 오류
페이지 전반적으로 기능 테스트 중에 경기 일정에서 전술설정 들어가면 접속 안되는 오류를 발견했습니다.
페이지 접속 오류
문제 발생
원래는 화면이 나와야 하지만 빈 페이지가 나와서 당황했습니다.
바로 로컬에서 테스트해봤습니다. 테스트 해보니 아래 오류 발생하였습니다.
Uncaught TypeError: Cannot read properties of null (reading 'user')
at index.tsx:1146:1
at Array.map (<anonymous>)
at index.tsx:1144:1
at Array.map (<anonymous>)
at Formation (index.tsx:1142:1)
at renderWithHooks (react-dom.development.js:16305:1)
at updateFunctionComponent (react-dom.development.js:19588:1)
at beginWork (react-dom.development.js:21601:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
다른 계정으로 다시 이 화면 들어가니 다른 화면은 멀쩡하게 잘 나왔습니다. klinsmann@gaeggul.com 이 계정에서만 오류가 나고 있었습니다.
원인 찾기
로컬 swagger로 api 확인해봤습니다.
구단주 계정으로 로그인 후 전술화면에 쓰이는 api를 하나씩 확인해봤습니다. 먼저 구단주 검증은 잘 되었습니다.
팀별 경기별 포메이션 조회도 동작은 잘했습니다. 그러나 결과 데이터는 없었습니다. 원래는 나와야 하는 데이터여서 여기서 부터 코드를 확인해보았습니다.
formation.service.ts
/**
* 팀별 포메이션 조회
* @param teamId
* @param matchId
* @returns
*/
async getMatchFormation(teamId: number,matchId: number,id?: number) {
const whereCondition = {
team_id: teamId,
match_id: matchId,
};
// position 변수가 제공되면 where 조건에 추가
if (id) {
whereCondition['member_id'] = id;
}
const matchFormation = await this.matchFormationRepository.find({
where: whereCondition,
relations: {
member: true,
},
});
if (!matchFormation) {
throw new NotFoundException('팀별 포메이션 정보가 없습니다.');
}
// 각 member에 대해 user 정보를 불러옵니다.
for (const formation of matchFormation) {
if (formation.member) {
const memberInfo = await this.memberRepository.findOne({
where:{
id:formation.member.id
},relations:{
user:true
}
});
formation.member.user = memberInfo.user; // 여기서 user 정보를 member 객체에 할당
}
}
return matchFormation;
}
match_formations 테이블에 데이터 확인(팀 id:1, 경기 id: 49, 2월 24일자 경기 ) 데이터 없었습니다. 다른 일정 데이터도 동일했습니다.
members 테이블도 연관되어 있길래 조회해 보았습니다. 팀1로 조회한 members 테이블 결과를 보다가 이상한 점을 발견할 수 있었습니다. user_id 40번이 여러개 들어있었습니다. 원래 members 테이블은 축구팀의 팀원 정보를 저장하는 용도라 사용자는 한팀에 한 row의 데이터만 가지고 있을 수 있습니다. 하지만 모종의 이유로 한 계정이 여러 members 테이블에 저장되는 바람에 조회가 안되던 것이었습니다.
당연히 이 이유겠지 싶었는데..
id | created_at | updated_at | deleted_at | is_staff | join_date | user_id | team_id |
31 | Tue Feb 13 2024 07:51:02 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:44:13 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:44:13 GMT+0900 (한국 표준시) | 0 | Tue Feb 13 2024 07:51:02 GMT+0900 (한국 표준시) | 40 | 1 |
42 | Wed Feb 14 2024 02:47:18 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:55:05 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:55:05 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 02:47:18 GMT+0900 (한국 표준시) | 40 | 1 |
43 | Wed Feb 14 2024 02:57:00 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:57:35 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:57:35 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 02:57:00 GMT+0900 (한국 표준시) | 40 | 1 |
44 | Wed Feb 14 2024 02:58:08 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:58:28 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:58:28 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 02:58:08 GMT+0900 (한국 표준시) | 40 | 1 |
45 | Wed Feb 14 2024 02:58:54 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:59:09 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:59:09 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 02:58:54 GMT+0900 (한국 표준시) | 40 | 1 |
46 | Wed Feb 14 2024 02:59:35 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:59:51 GMT+0900 (한국 표준시) | Wed Feb 14 2024 02:59:51 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 02:59:35 GMT+0900 (한국 표준시) | 40 | 1 |
47 | Wed Feb 14 2024 03:00:09 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:00:24 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:00:24 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 03:00:09 GMT+0900 (한국 표준시) | 40 | 1 |
48 | Wed Feb 14 2024 03:00:37 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:00:49 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:00:49 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 03:00:37 GMT+0900 (한국 표준시) | 40 | 1 |
49 | Wed Feb 14 2024 03:01:33 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:01:48 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:01:48 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 03:01:33 GMT+0900 (한국 표준시) | 40 | 1 |
50 | Wed Feb 14 2024 03:03:05 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:03:22 GMT+0900 (한국 표준시) | Wed Feb 14 2024 03:03:22 GMT+0900 (한국 표준시) | 0 | Wed Feb 14 2024 03:03:05 GMT+0900 (한국 표준시) | 40 | 1 |
그러면서 테이블을 다시 보니까 deleted_at에 다 값이 들어 있었습니다. 삭제된 데이터였습니다.
진짜 원인은
진짜 문제 해결점을 찾은 듯 했습니다. 다시 정리하는 문제 원인은 member에서 deleted_at 에 값 들어간 인원 (팀 탈퇴한 인원)이 같이 조회되는 바람에 오류 발생하였던 것이었습니다.
수정한 코드
원인을 발견하고 나서 바로 코드를 수정하기 시작했습니다. 막상 위 코드를 수정하려 보니 members 테이블에 조건 거는게 쉽지 않았습니다. 그래서 쿼리 빌더를 사용하여 다른테이블 접근이 용이하도록 했습니다. 그렇게 수정한 코드는 다음과 같습니다.
/**
* 팀별 포메이션 조회
* @param teamId
* @param matchId
* @returns
*/
async getMatchFormation(teamId: number,matchId: number,id?: number) {
const whereCondition = {
team_id: teamId,
match_id: matchId,
};
// position 변수가 제공되면 where 조건에 추가
if (id) {
whereCondition['member_id'] = id;
}
const matchFormation = await this.matchFormationRepository
.createQueryBuilder("matchFormation")
.innerJoinAndSelect("matchFormation.member", "member", "member.deleted_at IS NULL")
.leftJoinAndSelect("member.user", "user") // 여기서는 LEFT JOIN을 사용하여 user 정보는 있으면 가져오고 없으면 무시합니다.
.where("matchFormation.team_id = :teamId", { teamId })
.andWhere("matchFormation.match_id = :matchId", { matchId })
.andWhere(id ? "matchFormation.member_id = :id" : "1=1", { id })
.getMany();
return matchFormation;
}
앞서 말했듯 변경된 코드에는 쿼리빌더를 사용하였습니다.
수정 코드 설명
find 메소드는 간단한 조회에 적합하지만, 조건이 복잡해지거나 특정 조건(member.deleted_at IS NULL)을 만족하는 관계를 가진 엔티티를 조회할 때는 구현하기에 어려움이 있었습니다. 이 때 쿼리 빌더를 사용하면서 SQL 쿼리와 유사한 방식으로 보다 세밀하고 복잡한 쿼리를 작성할 수 있었습니다.
쿼리 빌더를 사용하니 innerJoinAndSelect와 같은 메소드를 통해 조인 시 특정 조건을 적용할 수 있었습니다. 이를 통해 삭제되지 않은(deleted_at가 null인) 멤버만을 조회할 수 있으며, 이런 경우 innerJoinAndSelect는 해당 조건에 부합하는 관계만 결과에 포함할 수 있었습니다.
또한, find 메소드를 사용할 때는 relations 옵션을 사용하면 때로는 불필요한 데이터까지 가져오는 경우가 있었습니다. 이때, 쿼리 빌더를 사용하니 필요한 필드만 선택(select)하여 성능을 최적화할 수 있었습니다.
수정 후 정상 동작
위 코드로 수정하고 다시 정상적으로 동작하는 것을 볼 수 있었습니다.
▼ 이전 진행한 프로젝트들 ▼
'내일배움캠프 > 축구팀 관리 프로젝트' 카테고리의 다른 글
축구팀 관리 프로젝트 - 대규모 트래픽 관리 위해 스케일업, 캐싱 적용 (1) | 2024.02.25 |
---|---|
축구팀 관리 프로젝트 40일차 - 최종발표, 공부는 계속된다, 취업도 (1) | 2024.02.21 |
축구팀 관리 프로젝트 37일차 - 목표 테스트 커버리지 80% 중 56% 달성 (0) | 2024.02.18 |
축구팀 관리 프로젝트 36일차 - jwt 모의함수 구현 오류 (1) | 2024.02.16 |
축구팀 관리 프로젝트 34일차 - 유저테스트 시작, 취업 준비 (0) | 2024.02.14 |