분류 전체보기 152

[DB Deep Dive] 5. 내 쿼리의 성적표 EXPLAIN: Using filesort 제거와 Using where의 이해

1. 대용량 스파이크 부하 테스트: 옵티마이저를 깨워라지난 [상편]에서 우리는 데이터가 고작 43건일 때 MySQL 옵티마이저가 복합 인덱스를 무시하고 풀 테이블 스캔(ALL)을 때려버리는 영리한 배신을 목격했습니다. 인덱스의 진짜 위력을 증명하기 위해, 저는 JMeter를 활용하여 운영 환경과 유사한 대용량 트래픽을 강제로 발생시키기로 했습니다.단순히 데이터를 넣는 것을 넘어 서버의 비동기 처리(Async) 성능까지 함께 검증하기 위해, Ramp-up(진입 시간)을 단 1초로 설정하고 10,000건의 주문 요청을 동시에 쏘는 스파이크(Spike) 부하 테스트를 세팅했습니다. 찰나의 순간에 쏟아지는 폭격을 Tomcat 스레드와 비동기 큐가 어떻게 버텨낼지 확인하기 위함이었습니다. 2. 예기치 못한 에러,..

정리/DB 2026.05.21

[DB Deep Dive] 4. 인덱스를 걸었는데 풀 스캔(ALL)이 뜬다고? (feat. 옵티마이저의 배신)

1. 시작하며: 완벽하다고 믿었던 내 복합 인덱스의 배신가상 자산 거래소 프로젝트를 진행하며 가장 데이터가 많이 누적될 테이블은 단연 orders (주문 내역) 테이블이었습니다. 유저가 자신의 과거 거래 리스트를 조회할 때, 시스템은 특정 유저의 ID를 조건으로 검색하고, 주문 일시를 기준으로 최신순 정렬 및 페이징 처리를 수행해야 합니다.이에 대비하여 저는 데이터베이스 설계 단계에서부터 user_id와 order_date를 묶은 복합 인덱스(Multi-Column Index)인 idx_user_date를 생성해 두었습니다. 동등 조건으로 검색 범위를 좁히고, B+Tree 인덱스 구조를 통해 정렬 부하를 완벽히 제거하겠다는 정석적인 전략이었습니다.// 가상 자산 주문 내역 조회를 위한 JPA Reposi..

정리/DB 2026.05.21

[DB Deep Dive] 3. "인덱스를 걸었는데 왜 안 탈까요?" EXPLAIN 실행 계획 분석과 튜닝

1. 시작하며: 내 쿼리는 정말 인덱스를 타고 있을까?우리가 아무리 열심히 B+Tree 복합 인덱스를 설계해 두었다고 해도, 쿼리를 잘못 작성하면 DB의 옵티마이저(Optimizer)는 인덱스를 쿨하게 무시하고 테이블 풀 스캔(Full Scan)을 때려버립니다. 대규모 트래픽 환경에서 이는 곧바로 장애로 이어집니다.따라서 백엔드 개발자는 쿼리를 작성한 후 반드시 EXPLAIN(실행 계획) 명령어를 통해 옵티마이저의 속마음을 들여다보고 쿼리가 의도대로 작동하는지 검증해야 합니다. 수많은 실행 계획 컬럼 중, 실무에서 쿼리 튜닝을 할 때 가장 중요하게 쳐다보는 핵심 컬럼들(type, Extra, rows)을 완벽하게 분석해 보겠습니다.2. 튜닝의 핵심 지표: type 컬럼 (접근 방식)type 컬럼은 옵티마..

정리/DB 2026.05.19

[DB Deep Dive] 2. Clustered vs Non-Clustered 인덱스의 차이와 InnoDB의 비밀

1. 시작하며: 인덱스, 디스크 위에는 어떻게 저장될까?지난 1편에서는 인덱스의 논리적인 자료구조인 B+Tree에 대해 알아보았습니다. 하지만 이 B+Tree가 실제 하드 디스크(물리적 공간) 위에 어떻게 배치되는지에 따라 데이터베이스의 아키텍처는 완전히 두 갈래로 나뉘게 됩니다.오늘은 인덱스의 두 가지 핵심 물리적 구현체인 Clustered Index(클러스터드 인덱스)와 Non-Clustered Index(논클러스터드 인덱스)의 결정적인 차이를 알아보고, 실제 쿼리를 날릴 때 어떤 인덱스가 유리한지 성능을 비교해 보겠습니다. 2. 영어 사전 vs 찾아보기 색인 (Clustered vs Non-Clustered)데이터베이스 테이블에 인덱스를 거는 방식은 크게 두 가지 비유로 완벽하게 설명할 수 있습니다..

정리/DB 2026.05.18

[DB Deep Dive] 1. 내 쿼리는 왜 느릴까? 인덱스(Index)의 본질과 B+Tree가 선택받은 이유

1. 시작하며: 왜 내 쿼리는 데이터가 많아질수록 느려질까?우리가 구축한 플랫폼이나 대규모 서비스를 운영하다 보면, 초기에는 순식간에 끝나던 조회(SELECT) 쿼리가 데이터가 10만 건, 100만 건을 넘어가는 순간 급격하게 느려지는 현상을 마주하게 됩니다. 데이터베이스의 모든 레코드를 처음부터 끝까지 싹 다 뒤지는 전체 탐색(Full Scan)이 일어나기 때문입니다.이 문제를 해결하기 위해 백엔드 개발자가 손에 쥐어야 할 가장 강력한 무기가 바로 인덱스(Index)입니다. 오늘은 인덱스의 본질적인 개념과 장단점, 그리고 데이터베이스가 왜 하필 수많은 자료구조 중 B+Tree를 선택했는지 그 내부 깊은 곳의 작동 원리를 파헤쳐 보겠습니다. 2. 인덱스(Index)의 본질과 양날의 검 (Overhead)..

