GitHub Actions에서 N+1 쿼리를 자동으로 감지하고 PR에 코멘트 달기들어가며N+1 쿼리는 ORM을 사용하는 애플리케이션에서 가장 흔하게 발생하는 성능 문제입니다. 개발 환경에서는 데이터가 적어 눈에 띄지 않지만, 프로덕션에서는 심각한 성능 저하를 일으킵니다.이 글에서는 GitHub Actions CI 파이프라인에서 N+1 쿼리를 자동으로 감지하고, 코드 라인에 직접 리뷰 코멘트를 다는 방법을 소개합니다.목표PR에서 변경된 테스트 파일에서 발생한 N+1만 감지N+1이 발생한 정확한 테스트 위치에 리뷰 코멘트N+1이 수정되면 자동으로 코멘트 삭제rswag run_test! 실행 시에만 N+1 감지 활성화기존 접근 방식의 한계Prosopite만 사용할 때Prosopite는 N+1 쿼리를 감지하는 ..
Backend
문제 상황프로덕션 환경에서 특정 API의 메모리 사용량이 비정상적으로 높다는 알림을 받았습니다. 요청이 들어올 때마다 메모리가 계속 증가하고, GC(Garbage Collection)가 제대로 동작하지 않는 것처럼 보였습니다.1단계: 메모리 프로파일링 미들웨어 구현먼저 문제를 정량적으로 측정하기 위해 메모리 프로파일링 미들웨어를 구현했습니다.# app/core/memory_profiler.pyimport gcimport tracemallocfrom collections.abc import Callableimport psutilfrom starlette.middleware.base import BaseHTTPMiddlewarefrom starlette.requests import Requestfrom st..
운영 중인 서비스에서 특정 조회 API가 간헐적으로 실패하며 다음과 같은 MySQL 에러가 발생했습니다.Out of sort memory, consider increasing server sort buffer size처음에는 단순한 쿼리 성능 이슈로 보였지만, 실제 원인은 MySQL의 정렬(filesort) 과정에서 발생한 메모리 부족(OOSM) 이었습니다.이번 글에서는 OOSM이 무엇인지, 왜 발생하는지, 그리고 우리가 실제로 어떻게 해결했는지를 공유합니다.OOSM이란 무엇인가?OOSM(Out Of Sort Memory) 은 MySQL이 ORDER BY, GROUP BY 등을 처리하는 과정에서 정렬을 위한 메모리(sort buffer)가 부족할 때 발생하는 에러입니다.MySQL은 다음 두 가지 방식으로..
a.b.c 조전까지만 해도 무심코 쓰던 코드입니다. A모델에 B가 있으니, 체인으로 c를 꺼내 쓰는 게 자연스럽게 느껴졌죠. 하지만 이 패턴은 Law of Demeter—최소 지식 원칙—을 정면으로 위배합니다. “내 옆집 문을 열어달라고 내 친구에게 부탁하지 말라”는 말처럼, 객체는 자신과 직접 관련된 이웃에게만 말을 걸어야 합니다. 그렇지 않으면 내부 구조가 외부에 노출돼 결합도가 치솟고, 작은 구조 변경이 연쇄 폭발로 이어집니다.이번 글에서는 운영 중인 rails 프로젝트를 예시로, Law of Demeter를 Rails에서 어떻게 지키고 있는지, delegate를 활용하면 어떤 장단점까지 정리해 봅니다.1. 문제는 어디서 시작됐나?A는 B과 1:1 관계를 맺고 있습니다. 외부에서 B의 c를 알고 싶..
Locale 공통화는 다국어 서비스를 운영할 때 필수적인 인프라로, 모든 API가 동일한 언어 판별 규칙을 따르도록 일원화한 작업입니다. 이를 통해 코드 중복을 줄이고 서비스 전반의 언어 일관성을 확보했습니다.왜 Locale이 필요한가?저희 서비스는 다국어(한국어, 영어) 를 지원합니다.동적 데이터는 하나의 DB 컬럼을 JSON 형태로 관리하고 있습니다.예를 들어 블로그 글이 있다고 해봅시다.글의 제목은 한국어와 영어를 모두 지원해야 하므로 모델은 이렇게 정의됩니다.class Post { title: {ko: "Spring Locale 공통화 여정", en: "The Journey to Unify Locale Management in Spring" }}이제 이 데이터를 가지고 DTO에서 lang을 받아..
들어가며최근 JupyterHub와 내부 시스템 간의 API 호출 과정에서 Timeout 설정값을 어떻게 잡는 게 적절할지 논의가 있었습니다. 단순히 “적당히 5초쯤?”으로 정하기보단, 실제 데이터 기반으로 최적값을 찾아보자는 접근을 해보았습니다.이 글에서는Timeout 값을 결정할 때 어떤 기준이 필요한지AWS Athena를 활용해 API 응답 시간을 분석하는 과정최종적으로 도출된 Timeout 설정값을 공유합니다.1️⃣ 문제 인식: “Timeout을 몇 초로 해야 할까?”해당 API는 주로 두 가지 요청을 처리합니다.토큰 발급유저 생성요청 자체는 많지 않지만, 호출 시점마다 응답이 지연될 때 전체 시스템에 영향을 줄 수 있습니다.특히 너무 긴 Timeout은 스레드 점유 시간 증가 → 전체 처리량 감소..
이걸 어떻게 진행하게 되었는가?Rails 프로젝트에서 Devise-Security (비밀번호 만료·재사용 방지 등 추가 보안 기능) 를 적용하려 했는데, 한국어(locale ko)가 존재하지 않는 걸 발견했어요. 서비스 전반에 한–영 메시지가 섞이면 사용자 경험이 깨지니 직접 번역해서 기여하기로 결정했습니다.진행 방법1. 레포지토리 Fork & 세팅# GitHub에서 Fork 후git clone git@github.com:my-id/devise-security.gitcd devise-securitygit checkout -b feature/i18n-ko2. 과거 PR 파헤치기PR [#240] “Add Spanish translations”를 참고했어요.동일한 패턴(로케일 파일 + 테스트 추가)을 재사용...
무결성이란?무결성(integrity)란 데이터가 정확하고, 일관되며, 신뢰할 수 있는 상태를 유지하도록 보장하는 제약(Constraint)의 모음.무결성 종류1. 개체 무결성 (Entity Integrity)한 테이블(릴레이션)의 **행(row) = 개체(entry)**를 고유하게 식별한 **기본기(PK)**가 반드시 존재해야 하며, 그 컬럼은 NULL 값을 가질수 없음.이게 필요한 이유는 행이 중복되면 JOIN, UPDATE 시 어떤 개체를 가리키는지 알수가 없어 데이터 불일치와 연쇄 오류가 발생한다.대표적인 제약이 PK, Unique NOT NULL 등예제CREATE TABLE student ( student_id INT PRIMARY KEY, -- NULL 금지 + 중복 금지 name ..
서론예전에 아래와 같은 글을 작성한적이 있다.ruby의 배열내의 카운트를 효과적으로 세는 법해당 글에 아래와 같은 댓글을 누군가 달아주셨다.루비를 쓰면서 최근 처음보는 Enumerable#tally 를 추천해주셨고 관련된 메서드를 한번 글로 작성하면서 공부해보려고 한다.해당 글은 Ruby 3.3.6을 기반으로 테스트합니다.Enumerable#tallyhttps://ruby-doc.org/3.3.6/Enumerable.html#method-i-tally module Enumerable - RDoc Documentationeach_entry(*args) {|element| ... } → self click to toggle source each_entry(*args) → enumerator Calls the..
서론이전 장에서는 숫자야구게임의 정책문서를 작성하며, 테크스펙을 작성해 개발시 문제가 될 영역들을 체크해봤다.Step2에서 필요한 Reposiotry에서의 로직을 구현해보겠습니다.GameRepository추상화를 위해 Inteface를 구현합니다.package baseball.repository;import baseball.domain.Game;import java.util.List;import java.util.Optional;public interface GameRepository { int insert(Game game); Optional findById(int gameId); List findAll(); List findAllByPlayerTimes(int playerTime..