스프링과 JPA 기반 웹 애플리케이션 개발 #56 스터디 조회 (+textarea 태그, +PSQL의 Large Object [LOB])

Jake Seo·2021년 6월 18일
0

스프링과 JPA 기반 웹 애플리케이션 개발 #56 스터디 조회 (+textarea 태그, +PSQL의 Large Object [LOB])

해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.

강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.

제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.


input type="textarea" 는 없는 태그이다.

        <!-- input type="textarea"는 존재하지 않는다. textarea 태그를 써야 함.. -->
        <textarea id="shortDescription" th:field="*{shortDescription}" class="form-control"
               placeholder="스터디를 짧게 소개해주세요." aria-describedby="shortDescriptionHelp" required />

textarea 태그를 사용해야 하는데 자꾸 input 태그의 type 속성에 textarea를 넣는 실수를 했다. 다음부턴 꼭 하지 말아야겠다. 이거 때문에 th:field가 안먹혔는데, 자꾸 원인을 이상한데서 찾아서 삽질만 1시간 했다.

clob 형태의 데이터를 postgres db에서 조회하면 숫자만 나온다.

나는 분명 긴 문자열을 입력했는데, 숫자만 나와서 당황했다. 알고보니 문자열은 제대로 저장이 되어 있었는데 clob(postgres에서는 text)타입을 DB에 저장하면 select으로 조회했을 때, 저렇게 숫자만 나온다. jdbc 드라이버를 이용해서 조회하니 값이 제대로 나온다. 쿼리에서도 문자열 그대로 볼 수 있는 방법이 있는 것 같은데, 찾다가 못찾았다.

https://stackoverflow.com/questions/53377735/postgresql-values-of-column-type-text-are-shown-as-numbers 앞의 링크를 클릭해보면, 사람들이 말하기로는 hibernate의 문제인 것 같다고 한다.

@Type(type = "org.hibernate.type.TextType")을 적용해주면 제대로 나온다

@Lob @Type(type = "org.hibernate.type.TextType") @Basic(fetch = FetchType.EAGER)
private String fullDescription;
@Lob @Type(type = "org.hibernate.type.TextType") @Basic(fetch = FetchType.EAGER)
private String image;

아마 Hibernate에서 벤더별로 상이한 @Lob 타입을 그냥 동일한 방식으로 적용시키기 위해서 일 수도 있고 혹은 이전처럼 숫자로만 가리키는게 성능이 더 좋거나 하는 이유가 있을 수도 있다.

https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/type/TextType.html

여기 보면 타입에 대한 정보가 나온다.

https://www.baeldung.com/jpa-annotation-postgresql-text-type

여기에는 jpa @Lob에 대한 정보가 나온다. text 타입의 필드를 만들어놓고 hibernate에게 관리를 위임한다고만 나와있다.

http://www.solewing.org/blog/2015/08/hibernate-postgresql-and-lob-string/

위에 자세한 정보가 나와있다.

간략 설명

Lob(Large Object)을 관리하는 것은 디비에서 표준화가 되어있지 않다고 나온다.

select
       id,
       convert_from(
           loread(
               lo_open(full_description::int, x'40000'::int),
               x'40000'::int
               )
           , 'UTF-8') as full_descrip
from study;

위와 같이 psql의 내부 함수를 이용하면 실제 내용을 볼 수 있다. psql에 large object 저장소가 있다. 위에 기재된 psql의 메소드들로 해당 데이터의 실제 문자열을 조회할 수 있다. x'40000'::int 라는 게 의미하는 것은 읽기 전용(INV_READ) 모드로 해당 오브젝트를 열겠다는 것이다.

공식 문서 내용

study/view.html 생성

조회용 HTML이다. 대부분의 내용은 fragments.html에서 가져온다.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>스터디 상세 페이지</title>
    <th:block th:replace="fragments :: headLibraryInjection"></th:block>
</head>
<body class="bg-light">
<th:block th:replace="fragments :: main-nav"></th:block>
<th:block th:replace="fragments :: study-banner"></th:block>

<div class="container">
    <th:block th:replace="fragments :: study-info"></th:block>
    <th:block th:replace="fragments :: study-menu (studyMenu='info')"></th:block>

    <div class="row px-3 justify-content-center">
        <!-- th:utext 는 내부적으로 들어있는 html 을 렌더링해서 보여주는 역할을 한다. -->
        <div class="col-10 pt-3" th:utext="${study.fullDescription}"></div>
    </div>

    <th:block th:replace="fragments :: footer"></th:block>
</div>

<script th:replace="fragments :: form-validation"></script>
<script type="application/javascript">
    $(function() {
        $('[data-toggle="tooltip"]').tooltip()
    })
</script>
</body>
</html>
  • th:utexthtml 형식으로 된 텍스트를 html로 렌더링해서 보여주는 역할을 한다.

EntityGraph를 이용해 데이터 한번에 불러오기

이건 이번에 처음 써보는 기능이었다.

Study

// EAGER FETCH 할 수 있도록
@NamedEntityGraph(name = "Study.withAll", attributeNodes = {
        @NamedAttributeNode("studyAccounts"),
        @NamedAttributeNode("studyTags"),
        @NamedAttributeNode("studyZones")
})
@Entity
@Getter @Setter @EqualsAndHashCode(of = "id")
@Builder @NoArgsConstructor @AllArgsConstructor
public class Study {

Study 클래스에 @NamedEntityGraph라는 애노테이션을 위와 같이 작성했다. 해당 EntityGraph를 이용할 때는 @NamedAttributeNode()에 기재된 @OneToMany 관계로 매핑된 테이블도 한번에 join을 이용해 가져오겠다는 의미이다.

쿼리 개수를 줄이고 싶을 때 EntityGraph를 사용한다. 하지만, 되려 조인을 이용하기 때문에 속도가 더 느려질 수도 있으니 주의해야 한다. 그 경우에는 @EntityGraph 말고, batch_fetch_size를 이용한 방법이나, fetch join을 고려해야 할 것이다.

참고: 실전! 스프링 부트와 JPA 활용 2 - API 개발과 성능 최적화 #14 주문 조회 V3.1: 엔티티를 DTO로 변환 - 페이징과 한계돌파
자바 ORM 표준 JPA 프로그래밍 - 기본편 #48 페치 조인 2 - 한계

StudyRepository

...
// 엔티티 그래프에 명시한 타입은 EAGER 모드로 가져오고, 나머지는 기본 타입에 따른다.
    @EntityGraph(value = "Study.withAll", type = EntityGraph.EntityGraphType.LOAD)
    // 요청이 많을 때는 쿼리 개수를 줄이는 게 도움이 될 확률이 높다.
    // 그러나 언제나 무거운 쿼리 1개를 보내는 게 나은지, 가벼운 쿼리 여러개를 보내야 하는지
    // 양 방향 모두 고려해봐야 한다.
    // 이 방법으로 N+1 문제도 해결해볼 수 있다.
    Study findByPath(String path);

Repository@EntityGraph라는 애노테이션을 작성해주면, 이제 findByPath 메소드를 사용할 때는 엔티티 그래프를 이용해 쿼리 한번에 데이터를 가져온다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글