2017년 6월 30일 금요일

Redis와 전자정부프레임워크 연동

Redis와 전자정부프레임워크 연동

Spring과 Redis을 사용하기 위해서는 “spring-data-redis”와 “jedis”(Java redis Client)의 현재 버전은 1.8.4와 2.9.0 이 버전을 사용하기 위해서는  Spring 4.3.9이상이 필요합니다.
현재 전자정부 프레임워크 3.6 버전에 포함된 Spring은 4.1.2이기 때문에 이를 지원하는  1.5.2, 2.6.2 버전으로 설정하였습니다.

Redis 설치
  1. Ubuntu인경우 “sudo apt-get install redis-server” 명령어로 간단하게 설치가능
  2. Windows인경우 “https://github.com/MSOpenTech/redis/releases”에서 설치파일을 다운로드 받아 설치한다.

추천하는 방식은 1, 2, 3도 아닌 Docker 이미지로 설치하는 방식이다.
“docker pull redis”
“docker run --name my-redis -d -p 46379:6379 redis” → 이런식으로

Redis Desktop Manager 설치
RDM이라고 줄여 말하기도 하는데 Windows, Linux, MacOS에서 Redis 서버를 관리할수 있는 오픈소스 GUI 관리도구 이다. 설치하면 알겠지만 정말 딱 필요한 기능만 있습니다.

전자정부 프레임워크에 설정하기
개인적으로 Object Configuration 방식이 아닌 XML Configuration 방식을 선호하기에 XML 방식으로 설명하겠습니다.

  • pom.xml에 Dependency 추가
<!-- for Redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
  
<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>2.6.2</version>
</dependency>

  • redis 관련 Context 추가
resource/egovframework/context-redis.xml 파일을 만들어 아래내용을 추가
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
       http://www.springframework.org/schema/beans     
       http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.1.xsd">

<context:property-placeholder location="classpath:config/redis.properties" />
<!-- Redis -->
<bean id="connectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:use-pool="true"
p:host-name="localhost"
p:port="46379" />

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="connectionFactory" />

<!-- end of Redis -->

</beans>

이렇게 하면 전자정부 프레임워크에서 Redis을 사용하기 위한 준비는 끝입니다. 정말 간단하죠.

Spring에서는 Redis을  DAO Layer로 구분짓습니다. 그러니 Redis는 Service Layer에서 호출 되거나 다른 DAO에서 호출되어야 합니다. 기존에 구축되어 있는 프로젝트에 Redis을 추가하는 경우 Service Layer을 수정하는 것보다는 DAO Level을 수정하는 방식이 Side impact를 최소화할 수 있습니다. 여기서는 전자정부프레임워크에 포함된 Sample 프로젝트에 Redis을 추가하는 방식으로 진행해보겠습니다.

  • RedisTemplateSupport.java
전자정부프레임워크의 “EgovAbstractMapper”와 비슷한 일을 하는 객체를 만들어줍니다.
별것 없는 코드지만 이것을 만들어주는 것은 코드일 관성을 유지하기위한 조치라고 생각하면 되겠습니다.

package kr.co.keytech.framework.redis;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;

public class RedisTemplateSupport {

@Autowired
protected RedisTemplate template;
@Resource(name = "redisTemplate")
protected ListOperations listOps;

@Resource(name = "redisTemplate")
protected HashOperations hashOps;

@Resource(name = "redisTemplate")
protected SetOperations setOps;
}

  • SampleRedisDAO.java
전자정부 프레임워크 Sample프로젝트 을 보면 SampleDAO가 있습니다. 이와 똑같은 API가 있는 SampleRedisDAO을 만들어줍니다. RedisDAO가 하는 일은 다음과 같습니다.
  1. 요청에 대해서 Redis에 Cache가 있으면 그정도를 전달합니다.
  2. 없다면 DAO에 요청을 하고 Cache에 넣은 다음에 전달, 다음에 똑같은 요청이 있다면 Cache에가 전달될것입니다.
  3. 갱신요청이나 생성요청 삭제요청도 Cache도 작업해주고 DAO작업해줍니다.

package kr.co.keytech.egov.sample.dao;

import java.util.List;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import kr.co.keytech.egov.sample.vo.SampleDefaultVO;
import kr.co.keytech.egov.sample.vo.SampleVO;
import kr.co.keytech.framework.redis.RedisTemplateSupport;

