[인프런 워밍업 스터디 클럽 1기 BE] 두 번째 발자국

[인프런 워밍업 스터디 클럽 1기 BE] 두 번째 발자국

2주차 5/7 - 5/10

image


Section 3 역할의 분리와 스프링 컨테이너

목표

  1. 좋은 코드가 왜 중요한지 이해하고, 원래 있던 Controller 코드를 보다 좋은 코드로 리팩토링한다.

  2. 스프링 컨테이너와 스프링 빈이 무엇인지 이해한다.

  3. 스프링 컨테이너가 왜 필요한지, 좋은 코드와 어떻게 연관이 있는지 이해한다.

  4. 스프링 빈을 다루는 여러 방법을 이해한다.

스프링 컨테이너

스프링 빈 (Spring Bean)

서버가 시작되면, 스프링 서버 내부에 거대한 컨테이너를 만들게 된다.

컨테이너 안에는 클래스가 들어가게 된다. 이때 다양한 정보도 함께 들어있고, 인스턴스화도 이루어진다.

image→ 스프링 컨테이너 안으로 들어간 클래스를 스프링 빈이라고 한다.

cf) JdbcTemplate도 Dependency (의존성)에 의해 스프링 빈으로 등록되어 있다.

서버가 시작되면

  1. 스프링 컨테이너(클래스 저장소)가 시작된다.

  2. 기본적으로 많은 스프링 빈들이 등록된다. 예를 들어, JdbcTemplate이 등록된다.

  3. 우리가 설정해준 스프링 빈이 등록된다. 예를 들어, UserController가 등록된다.

  4. 이때 필요한 의존성이 자동으로 설정된다. 예를 들어, UserController를 만들 때 JdbcTemplate을 알아서 넣어준다.

Repository와 Service 스프링 빈 등록하는 방법

  • Repository를 스프링 빈으로 등록할 때는 @Repository 어노테이션 사용

  • Service를 스프링 빈으로 등록할 때에는 @Service 어노테이션 사용

     

    UserControllerUserService가 스프링 빈이니 굳이 직접 new 연산자를 통해 인스턴스화해줄 필요가 없다! 또한 UserRepositoryJdbcTemplate을 직접 가지고 있기 때문에 JdbcTemplate도 가지고 있을 필요도 없어진다.

스프링 컨테이너를 왜 사용할까?

스프링 컨테이너를 이용하는 이유

컨테이너가 BookService를 대신 인스턴스화하고, 그 때 알아서 BookMemoryRepository 혹은 BookMySqlRepository 중 하나를 BookRepository로 결정해준다!

= 제어의 역전 (IoC, Inversion of Control)

이때 컨테이너가 BookService를 만들어 줄 때 BookMemoryRepository와 BookMySqlRepository 중 하나를 선택해서 넣어주는 과정

= 의존성 주입 (Dependency Injection)

image- @Primary 어노테이션을 이용해 우선권을 제어할 수 있다.

책 이름을 메모리에 저장하는 API 예시

controller > book > BookController.java

package com.group.libraryapp.controller.book;

import com.group.libraryapp.service.book.BookService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BookController {

		// new BookService() 안해도 됨!
    private final BookService bookService;

		// constructor
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @PostMapping("/book")
    public void saveBook() {
        bookService.save();
    }
}

service > book > BookService.java

package com.group.libraryapp.service.book;

import com.group.libraryapp.repository.book.BookRepository;
import org.springframework.stereotype.Service;

@Service  // 어노테이션 추가
public class BookService {
    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    public void save() {
        bookRepository.save();
    }
}

repository > book > BookMemoryRepository.java

package com.group.libraryapp.repository.book;

import org.springframework.stereotype.Repository;

@Repository  // 어노테이션 추가
public class BookMemoryRepository implements BookRepository{

    @Override
    public void save() {
        System.out.println("Memory Repository");  // 테스트용 출력
    }
}

repository > book > BookMySqlRepository.java

package com.group.libraryapp.repository.book;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;

@Primary  // 우선권을 부여하는 어노테이션
@Repository
public class BookMySqlRepository implements BookRepository {

    @Override
    public void save() {
        System.out.println("MySql Repository");  // 테스트용 출력
    }
}

스프링 컨테이너를 다루는 방법

빈을 등록하는 방법

  • @Configuration

    • 클래스에 붙이는 어노테이션

    • @Bean을 사용할 때 함께 사용해줘야 한다!

  • @Bean

    • 메소드에 붙이는 어노테이션

    • 메소드에서 반환되는 객체를 스프링 빈에 등록한다.

  • @Component

    • 주어진 클래스는 ‘컴포넌트’로 간주

    • 1) 컨트롤러, 서비스, 리포지토리가 모두 아니고 2) 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용

@Service + @Repository vs @Configuration + @Bean

  • 개발자가 직접 만든 클래스를 스프링 빈으로 등록할 때 => @Service + @Repository

  • 외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때 => @Configuration + @Bean

