Rails Denormalized Cache Vs Count Query

Rails 비정규화 캐시 컬럼과 COUNT 쿼리 불일치: 씨드 데이터가 0%를 만들었을 때

Rails 앱에 데모용 씨드 데이터를 직접 삽입했는데, 화면에서 모든 퍼센트가 0% 로 표시되는 상황을 만났다. 서버 로그도 깨끗하고, 데이터는 DB에 분명히 들어가 있는데, 숫자만 안 나온다. 상황 투표 기능이 있는 Rails 앱이다. 선택지(Choice)마다 득표 수를 보여주는 화면이 있고, 전체 투표수 대비 퍼센트를 계산해서 프로그레스 바와 숫자로 표시한다. 데모를 보여줘야 해서 외부 API에서 실시간 데이터를 가져와 씨드 데이터로 넣었다. 방식은 간단했다. # 씨드 데이터: 컬럼을 직접 업데이트 choice.update_column(:vote_count, 4712) pick.update_column(:total_votes, 6536) DB를 직접 조회하면 숫자가 잘 들어가 있다. 그런데 화면에서는: ...

2025-12-16 · 4분 소요 · Seunghan
Crypto Exchange Api Integration Lessons

5개 암호화폐 거래소 API 연동하면서 겪은 삽질 모음

Ruby on Rails로 여러 암호화폐 거래소의 펀딩레이트(funding rate)를 수집하는 기능을 만들면서 겪은 문제들을 정리한다. 5개 거래소를 붙이면서 각 거래소마다 API 동작 방식이 달랐고, 공식 문서와 실제 동작이 다른 경우도 있었다. 거래소 API의 공통 기반 클라이언트 만들기 여러 거래소를 붙이기 전에 공통 HTTP 클라이언트를 먼저 만들었다. Faraday를 사용했고, 재시도와 Circuit Breaker를 여기에 몰아 넣었다. Faraday + faraday-retry 설정 # Gemfile gem "faraday" gem "faraday-retry" def connection @connection ||= Faraday.new(url: base_url) do |f| f.request :retry, { max: 3, interval: 0.5, backoff_factor: 2, interval_randomness: 0.5, # jitter retry_statuses: [429, 503, 504], retry_block: -> (env, options, retries, exc) { Rails.logger.warn("[#{exchange_name}] Retrying... #{retries} left. Status: #{env.status}") } } f.adapter Faraday.default_adapter f.options.timeout = 10 f.options.open_timeout = 5 end end backoff_factor: 2와 interval_randomness: 0.5(jitter)를 조합하면 재시도 간격이 0.5초 → 1초 → 2초로 지수 증가하면서 약간의 무작위성이 붙는다. 거래소 API가 Rate Limit(429)을 돌려줄 때 모든 클라이언트가 동시에 재시도하는 “thundering herd” 문제를 막아준다. ...

2025-12-06 · 6분 소요 · Seunghan
Api Response Wrapper Token Parsing Debug

로그인이 자꾸 풀린다 — API 래퍼 포맷 불일치가 만든 연쇄 버그

모바일 앱에서 로그인이 자꾸 풀린다. 로그인 직후는 정상인데, 앱을 잠깐 백그라운드로 내렸다가 다시 열면 로그인 화면이 뜬다. SecureStorage에 토큰 저장도 확인했고, Dio 인터셉터로 401 자동 갱신도 구현되어 있는데 왜? 증상 재현 앱 로그인 → 정상 동작 액세스 토큰 만료 시점 전후로 앱 재시작 → 세션 복원 실패, 강제 로그아웃 서버 로그에서 힌트를 찾았다. FormatException: "user" field is missing or null 토큰 갱신 응답을 파싱하다가 터지고 있었다. 구조 파악 서버는 모든 API 응답을 공통 래퍼로 감싼다. ...

2025-12-02 · 4분 소요 · Seunghan
Spa Blank Screen Inertia Usepage Url Debugging

