Elasticserach에 Excel 데이터 입력하기 - 기본 설정과 적용 라이브러리


올해에는 선택과 집중을 분명히 하기로 했는데…제 버릇 개 못준다더니…어김없이 또 여기저기를 들쑤시기 시작했다…-.-


Hadoop이니 Hbase니 Spark니 잔뜩 설치해놓고는 다시 Elasticsearch에 관심을 갖게 된 것이다. 어떤 것인지 한 번
설치나 해보자고 시작한 것이 쉬운 설치 방법과 사용법에 혹해서 더 깊은 내용을 알고 싶어진 것이다. 마침 분석해보고
싶은 데이터가 있어 이참에 한번 Elasticsearch를 이용해보자고 마음먹었다.


현재까지 진행된 작업은 spring boot 기반의 웹 시스템을 통해 Excel파일을 업로드하면 이를 파싱하여 
Elasticsearch로 입력하고 업로드된 파일들은 별도로 관리 가능하도록 만든 것이다. 앞으로 3차례에 걸쳐 이 개발 
과정을 정리해보도록 하겠다.


발단


2월 경…새 집으로 이사를 좀 하는게 어떨까 싶어 새 집을 물색하였다. 그리고 기왕지사 옮기는 것, 가급적이면
앞으로 집 값도 좀 올라 주면 좋을 것 같다는 생각이 들었다. 하지만 부동산이라고는 쥐뿔도 모르는 상태에서 어디
그게 쉬운일이던가…그냥 새 집은 새 집이고 마침 어떤 데이터로 빅데이터나 AI를 공부해보나 하던 참이라 부동산
데이터를 사용해보면 어떨까 하는 생각이 들어 공공 데이터 포털에서 부동산 관련 데이터를 모으기 시작했다.


처음 모은 데이터는 1996년 부터 2017년까지의 공시지가 데이터였다. 그리고 첫 난관이 시작되었다.
데이터는 모았는데 이 데이터를 어떻게 Elasticsearch에 넣어야 할지 방법을 몰랐던 것이다. 그렇게 Excel
데이터를 Elasticsearch로 입력하는 방법을 찾다가 가장 적절해 보이는 솔루션으로 찾은 것이 excelastic이라는
vert.x 기반의 stand alone 애플리케이션이었다.


그런데 말입니다…안타깝게도 이 애플리케이션도 문제가 있었다. 클라이언트 PC 및 Elasticsearch가 설치된 서버의
사양과도 관계가 있겠지만 10만 건 정도 입력을 시도하면 여지없이 OOM이 발생하여 정상 입력이 되지 않았다.
시행착오를 거쳐 확인한 안정적인 입력 건수는 약 5만 건 정도였다. 공시지가 데이터가 년도당 약 50만 건의 데이터가
있는데 이 파일을 Elasticsearch로 입력하려면 파일을 10개로 쪼개는 작업을 해주어야 한다는 말이다. 이 작업 조차
웬만한 PC에서는 쉽지 않다. 내 맥미니가 i5(2.5GHz)에 RAM 16Gb인데도 50만 건 들어있는 Excel 파일을 열어서
5만 건씩 10개로 쪼개다보면 버벅거리기가 일쑤였다.


그래서 목마른 놈이 우물을 판다고…직접 하나 만들기로 했다. 그리고 기왕 만드는 김에 이것저것 기능을 좀 추가해보자
했는데 마침 또 회사에서 KMS를 Elasticsearch 기반으로 만들면 어떻겠냐는 이야기가 나와 겸사겸사 함께 진행해
보기로 했다. 


관련 기술들


이 작업에 사용된 기술들은 다음과 같다.


  1. Spring boot 2.0.0
  2. jQuery + bootstrap (UI는 AdminLTE라는 오픈소스 사용)
  3. Elasticsearch 6.2.1 ( + X-Pack)
  4. MySQL 5.6.38
  5. Spring Tool Suite 3.9.2


