본문 바로가기

항해99/스터디

[스터디] 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 스프링 부트 / JPA

스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 이동욱 / 프리렉

 

그레이들 프로젝트 -> 스프링 부트 프로젝트로 변경하기

 

  • 플러그인 의존성 관리를 위한 설정

- ext (gradle에서 사용하는 전역 변수를 설정하겠다) : 여기서는 springBootVersion 전역변수 생성 후 그 값을 2.1.7.RELEASE 로 설정 (스프링 부트 그레이들 플러그인의 2.1.7.RELEASE를 의존성으로 받는다)

buildscript {
    ext {
        springBootVersion = '2.1.7.RELEASE' // 2.1.7, 2.1.8, 2.1.9 ok
    }
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
// 아래와 같이 되어있으면x

plugins {
    id 'org.springframework.boot' version '2.2.1.RELEASE'
    ...
}

 

  • 앞에 선언한 플러그인 의존성들을 적용할 것인지 결정하는 코드

- io.spring.dependency-management :  스프링 부트의 의존성을 관리해주는 플러그인 ( 꼭 추가!!)

- 이 4개의 코드는 스프링 부트 사용하기 위한 필수 코드들

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

 

  • 나머지 코드

- repository : 각종 라이브러리(의존성)를 어떤 원격 저장소에서 받을지 결정

- jcenter : mavernCentral의 문제점 ( 많은 과정과 설정 ) 을 개선해서 라이브러리 업로드를 간단하게 해줌 (여기서는 일단 두개 다 사용)

- dependencies : 프로젝트 개발에 필요한 의존성들을 선언하는 곳

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')

    testCompile('org.springframework.boot:spring-boot-starter-test')
}

 

JPA 소개

- JPA는 자바 객체와 관계형 데이터베이스 간의 매핑을 위한 자바 표준 ORM 기술

- 개발자는 이를 통해 객체 지향적인 방식으로 데이터를 다룰 수 있으며, 데이터베이스 스키마와 객체 간의 불일치 문제를 해결할 수 있음

(스키마 : 관계형 데이터 베이스에서 데이터 구조를 정의하고 구조화 하는 것)

 

Entity 클래스 작성

- JPA에서는 관리할 엔티티를 나타내는  클래스를 작성해야함

- 데이터베이스의 테이블과 매핑되어야 하며, @Entity 어노테이션을 클래스 위에 붙여 JPA가 해당 클래스를 엔티티로 인식하게함

import javax.persistence.Entity; // JPA 엔티티를 정의하기 위한 어노테이션
import javax.persistence.Id; // 엔티티의 기본 키를 정의하기 위한 어노테이션

@Entity // JPA가 해당 클래스를 엔티티로 인식하도록 설정
public class User {
    @Id // 엔티티의 기본 키를 나타내는 필드임을 표시
    private Long id; // 사용자의 고유 식별자
    
    private String username; // 사용자명
    private String email; // 이메일 주소
    // 여기 부분은 생략
}

 

Repository 인터페이스 작성

- 데이터베이스에 접근하기 위한 메서드들을 정의하는 인터페이스

- Spring Data JPA 에서는 'JpaRepository'를 상속받는 인터페이스를 작성해서 CRUD 기능 구현

// Spring Data JPA에서 제공하는 JpaRepository 인터페이스를 사용하기 위한 import
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    // 추가적인 메서드 작성
}

 

CRUD 기능 구현

- Repository 인터페이스를 구현 후 데이터베이스의 CRUD(Create, Read, Update, Delete) 기능을 구현

- Spring Data JPA가 제공하는 메서드들을 활용해 개발자는 SQL 쿼리를 직접 작성하지 않고 데이터 조작 가능

// 스프링의 의존성 주입을 위한 어노테이션
import org.springframework.beans.factory.annotation.Autowired; 
// 스프링의 서비스 클래스를 정의하기 위한 어노테이션
import org.springframework.stereotype.Service; 

