1️⃣ Filter
✅ 공통 관심사항 ( = 횡단 관심사 )
여러 위치에서 공통적으로 사용되는 부가 기능이고 Filter가 나온이유는 공통 관심사의 처리 때문임.

CRUD는 핵심 기능 ➡ 비즈니스 로직
파란 기능들이 부가기능 ➡ 비즈니스 로직과는 별개로 동작하는 기능
🚀 요구사항 : 로그인 한 유저만 특정 API를 사용할 수 있어야 한다.
- 해결방법
- 언제나 핵심은 수정에 있다!
- 화면에서 로그인 하지 않으면 API를 사용하지 못하도록 막는다.
- 유저가 HTTP 요청을 마음대로 조작할 수 있다.
- Controller에서 로그인 여부를 체크하는 Logic을 작성한다.
- 실제로는 인증이 필요한 모든 컨트롤러에 공통으로 로그인 여부를 체크해야 한다.
- 로그인 로직이 변경될 때 마다 로그인 여부를 체크하는 Logic 또한 변경될 가능성이 높다.
@RestController
@RequestMapping("/post")
public class PostController {
@PostMapping
public PostResponseDto create(PostCreateRequestDto request) {
// 로그인 여부 확인 로직
// 생성 로직
}
@PutMapping("/{postId}")
public void update(
@PathVariable Long postId,
PostUpdateRequestDto request
) {
// 로그인 여부 확인 로직
// 수정 로직
}
@DeleteMapping("/{postId}")
public void delete(@PathVariable Long postId) {
// 로그인 여부 확인 로직
// 삭제 로직
}
}
위와같이 여러가지 로직에서 공통으로 관심이 있는 부분을 공통 관심사 라고 한다.
➡ Spring AOP를 활용할 수 있다.
➡ Web과 관련된 공통 관심사는 Servlet Filter나 Spring Intercepter를 사용한다.
- HttpServletRequest 객체를 제공하기 때문에 HTTP 정보나 URL 정보에 접근하기 쉽다.
✅ Servlet Filter
Servlet Filter는 보안, 로깅, 인코딩, 인증/인가 등 다양한 작업을 처리하기 위해 사용
특징
- 공통 관심사 로직 처리
- 공통된 로직을 중앙 집중적으로 구현하여 재사용성이 높고 유지보수가 쉽다.
- 모든 요청이 하나의 입구를 통해 처리되어 일관성을 유지한다.
- HTTP 요청 및 응답 필터링
- Filter Chain
- 여러 개의 필터가 순차적으로 적용될 수 있다.
- filterChain.doFilter(request, response); 다음 필터로 제어를 전달한다.
- doFilter()
- 실제 필터링 작업을 수행하는 주요 메소드로 필터가 처리할 작업을 정의한다.
- 다음 필터로 제어를 넘길지 여부를 결정한다.
적용

- Filter를 적용하면 Servlet이 호출되기 이전에 Filter를 항상 거치게된다.
- 공통 관심사를 필터에만 적용하면 모든 요청 or 응답에 적용된다 .
- Filter는 특정 URL Pattern에 적용할 수 있다.
- Spring을 사용하는 경우 Servlet은 Dispatcher Servlet이다.
Filter Chain

- Filter는 개발자가 자유롭게 추가할 수 있다.
- Filter는 순서를 지정하여 추가할 수 있다.
- Filter는 Chain 형식으로 구성
✅ Filter Interface
Java Servlet에서 HTTP 요청과 응답을 가로채고, 이를 기반으로 다양한 처리 작업을 수행하는 데 사용되는 Interface이다.
⚡ jakarta.servlet.Filter

