@SpringBootApplication
public class SpringBootDeveloperApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDeveloperApplication.class, args);
}
}
스프링 부트 사용에 필요한 기본 설정
@SpringBootConfiguration: 스프링 부트 관련 설정을 나타냄
@ComponentScan: 사용자가 등록한 빈을 읽고 등록, @Component라는 애너테이션을 가진 클래스들을 찾아 빈으로 등록
@EnableAutoConfiguration: 스프링 부트에서 자동 구성을 활성화, 서버가 실행될 때 스프링 부트의 메타 파일을 읽고 정의된 설정들을 자동으로 구성
테스트 컨트롤러 살펴보기
@RestController
public class TestController {
@GetMapping("/test")
public String test() {
return "Hello, world!";
}
}
@RestController: 라우터 역할, HTTP 요청과 메서드를 연결, 클라이언트의 요청에 맞는 메서드를 실행
TestController를 라우터로 지정해 /test라는 GET 요청이 왔을 때 test() 메서드를 실행하도록 구성
@Compponent 애너테이션이 있는 클래스는 빈으로 등록되며, @Controller, @RestController, @Configuration, @Repository, @Service 모두 @Component 애터네이션을 가지고 있음
[스프링 부트 3 구조 살펴보기]
계층
프리젠테이션 계층: HTTP 요청을 받고 이 요청을 비즈니스 계층으로 전송하는 역할, 컨트롤러
비즈니스 계층: 모든 비즈니스 로직을 처리, 서비스
퍼시스턴스 계층: 모든 데이터베이스 관련 로직을 처리,리포지토리
[테스트 코드]
given-when-then 패턴
given: 테스트 실행 준비단계
when: 테스트 진행 단계
then: 테스트 결과 검증 단계
JUnit
자바 언어를 위한 단위 테스트 프레임워크
테스트 방식을 구분할 수 있는 애너테이션을 제공
@Test 애너테이션으로 메서드를 호출할 때마다 새 인스턴스를 생성, 독립 테스트 가능
예상 결과를 검증하는 어설션 메서드 제공
사용 방법이 단순, 테스트 코드 작성 시간이 적음
자동 실행, 자체 결과를 확인하고 즉각적인 피드백을 제공
import org.aspectj.lang.annotation.After;
import org.junit.jupiter.api.*;
public class JUnitCycleTest {
@BeforeAll // 전체 테스트를 시작하기 전에 1회 실행하므로 메서드는 static으로 선언
static void beforeAll() {
System.out.println("@BeforeAll");
}
@BeforeEach // 테스트 케이스를 시작하기 전마다 실행
public void beforeEach() {
System.out.println("@BeforeEach");
}
@Test
public void test1() {
System.out.println("test1");
}
@Test
public void test2() {
System.out.println("test2");
}
@Test
public void test3() {
System.out.println("test3");
}
@AfterAll // 전체 테스트를 마치고 종료하기 전에 1회 실행하므로 메서드는 static으로 선언
static void afterAll() {
System.out.println("@AfterAll");
}
@AfterEach // 테스트 케이스를 종료하기 전마다 실행
public void afterEach() {
System.out.println("@AfterEach");
}
}
[데이터베이스]
데이터베이스 관리자
DBMS, database management system
관계형 DBMS = RDBMS(relational): 테이블 형태로 이루어진 데이터 저장소
데이터베이스 용어
테이블: 데이터를 구성하기 위한 가장 기본적인 단위
행 row: 가로로 배열된 데이터의 집합, 고유한 식별자인 기본키를 가짐, 레코드 record라고 부르기도 함
열 column: 행에 저장되는 유형에 데이터
기본키 primary key: 행을 구분할 수 있는 식별자, 테이블에서 유일해야 하며 중복값을 가질 수 없음, 수정되어서는 안 되며 유효한 값이어야 함(NULL이 될 수 없음)
쿼리 query: 데이터를 조회하거나 삭제, 생성, 수정 같은 처리를 하기 위해 사용하는 멸여문
[ORM, object-relational mapping]
ORM
자바의 객체와 데이터베이스를 연결하는 프로그래밍 기법
장점: SQL을 직접 작성하지 않아도 됨
단점: 프로젝트 복잡성이 커질수록 사용 난이도 올라감, 복잡하고 무거운 쿼리는 해결 불가능한 경우도 있음
JPA (java persistence API)
자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
인터페이스이므로 실제 사용을 위해서는 ORM 프레임워크를 추가로 선택해야함
하이버네이트: JPA 인터페이스를 구현한 구현체, 자바용 ORM 프레임워크, 내부적으로는 JDBC API 사용
[엔티티]
엔티티
데이터베이스의 테이블과 매핑되는 객체
본질적으로는 자바 객체이나 테이블과 직접 연결된단든 특징이 있어 구분지어 부름
데이터베이스에 영향을 미치는 쿼리를 실행하는 객체
엔티티 매니저
엔티티를 관리해 데이터베이스와 애플리케이션 사이에서 객체를 생성, 수정, 삭제하는 등의 역할
엔티티 팩토리: 엔티티 매니저를 만드는 곳, @PersistenceContext 또는 @Autowired 사용
영속성 컨텍스트
JPA의 중요한 특징 중 하나로, 엔티티를 관리하는 가상의 공간
1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩
엔티티의 상태
분리 detached: 영속성 컨텍스트가 관리하고 있지 않는 상태
관리 managed: 영속성 컨텍스트가 관리하는 상태
비영속 transient: 영속성 컨텍스트와 전혀 관계가 없는 상태
삭제 removed: 삭제된 상태
[스프링 데이터와 스프링 데이터 JPA]
스프링 데이터 JPA
리포지터리 역할을 하는 인터페이스를 만들어 데이터베이스의 테이블 조회, 수정, 생성, 삭제 같은 작업을 간단히 할 수 있음
public interface MemberRepository extends JpaRepository<Member, Long>{
} // 기존 CRUD 메서드를 사용하기 위한 JpaRepository 상속 예
리포지토리: 엔티티에 있는 데이터들을 조회하거나 저장, 변경, 삭제를 할 때 사용하는 인터페이스
JpaRepository 클래스를 상속받을 때, 엔티티 Member와 엔티티 기본키 타입 Long을 인수로 넣어
- '사용하는 객체' 클래스에서 '사용되는 객체' 클래스의 타입을 직접 지정해 버리면 '사용되는 객체' 클래스를 변경할 경우 이를 이용하고 있는 곳을 모두 수정해야 함
- 수정할 부분이 늘어나면 실수가 발행할 위험과 시간이 늘어남
2) 인터페이스 의존
- 참조를 받는 유형으로 사용할 수 있으므로 변수의 이름을 변경하지 않아도 됨
- 인터페이스가 선언된 메서드를 이용하면 클래스가 바뀌어도 메서드명을 변경하지 않아도 됨
- 클래스 의존보다 인터페이스 의존을사용하는 것으로 수정할 곳을 줄일 수 있음
2. 관점 지향 프로그래밍(AOP)
1) 중심적 관심사(Primary Concern)
- 실현해야 할 기능을 나타내는 프로그램
2) 횡단적 관심사(Crosscutting-Concerns)
- 본질적인 기능은 아니지만 품질이나 유지보수 등의 관점에서 반드시 필요한 기능을 나타내는 프로그램
- 예외처리, 로그 정보 화면이나 파일 등으로 출력 처리, 데이터베이스의 트랜잭션 제어 등
3. DI 컨테이너 다섯 가지 규칙
1) 인터페이스를 이용하여 의존성을 만든다
- 의존하는 부분에 인터페이슬르 이용한다는 것
2) 인스턴스를 명시적으로 생성하지 않는다
- 인스턴스 생성에 new 키워드를 사용하지 않는다는 것
3) 어노테이션을 클래스에 부여한다 & 4) 스프링 프레임워크에서 인스턴스를 생성한다
- 인스턴스를 생성하려는 클래스에 인스턴스 생성 어노테이션을 부여한다는 것
- @Controller: 인스턴스 생성 지시, 스프링 MVC를 이용할 때 컨트롤러에 부여
- @Service: 인스턴스 생성 지시, 트랜잭션 경계가 되는 도메인(서비스) 기능에 부여
- @Repository: 인스턴스 생성 지시, 데이터베이스 액세스(리포지토리) 기능에 부여
- @Component: 위 용도 이외의 클래스에 부여
5) 인스턴스를 이용하고 싶은 곳에 어노테이션을 부여한다
- 스프링 프레임워크에 의해 생성된 인스턴스를 이용하는 클래스에 참조를 받는 필드를 선언하고 필드에 @Autowired 어노테이션 부여
4. DI 프로그램 만들기
// 인사 인터페이스
public interface Greet {
// 인사하기
void greeting();
}
// Greet 구현 클래스, 아침 인사 하기
@Component
public class MorningGreet implements Greet{
@Override
public void greeting(){
System.out.println("-------------");
System.out.println("좋은 아침입니다");
System.out.println("-------------");
}
}
//스프링부트 시작 클래스
@SpringBootApplication
public class BookstudyApplication {
//메인 메서드: 자신의 execute 메서드를 호출하도록 작성
public static void main(String[] args) {
SpringApplication.run(BookstudyApplication.class, args)
.getBean(BookstudyApplication.class).execute();
}
//주입하는 곳(인터페이스)
@Autowired
Greet greet;
//실행 메서드
private void execute(){
greet.greeting();
}
}
[소스코드 설명]
- 스프링 프레임워크는 기동 시 컴포넌트 스캔에 의해 MorningGreet 클래스에 @Component 어노테이션이 부여되어 MorningGreet 인스턴스가 생성됨
- 직접 어드바이스를 만드는 경우 패키지, 클래스, 메서드 등 어드바이스 삽입 대상을 조건으로 지정할 수 있음
- 지정하는 조건 방법에는 포인트 컷 사용
- execute(반환값 패키지.클래스.메서드(인수))
package me.yuan.bookstudy.used.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
@Aspect //어드바이스를 기술하는 클래스
@Component //인스턴스 생성
public class SampleAspect {
@Before("execution(* me.yuan.bookstudy.used.*Greet.*(..))") //메서드 실행 전에 호출
public void beforeAdvice(JoinPoint joinPoint) {
//시작 부분 표시
System.out.println("=== Before Advice ===");
//날짜를 출력
System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(new java.util.Date()));
//메서드 이름 출력
System.out.println(String.format("메서드:%s", joinPoint.getSignature().getName()));
}
@After("execution(* me.yuan.bookstudy.used.*Greet.*(..))") //메서드 실행 후에 호출
public void afterAdvice(JoinPoint joinPoint) {
//시작 부분 표시
System.out.println("=== After Advice ===");
//날짜를 출력
System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(new java.util.Date()));
//메서드 이름 출력
System.out.println(String.format("메서드:%s", joinPoint.getSignature().getName()));
}
@Around("execution(* me.yuan.bookstudy.used.*Greet.*(..))") //메서드 실행 전후에 호출
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
//시작 부분 표시
System.out.println("=== Arou줌d Advice ===");
System.out.println("*** 처리전 ***");
//지정한 클래스의 메서드 실행
Object result = joinPoint.proceed();
System.out.println("*** 처리후 ***");
//반환값을 돌려줄 필요가 있는 경우에는 Object 타입의 반환값을 돌려줌
return result;
}
}
4) Around Advice와 다른 어드바이스와의 차이
- 인수는 ProceedingJoinPoint 인터페이스 타입을 지정
- 어드바이스 중에서 ProceedingJoinPoint 인터페이스의 proceed() 메서드를 호출
- 어드바이스 적용 대상의 메서드를 임의의 타이밍으로 호출할 수 있으므로 전후로 다양한 처리가 가능
7. @Transactional
- 트랜잭션 관리에 사용하는 어노테이션
- 데이터베이스 액세스 처리 메서드가 정상 종료하면 트랜잭션을 커밋하고 예외가 발생하면 롤백