연관관계
회원과 상품의 관계를 생각해보자.
회원 1명 → 상품 여러 개 등록 가능
상품 1개 → 회원 1명에게 속함
이런 관계를 DB에서는 외래키(Foreign Key)로 표현하고, JPA에서는 @ManyToOne, @OneToMany 로 표현한다.
Member (1) ←→ Item (N)
Item 테이블에 member_id 컬럼이 생김 (외래키)
상품 엔티티 만들기
com.seongmo.myshop 하위에 item 패키지를 만들고 아래 파일들을 생성하자.
com.seongmo.myshop
├── member
└── item
├── Item.java
├── ItemStatus.java
├── ItemController.java
├── ItemService.java
├── ItemRepository.java
└── dto
├── ItemCreateRequest.java
└── ItemResponse.java
ItemStatus.java 만들기
package com.seongmo.myshop.item;
public enum ItemStatus {
SELLING, // 판매중
RESERVED, // 예약중
SOLD_OUT // 판매완료
}
상품 상태를 enum으로 관리한다.
Item.java 만들기
package com.seongmo.myshop.item;
import com.seongmo.myshop.member.Member;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "item")
@Getter
@NoArgsConstructor
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String description;
@Column(nullable = false)
private int price;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private ItemStatus status;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member seller;
public Item(String title, String description, int price, Member seller) {
this.title = title;
this.description = description;
this.price = price;
this.status = ItemStatus.SELLING;
this.seller = seller;
}
}
- @ManyToONe 은 "상품 여러 개가 회원 한 명에게 속한다"는 관계를 선언한다.
- fetch = FetchType.LAZY 는 상품을 조회할 때 회원 정보를 즉시 가져오지 않고 실제로 필요할 때만 가져오도록 한다. 연관관계에서는 기본적으로 LAZY 를 사용하는 것이 권장된다.
- @JoinColumn(name = "member_id") 는 DB에 생성될 외래키 컬럼 이름을 지정한다.
- @Enumerated(EnumType.STRING) 은 enum 값을 DB에 문자열로 저장한다. 숫자(0,1,2)로 지정하면 나중에 enum 순서가 바뀔 때 데이터가 꼬일 수 있어서 STRING을 사용한다.
ItemCreateRequest.java 만들기
package com.seongmo.myshop.item.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class ItemCreateRequest {
private String title;
private String description;
private int price;
private Long memberId;
}
ItemResponse.java 만들기
package com.seongmo.myshop.item.dto;
import com.seongmo.myshop.item.Item;
import lombok.Getter;
@Getter
public class ItemResponse {
private Long id;
private String title;
private String description;
private int price;
private String status;
private String sellerNickname;
public ItemResponse(Item item) {
this.id = item.getId();
this.title = item.getTitle();
this.description = item.getDescription();
this.price = item.getPrice();
this.status = item.getStatus().name();
this.sellerNickname = item.getSeller().getNickname();
}
}
ItemRepository.java 만들기
package com.seongmo.myshop.item;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ItemRepository extends JpaRepository<Item, Long> {
List<Item> findBySellerId(Long sellerId);
}
- findBySellerId 는 특정 회원이 등록한 상품 목록을 조회하는 메서드다. JPA가 메서드 이름을 보고 자동으로 쿼리를 만들어준다.
ItemService.java 만들기
package com.seongmo.myshop.item;
import com.seongmo.myshop.item.dto.ItemCreateRequest;
import com.seongmo.myshop.item.dto.ItemResponse;
import com.seongmo.myshop.member.Member;
import com.seongmo.myshop.member.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
private final MemberRepository memberRepository;
@Transactional
public ItemResponse createItem(ItemCreateRequest request) {
Member seller = memberRepository.findById(request.getMemberId())
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다."));
Item item = new Item(
request.getTitle(),
request.getDescription(),
request.getPrice(),
seller
);
Item savedItem = itemRepository.save(item);
return new ItemResponse(savedItem);
}
public ItemResponse getItem(Long id) {
Item item = itemRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 상품입니다."));
return new ItemResponse(item);
}
public List<ItemResponse> getItemsBySeller(Long memberId) {
return itemRepository.findBySellerId(memberId)
.stream()
.map(ItemResponse::new)
.collect(Collectors.toList());
}
}
ItemController.java 만들기
package com.seongmo.myshop.item;
import com.seongmo.myshop.item.dto.ItemCreateRequest;
import com.seongmo.myshop.item.dto.ItemResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/items")
public class ItemController {
private final ItemService itemService;
@PostMapping
public ResponseEntity<ItemResponse> createItem(@RequestBody ItemCreateRequest request) {
return ResponseEntity.ok(itemService.createItem(request));
}
@GetMapping("/{id}")
public ResponseEntity<ItemResponse> getItem(@PathVariable Long id) {
return ResponseEntity.ok(itemService.getItem(id));
}
@GetMapping("/seller/{memberId}")
public ResponseEntity<List<ItemResponse>> getItemsBySeller(@PathVariable Long memberId) {
return ResponseEntity.ok(itemService.getItemsBySeller(memberId));
}
}
실행 및 확인
Intellij에서 실행해보면 item 테이블이 자동으로 생성되는 것을 확인할 수 있다.
Postman으로 상품 등록 요청을 해보자.

또한, DBeaver에서도 item 테이블이 잘 생성된 것을 확인할 수 있다.

특정 회원의 전체 판매 상품을 조회할 수도 있다

'Backend > Spring' 카테고리의 다른 글
| [Backend/Spring] Spring Boot - 예외 처리 (0) | 2026.03.29 |
|---|---|
| [Backend/Spring] Spring Boot - @OneToMany 와 N+1 문제 (0) | 2026.03.29 |
| [Backend/Spring] Spring Boot - JPA와 MySQP 연동 (0) | 2026.03.29 |
| [Backend/Spring] Spring Boot - MVC 계층 구조와 API (0) | 2026.03.29 |
| [Backend/Spring] Spring Boot - IoC/DI/Bean 개념과 MySQL 연결 (0) | 2026.03.29 |