SPA 배포 후 빈 화면: Inertia.js usePage().url은 string이다

Rails + Inertia.js + Svelte 앱을 배포한 뒤 접속하면 완전히 빈 화면만 보였다. 서버는 정상이고 에셋도 다 로드되는데 화면이 안 그려지는 상황. 원인 추적부터 해결까지 정리한다. 증상 배포된 URL 접속 시 빈 화면 (흰색 배경만 표시) 로컬 개발 서버에서는 정상 동작 아무런 에러 페이지 없이 그냥 빈 화면 진단 과정 Step 1: HTTP 응답 확인 curl -s -o /dev/null -w "%{http_code}" https://example.com/ # 200 HTTP 200 OK. 서버 자체는 정상 응답 중이다. ...

2025-11-22 · 3분 소요 · Seunghan
Rails Inertia Svelte Pet Avatar Image Color

Rails + Inertia + Svelte 5: 아바타 이미지/색상 선택 기능 구현에서 삽질한 것들

Rails 8 + Inertia.js + Svelte 5 스택으로 펫(반려동물) 프로필 아바타를 이미지 또는 색상으로 선택하는 기능을 구현하면서 겪은 문제들을 정리한다. 문제 1: 색상이 DB에 저장되지 않았다 증상 처음 코드를 보니 펫 카드에 색상을 표시할 때 이런 식으로 되어 있었다. const PET_COLORS = ['#f3caa1', '#b7ddf9', '#d3c8ff', '#c5d5f4', '#ffd9aa'] function petColor(index: number): string { return PET_COLORS[index % PET_COLORS.length] } 펫을 생성한 순서(인덱스) 로 색상을 결정하는 구조였다. 색상을 DB에 아예 저장하지 않았으니, 사용자가 색상을 바꿔도 새로고침하면 원래 색상으로 돌아왔다. 해결 마이그레이션으로 avatar_color 컬럼을 추가하고 기본값을 지정했다. ...

2025-11-15 · 4분 소요 · Seunghan
Rails8 Deploy Lessons

Rails 8 첫 배포에서 마주친 5가지 문제: 보안, 마이그레이션, 호환성

Rails 8 프로젝트를 처음 클라우드 서비스에 배포하면서 하루 동안 연속으로 5가지 문제를 만났다. 각각 독립적인 문제처럼 보였지만, 하나를 고치면 다음 문제가 드러나는 패턴이었다. 기록으로 남긴다. 1. 공개 저장소에 민감한 파일이 들어간 경우 증상 git log --all --full-history -- config/secrets.yml 같은 명령으로 확인해보면, 예전 커밋에 secret_key_base가 하드코딩된 파일, 앱 서명 키스토어 파일 등이 포함되어 있다. 해결: git filter-repo로 히스토리에서 완전 삭제 pip install git-filter-repo # 특정 파일들을 히스토리 전체에서 제거 git filter-repo --path config/secrets.yml --invert-paths git filter-repo --path app-release.keystore --invert-paths # 강제 푸시 git push origin main --force 주의: --force 는 팀 작업 중이라면 사전 공지 필수. 모든 팀원이 re-clone해야 한다. ...

2025-11-11 · 4분 소요 · Seunghan
Apple Sso 403 Email Verified Type Mismatch

Apple Sign-In 403 에러: email_verified 타입 불일치와 복붙 버그 3종 세트

Apple Sign-In이 403 Forbidden으로 실패하는데, Google Sign-In은 정상 동작하는 상황이었다. 동일한 스택(Rails 8 + Flutter)의 다른 프로젝트에서는 Apple 로그인이 잘 되고 있어서 비교 분석했다. 증상 Apple 로그인: 403 Forbidden Google 로그인: 정상 성공 에러 메시지: "Email not verified by Apple" 원인 1: email_verified 타입 불일치 (핵심) Apple과 Google은 JWT에서 email_verified 필드를 다른 타입으로 반환한다. Provider email_verified 타입 값 예시 Google boolean true Apple string 또는 boolean "true" 또는 true 문제의 코드: ...

