๐Ÿ’ป ์ฝ”๋”ฉ ์ผ๊ธฐ : [Spring] '์›น ํฌ๋กค๋ง' ํŽธ

ybkยท2024๋…„ 6์›” 4์ผ

spring

๋ชฉ๋ก ๋ณด๊ธฐ
50/55
post-thumbnail

๐Ÿ”” '์›น ํฌ๋กค๋งํ•˜๊ณ  DB์— ์ €์žฅํ•˜๊ธฐ'์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž!


๐Ÿ’Ÿ ์›น ํฌ๋กค๋ง ํ•˜๋Š” ๋ฐฉ๋ฒ•

์›น ํฌ๋กค๋ง : ์›น ํฌ๋กค๋ง์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ , ์ด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

  • Java์—์„œ ์›น ํฌ๋กค๋ง์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Jsoup ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›น ํŽ˜์ด์ง€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.
  • ํฌ๋กค๋ง๋œ ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’Ÿ ์ ์šฉํ•˜๊ธฐ

ProductController.java

@RestController
@RequestMapping("/api/product")
@RequiredArgsConstructor
public class ProductController {
    private final ProductService service;

    @GetMapping("/list")
    public List<Product> product() throws IOException {
        return service.getProduct();
    }
}
  • ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

ProductService.java

@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class ProductService {

    private final ProductMapper mapper;
    private static String product_url = "๋งํฌ";

    public List<Product> getProduct() throws IOException {
        List<Product> productList = new ArrayList<>();
        Document document = Jsoup.connect(product_url).get();

        Elements contents = document.select("section div.cards-wrap article.card-top");

        for (Element content : contents) {
            Product product = Product.builder()
                    .image(content.select("div img").attr("abs:src")) //์ด๋ฏธ์ง€
                    .title(content.select("h2").text()) // ์ œ๋ชฉ
                    .url(content.select("a").attr("abs:href"))  // ๋งํฌ
                    .build();
            productList.add(product);

            // ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
            if (mapper.countByImage(product.getImage()) == 0) {
                mapper.insertProduct(product);
            }
        }
        return productList;
    }
}
  • ํฌ๋กค๋ง์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ํฌ๋กค๋ง๋œ ๋ฐ์ดํ„ฐ๋ฅผ DB์— ์ €์žฅํ•˜๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

  • ํฌ๋กค๋ง์„ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘

    • Jsoup.connect(PRODUCT_URL).get() : Jsoup์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น URL์—์„œ HTML ๋ฌธ์„œ๋ฅผ ๋กœ๋“œํ•˜๊ณ  ์ด๋ฅผ Document ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • document.select : HTML ๋ฌธ์„œ์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ์š”์†Œ๋“ค์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. CSS ์„ ํƒ์ž์— ๋งž๋Š” ์š”์†Œ๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    • ๊ฐ ์š”์†Œ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ์ด๋ฏธ์ง€ url, ์ œ๋ชฉ, ๋งํฌ๋ฅผ ์ถ”์ถœํ•˜์—ฌ Product ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๊ฐ€ DB์— ์—†์œผ๋ฉด(count๊ฐ€ 0) DB์— ์ €์žฅํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด DB์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.


ProductMapper.java

@Mapper
public interface ProductMapper {

    @Insert("INSERT INTO product (image, title) VALUES (#{image}, #{title})")
    int insertProduct(Product product);

    @Select("SELECT COUNT(*) FROM product WHERE image=#{image}")
    int countByImage(String image);
}
  • insertProduct : ์ƒํ’ˆ์„ DB์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • countByImage : ํ•ด๋‹น ์ƒํ’ˆ์ด ํ˜„์žฌ DB์— ์žˆ๋Š”์ง€ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

Product.jsx(React)

export function ProductList() {
  const [productList, setProductList] = useState([]);
  useEffect(() => {
    axios.get(`/api/product/list`).then((res) => setProductList(res.data));
  }, []);

  return (
    <div>
      <Table>
        <Thead>
          <Tr>
            <Th>์ด๋ฏธ์ง€</Th>
            <Th>์ œ๋ชฉ</Th>
          </Tr>
        </Thead>
        <Tbody>
          {productList.map((product) => (
            <Tr key={product.title}>
              <Td onClick={() => (window.location.href = product.url)}>
                <Image src={product.image} />
              </Td>
              <Td>{product.title}</Td>
            </Tr>
          ))}
        </Tbody>
      </Table>
    </div>
  );
}

export default ProductList;
  • GET ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€ ํ™”๋ฉด์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.



product ํ…Œ์ด๋ธ” ์ƒ์„ฑ

CREATE TABLE product (
                         id INT AUTO_INCREMENT PRIMARY KEY,
                         image VARCHAR(500) NOT NULL,
                         title VARCHAR(500) NOT NULL
);
  • ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ DB์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

profile
๊ฐœ๋ฐœ์ž ์ค€๋น„์ƒ~

0๊ฐœ์˜ ๋Œ“๊ธ€