본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


몽고DB/Java 몽고DB

스프링(전자정부), 몽고DB와의 연동 (몽고DB 터널링, Mongodb Ternering, Mongodb ssh)

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2019. 5. 9.

 

 

스프링에서 몽고db를 연동하는 방법을 찾아보면, 스프링 boot랑 관련된 내용은 꽤 많이 나오는데..스프링과 관련된 내용은 그다지 많지 않는 것 같다.

몽고db와의 연동은 하이버네이트를 사용해 보았거나 JPA방식의 연동을 해 본 경험이 있으면 그다지 어렵지가 않다.

아무튼, 몽고db와의 연동을 위해서는 라이브러리를 받아야 한다. pom.xml에 라이브러리를 등록하여 주자.

라이브러리 버전때문에 문제가 발생 할 수 있으니 의존성관련 문제가 발생하면 구글링을 통해 맞는 버전을 찾자.

	    <dependency>
	        <groupId>org.mongodb</groupId>
	        <artifactId>mongo-java-driver</artifactId>
	        <version>3.2.2</version>
	    </dependency>	
	    <dependency>
	        <groupId>org.springframework.data</groupId>
	        <artifactId>spring-data-mongodb</artifactId>
	        <version>1.9.1.RELEASE</version>
	    </dependency>

 

위 2개의 라이브러리가 추가 한 뒤에 설정파일(xml)을 만들어주자. servlet.xml 같은 파일에 그냥 추가하여도 된다.

Namespace가 추가되야 하므로 유의하자

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mongo="http://www.springframework.org/schema/data/mongo"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.8.xsd">
	
   <!-- mongo -->
    <mongo:mongo-client
        host="주소"
        port="포트" credentials="사용자아이디:비밀번호@컬렉션이름" >
        <mongo:client-options
            connections-per-host="8"
            threads-allowed-to-block-for-connection-multiplier="4"
            connect-timeout="1000"
            max-wait-time="1500"
            socket-keep-alive="false"
            socket-timeout="1500"
        />
    </mongo:mongo-client>
 
    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongo" />
        <constructor-arg name="databaseName" value="컬렉션 이름" />
    </bean>
     	
</beans>

해당파일은 resources에 context-mongo.xml 로 저장하였다.

여기서 credentials 라는 부분이 있는데, 반드시 "사용자아이디:비밀번호@컬렉션이름" 형식으로 입력해야 된다.

예) admin:1234@test_collection

해당 파일이 스프링이 로드되면서 읽혀지도록 web.xml에 추가를 하여 준다.

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:내가만든resources경로/context-mongo.xml</param-value>
	</context-param>

여기까지 하였고, 서버를 구동하였을 때 아무런 문제가 없다면 일단 1차 목표는 달성 한 것이다.

다음으로 실제 사용방법이다. 여기서는 MongoRepository 를 사용한 방법이 아니라 MongoTemplate를 사용한 내용이다. (lombok은 사용하지 않았다)

 

setter, getter를 위한 클래스를 만든다. 패키지명은 따로 포함하지 않았다.

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document  //매핑을 위한 가이드 역할
public class MongoPojo {
	@Id   //요것이 고유 키 값
	private String id;
	
	private int number;    //숫자를 매핑
	private String text;   //문자를 매핑
	private Object all;  //모든 객체를 매핑
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public int getNumber() {
		return number;
	}
	public void setNumber(int number) {
		this.number = number;
	}
	public String getText() {
		return text;
	}
	public void setText(String text) {
		this.text = text;
	}
	public Object getAll() {
		return all;
	}
	public void setAll(Object all) {
		this.all = all;
	}
}

다음으로 비지니스로직을 수행 할 서비스 클래스를 만들어준다. 패키지명은 따로 포함하지 않았다. 마찬가지로 위에 만든 클래스도 Import하지 않았다.

import java.util.HashMap;
import java.util.List;

import javax.annotation.Resource;

import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

@Service("MongoService")
public class MongoService {
	
	private String dbName = "컬렉션이름";
	
	@Autowired
	private MongoTemplate mongodb;  //몽고Template

	//단일정보 가져오기
	@Override 
	public MongoPojo getInfor(MongoPojo vo) {
		Query query = new Query(Criteria.where("_id").lt(new ObjectId("5cd276f2d079c73e93149837")));
		MongoPojo obj = mongodb.findOne(query, MongoPojo.class, dbName);
		return obj;
	}

	 //리스트형태 데이터 전부 가져오기
	public List<MongoPojo> getList(MongoPojo vo) {
		Query query = new Query(Criteria.where("number").gte(123));  //조건1
		query.addCriteria(Criteria.where("text").regex("검색문자조건"));  //조건2
		return mongodb.find(query, MongoPojo.class, dbName);
	}

