/code-review medium 2회차 · Claude Code · Opus 5.5 · 클로드의 마지막 답 원문 · 목록

points.py에서 실제 버그 7개와 정리할 점 3개를 찾았습니다. 이번 리뷰는 검증 단계 없이 진행해서 일부는 오탐일 수 있습니다. 가장 심각한 건 앞의 세 개로, 모두 돈과 직결됩니다.

나머지 버그는 use()에 음수를 넣으면 포인트가 생기는 문제, 만료일이 하루 빨리 처리되는 문제(새로 생긴 버그는 아니지만 use()가 이 판정을 씁니다), 2월 29일 생일자가 평년에 2배 적립을 못 받는 문제 등입니다. 정리할 점과 테스트가 없다는 지적까지 포함한 전체 목록은 리뷰 결과에 있습니다.

찾은 문제 목록 (ReportFindings 도구로 넘긴 원문, 10건)

#위치분류요약일어나는 상황
1points.py:35정확성(버그)생일 적립률이 영구 누적됨
생일에 `self.rate`를 직접 2배로 바꿔서 그 값이 영구히 남습니다. 생일에 결제할 때마다 다시 2배가 되고, 생일이 지나도 원래 비율로 돌아오지 않습니다.
생일에 결제를 2번 하면 첫 결제는 10%, 둘째는 20%로 적립됩니다. 다음 날 10,000원을 결제하면 500 대신 2,000포인트가 쌓이고, 생일이 돌아올 때마다 비율이 계속 2배가 됩니다.
2points.py:37정확성(버그)포인트 사용 후 하루 한도가 다시 풀림
하루 한도 계산이 그날 적립한 양이 아니라 남은 포인트(entry[1])를 더합니다. `use()`가 이 값을 깎기 때문에, 포인트를 쓰고 나면 같은 날 다시 적립할 수 있게 됩니다.
같은 날 5,000을 적립하고 5,000을 쓰면 earned_today가 0이 됩니다. 그 뒤 또 5,000을 적립할 수 있어서 하루 한도를 계속 넘을 수 있습니다.
3points.py:54정확성(버그)use()가 만료된 적립분부터 차감함
`use()`가 만료 여부를 보지 않고 가장 오래된 적립분부터 깎습니다. 그래서 이미 만료된 포인트가 먼저 차감되고, 유효한 포인트는 그대로 남습니다.
400일 전에 1,000(만료), 오늘 1,000(유효)을 적립했다고 하면 balance는 1,000입니다. use(1000)을 하면 만료분만 0이 되고 오늘 적립분 1,000은 그대로라서, 1,000을 쓴 뒤에도 잔액이 1,000으로 남습니다.
4points.py:51정확성(버그)use()에 음수를 넣으면 포인트가 생겨남
`use()`가 음수나 0을 막지 않습니다. 음수를 넣으면 take가 음수가 되어 적립분에 포인트가 더해집니다.
use(-1000, today)를 호출하면 잔액 검사를 통과하고, 첫 entry에서 take=-1000이 되어 entry[1]이 1,000 늘어납니다. 포인트를 공짜로 만들 수 있습니다.
5points.py:45정확성(버그)만료 판정이 하루 빠름
만료 조건 `< EXPIRE_DAYS - 1`이 하루 짧습니다. 적립일을 1일째로 치면 365일째는 days==364인데, 이 조건에서는 그날 포인트가 빠집니다. 이 판정을 `use()`도 그대로 씁니다.
1월 1일에 적립한 포인트는 365일째 되는 날까지 써야 하는데, 그날 balance에서 빠지고 use()에서는 '포인트가 부족합니다'가 납니다. 조건은 `< EXPIRE_DAYS`여야 합니다.
6points.py:29정확성(버그)2월 29일 생일자는 평년에 2배 적립 없음
2월 29일 생일은 윤년이 아닌 해에 (month, day)가 맞는 날이 없어서 생일 2배 적립을 받지 못합니다.
birthday=date(2000,2,29)인 손님은 2025~2027년에 2배 적립을 한 번도 받지 못합니다. 2/28이나 3/1로 대신 처리하는 규칙이 필요합니다.
7points.py:38정확성(버그)한도 도달 후 0/음수 entry가 쌓임
하루 한도에 이미 도달했어도 0포인트짜리 entry가 계속 추가됩니다. 또 earned_today가 어떤 이유로든 한도를 넘으면 음수 포인트가 기록될 수 있는데, 이를 막는 하한(max 0)이 없습니다.
한도에 도달한 날 결제할 때마다 [today, 0]이 쌓여서 entries가 불필요하게 커지고 이후 계산마다 그만큼 더 순회합니다. 한도 상수를 낮추면 기존 적립분 때문에 음수 적립이 생길 수 있습니다.
8points.py:23단순화rate를 인스턴스 상태로 둘 필요 없음
`self.rate`는 상수에서 계산할 수 있는 값인데 인스턴스 상태로 들고 있습니다. `earn()` 안에서 지역 변수로 `rate = POINT_RATE * (2 if 생일 else 1)`을 쓰면 됩니다.
필요 없는 가변 상태 때문에 1번 버그(비율 누적)가 생겼습니다. 지역 변수로 바꾸면 이런 종류의 버그가 생길 여지가 없어집니다.
9points.py:54효율use()에서 불필요한 정렬과 이중 순회
`use()`가 매번 balance()로 전체를 한 번 돌고, 다시 전체를 정렬합니다. entries는 append 순서대로 이미 날짜순이라 정렬할 필요가 없습니다.
적립 내역이 많은 손님은 결제/사용할 때마다 O(n log n) 작업을 합니다. 순서대로 한 번만 돌면서 유효분만 차감하면 만료 버그도 같이 고쳐집니다.
10points.py:49테스트새 규칙 테스트 없음
생일 2배, 하루 한도, 오래된 순 차감 같은 새 규칙에 대한 테스트가 하나도 없습니다.
생일에 두 번 결제하기, 쓰고 나서 다시 적립하기, 만료분이 섞인 상태에서 사용하기 같은 경우를 테스트했다면 위 버그들이 바로 드러났을 것입니다.