- 주요 메서드
- init()
- Filter를 초기화하는 메서드이다.
- Servlet Container가 생성될 때 호출된다.
- default method이기 때문에 implements 후 구현하지 않아도 된다.
- doFilter()
- Client에서 요청이 올 때 마다 doFilter() 메서드가 호출된다.
- doFilter() 내부에 필터 로직(공통 관심사 로직)을 구현하면 된다.
- WAS에서 doFilter() 를 호출해주고 하나의 필터의 doFilter()가 통과된다면
- Filter Chain에 따라서 순서대로 doFilter() 를 호출한다.
- 더이상 doFilter() 를 호출할 Filter가 없으면 Servlet이 호출된다.
- Client에서 요청이 올 때 마다 doFilter() 메서드가 호출된다.
- destroy()
- 필터를 종료하는 메서드이다.
- Servlet Container가 종료될 때 호출된다.
- default method이기 때문에 implements 후 구현하지 않아도 된다.
@Slf4j
public class CustomFilter implements Filter {
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
// Filter에서 수행할 Logic
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
log.info("request URI={}", requestURI);
// chain 이 없으면 Servlet을 바로 호출
chain.doFilter(request, response);
}
}
ServletRequest 는 기능이 별로 없어서 대부분 기능이 많은 HttpServletRequest 를 다운 캐스팅 하여 사용한다.
Filter 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean customFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
// Filter 등록
filterRegistrationBean.setFilter(new CustomFilter());
// Filter 순서 설정
filterRegistrationBean.setOrder(1);
// 전체 URL에 Filter 적용
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
- setFilter()
- 등록할 필터를 파라미터로 전달하면 된다.
- setOrder()
- Filter는 Chain 형태로 동작한다.
- 즉, 실행될 Filter들의 순서가 필요하다.
- 파라미터로 전달될 숫자에 따라 우선순위가 정해진다.
- 숫자가 낮을수록 우선순위가 높다.
- addUrlPatterns()
- 필터를 적용할 URL 패턴을 지정한다.
- 여러개 URL 패턴을 한번에 지정할 수 있다.
- 규칙은 Servlet URL Pattern과 같다.
- filterRegistrationBean.addUrlPatterns("/*")
- 모든 Request는 Custom Filter를 항상 지나간다.
2️⃣ 객체와 RDB
✅ 객체와 RDB
📚 객체는 클래스를 통해 만들어지며 속성(field)와 기능(method)를 포함하며 관계형 데이터베이스는 데이터를 테이블 형식으로 표현하며 각 테이블은 열(column)과 행(row)으로 구성된다.
- 객체 지향 언어 Java
- 객체를 저장할 수 있는 다양한 종류의 Database
- RDB(무결성, 일관성)
- NoSQL
- File 외기타 등등..
- 관계형 데이터베이스와 객체 지향의 패러다임 불일치 문제가 발생한다.
- 객체를 저장할 수 있는 다양한 종류의 Database
- 관계형 DB에 객체 저장 시 발생하는 문제점
- 리소스별 CRUD 반복
- 객체의 수정
- 패러다임 불일치 문제
✅ 패러다임 불일치 문제 1
객체에서는 상속과 다형성을 통해 객체 관계를 표현할 수 있지만 RDB는 이 개념을 직접 지원하지 않고 별도 매핑이 필요하다. 또한, 객체는 참조로 관계를 표현하고 RDB는 JOIN을 사용하여 관계를 결합한다.
1. 상속

- DB는 상속관계가 없다.
- 관계형 DB에서는 Data를 슈퍼타입, 서브타입 관계로 설정한다.
2. 연관관계
테이블의 연관관계는 외래 키를 사용한다
객체의 연관관계는 참조를 사용한다.
public class Tutor {
private Long id; // PK
private Company company; // 참조 연관관계
private String name;
public Company getCompany() {
return company;
}
public Company setCompany(Company company) {
this.company = company;
}
}
public Company {
private Long id; // PK
private String name;
}
✅ 패러다임 불일치 문제 2
객체 지향 언어에서 데이터와 동작을 함께 캡슐화하는 방식과, RDB가 데이터를 정규화된 테이블에 관계 중심으로 저장하는 방식의 차이에서 발생한다. 이로 인해 객체를 데이터베이스에 저장하거나 조회할 때 복잡한 매핑과 변환이 필요해지고 코드의 복잡성과 개발자의 부담이 증가한다.
1. 객체 그래프