	//페이징 처리가 포함된 리스트 
	public HashMap<Object,Object> getPageList(MongoPojo vo, int startNum, int endNum) {
		final HashMap<Object,Object> result = new HashMap<Object,Object>(2);

		Query query = new Query(Criteria.where("number").gte(123));  //조건
		Pageable pageable = new PageRequest(startNum, endNum);
		query.with(pageable);  //페이지 정보를 포함
		
		List<MongoPojo> obj = mongodb.find(query, MongoPojo.class, dbName);
		long total = mongodb.count(query, MongoPojo.class, dbName);
		Page<MongoPojo> list = new PageImpl<MongoPojo>(obj, pageable, total);
		
		result.put("total",total);
		result.put("list",list);
		return result;
	}
	
	//등록
	public String insertData(MongoPojo vo) throws Exception {
		mongodb.insert(vo, dbName);  //vo객체에 정보를 토대로 알아서 등록한다.
		return vo.getId();  //id가 등록된 고유 키 값이다. 등록이 성공하면 vo에 담아준다.
	}

	//수정
	public void updateData(MongoPojo vo) throws Exception {
		mongodb.save(vo, dbName);  //vo객체의 정보를 토대로 알아서 매칭하여 수정한다.
        //update 이름으로 시작하는 메소드가 훨씬 더 사용하기 편리하다.
	}

	//삭제
	public void deleteData(MongoPojo vo) throws Exception {
		mongodb.remove(vo, dbName);  //vo객체의 정보를 토대로 알아서 삭제한다.
	}	
	
}

위 내용이 끝이다. 물론 단순하게 기능을 나열하였기에 복잡한 조회나 기능이 추가되면 수정이 필요하다.

데이터 조회에 대해서 살펴볼 부분이 있는데.. 여기서 Query라는 클래스가 있다.

 

Query라는 클래스는 말 그대로 조회를 위한 조건의 집합이다.

Query라는 클래스는 생성자로 Criteria(한글 뜻 : 기준)는 클래스를 가질수 있거나 아니면 addCriteria라는 메소드로 Criteria 클래스를 받을 수 있다.

Criteria는 where 이라는 메소드로 조건을 받으며, 이후 gte, regex라는 메소드 등을 활용하여 조건에 대한 값을 추가하여 조건이 이어 질 수 있다. (빌더패턴)

* 이러한 의미이다 : Criteria.조건.내용

where에는 내가 주고 싶은 조건을 입력하고 이후 반드시 해당 내용을 지정하는 메소드를 호출해야 하는데, 위 예제는 gte와 regex만 사용하였다. 기타 나머지 자주 사용되는 메소드는 아래와 같다.

메소드 명칭 의미
gt 주어진 값보다 더 큰
gte 주어진 값보다 크거나 같은
lt 주어진 값보다 더 작은 (일반 조회에서는 equals로도 사용된다)
lte 주어진 값보다 작거나 같은
ne 주어진 값과 동일하지 않은
in 주어진 배열에 속하는 어떤
nin 주어진 배열에 속하지 않는 어떤
regex 문자를 포함하고 있는

getInfor라는 메소드는 1개의 대상을 조회하는 메소드인데, 여기서 id를 조회하는 경우에 new ObjectId를 사용한 점이 흥미롭다. vo객체 그 자체를 주어도 사실 상관 없다.

 

또한 친절하게도 Pageable이라는 인터페이스를 통해서 페이징도 쉽게 지원한다.

Query클래스에 with라는 메소드를 통해서 구현한 Pageable을 추가하면 된다. 사용법은 getPageList 메소드를 참조하자.

 

 

* 추가(에노테이션 설정방법, 터널링 방법)

xml에 등록하지 않고 에노테이션으로 하는 방법이다.

에노테이션으로는 컴포넌트 에노테이션을 사용 하였다.

import javax.annotation.PostConstruct;

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;

@Component
public class SSHWithMongoTemplate {
		
	private static String MONGO_DB_NAME ;
	private static String MONGO_USER ;
	private static String MONGO_PWD ;
	private static Integer MONGO_PORT ;

	private static MongoTemplate DB;
	
	@PostConstruct //빈 객체가 구성된 이후에 동작하도록 한다.
	public void onInit() {
		MONGO_DB_NAME = "DB이름";
		MONGO_PORT = 27017;
		MONGO_USER = "사용자아이디";
		MONGO_PWD = "비번";
		Init();  	
	}
	//null인경우
	private void Init() {
		if(DB == null){
			try {
			    String combineUrl = "mongodb://"+MONGO_USER + ":" + MONGO_PWD + "@127.0.0.1:"+MONGO_PORT + "/" + MONGO_DB_NAME;
				MongoClient mongoClient = new MongoClient(new MongoClientURI(combineUrl));
				DB = new MongoTemplate(mongoClient, MONGO_DB_NAME);								
			} catch (Exception e) {
				e.printStackTrace();
			}			
		} else {
			System.out.println("template is not empty");
		}
	}	
    
	public MongoTemplate getDB(){
		if(DB == null){
			Init();
		}
		return DB;
	}


}

 

해당 컴포넌트를 생성하면 서비스나 컨트롤러에서 사용하는방법은 다소 번거롭더라도 아래처럼 바꾸어야 한다.

//임포트 생략..