각 기술의 세세한 부분보다는 작업을 진행하면서 어려움을 겪었던 부분들 혹은 편리했던 기능들에 대한 팁 수준의
정리를 진행하고자 한다.


Spring boot로 삽질하기


작년까지는 현재 일하는 곳의 업무 시스템 개발을 위해 Spring으로 2차례 정도 가벼운 웹 시스템을 개발한 적이 있다.
그 과정에서 Spring boot를 개인 프로젝트에 사용한 적은 있지만 잠깐 건드려보다가 방치되고 말았다. 그리고는 이번에
다시 Spring boot를 이용하기로 했다. 마음같아서는 마이크로서비스에 대한 공부도 곁들여 하면서 구현을 해보고
싶었으나 너무 학습의 범위가 넓어질 것 같아 그냥 Spring을 쓰듯 Spring boot를 쓰기로 했다…-.-


STS에서 서버(Tomcat) 사용하기


경력에 걸맞지 않은 초보적인 실수가 참 많다…ㅠ.ㅠ 늘 겪는 실수 중 하나가 프로젝트를 생성한 후 STS에서 바로
서버 연결하여 실행하는 부분인데 이번에도 여지없이 프로젝트를 생성하고 나니 프로젝트의 서버 설정이 뭔가 이상하다.


서버를 선택할 수 있는 화면이 나오지 않고 “This project is not associated with any server”라는 문구가 보인다.


이 것은 프로젝트 생성 처음 설정에서 packaging 항목을 jar로 선택한 결과이다. jar로 선택한 경우 Stand alone
프로젝트로 판단하여 외부 서버와 연결하는 설정이 나타나지 않는다. 




packaging을 war로 하면 서버 설정 창에서 Tomcat 등의 was와 연결이 가능해진다.




Security 사용


프로젝트를 생성한 후 기본적인 REST API를 구현하였고 테스트를 위해 STS 내에서 Tomcat을 구동시켜 브라우저를
통해 URL을 호출하여보았다. 그런데…난데없이 계정 입력창이 뜨는 것이 아닌가?


Spring security


확인 결과 이 것은 프로젝트 생성 시 의존성 설정 부분에서 Security를 체크했기 때문이었다. 



Security를 체크함으로 해서 많은 부분에 영향을 받았다. 파일 업로드, iframe 사용 등에서도 문제가 생겨 확인해보면
모두 Security 관련 설정 때문이었다. 계정 로그인 처리, 파일 업로드 문제, iframe 사용과 관련된 각각의 내용들을
모두 확인 후 적용하였으나 아직은 잘 모르는 부분이 많기에 아래 코드로 Security는 bypass하는 수준에서 적용을
마무리 하였다.


@Override
public void configure(WebSecurity web) throws Exception {
	// TODO Auto-generated method stub
	super.configure(web);
		
	web.ignoring().antMatchers("/**");
}


위 코드는 JAVA config 설정을 이용할 경우 WebSecurityConfigurerAdapter를 상속받은 config 클래스를 
생성하여 코딩하면 된다. Spring (boot)에서 Security를 사용하는 방법은 아래 링크를 참조하였다.


https://spring.io/guides/gs/securing-web/


DB 연결 설정


Spring을 이용하는 경우에는 보통 다음과 같은 과정을 거쳐 DB를 연결하고 CRUD 작업을 진행하였다.


  1. DataSource 처리를 위한 Config 클래스 생성
  2. 필요에 따라 properties 파일에 DB 연결정보 추가
  3. Service 인터페이스와 그 구현 클래스 생성
  4. Controller 클래스에서 의존성 주입을 통해 Service에 선언한 CRUD 메서드를 이용하여 작업


