웹개발의 봄, Spring 4주차

귀찮Lee·2022년 4월 7일
0
post-custom-banner

2022년 4월 7일(목)
[스파르타코딩클럽] 웹개발의 봄, Spring 2주차 과정 - 2

네이버 쇼핑 검색 API

  • ARC로 검색하기
    • X-Naver-Client-id, X-Naver-Client-Secret 은 방금전에 만들었던 "내 어플리케이션"에서 확인
    • RequestURL 은 해당 페이지 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 / Service / Repository)

  1. Controller
    • ProductRestController: 관심 상품 관련 컨트롤러
    • SearchRequestController: 검색 관련 컨트롤러
  2. Service
    • ProductService: 관심 상품 가격 변경
  3. Repository
    • Product: 관심 상품 테이블
    • ProductRepository: 관심 상품 조회, 저장
    • 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);
    }
}

◎ 네이버 쇼핑 검색 API 적용

  • JSON 관련 라이브러리 설치
  1. 구글에 maven central 검색 후 첫 번째 결과 클릭
  2. 검색창에 json 입력 후 엔터
  3. JSON In Java 클릭
  4. 숫자 가장 높은 버전 클릭
  5. Gradle 탭 클릭
  6. 내용 복사하여 build.gradle > dependencies 안에 붙여넣기
  7. dependencies 옆의 Run 버튼 클릭
  8. 임포트 완료!
  • 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);
    }

}
profile
배운 것은 기록하자! / 오류 지적은 언제나 환영!
post-custom-banner

0개의 댓글