[쿼리튜닝] 공통쿼리 개선
문제 상황 시스템의 주요 통합 조회 화면에서 조회시간이 오래걸려 사용자들의 불만이 지속적으로 발생했습니다. 확인 결과 조회쿼리가 공통 쿼리로 사용되고 있어 20개 이상의 팝업과 그리드에서 동시에 사용 중이었고, 모든 화면의 응답시간이 평균 4.5초로 매우 느렸습니다. 하나의 공통 쿼리를 호출하는 수많은 페이지들….. 해당 공통쿼리 실행계획 ...
문제 상황 시스템의 주요 통합 조회 화면에서 조회시간이 오래걸려 사용자들의 불만이 지속적으로 발생했습니다. 확인 결과 조회쿼리가 공통 쿼리로 사용되고 있어 20개 이상의 팝업과 그리드에서 동시에 사용 중이었고, 모든 화면의 응답시간이 평균 4.5초로 매우 느렸습니다. 하나의 공통 쿼리를 호출하는 수많은 페이지들….. 해당 공통쿼리 실행계획 ...
📚 파일 업로드 시리즈: [DevBid - AWS] S3 파일업로드 - 기본 구현과 발견된 문제점 파일업로드 Presigned URL 리팩토링 (현재 글) 기존 방식의 문제점 이전글 S3 파일업로드에서 서버를 경유하는 방식으로 S3 파일 업로드를 구현했다. 하지만 실제 운영을 생각해보면 몇 가지 문제가 있었다. 서...
📚 파일 업로드 시리즈: AWS S3 파일업로드 (현재 글) [DevBid - AWS] 파일업로드 Presigned URL 리펙토링 개요 개인 프로젝트 DevBid 경매 플랫폼에서 상품 이미지 업로드 기능을 구현했다. 판매자가 경매 등록 시 대표 이미지 1장과 추가 이미지 최대 4장까지 업로드할 수 있도록 했다. 초...
리팩터링 계기 개인 프로젝트를 만들면서 URL을 대충 /products_main, /product/new 이런 식으로 만들었는데, 김영한 선생님의 HTTP 강의를 들으면서 “아, 내 URL 설계가 엉망이구나”를 깨닳았다. 특히 RESTful 원칙을 배우면서 URL은 자원(리소스)을 표현해야 한다 동사가 아닌 명사 중심으로 HTTP 메서드...
개요 DevBid의 상품 등록 페이지에서 카테고리 트리를 로드할 때 N+1 문제가 발생했다. 루트 카테고리 수만큼 추가 쿼리가 실행되어 총 9개의 SQL이 발생했고, 이를 3개로 줄이는 과정을 다룬다. 상품 등록 페이지 로딩 시 Category 트리 로딩 때문에 9개의 SQL 발생 재귀 DTO 변환에서 category.getChildren...
JPQL (Java Persistence Query Language) JPQL란? 테이블이 아닌 객체를 검색하는 객체지향 쿼리 --SQL: 테이블 대상 SELECT * FROM MEMBER WHERE USERNAME = 'kim'; --JPQL: 엔티티 객체 대상 SELECT m FROM Member m WHERE m.username = 'ki...
Proxy 프록시란? 실제 엔티티 대신 사용되는 가짜 객체 // DB를 통해 실제 엔티티 객체 조회 Member member = em.find(Member.class, 1L); // DB조회를 미루는 가짜 프록시 엔티티 객체 조회 Member member = em.getReference(Member.class, 1L); System.out.p...
객체와 테이블의 연관관계 차이 테이블의 연관관계 테이블은 외래키 하나로 양방향 조회가 가능하다. -- MEMBER 테이블 ID (PK) USERNAME TEAM_ID (FK) -- TEAM 테이블 ID (PK) NAME -- 양방향 조회가 모두 가능 -- 멤버 → 팀 조회 SELECT * FROM MEMBER M JOIN TEAM T ON M...
JPA(Java Persistence API) SQL 없이 객체를 데이터베이스에 직접 저장할 수 있게 하고, 객체와 관계형 데이터베이스를 명확히 구분해준다. 즉, 중간에서 매핑 역할을 수행하는 ORM(Object-Relational Mapping) 기술의 표준 인터페이스이다. 객체지향과 관계형 데이터베이스의 패러다임 불일치를 해결해준다. ...
점점 비대해지는 savePlan 공급사 점검계획 등록/수정 기능을 만들다가 요구사항이 추가되면서 하나의 메서드에 모든 로직이 쌓이기 시작했다 // 최초저장 if (vo.getPlanId() == null || vo.getPlanId().isEmpty()) { planRepository.save(vo); processAddedSchedu...