@Repository
public class SampleRedisDAO extends RedisTemplateSupport {

private static final Logger LOGGER = LoggerFactory.getLogger(SampleRedisDAO.class);
private static final String KEY = "SAMPLE";
@Resource(name="sampleDAO")
private SampleDAO sampleDAO;
/**
* 글을 등록한다.
* @param vo - 등록할 정보가 담긴 SampleVO
* @return 등록 결과
* @exception Exception
*/

public void insertSample(SampleVO vo) throws Exception {
sampleDAO.insertSample(vo);
LOGGER.debug("#####################################");
LOGGER.debug("A sampleVO insert to Redis, ID="+vo.getId());
try {
hashOps.put(KEY, vo.getId(), vo);
} catch (Exception e) {
LOGGER.info(e.getMessage());
}
}
/**
* 글을 수정한다.
* @param vo - 수정할 정보가 담긴 SampleVO
* @return void형
* @exception Exception
*/
public void updateSample(SampleVO vo) throws Exception {
sampleDAO.updateSample(vo);
LOGGER.debug("#####################################");
LOGGER.debug("A sampleVO update to Redis, ID="+vo.getId());
try {
hashOps.put(KEY, vo.getId(), vo);
} catch (Exception e) {
LOGGER.info(e.getMessage());
}
}

/**
* 글을 삭제한다.
* @param vo - 삭제할 정보가 담긴 SampleVO
* @return void형
* @exception Exception
*/
public void deleteSample(SampleVO vo) throws Exception {
sampleDAO.deleteSample(vo);
LOGGER.debug("#####################################");
LOGGER.debug("A sampleVO delete at Redis, ID="+vo.getId());
try {
hashOps.delete(KEY, vo.getId());
} catch (Exception e) {
LOGGER.info(e.getMessage());
}
}

/**
* 글을 조회한다.
* @param vo - 조회할 정보가 담긴 SampleVO
* @return 조회한 글
* @exception Exception
*/
public SampleVO selectSample(SampleVO vo) throws Exception {
SampleVO sampleVO =  (SampleVO)hashOps.get(KEY, vo.getId());
if(sampleVO != null) {
LOGGER.debug("#####################################");
LOGGER.debug("Finded a SampleVO at Redis, ID="+vo.getId());
} else {
sampleVO = sampleDAO.selectSample(vo);
}
try {
hashOps.put(KEY, vo.getId(), sampleVO);
} catch (Exception e) {
LOGGER.info(e.getMessage());
}
return sampleVO;
}

/**
* 글 목록을 조회한다.
* @param searchVO - 조회할 정보가 담긴 VO
* @return 글 목록
* @exception Exception
*/
public List<SampleVO> selectSampleList(SampleDefaultVO searchVO) throws Exception {
return sampleDAO.selectSampleList(searchVO);
}

/**
* 글 총 갯수를 조회한다.
* @param searchVO - 조회할 정보가 담긴 VO
* @return 글 총 갯수
* @exception
*/
public int selectSampleListTotCnt(SampleDefaultVO searchVO) throws Exception {
return sampleDAO.selectSampleListTotCnt(searchVO);
}

}

  • EgovSampleServiceImpl.java 수정
Service Layer에서 사용중인 DAO를 RedisDAO로 변경합니다.  기존 DAO와 똑같은 구조이기 때문에 아주간단하게 변경될것입니다.

@Resource(name="sampleDAO")
private SampleDAO sampleDAO;
이것을..

@Resource(name="sampleRedisDAO")
private SampleRedisDAO sampleDAO;
이렇게..

  • TestCase 작성
다들 아시는 일반적인 ServiceLayer 단위테스트 방식대로 TestCase을 작성합니다.(모르신다면 큰일입니다. 빨리 공부하셔야죠.)

package kr.co.keytech.egov.sample.service;

import java.util.Date;

import javax.annotation.Resource;

import org.junit.Test;
import org.springframework.test.annotation.Rollback;

import kr.co.keytech.egov.sample.vo.SampleVO;
import kr.co.keytech.framework.AbstractTestCase;

public class EgovSampleServiceTestCase extends AbstractTestCase{

/** EgovSampleService */
@Resource(name = "sampleService")
private EgovSampleService sampleService;
@Test
@Rollback(value = false)
public void testInsertSample() {
SampleVO vo = new SampleVO();
vo.setName("YunChang.Lee");
vo.setDescription("Insert TestCase="+new Date());
vo.setRegUser("KeyTech");
vo.setUseYn("Y");
try {
sampleService.insertSample(vo);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
@Rollback(value = false)
public void testUpdateSample() {
}
@Test
public void testDeleteSample() {
String id = "SAMPLE-00135";
SampleVO vo = new SampleVO();
vo.setId(id);
try {
sampleService.deleteSample(vo);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testSelectSample() {
String id = "SAMPLE-00135";
SampleVO vo = new SampleVO();
vo.setId(id);
try {
vo = sampleService.selectSample(vo);
System.out.println(objectToString(vo));
} catch (Exception e) {
e.printStackTrace();
}
}
}


TestCase에 보시면 objectToSring이라는 메소드가 있는데 이것은 테스트을 위해서 Object을 Json으로 바꿔주는 API 입니다. 그외에도 몇개 더있는데 다음과 같습니다.

/**
* JSON 문자열을 Value Object 로 변환한다.
*
* @param str
* @param objType
* @return
*/
public static <T> T stringToObject(String str, Class<T> objType) {
if (str == null || str.length() == 0) {
return null;
}
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.readValue(str, objType);
} catch (IOException e) {
e.printStackTrace(System.err);
return null;
}
}
/**
* Value Object 를 JSON 문자열로 변환한다.
*
* @param obj
* @return
*/
public static String objectToString(Object obj) {
if (obj == null) {
return null;
}
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(obj);
} catch (IOException e) {
e.printStackTrace(System.err);
return null;
}
}
public static <T> T stringToJson(String json, Class<T> clazz) {
if (json == null || clazz == null) {
return null;
}
ObjectMapper mapper = new ObjectMapper();
T targetObject;
try {
targetObject = mapper.readValue(json, clazz);
} catch (JsonParseException e) {
e.printStackTrace(System.err);
return null;
} catch (JsonMappingException e) {
e.printStackTrace(System.err);
return null;
} catch (IOException e) {
e.printStackTrace(System.err);
return null;
}
return targetObject;
}

아주 간단하게 Redis와 전자정부 프레임워크를 연동해봤습니다. 이글에서는 Redis에 대한 기능설명을 하지 않았습니다. 왜냐면 간단하게 몇문장으로 Redis을 논하기 어렵기 때문입니다.

Redis에 대해서는 따로 자료를 정리해서 올리도록하겠습니다.

블록체인 개요 및 오픈소스 동향

블록체인(block chain) 블록체인은 공공 거래장부이며 가상 화폐로 거래할때 발생할때 발생할 수 있는 해킹을 막는 기술. 분산 데이터베이스의 한 형태로, 지속적으로 성장하는 데이터 기록 리스트로서 분산 노드의 운영자에 의한 임의 조작이 불가...