2025-10-25 · 3분 소요 · Seunghan
Rails Missing Migration Sessions Table

Production DB에 테이블이 없다: schema.rb와 migration 파일 불일치 사고

회원가입, 로그인이 전부 안 된다는 제보를 받았다. 앱에서는 “예상하지 못한 오류가 발생했습니다"만 반복. 증상 회원가입 시도 → 500 Internal Server Error 로그인 시도 → 동일하게 500 Health check API → 200 OK, DB 연결 정상 서버는 살아있고 DB도 연결되어 있는데, 인증 관련 기능만 전멸. 조사 과정 1단계: 서버 상태 확인 SSH로 접속해서 Rails 환경 확인. rails runner "puts Rails.env" # => production rails runner "puts User.count" # => 13 서버 정상, DB 연결 정상, 유저 데이터도 존재. ...

2025-10-18 · 4분 소요 · Seunghan
Rails Flutter Server Health Check 4 Issues

Rails + Flutter 앱 서버 점검기: 한 번에 터진 4가지 문제와 해결

앱 테스트 빌드를 올리고 직접 돌려보니 한꺼번에 4가지가 안 됐다. Google 로그인 실패, AI 일정 생성이 엉뚱한 결과, 알림 버튼 누르면 크래시, 인기 여행지 섹션이 텅 비어있음. 하나씩 원인을 찾고 고친 과정을 정리한다. 1. Google SSO는 실패하는데 Apple 로그인은 성공 증상 Apple Sign-In은 정상 동작하지만 Google Sign-In만 500 에러. 클라이언트에서는 로그인 실패 토스트만 보인다. 원인 컨트롤러는 이전 커밋에서 수정했지만, Model의 from_omniauth 메서드는 그대로였다. # User 모델 — 마이그레이션 후에도 옛날 컬럼명 참조 def self.from_omniauth(auth) user = find_or_initialize_by(provider: auth.provider, uid: auth.uid) # uid 컬럼 없음 user.image = auth.info.image # image 컬럼도 없음 end DB 스키마에서는 uid → provider_uid, image → avatar_url로 마이그레이션된 상태. 컨트롤러 쿼리는 수정했지만 모델 내부 메서드가 여전히 옛 컬럼을 참조하고 있었다. ...

2025-10-15 · 5분 소요 · Seunghan
Flutter Sync Queue Aggressive Error Handling

Flutter Sync Queue에서 불필요한 에러가 반복 노출되는 문제 해결

모바일 앱에서 오프라인 동기화를 위해 Transactional Outbox 패턴을 구현하던 중, 동기화가 실제로는 정상 완료되었는데도 “동기화 실패” 에러가 반복적으로 사용자에게 노출되는 문제를 발견했다. 현상 앱에서 다음과 같은 에러가 반복적으로 발생했다: AppException: Failed to push changes: AppException: Push completed with failures; retry count: 2, pending changes remain in queue. 서버 로그를 확인하면 동기화 pull은 정상 동작하고, 실제 데이터도 이미 동기화된 상태였다. 구조 파악: Transactional Outbox 패턴 앱의 동기화 구조는 다음과 같다: ┌────────────────┐ ┌──────────────┐ ┌────────────────┐ │ Local DB │────▶│ Sync Queue │────▶│ Remote API │ │ (Drift/SQLite)│ │ (Outbox) │ │ (Rails) │ └────────────────┘ └──────────────┘ └────────────────┘ 로컬에서 데이터 변경 → sync queue에 pending 아이템 추가 performFullSync() 호출 시 pull → push 순서로 동기화 push 단계에서 queue의 각 아이템을 서버에 전송 성공하면 queue에서 제거, 실패하면 retry count 증가 원인 분석 _pushChanges() 메서드의 에러 처리 로직에 문제가 있었다: ...

2025-10-04 · 4분 소요 · Seunghan
개인정보처리방침 문의