처음엔 이 과정만 생각하고 진행하다가 꽤 심한 삽질을 했다. 내가 ORM과 관련하여 JPA를 사용하도록 설정한 것을
깜빡 한 것이다. JPA를 이용할 경우에는 1번과 3번의 과정이 필요없다. application.properties에 DB 연결 설정만
등록하면 바로 DB와 연결이 되며 Entity 클래스와 Repository 인터페이스를 구현하여 사용하면 된다.


JPA를 통한 MySQL 연동은 아래 두 곳의 사이트에서 도움을 받았다.


https://docs.spring.io/spring-data/jpa/docs/2.0.5.RELEASE/reference/html/

https://www.callicoder.com/spring-boot-rest-api-tutorial-with-mysql-jpa-hibernate/


Elasticsearch API 사용


Spring 프로젝트 중에도 Spring Data Elasticsearch라는 관련 프로젝트가 있다. 관련 링크는 아래와 같다.


https://projects.spring.io/spring-data-elasticsearch/


하지만 내용을 살펴보면 알겠지만 가장 최신 버전의 릴리즈도 Elasticsearch 5.5.0까지만 지원을 한다.
아래 링크를 보면 Spring Data Elasticsearch의 각 버전과 그 버전에서 지원하는 Elasticsearch 버전이
정리되어 있다.


https://github.com/spring-projects/spring-data-elasticsearch


하지만 나는 이미 Ealsticsearch 6.2.1 버전을 설치한터라 안타깝게도 Spring Data Elasticsearch는
사용하지 못하고 별도로 6.2 버전대의 라이브러리를 pom.xml 파일에 다음과 같이 추가하였다.


<dependency>
	<groupId>org.elasticsearch</groupId>
	<artifactId>elasticsearch-core</artifactId>
	<version>6.2.2</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch</groupId>
	<artifactId>elasticsearch-hadoop-mr</artifactId>
	<version>6.2.2</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch</groupId>
	<artifactId>elasticsearch-spark-20_2.11</artifactId>
	<version>6.2.2</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch.client</groupId>
	<artifactId>elasticsearch-rest-high-level-client</artifactId>
	<version>6.2.2</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch</groupId>
	<artifactId>elasticsearch-hadoop</artifactId>
	<version>6.2.2</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch.client</groupId>
	<artifactId>elasticsearch-rest-client</artifactId>
	<version>6.2.2</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch</groupId>
	<artifactId>elasticsearch</artifactId>
	<version>6.2.2</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
	<groupId>org.elasticsearch.client</groupId>
	<artifactId>transport</artifactId>
	<version>6.2.2</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch.plugin</groupId>
	<artifactId>transport-netty4-client</artifactId>
	<version>6.2.2</version>
</dependency>
<!-- Elasticsearch 설치 후 X-Pack을 설치했기 때문에 추가 -->
<dependency>
	<groupId>org.elasticsearch.client</groupId>
	<artifactId>x-pack-transport</artifactId>
	<version>6.2.2</version>
</dependency>


현재 상태에서 모든 라이브러리가 다 필요한 것은 아니지만 추후 Hadoop이나 Spark와의 연동도 염두에 두고 있기에
그냥 함께 설치하였다.


Excel parsing


Excel 파일을 분석하는 것은 가장 많이 사용하는 POI 라이브러리를 사용하였다.
하지만 일반적으로 알려진 사용법으로는 벌써 이 단계에서 50만건을 처리하는데 OOM이 발생하였다.
해결책을 찾아야 했다. 게으른 개발자의 숙명으로 직접 코딩을 해야 하는 방법보다는 누군가 만들어놓은 라이브러리가
없을까를 우선하여 구글링을 하였다…ㅠ.ㅠ


역시나 세상에는 나같은 불쌍한 중생을 거둬 먹이는 천사같은 분들이 늘 존재한다. 마침 내가 필요로 하는 용도의
라이브러리가 똭! 눈에 띄였다. 이 라이브러리를 설치하여 사용하니 50만건을 OOM 없이 빠른 속도로 parsing해
주었다. 라이브러리는 pom.xml에 다음과 같이 추가하면 된다.


