본문 바로가기

Spring36

백엔드 성능 최적화 - Redis를 활용한 리모트 캐싱 들어가며백엔드 애플리케이션에서 성능 병목 현상이 발생하는 가장 흔한 원인 중 하나는 데이터베이스 조회입니다. 특히 동일한 데이터를 반복적으로 조회하거나, 복잡한 연산이 필요한 데이터를 매번 새로 계산하는 것은 시스템 전체의 성능을 크게 저하시킬 수 있습니다. 이런 문제를 해결하기 위한 핵심 기술이 바로 캐싱입니다. 캐싱이란 무엇인가캐싱은 자주 사용되거나 계산 비용이 높은 데이터를 임시로 저장해두었다가, 동일한 요청이 들어올 때 빠르게 응답할 수 있도록 하는 기술입니다. 데이터베이스 조회 대신 메모리에서 데이터를 가져오므로 응답 속도가 현저히 빨라집니다. Redis란Redis(Remote Dictionary Server)는 메모리 기반의 키-값 저장소로, 다음과 같은 특징을 가지고 있습니다.인메모리 데이터.. 2025. 7. 14.
JPA 컬렉션 페치 조인의 페이징 문제와 해결 전략 문제 상황: 컬렉션 페치 조인 + 페이징의 함정안녕하세요! 오늘은 많은 개발자분들이 JPA를 사용하면서 한 번쯤은 겪어보셨을 컬렉션 페치 조인과 페이징의 문제에 대해 다뤄보겠습니다. 문제가 되는 코드 예시@Repositorypublic interface TodoRepository extends JpaRepository { // ❌ 이런 코드를 작성하면 문제가 발생합니다 @Query("SELECT t FROM Todo t JOIN FETCH t.comments WHERE t.user.id = :userId") Page findTodosWithComments(@Param("userId") Long userId, Pageable pageable);} 왜 문제가 될까요?핵심 원인: 카테시.. 2025. 6. 27.
JPA 영속성 관리 (4편) : 대용량 데이터 처리와 성능 최적화 전체 시리즈 목차1편: N+1 문제 해결과 Fetch Join 전략 2편: OSIV 패턴과 API 설계 전략3편: 영속성 컨텍스트와 엔티티 생명주기 관리4편: 대용량 데이터 처리와 성능 최적화 ← 현재1. 대용량 데이터 처리 시 메모리 폭발 문제문제 상황우리 서비스에서 100만 명의 사용자에게 일괄 알림을 보내야 하는 상황이 발생했습니다.// ❌ 이렇게 했더니 서버가 터졌습니다!@Service@Transactionalpublic class NotificationService { public void sendBulkNotification() { List users = userRepository.findAll(); // 100만명 조회 for (User u.. 2025. 6. 27.
JPA 영속성 관리 (3편) : 영속성 컨텍스트와 엔티티 생명주기 관리 전체 시리즈 목차1편: N+1 문제 해결과 Fetch Join 전략2편: OSIV 패턴과 API 설계 전략3편: 영속성 컨텍스트와 엔티티 생명주기 관리 ← 현재4편: 대용량 데이터 처리와 성능 최적화1. 영속성 컨텍스트의 핵심 원리같은 트랜잭션 내에서의 동일성 보장JPA의 가장 중요한 원칙 중 하나는 같은 트랜잭션 안에서는 항상 같은 영속성 컨텍스트에 접근한다는 것입니다.@Service@Transactionalpublic class TodoService { private final TodoRepository todoRepository; public void demonstratePersistenceContext(Long todoId) { // 첫 번째 조회 .. 2025. 6. 27.
JPA 영속성 관리 (2편) : OSIV 패턴과 API 설계 전략 전체 시리즈 목차1편: N+1 문제 해결과 Fetch Join 전략2편: OSIV 패턴과 API 설계 전략 ← 현재3편: 영속성 컨텍스트와 엔티티 생명주기 관리4편: 대용량 데이터 처리와 성능 최적화1. OSIV(Open Session In View) 패턴 이해하기OSIV란 무엇인가?JPA에서는 OEIV(Open EntityManager In View)라고 하지만, 관례적으로 OSIV로 통일해서 사용합니다. Spring Boot에서는 기본적으로 활성화되어 있습니다.# application.ymlspring: jpa: open-in-view: true # 기본값: true OSIV 동작 원리더보기 OSIV의 장점:복잡한 Fetch Join 전략 없이도 필요한 데이터에 접근 가능컨트롤러 레벨에서 동.. 2025. 6. 27.
JPA 영속성 관리 (1편) : N+1 문제 해결과 Fetch Join 전략 전체 시리즈 목차1편: N+1 문제 해결과 Fetch Join 전략 ← 현재2편: OSIV 패턴과 API 설계 전략3편: 영속성 컨텍스트와 엔티티 생명주기 관리4편: 대용량 데이터 처리와 성능 최적화1. N+1 문제의 근본 원인왜 N+1 문제가 발생할까?많은 개발자들이 놓치는 핵심 사실이 있습니다. JPA가 JPQL을 분석해서 SQL을 생성할 때는 글로벌 페치 전략을 참고하지 않고 오직 JPQL 자체만 사용한다는 것입니다.@Entitypublic class Comment { @ManyToOne(fetch = FetchType.LAZY) // 글로벌 페치 전략 private User user;}// 문제가 되는 JPQL@Query("SELECT c FROM Comment c WHERE c.tod.. 2025. 6. 27.
JPA가 해결하는 근본적인 문제 문제의 출발점안녕하세요, 오늘은 JPA가 왜 필요한지에 대해 이야기해보려고 합니다. 우리 개발자들은 객체지향 프로그래밍을 합니다. 추상화, 캡슐화, 상속, 다형성 같은 원칙들을 지키면서 객체를 중심으로 아키텍처와 모델링을 하죠. 그런데 여기서 문제가 생깁니다.태생적인 간극데이터베이스에는 객체라는 개념이 존재하지 않습니다.관계형 데이터베이스는 순수하게 데이터를 저장하는 곳입니다. 테이블과 컬럼, 그리고 관계만 있을 뿐이죠.객체지향 세계: Member → Team (참조 관계)관계형 DB 세계: member 테이블 → team_id (외래키)완전히 다른 두 세계입니다.SQL로는 한계가 있었다이 간극을 메우기 위해 우리는 SQL이라는 언어를 사용했습니다.하지만 이 간극은 쉽게 좁혀지지 않았어요.// 우리가 원.. 2025. 6. 25.
JPA : 객체와 관계형 DB의 패러다임 불일치를 해결하는 방법 왜 JPA가 필요한가?객체지향과 관계형 데이터베이스의 근본적인 차이객체지향 프로그래밍을 하다 보면 자연스럽게 객체 모델링을 하게 됩니다. 하지만 데이터를 저장할 때는 관계형 데이터베이스를 사용해야 하죠. 여기서 패러다임 불일치 문제가 발생합니다.// 우리가 원하는 객체지향적 코드class Member { private String name; private Team team; // 객체 참조 public Team getTeam() { return team; // 객체 그래프 탐색 }}// 하지만 실제로는...class MemberDAO { public Member findMember(Long memberId) { // SQL에 종속적인 코드.. 2025. 6. 25.
데이터베이스 인덱스의 구조와 특징 개요데이터베이스에서 빠른 검색을 위해 사용되는 인덱스는 여러 가지 자료구조로 구현됩니다. 이 글에서는 주요한 인덱스 구조들의 특징과 장단점을 살펴보겠습니다.인덱스의 주요 구조1. Binary Search Tree (BST)구조 특징:이진 탐색 트리 구조각 노드가 최대 2개의 자식 노드를 가짐왼쪽 자식은 부모보다 작고, 오른쪽 자식은 부모보다 큰 값검색 성능:평균 시간복잡도: O(log n)최악의 경우: O(n) - 편향 트리일 때 2. B-Tree 구조 특징:다진 탐색 트리 구조각 노드가 여러 개의 키를 가질 수 있음모든 리프 노드가 같은 레벨에 위치자식 노드의 개수가 2개 이상으로 확장 가능주요 장점:디스크 I/O 최적화균형 잡힌 트리 구조 유지 3. B+ Tree구조 특징: .. 2025. 6. 18.
JPA 엔티티에서 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용하는 이유 들어가며JPA 엔티티 클래스를 작성하다 보면 다음과 같은 코드를 자주 접하게 됩니다.@Entity@NoArgsConstructor(access = AccessLevel.PROTECTED)public class User { // 필드들...}왜 AccessLevel.PROTECTED를 사용하는 걸까요? PUBLIC이나 PRIVATE는 안 될까요? 이번 글에서는 JPA의 프록시 메커니즘과 객체지향 설계 원칙을 통해 그 이유를 알아보겠습니다.JPA가 기본 생성자를 요구하는 이유 1. 프록시 객체 생성JPA는 지연 로딩(Lazy Loading)을 위해 프록시 객체를 생성합니다. 프록시는 실제 엔티티를 상속받은 가짜 객체로, 필요할 때만 실제 데이터를 로딩합니다.// 실제 엔티티@Entitypublic cl.. 2025. 6. 16.
Static 메서드, 언제 사용하고 언제 피해야 할까? 들어가며Spring Boot 프로젝트를 진행하다 보면 ApiResponse.success(data)와 같은 static 메서드를 자주 접하게 됩니다. 하지만 언제 static 메서드를 사용해야 하고, 언제 일반 인스턴스 메서드를 사용해야 하는지 명확한 기준을 정하기는 쉽지 않습니다.이번 글에서는 실제 코드 예시를 통해 static 메서드의 적절한 사용 시기와 피해야 할 상황을 알아보겠습니다.Static 메서드를 사용하는 이유 1. 편의성과 가독성먼저 ApiResponse 클래스를 예로 들어보겠습니다.// Static 메서드가 없다면@PostMapping("/users")public ResponseEntity> createUser() { User user = userService.createUser(.. 2025. 6. 16.
Spring Bean 생명주기 - 실무에서 알아야 할 핵심만 초기화와 소멸 콜백 방법Bean의 초기화와 소멸 시점에 특정 작업을 수행하는 3가지 방법을 알아보겠습니다.1. @PostConstruct / @PreDestroy (권장)@Servicepublic class FileService { private FileWriter fileWriter; @PostConstruct public void init() { try { fileWriter = new FileWriter("application.log"); System.out.println("파일 연결 완료!"); } catch (IOException e) { throw new RuntimeExcepti.. 2025. 6. 5.