1. 엔티티 매핑 관련
어노테이션설명
@Entity 클래스가 JPA 엔티티임을 나타냅니다.
@Table(name = "table_name") 엔티티와 매핑되는 테이블 이름을 지정합니다.
@Id 기본 키 필드를 지정합니다.
@GeneratedValue(strategy = GenerationType.IDENTITY) 기본 키 값을 자동 생성합니다.
@Column(name = "column_name") 특정 필드를 테이블 컬럼과 매핑합니다.
@Transient 데이터베이스에 저장하지 않는 필드를 선언합니다.
@Embedded 값 타입 객체를 포함시킬 때 사용합니다.

예시 코드

package com.example.demo.hr;

import java.sql.Date;
import java.util.List;

import org.hibernate.annotations.BatchSize;

import com.fasterxml.jackson.annotation.JsonIgnore;

import jakarta.annotation.Generated;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString(exclude={"job","manager","dept"})
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name="tbl_employees")
public class EmpEntity {

	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	Long employee_id;
	
	String first_name;
	String last_name;
	String email;
	String phone_number;
	Date hire_date;
	
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="job_id")//생성시 칼럼 이름은 //외래키 만드는 구문
	JobEntity job;
	
	Long salary;
	Double commission_pct;
	//fetch는 조회연관,cascade는 DML 연관 (나의 ENTITY 변화가 연관관계 ENTITY 영향)
	@ManyToOne
	@JoinColumn(name="manager_id")//생성시 칼럼 이름은 //외래키 만드는 구문
	EmpEntity manager;
	
	
	@ManyToOne(cascade =CascadeType.ALL ,
			fetch=FetchType.LAZY)
	@JoinColumn(name="department_id")//생성시 칼럼 이름은 //외래키 만드는 구문
	DeptEntity dept;
	


}
 

2. 연관 관계 매핑

어노테이션설명
@OneToOne 1:1 관계를 매핑합니다.
@OneToMany 1:N 관계를 매핑합니다.
@ManyToOne N:1 관계를 매핑합니다.
@ManyToMany N:M 관계를 매핑합니다.
@JoinColumn(name = "fk_name") 외래 키 컬럼을 지정합니다.
@JoinTable 다대다 관계에서 조인 테이블을 지정합니다.
@Cascade (Hibernate) 연관된 엔티티에 대한 연산을 전파합니다.

 

3. 데이터 로딩 방식 (FetchType)

어노테이션설명
fetch = FetchType.LAZY 지연 로딩
fetch = FetchType.EAGER 즉시 로딩

4. 상속 매핑

어노테이션설명
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 단일 테이블 전략
@DiscriminatorColumn(name = "type") 상속된 엔티티 타입 구분 컬럼 지정

5. 기타 유용한 어노테이션

어노테이션설명
@Enumerated(EnumType.STRING) Enum 타입 필드를 문자열로 저장
@Temporal(TemporalType.DATE) 날짜 및 시간 매핑
@Lob 대용량 데이터 매핑
@PrePersist, @PostLoad 엔티티 생명주기 이벤트 처리

6. 생명주기 이벤트 콜백

어노테이션설명
@PrePersist 엔티티가 저장되기 전에 호출
@PostPersist 엔티티가 저장된 후 호출
@PreUpdate 업데이트 전에 호출
@PostUpdate 업데이트 후 호출
@PreRemove 삭제 전에 호출
@PostRemove 삭제 후 호출
@PostLoad 엔티티 조회 후 호출

'SpringBoot > JPA' 카테고리의 다른 글

JPA EAGER / LAZY  (0) 2025.02.06
JPQL  (0) 2025.02.03

JPA에서 EAGER와 LAZY는 엔티티 연관 관계를 조회할 때 데이터 로딩 방식을 지정하는 FetchType 옵션이다.

1. EAGER (즉시 로딩)

  • 연관된 엔티티를 즉시 조회합니다.
  • 부모 엔티티를 조회할 때 연관된 자식 엔티티도 즉시 함께 가져옵니다.