@Service("MyService")
public class MyServiceImpl extends EgovAbstractServiceImpl implements MyService{
	
	@Autowired
	private SSHWithMongoTemplate template;  //앞서 만든 몽고db 컴포넌트
	private MongoTemplate mongodb;  //실제 사용할 몽고템플릿

	@PostConstruct  //마찬가지로 빈객체 생성 이후에 몽고템플릿 기능을 살려주도록 한다.
	public void init() {
		mongodb = template.getDB();	//만들어진 몽고db 컴포넌트에서 템플릿 객체를 가져오는 부분.
	}
    
    //이하 비지니스 로직..
}

 

그렇다면 이를 응용하면 터널링을 통한 몽고db 접속도 가능 해 진다.

몽고DB에 직접 접속 할 수 없으면서 SSH를 사용 해서 접속을 해야만 하는 상황이라면 위 방식이 효율적이다.

터널링을 위해서는 SSH 접속을 도와주는 라이브러리가 필요 하다.

JSch 라이브러리를 설정하도록 하자.

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

 

이후 단계로는,

1. ssh 에 접속 한다.

2. 접속이 완료되면 포트 포워딩을 실시 한다.

3. 데이터 베이스에 접속한다.

  - 데이터베이스에 접속 할 때는 포트포워딩한 가상 포트를 부여하도록 한다.

  - 즉, 내가 접속해야될 데이터베이스 포트가 27017이라면 27017로 접속하는 것이 아니라 임의로 설정한 포트를 사용 한다.

  - 아래 완성된 소스코드 가운데 부분에 setPortForwardingL() 메소드를 참조하자.

 

아래 완성된 소스코드이다.

import javax.annotation.PostConstruct;

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;

@Component
public class SSHWithMongoTemplate {

    //ssh관련 설정 값들 ---------- ---------- ----------
    private static String SSH_USER = "";
    private static String SSH_PASSWORD = "";
    private static String SSH_HOST = "";
    private static Integer SSH_PORT = 1;

    //몽고DB관련 설정 값들 ---------- ---------- ----------
    private static String MONGO_DB_NAME ;
    private static String MONGO_USER ;
    private static String MONGO_PWD ;
    private static Integer MONGO_PORT ;
		

    private static Session SSH_SESSION;  //SSH 세션 
    private static MongoTemplate DB;  //몽고템플릿 객체

    @PostConstruct
    public void onInit() {
		MONGO_DB_NAME = "데이터베이스이름";
		MONGO_PORT = 27017;
		MONGO_USER = "데이터베이스 사용자 이름";
		MONGO_PWD = "데이터베이스 비밀번호";
		SSH_USER = "SSh 사용자 이름";
		SSH_HOST = "SSh 주소";
		SSH_PASSWORD = "SSh 비밀번호";
		SSH_PORT = 22;  //SSh 포트번호
		Init();	
    }

    private void Init() {
        if(DB == null){
            try {
                //ssh 접속부분 -----------------
                java.util.Properties config = new java.util.Properties();
                config.put("StrictHostKeyChecking", "no");
                JSch jsch = new JSch();
                SSH_SESSION = null;
                SSH_SESSION = jsch.getSession(SSH_USER, SSH_HOST, SSH_PORT);
                SSH_SESSION.setPassword(SSH_PASSWORD);
                SSH_SESSION.setConfig(config);
                SSH_SESSION.connect();

                //ssh 접속 이후 포트 포워딩 부분------------------
                final int virtualInt = 23192;   //가상포트 23192를 부여한다. 포트충돌 방지

                //여기가 핵심, 몽고 포트는 27017을 사용 할 지라도 가상포트를 사용해서 몽고DB에 접속한다.
                SSH_SESSION.setPortForwardingL(virtualInt, "127.0.0.1", MONGO_PORT);  

                String combineUrl = "mongodb://"+MONGO_USER + ":" + MONGO_PWD + "@127.0.0.1:"+virtualInt + "/" + MONGO_DB_NAME;
                MongoClient mongoClient = new MongoClient(new MongoClientURI(combineUrl));
                DB = new MongoTemplate(mongoClient, MONGO_DB_NAME);
            } catch (Exception e) {
                e.printStackTrace();
            }			
        } else {
			System.out.println("template is not empty");
        }
    }	

    public MongoTemplate getDB(){
        if(DB == null){
            Init();
        }
        return DB;
    }


}

23192라는 포트 번호는 마음데로 주어도 상관 없는데 27017이나 22, 23 등 기존에 사용중인 포트번호는 피하자.

만약 id와 password가 없다면 하단에 보이는 아래부분을 고쳐주어야 한다.

//변경 전
String combineUrl = "mongodb://"+MONGO_USER + ":" + MONGO_PWD + "@127.0.0.1:"+virtualInt + "/" + MONGO_DB_NAME;

//변경 후
String combineUrl = "mongodb://127.0.0.1:"+virtualInt + "/" + MONGO_DB_NAME;

 

 

* 스프링 웹 리스너를 활용한 터널링 방법

https://lts0606.tistory.com/278

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글