- 객체는 연관된 객체를 탐색할 수 있어야 한다.
- SQL Query
- 실제로는 실행된 SQL 만큼만 탐색할 수 있다.
- Entity의 신뢰성에 문제가 발생한다.
- Layered architecture는 다음 계층을 믿고 사용할 수 있어야한다.
- Entity는 믿고 사용할 수 있어야한다.
- productRepository.findById();의 신뢰성 확인이 필요하다.
2. 객체의 비교
Product product1 = productRepository.findById(productId);
Product product2 = productRepository.findById(productId);
product1 == product2; // false
- 데이터는 같지만, 새로운 인스턴스기 때문에 객체의 주소값이 다르다.
▶ Collection에 저장하면 편하게 사용할 수 있다.
- 결론
- 객체 지향적으로 설계하면 코드가 점점 복잡해진다.
- SQL Query, Mapping이 까다롭다.
- Collection처럼 객체가 관리된다면 편리해진다.
- 객체 지향적으로 설계하면 코드가 점점 복잡해진다.
- JPA(Java Persistence API) 등장
- 마치 Java의 Collection처럼 객체를 저장하고 사용할 수 있게 해준다.
- JPA를 사용하면 패러다임 불일치 문제를 모두 해결할 수 있다.
3️⃣ JPA
✅ JPA
객체 지향 프로그래밍 언어인 Java와 관계형 데이터베이스 간의 패러다임 불일치 문제를 해결하여 데이터베이스 작업을 객체 지향적으로 수행할 수 있도록 지원한다.

- 대표적인 구현체로 Hibernate를 주로 사용한다.
- 표준으로 만들어지면 더욱 명확하게 정의하고 사용할 수 있는 장점이 생긴다.
ORM(Object-Relational Mapping)
ORM은 Java 뿐만이 아니라 다양한 언어에서 존재하는 기술이다.
- 객체와 관계형 DB를 자동으로 Mapping하여 패러다임 불일치 문제를 해결한다.
- JDBC API

- 데이터베이스와 상호작용 하기위해 JDBC API를 개발자가 직접 사용했다.

- 개발자가 아닌 JPA가 중간에서 개발자의 역할을 대신한다. ( 개발하기 편해짐 )
✅ 사용하는 이유
JPA의 사용 목적은 SQL 중심적인 개발에서 객체 중심으로 개발하기 위함에 있다.
1. 생산성
// 저장 C
jpa.persist(tutor);
// 조회 R
Tutor tutor = jpa.find(Tutor.class, tutorId);
// 수정 U
tutor.setName("수정할 이름");
// 삭제 D
jpa.remove(tutor);
- persist란 영구히 저장한다는 뜻
- 마치 컬렉션에 저장한듯 객체가 저장되어 조회가 간편하다.
- 수정하고자 할 때 꺼낸 객체에 setName() 하면된다.
2. 유지보수성
// 기존
public class Tutor {
private String id;
private String name;
}
// 필드 수정
public class Tutor {
private String id;
private String name;
private Integer age;
}
- 객체 필드가 수정 되어도 SQL은 JPA가 알아서 처리한다.
3. 패러다임 불일치 문제 해결
- 상속
- CRUD 방식 다 가능. ( 1. 생산성과 동일 )
- 연관관계
- Collection 처럼 사용 할수 있음.
- 객체 그래프 탐색
Tutor tutor = jpa.find(Tutor.class, tutorId);
Company company = tutor.getCompany();
- JPA를 사용하면 신뢰할 수 있는 엔티티, 계층이 된다.
- JOIN or SQL Query 두번 실행은 JPA로 편하게 할 수 있다.
- 객체 비교
Tutor tutor1 = jpa.find(Tutor.class, tutorId);
Tutor tutor2 = jpa.find(Tutor.class, tutorId);
tutor1 == tutor2; // true
- 단, 전제조건으로 동일한 트랜잭션 필수
4. 성능
- 1차 캐시
- SQL Query 한번만 실행
Tutor tutor1 = jpa.find(Tutor.class, tutorId); // 실행 결과 1차 캐시에 저장
Tutor tutor2 = jpa.find(Tutor.class, tutorId); // 캐시에서 조회
tutor1 == tutor2; // true
- 쓰기 지연
- ORM과 같은 중간 기술이 있으면 한번에 모아서 요청을 보내는것이 가능하다.
- 네트워크 통신이 한번만 발생하여 비용이 감소된다.
// 트랜잭션 시작
transaction.begin();
jpa.persist(company);
jpa.persist(tutor1);
jpa.persist(tutor2);
// 트랜잭션 제출, JDBC BATCH SQL
transaction.commit();
- 지연 로딩, 즉시 로딩
- 지연로딩
- 필요할 때만 조회하기 때문에 통신 비용 감소
- 즉시로딩
- 한번만 조회하기 때문에 네트워크 통신 비용 감소
- 지연로딩
// 지연 로딩
Tutor tutor = tutorRepository.find(tutorId); // SELECT * FROM tutor
Company company = tutor.getCompany();
String companyName = company.getName(); // SELECT * FROM company
// 즉시 로딩
// SELECT t.*, c.* FROM tutor t JOIN Company c ON
Tutor tutor = tutorRepository.find(tutorId);
Company company = tutor.getCompany();
String companyName = company.getName();
✅ hibernate.dialect
Hibernate가 사용하는 데이터베이스 방언(dialect)을 지정하는 설정으로 데이터베이스와 Hibernate가 상호작용할 때 특정 데이터베이스에 맞게 SQL 구문을 자동으로 조정하는 역할

