Backend · 프로젝트 · 2025.11 –
아이리딩Ireading
독서 기록으로 독서 습관과 진도율을 확인하는 B2B 서비스의 백엔드입니다. 스토어에 출시해 운영 중입니다.
- 역할
- 백엔드
- 운영
- 스토어 출시, 운영 중
Case 01
동시 요청 경합 방어
리뷰 작성, 챕터 기록, 소셜 연동 해제, 토큰 재발급 등 6곳의 경합을 찾아, 상황에 맞는 도구로 하나씩 막았습니다.
문제
- 6개 경로에서 같은 요청이 동시에 들어오면 데이터가 중복되거나 상태가 유실됐습니다.
판단 근거
- 경로마다 맞는 도구가 다릅니다. 이미 있는 행은 비관적 락, 단일 행 갱신은 조건부 UPDATE, 아직 없는 행의 삽입은 UNIQUE 제약으로 막습니다.
해결
- 소셜 연동 해제는 비관적 락으로, 두 계정을 동시에 해제해도 마지막 하나는 남도록 했습니다.
- 토큰 재발급은 현재 토큰이 맞을 때만 갱신하는 조건부 UPDATE로 바꾸고, 갱신된 행이 없으면 실패로 처리합니다.
- UNIQUE 인덱스를 추가하기 전에 기존 중복을 정리했습니다. 버릴 챕터의 페이지 기록은 남길 행으로 옮겨 데이터 손실 없이 마이그레이션했습니다.
결과
- 이 중 4곳은 실제 MySQL에 동시 요청 2건을 보내 '1건 성공, 1건 409, DB 1행'이 되는지 통합 테스트로 검증
Case 02
외부 도서 API 통합과 안정화
두 API에 나뉘어 있던 도서 검색을 알라딘 API 하나로 통합하고, 외부 호출 수를 제한하는 워커 풀로 레이트리밋 초과를 막았습니다.
문제
- 도서 검색이 네이버와 국립중앙도서관 두 API에 나뉘어 있었습니다.
- 여러 권을 한꺼번에 등록하면 책마다 DB 조회와 외부 호출이 반복되고, 외부 API의 호출 한도를 넘을 위험이 있었습니다.
판단 근거
- 외부 호출은 병렬로 해도 되지만 동시 실행 수에는 상한이 있어야 합니다.
- DB 쓰기는 하나의 트랜잭션 연결에서 동시에 할 수 없으므로 순서대로 해야 합니다.
해결
- 알라딘 API 하나로 통합했습니다. 알라딘은 오류를 정상 응답 본문으로 내려주므로 따로 처리하고, 타임아웃은 5초로 두었습니다.
- 동시 실행 개수를 제한하는 워커 풀을 직접 구현해, 외부 호출만 동시에 하고 DB 쓰기는 순서대로 합니다.
- 같은 ISBN을 동시에 저장하다 UNIQUE에 걸리면, 방금 커밋된 행이 보이도록 잠금 조회로 다시 읽습니다.
- 메타데이터가 빠진 책은 7일 간격으로 다시 채우고, 출간일이 하루 밀리던 타임존 버그를 고쳤습니다.
결과
- 100건 일괄 등록 시 ISBN별 SELECT 200회 → 2회, 외부 호출 순차 → 동시 4건
Case 03
본인인증 기반 가입과 소셜 로그인 4종
본인인증 결과를 서명된 단기 토큰으로 발급해 이후 가입 단계가 외부 API를 다시 부르지 않게 하고, 미성년자 가입의 예외 상황을 다뤘습니다.
문제
- 가입은 본인인증, 중복 계정 확인, 가입 또는 기존 계정 연동으로 이어집니다. 단계마다 외부 본인인증 API에 기대면 외부 의존이 여러 곳에 퍼집니다.
- 만 14세 미만은 법정대리인 인증이 필요한데, 법정대리인 번호는 형제자매가 함께 쓸 수 있습니다.
판단 근거
- 외부 API 호출은 한 곳에 가두고, 이후 단계는 서버가 서명한 결과를 믿는 편이 단순하고 안전합니다.
- 미성년자는 전화번호만으로 같은 사람인지 판단할 수 없습니다.
해결
- PortOne 본인인증은 확인 단계에서 한 번만 호출하고, 그 결과를 서명된 단기 JWT로 발급해 이후 단계는 이 토큰을 믿습니다.
- 미성년자는 전화번호, 이름, 생년월일이 모두 일치해야 같은 계정으로 판단합니다.
- Google·Apple·Kakao·Naver의 ID Token을 JWKS로 검증하고, provider마다 검증기를 따로 두는 전략 패턴으로 만들어 새 provider는 구현체만 추가하면 되게 했습니다.
- 탈퇴한 사용자에 묶인 소셜 계정은 정리한 뒤 다시 가입할 수 있게 했습니다.
결과
- 외부 본인인증 호출을 한 곳에 모아, 이후 가입 단계는 외부 API 없이 진행