Spring_24_JPA Auditing, DTO↔Entity 변환

OngTK·2025년 11월 5일

Spring

목록 보기
24/25

🧩 JPA — Auditing, DTO↔Entity 변환, 어노테이션 심화

이전 JPA 기본 정리에서 추가로 들어간 부분만 모아 정리.
첨부 소스(AppStart.java, BaseTime.java, MovieEntity.java, MovieDto.java, MovieRepository.java, MovieService.java, MovieController.java)의 구조를 예시 코드에 반영.


1) 주요 어노테이션(심화 설명 포함)

@Entity                      // 클래스 ↔ 테이블 매핑
@Table(name = "movie")       // 실제 테이블명 지정
public class MovieEntity {   // PK, 컬럼 등은 아래 예시에서 상세
    ...
}
  • @Entity
    • 해당 클래스를 데이터베이스 테이블과 매핑
    • 기본 생성자(파라미터 없는 생성자) 필수
  • @Id
    • PK(기본키) 필드 지정
  • @Table(name="...")
    • 테이블명 지정(스키마, 인덱스 옵션 등 추가 가능)
  • @GeneratedValue(strategy = GenerationType.IDENTITY)
    • PK 자동 증가(AUTO_INCREMENT). MySQL에서 일반적으로 사용
    • 주의: 오타@GeneratedValued가 아니라 @GeneratedValue
    • 다른 DB는 SEQUENCE 등을 사용하기도 함
  • @Column
    • 컬럼 속성 상세 제어
    • 대표 속성
      • nullable = true/false : NOT NULL 제약
      • unique = true/false : 단일 컬럼 UNIQUE 제약(복합 유니크는 @Table(uniqueConstraints=...))
      • name = "컬럼명" : 실제 컬럼명
      • length = n : 문자열 길이
      • insertable = true/false : INSERT SQL 생성 시 포함 여부
      • updatable = true/false : UPDATE SQL 생성 시 포함 여부

✍️ 예시: MovieEntity (추가 포인트 포함)

@Entity
@Table(name = "movie")
public class MovieEntity extends BaseTime {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 120)
    private String title;

    @Column(name = "director", length = 60, nullable = false)
    private String director;

    @Column(unique = true, length = 20)
    private String code; // 비즈니스 키 (예: 영화코드). 단일 unique 예시

    @Column(updatable = false)
    private String createdBy; // insert 시에만 반영하고 이후 수정 금지

    // 기본 생성자, getter/setter ...
}

2) JPA Auditing (엔티티 생성/수정 자동 기록)

✅ 정의

  • 엔티티의 생성/수정 시각 등을 JPA가 자동으로 주입해주는 기능
  • INSERT/UPDATE 시점을 감지하여 필드를 채워줌

✅ 설정 순서

1) AppStart (메인 클래스) 위에 @EnableJpaAuditing 추가

@SpringBootApplication
@EnableJpaAuditing
public class AppStart {
    public static void main(String[] args) {
        SpringApplication.run(AppStart.class, args);
    }
}

2) BaseTime 추상 클래스(공통 감사 필드)

@Getter
@MappedSuperclass // 상속 엔티티에 필드를 매핑하도록 함
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTime {

    @CreatedDate               // INSERT 시 자동 주입
    private LocalDateTime createdAt;

    @LastModifiedDate          // UPDATE 시 자동 갱신
    private LocalDateTime updatedAt;
}

3) 감사 필드를 쓰고 싶은 엔티티가 BaseTime 상속

@Entity
@Table(name = "movie")
public class MovieEntity extends BaseTime {
    ...
}

🔎 추가 팁

  • @CreatedBy, @LastModifiedBy 등 작성자/수정자도 AuditorAware를 구현하면 자동 주입 가능
  • @Column(updatable=false) 와 조합해 “최초 생성자/생성일 불변” 정책 구현에 활용

3) JPA Entity ↔ MVC DTO/VO 변환 전략

✅ 원칙

  • Controller 에서는 Entity 직접 노출 지양
    • Controller ⇄ DTO
  • Service 에서 비즈니스 로직 + Entity 조작
    • Service ⇄ Entity
  • 레이어 간 명확한 역할 분리로 유지보수성과 보안성 향상

✅ 흐름도

