본문 바로가기
study

[JSCODE] 상점 API 추가

by 당코 2023. 3. 29.

이번엔 상점 엔티티를 추가하고 관련된 몇 개의 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를 만들어보면서 생각해야 할 것이 많았다.

여러 기능을 하는 클래스들과 메서드를 만들면서 왜 개발자들이 이름 짓기를 어려워하는지 몸소 깨달을 수 있었다. 

자기만의 기준을 새워놓고 그 기준에 따라서 네이밍을 하는 것이 중요할 것 같다.

작은 기능들 하나하나부터 완성도 있게 구현하는 연습이 필요할 것 같다.