- 방언 : SQL 표준을 지키지 않는 특정 데이터베이스 만의 고유한 기능
ex) 페이징
- MySQL : LIMIT
- Oracle : ROWNUM
- JPA는 특정 데이터베이스에 종속되지 않는다.
- MySQL → Oracle 마음대로 변경이 가능하다.
- 실무에서 사용되는 DB의 Dialect는 대부분 이미 존재
4️⃣ 영속성 컨텍스트
✅ 영속성 컨텍스트
Entity 객체를 영속성 상태로 관리하는 일종의 캐시 역할을 하는 공간
➡ 여기에 저장된 Entity는 데이터베이스와 자동으로 동기화되며 같은 트랜잭션 내에서는 동일한 객체가 유지

- 논리적인 개념
- 눈에 보이지 않는 공간이 생긴다.
- Entity Manager 를 통해서 영속성 컨텍스트에 접근한다.
- EntityManager.persist(entity);
- Entity(객체)를 영속성 컨텍스트에 영속(저장)한다.
✅ Entity
데이터베이스에서 Entity란 저장할 수 있는 데이터의 집합
JPA에서 Entity란 데이터베이스의 테이블을 나타내는 클래스를 의미

Entity 생명주기

- 비영속(new/transient)
- 영속성 컨텍스트가 모르는 새로운 상태
- 데이터베이스와 전혀 연관이 없는 객체
- 영속(managed)
- 영속성 컨텍스트에 저장되고 관리되고 있는 상태
- 데이터베이스와 동기화되는 상태
- 준영속(detached)
- 영속성 컨텍스트에 저장되었다가 분리되어 더 이상 기억하지 않는 상태
- 삭제(removed)
- 영속성 컨텍스트에 의해 삭제로 표시된 상태
- 트랜잭션이 끝나면 데이터베이스에서 제거
➡ 엔티티 상태는 비영속, 영속, 준영속, 삭제 상태로 나뉘며 영속 상태일 때만 JPA의 영속성 컨텍스트가 데이터를 관리
✅ 1차 캐시
엔티티를 영속성 컨텍스트에 저장할 때 생성되는 메모리 내 캐시이다.
➡ 엔티티는 먼저 1차 캐시에 저장되고 이후 같은 엔티티를 요청하면 DB를 조회하지 않고 1차 캐시에서 데이터를 반환하여 성능 ✈

- Database가 아닌 1차 캐시에 저장된 Entity를 먼저 조회한다.
✅ 동일성 보장
동일한 트랜잭션 안에서 특정 엔티티를 여러 번 조회해도 항상 같은 객체 인스턴스를 반환.
영속성 컨텍스트는 1차 캐시를 사용하여 같은 엔티티를 중복 조회해도 동일한 객체를 참조하게 하여 일관성을 유지함.

- 동일한 트랜잭션 내에서 조회된 Entity는 같은 인스턴스를 반환한다.
- DB에 저장된 데이터를 조회하여 1차 캐시에 저장한다.
- 1차 캐시에 저장된 데이터를 조회한다
✅ 쓰기 지연
엔티티 객체의 변경 사항을 DB에 바로 반영하지 않고 트랜잭션이 커밋될 때 한 번에 반영하는 방식으로 이를 통해 성능을 최적화하고 트랜잭션 내에서의 불필요한 DB 쓰기 작업을 최소화
➡ 동일한 트랜잭션 내에서 생성된 SQL들을 Commit 시점에 한꺼번에 반영한다.

