[인프런 워밍업 클럽 1기/BE] 두번째 발자국
인프런 워밍업 클럽1기 두번째 회고록
Day06 : 스프링 컨테이너의 의미와 사용 방법
Day07 : Spring Data JPA를 사용한 데이터베이스 조작
Day08 : 트랜잭션과 영속성 컨텍스트
Day09 : 조금 더 복잡한 기능을 API로 구성하기
Day10 : 객체지향과 JPA 연관관계
[Day06] 스프링 컨테이너의 의미와 사용 방법
스프링 컨테이너와 빈?
스프링 컨테이너
서버가 시작될 때 스프링 서버 내부에 만들어지는 컨테이너
컨테이너 내부에 클래스가 들어가게 된다
스프링 빈
스프링 컨테이너 내부로 들어간 클래스
빈을 식별할 수 있는 이름, 타입 등 다양한 정보가 저장되고 인스턴스화된다
스프링 컨테이너 사용 이유
Service의 코드를 변경하지 않고 Repository만 갈아 끼울 수 있다
제어의 역전(IoC; Inversion of Control)
컨테이너가 대신 인스턴스화 하고, 그 때 알아서 결정해준다
컨테이너가 Repository 구현체 중 하나를 선택해 Service를 만들어준다
의존성 주입(DI; Dependency Injection)
컨테이너가 Service를 인스턴스화 할 때 Repository 중 하나를 선택해 넣어주는 과정
스프링 빈 다루기
@Configuration
: 클래스에 붙이는 어노테이션,@Bean
과 함께 사용@Bean
:메소드에 붙이는 어노테이션, 반환 객체를 스프링 빈에 등록@Component
RestController, Service, Repository, Configuration은 모두 Component를 가짐
해당 어노테이션이 붙은 클래스를 스프링 서버가 뜰 때 자동 감지
@Primary
: 의존성 주입에서 우선권을 결정하는 어노테이션@Qualifier
의존성을 주입 받는 쪽에서 특정 스프링 빈을 선택할 수 있도록 한다
스프링 빈을 사용하는 쪽/등록하는 쪽 모두 사용 가능 → Qualifier 어노테이션에 적어준 값이 같은 것끼리 연결된다
@Qualifier
가@Primary
보다 우선순위가 높다
[Day07] Spring Data JPA를 사용한 데이터베이스 조작
SQL 직접 작성의 한계점
작성 실수를 인지하는 시점이 느리다(런타임에 발견된다)
특정 데이터베이스에 종속적이다
반복 작업
테이블과 객체의 패러다임 차이
이런 문제점을 해소하기 위해 등장한 것이 JPA (Java Persistence API) 이다
JPA
자바 진영의 ORM
객체와 관계형DB를 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영 규칙
Hibernate
JPA의 구현체 프레임워크 → JPA는 인터페이스이므로 실질적인 구현 코드가 필요하다
내부적으로 JDBC를 사용
Spring Data JPA
복잡한 JPA 코드를 스프링에서 쉽게 사용할 수 있도록 도와주는 라이브러리
SQL을 작성하지 않아도 쿼리가 나갈 수 있도록 자동으로 처리해준다
[Day08] 트랜잭션과 영속성 컨텍스트
트랜잭션
쪼갤 수 없는 업무의 최소 단위
모든 SQL을 성공시키거나(commit) / 하나라도 실패하면 모두 실패시킨다(rollback)
@Transactional
어노테이션을 대상 메서드에 붙여 적용한다
영속성 컨텍스트
테이블과 매핑된 Entity 객체를 관리/보관하는 역할
트랜잭션 사용 → 영속성 컨텍스트 생성
트랜잭션 종료 → 영속성 컨텍스트 종료
영속성 컨텍스트의 능력
변경 감지(Dirty Check)
영속성 컨텍스트 내의 Entity는 명시적
save
가 없어도 변경을 감지해 자동으로 저장된다
쓰기 지연(Lazy Loading)
DB의 INSERT/UPDATE/DELETE SQL을 그때그때 날리지 않고 트랜잭션이 commit 될 때 모아서 한 번만 날린다
통신 횟수가 줄어드는 이점
1차 캐싱
조회 요청이 올 때 우선 1차 캐시에서 데이터를 찾는다
만약 1차 캐시에 데이터가 있으면 → DB를 찾아보지 않고 바로 반환
만약 1차 캐시에 데이터가 없다면 → DB에서 데이터를 찾고 1차 캐시에 저장 후 반환
[Day09] 조금 더 복잡한 기능을 API로 구성하기
도서를 생성, 대출, 반납하는 API를 만들어보며 이전에 배운 것들을 익힐 수 있었다.
book, user_loan_history 테이블 설계 및 추가
테이블에 맞춰 객체를 만들어준다
DTO, Controller, Service 구현
HTTP Body 스펙이 동일할 때, DTO를 새로 만들어야 할까?
→ 새로 만드는 것이 좋다
→ 한쪽의 기능에 변화가 생겼을 때 side-effect 없이 대처할 수 있기 때문!
[Day10] 객체지향과 JPA 연관관계
객체 지향적으로
Service에서 UserLoanHistory
에 접근하던 기존의 구조
→ User
에서 UserLoanHistory
를 가져와 처리하도록 바꾸고 싶다
→ User
와 UserLoanHistory
는 서로를 알고 있어야 한다
연관 관계
@ManyToOne
N : 1 의 관계를 표현하기 위해 사용하는 어노테이션
1명의 사람이 여러 개의 대출 기록을 가질 수 있다 →
UserLoanHistory
와User
의 관계는 N : 1단방향으로만 활용 가능하다
UserLoanHistory
에서User
로 접근할 수 있다User
에서UserLoanHistory
로 접근할 수 없다
@OneToMany
1 : N 의 관계를 표현하기 위해 사용하는 어노테이션
mappedBy
옵션: 연관관계의 주인이 아님 → JPA에게 알려주기 위해 필요한 옵션
@OneToOne
1 : 1 의 관계를 표현하기 위해 사용하는 어노테이션
@ManyToMany
N : M 의 관계를 표현하기 위해 사용하는 어노테이션
직관적이지 않으므로 사용하지 않는 것을 추천
연관관계의 주인
객체가 연결되는 기준 → 연관관계의 주인을 기준으로 테이블이 연결된다
Table을 보았을 때 관계의 주도권(FK)을 가지고 있는 쪽을 의미한다
User
와UserLoanHistory
의 테이블을 보면UserLoanHistory
가 FK를 가지고 있다연관관계의 주인은
UserLoanHistory
연관 관계의 주인이 아닌
User
에mappedBy
옵션을 달아주어야 한다
주인을 통해 객체를 이어줬을 때 즉시 반대쪽이 이어지는 것은 아니다(주의)
하나의 setter 안에서 객체를 완전히 연결 시켜 해결할 수 있다
옵션
cascade
cascade = “폭포처럼 흐르다”
객체가 저장되거나 삭제될 때, 연관 관계에 놓인 테이블까지 함께 저장되거나 삭제됨
orphanRemoval
orphan = “고아” + removal = “제거”
연관관계가 끊어진 데이터를 자동으로 제거
지연 로딩
영속성 컨텍스트의 4번째 능력이다
예시:
User
와UserLoanHistory
는 연관관계이다User
를 가져올 때,UserLoanHistory
를 필요할 때 가져온다
fetch
옵션LAZY
: 꼭 필요할 때 가져온다EAGER
: 처음 데이터를 로딩할 때 바로 가져온다
제출 과제
[과제4] 과일 가게 API
API의 요청에 따라 MySQL DB에 데이터를 저장하고 데이터를 가져오는 것을 연습할 수 있었다. Controller에서 SQL을 작성해 jdbcTemplate로 전달하도록 구현된 상태이다.
[과제5] 클린 코드 적용하기
과제 5를 바탕으로 깜짝 라이브가 있었다. 라이브의 핵심 내용을 요약하자면 다음과 같다.
리팩토링 전 가장 먼저 테스트 코드를 준비해야 한다
테스트 코드를 작성하기 어려운 구조라면, 테스트 코드 작성을 위한 준비부터 해야 한다
(Point!) 테스트를 작성하려고 노력하면 자연스럽게 좋은 구조가 나올 확률이 올라간다
과제 5를 진행하며 테스트 코드에 대한 생각은 하지 않았기 때문에… 과제 5의 코드를 바탕으로 실제 테스트코드를 작성하고 리팩토링 하는 과정을 살펴보며 얻어가는 부분이 많았다. 특히 랜덤 부분을 테스트하기 위해 NumberGenerator라는 인터페이스를 생성하고 인터페이스를 inplements하는 RandomNumberGenerator를 만들었던 부분이 인상적이었다.
댓글을 작성해보세요.