1. 데이터베이스(Database)란?
- 데이터베이스 : 데이터를 매우 효율적으로 보관하고 꺼내볼 수 있는 곳
- 이점 : 굉장히 많은 사람이 안전하게 데이터를 사용, 관리 가능
1.1. DBMS(Database Management System)
- 데이터베이스를 관리하기 위한 소프트웨어
- 데이터베이스는 많은 사람이 공유할 수 있어야하므로 동시 접근 가능해야함
- 이외의 요구사항 만족하면서도 효율적으로 데이터베이스를 관리하고 운영
- ex. MySQL, 오라클(Oracle)
- 분류 : 관계형(가장 많이 사용), 객체-관계형, 도큐먼트형, 비관계형 등
관계형 DBMS (Relational DBMS, RDBMS)
관계형? : 이 DBMS가 관계형 모델을 기반으로 하기 때문
- 테이블 형태로 이루어진 저장소
ex. 회원 테이블의 고유 키(id), 회원 관련된 값( 이메일, 나이 ... )으로 구성
회원테이블 | ||
ID | 이메일 | 나이 |
1 | a@test.com | 20 |
2 | b@test.com | 30 |
3 | c@test.com | 40 |
{1 , a@test.com, 10 } 을 묶어서 -> 행(row),
ID, 이메일, 나이 와 같은 구분 -> 열(Column)
H2, MySQL
1) H2 : Java로 작성되어있는 RDBMS
- 스프링 부트가 지원하는 인메모리 관계형 데이터베이스
- 데이터를 다른 공간에 따로 보관X , 애플리케이션 자체 내부에 데이터를 저장하는 특징
- 애플리케이션을 다시 실행하면 데이터는 초기화됨
- but 간편하게 사용하기 좋아서 개발 시 테스트 용도로 많이 사용, 실제 서비스에는 사용X
2) MySQL
- 실제 서비스에 많이 사용하는 RDBMS
<꼭 알아야할 데이터베이스 용어>
1) 테이블(table)
: 데이터베이스에서 데이터를 구성하기 위한 가장 기본적인 단위
- 테이블은 행과 열로 구성
- 행은 여러 속성으로 구성
2) 행 (row) == 레코드 (record)
- 테이블 구성 요소 중 하나
- 테이블의 가로로 배열된 데이터의 집합
- 행은 반드시 고유 식별자인 기본키(Primary Key, PK) 가짐
ex. 회원 테이블 -> ID 가 1번인 회원의 이메일, 나이 같은 정보가 모여 있는 집합이 1번 회원에 대한 행
3) 열 (column)
- 테이블 구성 요소 중 하나
- 행에 저장되는 유형의 데이터
- 열은 각 요소에 대한 속성을 나타내며 무결성 보장
ex. 회원 테이블 -> 이메일은 문자열, 나이는 숫자유형
->이메일 열에 숫자 들어가거나, 나이 열에 문자열 들어갈 수 없음
-> 데이터에 대한 무결성 보장O
4) 기본키(Primary Key, PK)
- 행을 구분할 수 있는 식별자
- 이 값은 테이블에서 유일해야하며 중복 값을 가질 수 없음
- 보통 데이터를 수정하거나 삭제, 조회할때 사용
- 다른 테이블과 관계를 맺어 데이터를 가져올 수도 있음
- 기본키의 값은 수정되어서는 안되며 유효한 값이어야함 (= NULL이 될 수 없음)
5) 쿼리 (query)
- 데이터베이스에서 데이터를 조회하거나 삭제, 생성, 수정 같은 처리를 하기 위해 사용하는 명령문
- SQL(Structured Query Language) 이라는 데이터베이스 전용 언어를 사용하여 작성
2. ORM(Object-Relational Mapping)이란?
- ORM(Object-Relational Mapping) : 자바의 객체와 데이터베이스를 연결하는 프로그래밍 기법
== 객체와 데이터베이스를 연결해 자바 언어로만 데이터베이스를 다룰 수 있게 하는 도구
ex. 데이터베이스에 {age:20, name : 홍길동} 값 들어있음
->자바에서 사용하려면 어떻게?
->SQL 언어로 데이터 꺼내 사용하려면?->SQL 새로 공부해야하니 비효율적
=> ORM이 있다면 데이터베이스의 값을 마치 객체처럼 사용가능
== SQL 전혀 몰라도 자바 언어로만 데이터베이스에 접근해서 원하는 데이터 받아올 수 있음
<장점>
장점1) SQL을 직접 작성하지 않고, 사용하는 언어로 데이터버이스에 접근 가능
장점2) 객체 지향적으로 코드를 작성 가능->비즈니스 로직에만 집중 가능
장점3) 데이터베이스 시스템이 추상화되어 있기 때문에 MySQL에서 PostgreSQL로 전환한다고 해도 추가로 드는 작업이 거의 없음 == 데이터베이스 시스템에 대한 종속성이 줄어듦
장점4) 매핑하는 정ㅈ보가 명확하기 때문에 ERD에 대한 의존도를 낮출 수 있고 유지보수할 때 유리함
<단점>
단점1) 프로젝트의 복잡성이 커질수록 사용 난이도도 올라감
단점2) 복잡하고 무거운 쿼리는 ORM으로 해결이 불가능한 경우가 있음
3. JPA(Java Persistence API)와 하이버네이트(Hibernate)
- ORM에도 여러 종류 존재
- Java에서의 ORM 표준 : JPA(Java Persistence API)
- JPA(Java Persistence API) : 자바 객체와 데이터베이스를 연결해 데이터 를 관리. 객체 지향 도메인 모델과 데이터베이스의 다리 역할을 함
- 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
- 인터페이스 이므로 실제 사용을 위해서는 ORM 프레임워크( ex. 하이버네이트 )를 추가로 선택해야함
- 하이버네이트(Hibernate) : JPA 인터페이스를 구현한 구현체이자 자바용 ORM 프레임워크
- 내부적으로는 JDBC API를 많이 사용
- 하이버네이트 목표 : 자바 객체를 통해 데이터베이스 종류에 상관없이 데이터베이스를 자유자재로 사용가능하게 함
3.1. 엔티티 매니저(Entity Manager)
1) 엔티티(Entity)
- 데이터베이스의 테이블과 매핑되는 객체
- 본질적으로는 자바 객체이므로, 일반 객체와 다르지 않음
- but 데이터베이스의 테이블과 직접 연결된다는 아주 특별한 특징이 있어 구분지어 부름
=> 엔티티는 객체이긴 하지만 데이터베이스에 영향을 미치는 쿼리를 실행하는 객체
2) 엔티티 매니저(Entity Manager)
- 엔티티를 관리해 데이터베이스와 애플리케이션 사이에서 객체를 생성, 수정, 삭제하는 등의 역할을 함
- 엔티티 매니저 팩토리 (Entity Manager Factory) : 엔티티 매니저를 만드는 곳
ex. 회원 2명이 동시에 회원가입 처리하는 경우 엔티티 매니저의 업무 처리 방식
- 회원1의 요청에 대해서 가입 처리를 할 엔티티 매니저를 텐티티매니저 팩토리가 생성하면
-> 이를 통해 가입 처리해 데이터베이스에 회원정보를 저장
- 회원 2도 마찬가지
- 회원 1,2를 위해 생성된 엔티티 매니저는 필요한 시점에 데이터베이스와 연결한 뒤 쿼리함
- 스프링부트에서는 직접 엔티티 매니저 팩토리 만들어서 관리x
=>스프링부트는 내부에서 엔티티 매니저 팩토리를 하나만 생성해서 관리
- @Persistence Context 또는 @Autowired 애너테이션을 사용해서 엔티티 매니저를 사용
@PersistenceContext
EntityManager em; // 프록시 엔티티 매니저. 필요할 때 진짜 엔티티 매니저 호출
- 스프링 부트는 기본적으로 빈을 하나만 생성해서 공유
-> 동시성 문제 발생 가능
=> 실제로는 엔티티 매니저가 아닌 실제 엔티티 매니저와 연결하느 프록시(가짜) 엔티티 매니저를 사용
- 필요할때 데이터베이스 트랜잭션과 관련된 실제 엔티티 매니저를 호출
=> 엔티티 매니저는 Spring Data JPA 에서 관리 -> 사용자가 직접 생성하거나 관리할 필요가 없음!
3.2. 영속성 컨텍스트
- 엔티티매니저는 엔티티를 영속성 컨텍스트에 저장
- 영속성 컨텍스트 : 엔티티를 관리하는 가상의 공간 (JPA의 중요한 특징 중 하나!)
->영속성 컨텍스트가 존재하기 때문에 데이터 베이스에서 효과적으로 데이터를 가져올 수 있고, 엔티티를 편하게 사용 가능
- 기존 : 데이터 조작을 위해 쿼리를 직접 작성해야했음
- 스프링 부트 : 이런 쿼리를 자바코드로 작성, 이를 JPA로 알아서 쿼리로 변경해주는 것이 매우 편리
<특징>
1) 1차 캐시
- 영속성 컨텍스트는 내부에 1차 캐시를 가지고 있음
- 캐시의 키는 엔티티의 @Id 애너테이션이 달린 기본키 역할을 하는 식별자
- 캐시의 값은 엔티티
- 엔티티를 조회하면 1차캐시에서 데이터를 조회하고 값이 있으면 반환
- 값이 없으면 데이터베이스에서 조회해 1차 캐시에 저장한 다음 반환
- 이를 통해 캐시된 데이터를 조회할 때에는 데이터베이스를 거치지 않아도 됨
-> 매우 빠르게 데이터를 조회할 수 있음
2) 쓰기 지연 (transactional write-behind)
- 트랜잭션을 커밋하기 전까지는 데이터베이스에 실제로 질의문을 보내지 않고 쿼리를 모았다가 트랜잭션을 커밋하면 모았던 쿼리를 한 번에 실행하는 것
ex. 데이터 추가 쿼리가 3개라면?
-> 영속성 컨텍스트는 트랜잭션을 커밋하는 시점에 3개의 쿼리를 한꺼번에 전송
-> 적당한 묶음으로 쿼리를 요청할 수 있어 데이터베이스의 시스템의 부담을 줄일 수 있음
3) 변경감지
- 트랜잭션을 커밋하면 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교
-> 변경된 값이 있다면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영
-> 적당한 묶음으로 쿼리를 요청 가능, 데이터베이스의 시스템의 부담을 줄일 수 있음
4) 지연로딩(lazy loading)
- 쿼리로 요청한 데이터를 애플리케이션에 바로 로딩 X , 필요할 때 쿼리를 날려 데이터를 조회하는것
cf. 즉시 로딩 : 조회할 때 쿼리를 보내 연관된 모든 데이터를 가져옴
* 특징들의 공통점
- 데이터베이스의 접근을 최소화
-> 성능을 높일 수 있음
(캐시를 하거나, 자주 쓰지 않게 하거나, 변화를 자동 감지해서 미리 준비하거나... 등의 방법 통해)
3.3. 엔티티의 상태
- 이 상태는 특정 메서드를 호출해 변경 가능
- 필요에 따라 엔티티의 상태를 조절해 데이터를 올바르게 유지하고 관리가능
1) 분리(detached) 상태
- 영속성 컨텍스트가 관리하고 있지 않는 상태
2) 관리(managed) 상태
- 영속성 컨텍스트가 관리하는 상태
3) 비영속(transient) 상태
- 영속성 컨텍스트와 전혀 관계가 없는 상태
4) 삭제된 (removed) 상태
package org.example.springbootdeveloper;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
public class EntityManagerTest {
@Autowired
EntityManager em;
public void exmaple(){
// 1) 엔티티 매니저가 엔티티를 관리하지 않는 상태(비영속 상태)
// 엔티티를 처음 만들면 엔티티는 비영속 상태가 됨
Member member = new Member(1L, "홍길동");
//2) 엔티티가 관리되는 상태
// persist() 메서드 사용해 엔티티를 관리 상태로 만들 수 있음
// Member 객체는 영속성 컨텍스트에서 상태가 관리됨
em.persist(member);
//3) 엔티티 객체가 분리된 상태
// 만약 엔티티를 영속성 컨텍스트에서 관리하고 싶지 않다면 detach() 메서드 사용해 분리상태로 만들 수 있음
em.detach(member);
// 4) 엔티티 객체가 삭제된 상태
// 더 이상 객체가 필요 없다면 remove() 메서드 사용해서 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제 가능
em.remove(member);
}
}
4. 스프링 데이터(Spring Data) 와 스프링 데이터 JAP(Spring Data JPA)
- 스프링 데이터(Spring Data) : 비즈니스 로직에 더 집중할 수 있게 데이터베이스 사용 기능을 클래스 레벨에서 추상화 함
- 스프링 데이터에서 제공하는 인터페이스를 통해서 스프링 데이터를 사용 가능
- 이 인터페이스에서는 CRUD를 포함한 여러 메서드가 포함되어 있음, 알아서 쿼리를 만들어줌
- 페이징 처리 기능, 메서드 이름으로 자동으로 쿼리를 빌딩하는 기능 제공 등 많은 장점 존재
- 데이터베이스의 특성에 맞춰 기능을 확장해 제공하는 기술도 제공
ex. 표준 스펙인 JPA는 스프링에서 구현한 스프링 데이터 JPA (Spring Data JPA)사용
ex. 몽고디비는 스프링 데이터 몽고디비 (Spring Data MongoDB) 사용
4.1. 스프링 데이터 JAP(Spring Data JPA)
- 스프링 데이터의 공통적인 기능에서 JPA의 유용한 기술이 추가된 기술
- 스프링 데이터의 인터페이스인 PagingAndSoringRepository를 상속받아 JpaRepository 인터페이스를 만들었음
- JPA를 더 편리하게 사용하는 메서드를 제공
ex. 지금까지 엔티티 상태 변경을 위한 메서드 호출 예
@PersistenceContext
EntityManager em;
public void join(){
// 기존에 엔티티 상태를 바꾸는 방법(메서드를 호출해서 상태 변경)
Member member = new Member(1L, "홍길동");
em.persist(member);
}
- EntityManager: EntityManager는 JPA의 핵심 인터페이스로, 엔티티 객체를 영속성 컨텍스트에 저장, 조회, 삭제 등을 할 수 있음. 이 코드에서는 em.persist(member)를 통해 새로운 Member 엔티티를 영속성 컨텍스트에 추가하고, 이를 데이터베이스에 저장함.
- 직접적인 제어: 이 방식은 JPA 표준 API를 직접 사용하여 엔티티 상태를 관리하는 방법.
- 개발자가 EntityManager의 메서드를 직접 호출하여 트랜잭션의 범위를 제어하고, 데이터베이스 작업을 세밀하게 조정 가능 - 복잡성: 직접적인 제어가 가능하지만, 더 많은 코드가 필요하고 복잡도가 높아질 수 있음. 트랜잭션 관리는 개발자가 직접 처리해야 하며, 복잡한 쿼리나 엔티티 관리를 위해 많은 추가 코드가 필요할 수 있음.
- 스프링 데이터 JPA 사용하면 -> 리포지터리 역할을 하는 인터페이스를 만들어 데이터베이스의 테이블 조회,수정,생성,삭제 같은 작업 간단히 가능
public interface MemberRepository extends JpaRepository<Member, Long>{
}
- JpaRepository 인터페이스를 생성할 MemberRepository 인터페이스에서 상속받음
- 제네릭에는 관리할 <엔티티 이름, 엔티티 기본값의 타입> 입력
->기본 CRUD 메서드 사용가능
- JpaRepository: JpaRepository는 Spring Data JPA가 제공하는 인터페이스로, 기본적인 CRUD (Create, Read, Update, Delete) 및 페이징, 정렬 등의 기능을 자동으로 제공.
- MemberRepository 인터페이스는 JpaRepository를 확장함으로써, 별도의 구현 없이도 Member 엔티티에 대한 데이터베이스 작업을 쉽게 수행 가능 - 간소화된 코드: JpaRepository를 확장하면, findAll(), save(), delete() 등과 같은 여러 기본적인 메서드를 사용 가능
- 복잡한 쿼리도 메서드 이름만으로 자동 생성할 수 있으며, 필요한 경우 @Query를 사용하여 JPQL 또는 네이티브 쿼리를 작성 가능 - 편리함: Spring Data JPA가 대부분의 반복적인 데이터 접근 코드를 처리해주기 때문에 개발자는 비즈니스 로직에 집중 가능
- 트랜잭션 관리도 Spring이 자동으로 처리해줌
<요약>
- EntityManager 사용: 개발자가 JPA 표준 API를 사용하여 엔티티와 영속성 컨텍스트를 직접 관리함 더 많은 제어권이 있지만 코드가 복잡할 수 있음
- JpaRepository 사용: Spring Data JPA가 제공하는 리포지토리 인터페이스를 통해 데이터 접근 로직을 자동으로 처리하며, 코드가 간결하고 생산성이 높아짐. Spring이 많은 부분을 자동화해줌.
일반적으로 Spring 애플리케이션에서는 JpaRepository를 사용하는 것이 더 일반적이며, 간단한 CRUD 작업이나 쿼리에 적합. 그러나 복잡한 트랜잭션 관리나 성능 최적화가 필요한 경우에는 EntityManager를 직접 사용하는 경우도 존재.
4.2. 스프링 데이터 JPA 에서 제공하는 메서드 사용해보기
package org.example.springbootdeveloper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class MemberService {
@Autowired
MemberRepository memberRepository;
public void test(){
// 1) 생성 (Create)
memberRepository.save(new Member(1L, "A"));
// 2) 조회 (Read)
Optional<Member> member = memberRepository.findById(1L); // 단건 조회
List<Member> allMembers = memberRepository.findAll(); // 전체 조회
// 3) 삭제 (Delete)
memberRepository.deleteById(1L);
}
}
1) save() 메서드를 호출 -> 데이터 객체 저장 가능
전달 인수로 엔티티 Member 넘기면 -> 반환값으로 저장한 엔티티를 반환받을 수 있음
2) findById() 메서드에 id를 지정해 엔티티를 하나 조회 가능
findAll() 메서드는 전체 엔티티를 조회
3) deleteById() 메서드에 id를 지정하면 엔티티 삭제 가능
5. 예제 코드 살펴보기
- 애너테이션의 역할 알아보기
5.1. Member.java 파일
// src / main / java / org.example / springbootdeveloper / Member.java
package org.example.springbootdeveloper;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity // 1) 엔티티로 지정
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 2) 기본 생성자
@AllArgsConstructor
public class Member {
@Id // 3) id 필드를 기본키로 지정
@GeneratedValue(strategy = GenerationType.IDENTITY) // 4) 기본키를 자동으로 1씩 증가
@Column(name = "id", updatable = false)
private Long id; // DB테이블의 'id' 컬럼과 매칭
@Column(name = "name", nullable = false) // 5) name 이라는 not null 컬럼과 매핑
private String name; // DB 테이블의 'name' 컬럼과 매칭
}
1) @Entity : Member 객체를 JPA가 관리하는 엔티티로 지정
== Member 클래스와 실제 데이터베이스의 테이블을 매핑 시킴
- @Entity 속성 중 name 사용 -> name의 값을 가진 테이블 이름과 매핑됨
- 테이블 이름을 지정하지 않으면 클래스 이름과 같은 이름의 테이블과 매핑됨
ex. 여기서는 이름을 지정X
-> 클래스 이름과 같은 데이터베이스의 테이블인 member 테이블과 매핑됨
- @Entity 애너테이션에서 테이블을 지정하고 시파면 name 파라미터에 값 지정
@Entity(name = "member_list") // 'member_list'라는 이름을 가진 테이블과 매핑
public class Article {
//.. 생략...
}
2) @NoArgsConstructor(access = AccessLevel.PROTECTED) : protected 기본 생성자
- 엔티티는 반드시 기본생성자가 있어야함
- 접근 제어자는 public 또는 protected 여야함
- public 보다는 protected가 더 안전
- 접근제어자가 protected인 기본 생성자를 생성
3) @Id
- Long 타입의 id 필드를 테이블의 기본키로 지정
4) @GeneratedValue
- 기본키의 생성방식을 결정
ex. 여기서는 자동으로 기본키가 증가되도록 지정
<자동키 생성 설정방식>
- AUTO : 선택한 데이터베이스 방언(dialect)에 따라 방식을 자동으로 선택(기본값)
- IDENTITY : 기본키 생성을 데이터베이스에 위임(=AUTO_INCREMENT)
- SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본키를 할당하는 방법. 오라클에서 주로 사용
- TABLE : 키 생성 테이블 사용
5) @Column : 데이터베이스의 컬럼과 필드를 매핑
<@Column 애너테이션의 속성>
- name : 필드와 매핑할 컬럼 이름. 설정하지 않으면 필드 이름으로 지정해줌
- nullable : 컬럼의 null 허용 여부. 설정하지 않으면 true (nullable)
- unique : 컬럼의 유일한 값(unique) 여부. 설정하지 않으면 false(non · unique)
- columnDefinition : 컬럼 정보 설정. default 값을 줄 수 있음
5.2. MemberRepository.java 파일
// src / main / java / org.example / springbootdeveloper / MemberRepository.java (Interface)
package org.example.springbootdeveloper;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
}
- 리포지토리(Repository) : 엔티티에 있는 데이터들을 조회, 저장, 변경, 삭제 할 때 사용하는 인터페이스
- JpaRepository 클래스 (== 스프링 데이터 JPA에서 제공하는 인터페이스) 를 상속받아 간단하게 구현 가능
- JpaRepository 클래스를 상속받을 때, 엔티티 Member와 엔티티의 기본키 타입 Long을 인수로 넣어줌
- 해당 리포지토리를 사용할 때 JpaRepository에서 제공하는 여러 메서드를 사용가능하게 됨
6. 정리
- ORM(Object Relational Mapping) : 관계형 데이터베이스와 프로그램 간의 통신 개념
ㄴ 객체와 데이터베이스를 연결하는 프로그래밍 기법
- JPA(Java Persistence API): 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 기술 명세
ㄴ 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
ㄴ 엔티티 : 영속성을 가진 객체
ㄴ 엔티티 매니저 : 엔티티를 관리하며 조회, 삭제, 수정, 생성하는 역할을 함
ㄴ 엔티티 매니터 팩토리 : 엔티티 매니저를 만드는 곳
ㄴ 엔티티 매니저 특징 : 엔티티를 영속성 컨텍스트에 저장
ㄴ 영속성 컨텍스트 특징 : 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩
ㄴ 엔티티의 상태 : 분리, 관리, 비영속, 삭제 상태
- 하이버네이트(Hibernate) : JPA의 대표적인 구현체
ㄴ 자바 언어를 위한 ORM 프레임워크
- 스프링데이터 JPA (Spring Data JPA) : JPA를 쓰기 편하게 만들어놓은 모듈
드디어 그 유명한 JPA가 뭔지 공부했다! 오늘 면접에서도 JPA가 뭐냐고 물어봐서 Mybatis 겨냄과 비교해가면서 대답을 했었는데, 찬찬히 관련된 개념을 살펴보니 더 이해가 깊어진 것 같다.
예전 3월말에 팀 프로젝트를 진행할때 다른 팀은 스프링 부트를 사용해서 구경을 한 적이있었다.
그때 Mapper 를 사용하지 않고 테이블명과 컬럼명을 직접 Java 코드로 작성하면서 @Id 이런 애너테이션을 작성하는 것을 본 적이 있었다. 스프링이랑 스프링부트는 정말 많이 다르구나, 어렵구나! 라는 생각을 했었는데, 하나하나 개념을 뜯어놓고 보면 그리 다른 것 같지는 않다.
sql문을 작성하지 않아도 된다는 장점이 존재하는 것 같지만 sql문이 더 익숙해져버린 사람에게는 뭐가 더 이득일지... 이건 프로젝트를 실제로 해봐야 와닿는 지점일 것 같다.
'Spring Boot' 카테고리의 다른 글
[Spring Boot]10. 스프링 부트3와 테스트 - JUnit, AssertJ (0) | 2024.08.21 |
---|---|
[Spring Boot] 9. 스프링 부트 3 구조 살펴보기 (0) | 2024.08.20 |
[Spring Boot] 8. 스프링 부트 3 코드 이해하기 (0) | 2024.08.20 |
[Spring Boot] 7. 스프링 부트3 과 자바 버전 (2) | 2024.08.19 |
[Spring Boot] 6. 스프링 부트 자동 구성 (0) | 2024.08.19 |