[241205 TIL]

    JPA 도움닫기


    REST API, Spring Security, JPA 연관관계와 영속성 컨텍스트 정리


    1. REST API 메서드와 역할

    REST API는 클라이언트-서버 간 통신을 위한 표준으로, 5가지 주요 메서드를 제공합니다.

    REST API 메서드

    1. GET
      • 서버의 리소스에 액세스.
      • 데이터를 필터링하기 위해 쿼리 파라미터 사용 가능.
      • 예: GET /users?id=1
    2. POST
      • 데이터를 서버에 전송하여 리소스를 등록하거나 저장.
      • Body에 JSON, XML 등의 데이터를 포함하여 전송.
      • 예: POST /users (Body: { "name": "John" })
    3. PUT
      • 서버의 기존 리소스를 전체 업데이트.
      • 제공된 데이터로 리소스의 모든 필드를 수정.
      • 예: PUT /users/1 (Body: { "name": "John", "age": 30 })
    4. PATCH
      • 서버의 기존 리소스를 부분 업데이트.
      • 예: PATCH /users/1 (Body: { "name": "John" } → "name" 필드만 수정)
    5. DELETE
      • 리소스를 제거.
      • 인증 및 권한이 필요.
      • 예: DELETE /users/1

    2. Spring Security - 인증과 인가

    Spring Security는 애플리케이션 보안을 위한 강력한 도구로, **인증(Authentication)**과 인가(Authorization) 기능을 제공합니다.

    Authentication (인증)

    • 사용자의 신원을 확인하는 과정.
    • 다양한 인증 방식을 지원:
      • Username/Password
      • OAuth2
      • Bearer Token
    • 인증 흐름: Authentication Filter에서 요청의 인증 정보를 추출 및 검증.

    Authorization (인가)

    • 사용자의 권한을 확인하여 특정 리소스에 대한 접근 제어.
    • 엔드포인트 별로 권한을 설정할 수 있음:
      • /admin/** → ADMIN 권한 필요.
      • /users/** → USER 권한 필요.
      • /auth/** → 인증 불필요.
    • 설정 예:
      http.authorizeRequests()
          .antMatchers("/auth/**").permitAll()
          .antMatchers("/admin/**").hasRole("ADMIN")
          .anyRequest().authenticated();
      

    3. JPA의 연관관계와 영속성 컨텍스트

    JPA는 데이터베이스의 테이블 간 관계를 객체지향적으로 매핑하여 관리합니다.

    JPA 연관관계

    1. 일대일(1:1)
      • 한 엔티티가 다른 엔티티와 1:1로 매핑.
      • 예: 상품 기본정보/상품 상세정보, 회원 정보/회원 프로필 정보.
    2. 일대다(1:N), 다대일(N:1)
      • 한 엔티티가 여러 개의 엔티티와 매핑.
      • 예: 게시글/좋아요, 주문/상품.
    3. 다대다(N:M)
      • 여러 엔티티가 여러 개의 다른 엔티티와 연결.
      • 예: 학생/과목, 게시글/태그.
      • 보통 중간 테이블을 사용하여 관리.

    Persistence Context (영속성 컨텍스트)

    JPA의 핵심 요소로, 엔티티 객체를 저장 및 관리하며 데이터베이스와의 작업을 효율화.

    상태

    1. 비영속 (Transient)
      • 엔티티가 영속성 컨텍스트에서 관리되지 않는 상태.
    2. 영속 (Managed)
      • 엔티티가 영속성 컨텍스트에서 관리되는 상태.
      • 변경 감지(Dirty Checking), 쓰기 지연 등의 기능 지원.
    3. 준영속 (Detached)
      • 관리되던 엔티티가 영속성 컨텍스트에서 분리된 상태.
    4. 삭제 (Removed)
      • 엔티티가 삭제 상태로 표시되며, 트랜잭션이 끝나면 데이터베이스에서도 삭제.

    장점

    • ACID 보장: 트랜잭션의 원자성, 일관성, 격리성, 지속성 보장.
    • 쓰기 지연 (Write-Behind)
    • 변경 감지 (Dirty Checking)

    영속 상태로 만드는 방법

    1. @Transactional 선언.
    2. Repository에서 엔티티를 저장하거나 조회.

    스프링 DB 접근 기술

     

    H2 데이터베이스 설치

    개발이나 테스트 용도로 가볍고 편리한 DB, 웹 화면 제공 [ https://www.h2database.com ]

    # 권한 주기
    chmod 755 h2.sh
    
    # 실행
    ./h2.sh

     

    데이터베이스 파일 생성방법

    1. JDBC URL: jdbc:h2:~test (최초 한번)
    2. Terminal 에서 home에 test.mv.db 파일 생성 확인
    3. 이후부터 JDBC URL: jdbc:h2:tcp://localhost/~/test

     

    더보기

    ⚠️ H2 데이터베이스 접속 후 주소창에 앞 부부만 localhost 로 변경


    순수 JDBC

    ⚠️ JDBC 로 직접 코딩하는 것은 20년 전 이야기이므로 그냥 이렇게 했었다~정도만 참고하고 넘어가기

     

     

    환경 설정

    • build.gradle 에 jdbc, h2 관련 라이브러리 추가
    • application.properties 에 스프링 부트 데이터베이스 연결 설정 추가

     

     

    Jdbc 리포지토리 구현 및 설정 변경

    • repository>JdbcMemberRepository.java 생성
    • SpringConfig.java 변경 - MemberRepository 를 JdbcMemberRepository 로 변경

    ✏️ DataSource: 데이터베이스 커넥션을 획득할 때 사용하는 객체

    - 스프링 부트는 데이터베이스 커넥션 정보를 바탕으로 DataSource를 생성하고 스프링 빈으로 만들어둬서 DI를 받을 수 있음

     

     

    구현 클래스 추가 이미지

    • 개방-폐쇄 원칙(OCP, Open-Closed Principle):  확장에는 열려있고, 수정, 변경에는 닫혀있음
    • 스프링의 DI 을 사용하면 기존 코드를 전혀 손대지 않고설정만으로 구현 클래스를 변경 가능 (SpringConfig 수정)

     


    스프링 JdbcTemplate

    환경 설정

    • 순수 Jdbc 와 동일
     

     

    스프링 JdbcTemplate 리포지토리 구현 및 설정 변경

    • repository>JdbcTemplateMemberRepository.java 생성
    • SpringConfig.java 변경 - MemberRepository 를 JdbcTemplateMemberRepository 로 변경

    ‼️ JDBC API 에서 본 반복 코드를 대부분 제거해주지만 SQL 은 직접 작성해야 함


    JPA

    ✔️ 장점

    • 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해줌
    • SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환
    • 개발 생산성 크게 증가

     

     

    환경 설정

    • build.gradle 에 JPA 관련 라이브러리 추가 (spring-boot-starter-data-jpa)
    • application.properties 에 JPA 설정 추가 
      ✏️ show-sql: JPA 가 생성하는 SQL 출력
      ✏️ ddl-auto: 테이블을 자동으로 생성하는 기능 - none: 이 기능이 꺼짐 / create: 엔티티 정보를 바탕으로 테이블 직접 생성

     

     

    JPA 엔티티 매핑

    • domain>Member.java 수정 - @Entitiy, @Id, @GeneratedValued(strategy = GenerationType.IDENTITY) 추가

     

     

    JPA 회원 리포지토리 구현 및 설정 변경

    • repository>JpaMemberRepository.java 생성
    • SpringConfig.java 수정 - MemberRepository 를 JpaMemberRepository 로 변경

    ✏️ EntityManager: 데이터 소스 등을 내부적으로 처리

    - 데이터베이스 커넥션 정보 등을 자동으로 짬뽕해서 스프링 부트가 EntityManager 를 만들어줌

    - JPA 를 사용하려면 EntityManager 를 주입받아야 함

     

     

    ✏️ persist : 영구적으로 저장

    ✏️ find: 조회

     

     

     

    트랜잭션 추가

    • service>MemberService.java 수정 - @Transactional 추가

    ✏️ @Transactional: 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작

    - 메서드가 정상 종료되면 트랜잭션 Commit / 런타임 예외 발생하면 Rollback

    - JPA 를 통한 모든 데이터 변경은 트랜잭션 안에서 실행


    스프링 데이터 JPA

    ✔️ 장점

    • 리포지토리에 구현 클래스 없이 인터페이스만으로 개발
    • 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공 

     

     

    스프링 데이터 JPA 제공 기능

    • 인터페이스를 통한 기본적인 CRUD
    • findByName()findByEmail() 처럼 메서드 이름 만으로 조회 기능 제공
    • 페이징 기능 자동 제공

     

     

    환경 설정

    • JPA 와 동일

     

     

    스프링 데이터 JPA 리포지토리 구현 및 설정 변경

    • repository>SpringDataJpaMemberRepository.java 인터페이스 생성
      package hello.hello_spring.repository;
      
      import hello.hello_spring.domain.Member;
      import org.springframework.data.jpa.repository.JpaRepository;
      
      import java.util.Optional;
      
      public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
      
          @Override
          Optional<Member> findByName(String name);
      }
    • SpringConfig.java 수정
      package hello.hello_spring;
      
      import hello.hello_spring.repository.*;
      import hello.hello_spring.service.MemberService;
      import jakarta.persistence.EntityManager;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      
      @Configuration
      public class SpringConfig {
      
          private final MemberRepository memberRepository;
      
          public SpringConfig(MemberRepository memberRepository) {
              this.memberRepository = memberRepository;
          }
      
          @Bean
          public MemberService memberService() {
              return new MemberService(memberRepository);
          }
      }

    스프링 통합 테스트

    • 단위 테스트: 순수하게 자바 코드로만 최소한의 단위로 테스트
    • 통합 테스트: 스프링 컨테이너와 DB까지 연결된 테스트

    ‼️ 순수한 단위테스트가 훨씬 좋은 테스트일 확률이 높음 - 스프링 컨테이너 없이 단위 단위 쪼개서 테스트할 수 있도록 훈련 필요

     

     

    통합 테스트 생성

    • MemberServiceIntegrationTest.java 생성

    ✏️ @SpringBootTest: 스프링 컨테이너와 테스트를 함께 실행

    ✏️ @Transactional: 테스트 케이스에 이 애노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고 테스트 완료 후에 항상 롤백 ➡️ DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않음

    + Recent posts