728x90
반응형
SMALL

분류 전체보기 220

[Spring Boot] 서버 재시작을 하지 않고 static 파일 변경 적용하기

Spring boot로 개발을 하고 서버사이드 렌더링을 위해 Thymeleaf를 사용하다보면 template 파일 수정이 빈번하게 일어난다. 그 때마다 변경사항을 반영하기 위해 서버를 재시작하기는 꽤 귀찮은 일이다. 이 때 개발환경에서는 spring-boot-devtools라는 라이브러리를 사용해서 좀 더 간편하게 변경사항을 반영시킬 수 있다. build.gradle implementation 'org.springframework.boot:spring-boot-devtools' build.gradle 파일에서 spring-boot-devtools 라이브러리를 하나 추가해준다. 서버를 재시작 하면 서버 실행 로그의 쓰레드의 이름이 restartedMain으로 보여진다. 이러면 spring-boot-devt..

[JPA] Part 18. 벌크 연산

쿼리 한 번으로 여러 테이블 로우(레코드)를 변경하는 걸 말한다. UPDATE, DELETE 같은 것들이 이제 벌크 연산이 가능하다. 뭐 별건 아닌데 조금 주의할 사항이 있다. 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리한다. 이게 뭐가 문제냐면 벌크 연산을 처리한 전과 후에 만약 영속성 컨텍스트에 관리하고 있는 레코드(객체)가 있다면 벌크 연산이 적용되지 않은 채 영속성 컨텍스트에 그대로 관리될 수 있다. 그래서 이걸 해결하는 방법은 벌크 연산을 처리하고 영속성 컨텍스트를 초기화하는 것이다. 또는 영속성 컨텍스트에 뭔가를 관리하기 전 벌크 연산을 먼저 수행하는 것이다. 예를 들어보자. 멤버 3명이 있고 최초에 age를 0으로 설정했다. 그리고 영속성 컨텍스트에 멤버 3명을 영속시켰는..

[JPA] Part 17. Named Query

Named Query란, 문자열로만 이루어진 쿼리문에 이름을 부여해서 가져다가 사용할 수 있는 방식을 말하는데, 이게 굉장히 유용하다. 특히 Spring Data JPA와 같이 사용하게 된다면. 우선 Spring Data JPA가 없다고 가정했을 때 작성하는 방법은 @Entity 어노테이션이 있는 엔티티에 @NamedQuery 어노테이션을 사용하면 된다. @Entity @NamedQuery( name = "Member.findByUsername", query = "select m from Member m where m.username = :username") public class Member { ... } 이처럼 사용할 수 있고 이거를 가져다가 사용하는 쪽은 다음과 같이 사용하면 된다. List res..

[JPA] Part 16. Fetch JOIN (JPQL)

페치 조인은 JPQL에서 엄청 엄청 중요한 내용 중 하나라는 생각이 든다. 왜 그러냐면 이 페치 조인이 연관된 엔티티를 SQL 한 번에 다 가져올 수 있는 방법인데 이게 필요한 경우가 상당수 존재하며, 이 방식을 사용하지 않은 상태에서 지연 로딩을 걸고 필요할 때마다 가져오는 방식만을 사용한다면 가져와야 하는 연관 엔티티가 필요할 때마다 SQL이 계속 나가게 된다. 가장 쉬운 예시로 멤버를 조회할 때 멤버가 속한 팀까지 알아와야 하는 경우에 페치 조인을 사용하지 않고 지연 로딩인 상태에서 멤버 100명을 조회하면 멤버 100명을 조회하는 쿼리 한 번이 우선 나가게 된다. 여기서 팀은 같이 조회되지 않는다 지연 로딩이기 때문에. 그리고 실제 멤버의 팀을 호출하는 순간에 팀을 알아오는 SQL이 나가는데 멤버..

[JPA] Part 15. JPQL

JPQL(Java Persistence Query Language)는 데이터베이스의 SQL과 유사하나 객체지향 쿼리 언어이며 그렇기에 테이블을 대상으로 쿼리하는 게 아니라 엔티티 객체를 대상으로 쿼리한다는 차이점이 있다. JPQL은 SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다. 이 말은 데이터베이스마다 다른 방언(MySQL은 LIMIT, Oracle은 ROWNUM 같은)에 상관없이 동작한다는 의미이다. 그러나 결국, DB는 SQL만을 받기 때문에 JPQL은 결국에는 SQL로 변환된다. 이 JPQL을 잘 이해하고 사용할 줄 알아야 기본적인 쿼리문을 사용하는데 문제가 없고 복잡한 쿼리를 처리해주는 QueryDSL도 편하게 사용할 수 있다. 그래서 JPQL을 잘 사용할 줄 알아야하는 것 같다..

[JPA] Part 14. 컬렉션 값 타입

