Back-end/Spring

[SpringBoot] 4주차 스터디 ① (나만의 셀렉샵 만들기, 네이버 쇼핑 API)

poppy 2021. 9. 9. 02:09
반응형

네이버 쇼핑 API를 사용하여 나만의 셀렉샵을 만들어 보았다. 프로젝트 구조는 다음과 같다.

 

 

1. 네이버 쇼핑 API 신청하기

네이버 쇼핑 API 를 사용하기 위해서는 API 사용 신청을 해야 한다. 다음 링크에서 신청할 수 있다.

https://developers.naver.com/docs/search/shopping/

 

검색 API 쇼핑 검색 개발가이드

NAVER Developers - 검색 API 쇼핑 검색 개발가이드

developers.naver.com

 

다음과 같은 양식으로 API 이용 신청을 한다.

 

 

신청이 끝나면 Client ID 와 Client Secret 를 발급받을 수 있다.

 

 

2. Repo(Domain, Repository)

- Repo 는 가장 안쪽 부분을 의미하고, DB와 맞닿아 있다.

 

먼저 Product.java 파일을 생성한다. 이 파일을 관심 상품을 의미하고 DB의 테이블 역할을 하는 클래스이다.

@Getter // Lombok이 getter 자동 생성
@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.image = requestDto.getImage();
        this.link = requestDto.getLink();
        this.lprice = requestDto.getLprice();
        this.myprice = 0;
    }

    // 최저가(관심가격) 변경 시
    public void update(ProductMypriceRequestDto requestDto) {
        this.myprice = requestDto.getMyprice();
    }

    // 예약된 시간에 가격 변경 시
    public void updateByItemDto(ItemDto itemDto) {
        this.lprice = itemDto.getLprice();
    }
}

 

TimeStamped.java 파일을 생성하여 생성/수정 시간을 자동으로 업데이트 되도록 한다.

@Getter
@MappedSuperclass // 상속했을 때, 컬럼으로 인식하게 함
@EntityListeners(AuditingEntityListener.class) // 생성/수정 시간을 자동으로 업데이트 함
public abstract class Timestamped { // abstract - 직접 생성은 불가하고 상속만 가능함

    @CreatedDate
    private LocalDateTime createdAt; // 생성 시간

    @LastModifiedDate
    private LocalDateTime modifiedAt; // 수정 시간
}

 

생성/수정 시간을 JPA 가 자동으로 업데이트 하고, 스케줄러가 작동할 수 있도록 Application 에 어노테이션을 추가해준다.

@EnableScheduling // 스프링 부트에서 스케줄러가 작동하게 함
@EnableJpaAuditing // 시간 변경을 자동으로
@SpringBootApplication
public class Week04Application {

    public static void main(String[] args) {
        SpringApplication.run(Week04Application.class, args);
    }

}

 

ProductRepository.java 파일을 생성한다. 이 파일은 DB의 SQL 역할을 하는 인터페이스이다. JPA를 사용하기 위해 JpaRepository<domain 클래스, id의 type> 을 상속받는다.

public interface ProductRepository extends JpaRepository<Product, Long> {
}

 

3. DTO

- 테이블에 값을 넣을 때 클래스를 직접 사용하는 것은 좋지 않다. 그래서 완충재로 활용하는 것이 DTO 이다.

 

ProductRequestDto 는 관심 상품을 등록할 때 사용하는 DTO 이다.

@Getter
public class ProductRequestDto {
    private String title;
    private String link;
    private String image;
    private int lprice;
}

 

ProductMypriceRequestDto 는 자신이 설정한 최저가(관심가격)을 변경할 때 사용하는 DTO 이다.

@Getter
public class ProductMypriceRequestDto {
    private int myprice;
}

 

ItemDto 는 API 를 통해 검색된 결과를 가져올 때 사용하는 DTO 이다. 검색 결과가 String 으로 들어오기 때문에 화면에 보여주기 위해서는 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");
    }
}

 

4. Service

- Service 는 중간 부분이고, 실제 중요한 동작이 많이 일어나는 부분이다.

 

ProductService.java 파일을 생성하고 추가적으로 필요한 동작을 정의한다.

@RequiredArgsConstructor // 꼭 필요한 요소(final) 자동 생성
@Service // 이 클래스가 서비스임을 알려줌
public class ProductService {
    // final 은 꼭 필요한 요소임을 명시하는 것, 값이 변경 될 수 없음
    private final ProductRepository productRepository;

    @Transactional // SQL 쿼리가 일어나야 함을 스프링에게 알려줌, 자동으로 DB에 업데이트 됨
    public Long update(Long id, ProductMypriceRequestDto requestDto) {
        Product product = productRepository.findById(id).orElseThrow(
                () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
        );
        product.update(requestDto); // 관심 가격 변경
        return id;
    }

    @Transactional
    public Long updateBySearch(Long id, ItemDto itemDto) {
        Product product = productRepository.findById(id).orElseThrow(
                () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
        );
        product.updateByItemDto(itemDto); // 예약된 시간에 가격 변경
        return id;
    }
}

 

5. Controller

- Controller 는 가장 바깥 부분이고, 요청/응답을 처리한다.

 

ProductRestController 는 관심 상품 관련 요청/응답을 처리하는 controller 이다.

@RequiredArgsConstructor // 꼭 필요한 요소(final) 자동 생성
@RestController // JSON 으로 응답하기 위한 RestController 라는 의미
public class ProductRestController {

    private final ProductService productService;
    private final ProductRepository productRepository;

    // 등록된 전체 관심 상품 조회
    @GetMapping("/api/products")
    public List<Product> getProducts() {
        return productRepository.findAll();
    }

    // 관심 상품 등록
    @PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto) {
        Product product = new Product(requestDto);
        productRepository.save(product);
        return product;
    }

    // 최저가(관심가격) 변경
    @PutMapping("/api/products/{id}")
    public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) {
        return productService.update(id, requestDto);
    }
}

 

SearchRequestController 는 상품 검색 관련 요청을 처리하는 controller 이다.

@RequiredArgsConstructor // 꼭 필요한 요소(final) 자동 생성
@RestController // JSON 으로 응답하기 위한 RestController 라는 의미
public class SearchRequestController {

    private final NaverShopSearch naverShopSearch;

    // Naver 쇼핑 API를 사용하여 검색된 상품 조회
    @GetMapping("/api/search")
    public List<ItemDto> getItems(@RequestParam String query) {
        String resultString = naverShopSearch.search(query);
        return naverShopSearch.fromJSONtoItems(resultString);
    }
}

 

반응형