[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