신입 개발자 면접 대비 스프링 개념
2025.07.08 업데이트
1.스프링이란?
스프링은 자바 객체를 스프링 컨테이너가 대신 생성하고 관리하며, 의존성 주입(DI)과 제어의 역전(IoC)을 통해 코드의 결합도를 낮추고 유지보수를 용이하게 해주는 프레임워크
-컨테이너란?
스프링이 객체(Bean)을 생성하고 관리하는 공간을 말함. 흔히 말하는 ApplicationContext나 BeanFactory가 스프링의 컨테이너 역할을 함.
-그런데 왜 ApplicationContext나 BeanFactory가 낮설까?
:스프링 부트를 사용하고 있기 때문에. 스프링 부트에서는 @SpringBootApplication 어노테이션이 붙은 클래스가 실행 될 때, 자동으로 ApplicationContext를 만들고 내부적으로 관리함.
-의존성 주입(Dependency Injection)?
DI란, 객체가 의존하는 다른 객체를 자신이 직접 생성하지 않고, 외부(스프링 컨테이너 등)에서 주입받는 것을 말함.
이렇게 되면, 직접 의존하는 객체를 생성하거나 검색해서 가져올 필요가 없어 결합도가 낮아지는 이점을 가지고 있음.
DI는 스프링의 핵심 철학인 제어의 역전(IoC)를 구현하는 대표적인 방법.
-제어의 역전(Inversion of Control)?
객체의 생성과 의존성관리 같은 제어권이 사용자(개발자)에게 있지 않고, 프레임워크에 있음.
2.ORM(Object Relational Mapping)이란?
-객체와 테이블을 매핑시키는 것.(자바는 객체 기반 언어리고, 데이터베이스는 테이블 기반) 관계형 데이터베이스를 OOP언어(객체지향 프로그래밍 언어)로 변환해주는 기술로 비즈니즈 로직에 집중할 수 있게 해주는 이점을 가짐.(SQL을 직접 쓰지 않아도 되니까) 그러나 복잡한 쿼리를 메소드로 처리하기 힘들다는 단점이 있음.
-JPA( Java Persistence API)?
ORM을 위해 자바에서 제공하는 API.
JPA는 자바 ORM 기술의 표준 '인터페이스'고 'Hibernate' 는 JPA의 대표적인 '구현체'
-JDBC(Java DataBase Connectivity)?
Java에서 데이터베이스와 연결하고 쿼리를 실행하기 위한 인터페이스로 가장 오래된 방식이며 SQL쿼리를 그대로 사용해야함.
-MyBatis?
MyBatis도 JDBC처럼 SQL쿼리를 그대로 사용하지만 이 쿼리를 .xml파일 또는 어노테이션으로 관리하고, 리턴결과를 Java객체로 매핑하여 받을 수 있는 이점이 있음
-JPA와 Spring Data JPA
JPA가 객체와 테이블간의 매핑을 위한 표준 명세로 '인터페이스' 라면, Spring Data JPA는 JPA를 더 쉽게 사용할 수 있도록 하는 라이브러리다. JPA 에서 가장 핵심적인 객체가 EntityManager객체다. JPA에서 모든 DB 작업을 담당하기 때문이다.
Spring Data JPA를 사용했다면 단순히 레포지토리를 만들고 JpaRepository를 상속하면 된다. 그러면 내부적으로 EntityManager를 실행한다.
-JPA의 N+1 문제
JPA를 사용하면 N+1문제를 지나칠 수가 없다...
N+1문제란 연관관계가 있는 엔티티를 조회할 때 조회된 데이터 개수(N)개 만큼 쿼리가 추가로 발생하는 현상을 말한다
즉, 1번의 쿼리를 날렸을 때 의도하지 않은 N번의 쿼리가 추가적으로 실행되는 것이다.
예를 들어, 게시판 서비스를 만들고 자유게시판,QnA게시판,알고리즘 게시판이 있다고 하자. 그리고 이 3개의 게시판에는 각각 10개의 게시글이 있다.
이 상황에서, 게시판 카테고리 리스트를 불러오면 게시판 카테고리 리스트를 불러오는 쿼리 1개에 추가적으로 각 게시판마다 연결된 게시글들을 불러오는 쿼리 3개(각 게시판 마다 연결된 게시글)가 추가되어 실행되는 것이다.
여기에 대한 해결점은 Fetch Join, batch size어노테이션을 사용하면 된다.
Fetch Join은 JPQL에서 미리 두 테이블을 Join하여 한번에 데이터를 가져오도록 하는 방식이기 때문에 N+1문제가 해결된다.
batch size는 지연 로딩(lazy loading)을 사용할 때, 여러 개의 연관 객체를 1개의 쿼리로 IN 절로 묶어서 가져오도록 한다.
N+1문제가 발생하는 자세한 원인에 대해서는 아래 글을 참고하길 바란다.
JPA N+1 이슈는 무엇이고, 해결책은 무엇인가요?
1번 조회해야할 것을 N개 종류의 데이터 각각을 추가로 조회하게 되서 총 N+1번 DB조회를 하게 되는 문제이다. 즉, JPA의 Entity 조회시 Query 한번 내부에 존재하는 다른 연관관계에 접근할 때 또 다시
velog.io
3. 스프링 부트?
스프링은 자바 기반 애플리케이션 개발을 지원하는 프레임워크인데 개발자가 직접 설정파일을 작성하여 스프링 컨테이너를 구성하고, 필요한 빈 객체를 등록하고, 빈 객체간 의존성을 모두 수동으로 해야한다. 또한 별도로 WAS(Web Application Server)를 설치하고 설정해야한다. 이러한 단점들을 해결하기 위한 것이 스프링 부트다.
즉, 스프링 부트는 스프링을 더 빠르고 편리하게 사용하기 위한 확장된 툴다. 그래서 스프링부트는 다양한 자동 설정으로 스프링을 사용할 수 있고, 별도의 WAS를 설치할 필요없이 내장된 서버(Tomcat, Jetty)가 있어 애플리케이션을 실행할 수 있다.
(흔히 인텔리제이에서 실행 버튼만 눌러도 서버가 실행되는 것이 이 WAS가 내장되어있기 때문임)
-WAS?
내가 작성한 백엔드 코드를 작동시키는 프로그램.
예를 들어, 아래와 같은 백엔드 코드를 작성했다.
그러면 클라이언트가 ~/hello로 보낸 요청을 WAS가 이 요청을 받아서 sayHello메서드를 실행하고, 그 리턴 결과를 다시 보내는 것도 WAS다. (정확히는 WAS의 내장된 서블릿 컨테이너가 실행하고 리턴해준다)
@GetMapping("/hello")
public String sayHello() {
return "Hello, world!";
}
-.jar 파일과 .war파일의 차이?
스프링 부트로 만든 백엔드 프로젝트를 배포할 때 ./gradlew bootJar 명령어로 jar파일을 만들고 java -jar로 실행 한 적이 있을 것이다.
.jar파일은 일반적인 Java 어플리케이션을 실행하기 위한 압축파일이다. 이 압축파일에는 우리가 작성한 코드들과, 설정파일, 라이브러리,그리고 WAS까지 같이 압축되어 있다. 그래서 스프링 부트로 만든 jar파일은 곧바로 실행까지 가능한 것이다.
(그러나 단순 스프링의 .jar는 WAS가 없는 단지 내가 작성한 코드와 설정파일의 묶음이다. 그래서 일반적으로 .jar 파일은 스프링부트에서 사용하는 포맷을 일컫는다.)
.war파일은 스프링 부트가 아닌 스프링에서 만든 웹 애플리케이션을 WAS에 배포하기 위해 압축한 파일이다.
-WS? WAS?
WS(WebServer)는 정적인 콘텐츠를 처리하는 서버. WAS(Web Application Server)는 동적인 콘텐츠까지 처리하는 서버다.
인터넷 초창기에는 논문과 같은 텍스트나 단순한 HTML 파일(단순 홈페이지)을 만들어 서버에 업로드하고 사용자는 그 정적인 콘텐츠를 그대로 받아보는 방식이었다. 이를 처리한 것이 WS다.
그런데 웹이 발전하면서 사용자의 요청에 따라 결과가달라지는 서비스가 등장했다. 회원가입이나 로그인, 검색 등 이러한 동적인 콘텐츠까지 처리하는 것이 WAS이다. 정확히는 WS에 발전되어 서블릿컨테이너 개념이 포함된 것이 WAS(WS+Servelet Container)다. 동적인 요청을 실제로 처리하는 주체가 바로 WAS에 있는 서블릿(Servlet)이다.
4.서블릿 컨테이너
서블릿은 클라이언트의 요청(HTTP요청)을 받아 처리하고 그에 대한 응답을 생성하는 자바 클래스로 WAS안에서 실행된다.다시 아래 코드를 보면 /hello로 들어오는 HTTP요청을 서블릿 객체가 해당 요청에 맞게 sayHello 메서드를 호출하는 것이다.
@GetMapping("/hello")
public String sayHello() {
return "Hello, world!";
}
-서블릿 컨테이너
서블릿들의 생성하고 관리하며 HTTP요청과 응답을 처리할 수 있도록 하는 환경이 서블릿 컨테이너이다.
서블릿 컨테이너는 자바 웹 개발자가 HTTP 통신을 직접 구현하지 않도록 도와준다.
(원래는 HTTP 통신을 구현하기 위해 개발자가 직접 TCP/IP 기반 소켓을 열고, 포트를 리스닝하는 등 직접 구현해야했다)
이 서블릿 컨테이너는 또 HTTP요청이 들어올 때마다 자바 스레드를 만들고 이 스레드가 서블릿을 호출한다)
Servlet 개념이 낯설다면, 그것 역시 스프링이 자동으로 처리해주었기 때문이다.
5. 싱글 스레드? 멀티스레드?
싱글 스레드인지 멀티스레드인지를 결정짓는 것은 스프링(혹은 스프링부트)가 아니라 WAS다. 그리고 대부분의 WAS는 멀티스레드로 동작한다. 이 말은 즉슨, 스프링/스프링부트도 멀티스레드 환경에서 실행된다.
-스레드란?
한 프로세스(프로그램)이 작업을 처리하는 단위. 즉 한 프로그램이 동시에 여러가지 일을 처리하면 멀티스레드. 그렇지 않으면 싱글스레드.
우리가 게시판 서비스를 만들었는데 사용자 a와 b가 동시에 접속해있다. 이 때, a는 글을 작성했고 동시에 b도 다른 글을 작성했다.
웹 서버가 멀티스레드 기반이라면 a와b의 요청은 각각 별도의 스레드에서 동시에 처리된다. 반면 싱글 스레드였다면 a의 요청이 완전히 끝난 후에야 b의 요청을 처리한다. 그러므로 대부분의 WAS는 멀티스레드 기반으로 동작한다.
-200개의 스레드
우리가 흔히 사용하는 WAS인 Tomcat의 기본 최대 스레드 수는 200개이다. 이 200개의 스레드를 모아놓은 곳을 스레드 풀(Thread Pool)이라고 한다. WAS는 요청이 오면 이 스레드 풀에서 스레드를 하나 꺼내어 처리하고 요청 처리가 끝나면 이 스레드를 종료하지 않고 다시 스레드 풀로 반환하여 재사용한다. 왜냐하면? 요청마다 새로 스레드를 생성하면 성능 저하와 메모리 낭비가 심하다. 그래서 미리 스레드를 만들어 두고 필요할 때마다 꺼내쓰고 반환하는 방식으로 작동되는 것이다. 이것이 스레드풀이 존재하는 이유다.
따라서, 흐름은 아래와 같다
- 사용자가 웹브라우저에서 HTTP요청을 보낸다.
- 톰캣 같은 WAS의 서블릿 컨테이너가 수신한다.
- 서블릿 컨테이너는 스레드풀에서 스레드를 하나 꺼낸다. 이 스레드에서 서블릿 컨테이너는 서블릿객체(DispatcherServlet)를 호출한다.
- 서블릿 객체가 해당 HTTP요청을 처리한다.
(여기서도 모든 흐름이 개발자가 직접 다루는 것이 아니라 자동으로 일어나므로 제어의 역전(IoC)가 이루어지는 것이다.)
6. 인증 방식
-인증(Authentication) vs 인가(Authorization)
인증은 유저가 누구인지 확인하는 절차이고 인가는 인증된 유저의 요청에 대해서 권한을 확인하고 허가 해주는 것이다.
예를 들어, 로그인 서비스는 인증절차이고 , 내가 작성한 글에 한해서만 수정/삭제가 가능한 것은 인가절차이다.
401 Unauthorized 에러는 인증되지 않은 사용자의 요청에서 발생하고 403 Forbidden에러는 인증은 되었지만 권한이 없어 발생한다.
-세션방식 vs 토큰방식
세션방식은 서버에 클라이언트의 로그인 상태(세션)를 저장(stateful하다 라고 표현), 토큰 방식은 클라이언트의 JWT토큰을 클라이언트 쪽 로컬에 저장하고 매 요청마다 토큰을 요구함으로써 서버가 따로 클라이언트의 로그인 상태를 저장하지 않는다(Stateless).
세션(Session): 사용자와 서버사이에 연결된 상태(상태 정보)
이로 인해, 보안적인 측면에서는 세션이 탈취되면 서버에서 세션을 즉시 처리할 수 있는 세션방식이 뛰어나다. 그럼에도 불구하고 토큰 방식을 최근에 많이 사용하는 이유는 세션을 서버에 저장하는 방식은 서버 메모리나 db에 부하가 심하고, 여러 서버에 세션을 공유해야 하므로 서버 확장 시 세션 관리가 어려워지는 단점이 있기 때문이다. 그리고 더군다나 요즘 같은 멀티 디바이스 환경에는 토큰 방식이 훨씬 적절하다.
(네이버만 해도 모바일에서도 로그인되고 컴퓨터에서도 로그인이 되는데 그러면 세션방식에서는 사용자의 디바이스 수만큼 세션이 서버에 저장되므로 그 용량이 어마어마 할 것이다... 따라서 클라이언트쪽에 저장되는 토큰방식이 적절하다)
7. 계층형 아키텍처
계층형 아키텍처는 시스템을 여러 개의 계층으로 분할하여 각 계층이 특정 역할을 담당하도록 하는 소프트웨어 아키텍처다.
(스프링 MVC도 사실 이 계층형 아키텍처를 기반으로 구축된 것이다.)
- Controller 계층 : 클라이언트의 요청을 받아 처리하는 '진입 지점'. 요청을 받아 Service 계층에 전달하고 결과를 받아 클라이언트에게 다시 전달한다.
- Service 계층 : 실질적인 비즈니스 로직을 처리하는 계층
- Repository 계층 : DB와 직접 통신하는 계층. 데이터의 저장,조회,수정,삭제를 지시함.
이 때, 우리가 Controller(RestController), Service,Repository 어노테이션을 붙으면 싱글톤 객체로 생성된다.
사용자의 요청이 올 때 마다 각각의 계층 객체를 매번 생성하는 것보다. 싱글톤으로 생성해두면 이후에는 같은 객체를 재사용하기 때문에 빠르고 효율적이다.
(싱글톤? 애플리케이션 실행 시 빈(Bean) 객체를 단 하나만 생성해서 공유하는 방법)
-DTO(Data Transfer Object), DAO(Data Access Object)
DTO는 계층 간 데이터를 주고받기 위해 사용하는 객체. 효율적인 데이터 전달을 위해 만들어졌다고 보면 된다.
예를 들어, 클라이언트 쪽에 유저정보를 넘겨야할 때 유저 엔티티 객체 자제를 넘겨야한다고 해보자.
클라이언트는 유저의 이름과 성별만 필요한데 굳이 불필요하게 id값,패스워드, 주소 같은 정보도 같이 넘어져 간다.
이는 비효율일뿐더러, 보안에도 좋지않다. 따라서 엔티티로 부터 필요한 정보만 뽑아 DTO를 만들어 클라이언트에게 넘기는 것이
효율적이다.
DAO는 DB에 접근하는 역할을 하는 객체로 Repository가 대신한다고 보면 된다.
8. MVC패턴? RESTful API
MVC패턴과 RESTful API는 같은 카테고리 속 대비되는 개념이 아니다. 애초에 다른 카테고리다.
MVC패턴은 역할을 분리하는 '소프트웨어 아키텍처' 이고 RESTfulAPI는 자원 중심의 HTTP메서드(GET,POST,PUT,DELETE 등)을 활용하는 API설계방식이다.
-MVC패턴
애플리케이션의 구조를 Model,View,Controller 3가지 주요 컴포넌트로 나누어 개발하는 방법.
모델은 비즈니스 로직과 데이터 처리, 뷰는 주로 HTML,JSP나 타임리프를 통해 구현된 화면, 컨트롤러는 사용자의 요청을 받아 모델에 넘겨 비즈니스 로직을 실행하고 처리결과를 모델로부터 받아 뷰로 넘기는 역할을 한다.
서버 사이드 렌더링에서 (즉 서버에서 화면을 반환할 때) 주로 사용된다.
-RESTful API
REST API라고도 하며, 프론트엔드와 백엔드가 독립적으로 구성될 때, 즉 프론트엔드가 동적인 페이지를 렌더링 할 때, 백엔드와 JSON데이터를 주고받는 API에서 사용되는 것이다.
내가 주로 해오던 방식은 프론트엔드 프레임워크가 따로 있고 RestControler, Service, Repository 3가지 계층으로 API를 처리했다. 즉 나는 MVC 패턴 기반의 구조를 사용했으나 정확히는 MVC패턴은 아니고 동시에 RESTful API 방식으로 백엔드를 설계했던 것으로 볼 수 있다. 일단, HTTP 메서드를 활용하여 JSON데이터를 반환했으므로 RESTful API방식이다. 화면은 프론트엔드쪽에서 만드니 화면이 아닌 JSON데이터를 반환했다. 따라서 MVC 패턴에서 V는 없다. 하지만 RestController 계층에서 사용자의 요청을 받아 데이터를 비즈니스 로직을 처리하는 Service 계층에 넘겼다. 그리고 데이터처리는 Repository 계층에서 담당했다.
그러므로 MVC패턴에서 Model역할은 Service,Repository계층에서 구현했고, Controller역할은 RestController계층에서 담당했으니 MVC 패턴 기반의 구조를 사용했다고 할 수 있다.
9. 의존성 주입방식 3가지
앞서 DI(Dependency Injection)에 대해 서술한 적 있다.
DI는 객체를 직접 생성하지 않고 외부(스프링 컨테이너)로부터 객체를 주입받는 것을 말하며 결합도가 낮아 유지보수가 용이해진다는 장점이 있다.
예를 들어, 카메라 객체가 있고 카메라 객체는 반드시 렌즈 객체가 있어야한다고 보자.
-필드 주입
@Component
public class Camera {
@Autowired
private Lens lens;
public void shoot() {
System.out.println("Shooting with " + lens.getType());
}
}
필드에 Autowired 어노테이션만 붙여주면 자동으로 의존성이 주입되어 매우 간단한 방식이다.
그러나 객체 생성 시 의존성이 불명확하다는 단점이 존재한다.
-Setter 주입 방식
@Component
public class Camera {
private Lens lens;
@Autowired
public void setLens(Lens lens) {
this.lens = lens;
}
public void shoot() {
System.out.println("Shooting with " + lens.getType());
}
}
Setter메서드에 @Autowired 어노테이션을 붙이면 되는 방식이다.그러나 외부에서 setLens 메서드를 호출하여 의존성을 언제든지 변경할 수 있다는 점에서 단점을 가진다.
-생성자 방식
@Component
public class Camera {
private final Lens lens;
@Autowired //클래스의 생성자가 하나이고 생성자로 주입받는 객체가 빈으로 등록되어있으면 Autowired생략 가능
public Camera(Lens lens) {
this.lens = lens;
}
public void shoot() {
System.out.println("Shooting with " + lens.getType());
}
}
가장 권장되는 방식이다.
생성자 주입 방식은 final 필드로 불변객체로 유지되어 외부에서 변경할 수 없어 안정적이고 순환 참조 방지에 유리하다는 장점을 가진다.
예를 들어 아래와 같은 코드가 있을 때
@Service
public class A{
@Autowired
private B b;
public void HelloA() {
b.HelloB();
}
}
@Service
public class B{
@Autowired
private A a;
public void HelloB() {
a.HelloA();
}
}
필드 주입과 Setter주입 방식은 빈이 생성된 후에 참조를 하기 때문에 어플리케이션을 실행할 때 실행이 되고 이후에 이 코드가 실행될 때 오류가 발생한다. 다시 말해서 실제 이 코드가 실행되기 전까지 오류인지 파악할 수 없다.
그러나 생성자 주입방식은 애초에 어플리케이션을 실행하는 시점. 즉 런타임 시점에서 바로 에러가 터지므로 오류를 확인함으로써 순환참조를 방지할 수 있다. 이러한 이점으로 생성자 주입방식이 권장된다.
10. Spring Security
Spring Security는 보안을 담당하는 프레임 워크. 인증(Authentication)과 인가(Authorization)을 쉽게 구현할 수 있도록 도와주며, 다양한 보안 기능을 제공. 서블릿 필터 체인을 기반으로 동작하며, 요청이 컨트롤러에 도달하기 전에 보안 검사를 수행한다.
(실제로 아무설정을 하지않고 Spring Security 프레임워크를 사용하면 폼로그인이 설정된다.)
https://www.elancer.co.kr/blog/detail/235
Spring Security란? 사용하는 이유부터 설정 방법까지 알려드립니다! I 이랜서 블로그
홈페이지에 인증 및 권한 기능을 빠르게 부여해 인증 및 권한 보호 기능을 손쉽게 추가할 수 있는 Spring의 프레임워크 중 하나인 ‘Spring Security’에 대해 이랜서에서 자세히 알려드립니다. I spring
www.elancer.co.kr
Spring Security 아키텍처
- 사용자의 요청이 서버로 들어오면, Authentication Filter(UsernamePasswordAuthenticationFilter)가 요청을 가로채고 그 요청을 Authentication Manager에게 위임한다.
- Authentication Manager은 등록된 Authentication Provider를 조회하며 인증을 요구한다
- Authentication Provider가 실제 데이터를 조회하여 UserDetails결과를 리턴한다.
- 인증이 성공되면 결과는 SecurityContextHolder에 저장이 되어 저장된 유저정보를 Controller에서 사용할 수 있게 된다.
*JWT 방식에서는 UsernamePasswordAuthenticationFilter를 사용하지 않고 커스텀한 필터를 직접 구현해 사용함
필터와 인터셉트의 차이
- 필터는 dispatcher servlet으로 요청이 도착하기 전에 동작한다.
- 인터셉터는 dispatcher servlet을 지나고 controller에 도착하기 전에 동작한다
11.JWT(Json Web Token)
JWT는 서버와 클라이언트 간에 인증 정보를 안전하게 전송하기 위한 토큰. 서버가 사용자의 로그인 인증요청을 받아들이면, 토큰을 만들어 클라이언트에게 전달하고 클라이언트는 이후의 모든 요청에 이 토큰을 HTTP 헤더에 포함시켜 서버에 인증을 요청한다.
*세션 기반 인증과 달리 서버에 (로그인)상태를 저장하지 않기 때문에 Stateless 인증 방식이라 불리는 것
JWT의 구성요소
1.헤더 : 해싱 알고리즘 정보(ex. RS256)와 토큰의 타입(ex.JWT)에 대한 정보를 담고 있음
2.페이로드 : 실제 토큰에 담은 사용자 정보(Claim)이 담겨있음.
3.시그니처: 헤더와 페이로드를 비밀키로 암호화 한 것.
*헤더와 페이로드는 단순히 인코딩되기 때문에 다시 디코딩하면 누구나 내용을 확인 할 수 있고 조작도 가능하다. 그러나 Signature은 서버에서 관리되는 비밀키가 유출되지 않는 이상 복호화할 수 없다. 왜냐하면 해싱 알고리즘 자체가 복호화 할 수 없는 알고리즘이기 때문.이로 인해 이 Signature는 토큰의 위변조 여부를 확인하는데 사용된다.
12. 영속성 컨텍스트
영속성 컨텍스트는 엔티티를 영구 저장하는 환경이라는 뜻으로 애플리케이션과 데이터베이스 사이에서 객체를 보관하는
가상의 데이터베이스 역할이라고 보면 된다.
JPA에서 가장 핵심적인 객체는 Entity Manager 라고 했다.
*Entity Manager가 엔티티를 저장하고, 수정하고, 삭제하고, 조회하는 등 엔티티와 관련된 모든 일을 처리하기 때문이다.
이 Entity Manager 객체가 생성될 때마다 1:1로 영속성 컨텍스트 하나가 만들어진다. 그리고 EntityManager를 통해 persist(), find(),remove()메서드를 통해 영속성 컨텍스트에 접근하여 데이터를 관리할 수 있다.
영속성 컨텍스트는 다음과 같은 이점을 가진다
- 1차 캐시 : persist()를 통해 영속성 컨텍스트에 등록하면 엔티티는 영속성 내부의 1차 캐시라는 곳에 저장된다.(DB에 저장되기 전 이 가상의 공간인 영속성 컨텍스트에 먼저 저장된다고 보면된다.) 이후에 find()를 통해 데이터를 조회시 DB에서 곧바로 조회하는 것이 아니라 이 1차 캐시에서 먼저 조회한다. 이를 통해 성능 최적화, 불필요한 DB조회를 줄이는 캐시로써 장점을 가지고 있다.
- 동일성 보장 : 같은 트랜잭션 내에서 같은 엔티티를 여러 번 조회하면 항상 같은 객체 인스턴스를 반환
- 쓰기 지연 : 객체를 영속성 컨텍스에 저장해도 DB에 곧바로 insert 쿼리를 날리지 않는다. EntityManager는 트랜잭션을 커밋하기 전까지는 INSERT 쿼리를 모아두었다가, 트랜잭션이 커밋할 때 모아둔 쿼리를 DB에 보낸다. 이 역시도 DB에 대한 쿼리 요청을 최소화한다.
- 변경 감지(dirty checking) : 영속성 컨텍스트가 관리하는 영속상태의 엔티티에 적용되는 것으로 해당 엔티티의 객체를 수정하면 따로 수정된 데이터를 저장하는 코드를 작성하지 않아도 자동으로 변경된 필드만 감지하여 UPDATE 쿼리가 실행되어 변경된다.
- 지연 로딩(Lazy Loading) : JPA에서 연관된 데이터를 조회할 때, 처음부터 모든 데이터를 다 가져오지 않고 실제로 그 데이터가 필요한 순간에 SELECT 쿼리를 실행해서 가져오는 것
엔티티의 생명 주기
- 비영속(new) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태. new 키워드를 통해 엔티티 객체를 새롭게 생성할 떈 비영속 상태에 있다.
- 영속(persist) : 영속성 컨텍스트에 관리되는 상태. EntityManager.persist()를 통해 영속성 컨텍스트에 등록한다.
- 준영속(detach) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(remove) : 영속성 컨텍스트에서 삭제된 상태
Spring Data JPA에서 엔티티 생명주기
Spring Data JPA를 사용한다면 따로 EntityManager객체를 호출하여 사용하질 않기 때문에 엔티티의 생명주기가 언제 변화되는 건지 헷갈릴 수 있다.
- 비영속(new) : 마찬가지로 엔티티 객체를 new로 생성했다면 영속성 컨텍스트에 아직 등록되지 않은 비영속 상태에 있다.
- 영속(persist) : save() 메서드를 호출하면 내부적으로 entitymanager.persist()가 호출되어 영속성 컨텍스트에 등록된다.
따라서 이때부터 JPA가 변경감지(dirty checking)가 가능해진다. 그리고 트랜잭션 커밋 시 DB에 반영되는 것이다.
또한, findBy~() 등 조회 메서드를 쓰면 내부적으로 entitymanger.find()가 호출된다. 그러면 DB에서 조회와 먼저 영속성 컨텍스트의 1차 캐시에 저장해놓는다. 즉 영속 상태가 되는 것이다. 이때도 마찬가지로 변경감지가 가능해지는 것이다. - 삭제(remove) : delete()를 호출하면 내부적으로 entitymanger.remove()가 실행되어 영속성 컨텍스트에서 삭제된다. 그리고트랜잭션 커밋 시 DB에서 삭제까지 완료된다.