특징

  • N+1 문제가 발생할 수 있음
  • 즉시 로딩이므로 불필요한 데이터가 로드될 가능성 있음
  • 연관 데이터가 반드시 필요한 경우 적합

코드 예시

@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name="job_id")//생성시 칼럼 이름은 //외래키 만드는 구문
	JobEntity job;

 

2. LAZY (지연 로딩)

  • 연관된 엔티티를 실제로 접근하는 시점에 조회합니다.
  • Proxy 객체를 통해 데이터를 필요할 때만 로딩합니다.

특징

  • N+1 문제를 예방할 수 있음 (JPQL 또는 Fetch Join으로 최적화 가능)
  • 필요할 때만 데이터를 로드하여 메모리와 성능 최적화
  • 연관 데이터가 필수적이지 않을 경우 적합

코드 예시

@Entity public class Parent { @OneToMany(fetch = FetchType.LAZY) private List<Child> children; }

EAGER vs LAZY 요약

특징EAGER LAZY

로딩 시점 즉시 지연
성능 이슈 N+1 문제 위험 최적화 가능
사용 사례 데이터 필수 데이터 선택적

권장 사항

  • 기본적으로 FetchType.LAZY를 사용하는 것이 좋다
  • 필요한 경우에만 JPQL의 fetch join이나 @EntityGraph를 통해 최적화 하는것이 좋다

'SpringBoot > JPA' 카테고리의 다른 글

JPA 어노테이션  (0) 2025.02.06
JPQL  (0) 2025.02.03

JPQL (Java Persistence Query Language)**는 **JPA (Java Persistence API)에서 사용되는 쿼리 언어 객체 지향 프로그래밍 환경에서 데이터베이스에 접근하기 위해 사용되며, SQL과 유사하지만 엔티티(Entity)와 필드를 대상으로 쿼리를 작성하는 것이 특징

JPQL 특징

  • 객체 중심 쿼리 언어: JPQL은 SQL처럼 테이블과 컬럼을 대상으로 하지 않고 엔티티와 필드를 대상으로 쿼리
  • 데이터베이스 독립적: 특정 DBMS에 종속되지 않음
  • 표준 JPA 쿼리 방식: JPA가 지원하는 모든 데이터베이스에서 사용 가능

JPQL 예제

기본 JPQL

java
복사편집
// 모든 고객 정보 조회 String jpql = "SELECT c FROM CustomerEntity c"; TypedQuery<CustomerEntity> query = entityManager.createQuery(jpql, CustomerEntity.class); List<CustomerEntity> customers = query.getResultList();

위 쿼리에서:

  • CustomerEntity는 JPA 엔티티 클래스
  • c는 엔티티의 별칭(alias)
  • SELECT c는 CustomerEntity 객체를 가져옴

조건절 추가

java
복사편집
String jpql = "SELECT c FROM CustomerEntity c WHERE c.name = :name"; TypedQuery<CustomerEntity> query = entityManager.createQuery(jpql, CustomerEntity.class); query.setParameter("name", "Alice"); CustomerEntity customer = query.getSingleResult();

JPQL과 SQL 비교

특징JPQLSQL

대상 엔티티 테이블
필드 참조 엔티티 필드 컬럼 이름
표준화 JPA 표준 DB 종속적
사용 목적 객체 데이터 관리 DB 데이터 관리

JPQL 장점

  1. 데이터베이스 독립성: 데이터베이스 변경에 영향을 덜 받음
  2. 객체 지향 쿼리: 엔티티 객체를 기반으로 쿼리 작성
  3. 코드 가독성: 엔티티 기반으로 이해하기 쉬운 쿼리 작성

정리

JPQL은 JPA의 핵심 기능 중 하나로, 객체 지향 프로그래밍과 관계형 데이터베이스 사이의 간극을 줄여줍니다. SQL과 비슷한 구문을 사용하면서도 객체를 직접 다룰 수 있다는 점에서 효율적입니다. JPA 기반 프로젝트에서는 필수적으로 알아야 하는 기술입니다.

