본문으로 건너뛰기

Template pattern

· 약 4분
Ryukato
BackEnd Software Developer

프로젝트 진행중 비상연락망을 엑셀로 업로드하여 데이터를 DB table에 insert하는 기능을 구현해야하는데 문제는 비상연락망이 하나가 아니라 현업 비상연락망, 외부기관 및 협력업체 이렇게 세가지가 있다고 한다. 차후에 추가될지도(그렇지 않겠지만) 모른다는 생각이 들어 template pattern으로 해보면 어떨가 하여 샘플 코드를 아래와 같이 짜 보았다. 전자정부 프레임워크인가 프로젝트 workspace에 있는 패키지 중에 ExcelUtil이라는 클래스가 있는데 엑셀 내용을 읽어서 List<Map<String,Object>> 타입으로 반환해주는 함수가 있었는데 이를 이용하면 쉽게 아래의 AbstractExcelUploadTemplate 클래스의 read 메서드를 구현할 수 있을 거라는 생각이 든다. 그래서 일단은 테스트 용이기때문에 테스트 데이터를 반환해주는 용도로 사용하였다.

Interface​

실제 구현 내용을 포함함 AbstractExcelUploadTemplate를 숨기는 용도로 그리고 그냥 Spring에서 interface와 구현체를 선언해서 하는 것에 익숙해 일단 interface를 만들었다. 별거 없다. upload 하나다.

ExcelUploader class​
public interface ExcelUploader {
void upload(String filePath) throws Exception;
}

Template Class​

AbstractExcelUploadTemplate class​
public abstract class AbstractExcelUploadTemplate implements ExcelUploader {
abstract Map<String,String> buildColMapper(Set<String> colNameSet);
abstract void insertExcelRow(Map<String,Object> queryParam) throws Exception;

protected List<Map<String,Object>> read(String filePath){
List<Map<String,Object>> list = new ArrayList<Map<String,Object>>(10);

for(int i=0;i<10;i++){
Map<String,Object> item = new HashMap<String,Object>();

for(int j=0;j<10;j++){
String key = "col"+j;
String value="test_data_"+i+"_"+j;
item.put(key, value);
}// end of inner for loop

list.add(item);
}// end of outer for loop
return list;
}

protected Map<String,Object> buildQueryParam(Map<String,Object> rowItem) {
if(rowItem == null) return null;

Set<String> colNameSet = rowItem.keySet();
Map<String,Object> queryParam = new HashMap<String,Object>();
Map<String,String> colMapper = buildColMapper(colNameSet);

for(String colName:colNameSet){
String key = colMapper.get(colName);
Object value = rowItem.get(colName);
queryParam.put(key, value);
}
return queryParam;
}

//template method
public void upload(String filePath) throws Exception{
List<Map<String,Object>> list = read(filePath);
Iterator<Map<String,Object>> it = list.iterator();

while(it.hasNext()){
Map<String,Object> rowItem = it.next();
Map<String,Object> queryParam = buildQueryParam(rowItem);
if(queryParam == null){
continue;
}
insertExcelRow(queryParam);
}
}
}

Implementation classes​

public class EmergencyUploader extends AbstractExcelUploadTemplate {

@Override
Map<String, String> buildColMapper(Set<String> colNameSet) {
Map<String,String> colMapper = new HashMap<String,String>();
colMapper.put("col0", "newCol0");
colMapper.put("col1", "newCol1");
colMapper.put("col2", "newCol2");
colMapper.put("col3", "newCol3");
colMapper.put("col4", "newCol4");
colMapper.put("col5", "newCol5");
colMapper.put("col6", "newCol6");
colMapper.put("col7", "newCol7");
colMapper.put("col8", "newCol8");
colMapper.put("col9", "newCol9");
return colMapper;
}

@Override
void insertExcelRow(Map<String, Object> queryParam) throws Exception{
System.out.println(queryParam);
//call insert method of DAO object
}
}

각 비상연락망 마다 달라지는 부분은 엑셀의 컬럼명과 Table의 칼럼명이고 테이블에 insert할 쿼리 반면 전체적인 흐름 - 엑셀을 읽고 엑셀 칼럼과 테이블 칼럼을 매핑 시키고 테이블에 insert할 데이터를 생성해서 테이블에 데이터를 insert하는 동일한 흐름을 가지기 때문에 buildColMapper, insertExcelRow 두 메서드를 추상 메서드로 선언하고 실제 구현은 각 구현체에게 위임함으로써 전체적인 흐름은 유지를 하되 변경되는 부분만 구현체에서 구현해주면 된다는 것. 이것이 Template pattern의 핵심이 아닐까 생각한다.