第4週


[学習目標]
1.Controller-ServiceRepositoryについて
2.JavaによるAPIの実装方法
3.スプリングスケジューラを使用して、サーバが必要な時間にタスクを実行できるようにする

開始前


開発の中核


分業+緩やかな結合
  • 分業:それぞれの責任を果たし、機能は正常
  • ばらばら結合:ばらばら結合、柔軟性と拡張性
  • Spring 3層

  • 3層(コントローラ、サービス、およびレポート)は、分業および分散結合の典型的な例
  • である.
  • スプリングを回転させる脊椎作用
  • 各階層間では決してEntityを直接使用するのではなく、DTO
  • を作成して使用します.

    Controller


    一番外側からリクエストを受信し、応答するロールを返します.

    Service


    そこから具体的な操作手順を決める

    Repository


    DBと直接コミュニケーションして資料を作成、表示、変更、削除する

    API Handling

  • 分散結合の典型的な例
  • 私たちが使用するNAVER検索APIの内部ロジックが何であるかにかかわらず、約束の要求に従えば、私たちは規定の結果を得ることができます.
  • JSONは、JAVAにデータを要求し、その結果を処理する方法を知る必要があります.
  • NAVERショッピングAPI


    Naver Shopping API説明ドキュメント
    {
      "title": "<b>아디다스</b> 알파바운스 BB슬라이드 BA8775",
      "link": "https://search.shopping.naver.com/gate.nhn?id=24457175865",
      "image": "https://shopping-phinf.pstatic.net/main_2445717/24457175865.20201014195220.jpg",
      "lprice": "27990",
      "hprice": "",
      "mallName": "네이버",
      "productId": "24457175865",
      "productType": "1",
      "brand": "아디다스",
      "maker": "아디다스",
      "category1": "패션잡화",
      "category2": "남성신발",
      "category3": "슬리퍼",
      "category4": ""
    },

    JAVA利用NAVERショッピングAPI利用

    RestTemplate rest = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.add("X-Naver-Client-Id", "**********");
    headers.add("X-Naver-Client-Secret", "**********");
    String body = "";
    
    HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
    ResponseEntity<String> responseEntity = rest.exchange("https://openapi.naver.com/v1/search/shop.json?query=아디다스", HttpMethod.GET, requestEntity, String.class);
    HttpStatus httpStatus = responseEntity.getStatusCode();
    int status = httpStatus.value();
    String response = responseEntity.getBody();
    System.out.println("Response status: " + status);
    System.out.println(response);
    

    IntelliJの設定



    src > main > java > com.sparta.week04 > utils

    API設計


    キーワードを使用して商品を検索し、結果をリストに表示
    GET/api/search?Query=クエリー
    return List<ItemDto>興味のある商品を登録する
    POST/api/products
    興味のある商品を調べる
    GET/api/products
    興味のある商品に興味のある価格を登録し、その価格を下回った場合に表示
    PUT/api/products/{id}

    設計3階層


  • Controller
  • ProductRestControl:興味のあるコントローラ
  • SearchRequestControl:関連コントローラの検索

  • Service
  • ProductService:興味のある商品価格の変更

  • Repository
    ここでデータベースに格納されているのはProductだけです!
  • Product:興味のある商品表
  • ProductRepository:興味のある商品を表示・記憶
  • ProductRequestDto:興味のある商品を登録
  • ProductMypriceRequestDto:興味価格の変更
  • ItemDto:検索結果の交換
  • org.jsonJavaヘルプjsonのライブラリ

    Models

    com > sparta > week04 > models1)各モデル、レポート、タイムスタンプ
    Product.java
    商品表の場合、updateにはupdateがあり、ユーザーが指定した最低価格を関数とスケジューラの価格に変更します.
    package com.sparta.week04.models;
    
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    import javax.persistence.*;
    
    @Getter // get 함수를 일괄적으로 만들어줍니다.
    @NoArgsConstructor // 기본 생성자를 만들어줍니다.
    @Entity // DB 테이블 역할을 합니다.
    public class Product extends Timestamped {
    
        // ID가 자동으로 생성 및 증가합니다.
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Id
        private Long id;
    
        // 반드시 값을 가지도록 합니다.
        @Column(nullable = false)
        private String title;
    
        @Column(nullable = false)
        private String image;
    
        @Column(nullable = false)
        private String link;
    
        @Column(nullable = false)
        private int lprice;
    
        @Column(nullable = false)
        private int myprice;
    
        public Product(ProductRequestDto requestDto) {
            this.title = requestDto.getTitle();
            this.link = requestDto.getLink();
            this.lprice = requestDto.getLprice();
            this.image = requestDto.getImage();
            this.myprice = 0;
        }
    
        public void update(ProductMypriceRequestDto requestDto) {
            this.myprice = requestDto.getMyprice();
        }
    
        public void updateByItemDto(ItemDto itemDto) {
            this.lprice = itemDto.getLprice();
        }
    }
    
    Timestamped
    変更日を作成するclass
    package com.sparta.week04.models;
    
    @Getter 
    // get 함수를 자동 생성합니다.
    @MappedSuperclass 
    // 멤버 변수가 컬럼이 되도록 합니다.
    @EntityListeners(AuditingEntityListener.class) 
    // 변경되었을 때 자동으로 기록합니다.
    
    public abstract class Timestamped {
        @CreatedDate // 최초 생성 시점
        private LocalDateTime createdAt;
    
        @LastModifiedDate // 마지막 변경 시점
        private LocalDateTime modifiedAt;
    }
    
    ProductRepository.java
    package com.sparta.week04.models;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface ProductRepository extends JpaRepository<Product, Long> {
    }

    Dtoの作成

    updateByItemDtoProductRequestDto.java
    package com.sparta.week04.models;
    
    import lombok.Getter;
    
    @Getter
    public class ProductRequestDto {
        private String title;
        private String link;
        private String image;
        private int lprice;
    }
    
    ItemDto.java
    package com.sparta.week04.models;
    
    import lombok.Getter;
    import org.json.JSONObject;
    
    @Getter
    public class ItemDto {
        private String title;
        private String link;
        private String image;
        private int lprice;
    
        public ItemDto(JSONObject itemJson) {
            this.title = itemJson.getString("title");
            this.link = itemJson.getString("link");
            this.image = itemJson.getString("image");
            this.lprice = itemJson.getInt("lprice");
        }
    }
    
    ProductMypriceDto.java
    package com.sparta.week04.models;
    
    import lombok.Getter;
    
    @Getter
    public class ProductMypriceRequestDto {
        private int myprice;
    }

    Service

    com > sparta > week04 > modelsProductService.java
    package com.sparta.week04.service;
    
    ...
    
    import javax.transaction.Transactional;
    
    @RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
    @Service // 서비스임을 선언합니다.
    public class ProductService {
    
        private final ProductRepository productRepository;
    
        @Transactional // 메소드 동작이 SQL 쿼리문임을 선언합니다.
        public Long update(Long id, ProductMypriceRequestDto requestDto) {
            Product product = productRepository.findById(id).orElseThrow(
                    () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
            );
            product.update(requestDto);
            return id;
        }
        @Transactional // DB 가 업데이트 되어야한다.
        public Long updateBySearch(Long id, ItemDto itemDto) {
            Product product = productRepository.findById(id).orElseThrow(
                    () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
            );
            product.updateByItemDto(itemDto);
            return id;
        }
    }

    Utils

    com > sparta > week04 > serviceNaverShopSearch.java
    NAVER APIを使用して値をロードするクラス
    構成部品の登録
    簡単に言えばspring権限を与える!必要なときは自分で見て使いましょう.いわゆる
    スプリングが構成部品を自動的にインポートおよび使用できるようにします.
    RepositoryまたはServiceはコンポーネントが登録されているため、スプリングは自由に使用できます
    スプリングには、任意にインポートおよび書き込みできるクラスのリストが表示されます.
    リスト内のそれらは構成部品として登録されています.
    コントローラ、サービス、エンティティなどがコンポーネントとして登録されています
    NaverShopSearchは手動で登録する必要があります.
    itemDto形式はJSON、com > sparta > week04 > utilsカッコに文字列を記入する
    JSONが正しいとエラーになります.
    だからJSONじゃなくString形式

    org.jsonパッケージのインストール


    JavaでJSONを扱うためにはJSOnObjectとJSOnArrayクラスが必要です.インポート
    1)Maven中央検索
    2)検索json
    3)JSON In Javaクリック
    4)数値の最上位バージョンをクリック
    5)Gradleタブをクリック
    6)コンテンツをコピーして構築する.gradle>依存項目に貼り付け
    7)依存項目の横にあるrunボタンをクリック
    =>import完了!
    package com.sparta.week04.utils;
    
    ...
    
    @Component
    public class NaverShopSearch {
        public String search(String query) {
            RestTemplate rest = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.add("X-Naver-Client-Id", "p234pvd2_wQpIjp6KlKi");
            headers.add("X-Naver-Client-Secret", "bsVXyLy17e");
            String body = "";
    
            HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
            ResponseEntity<String> responseEntity = rest.exchange("https://openapi.naver.com/v1/search/shop.json?query=" +  query, HttpMethod.GET, requestEntity, String.class);
            HttpStatus httpStatus = responseEntity.getStatusCode();
            int status = httpStatus.value(); // 응답 상태 코드
            String response = responseEntity.getBody();
            System.out.println("Response status: " + status);
            System.out.println(response);
    
            return response;
        }
    
        public List<ItemDto> fromJSONtoItems(String result) {
            JSONObject rjson = new JSONObject(result);
            JSONArray items = rjson.getJSONArray("items");
            List<ItemDto> itemDtoList = new ArrayList<>();
    
            // JSONArray 에서는 length 로 꺼냄
            for (int i = 0; i < items.length(); i++) {
                JSONObject itemJson = (JSONObject) items.get(i);
                // JSONObject itemJson = items.getJSONObject(i);
                ItemDto itemDto = new ItemDto(itemJson);
                itemDtoList.add(itemDto);
            }
            return itemDtoList;
        }
    }
    Scheduler.java
    スケジューラを通じて毎日午前1時に新しい価格を提出します.
    package com.sparta.week04.utils;
    
    ...
    
    @RequiredArgsConstructor // final 멤버 변수를 자동으로 생성합니다.
    @Component // 스프링이 필요 시 자동으로 생성하는 클래스 목록에 추가합니다.
    public class Scheduler {
    
        private final ProductRepository productRepository;
        private final ProductService productService;
        private final NaverShopSearch naverShopSearch;
    
        // 초, 분, 시, 일, 월, 주 순서
        // cron : 시간이 맞을때 작동울 해라
        // 0~23시까지 가능 1시  0분 1초부터 1시 59분 59초까지 매초 실행 * * 1 * * *
        // 1시 0분 0초일 때 실행
        @Scheduled(cron = "0 0 1 * * *")
        public void updatePrice() throws InterruptedException { // 만약에 오류가 발생하면, 방해하는 요소가 발생했다고 오류를 보여줘라
            System.out.println("가격 업데이트 실행");
            // 저장된 모든 관심상품을 조회합니다.
            List<Product> productList = productRepository.findAll();
            for (int i=0; i<productList.size(); i++) {
                // 1초에 한 상품 씩 조회합니다 (Naver 제한: 요청이 너무 자주 오면 네이버에서 막아버림)
                TimeUnit.SECONDS.sleep(1); // 타임 단위 기준으로 초마다 한번씩 잠깐 쉬어라. = 1초에 한번 씩 for 문이 돌게 된다.
                // i 번째 관심 상품을 꺼냅니다.
                Product p = productList.get(i);
                // i 번째 관심 상품의 제목으로 검색을 실행합니다.
                String title = p.getTitle();
                String resultString = naverShopSearch.search(title);
                // i 번째 관심 상품의 검색 결과 목록 중에서 첫 번째 결과를 꺼냅니다.
                List<ItemDto> itemDtoList = naverShopSearch.fromJSONtoItems(resultString);
                ItemDto itemDto = itemDtoList.get(0);
                // i 번째 관심 상품 정보를 업데이트합니다.
                Long id = p.getId();
                productService.updateBySearch(id, itemDto);
            }
        }
    }

    Controller


    ProductRestController.java
    package com.sparta.week04.controller;
    
    ...
    
    @RequiredArgsConstructor // final 로 선언된 멤버 변수를 자동으로 생성합니다.
    @RestController // JSON 으로 데이터를 주고받음을 선언합니다.
    public class ProductRestController {
    
        private final ProductRepository productRepository;
        private final ProductService productService;
    
        // 등록된 전체 상품 목록 조회
        @GetMapping("/api/products")
        public List<Product> getProducts() {
            return productRepository.findAll();
        }
    
        @PostMapping("/api/products")
        public Product createProduct(@RequestBody ProductRequestDto requestDto) {
            Product product = new Product(requestDto);
            return productRepository.save(product);
        }
    
        // 최저가 변경 API
        @PutMapping("/api/product/{id}")
        public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) {
            return productService.update(id, requestDto);
        }
    }
    
    SearchRequestController.java
    NAVER API値を入力するAPI
    package com.sparta.week04.controller;
    
    ...
    
    @RequiredArgsConstructor // final 로 선언된 클래스를 자동으로 생성합니다.
    @RestController // JSON 으로 응답함을 선언합니다.
    public class SearchRequestController {
    
        private final NaverShopSearch naverShopSearch;
    
        @GetMapping("/api/search")
        public List<ItemDto> getItems(@RequestParam String query) {
            String resultString = naverShopSearch.search(query);
            return naverShopSearch.fromJSONtoItems(resultString);
        }
    }