반응형
05-17 16:44
Today
Total
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
관리 메뉴

개발하는 고라니

[Spring] 크롤링 해온 정보 DB에 저장 본문

Framework/Spring

[Spring] 크롤링 해온 정보 DB에 저장

조용한고라니 2020. 12. 14. 19:46
반응형

* '코드로 배우는 스프링 웹 프로젝트' 교재를 1.5회(?) 완독 후, 스스로의 힘으로 게시판을 구현해보았다.

물론 미흡한 점이 심심찮게 발견되어 수정의 수정을 거듭했다. 그렇게 3~4주를 보내고 나니 다음엔 무엇을 만들어볼까 하던 차에, 문득 떠오른 것이 있다.

 

'무수히 많은 치킨 가게가 있고, 그 많은 메뉴들을 한 곳에서 모아보면 어떨까?'

 

 그래서 프로젝트라고 하기엔 뭣하지만 원하는 작업을 구현해보며 모르는 부분이 있으면 배워가고, 알던 내용이 있으면 다시 되새기는 목적을 가지고 시도해보려 한다.

 

 먼저 '푸라닭' 사이트에서 메뉴 정보들을 크롤링해 뿌려주는 식으로 시작해보고 결과가 괜찮으면 다른 브랜드들도 시도해보는 식으로 점차 넓혀가겠다.


@ 이전 게시물에 이어 업로드 함.

2020/12/14 - [Programming Language/Java] - [Java] 'Jsoup'을 이용한 Java 크롤링

 

[Java] 'Jsoup'을 이용한 Java 크롤링

* '코드로 배우는 스프링 웹 프로젝트' 교재를 1.5회(?) 완독 후, 스스로의 힘으로 게시판을 구현해보았다. 물론 미흡한 점이 심심찮게 발견되어 수정의 수정을 거듭했다. 그렇게 3~4주를 보내고 나

dev-gorany.tistory.com

# TABLE 생성하기

* Oracle Database를 사용해 이전에 크롤링한 치킨 메뉴들의 정보를 저장할 테이블을 만들어보자.

  • 테이블에 들어갈 내용
    • brand : 치킨 브랜드의 구분 컬럼 (예: (푸라닭, 1), (교촌, 2) ...)
    • cno : 생성되는 각 메뉴들의 index
    • name : 메뉴의 이름
    • price : 메뉴의 가격
    • img : 메뉴의 사진(경로가 될 것) //추후 구현
    • enabled : 메뉴가 단종될 경우 0, 판매 중일 경우 1
    • content : 메뉴의 설명
    • info : 알레르기 정보(?)

정도가 될 수 있겠다.

 

* Create Table SQL

CREATE TABLE MENU (

    BRAND NUMBER(3), 
    CNO NUMBER NOT NULL, 
    NAME VARCHAR2(60) NOT NULL, 
    PRICE VARCHAR2(10), 
    IMG VARCHAR2(60), 
    ENABLED NUMBER(1) NOT NULL, 
    CONTENT VARCHAR2(200), 
    INFO VARCHAR2(200), 
     
    CONSTRAINT MENU_PK PRIMARY KEY(CNO) 
);

> 모든 정보가 Essential이라 Not Null 이어야 한다는 생각도 있으나, 먼저 메뉴 목록만 DB에 저장하려고 하니 일부만 not null로 설정한다. PK는 CNO로 설정했다.

 

* Sequence 생성

 

* CNO의 열 시퀀스 설정

> 이처럼 설정하면 Insert SQL 구문 시 CNO_SEQ.NEXTVAL을 사용하지 않아도 저절로 시퀀스 값이 들어간다.

 

# MenuVO 생성하기

* VO를 편하게 설정하기 위해 LOMBOK을 DI 시킨다.

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.0</version> <scope>provided</scope>
</dependency>

> Lombok의 @Data 어노테이션을 사용하면 멤버변수들의 Getter/Setter, toString(), 기본 Constructor를 간편하게 쓸 수 있게 해준다.

 

# Spring과 Oracle Database 연동

 * 우선 JDBC 연결을 하려면 JDBC Driver가 필요하다. 모든 라이브러리가 maven을 이용하나 Oracle DB의 JDBC Driver는 11g 까지 공식적으로 Maven으로는 지원되지 않기에 직접 .jar 파일을 프로젝트에 추가시켜 주어야한다.

 

 * 첨부된 ojdbc8.jar 파일을 /WEB-INF 디렉토리 및에 추가해준다. 필자는 /WEB-INF/lib/ojdbc8.jar 처럼 추가했다.

ojdbc8.jar
3.97MB

 1) 커넥션 풀 설정

 > 일반적으로 다수가 동시에 처리해야 하는 웹 어플리케이션의 경우 DB연결을 이용할 때는 '커넥션 풀(Connection Pool)'을 이용하므로, Spring에 커넥션 풀을 등록해서 사용하는 것이 좋다.

 

 > Java에서는 DataSource라는 인터페이스를 통해 커넥션 풀을 사용한다.

 

 > DataSource를 통해 매번 DB와 연결하는 방식이 아닌, 미리 연결을 맺고 반환하는 구조를 이용하여 성능 향상을 기대한다.

 

 > 커넥션 풀은 여러 종류가 있고, spring-jdbc 라이브러리를 이용할 수도 있으나, 코드로 배우는 스프링 웹 프로젝트에서 사용한 'HikariCP'를 사용해보겠다.

 