'SpringBoot > JPA' 카테고리의 다른 글

JPA 어노테이션  (0) 2025.02.06
JPA EAGER / LAZY  (0) 2025.02.06

프로젝트 생성시 jar 로 생성함

 

application.properties

server.port=7777 //서버 포트 설정 

server.servlet.context-path=/shinhan  //contextpath 설정

'SpringBoot' 카테고리의 다른 글

Spring Boot JPA 와 Mapper  (0) 2024.06.17
1. 청크 전송 인코딩(Chunked Transfer Encoding)란?
  • HTTP/1.1에서 사용하는 데이터 전송 방식 중 하나입니다.
  • 데이터를 한꺼번에 전송하지 않고 작은 청크(chunk) 단위로 나눠 순차적으로 클라이언트에 보냅니다.
  • 마지막 청크는 길이가 0으로 표시되어 전송이 종료되었음을 알립니다.

예:

http
코드 복사
HTTP/1.1 200 OK Transfer-Encoding: chunked 4 Wiki 5 pedia 0
  • 문제 발생: 만약 서버가 전송을 완료하지 못하고 연결이 끊기거나, 마지막 청크(0)가 제대로 전달되지 않으면 클라이언트는 응답이 불완전하다고 판단해 ERR_INCOMPLETE_CHUNKED_ENCODING 에러를 표시합니다.

2. 에러 발생 원인

이 에러는 서버, 클라이언트, 네트워크 환경 등 다양한 요인으로 발생할 수 있습니다. 주요 원인은 다음과 같습니다:

(1) 서버의 응답 중단

  • 서버가 응답을 보내는 도중 오류가 발생하여 전송이 완료되지 않은 경우.
    • 서버 프로세스가 강제 종료되거나 비정상적으로 동작.
    • 네트워크 연결 문제로 응답이 중단.
    • 대용량 데이터를 스트리밍할 때 메모리 부족 등으로 응답이 중단.

(2) HTTP 헤더 문제

  • 서버에서 HTTP 응답 헤더를 잘못 설정한 경우.
    • Transfer-Encoding: chunked 헤더가 누락되거나 잘못 설정.
    • 헤더와 실제 데이터가 일치하지 않는 경우(예: 데이터 크기 오류).

(3) 프록시 서버 문제

  • 클라이언트와 서버 사이의 프록시가 응답을 제대로 처리하지 못했을 때 발생.
    • 프록시가 청크를 분리하거나 잘못된 데이터로 변환.
    • 연결이 중간에 끊어지거나 타임아웃 발생.

(4) 브라우저 문제

  • 클라이언트(웹 브라우저)가 청크 데이터를 제대로 처리하지 못하거나 오류를 발생시키는 경우.

(5) 네트워크 환경 문제

  • 클라이언트와 서버 간 네트워크 연결이 불안정하거나 패킷 손실이 발생하는 경우.

3. 해결 방법

(1) 서버 측 점검

  1. 서버 로그 확인:
    • 서버에서 요청을 처리하는 과정에 오류가 발생했는지 확인.
    • 예: 메모리 부족, 프로세스 중단, 네트워크 타임아웃.
  2. 응답 헤더 설정 확인:
    • Transfer-Encoding: chunked가 올바르게 설정되었는지 확인.
    • 정적 파일이나 대용량 데이터를 전송할 경우, 콘텐츠 크기(Content-Length)를 명시하거나 청크 전송 방식이 제대로 작동하는지 확인.
  3. 마지막 청크 전송 확인:
    • 전송 종료 시 0 크기의 청크가 제대로 추가되고 전송되었는지 확인.
  4. 타임아웃 설정 확인:
    • 서버가 긴 요청을 처리하는 동안 연결이 끊어지지 않도록 타임아웃 설정을 늘림.
    • 예: Nginx, Apache, Node.js와 같은 웹 서버에서 타임아웃 설정을 확인.