정리/DB 2026.05.18

순수 Java로 WAS 구현 (9) - 프론트 컨트롤러에 Jackson 도입하기: 역직렬화와 리플렉션의 비밀

1. 시작하며: GET 요청의 한계와 JSON 바디의 필요성지금까지 우리는 GET 방식을 통해 URL로 들어오는 요청을 프론트 컨트롤러로 동적 라우팅하는 데 성공했습니다. 하지만 실제 웹 서비스에서는 회원가입이나 로그인처럼 보안이 중요하고 데이터 양이 많은 경우 무조건 POST 방식을 사용해야 합니다.POST 방식은 데이터를 URL이 아닌 HTTP 메시지의 Body(본문) 안에 안전하게 숨겨서 보냅니다. 최근에는 이 본문 데이터로 JSON(JavaScript Object Notation) 포맷을 가장 많이 사용합니다. 이번 시간에는 클라이언트가 보낸 JSON 문자열을 자바 객체(DTO)로 변환해 주는 마법의 도구, Jackson 라이브러리를 도입해 보겠습니다. 2. Jackson 라이브러리와 역직렬화(D..

정리/WAS 2026.05.16

순수 Java로 WAS 구현 (8) - 프론트 컨트롤러(DispatcherServlet) 직접 구현과 HTTP 응답 조립

1. 시작하며: if-else 지옥에서 벗어나기이전 포스팅에서 우리는 수많은 API 요청을 if-else문으로 처리하는 것의 한계를 깨닫고, 프론트 컨트롤러(Front Controller) 패턴을 도입하기로 결정했습니다. 이번 시간에는 단 하나의 문지기 서블릿(DispatcherServlet)이 리플렉션(Reflection)을 무기로 모든 요청을 알맞은 컨트롤러에 동적으로 분배하는 마법을 직접 코드로 구현해 보겠습니다. 2. 아키텍처 설계 및 HandlerMapping (매핑 사전) 만들기가장 먼저 프론트 컨트롤러인 DispatcherServlet 클래스를 만듭니다. 이 문지기는 클라이언트가 요청한 URL(예: /user)을 보고, 실제 메모리에 있는 어떤 자바 클래스(예: org.example.cont..

정리/WAS 2026.05.16

순수 Java로 WAS 구현 (7) - 톰캣의 정체와 프론트 컨트롤러(Front Controller)의 탄생

1. 서론: 100개의 API, 100개의 if문?지금까지 우리는 클라이언트의 요청(HTTP 메시지)을 읽고, 파싱하고, 리플렉션이라는 마법의 도구까지 손에 넣었습니다. 하지만 막상 파싱된 URI(예: /login, /join, /board)를 바탕으로 비즈니스 로직을 실행하려고 하니 막막해집니다.만약 서버가 처리해야 할 API가 100개라면, RequestHandler 안에 if (uri.equals("/login")) { ... } else if (uri.equals("/join")) { ... } 처럼 100개의 분기문을 만들어야 할까요? 이는 유지보수 관점에서 끔찍한 일입니다. 이 문제를 과거의 선배 개발자들은 어떻게 해결했을까요? 2. 서블릿(Servlet)과 톰캣(Tomcat)의 진짜 정체이 ..

정리/WAS 2026.05.15

순수 Java로 WAS 구현 (6) - 프레임워크의 마법, 리플렉션(Reflection) 완벽 이해

1. 리플렉션(Reflection)이란?WAS나 스프링(Spring) 프레임워크를 쓰다 보면 문득 궁금해집니다. "내가 만든 클래스나 메서드 이름을 프레임워크가 어떻게 알고 실행해 주는 걸까?"그 해답이 바로 리플렉션(Reflection)입니다. 리플렉션은 구체적인 클래스 타입을 알지 못해도, 런타임(실행 중)에 클래스의 이름만으로 해당 클래스의 정보(메서드, 타입, 변수 등)에 접근하고 객체를 생성하거나 메서드를 호출할 수 있게 해주는 자바의 강력한 API입니다. 2. Class 객체를 가져오는 3가지 방법리플렉션을 시작하려면 가장 먼저 해당 클래스의 메타데이터가 담긴 Class 객체를 메모리(JVM 힙 영역)에서 가져와야 합니다. 상황에 따라 3가지 방법을 사용할 수 있습니다.// 예시로 사용할 타겟..

정리/WAS 2026.05.15

순수 Java로 WAS 구현 (5) - HTTP 요청 파서(Parser) 구현과 3가지 트러블슈팅

1. 아키텍처 설계: 점원과 번역가의 역할 분리지난번 HTTP 스펙 분석을 통해, 클라이언트가 보내는 요청이 결국 '정해진 규칙을 가진 긴 문자열'이라는 것을 알게 되었습니다. 이제 이 문자열을 잘라서 의미 있는 자바 객체로 변환해 주는 파서(Parser)를 만들어야 합니다.본격적인 코딩에 앞서, 객체지향적인 책임 분배를 위해 클래스 구조를 다음과 같이 설계했습니다.WebApplicationServer: 가게 입구(Port 8080)를 지키며 소켓 연결을 무한 대기합니다.RequestHandler: 손님이 오면 할당되는 '점원(Thread)'입니다. 입출력 스트림을 관리합니다.HttpRequestParser: 문자열을 잘라주는 '번역가'입니다. (유틸리티 클래스로 활용)HttpRequest: 파싱된 결과..

정리/WAS 2026.05.15