Web開発の春、Spring 4週目


2022年4月7日(木)
[スパルタエンコーディングクラブ]Web開発の春、Spring第2週レッスン-2

◎NAVER购物检索API

  • APIの使用を申請する
  • APIの使用:
  • の検索
  • 非登録オープンAPIサービス環境:WEB
    http://localhost登録//後でAWSを使用するときにドメイン名を追加します.
  • 検索
  • ARC
  • X-Naver-Client-id、X-Naver-Client-secret作成したばかりの「マイアプリケーション」で
  • を確認
  • 要求URL 該当ページ JSON形式アドレス
  • を使用
  • ?query=[検索する内容]を
  • に入れる
  • 後で追加できる変数は、ログインページ
  • を参照してください.
  • Javaで使用
  • ARCでJavaコードをチェック
  • のコード
  • を使用
    public class NaverShopSearch {
        public String search() {
        
            RestTemplate rest = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.add("X-Naver-Client-Id", "발급받은 Client ID");
            headers.add("X-Naver-Client-Secret", "발급받은 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=adidas", HttpMethod.GET, requestEntity, String.class);
            HttpStatus httpStatus = responseEntity.getStatusCode();
            int status = httpStatus.value();
            String response = responseEntity.getBody();
            System.out.println("Response status: " + status); // 200 : 정상작동
            System.out.println(response); // 넘어온 데이터 확인
    
            return response;
        }
    
    	// main 함수는 실행 가능
        public static void main(String[] args) {
            NaverShopSearch naverShopSearch = new NaverShopSearch();
            naverShopSearch.search(); // 위의 search 함수 실행 가능
        }
    }

    ◇設計3階(コントローラ/サービス/レポート)

  • Controller
  • ProductRestControl:対象コントローラ
  • SearchRequestControl:関連コントローラの検索
  • Service
  • 製品サービス:興味のある商品価格
  • を変更
  • Repository
  • 製品:興味ある製品表
  • 製品レポート:興味のある製品を表示し、
  • を保存
  • ProductRequestDto:興味のある商品を登録
  • ProductMypriceRequestDto:興味価格の変更
  • ItemDto:検索結果を交換
  • Repository//com.sparta.week04.models
  • // Timestamped.js
    @Getter // get 함수를 자동 생성합니다.
    @MappedSuperclass // 멤버 변수가 컬럼이 되도록 합니다.
    @EntityListeners(AuditingEntityListener.class) // 변경되었을 때 자동으로 기록합니다.
    public abstract class Timestamped {
        @CreatedDate // 최초 생성 시점
        private LocalDateTime createdAt;
    
        @LastModifiedDate // 마지막 변경 시점
        private LocalDateTime modifiedAt;
    }
    // Product.js
    @Getter // get 함수를 일괄적으로 만들어줍니다.
    @NoArgsConstructor // 기본 생성자를 만들어줍니다.
    @Entity // DB 테이블 역할을 합니다.
    public class Product extends Timestamped{
    
        // ID가 자동으로 생성 및 증가합니다.
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Id // 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;
    	
        // productRequestDto 를 통해 Product 생성
        public Product(ProductRequestDto requestDto) {
            this.title = requestDto.getTitle();
            this.link = requestDto.getLink();
            this.image = requestDto.getImage();
            this.lprice = requestDto.getLprice();
            this.myprice = 0;
        }
    
    	// 
        public void updateByItemDto(ItemDto itemDto) {
            this.lprice = itemDto.getLprice();
        }
    	
        // myprice update시 사용
        public void update(ProductMypriceRequestDto mypriceRequestDto){
            this.myprice = mypriceRequestDto.getMyprice();
        }
    }
    // ProductRepository.js : create, get, delete 시 이용
    // 사용내장함수 .findAll() .save() .deleteById()
    public interface ProductRepository extends JpaRepository<Product, Long> {
    }
    // ProductMypriceRequestDtp.js // myprice 데이터 들고 다니는 class
    // product.update() 함수 인자로 사용됨
    // 
    @Getter
    public class ProductMypriceRequestDto {
        private int myprice;
    }
    // ProducrRequestDto.js // Database에 저장할 데이터 들고 다니는 class
    @Getter
    public class ProductRequestDto {
        private String title;
        private String link;
        private String image;
        private int lprice;
    }
  • Service//com.sparta.week04.service
  • @RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
    @Service // 서비스임을 선언합니다.
    public class ProductService {
    
        private final ProductRepository productRepository;
    	
        //  myprice 데이터 수정시 내장 함수로 사용
        @Transactional // 메소드 동작이 SQL 쿼리문임을 선언합니다.
        public Long update(Long id, ProductMypriceRequestDto requestDto) {
            Product product = productRepository.findById(id).orElseThrow(
                    () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
            );
            product.update(requestDto);
            return id;
        }
    	
        // 
        @Transactional // 메소드 동작이 SQL 쿼리문임을 선언합니다.
        public Long updateBySearch(Long id, ItemDto itemDto) {
            Product product = productRepository.findById(id).orElseThrow(
                    () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
            );
            product.updateByItemDto(itemDto);
            return id;
        }
    }
  • Controller//com.sparta.week04.controller
  • //ProductRestController.js
    @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);
        }
    
    	// myprice 데이터 수정
        @PutMapping("/api/products/{id}")
        public Long updateMyprice(@PathVariable Long id, @RequestBody ProductMypriceRequestDto mypriceRequestDto){
            productService.update(id, mypriceRequestDto);
            return id;
        }
    }
    // SearchRestController.js
    @RequiredArgsConstructor // final 로 선언된 클래스를 자동으로 생성합니다.
    @RestController // JSON으로 응답함을 선언합니다.
    public class SearchRequestController {
    
        private final NaverShopSearch naverShopSearch;
    	
        // 네이버 쇼핑 API에서 데이터를 받아옴
        @GetMapping("/api/search")
        public List<ItemDto> getItems(@RequestParam String query) {
            String resultString = naverShopSearch.search(query);
            return naverShopSearch.fromJSONtoItems(resultString);
        }
    }

    ◇NAVERショッピング検索APIを適用


    インストール
  • JSON関連ライブラリ
  • GoogleでMaven Central検索後、
  • をクリックします.
  • 検索ウィンドウにjsonを入力し、
  • を入力します.
  • JSON in Javaクリック
  • 最高バージョン
  • をクリック
  • Gradleタブ
  • をクリックします.
  • のコンテンツをコピーして構築します.gradle>依存項目に貼り付ける
  • 依存項目の隣にあるRunボタン
  • をクリックします.
  • 導入完了!
  • Javaファイルで
  • を使用
    // com.sparta.week04.controller  SearchRequestController.js
    
    @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);
        }
    }
    // com.sparta.week04.utils  NaverShopSearch.js
    @Component
    public class NaverShopSearch {
    	
        // 네이버 쇼핑 API에서 정보 받아서 response로 내보내줌
        // 
        public String search(String query) {
            RestTemplate rest = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.add("X-Naver-Client-Id", "eoFux1Z5Y830gAqw7LQs");
            headers.add("X-Naver-Client-Secret", "IzvVISSZUx");
            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);
    
            return response;
        }
    
        public List<ItemDto> fromJSONtoItems(String result) {
            JSONObject rjson = new JSONObject(result);
            JSONArray items = rjson.getJSONArray("items");
    
            List<ItemDto> itemDtoList = new ArrayList<ItemDto>();
            for (int i=0; i<items.length(); i++) {
                JSONObject itemJson = items.getJSONObject(i);
                ItemDto itemDto = new ItemDto(itemJson);
                itemDtoList.add(itemDto);
            }
            return itemDtoList;
        }
    }
    // com.sparta.week04.models  ItemDto.js
    // 네이버 쇼핑 API에서 받아온 JSON 데이터를 담는 Dto
    @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");
        }
    }

    ◇スケジューラの作成


  • 特定の時間に繰り返し実行を許可

  • 適用
  • com.sparta.week04.utils  Scheduler.js
    
    @RequiredArgsConstructor // final 멤버 변수를 자동으로 생성합니다.
    @Component // 스프링이 필요 시 자동으로 생성하는 클래스 목록에 추가합니다.
    public class Scheduler {
    
        private final ProductRepository productRepository;
        private final ProductService productService;
        private final NaverShopSearch naverShopSearch;
    
        // 초, 분, 시, 일, 월, 주 순서  / 매일 새벽 1시에 실행
        @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);
                // 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);
            }
        }
    }
    // com.sparta.week04.service  ProductService.js의 일부
    
    @RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
    @Service // 서비스임을 선언합니다.
    public class ProductService {
    
        private final ProductRepository productRepository;
    
        @Transactional // 메소드 동작이 SQL 쿼리문임을 선언합니다.
        public Long updateBySearch(Long id, ItemDto itemDto) {
            Product product = productRepository.findById(id).orElseThrow(
                    () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
            );
            product.updateByItemDto(itemDto);
            return id;
        }
    }
    // Week04Application.js
    
    @EnableScheduling // 스프링 부트에서 스케줄러가 작동하게 합니다.
    @EnableJpaAuditing // 시간 자동 변경이 가능하도록 합니다.
    @SpringBootApplication // 스프링 부트임을 선언합니다.
    public class Week04Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Week04Application.class, args);
        }
    
    }