(2) 클라이언트 측 해결

  1. 네트워크 연결 점검:
    • 네트워크 연결 상태를 확인하고, 불안정한 네트워크 환경에서 테스트를 반복.
  2. 브라우저 캐시 초기화:
    • 캐시된 데이터가 문제가 될 수 있으므로 브라우저 캐시를 지우고 다시 시도.
  3. 다른 브라우저 사용:
    • 특정 브라우저에서 발생할 수 있는 문제이므로 다른 브라우저에서 테스트.
  4. 프록시 설정 점검:
    • 프록시 서버가 응답을 처리하지 못할 수 있으므로 프록시 설정을 변경하거나 사용하지 않도록 설정.

(3) 프록시 및 로드 밸런서 점검

  • 프록시 서버가 데이터를 손상시키는지 확인.
  • 로드 밸런서가 클라이언트와 서버 간의 연결을 유지하고 있는지 확인.

4. 예방 방법

  • 안정적인 네트워크 환경 유지.
  • 타임아웃 설정 조정.
  • 서버에서 데이터 전송 완료를 보장하도록 철저히 테스트.
  • 대용량 데이터를 전송할 때는 데이터 스트리밍 방식을 최적화.

'SpringBoot > Error' 카테고리의 다른 글

페이징 기능이슈  (0) 2025.01.07
IntelliJ Console 창 문자 깨짐 현상  (0) 2024.05.30

쿼리 수정전