엔티티에 컬렉션으로 관리하는 데이터는 흔히 있을 수 있는 일이지만 DB는 기본적으로는 컬렉션 데이터를 지원하지 않는다. 물론 요즘은 여러 방법으로 컬렉션을 테이블에서 관리할 수 있지만(JSON으로 데이터를 저장한다든지 등) 그러나 정석적인 방법은 컬렉션 데이터를 테이블 화해서 참조로 관리하는 것이다. 아래 그림을 보자. 멤버라는 엔티티가 관리하는 데이터 favoriteFoods와 addressHistory는 컬렉션 값 타입이다. 이런 엔티티를 테이블화 하기 위해서는 각 컬렉션 값 타입을 테이블로 분리해서 1:N 관계로 만드는 것이다. 이게 정석적인 방법이다. 위 그림을 보면 Address도 FavoriteFood도 모든 필드가 하나의 PK인데 이 이유는 컬렉션 '값 타입'이기 때문이다. 값 타입은 하나의..

[JPA] Part 13. 임베디드 타입

임베디드 타입은 꽤나 사용성을 높여준다. 임베디드 타입은 무엇이냐면 특정 엔티티에 필요한 필드를 클래스타입으로 받는 경우이다. 아래 예를 보자. 좌측 테이블은 기본타입으로만 설정된 테이블이다. 물론 이게 잘못된 건 아니다. 근데 기본 타입이 아니고 클래스로 설계된 타입을 사용할 때 얻는 이점이 매우 많기 때문에 이를 우측 테이블인 임베디드형 테이블로 변환할 수 있다. 임베디드 타입을 사용시 어떤 이점이 있을까? 재사용성 높은 응집도 의미 있는 메서드를 만들어 사용할 수 있고 그에 따라 객체 지향형 설계가 가능해짐 예) Period.isWork()와 같은 메서드를 만들어서 해당 객체에서만 사용되는 메서드를 Period 클래스에서 구현 가능 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에..

[JPA] Part 12. CASCADE

CASCADE는 영속성 전이를 어떤식으로 동작시킬 거냐를 지정하는 옵션이다. 일단 가장 먼저 말할거는 이 CASCADE는 연관관계나 지연로딩 즉시로딩과 전혀 아무런 상관이 없다. 그저 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티를 어떻게 할건지를 지정한다. 예를 들면 부모를 저장할 때 부모에 연관된 자식도 같이 저장하는 경우를 말한다. 그러니까 CASCADE는 부모 엔티티에서만 설정한다고 봐도 사실 무방하다고 본다. 말로는 잘 이해가 안되고 코드로 보면 바로 이해가 된다. Parent Class package org.example.entity.cascade; import javax.persistence.*; import java.util.ArrayList; import java.util.List; ..

[JPA] Part 11. 지연로딩과 즉시로딩

지연로딩과 즉시로딩에 대해 공부한 내용을 적고자 한다. 내가 JPA를 처음 이론적인 공부를 하지 않고 그냥 무작정 사용했을 때 이런 내용이 있는지도 사실 모르고 데이터를 받아올 때 무수히 많은 SQL문을 남발하곤 했는데, 그 남발하게된 SQL문의 원인 중 하나가 여기에 있다. 우선 지연로딩과 즉시로딩은 JPA가 데이터를 데이터베이스로부터 조회할 때 조회할 레코드에 참조 객체(테이블 관점에서는 외래키)가 있는 경우 해당 데이터까지 한꺼번에 다 가져올지 말지를 정하는 기준을 말한다. 다음 상황을 가정해보자. 팀 엔티티와 멤버 엔티티가 있고 팀과 멤버는 일대다 관계이다. 이 때 멤버를 조회할 때 팀도 한번에 조회해야 할까? 코드로 이를 직접 비교해보자. 지연로딩(LAZY Fetch) Member Class p..

[JPA] Part 10. 프록시

JPA에서 중요한 개념 중 하나인 프록시라는 게 있다. 중요한 개념이라기보단 중요하게 사용되는 지연로딩과 즉시로딩을 사용하려면 반드시 깊은 이해가 필요하다고 개인적으로 생각한다. 왜 그러냐면 회사에서 프로젝트를 진행 중에 이해도가 깊지 않은 상태에서 지연로딩을 마구잡이로 썼다가 프록시 초기화 오류를 진짜 무진장 만났던 기억이 아직도 생생하다. 우선 서사는 이렇다. 멤버라는 테이블과 팀이라는 테이블이 있을 때를 가정해보자. 멤버는 팀에 속하고 아래 그림과 같다. 이런 구조를 가진 두 테이블이 있을 때 멤버를 조회한다고 가정해보자. 특정 멤버를 조회할 때 팀도 같이 조회해야 할까? 예를 들어 이런 메서드가 있다고 생각해보자. private static void printMember(EntityManager ..

728x90
반응형
LIST