- tutor1 1차 캐시에 저장
- 쓰기 지연 저장소에 tutor1 INSERT SQL 저장
- tutor2 1차 캐시에 저장
- 쓰기 지연 저장소에 tutor2 INSERT SQL 저장
- flush 되면서 SQL 쿼리 실행 ( DB 반영되기전에 기다림 )
- 트랜잭션이 Commit 되면서 실제 DB에 반영
✅ 변경 감지
영속성 컨텍스트가 엔티티의 초기 상태를 저장하고 트랜잭션 커밋 시점에 현재 상태와 비교해 변경 사항이 있는지 확인하는 기능
em.remove() 를 통해 Entity를 삭제할 때도 위와 같은 방식으로 동작한다. DELETE SQL이 트랜잭션 Commit 시점에 실행
✅ flush
영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 기능으로, 변경된 엔티티 정보를 SQL로 변환해 데이터베이스에 동기화한다. 트랜잭션 커밋 시 자동으로 실행되지만 특정 시점에 데이터베이스 반영이 필요할 때 수동으로 호출할 수도 있다.
- flush 사용 방법
- 자동 호출
- 트랜잭션이 Commit 되는 시점에 자동으로 호출된다.
- 수동 호출
- em.flush() 를 통해 수동으로 호출할 수 있다.
- 트랜잭션이 Commit되기 전에 SQL이 실행
- 자동 호출
5️⃣ Entity 제작
✅ @Entity
@Entity가 있다면 JPA가 관리하는 Entity로 만들어진다.
@Entity(name = "Tutor") // 기본 값, name 속성은 생략하면 된다.
@Table(name = "tutor")
public class Tutor {
// PK
@Id
private Long id;
// 필드
private String name;
// 기본 생성자
public Tutor() {
}
// 쉽게 사용하기 위해 생성자 추가
public Tutor(Long id, String name) {
this.id = id;
this.name = name;
}
}
@Entity
- JPA를 사용하여 객체를 테이블과 매핑할 때 사용한다.(필수)
- PK 값이 필수이다.(@Id 사용)
- 기본 생성자가 필수이다.
- final, enum, interface, inner 클래스에는 사용 ❌
- 필드에 final 키워드를 사용 ❌
- 속성
- name
- Entity 이름 지정
- 기본 값은 클래스 이름과 같다.
- 혼동을 방지하기 위해 기본 값을 사용(생략)하면 된다.
- name
@Table
- 속성
- name
- Entity와 매핑할 테이블 이름을 지정
- 기본 값은 Entity 이름(Tutor)을 사용
- catalog
- 데이터베이스 catalog 매핑
- schema
- 데이터베이스 schema 매핑
- uniqueConstraints
- DDL 생성 시 유니크 제약 조건 설정
- name

