이번엔 상점 엔티티를 추가하고 관련된 몇 개의 api를 만들어 보았다.
Store 엔티티
@Entity
@Getter
public class Store {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
private Address address;
private String phoneNumber;
@OneToMany(mappedBy = "store")
private List<ProductEntity> products = new ArrayList<>();
// 연관관계 메서드
public void addProduct(ProductEntity product) {
products.add(product);
product.setStore(this);
}
//생성 메서드
public static Store createStore(String name, Address address,
String phoneNumber, ProductEntity... products) {
Store store = new Store();
store.name = name;
store.address = address;
store.phoneNumber = phoneNumber;
for (ProductEntity product : products) {
store.addProduct(product);
}
return store;
}
}
상점은 Id, Name, Address, PhoneNumber, Products를 가지고 있다.
Product와 Store은 다대일 관계가 있어 양방향 연관관계를 가지도록 하였다.
따라서 Store에 product를 추가하는 연관관계 메서드 addProduct()를 만들어 편하게 관리할 수 있게 하였다.
생성 메서드를 만들어 생성 시 사용할 수 있게 만들었다.
Controller
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/store")
public class StoreController {
private final StoreService storeService;
@PostMapping
public StoreCreateResponse storeAdd(@RequestBody StoreCreateRequest request) {
Long id = storeService.save(request);
return new StoreCreateResponse(id);
}
@PostMapping("/{id}")
public Long productAdd(@PathVariable Long id, @RequestBody ProductIdDto product) {
storeService.addProduct(id, product);
StoreInfoResponse findStore = storeService.findOne(id);
return findStore.getId();
}
@GetMapping
public Result<StoreInfoResponse> storeList() {
return new Result(storeService.findOrders());
}
@GetMapping(params = "id")
public StoreInfoResponse storeGet(@RequestParam Long id) {
return storeService.findOne(id);
}
}
컨트롤러를 만들 때 고려해 본 것은 엔티티 그 자체를 외부로 노출시키지 않고 DTO로 변환시키는 것이었다.
API의 기능마다 응답하는 정보의 종류가 다르다 보니 DTO를 각각 만들어 응답했다.
그리고 Json으로 응답을 보낼 때 리스트를 그대로 보내게 되면 유연성이 떨어지기 때문에 Result라는 껍데기를 씌워서 응답을 보내주도록 하였다.
Service
@Service
@RequiredArgsConstructor
@Transactional
public class StoreService {
private final ProductJpaRepository productRepository;
private final StoreRepository storeRepository;
public Long save(StoreCreateRequest request) {
Store store = Store.createStore(request.getName(), request.getAddress(), request.getPhoneNumber());
Store save = storeRepository.save(store);
return store.getId();
}
public StoreInfoResponse findOne(Long id) {
Optional<Store> store = storeRepository.findById(id);
return new StoreInfoResponse(store.get());
}
public void addProduct(Long storeId, ProductIdDto product) {
Optional<Store> store = storeRepository.findById(storeId);
Optional<ProductEntity> findProduct = productRepository.findById(product.getId());
store.get().addProduct(findProduct.get());
}
public List<StoreInfoResponse> findOrders() {
List<Store> orders = storeRepository.findAll();
return orders.stream()
.map(o -> new StoreInfoResponse(o))
.collect(Collectors.toList());
}
}
service에서는 repository에서 찾아온 엔티티를 DTO로 변환시켜서 컨트롤러로 전달해 주었다.
Repository
public interface StoreRepository extends JpaRepository<Store, Long> {
}
간단한 등록, 조회 api를 만들어보면서 생각해야 할 것이 많았다.
여러 기능을 하는 클래스들과 메서드를 만들면서 왜 개발자들이 이름 짓기를 어려워하는지 몸소 깨달을 수 있었다.
자기만의 기준을 새워놓고 그 기준에 따라서 네이밍을 하는 것이 중요할 것 같다.
작은 기능들 하나하나부터 완성도 있게 구현하는 연습이 필요할 것 같다.
'study' 카테고리의 다른 글
[JSCODE] 익명 게시판 서비스 개발 시작 (0) | 2023.05.11 |
---|---|
[JSCODE] 스프링 스터디 회고 (0) | 2023.03.31 |
[JSCODE] Spring Data JPA (0) | 2023.03.24 |
[JSCODE] Spring Data JPA 세팅하기 (0) | 2023.03.21 |
[JSCODE] controller, service, repository 구분하기 (0) | 2023.03.18 |