1-1) 라이브러리 추가와 DataSource 설정

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>2.7.8</version>
</dependency>

 * pom.xml에 HikariCP DI를 해준다.

 

 * root-context.xml에 bean 등록

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
    <property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
    <property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost:1521/xepdb1"></property>
    <property name="username" value="ID"></property>
    <property name="password" value="PASSWORD"></property>
</bean>

<!-- HikariCP configuration -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
    <constructor-arg ref="hikariConfig"/>
</bean>

 

# Spring과 MyBatis 연동

* Spring Framework와 MyBatis를 연동해 좀 더 빠르게 SQL을 처리할 수 있는 구조를 만들어 보자.

* MyBaits는 흔히 SQL Mapping 프레임워크로 분류되고, JDBC 코드의 복잡하고 지루한 작업을 피하는 용도로 사용된다.

 

  • MyBatis의 Advantage
    • 자동으로 Connection close() 가능
    • MyBatis 내부적으로 PreparedStatement 처리
    • #{property}와 같이 속성을 지정하면 내부적으로 자동 처리
    • 리턴 타입을 지정하는 경우 자동으로 객체 생성 및 ResultSet 처리

 1) MyBatis 관련 라이브러리 추가

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

 2) SQLSessionFactory

* MyBatis에서 가장 핵심의 객체는 'SQLSession'과 'SQLSessionFactory'이다.

 

* SQLSessionFactory : 내부적으로 SQLSession이라는 것을 만들어내는 존재. SQLSession을 통해 Connection을 생성하거나 원하는 SQL을 전달하고, 결과를 반환받는 구조로 작성하게 된다.

 

* root-context.xml에 bean 등록

<!-- dataSource 밑에 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>

 > 스프링에 SQLSessionFactory를 등록하는 작업은 SqlSessionFactoryBean을 이용한다.

 

 3) 스프링과의 연동 처리

* SQLSessionFactory를 이용해 코드를 작성해도 직접 Conncection을 얻어 JDBC 코딩이 가능하나, 좀 더 수월하게 작업하려면 MyBatis의 Mapper를 작성하면 좋다.

 

* CrawlingMapper 인터페이스 생성

 > org.gorany.mapper 패키지에 추가했다.

public interface CrawlingMapper {
	
	public void insertName(List<MenuVO> list);
}

 > 푸라닭 치킨메뉴의 이름이 담긴 MenuVO객체를 담은 List가 파라미터로 들어와 반복문을 통해 insert될 것이다.

 

* Mapper 설정 - root-context.xml

 > Namespace에 'mybatis-spring'을 추가한다.

 > <mybatis-spring:scan base-package="org.gorany.mapper"/>를 추가한다.

 > 'org.gorany.mapper'가 아닌 본인이 작성한 mapper 인터페이스가 있는 패키지를 쓴다.

 

 

* XML Mapper 사용

 > mapper 패키지와 동일한 구조의 디렉토리를 src/main/resources 밑에 만들어준다.

사진에선 MenuMapper이지만 실제론 CrawlingMapper이다

> CrawlingMapper.xml

 (MyBatis는 forEach문 등을 사용해 동적으로 사용할 수 있다. 이에 대해선 다음에 자세하게 포스팅 하겠다.)

 

 

* log4jdbc-log4j2 설정

> MyBatis는 내부적으로 JDBC의 PreparedStatement를 이용해 SQL을 처리한다. 

> SQL에 전달되는 파라미터는 JDBC에서와 같이 '?'로 치환되어 처리된다.

> 따라서 SQL을 변환해서 PreparedStatement에 사용된 '?'가 어떤 값으로 처리되었는지 확인하는 기능을 추가하자.

<dependency>
    <groupId>org.bgee.log4jdbc-log4j2</groupId>
    <artifactId>log4jdbc-log4j2-jdbc4</artifactId>
    <version>1.16</version>
</dependency>

> 라이브러리를 추가한 뒤 로그 설정 파일을 추가하는 작업과 JDBC 연결 정보를 수정해야한다.

log4jdbc.log4j2.properties
0.00MB

 1) 이를 src/main/resources 밑에 붙여넣는다.

 2) JDBC 연결 정보는 위에서 수정되 있는 것을 기입했기 때문에 별도로 수정이 필요하지 않다.

 


# Web에서 GET 요청 후 크롤링 완료하기

* /home에서 <a href="/crawling/puradak">...</a>를 누르면 크롤링 --> DB 저장 완료

* 즉 /crawling/puradak페이지에 접근하면 내부적으로 크롤링되게 설정함

 

# CrawlingController

 

# CrawlingServiceImpl

 

# DB 저장 결과

 * 이전 포스팅에서 사용한 Crawler인터페이스와 PuradakCrawler 및 Application 클래스로는 mapper가 동작하지 않아 부득이하게 Web을 사용하게 되었다.

반응형
Comments