✅ hibernate.hbm2ddl.auto
JPA는 Application 로딩 시점에 DDL을 자동으로 생성하는 기능을 지원한다. 방언(dialect)을 사용하여 Entity Mapping만 하여도 데이터베이스에 맞는 적절한 DDL이 생성된다.
⛔ 자동으로 생성된 DDL은 실제 운영환경이 아닌 개발 환경에서만 사용한다. 필요한 경우에는 자동으로 생성된 DDL을 직접 수정 후 적용
| 값 | 설명 |
| create | 기존 테이블을 삭제(DROP) 후 다시 생성(CREATE) |
| create-drop | DROP 후 CREATE 하고 종료시점에 테이블을 삭제(DROP), 테스트 시 사용 |
| update | 변경된 사항만 DDL에 반영 |
| validate | Entity와 테이블이 정상적으로 매핑 되었는지 확인, 실패 시 예외 발생 |
| none | 속성을 사용하지 않음 |
- application.properties 에서 사용
<property name="hibernate.hbm2ddl.auto" value="create" />
// <property name="hibernate.hbm2ddl.auto" value="create-drop" />
// <property name="hibernate.hbm2ddl.auto" value="update" />
// <property name="hibernate.hbm2ddl.auto" value="validate" />
Validate의 경우
- Entity와 테이블이 정상적으로 매핑 되었는지 확인, 일치하지 않는다면 PersistenceException
실무에서는 validate 혹은 none 을 사용하고 개발 단계에서는 상황에 맞게 사용하면 된다
✅ 제약조건 설정
JPA를 사용하면 DDL 생성 시 제약조건을 설정할 수 있다. 실행 로직과는 별개로 DDL 생성시에만 활용
➡ DDL을 자동으로 생성할 때만 사용되며 Application 로직에는 영향이 없다.
- DDL 자동생성 제약조건 설정
- @Column
- unique : 유니크, 기본값 false
- nullable : 필수 여부, 기본값 true ( false면, Not null )
- length : 길이 ( 문자열만 가능, length=20 이면 VARCHAR(20) )
- @Column
@Column(unique = true, length = 20, nullable = false)
- @Table
- uniqueConstraints : 유니크, 이름을 직접 설정할 수 있다.
✅ 필드 매핑
JPA로 관리되는 클래스인 Entity의 필드는 테이블의 컬럼과 매핑된다.
@Entity
@Table(name = "board")
public class Board {
@Id
private Long id;
// @Column을 사용하지 않아도 자동으로 매핑된다.
private Integer view;
// 객체 필드 이름과 DB 이름을 다르게 설정할 수 있다.
@Column(name = "title")
private String bigTitle;
// DB에는 기본적으로 enum이 없다.
@Enumerated(EnumType.STRING)
private BoardType boardType;
// VARCHAR()를 넘어서는 큰 용량의 문자열을 저장할 수 있다.
@Column(columnDefinition = "longtext")
private String contents;
// 날짜 타입 DATE, TIME, TIMESTAMP를 사용할 수 있다.
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Transient
private int count;
public Board() {
}
}
사용되는 Annotation
| 종류 | 설명 |
| @Column | DB 컬럼 매핑에 사용 |
| @Temporal | 날짜 타입 |
| @Enumerated | enum 타입 |
| @Transient | DB 컬럼과 매핑하지 않을 때 사용 |
| @Lob | tinytext |
@Column 속성
| 속성 | 설명 | Default |
| name | 객체 필드와 매핑할 테이블의 컬럼 이름 | 객체 필드 이름 |
| nullable | DDL 생성 시 null 값의 허용 여부 설정 | true(허용) |
| unique | DDL 생성 시 하나의 컬럼에 유니크 제약조건을 설정 | |
| columnDefinition | DDL 생성 시 데이터베이스 컬럼 정보를 직접 설정할 수 있다. | |
| length | DDL 생성 시 문자 길이 제약조건 설정 단, String만 사용 가능 | 255 |
| insertable | 설정된 컬럼의 INSERT 가능 여부 | true |
| updatable | 설정된 컬럼의 UPDATE 가능 여부 | true |
@Enumerated
| 속성 | 값 | 설명 | Default |
| value | EnumType.ORDINAL ( 잘 사용 ❌ ) EnumType.STRING |
enum 순서 저장 enum 이름 저장 |
EnumType.ORDINAL |
@Temporal
| 속성 | 값 | 설명 | Default |
| value | TemporalType.DATE TemporalType.TIME TemporalType.TIMESTAMP |
DATE TIME TIMESTAMP |
2025-01-01 12:00:00 2025-01-01 12:00:00 |
- MySQL에서 TIMESTAMP는 DATETIME 이다.
📣최신 버전의 Hibernate에서 LocalDate, LocalDateTime는 @Temporal이 생략이 가능하다.
✅ 기본 키 (PK)
JPA Entity를 생성할 때 기본키는 필수로 생성
@Id
- 수동 생성
Tutor tutor = new Tutor(1L, "wonuk");
@GeneratedValue
- 자동 생성
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
⚡ PK 생성 전략
- 영속성 컨텍스트는 PK가 필수이다.
- 가장 권장되는 방식은 Long Type의 기본 키를 사용하는 것
- strategy 속성
- GenerationType
- IDENTITY : MySQL, PostgreSQL에서 사용, 데이터베이스가 PK 자동 생성
- SEQUENCE : Oracle에서 사용, @SequenceGenerator 와 함께 사용
- TABLE : 키 생성용 테이블을 사용, @TableGenerator 와 함께 사용
- AUTO : dialect에 따라 자동 지정, 기본값
- MySQL이면 IDENTITY, Oracle이면 SEQUENCE 로 설정
- GenerationType
6️⃣ 연관관계 Mapping
✅ 단방향
객체 간의 관계가 한쪽에서만 참조될 수 있는 관계.
➡ 설정이 단순하고 유지 관리가 쉬우며 불필요한 데이터 접근을 방지할 수 있다.
[1] DB 중심

