백엔드

API설계

에러처리, Rate Limiting, errorId

33개의 글
  1. 백엔드/API설계

    new Date(2026, 3, 6)의 9시간 드리프트: 테스트 픽스처는 왜 반드시 Date.UTC()여야 하는가

    CI는 초록불인데 로컬에선 빨간불이 떴다. 원인은 테스트 픽스처의 new Date(2026, 3, 6) 한 줄. 로컬 타임존 생성자는 개발자 KST와 CI UTC 사이에서 9시간 어긋나고, 주차 경계 테스트를 조용히 깨뜨렸다.

    13분 읽기
  2. 백엔드/API설계

    Active, Ongoing, Completed: 주차 기반 태스크 상태 쿼리의 3가지 의미론과 그 함정

    '이번 주 진행중인 태스크'를 쿼리하는 방법이 세 가지나 있었다. '주 중에 전환된', '주 동안 어느 시점이라도', '주 시작 시점에'. 말로는 비슷하지만 SQL은 전혀 다르고, 틀리면 이전 주부터 계속 진행되던 태스크가 리포트에서 사라진다.

    13분 읽기
  3. 백엔드/API설계

    과거 주차 조회에서 '그 당시 최신 스프린트'는 뭐였나: weekEnd 기준 latest sprint 설계

    주간 리포트 API가 '이번 주의 latest sprint' 개념으로 태스크를 골라왔는데, 과거 주차를 조회하면 '그 당시'가 아닌 '지금의' latest sprint가 튀어나왔다. carry-over 없이 버려진 태스크까지 리포트에 다시 나타나는 버그였다.

    14분 읽기
  4. 백엔드/Prisma

    순차 루프를 10배 빠르게: Promise.all 병렬화 + findMany distinct 배치화로 API 응답 시간 잡기

    주간 리포트 API가 과제 수에 비례하여 느려졌다. for 루프의 sprint+task 쿼리를 Promise.all로 병렬화해 10배, findFirst N회를 findMany(distinct) + createMany 2쿼리로 바꿔 50배. 두 최적화를 한 커밋에 담은 기록.

    5분 읽기
  5. 백엔드/Prisma

    과거의 진실을 기록하는 법: Append-only ProjectStatusLog로 구현한 historical 주간 리포트

    이번 주 주간 리포트를 보는데, 지난 달에 완료된 과제가 목록에서 사라졌다. 현재 상태(status=완료)로 필터하면 과거 주차 리포트가 오염된다 — 이 문제를 append-only 로그로 해결한 방법.

    6분 읽기
  6. 백엔드/Prisma

    findUnique → create의 함정: Prisma get-or-create 패턴의 P2002 레이스 컨디션과 3줄짜리 해법

    '없으면 만들어'는 간단해 보이지만 동시 요청 2개가 들어오면 둘 다 'findUnique가 없다'를 거쳐 둘 다 create를 호출합니다. unique constraint에 걸려 P2002가 터지는 전형적인 get-or-create 레이스. try/catch 3줄로 우아하게 풀었습니다.

    12분 읽기
  7. 백엔드/API설계

    Docker는 UTC, 사용자는 KST: Intl.DateTimeFormat만으로 만든 tz-aware ISO Week 유틸리티

    주간 리포트의 주차 경계가 서버 컨테이너(UTC)와 사용자(KST) 사이에서 어긋났다. date-fns-tz나 luxon을 도입하는 대신, Node 22의 full-ICU Intl.DateTimeFormat만으로 IANA 타임존 ISO week 유틸리티를 구현한 기록.

    15분 읽기
  8. 백엔드/API설계

    API 에러 처리 표준화: withErrorHandler 패턴으로 중앙집중화하기

    API 엔드포인트가 늘어나면서 에러 처리 코드가 중복되고 응답 형식도 제각각이었습니다. withErrorHandler 래퍼와 세분화된 에러 클래스로 문제를 해결한 경험을 공유합니다.

    11분 읽기
  9. 백엔드/API설계

    TypeScript Branded Types로 타입 안전성 높이기

    모든 ID가 string 타입이라 인자 순서를 바꿔도 컴파일 에러가 안 났습니다. Branded Types 패턴으로 런타임 오버헤드 없이 타입 안전성을 확보한 방법을 공유합니다.

    10분 읽기
  10. 백엔드/API설계

    Promise.all vs Promise.allSettled: 부분 실패를 허용하는 벌크 처리

    100명에 대해 데이터를 생성하는데 1명이 실패하면 전체 99명도 실패로 처리되었습니다. Promise.allSettled로 부분 실패를 허용하는 벌크 처리를 구현한 방법을 공유합니다.

    9분 읽기