찔끔찔끔씩😎

[웹개발의 봄, Spring] 4주차 (2) - <나만의 셀렉샵> 서버 본문

Server/Spring

[웹개발의 봄, Spring] 4주차 (2) - <나만의 셀렉샵> 서버

댕경 2022. 4. 11. 23:18
728x90
[웹개발의 봄, Spring] 4주차 (1) - 네이버 쇼핑 API 이용하기

프로젝트 만들기

🔎 프로젝트 시작시 확인할 점

1. Auto Import

  • Inser imports on paste: Always'

 

2. Annotation Processing

  • Compiler-Annotation Processors

🔎 API

키워드로 상품 검색하고 그 결과를 목록으로 보여주기 GET /api/search?query=검색어 List<ItemDto>
관심 상품 등록하기 POST
/api/products
Product
관심 상품 조회하기
GET /api/products List<Product>
관심 상품에 관심 가격 등록하고, 그 가격보다 낮은 경우 표시하기 PUT /api/products/{id} id

🔎 3계층

  • Controller
    • ProductRestController: 관심 상품 관련 컨트롤러
    • SearchRequestController: 검색 관련 컨트롤러

 

  • Service
    • ProductService: 관심 상품 가격 변경

 

  • Repository
    • Product: 관심 상품 테이블
    • ProductRepository: 관심 상품 조회, 저장
    • ProductRequestDto: 관심 상품 등록하기
    • ProductMypriceRequestDto: 관심 가격 변경하기
    • ItemDto: 검색 결과 주고받기

 

위 계층에 유의하여 관심 상품 조회 -> 관심 상품 저장 -> 관심 상품 최저가 설정 순으로 기능을 구현해보도록 한다.

 

<나만의 셀렉샵> 서버

🔎 관심 상품 조회하기

[모아보기] 탭을 눌렀을 때 등록된 관심 상품을 조회할 수 있는 기능 !

그러기 위해선 관심 상품들을 데이터베이스에 모아두어야할 것이다.

 

1. Timestamped 만들기
관심상품(Product) 클래스를 만들어야 한다. 하지만 이전에 Product가 상속받을 Timestamped 클래스를 만들자.

1) week04/models/Timestamped 만들기

@Getter // get 함수를 자동 생성합니다.
@MappedSuperclass // 멤버 변수가 컬럼이 되도록 합니다.
@EntityListeners(AuditingEntityListener.class) // 변경되었을 때 자동으로 기록합니다.
public abstract class Timestamped { // abstract: 반드시 다른 곳에서 상속되어서 사용될 것!
    @CreatedDate // 최초 생성 시점
    private LocalDateTime createdAt;

    @LastModifiedDate // 마지막 변경 시점
    private LocalDateTime modifiedAt;
}

2) Week04Applications 에 시간 자동변경 어노테이션(AuditingEntityListener) 추가

이때 AuditingEntityListener를 사용하기 위해 Application에게 알려줘야 한다. 
- @EnableJpaAuditing

@EnableJpaAuditing // 시간 자동 변경이 가능하도록 함 
@SpringBootApplication 
public class Week04Application { 
	public static void main(String[] args) { 
    	SpringApplication.run(Week04Application.class, args); 
    } 
}

 

2. Product 만들기

관심 상품 테이블에 저장해야 할 데이터는 title, image, link, lprice, myprice이다. 

1) week04/models/Product 만들기

@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;
}

 

3. ProductRepository 만들기

Product 테이블을 만들었으니, Repository도 바로 만들어 준다.

1) models/ProductRepository 만들기

: Product 대상, Long 형태

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

 

4. ProductRestController 만들기

관심 상품에 관한 요청을 처리해줄 컨트롤러가 필요하다.

"관심 상품 조회"를 위해서는 그냥 productRepository에서 findAll 해주면 될 것 같다!

1) controller/ProductRestController 만들기

2)

@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductRestController {

    private final ProductRepository productRepository;

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

 

 

🔎 관심 상품 등록하기

상품을 검색한 후, 등록 버튼을 눌렀을 때 관심 상품이 Repository에 생성되어야 한다.

검색 결과의 제목, 이미지, 링크, 최저가를 가져오기.

 

1. ProductRequestDto

- 위 네가지 정보를 가져가는 Dto를 먼저 만들어주자.

1) models/ProductRequesDto

@Getter
public class ProductRequestDto {
    // title, link, image, lprice만 들고다니면 된다.
    private String title;
    private String link;
    private String image;
    private int lprice;
}

 

2. Product 클래스에 생성자를 만들어 주자.

