Загружаем каталог…
Загружаем каталог…
지난 글 에서 대시보드 숫자가 왜 틀렸는지를 썼다. 집계를 고치다 보니 더 불편한 걸 발견했다. 집계가 틀린 게 아니라 데이터 자체가 틀어진 계정 이 쌓여 있었다. 334명. 2년 반 동안. 되돌릴 수 없는 되돌리기 서비스에는 학생 계정 번호가 두 종류 있다. 결제 전의 임시 번호 와 결제 후의 정식 번호 . 수강권 결제가 완료되면 임시 → 정식으로 바뀌고, 그 결제가 환불되면 다시 임시로 돌아가야 한다. 되돌리는 코드는 이렇게 생겼다. async restoreOriginalIdIfExists(userId: number) { const original = await this.findOriginalId(userId); // 캐시 또는 이력 테이블 if (!original) { this.logger.warn(`원본 번호를 찾을 수 없습니다: ${userId}`); return; // ← 여기 } await this.updateLoginId(userId, original); } return 한 줄. 원본을 못 찾으면 경고만 남기고 넘어간다. 캐시는 만료되고, 이력 테이블에는 모든 전환이 남아 있지 않았다. 그래서 환불은 됐는데 정식 번호를 그대로 달고 있는 계정 이 조용히 쌓였다. 에러가 아니라 경고였고, 경고를 보는 사람은 없었다. 이게 앞 글의 227명 중 일부로도 흘러들어가 있었다. 정식 번호를 달고 있으니 분류 로직이 "결제한 재원생"으로 봤던 것이다. 집계 버그와 데이터 버그가 같은 화면에서 겹쳐 있었다. "흔적이 없다"를 증명하는 비용 334명에게 새 임시 번호를 발급하는 마이그레이션을 쓰기로 했다. 그런데 번호를 바꾸는 건 되돌리기 어려운 작업이다. 이 계정들이 정말로 쓰이지 않고 있는지 를 먼저 확인해야 했다. 처음엔 이렇게 확인했다. 결제 완료 → 정식 번호 부여 이력 ✓ 환불 완료 ✓ 현재 반 배정 없음 ✓ 로그인·학습 진도·영상·퀴즈·출결·보상 기록 전부 0건 ✓ 여섯 개 테이블을 봤고, 전부 0이었다. 그래서 PR 본문에 이렇게 썼다. 이 학생들은 반 배정 이력 자체가 없다. 중도 이탈자와 겹치지 않는다. 그리고 리뷰를 기다리는 동안, 찜찜해서 한 번 더 봤다. 96개 테이블을 전부 뒤졌다 학생 식별자를 가진 테이블을 스키마에서 전부 긁었다. 96개 였다. 내가 확인한 건 그중 6개였다. 나머지 90개를 전부 세어봤더니, 334명 중 40명에게 흔적이 있었다. 흔적 인원 의미 알림장 12 담임이 반 학생에게 쓰는 것 — 과거 반 소속의 증거 과제 배정 5 강사 피드백 1 게시글 조회 14 그 계정으로 로그인해서 본 것 무료 프로그램 참여 14 미션 제출·만족도 평가 합집합 40 나머지 294명은 어떤 흔적도 없음 알림장이 특히 뼈아팠다. 알림장은 담임이 자기 반 학생에게 쓰는 것이다. 알림장이 12건 있다는 건 그 학생이 과거에 반에 속해 있었다는 뜻이고, 나는 바로 그걸 "이력 자체가 없다"고 단언했던 것이다. PR 본문을 고쳤다 새 커밋을 올리면서 PR 본문에 이 섹션을 추가했다. ⚠️ 정정 — "반 이력 자체가 없다"는 사실이 아닙니다 처음에는 *"이 학생들은 반 배정 이력 자체가 없다"* 고 적었으나, 학생 식별자를 가진 96개 테이블을 전수 조사한 결과 334명 중 40명에게 흔적이 있었습니다. 지울 수도 있었다. 아직 머지 전이었고, 리뷰어가 그 문장을 읽었는지도 알 수 없었다. 조용히 수정하면 아무도 모를 일이었다. 안 지운 이유는 이거다. 그 문장을 근거로 이 마이그레이션이 안전하다고 판단했기 때문이다. 근거가 바뀌었으면 판단도 다시 설명돼야 한다. 결론이 같더라도(실제로 같았다 — 40명도 현재 활동은 없어서 대상에 남겼다) 어떤 근거로 같은 결론에 도달했는지 는 달라진다. 그리고 솔직히, 다음에 비슷한 걸 쓸 때 내가 다시 꼼꼼해지려면 이 기록이 남아 있는 게 낫다. 마이그레이션은 앱과 똑같이 번호를 매겼다 새 임시 번호를 발급할 때, SQL 안에서 번호 생성 규칙을 다시 구현하고 싶은 유혹이 있었다. 그게 더 짧으니까. 안 했다. 앱의 계정 생성 코드가 쓰는 것과 같은 규칙 (접두사 + 연도 2자리 + 연내 일련번호)을 그대로 따랐다. 마이그레이션이 만든 번호와 앱이 만드는 번호가 다른 모양이면, 그 차이가 다음 사람에게는 또 하나의 수수께끼가 된다. 그리고 넣지 않은 것도 적어뒀다 — 왜 자동 복구를 안 붙였는가. 이력이 없을 때 자동으로 새 번호를 발급하게 고치면, 원본이 늦게 복구되는 경우(캐시 지연 등)에 멀쩡한 번호를 덮어쓸 수 있다. 334명은 사람이 확인한 일회성 데이터이고, 앞으로의 유입은 이력 보존 쪽을 고쳐서 막는 게 맞다. 그건 별도 작업으로 남겼다. 남은 세 가지 1 경고 로그는 처리가 아니다. logger.warn() 뒤에 return 을 쓰는 순간, 그 실패는 "처리됐다"가 아니라 "데이터로 쌓이기 시작했다"가 된다. 되돌리기·보상 트랜잭션처럼 실패하면 데이터가 어긋나는 경로 에서는 조용히 넘어가면 안 된다. 최소한 세는 지표라도 있어야 한다. 2 "없다"는 "있다"보다 증명 비용이 훨씬 비싸다. 뭔가 있다는 건 한 줄만 찾으면 되지만, 없다는 건 찾을 수 있는 모든 곳을 봐야 한다. 그걸 6개 테이블 보고 선언했다. 스키마에서 식별자를 가진 테이블 목록을 기계적으로 뽑는 것 이, 내 기억으로 테이블을 떠올리는 것보다 언제나 낫다. 3 공개한 분석은 공개적으로 정정한다. 내가 쓴 근거를 남들이 읽고 판단한다. 근거가 틀렸으면 그 판단도 다시 받아야 한다. 창피한 건 잠깐이고, 틀린 근거가 남아 있는 건 영원하다. 데이터 마이그레이션 전에 묻는 것들 대상이 "쓰이지 않는다"는 근거가 몇 개 테이블인가? 스키마에서 기계적으로 뽑았나, 기억으로 떠올렸나? 이 작업을 되돌릴 수 있나? 못 되돌리면 대상 목록을 파일로 남겼나? 앱이 쓰는 생성 규칙과 같은 규칙 을 쓰는가? 같은 일이 또 쌓이지 않게 하는 작업이 따로 잡혀 있나? (이번 건은 증상, 원인은 따로) 분석 중에 전제가 바뀌었다면, 그걸 기록에 남겼나? 다음 글은 조금 다른 종류다. 운영 DB 에 쌓인 상담 답변 10,953건을 전수 분석해서 상용구를 뽑았는데, 빈도 1위 문구가 올해는 0건 이었던 이야기.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
334명을 되살리며 내가 쓴 분석을 스스로 뒤집었다. 지난 글 에서 대시보드 숫자가 왜 틀렸는지를 썼다. 집계를 고치다 보니 더 불편한 걸 발견했다. 집계가 틀린 게 아니라 데이터 자체가 틀어진 계정 이 쌓여 있었다. 334명. 2년 반 동안. 되돌릴 수 없는 되돌리기 서비스에는 학생 계정 번호가 두 종류 있다. 결제 전의 임시 번호 와 결제 후의 정식 번호 . 수강권 결제가 완료되면 임시 → 정식으로 바뀌고, 그 결제가 환불되면 다시 임시로 돌아가야 한다. 되돌리는 코드는 이렇게 생겼다. async restoreOriginalIdIfExists(userId: number) { const original = await this.findOriginalId(userId); // 캐시 또는 이력 테이블 if…
Открыть источник