<dependency>
    <groupId>com.monitorjbl</groupId>
    <artifactId>xlsx-streamer</artifactId>
    <version>1.2.0</version>
</dependency>


라이브러리 소스는 아래 링크에서 확인할 수 있다.


https://github.com/monitorjbl/excel-streaming-reader


정리


가장 첫 단계로 Spring boot 및 java 프로그래밍 관련된 내용을 가지고 포스팅을 해보았다. 주 목적이 Spring boot나
java 프로그래밍이 아니므로 부족한 내용이 많겠지만 이와 관련해서는 더 자세하고 정확한 설명이 있는 사이트나 
블로그를 참조하는 편이 더 나을 것이다.


자꾸 요상한(?) 것들에 관심을 가지게 되면서 최종 롤인 iOS 개발자로서도 또 그 이전까지의 롤이었던 java개발자로서도
점점 역량이 떨어지는 것 같다…ㅠ.ㅠ 드문드문 Spring 또는 Spring boot를 접하다보니 별 것 아닌 일로 시간을 
허비하기 일쑤다. 그래도 불행 중 다행이랄까? 요즘은 워낙 양질의 자료를 다양한 경로로 쉽게 구할 수 있다보니 그럭저럭
이정도나마 하고 있지 않나 싶다.


다음 포스팅에서는 Elasticsearch의 JAVA API와 관련된 내용을 조금 더 상세하게 살펴보고 또 Elasticsearch의
설정 몇가지를 함께 알아보겠다.


전체 소스를 공유하려고 했는데 오늘 문득 프로젝트명, github의 레포지터리명, 프로젝트의 패키지명 등이 맘에 들지
않아 전체적으로 수정을 하고 있어 당장에는 힘들 것 같다. 이 시리즈가 마무리되는 시점에(이 글 포함 3개의 포스팅으로
계획 중) 전체 소스를 공유하도록 하겠다.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^



Spring boot와 mongoDB 연동하기 그리고 REST API


새삼스러운 이야기지만 Spring boot는 프로젝트 구성이 참으로 간편하다.

처음 STS에서 프로젝트 생성 시 Dependencies를 적절하게 선택하면 필요한 모든 라이브러리들이

알아서 착착 들어와주고 우리는 그저 그 라이브러리들을 적절히 사용하기만 하면 된다.


오늘 정리할 Spring boot와 mongoDB의  연동과 이를 테스트하기 위해 필요한 REST API 구현도

마찬가지이다.


그래서 어찌 보면 오늘 내용은 달리 설명할 부분이 없는 것이나 다름 없다.

아무래도 일반적인 지식으로 내용이 채워질 것이다.



1. 기능 목표


이번 주의 목표는 Spring boot와 mongoDB를 연동하여 Sign-up 정보를 mongoDB에

저장하고 이 정보를 이용하여 Login을 하는 것까지 구현을 하게 될 것이다.


2. 기술 목표


Spring boot와 mongoDB의 연동 및 이 과정에서 필요한 Spring boot에서의 REST API

구현에 대해 알아보도록 할 것이다.


3. 기술 적용 내용


  • Spring boot와 mongoDB 연동


Spring boot와 mongoDB의 연동 및 이 과정에서 필요한 Spring boot에서의 REST API

구현에 대해 알아보도록 할 것이다. 


우선 mongoDB는 설치가 된 것을 가정하고 설명을 진행한다. 나같은 경우  OSX에서 brew로

설치를 진행하고 설치 후 변경된 내용은 없다. 즉 가장 기본적인 상태로 설치가 되어있다.