스프링 빈을 주입받는 방법

  1. (가장 권장) 생성자를 이용해 주입받는 방식

    @RestController
    public class UserController {
        private final UserService userService;
    
        public UserController(UserService userService) {
            this.userService = userService;
        }
  2. setter와 @Autowired 사용

    @Autowired
    public void setUserController(UserService userService) {
        this.userService = userService;
    }

    → 누군가 setter를 사용하면 오작동할 수 있다.

  3. 필드에 직접 @Autowired 사용

    @Autowired
    private final UserService userService;

    → 테스트를 어렵게 만드는 요인이다.

스프링 빈의 우선순위 설정

  • @Qualifier 사용

  • 스프링 빈을 사용하는 쪽에서만 쓰면, 빈의 이름을 적어주어야 한다.

  • 양쪽 모두 사용하면, @Qualifier끼리 연결된다!

  • @Primary vs @Qualifier → 사용하는 쪽에서 직접 적어준 @Qualifier가 이긴다!

Section 4 생애 최초 JPA 사용하기

목표

  1. 문자열 SQL을 직접 사용하는 것의 한계를 이해하고, 해결책인 JPA, Hibernate, Spring Data JPA가 무엇인지 이해한다.

  2. Spring Data JPA를 이용해 데이터를 생성, 조회, 수정, 삭제할 수 있다.

  3. 트랜잭션이 왜 필요한지 이해하고, 스프링에서 트랜잭션을 제어하는 방법을 익힌다.

  4. 영속성 컨텍스트와 트랜잭션의 관계를 이해하고, 영속성 컨텍스트의 특징을 알아본다.

Spring Data JPA를 사용한 데이터베이스 조작

JPA (Java Persistence API) 개념

  • Persistence (영속성 ) : 데이터를 생성한 프로그램이 종료되더라도 데이터가 영구적인 속성을 갖는 것

  • API : 정해진 규칙

  • JPA : 객체와 관계형 데이터베이스의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙 / ORM (Object-Relational Mapping) 기술 표준

  • JPA를 실제 코드로 작성한 가장 유명한 프레임워크 = Hibernate (내부적으로 JDBC 사용)

     

Entity

= 저장되고 관리되어야 하는 데이터

  • user 객체에 @Entity 어노테이션 붙여주기

  • @Entity를 붙이게 되면, 스프링이 이를 인식하여 서버가 동작할 때, User 객체와 user 테이블을 같은 것으로 간주

  • @Column 어노테이션 : column에 붙여주기, 다양한 옵션 (필드에 null이 들어갈 수 있는지의 여부, 길이 제한, DB에서의 column 이름 설정 등)

     

최초로 JPA를 적용할 때

application.yml 파일에 설정 추가!

Spring Data JPA를 이용해 자동으로 쿼리 날리기 (기능 리팩토링)

  • save : 주어지는 객체를 저장하거나 업데이트해준다.

  • findAll : 주어지는 객체가 매핑된 테이블의 모든 데이터를 가져온다.

  • findById : id를 기준으로 특정한 1개의 데이터를 가져온다.

  • User : 이름을 기준으로 유저 데이터를 조회해 유저 객체를 반환한다. (유저 정보가 없다면, null)

  • findByName : name을 기준으로 특정한 1개의 데이터를 가져온다. (함수 이름만 작성하면 알아서 SQL 조립)

Spring Data JPA의 추가적인 쿼리 작성법 조사 및 연습!

트랜잭션과 영속성 컨텍스트

트랜잭션

여러 SQL을 사용해야 할 때 한 번에 성공시키거나, 하나라도 실패하면 모두 실패시키는 기능
→ 쪼갤 수 없는 업무의 최소 단위

  • commit : 트랜잭션이 시작된 후 사용된 SQL을 성공적으로 반영

  • rollback : 트랜잭션을 시작하고 사용한 SQL을 모두 취소

UserService에 트랜잭션 적용

  • 메소드에 @Transactional 어노테이션 붙여주기

  • 주의) org.springframework.transaction.annotation.Transactional

  • 데이터의 변경이 없고, 조회 기능만 있을 때는 readOnly 옵션

영속성 컨텍스트

테이블과 매핑된 Entity 객체를 관리/보관하는 역할

스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.

영속성 컨텍스트의 역할

  1. 변경 감지 (Dirty Check) : 영속성 컨텍스트 안에 불러와진 Entity는 명시적으로 save를 해주지 않더라도 알아서 변경을 감지해 저장할 수 있게 해준다.

  2. 쓰기 지연 : 트랜잭션이 commit 되는 시점에 여러 SQL을 모아서 한 번만 날린다.

  3. 1차 캐싱 : ID를 기준으로 Entity를 기억한다/

Section 5 책 요구사항 구현하기

목표

  1. 책 생성, 대출, 반납 API를 온전히 개발하며 지금까지 다루었던 모든 개념을 실습해본다.

  2. 객체지향적으로 설계하기 위한 연관관계를 이해하고, 연관관계의 다양한 옵션에 대해 이해한다.

  3. JPA에서 연관관계를 매핑하는 방법을 이해하고, 연관관계를 사용해 개발할 때와 사용하지 않고 개발할 때의 차이점을 이해한다.

     

조금 더 복잡한 기능을 API로 구성하기

책 생성 API, 대출 기능, 반납 기능 구현

 

과제

과제 4

과제 5


2주차 회고

이번 주차에 스프링의 굵직굵직한 개념들이 등장했는데, 코드 예시와 함께 단계별로 적용해보는 강의 내용이 이해에 도움이 많이 되었다. 특히 과제를 해결하고자 고민하는 과정에서 재미있고 자신감도 붙는 것 같다. 강의의 흐름을 따라가면서 더 공부해야 할 것들이 차곡차곡 정립되고 있다. 그때그때 찾아보고 적어두고 지식 불리기 중! 클린 코드 책도 읽겠다는 목표도 세웠다.

벌써 절반을 훌쩍 넘어버렸으니 다음 주는 더더 화이팅!

댓글을 작성해보세요.

채널톡 아이콘