// 스프링 빈으로 등록되는 서비스 클래스임을 표시
@Service 
public class UserService {
// UserRepository 인터페이스를 사용하기 위한 의존성 주입
    private final UserRepository userRepository; 

    @Autowired // 의존성 주입을 위한 어노테이션
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository; // 생성자를 통한 의존성 주입
    }

    public User createUser(User user) {
        return userRepository.save(user); // 사용자 생성 메서드
    }
}

 

JPA Auditing

- 엔티티의 생성일, 수정일 등을 자동으로 관리하는 기능

- @MapperSuperclass 어노테이션을 사용해 공통 필드를 가진 부모 클래스 정의

- @EntityListeners 어노테이션을 통해 해당 엔티티의 이벤트를 감지해 처리할 리스너 클래스 등록

import javax.persistence.EntityListeners; // JPA 엔티티의 이벤트를 수신하기 위한 어노테이션
import javax.persistence.MappedSuperclass; // 부모 클래스로 사용할 엔티티 클래스를 정의하기 위한 어노테이션
import java.time.LocalDateTime; // 날짜와 시간을 다루는 클래스

@MappedSuperclass // 해당 클래스를 부모 엔티티로 사용함을 표시
@EntityListeners(AuditingEntityListener.class) // 엔티티의 이벤트를 수신하는 리스너 클래스를 설정
public abstract class BaseEntity {
    @CreatedDate // 엔티티가 생성된 날짜를 저장하기 위한 필드
    private LocalDateTime createdAt; // 생성일
    
    @LastModifiedDate // 엔티티가 마지막으로 수정된 날짜를 저장하기 위한 필드
    private LocalDateTime updatedAt; // 수정일
}

 

페이징 처리

- Spring Data JPA에서는 페이징 처리를 위한 메서드를 제공

- Pageable : 객체를 메서드의 파라미터로 전달해서 원하는 페이지와 페이지 크기 기정

- Page : 객체를 반환 받아서 페이지 단위로 데이터를 조회하고 출력

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Page<User> getUsers(Pageable pageable) {
        return userRepository.findAll(pageable); // 페이징 처리를 위해 findAll 메서드 사용
    }
}

 

테스트 코드 작성

- JPA 기능 테스트를 위해선 단위 테스트 코드를 작성해야함

- 테스트 코드를 통해 각 JPA 기능이 예상대로 동작하는지 확인 가능

- 데이터베이스와 상호작용을 테스트 할 수 있음

 - @SpringBootTest 어노테이션을 주로 사용하여 스프링 부트 애플리케이션을 로드 -> @Transactional 어노테이션을 사용해 테스트 메서드를 트랜잭션으로 감싸 롤백 수행

import org.junit.jupiter.api.Test; // JUnit 테스트를 위한 어노테이션
import org.springframework.beans.factory.annotation.Autowired; // 스프링의 의존성 주입을 위한 어노테이션
import org.springframework.boot.test.context.SpringBootTest; // 스프링 부트 테스트를 위한 어노테이션
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; // JPA 테스트를 위한 어노테이션
import org.springframework.transaction.annotation.Transactional; // 트랜잭션 설정을 위한 어노테이션

@SpringBootTest // 스프링 부트 애플리케이션 컨텍스트를 로드하여 테스트 어노테이션
@Transactional // 테스트 메서드 실행 후 롤백 어노테이션
public class UserServiceTest {
    @Autowired // 의존성 주입을 위한 어노테이션
    private UserRepository userRepository;

    @Test // 테스트 메서드 표시하는 어노테이션
    public void testCreateUser() {
        User user = new User();
        user.setUsername("john_doe");
        user.setEmail("john@example.com");
        
        User savedUser = userRepository.save(user); // 사용자 저장
        
        assertNotNull(savedUser.getId()); // 사용자의 ID가 제대로 생성되었는지 검증
    }
}