그리고 Till60라는 이름의 DB를 하나 생성하였다. (MomgoDB를 인증모드로 접근하기 위한

설정 작업들이 있기는 하였으나 mongoDB 자체에 대한 설명을 하는 자리가 아니므로

‘4. 참조 (site 또는 서적)’ 항목에 링크로 대신한다.


내용을 단순하게 하기 위해 STS에서 새로 프로젝트를 생성하였다. Spring Starter Project를 

선택하였으며 Dependencies도 단순하게MongoDBWeb만을 선택하였다. 이렇게 생성했을 때 

gradle.build 파일의 dependencies는 다음과 같다.


dependencies {

compile('org.springframework.boot:spring-boot-starter-data-mongodb')

compile('org.springframework.boot:spring-boot-starter-web')

testCompile('org.springframework.boot:spring-boot-starter-test')

}


그리고 application.properties에는 mongoDB 연결을 위한 uri를 다음과 같이 설정하였다.


spring.data.mongodb.uri=mongodb://mazdah:password@localhost:27017/Till60


사실상 설정은 이 것이 다라고 보면 된다.


  • Spring boot REST API 구현


이전의 개인프로젝트나 업무상의 작은 프로젝트에서는 주로 @RestController를 이용하여 REST API를

구현했었다. 그런데 이번에 @RepositoryRestResource라는 annotation을 새롭게 알게 되고 그

간편함에 끌러 사용하게 되었다. 물론 후에 또 어떻게 바뀔지는 모르겠지만…


우선 @RepositoryRestResource는 spring-data-rest-core 라이브러리에 정의되어있으며 이는

Spring Starter Project 생성시  Dependencies에 Rest Repositories를 선택하면 포함된다.


주의 사항

나같은 경우 현재 사무실과 집에서 Git으로 소스를 공유하면서 작업을 하고 있는데 두 곳의 STS 버전이

달라서 그런지 아니면 프로젝트 생성 후 별도로 라이브러리를 추가한 것이 꼬였는지 처음 사무실에서

@RepositoryRestResource를 사용했을 때 브라우저에서 계속 404 에러가 발생을 했다.

확인 결과 필요한 라이브러리 중 spring-data-rest-webmvc가 빠져있어서 발생을 한 문제였다.

기왕에 Spring boot를 사용하기로 했으면 필요한 라이브러리를 일일이 찾아 삽입하기 보다는

 처음 프로젝트 생성시 Dependencies를 잘 설정해주는 것이 불필요한 오류를 막는 지름길 일 것이다.


 

@RepositoryRestResources는 Repositary 인터페이스에 적용을 하게 되며 이렇게 할 경우

별다른 RestController 없이 기본적인 REST API를 자동으로 생성해준다. 이 때 자동으로 생성되는

REST API의 기본 형태는 다음과 같다.


http://domain:port/{domain}

http://domain:port/{domain}/{id}

http://domain:port/{domain}/search

http://domain:port/{domain}/search/{search query}


이 때 domain은 @Entity 또는 @Document 등으로 설정된 Domain 클래스이며 내부적으로

클래스명의 앞부분을 소문자로만든 후 뒤에 s를 붙여 사용하게 된다. 즉, Person이라는 Domain

Class를 만들었다고 한다면 {domain} 위치에는 persons로 표시된다.


{search quesry}에는 Repository 인터페이스에 선언된 쿼리 함수와 그 함수의 파라미터로 전달될

쿼리 문자열이 들어가게 된다. 만일  findByName(@Param(“name”) String name)이라는 

쿼리 함수가 선언되어있다면 {search quesry}는 다음과 같이 표현된다.


findByName?name={name}


Person domain 객체를 이용하여 이름이 “Edison”이라는 사람을 찾는다면 전체 URI는 다음과 같다.


http://domain:port/persons/search/findByName?name=Edison


참고로 URI의 구성에 영향을 미치는 요소가 2가지가 있는데 우선 REST API의 베이스 URI를

Spring boot의 application.properties에 spring.data.rest.base-path라는 항목에 값을

넣으면 된다. 만일 이 값을 api로 지정했다면 위의 URI는 다음과 같이 바뀌게 된다.


http://domain:port/api/persons/search/findByName?name=Edison


그리고 @RepositoryRestResources에 path 요소를 설정하게 되면 앞서 말한 domain 값이

path에 설정한 값으로 바뀌게 된다. 즉, @RepositoryRestResources(path=“user”)로

설정을 하게 되면 위의 URI는 다시 아래와 같이 바뀐다.


http://domain:port/api/user/search/findByName?name=Edison


이렇게 이번 주에는 가장 기본적인 설정을 통해 Spring boot와 mongoDB를 연동하고 

Sign-up과 Login 기능을 구현하게 되었다.


4. 참조 (site 또는 서적)


Spring Rest 참조

http://docs.spring.io/spring-data/rest/docs/current/reference/html/

https://brunch.co.kr/@sbcoba/3


MongoDB 관련 문서

http://blog.nekoromancer.kr/2013/12/16/mongodb-인증모드로-접속하기/

https://docs.mongodb.com/manual/reference/method/db.createUser/#db.createUser


MongoDB 관리 툴

https://docs.mongodb.com/ecosystem/tools/administration-interfaces/

https://www.humongous.io/app/


5. 확인 사항 (의문 사항)


이처음 생성하고 현재 프로젝트를 진행하고 있는 Till60 프로젝트의 경우 Run As > Spring Boot App

하여  서버 기동을 하게 되면 아래와 같은 로그가 계속 표시된다.


23:40:47.916 [cluster-ClusterId{value='584c13e54fe3502b024dca36', description='null'}-localhost:27017] DEBUG org.mongodb.driver.cluster - Checking status of localhost:27017

23:40:47.918 [cluster-ClusterId{value='584c13e54fe3502b024dca36', description='null'}-localhost:27017] DEBUG org.mongodb.driver.cluster - Updating cluster description to  {type=STANDALONE, servers=[{address=localhost:27017, type=STANDALONE, roundTripTime=1.1 ms, state=CONNECTED}]


아마도 클러스터로 구성된 mongoDB의 ping check를 하는 로그인 듯한데 얘가 어디서 출력을 하고

있는 것인지 잘 모르겠다. 로그에 찍힌 패키지명으로 추측컨대 mongo-deriver 또는 mongo-java-

Driver라는 라이브러리에서 찍히는 것 같은데 Referenced Library에는 해당 라이브러리가 포함되어

있지 않다. 아직도 기본 구조를 이해를 못하고 있어 애로 사항이 좀 있다…ㅠ.ㅠ


아마도 클러스터로 구성된 mongoDB의 ping check를 하는 로그인 듯한데 얘가 어디서 출력을 하고

있는 것인지 잘 모르겠다. 로그에 찍힌 패키지명으로 추측컨대 mongo-deriver 또는 mongo-java-

Driver라는 라이브러리에서 찍히는 것 같은데 Referenced Library에는 해당 라이브러리가 포함되어

있지 않다. 아직도 기본 구조를 이해를 못하고 있어 애로 사항이 좀 있다…ㅠ.ㅠ


6. 평가


서두에서도 말했지만 Spring boot는 우선 설정에 큰 신경을 쓰지 않아도 좋기 때문에 작업 시간을

꽤 많이 단축시켜준다. 하지만 그 구조를 이해하지 못하는 경우에는 상당한 혼란도 생길 수 있다.

모든 기술들이 그렇겠지만 우선은 그 기본적인 내용을 이해하고 사용을 해야 겠다는 생각을 새삼

하게 된다.


어쨌든 이번 주에는 어렵지 않게(물론 이해 못하는 부분이 있기는 하지만) mongoDB와 연동을

하여 기본적인 CRUD는 처리할 수 있게 되었다. 더 진도를 나가기 전에 mongoDB 모델링이

필요할 것 같다. 다음 주에는 mongoDB 모델링에 대해서 진행을 해 볼 것이다.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^