본문 바로가기
Project/Backend 프로젝트

백엔드 프로젝트 8주차 스프링 입문 (2)

by 주원주 2023. 11. 11.

회원 관리 예제 - 백엔드 개발

 

🎯비즈니스 요구사항 정리

  • 데이터: 회원 ID, 이름
  • 기능: 회원 등록, 조회
  • 아직 데이터 저장소가 선정되지 않았다고 가정

일반적인 웹 애플리케이션 계층 구조

  • 컨트롤러: 웹 MVC의 컨트롤러 역할
  • 서비스: 핵심 비지니스 로직 구현
  • 리포지토리: 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
  • 도메인: 비즈니스 도메인 객체(회원, 주문, 쿠폰 등)로, 주로 데이터베이스에 저장하고 관리됨

클래스 의존관계

  • 아직 데이터 저장소가 선정되지 않은 상태(가정), 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계
  • 데이터 저장소는 RDB, NoSQL 등 다양한 저장소를 고민중인 상황으로 가정
  • 개발을 진행하기 위해 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용

 

🎯코드 구현 중 필요한 기능 

  • @AfterEach: 한 번에 여러 테스트를 실행하면 메모리 DB에 직전 테스트의 결과가 남아, 다음 테스트를 실행할 때 오류 발생의 원인이 될 수 있음. 이런 상황에서 @AfterEach를 사용하면 각 테스트가 종료될 때마다 메모리 DB에 저장된 데이터를 삭제하여 오류 방지 가능
  • 테스트는 각각 독립적으로 실행되어야 하며, 테스트 순서에 의존관계가 있다면 이는 좋은 테스트가 아님
  • @BeforeEach: 각 테스트 실행 전에 호출됨. 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고, 의존관계를 새로 맺어줌

 

스프링 빈과 의존관계

 

🎯컴포넌트 스캔과 자동 의존관계 설정

회원 컨트롤러에 의존 관계 추가 - 회원 컨트롤러가 회원 서비스와 회원 리포지토리를 사용할 수 있도록 함

  • DI(Dependency Injection): 의존성 주입
  • @Autowired: 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어줌. 이렇게 객체 의존관계를 외부에서 넣어주는 것이 바로 DI.
  • 생성자에 @Autowired를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아 주입함. 생성자가 1개만 있다먼 @Autowired는 생략 가능
  • DI는 개발자가 직접 주입할 수 있고, @Autowired에 의해 주입하는 것도 가능

스프링 빈을 등록하는 방법

  • @Controller가 있으면 자동 등록(스프링이 제공하는 컨트롤러)
  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바 코드로 직접 스프링 빈 등록

컴포넌트 스캔 원리

  • @Component 애노테이션이 있으면 스프링 빈으로 자동 등록
  • @Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문
  • @Component를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록됨
    • @Controller
    • @Service
    • @Repository

+) 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본적으로 싱글톤으로 등록함(유일하게 하나만 등록해서 공유). 따라서 같은 스프링 빈이면 모두 같은 인스턴스. 설정으로 싱글톤이 아니도록 할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용함

 

자바 코드로 직접 스프링 빈 등록하기

 

예시 코드

package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
 @Bean
 public MemberService memberService() {
 return new MemberService(memberRepository());
 }
 @Bean
 public MemberRepository memberRepository() {
return new MemoryMemberRepository();
 }
}

 

  • @Service, @Repository, @Autowired 애노테이션 없이 직접 스프링 빈 등록
  • @Bean: 스프링 빈에 등록할 것이라는 신호

참고 사항

  • XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않음
  • DI에는 필드 주입, setter 주입, 생성자 주입으로 3가지 방법이 존재. 의존관계가 실행 도중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장함(위 예제도 생성자 주입)
  • 실무에서 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용함. 그리고 정형화되지 않거나, 상화에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록함
  • @Autowired를 통한 DI는 스프링이 관리하는 객체에서만 동작함. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않음

 

스프링 DB 접근 기술

 

🎯H2 데이터베이스 설치, 순수 Jdbc

 

구현 클래스 추가

 

 

스프링 설정

  • 개방-폐쇄 원칙(OCP, Open-Closed Principle)
    • 확장에는 열려있고, 수정, 변경에는 닫혀있음
  • 스프링의 DI(Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있음
  • 회원을 등록하고 DB에 결과가 잘 입력되는지 확인
  • 데이터를 DB에 저장하므로 스프링 서버를 다시 실행해도 데이터가 안전하게 저장됨

 

🎯스프링 통합 테스트

  • @SpringBootTest: 스프링 컨테이너와 테스트를 함께 실행
  • @Transactional: 테스트 케이스에 이 애노테이션이 있으면, 테스트 시작 전에 트랜직션을 시작하고, 테스트 완료 후에 항상 롤백함. 이렇게 하면 DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않음

🎯스프링 JdbcTemplate

  • 순수 Jdbc와 동일한 환경설정
  • 스프링 JdbcTemplate과 MyBatis같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해줌, 하지만 SQL은 직접 작성해야 함

🎯JPA

  • JPA는 기존의 반복 코드에 더하여, 기본적인 SQL도 JPA가 직접 만들어서 실행함
  • JPA를 사용하면 SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있음
  • JPA를 사용하면 개발 생산성을 크게 높일 수 있음

코드 구현 중 알아야 할 것

  • spring-boot-starter-data-jpa: 내부에 jdbc 관련 라이브러리 포함, jdbc는 제거해도 됨
  • show-sql: JPA가 생성하는 SQL을 출력
  • ddl-auto: JPA는 테이블을 자동으로 생성하는 기능을 제공하는데 none를 사용하면 해당 기능을 끔.
    • create: 엔티티 정보를 바탕으로 테이블 직접 생성
  • 스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을 커밋함. 만약 런타임 예외가 발생하면 롤백
  • JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 함.

🎯스프링 데이터 JPA

  • 리포지토리에 구현 클래스 없이 인터페이스 만으로 개발 완료 가능
  • 반복 개발해온 CRUD 기능 제공
  • 실무에서 관계형 데이터베이스를 사용할 때 유용

스프링 데이터 JPQ 제공 클래스

 

스프링 데이터 JPA 제공 기능

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

AOP

🎯AOP가 필요한 상황

  • 모든 메소드의 호출 시간을 측정
  • 공통 관심사항(cross-cutting concern) vs 핵심 관심사항(core concern)
  • 회원 가입 시간, 회원 조회 시간 측정

위 상황에서의 문제점

  • 회원가입, 회원 조회에 시간을 측정하는 기능은 핵심 관심사항이 아님
  • 시간을 측정하는 로직은 공통 관심사항
  • 시간을 측정하는 로직과 핵심 비지니스의 로직이 섞여 유지보수가 어려움
  • 시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어려움
  • 시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야 함

 

🎯AOP 적용

  • AOP: Aspect Oriented Programming(관점 지향 프로그래밍)
  • 공통 관심사항과 핵심 관심사항을 분리

AOP를 적용함으로써 문제 해결

  • 회원가입, 회원조회 등 핵심 관심사항과 시간을 측정하는 공통 관심사항을 분리
  • 시간 측정 로직을 별도의 공통 로직으로 설정
  • 핵심 관심사항을 깔끔하게 유지
  • 변경이 필요하면 시간 측정 로직만 변경할 수 있음
  • 원하는 적용 대상 선택 가능

 

🎯AOP 동작 방식 설명

AOP 적용 전 의존관계

 

AOP 적용 후 의존관계

 

AOP 적용 전 전체 그림

 

AOP 적용 후 전체 그림