<!--페이징을 위한 카운트 쿼리 수정전-->
    
    <select id="selectTotalProductCount" parameterType="map" resultType="int">
    SELECT COUNT(*) 
    FROM PRODUCT P
    LEFT JOIN
    CATEGORY C ON P.category_seq = C.category_seq
    WHERE 1=1
        <!-- 카테고리 필터 -->
        <if test="category_seq != 0">
            AND P.category_seq = #{category_seq}
        </if>
        
        <!-- 검색어 필터 -->
        <if test="query != null and query != ''">
    AND    (P.product_serial ILIKE '%' || #{query} || '%')
        OR (P.product_name ILIKE '%' || #{query} || '%')
        OR (P.product_brand ILIKE '%' || #{query} || '%')
        OR (P.product_height ILIKE '%' || #{query} || '%')
        OR (P.product_weight ILIKE '%' || #{query} || '%')
        OR (P.product_wh ILIKE '%' || #{query} || '%')
        OR (P.product_color ILIKE '%' || #{query} || '%')
        OR (P.product_features ILIKE '%' || #{query} || '%')
        OR (C.category_name ILIKE '%' || #{query} || '%')
        </if>
        
        <!-- 브랜드 필터 -->
        <if test="product_brand != null and product_brand != ''">
            AND P.product_brand = #{product_brand}
        </if>
        
        <!-- 가격대 필터 -->
        <if test="priceRange != null and priceRange != ''">
            AND (
                #{priceRange} LIKE '%below10k%' AND P.product_pay &lt; 10000
                OR #{priceRange} LIKE '%10kTo20k%' AND P.product_pay BETWEEN 10001 AND 20000
                OR #{priceRange} LIKE '%20kTo30k%' AND P.product_pay BETWEEN 20001 AND 30000
                OR #{priceRange} LIKE '%30kTo40k%' AND P.product_pay BETWEEN 30001 AND 40000
                OR #{priceRange} LIKE '%40kTo50k%' AND P.product_pay BETWEEN 40001 AND 50000
                OR #{priceRange} LIKE '%50kTo70k%' AND P.product_pay BETWEEN 50001 AND 70000
                OR #{priceRange} LIKE '%80kTo100k%' AND P.product_pay BETWEEN 80001 AND 100000
                OR #{priceRange} LIKE '%above100k%' AND P.product_pay > 100000
            )
        </if>
</select>

쿼리 수정후

 


<if test="query != null and query != ''">
    AND   (  (P.product_serial ILIKE '%' || #{query} || '%')
        OR (P.product_name ILIKE '%' || #{query} || '%')
        OR (P.product_brand ILIKE '%' || #{query} || '%')
        OR (P.product_height ILIKE '%' || #{query} || '%')
        OR (P.product_weight ILIKE '%' || #{query} || '%')
        OR (P.product_wh ILIKE '%' || #{query} || '%')
        OR (P.product_color ILIKE '%' || #{query} || '%')
        OR (P.product_features ILIKE '%' || #{query} || '%')
        OR (C.category_name ILIKE '%' || #{query} || '%')
)
</if>

    <!-- 가격대 필터 -->
    <if test="priceRange != null">
        AND (
            #{priceRange} LIKE '%below10k%' AND P.product_pay &lt; 10000
            OR #{priceRange} LIKE '%10kTo20k%' AND P.product_pay BETWEEN 10001 AND 20000
            OR #{priceRange} LIKE '%20kTo30k%' AND P.product_pay BETWEEN 20001 AND 30000
            OR #{priceRange} LIKE '%30kTo40k%' AND P.product_pay BETWEEN 30001 AND 40000
            OR #{priceRange} LIKE '%40kTo50k%' AND P.product_pay BETWEEN 40001 AND 50000
            OR #{priceRange} LIKE '%50kTo70k%' AND P.product_pay BETWEEN 50001 AND 70000
            OR #{priceRange} LIKE '%80kTo100k%' AND P.product_pay BETWEEN 80001 AND 100000
            OR #{priceRange} LIKE '%above100k%' AND P.product_pay > 100000
        )
    </if>
   
    <select id="selectTotalProductCount" parameterType="map" resultType="int">
 SELECT COUNT(*) 
    FROM PRODUCT P
    LEFT JOIN
    CATEGORY C ON P.category_seq = C.category_seq
    WHERE 1=1
        <!-- 카테고리 필터 -->
        <if test="category_seq != 0 and category_seq != null">
            AND P.category_seq = #{category_seq}
        </if>
        
        <!-- 검색어 필터 -->
        <if test="query != null and query != ''">
    AND  (  (P.product_serial ILIKE '%' || #{query} || '%')
        OR (P.product_name ILIKE '%' || #{query} || '%')
        OR (P.product_brand ILIKE '%' || #{query} || '%')
        OR (P.product_height ILIKE '%' || #{query} || '%')
        OR (P.product_weight ILIKE '%' || #{query} || '%')
        OR (P.product_wh ILIKE '%' || #{query} || '%')
        OR (P.product_color ILIKE '%' || #{query} || '%')
        OR (P.product_features ILIKE '%' || #{query} || '%')
        OR (C.category_name ILIKE '%' || #{query} || '%')
        )</if>
        
        <!-- 브랜드 필터 -->
        <if test="product_brand != null and product_brand != ''">
            AND P.product_brand = #{product_brand}
        </if>
        
       <!-- 가격대 필터 -->
    <if test="priceRange != null">
        AND (
            #{priceRange} LIKE '%below10k%' AND P.product_pay &lt; 10000
            OR #{priceRange} LIKE '%10kTo20k%' AND P.product_pay BETWEEN 10001 AND 20000
            OR #{priceRange} LIKE '%20kTo30k%' AND P.product_pay BETWEEN 20001 AND 30000
            OR #{priceRange} LIKE '%30kTo40k%' AND P.product_pay BETWEEN 30001 AND 40000
            OR #{priceRange} LIKE '%40kTo50k%' AND P.product_pay BETWEEN 40001 AND 50000
            OR #{priceRange} LIKE '%50kTo70k%' AND P.product_pay BETWEEN 50001 AND 70000
            OR #{priceRange} LIKE '%80kTo100k%' AND P.product_pay BETWEEN 80001 AND 100000
            OR #{priceRange} LIKE '%above100k%' AND P.product_pay > 100000
        )
    </if>
</select>
</mapper>

 

최초 기능 설계시 

검색기능과 필터/정렬 기능을 각각 분리하여 SQL 문을설계 진행 -> 기능을 구현후 테스트 진행시 결과조건은 동일하고 

category_seq 와 query(검색어) 유무에 따른 결과 분리 

 

 

  • currentPage:
    • 설명: param.page가 비어 있으면(예: 첫 진입 시), 기본값으로 1을 설정합니다.
    • 결과: 현재 페이지 번호.
      <c:set var="currentPage" value="${empty param.page ? 1 : param.page}" />
       
  • totalPages:
    • 설명: 전체 페이지 수를 totalPages 변수에 저장합니다. 서버에서 전달된 값을 사용.
       
      <c:set var="totalPages" value="${totalPages}" />

이전 버튼

<li class="page-item ${currentPage == 1 ? 'disabled' : ''}">
    <a class="page-link mx-1" href="javascript:void(0);" aria-label="Previous" data-page="${currentPage > 1 ? currentPage - 1 : 1}">
        <i class="feather-icon icon-chevron-left"></i>
    </a>
</li>
 
  • 클래스 계산:
    • currentPage == 1이면 "disabled" 클래스를 추가해 비활성화.
  • 데이터 속성 계산:
    • data-page: currentPage가 1보다 크면 currentPage - 1(이전 페이지)을 설정.
      그렇지 않으면 1을 유지.

페이지 번호 계산

<c:forEach var="i" begin="${currentPage - 2 > 1 ? currentPage - 2 : 1}" end="${currentPage + 2 < totalPages ? currentPage + 2 : totalPages}">
    <li class="page-item ${currentPage == i ? 'active' : ''}">
        <a class="page-link mx-1" href="javascript:void(0);" data-page="${i}">${i}</a>
    </li>
</c:forEach>
  • begin 시작 값:
    • 설명: 현재 페이지에서 왼쪽으로 최대 2개 표시.
      • 만약 (currentPage - 2)가 1보다 작으면 1로 시작.
        ${currentPage - 2 > 1 ? currentPage - 2 : 1}
  • end 종료 값:
    • 설명: 현재 페이지에서 오른쪽으로 최대 2개 표시.
      • 만약 (currentPage + 2)가 totalPages보다 크면 마지막 페이지(totalPages)를 표시
${currentPage + 2 < totalPages ? currentPage + 2 : totalPages}

 

  • 활성화 클래스:
    • 현재 페이지(currentPage)와 반복 변수(i)가 같으면 active 클래스를 추가해 시각적으로 강조
class="${currentPage == i ? 'active' : ''}"

 '...' 표시

<li class="page-item ${currentPage + 2 < totalPages ? '' : 'd-none'}">
    <a class="page-link mx-1" href="#">...</a>
</li>
 
  • currentPage + 2 < totalPages이면 ... 표시.
    (즉, 현재 페이지 기준으로 오른쪽 2칸 이후에 더 많은 페이지가 있을 때만 표시.)

마지막 페이지 버튼

<li class="page-item ${currentPage == totalPages ? 'disabled' : ''}">
    <a class="page-link mx-1" href="javascript:void(0);" aria-label="Next" data-page="${currentPage < totalPages ? currentPage + 1 : totalPages}">
        <i class="feather-icon icon-chevron-right"></i>
    </a>
</li>

 

  • 클래스 계산:
    • currentPage == totalPages이면 "disabled" 클래스를 추가해 비활성화.
  • 데이터 속성 계산:
    • data-page: currentPage가 totalPages보다 작으면 currentPage + 1(다음 페이지)을 설정.
      그렇지 않으면 마지막 페이지(totalPages)를 유지.

 

Spring Data JPA의 장점

  1. 자동화된 CRUD 메서드 제공: Spring Data JPA는 기본적인 CRUD(Create, Read, Update, Delete) 작업을 자동으로 제공하는 인터페이스(JpaRepository, CrudRepository)를 통해 간단하게 데이터베이스 작업을 수행할 수 있습니다.
  2. 메서드 이름을 통한 쿼리 생성: 복잡한 쿼리를 메서드 이름만으로 생성할 수 있습니다. 예를 들어, findByUsername과 같은 메서드 이름을 사용하여 특정 필드로 데이터를 조회할 수 있습니다.
  3. 트랜잭션 관리: Spring Data JPA는 트랜잭션 관리를 자동으로 처리하여 데이터베이스 작업의 원자성을 보장합니다.
  4. JPQL 및 네이티브 쿼리 지원: 필요에 따라 JPQL(Java Persistence Query Language) 또는 네이티브 SQL 쿼리를 사용할 수 있습니다.

Mapper를 사용하는 경우

Mapper는 객체 간의 변환을 담당하는 도구입니다. 일반적으로 데이터베이스 엔티티와 비즈니스 로직에서 사용하는 DTO(Data Transfer Object) 간의 변환을 처리할 때 사용됩니다. Spring Data JPA를 사용하면 직접적인 매핑 작업이 줄어들지만, 여전히 특정 상황에서는 Mapper가 유용할 수 있습니다.

Mapper를 사용하는 상황

  1. DTO 사용: 데이터베이스 엔티티와 클라이언트 사이에서 데이터를 주고받기 위한 DTO를 사용할 때, Mapper를 이용해 엔티티와 DTO 간의 변환을 처리할 수 있습니다. 이를 통해 데이터베이스 모델을 클라이언트에게 직접 노출하는 것을 방지할 수 있습니다.
  2. 복잡한 변환 로직: 엔티티와 DTO 간의 변환 로직이 복잡한 경우, Mapper를 사용하여 변환 로직을 분리하고 재사용할 수 있습니다.
  3. 다양한 데이터 소스: 여러 데이터 소스에서 데이터를 수집하여 하나의 객체로 변환해야 하는 경우, Mapper를 사용하여 각 데이터 소스로부터 수집한 데이터를 통합할 수 있습니다.

Mapper 예시

Entity

java
코드 복사
package com.example.board.model; import jakarta.persistence.*; import lombok.Data; @Entity @Data public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String role; }

DTO

java
코드 복사
package com.example.board.dto; import lombok.Data; @Data public class UserDto { private Long id; private String username; private String role; }

Mapper 인터페이스 (MapStruct 사용)

java
코드 복사
package com.example.board.mapper; import com.example.board.dto.UserDto; import com.example.board.model.User; import org.mapstruct.Mapper; @Mapper(componentModel = "spring") public interface UserMapper { UserDto toDto(User user); User toEntity(UserDto userDto); }

Service에서 Mapper 사용

java
코드 복사
package com.example.board.service; import com.example.board.dto.UserDto; import com.example.board.mapper.UserMapper; import com.example.board.model.User; import com.example.board.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private UserMapper userMapper; public UserDto save(UserDto userDto) { User user = userMapper.toEntity(userDto); user = userRepository.save(user); return userMapper.toDto(user); } public UserDto findByUsername(String username) { User user = userRepository.findByUsername(username); return userMapper.toDto(user); } }

결론

Spring Data JPA를 사용하면 많은 데이터베이스 관련 작업이 자동으로 처리되기 때문에 Mapper를 사용할 필요가 줄어듭니다. 하지만, DTO를 사용하거나 복잡한 객체 변환이 필요한 경우 Mapper를 사용하는 것이 유리할 수 있습니다. MapStruct와 같은 라이브러리를 사용하면 이러한 작업을 더욱 간편하게 처리할 수 있습니다. 각 프로젝트의 요구사항에 맞게 적절한 도구와 방법을 선택하는 것이 중요합니다.

'SpringBoot' 카테고리의 다른 글

SpringBoot-설정  (0) 2025.01.16

IntelliJ Console 창 에 로그를 출력하는 도중 특수문자가 깨지는 현상을 확인하였다.

 

 

1. Settings  Encoding UTF-8로 설정 변경

 

2. Help -> Edit VM Custom Option 에 아래 두줄 추가

 

3. 결과 

 한글 깨짐없이정상반영 확인

'SpringBoot > Error' 카테고리의 다른 글

청크 전송 인코딩(Chunked Transfer Encoding)란?  (0) 2025.01.08
페이징 기능이슈  (0) 2025.01.07

+ Recent posts