- FK 값은 Tutor가 가지고 있다.
- Tutor만 참조할 수 있다.
- N:1, 다대일 연관관계, 가장 많이 사용된다.
- 여러명(N)의 Tutor가 어떤 Company(1)에 소속 되어있는지 설정할 수 있다.
[2] 객체 지향

- 객체는 다른 객체를 참조한다.
- N:1 관계는 @ManyToOne, @JoinColumn을 사용한다.
@Entity
@Table(name = "tutor")
public class Tutor {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// N:1 단방향 연관관계 설정
@ManyToOne
@JoinColumn(name = "company_id")
private Company company;
// 기본 생성자, getter/setter
}
⭐ Tutor의 FK와 Company의 PK를 @JoinColumn으로 매핑한다.
✅ 양방향
객체 간의 관계가 양쪽에서 서로를 참조할 수 있는 관계
➡ 양쪽에서 데이터를 쉽게 접근할 수 있지만 관계를 관리할 때 한쪽에서만 연관관계를 설정하거나 삭제하지 않도록 주의가 필요
테이블에는 변화가 ❌
객체 지향

@Entity
@Table(name = "tutor")
public class Tutor {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// N:1 단방향 연관관계 설정
@ManyToOne
@JoinColumn(name = "company_id")
private Company company;
// 기본 생성자, getter/setter
}
@Entity
@Table(name = "company")
public class Company {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// null을 방지하기 위해 ArrayList로 초기화 한다.(관례)
@OneToMany(mappedBy = "company")
private List<Tutor> tutors = new ArrayList<>();
// 기본 생성자, getter/setter
}
- 양방향 연관관계 설정을 위해 mappedBy 속성을 설정한다.
- Tutor의 company 필드와 매핑된다.
- 반대 방향으로 객체 그래프를 탐색할 수 있다.
✅ 양방향 연관관계의 주인
mappedBy는 JPA 양방향 연관관계 설정 시 사용되는 속성으로 두 엔티티 간의 관계에서 연관관계의 주인이 아닌 쪽에 선언한다. 이를 통해 외래 키 관리 책임을 주인 엔티티에 두고 매핑이 중복되지 않도록 한다.
- 두개의 Entity 중 하나를 연관관계의 주인으로 설정 해야한다.
- 연관관계의 주인은 mappedBy 속성을 사용하지 않는다.
- 연관관계의 주인이 아니라면 mappedBy 속성을 사용한다.
- 연관관계의 주인이 아니라면 조회만 가능하다.
- 연관관계의 주인만 외래 키를 관리(등록, 수정)할 수 있다.
- 연관관계의 주인 선정 기준
- 항상 FK가 있는 곳을 연관관계의 주인으로 지정한다.
- Company가 주인인 경우
- Company를 수정할 때 Tutor를 Update하는 SQL이 실행
- 두번의 SQL이 실행되어야 한다. 혼동되기 쉽다.
👀 새롭게 알게된것
https://jwt.io/ 사이트 에서 쿠키의 Token 내용을 JSON 형태로 디코딩 해서 정보를 알아볼수 있게 할수 있다.
'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글
| TIL 31 [ Spring 일정관리 앱 Lv0 ~ 1, + 미니세션 ( 예외처리 ) ] (0) | 2025.04.02 |
|---|---|
| TIL 30 [ Spring ( Data JPA ), 스탠다드반 ( Optional ) ] (0) | 2025.04.01 |
| Spring ( Session, Token, JWT ) (0) | 2025.03.30 |
| TIL 28 [ Spring ( Bean Validation, Cookie ) ] (0) | 2025.03.28 |
| TIL 27 [ Spring ( SOLID 객체지향, Container와 Bean, 싱글톤 ) + 스탠다드반 (DI / IOC) ] (0) | 2025.03.27 |