View(JSON) ←→ DTO ←→ Controller ←→ DTO ←→ Service ←→ Entity ←→ Repository(JPA)

✅ 변환 메서드 패턴

(A) DTO → Entity

public class MovieDto {
    private Long id;
    private String title;
    private String director;
    private String code;

    public MovieEntity toEntity() {
        MovieEntity e = new MovieEntity();
        // id는 보통 신규생성 시 null
        e.setTitle(this.title);
        e.setDirector(this.director);
        e.setCode(this.code);
        return e;
    }
}

(B) Entity → DTO

@Entity
@Table(name = "movie")
public class MovieEntity extends BaseTime {
    // id, title, director, code, createdAt, updatedAt ...

    public MovieDto toDto() {
        MovieDto dto = new MovieDto();
        dto.setId(this.id);
        dto.setTitle(this.title);
        dto.setDirector(this.director);
        dto.setCode(this.code);
        // 필요 시 감사 필드도 전달
        // dto.setCreatedAt(this.getCreatedAt());
        // dto.setUpdatedAt(this.getUpdatedAt());
        return dto;
    }
}

✅ Service/Controller 예시 (핵심만)

Repository

@Repository
public interface MovieRepository extends JpaRepository<MovieEntity, Long> {
    Optional<MovieEntity> findByCode(String code);
}

Service

@Service
@RequiredArgsConstructor
public class MovieService {
    private final MovieRepository movieRepository;

    public MovieDto create(MovieDto dto) {
        MovieEntity saved = movieRepository.save(dto.toEntity());
        return saved.toDto();
    }

    public MovieDto readByCode(String code) {
        MovieEntity e = movieRepository.findByCode(code)
            .orElseThrow(() -> new IllegalArgumentException("영화코드 없음"));
        return e.toDto();
    }

    @Transactional
    public MovieDto update(Long id, MovieDto dto) {
        MovieEntity e = movieRepository.findById(id)
            .orElseThrow(() -> new IllegalArgumentException("영화 없음"));
        e.setTitle(dto.getTitle());
        e.setDirector(dto.getDirector());
        e.setCode(dto.getCode());
        return e.toDto(); // Dirty Checking으로 UPDATE 자동 반영
    }
}

Controller

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/movies")
public class MovieController {
    private final MovieService movieService;

    @PostMapping
    public ResponseEntity<MovieDto> create(@RequestBody MovieDto dto) {
        return ResponseEntity.ok(movieService.create(dto));
    }

    @GetMapping("/code/{code}")
    public ResponseEntity<MovieDto> readByCode(@PathVariable String code) {
        return ResponseEntity.ok(movieService.readByCode(code));
    }

    @PutMapping("/{id}")
    public ResponseEntity<MovieDto> update(@PathVariable Long id, @RequestBody MovieDto dto) {
        return ResponseEntity.ok(movieService.update(id, dto));
    }
}

중요 주의사항

  • Controller에서는 Entity를 직접 반환하지 않는다.
    (엔티티 구조 노출·순환 참조·지연 로딩 문제 등 부작용 방지)
  • DTO로 변환해 필요한 데이터만 주고받을 것

4) 추가 체크리스트(이번에 새로 들어간 부분 위주)

  • @GeneratedValue(strategy = GenerationType.IDENTITY)MySQL에서 주로 사용 (AUTO_INCREMENT)
    • Oracle/PostgreSQL 등은 SEQUENCE를 사용하는 경우가 많음
  • @Column(insertable=false, updatable=false)DB 또는 트리거가 채우는 컬럼에 유용
  • Auditing 을 위한 @EnableJpaAuditing메인 클래스에 반드시 선언
  • BaseTime@MappedSuperclass + @EntityListeners(AuditingEntityListener.class) 로 구성
  • DTO↔Entity 변환 메서드를 각 클래스에 배치해 레이어 간 의존 방향을 명확히 유지

✅ 최종 요약

  • 추가된 기능 핵심:
    1) 어노테이션 심화 속성 정리 (@GeneratedValue, @Column의 insertable/updatable 등)
    2) JPA Auditing (생성/수정 시각 자동 관리, @EnableJpaAuditing, BaseTime)
    3) Entity↔DTO 변환 규칙 + 레이어 분리 원칙(Controller-DTO / Service-Entity)
profile
2025.05.~K디지털_풀스택 수업 수강중

0개의 댓글