@ManyToOne 어노테이션
: 관계 설정 - 여러 개의 Order가 하나의 Product를 참조한다
Order : Product = N : 1
헷갈리니까 Order 클래스에서 작성하였으니까, Many Order to One Product로 해석해보자
fetch = FetchType.LAZY
: 지연 로딩
optional = false
: null 허용 안함
@JoinColumn(name = "product_id", nullable = false) 어노테이션
: FK 설정
nullable = falsepublic Order(Product product) {
this.product = product;
}
new Order(); -> 안됨: 기본 생성자 protected라 못씀new Order(product); -> 가능!private Product product;
List<Order> orders = orderRepository.findAll();select * from orders;: 이걸로 주문 10개를 가져옴 = 이게 1번 쿼리 select * from product where id = 1;
select * from product where id = 2;
select * from product where id = 3;
...-> 주문 수만큼 추가 조회가 나감 = 이게 N번 쿼리LAZY
: Product가 필요할 때까지 조회 미뤄줌
- LAZY 자체가 N+1을 자동 해결하는 건 아니지만, 대신 불필요한 즉시 조회를 막아주고
- 필요할 때는 fetch join 같은 걸로 직접 최적화할 수 있게 해줌
EAGER
: order만 가져오는 게 아니라 연결된 product도 함께 조회하려고 함
- 무조건 조인 한 번으로 가져오는 게 아님 (즉시 로딩 != SQL JOIN 한 번)
-> 즉시 로딩해야 한다는 건 보장하지만, 어떤 SQL로 가져올지를 보장하지 않음
- 어떤 경우엔 join으로 한 번에 가져올 수도 있고
- 어떤 경우엔 먼저 orders로 조회하고
- 그 다음 product를 하나씩 따로 조회할 수도 있음
@RestController 어노테이션
: @Controller + @ResponseBody 합친 것
@RequestMapping 어노테이션
: 기본 URL 설정
@RequiredArgsConstructor 어노테이션
: Lombok
@RequiredArgsConstructor와 @NoArgsConstructor의 차이점
- @RequiredArgsConstructor: 필수 값만 받는 생성자
@RequiredArgsConstructor public class OrderService { private final ProductRepository productRepository; private final OrderRepository orderRepository; }
- @RequiredArgsConstructor를 사용하면
public OrderService(ProductRepository productRepository, OrderRepository orderRepository) { this.productRepository = productRepository; this.orderRepository = orderRepository; }
- 위 코드가 자동 생성됨
- @NoArgsConstructor: 아무 값도 안 받는 기본 생성자
@NoArgsConstructor public class Order { private Long id; private String name; }
- @NoArgsConsturctor를 사용하면
public Order() { }
- 위 코드가 자동 생성됨
@PostMapping 어노테이션
: HTTP POST 요청 처리
public ResponseEntity<ProductResponse> createProduct(@Valid @RequestBody ProductRequest request)
@RequestBody 어노테이션
: 요청 Body(JSON)를 객체로 변환
@Valid 어노테이션
ex) DTO에 @NotBlank 같은 게 있으면, 값이 없으면 자동으로 에러 발생
반환 타입 ResponseEntity<ProductResponse>
: HTTP 응답 전체를 컨트롤
ResponseEntity: Spring Framework에서 제공하는 클래스핵심 로직 ProductResponse response = productService.createProduct(request);
반환 값 return ResponseEntity.created(URI.create("/api/products/" + response.getId())).body(response);
ResponseEntity.created(...): HTTP 201 Created 상태코드로 응답한다는 의미URI.create("/api/products/" + response.getId()) -> 예를 들어 id가 10이면, `Location: /api/products/10' -> 이 리소스 여기서 확인 가능해요! 라는 의미.body(response): 실제 응답 데이터 넣는 부분 ex) 상태코드(201), 헤더(Location), 바디(response(JSON))HTTP/1.1 201 Created
Location: /api/products/10
Content-Type: application/json
{
"id": 10,
"name": "콜라",
"price": 2000
}
-> 실행하면 이런 식으로 HTTP 응답이 구성됨
반환 타입 ResponseEntity<void>
ResponseEntity.noContent().build()
: HTTP 204 No Content
HTTP/1.1 204 No Content
-> body 없음 (진짜 텅 비어있음)
JpaRepository<엔티티(Product), 타입(Long)>productRepository.save(product);, productRepository.findById(1L);List<Product> findByName(String name); -> 메서드 이름만 같은 형식으로 지으면, 메서드 이름을 보고 자동으로 쿼리가 생성되기 때문에 구현할 필요도 없음