ProductRequestDto를 만들었으니 이용해주는 게 인지상정!

Product 클래스에 ProductRequestDto 이용하는 생성자를 추가한다.

// 관심 상품 생성 시 이용할 생성자.
public Product(ProductRequestDto requestDto) {
    this.title = requestDto.getTitle();
    this.image = requestDto.getImage();
    this.link = requestDto.getLink();
    this.lprice = requestDto.getLprice();
    this.myprice = 0;
}

 

3. ProductRestController 만들기

"관심 상품 등록" api를 추가해주자.

PostMapping으로 api요청을 받은 뒤 Dto를 Repository에 추가해주면 되겠다.

@PostMapping("/api/products")
public Product createProduct(@RequestBody ProductRequestDto requestDto){
    // @RequestBody를 붙여주어야 함
    Product product = new Product(requestDto);
    return productRepository.save(product);
}

 

🔎 관심 상품 최저가 설정

"관심 상품의 관심 최저가를 등록하고, 가격이 이보다 낮아지는 경우 표기" 하는 기능 하나만 남았다.

관심 가격을 등록하려면 이 또한 DTO를 이용해야 된다. 

ProductMypriceRequestDto를 만들어서 myprice를 갖도록 한다. 

 

1. ProductMypriceRequestDto

- 본인이 설정한 최저가를 수정할 때 사용할, 가격정보만 들어있는 Dto

@Getter
public class ProductMypriceRequestDto {
    private int myprice;
}

 

2. Product 클래스에도 update 만들기

myprice를 변경할 때 사용할 것!

// 수정시에 사용될 생성자
public void update(ProductMypriceRequestDto requestDto) {
    this.myprice = requestDto.getMyprice();
}

 

3. ProductService 만들기

수정하기 위해서는 Service를 만들어주어야 한다. 

관심상품의 id와 ProductMypriceRequestDto를 받아서 해당 상품의 myprice정보를 update 해주자!

예외 처리도 해주도록 한다.

+ 잊지말고 Transactional 어노테이션을 꼭 해준다!

@RequiredArgsConstructor // final로 선언된 멤버 변수가 꼭 필요함을 알려주기 위함.
@Service // 서비스임을 알려준다.
public class ProductService {

    private final ProductRepository productRepository;

    @Transactional // 메소드 동작이 SQL 쿼리문임을 알려준다.
    public Long update(Long id, ProductMypriceRequestDto requestDto) {
        // id에 해당되는 애를 업데이트
        // productRepository에서 id에 해당되는 애를 findById
        Product product = productRepository.findById(id).orElseThrow(
                () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
        );
        product.update(requestDto);
        return id;
    }
}

 

🔎 + Scheduler 

상품가격(lpice) 매일 업데이트 해주기. 그래야 최저가를 바꿔주지!

관심 상품 title을 검색한 뒤  해당 상품의 lprice를 매일 새벽 1시에 업데이트하는 스케줄러를 만들었다.

 

1. Scheduler 클래스 만들기

1) utils/Scheduler

@RequiredArgsConstructor // final 멤버 변수를 자동으로 생성합니다.
@Component // 스프링이 필요 시 자동으로 생성하는 클래스 목록에 추가합니다.
public class Scheduler {

    private final ProductRepository productRepository;
    private final ProductService productService;
    private final NaverShopSearch naverShopSearch;

    // 초, 분, 시, 일, 월, 주 순서
    @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);
        }
    }
}

 

2. updateBySearch 함수 만들기

1) service/ProductService 에 추가하기

@Transactional // 메소드 동작이 SQL 쿼리문임을 선언.
public Long updateBySearch(Long id, ItemDto itemDto) {
    Product product = productRepository.findById(id).orElseThrow(
            () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
    );
    product.updateByItemDto(itemDto);
    return id;
}

 

3. Application, @EnableScheduling

스프링부트가 스케줄러를 작동시키도록 Application에 @EnableScheduling을 추가한다.

@EnableScheduling // 스프링 부트에서 스케줄러가 작동하게 함
@EnableJpaAuditing // 시간 자동 변경이 가능하도록 합니다.
@SpringBootApplication // 스프링 부트임을 선언합니다.
public class Week04Application {
   public static void main(String[] args) {
      SpringApplication.run(Week04Application.class, args);
   }
}

 

이로써 <나만의 셀렉샵>의 서버를 모두 완성했다!  이제 관련 클라이언트부분을 만들러 가보쟛~

  • 상품명에 따른 검색
  • 관심 상품 등록, 조회
  • 관심 상품에 대한 최저가 등록, 조회
Comments