SlideShare ist ein Scribd-Unternehmen logo
1 von 39
Downloaden Sie, um offline zu lesen
JI 구현 이야기, 리뷰 1 
신림프로그래머 최범균, 2014-10-14
내용 
• 리뷰1 (오늘) 
• 시작: 기능 목록 초안, 초기 컴포넌트 식별 
• 0.1 개발: 핵심 기능 구현, CLI 구현 
• 일부 설계 과정, 일부 구현 결과물 
• 리뷰2 (다음) 
• 0.2 개발: 웹 구현 
• 일부 설계/구현 결과물 
• 테스트 관련 생각할 거리: 단위 vs 통합, E2E 테스트 
2
이야기의 시작, 반복(중복) 
[커맨드라인] 
행렬 분해 
[커맨드라인] 
사용자용 데이터 
생성 
[커맨드라인] 
아이템용 데이터 
생성 
[자바] 
데이터 변환 후 
저장 
반복 
여러 알고리즘에 대해 과정 유사 
3
자동화 욕구 
4 
[커맨드라인] 
행렬 분해 
[커맨드라인] 
사용자용 데이터 생성 
[커맨드라인] 
아이템용 데이터 생성 
[자바] 
데이터 변환 후 
저장 
반복 
자동화 
[프로세스] 
경로, 타입, 숫자 등 많은 설정 
단계별 실패 확인 
데이터 변환 
과정, 실패 처리 등 
프로그램 
설정 정보 
경로, 타입, 숫자 등
기능 목록 
버전 내용 
0.1 1개 알고리즘에 대한 서비스 구현 
콘솔에서 서비스 실행: 설정 파일 사용 
5 
0.2 
웹 인터페이스 
- 작업 설정 생성 
- 작업 설정을 이용해서 백그라운드로 작업 실행 
- 실행 내역 추적 
0.3 다른 알고리즘 추가
0.1v
주요 모델 
• 분석 실행(프로세스) 
• 입력: 실행에 필요한 데이터 
• 알고리즘 실행: 입력을 이용해서 특정 알고리즘 수행 
• 결과 저장: 실행 결과를 원하는 형태로 보관 
7
0.1 버전 상위 수준 설계 초안 
입력 데이터 
경로 제공 결과 보관 
8 
주요 인터페이스 
핵심 컴포넌트 
알고리즘 실행 
설정 파일을 이용해서 
분석 기능 실행
요구 상세 
• Mahout을 이용해서 분석 1개 실행 
• 로컬에 설치된 Mahout을 사용 
• 기능 실행은 쉘스크립트를 이용해서 실행 
• Mahout 실행 환경 
• 로컬: 로컬 실행, 로컬 입력 파일 
• 하둡: 하둡 클러스터(MR) 실행, HDFS 입력 파일 
• 로컬에 설치된 하둡을 사용 
• 결과 저장 
• 로컬에 JSON 파일, (단순 테스트 목적) 콘솔 출력 
9
요구 상세 
• Mahout을 이용해서 분석 1개 실행 
• 로컬에 설치된 Mahout을 사용 
• 기능 실행은 쉘스크립트를 이용해서 실행 
10 
• Mahout 실행 환경 
JVM에서 외부 쉘을 실행 
사용자가 쉘을 마음대로 변경하면 
안 되므로, 런타임에 쉘을 동적 생성 
• 로컬: 로컬 실행, 로컬 입력 파일 
• 하둡: 하둡 클러스터(MR) 실행, HDFS 입력 파일 
• 로컬에 설치된 하둡을 사용 
• 결과 저장 
• 로컬에 JSON 파일, (단순 테스트 목적) 콘솔 출력 
Mahout 실행시, 하둡 클러스 
터 설정 가능하도록 
입력 파일로 로컬 경로와 HDFS 
경로를 사용할 수 있도록 하둡 결과 파일을 읽어와 처리
상위 수준 핵심 로직은 
11 
입력을 
이용함 
Mahout을 
실행함 
결과를 
저장함
핵심 부분 설계 시작 
12 
class RecAnalyticsServiceSpec 
extends Specification { 
def “생성”() { 
def RecAnalyticsService service = 
new RecAnalyticsService() 
} 
} 
public class RecAnalyticsService { 
!} 
* 공간의 제약으로 이름 등 일부 변경 
class RecAnalyticsServiceSpec 
extends Specification { 
def service = new RecAnalyticService() 
! 
def “로그가 없을 경우”() { 
when: 
service.createRecommendation() 
then: 
thrown(NoDataException) 
} 
} 
public class RecAnalyticsService { 
public void createRecommendation() { 
throw new NoDataException(); 
} 
} 
! 
public class NoDataException extends RuntimeEx…{ 
}
13 
def service = new RecAnalyticService() 
! 
def “로그가 없을 경우”() { 
when: 
service.createRecommendation() 
then: 
thrown(NoDataException) 
} 
! 
def “로그가 있다면”() { 
setup: 
def ActivityStorage activityStorage = Mock() 
service.setActivityStorage(activityStorage) 
when: 
service.createRecomendation() 
then: “로그가 있으면 NoDataException이 발생하지 않음” 
1 * activityStorage.hasActivityData() >> true 
notThrown(NoDataException) 
} 
! 
public interface ActivityStorage { 
boolean hasActivityData(); 
} 
! 
public class RecAnalyticsService { 
private ActivityStorage activityStorage; 
! 
public void createRecommendation() { 
if (!activityStorage.hasActivityData()) 
throw new NoDataException(); 
} 
! 
public void setActivityStorage(ActivityStorage …) { 
this.activityStorage = …; 
} 
}
14 
def service = new RecAnalyticService() 
def ActivityStorage mockActivityStorage = Mock() 
! 
def setup() { 
service.setActivityStorage(mockActivityStorage) 
} 
! 
def “로그가 없을 경우”() { 
when: 
service.createRecommendation() 
then: 
thrown(NoDataException) 
} 
! 
def “로그가 있다면”() { 
setup: 
mockActivityStorage.hasActivityData() >> true 
when: 
service.createRecomendation() 
then: “로그가 있으면 NoDataException이 발생하지 않음” 
notThrown(NoDataException) 
}
15 
def service = new RecAnalyticService() 
def ActivityStorage mockActivityStorage = Mock() 
def MahoutRunner mockMH = Mock() 
! 
def setup() { 
service.setActivityStorage(mockActivityStorage) 
service.setMahoutRunner(mockMH) 
} 
! 
def “로그가 있다면”() { 
setup: 
mockActivityStorage.hasActivityData() >> true 
! 
when: 
service.createRecomendation() 
then: “로그가 있으면 NoDataException이 발생하지 않음” 
notThrown(NoDataException) 
! 
when: 
service.createRecomendation() 
then: “Mahout 이용 분석 실행 실패하면, 익셉션 발생” 
1 * mockMH.run() >> { throw new MREx() } 
thrown(AnalyticServiceException) 
! 
} 
public interface MahoutRunner { 
public void run(); 
} 
! 
public class RecAnalyticsService { 
private ActivityStorage activityStorage; 
private MahoutRunner mahoutRunner; 
! 
public void createRecommendation() { 
if (!activityStorage.hasActivityData()) 
throw new NoDataException(); 
try { 
mahoutRunner.run(); 
} catch(MREx ex) { 
throw new AnalyticServicException(ex); 
} 
} 
… // setter 추가 
} 
! 
public class MREx extends RuntimeException {} 
public class AnalyticServiceException … {…}
16 
def service = new RecAnalyticService() 
def ActivityStorage mockActivityStorage = Mock() 
def MahoutRunner mockMH = Mock() 
def ResultSaver mockSaver = Mock() 
… // setup() 
def “로그가 있다면”() { 
setup: 
mockActivityStorage.hasActivityData() >> true 
def RecResult recResult = Mock() 
def UserItemSource uiSource = Mock() 
def SimItemSource siSource = Mock() 
recResult.getUserItem() >> uiSource 
recResult.getSimItem() >> recResult 
…// 다른 when-then 
when: 
service.createRecomendation() 
then: “Mahout 이용 분석 실행 실패하면, 익셉션 발생” 
1 * mockMH.run(_) >> { throw new MREx() } 
thrown(AnalyticServiceException) 
! 
when: 
service.createRecomendation() 
then: “Mahout 이용 분석 실행 성공하면, 결과 저장 시도” 
mockMH.run(mockActivityStorage) >> recResult 
1 * mockSaver.saveUserItem(uiSource) 
1 * mockSaver.saveSimItem(siSource) 
} 
public interface ResultSaver { 
void saveUserItem(UserItemSource source); 
void saveSimItem(SimItemSource source); 
} 
public interface UserItemSource {} 
public interface SimItemSource {} 
! 
public interface RecResult { 
UserItemSource getUserItem(); 
SimItemSource getSimItem(); 
} 
! 
public interface MahoutRunner { 
public void run(ActivityStorage activityStorage); 
} 
public class RecAnalyticsService { 
… 
private ResultSaver resultSaver; 
! 
public void createRecommendation() { 
if (!activityStorage.hasActivityData()) 
throw new NoDataException(); 
RecResult result; 
try { 
result = mahoutRunner.run(activityStorage); 
} catch(MREx ex) { 
throw new AnalyticServicException(ex); 
} 
resultSaver.saveUserItem(result.getUserItem()); 
resultSaver.saveSimItem(result.getSimItem()) 
} 
… // setter
추가적인 실패/성공 시나리오를 
진행하면서 
점진적으로 구현/설계 완성 
17
핵심 부분 설계 결과 
18
소속/역할은? 
19
설계 결과물 모듈 배치 결과 
20 
인터페이스 추출
구현/설계 진행 계속 
유사한 방식으로 
MahoutRunner 콘크리트 클래스의 
구현/설계를 
점진적으로 진행 
21
인터페이스 정의를 어떻게? 
22 
이 인터페이스의 메서드를 
정의하려면?
사용자 입장에서 메서드 도출 
UserItemSource와 SimItemSource의 사용자 
23 
▼ 
“ResultSaver의 콘크리트 클래스” 
public class ConsoleResultSaver implements … { 
! 
public void saveUserItem(UserItemSource source) { 
List<UserItem> userItems = source.getUserItems(); 
for (UserItem ui: userItems) { … } 
} 
! 
public void saveUserItem(UserItemSource source) { 
Iterator<UserItem> userItems = source.getIterator(); 
while(userItems.hasNext()) { 
UserItem ui = userItems.next(); 
… 
} 
} 
1안 
2안 
대량 결과 데이터를 처리할 수 있어야 하기 때문에, 
2안 선택
2안으로 구현 
쉘 실행 결과로 만들어진 파일로부터 
데이터를 읽어오는 Iterator 구현 필요 
24 
public interface UserItemSource { 
Iterator<UserItem> iterator(); 
}
2안, Iterator 구현 예 
25 
private class FileSystemSimItemIterator 
implements Iterator<SimItem> { 
private final SequenceFile.Reader reader; 
private final IntWritable key; 
private final VectorWritable value ; 
private SimItem nextItem; 
! 
public FileSystemSimItemIterator( 
FileSystem fileSystem, Path simItemFile) { 
reader = createSequenceFileReader( 
fileSystem, similarItemFile, new Configuration()); 
key = new IntWritable(); 
value = new VectorWritable(); 
moveNext(); 
} 
! 
@Override 
public boolean hasNext() { 
return nextItem != null; 
} 
! 
@Override 
public SimilarItems next() { 
checkNextItemExists(); 
SimItem result = nextItem; 
moveNext(); 
return result; 
} 
private void checkNextItemsExists() { 
if (nextItem == null) throw new NoSuchElementException(); 
} 
! 
private void moveNext() { 
boolean isNextRead = readNextKeyValue(); 
if (!isNextRead) { 
closeReader(); 
nextItem = null; 
return; 
} 
createNextItems(); 
} 
! 
private boolean readNextKeyValue() { 
try { 
return reader.next(key, value); 
} catch (…) {…} 
} 
! 
private void createNextItem() { 
List<ItemValue> allSimilarItems = new ArrayList<>(); 
for (Vector.Element ele : value.get().nonZeroes()) 
allSimilarItems.add(new ItemValue(ele.index(), ele.get())); 
nextItem = new SimItem(key.get(), allSimilarItems); 
} 
private void closeReader() { … } 
}
ResultCreator, *Source, Iterator 구현 관련 
26
CLI 부분 
27
CLI 요구 사항 
• 작업 실행에 필요한 정보를 파일로 설정 
• JSON 형식 사용 
• 콘솔에서 설정 파일 경로를 지정해서 작업 실행 
• 예) ji.sh -c recommendation.conf 
28
핵심은 
• 설정 파일을 읽어와 
• 알맞게 분석 서비스 객체(예, RecAnalyticService)를 구 
성한 뒤 
• 분석 서비스를 실행 
29
핵심 기능 구현/설계에 쓰인 테스트 코드(계속) 
class RecommendationDriverSpec extends Specification { 
def RecommendationDriver driver = new RecommendationDriver(); 
def ConfigLoader mockConfigLoader = Mock() 
def RecommendationAnalyticServiceFactory mockRecAnalyticServiceFactory = Mock() 
def RecommendationAnalyticService mockRecAnalyticService = Mock() 
def HelpPrinter mockHelpPrinter = Mock() 
def String configPath = "myPath" 
! 
def setup() { 
driver.setConfigLoader(mockConfigLoader) 
driver.setFactory(mockRecAnalyticServiceFactory) 
driver.setHelpPrinter(mockHelpPrinter) 
} 
! 
def "입력 인자 테스트, 비정상 입력일 때, 1 리턴해야 함"() { 
expect: 
driver.run() == 1 
driver.run("--configFile") == 1 
driver.run("-c") == 1 
} 
! 
def "입력 인자 테스트, 비정상 입력일 때, 도움말 출력함"() { 
30 
when: 
driver.run() 
then: 
1 * mockHelpPrinter.print(_) 
} 
… 계속
def "협업 객체 연동 테스트"() { 
setup: 
def recommendationConfig = new RecommendationConfig() 
! 
when: 
def code = driver.run("--configFile", configPath) 
then: "설정 정보 로딩에 실패하면, 1을 리턴해야 함" 
1 * mockConfigLoader.loadRecommendationConfig(configPath) >> { throw new ConfigLoaderException() } 
code == 1 
31 
! 
when: 
code = driver.run("--configFile", configPath) 
then: "설정 정보 로딩에 성공했으나, 설정 정보를 바탕으로 추천서비스 객체 생성에 실패한 경우, 1을 리턴해야 함" 
1 * mockConfigLoader.loadRecommendationConfig(configPath) >> recommendationConfig 
1 * mockRecAnalyticServiceFactory.create(recommendationConfig) >> { throw new CreationException() } 
code == 1 
! 
when: 
code = driver.run("--configFile", configPath) 
then: "설정 정보 로딩에 성공, 추천서비스 객체 생성에 성공했으나, 추천 생성에 실패하면, 1을 리턴해야 함” 
1 * mockConfigLoader.loadRecommendationConfig(configPath) >> recommendationConfig 
1 * mockRecAnalyticServiceFactory.create(recommendationConfig) >> mockRecAnalyticService 
1 * mockRecAnalyticService.createRecommendations() >> { throw new AnalyticsServiceException() } 
code == 1 
! 
when: 
code = driver.run("--configFile", configPath) 
then: "설정 정보 로딩 성공, 추천서비스 객체 생성에 성공하고, 추천 생성에 성공하면, 0을 리턴해야 함" 
1 * mockConfigLoader.loadRecommendationConfig(configPath) >> recommendationConfig 
1 * mockRecAnalyticServiceFactory.create(recommendationConfig) >> mockRecAnalyticService 
1 * mockRecAnalyticService.createRecommendations() 
code == 0 
}
결과 설계물 
32
역할? 
33 
1. 명령행 기본 옵션 검증 
및 도움말 출력 
2. 설정 파일 읽어와 분석 
기능 실행
역할 분리 
34 
1. 명령행 기본 옵션 검증 
및 도움말 출력 
2. 설정 파일 읽어와 분석 
기능 실행
설정 파일 읽기 구현(계속) 
35 
{ 
rec: { 
fileSystem: { 
type: "local" 
}, 
activityDataStorage: { 
activityDataPath: "/some/path/viewlog" 
}, 
analyticService: { 
type: "mahout" 
}, 
mahoutRunner: { 
type: "shellScript", 
javaHome: "${env.JAVA_HOME}", 
… 
outputPathPrefix: "/some/path/result" 
}, 
resultStorage: { 
type: "console" 
} 
} 
} 
public class ConfigLoaderImpl implements ConfigLoader { 
@Override 
public RecConfig loadRecConfig(String configPath) { 
File file = new File(configPath); 
if (!file.exists()) 
throw new ConfigLoaderException(); 
! 
RecConfig recConfig = new RecConfig(); 
! 
ConfigValueHelper configValue = new ConfigValueHelper(file); 
configValue.set("rec/fileSystem/type", recConfig::setFileSystemType); 
configValue.set(“rec/fileSystem/namenode", 
value -> recConfig.setHdfsNameNode(value)); 
configValue.setBoolean("rec/mahoutRunner/mahoutLocal", 
recConfig::setMahoutRunnerMahoutLocal); 
! 
… 
return configValue; 
} 
}
36 
public class ConfigValueHelper { 
! 
private final JsonNode rootNode; 
! 
public ConfigValueHelper(File configFile) { 
ObjectMapper mapper = new ObjectMapper(); 
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); 
try { 
rootNode = mapper.readTree(configFile); 
} catch (IOException e) { throw new ConfigLoaderException(e); } 
} 
! 
public void set(String path, Consumer<String> consumer) { // setBoolean, setInt 등 유사하게 정의 
getJsonNodeValueByPath(path, jsonNode -> json.asText()).ifPresent(consumer); 
// getJsonNodeValueByPath(path, JsonNode::asBoolean).ifPresent(consumer); 예, setBoolean의 구현 
} 
! 
private <T> Optional<T> getJsonNodeValueByPath(String path, Function<JsonNode, T> valueGetter) { 
String[] fields = path.split("/"); 
JsonNode currentNode = rootNode; 
for (String field : fields) { 
JsonNode childNode = currentNode.get(field); 
if (childNode == null) { 
currentNode = null; 
break; 
} 
currentNode = childNode; 
} 
return currentNode == null ? Optional.<T>empty() : Optional.of(valueGetter.apply(currentNode)); 
} 
}
Factory 
설정 정보의 값에 따라 
모든 객체를 생성하고 조립 
public class RecAnalyticServiceFactoryImpl implements RecAnalyticServiceFactory { 
@Override 
public RecommendationAnalyticService create(RecommendationConfig config) { 
checkAnalyticServiceType(config); 
MahoutRecAnalyticService service = new MahoutRecAnalyticService(); 
ServiceColleboratorFactory factory = ServiceColleboratorFactory.create(config); 
service.setActivityDataStorage(factory.createActivityDataStorage()); 
service.setMahoutRunner(factory.createMahoutRunner()); 
service.setResultSaver(factory.createResultSaver()); 
return service; 
} 
37
기타 
• analytic-service 
• 런타임에 쉘 생성: Velocity 이용해서 템플릿 처리 
• CLI 
• 설정 파일 로딩시 플레이스홀더 변환 처리 
• ${env.환경변수}, ${자바시스템프로퍼티} 
• e2e 테스트 (로컬, 하둡 클러스터 환경) 
• 메이븐 이용 멀티 프로젝트 사용 
• API 위주 서브 프로젝트: spi-* 
• 주요 서브 프로젝트 
• mahout-analytic-service, simple-data-storage 
• cli 
• 배포판 생성 서브 프로젝트: zip 파일로 생성 
38
끝. 논의시작! 
39

Weitere ähnliche Inhalte

Was ist angesagt?

헷갈리는 자바스크립트 정리
헷갈리는 자바스크립트 정리헷갈리는 자바스크립트 정리
헷갈리는 자바스크립트 정리은숙 이
 
Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...
Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...
Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...Young-Beom Rhee
 
Startup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JSStartup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JSCirculus
 
프론트엔드스터디 E04 js function
프론트엔드스터디 E04 js function프론트엔드스터디 E04 js function
프론트엔드스터디 E04 js functionYoung-Beom Rhee
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성HyeonSeok Choi
 
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)Circulus
 
빠르게 활용하는 파이썬3 스터디(ch1~4)
빠르게 활용하는 파이썬3 스터디(ch1~4)빠르게 활용하는 파이썬3 스터디(ch1~4)
빠르게 활용하는 파이썬3 스터디(ch1~4)SeongHyun Ahn
 
도메인구현 KSUG 20151128
도메인구현 KSUG 20151128도메인구현 KSUG 20151128
도메인구현 KSUG 20151128beom kyun choi
 
Jquery javascript_ed10
Jquery javascript_ed10Jquery javascript_ed10
Jquery javascript_ed10hungrok
 
골때리는 자바스크립트 발표자료
골때리는 자바스크립트 발표자료골때리는 자바스크립트 발표자료
골때리는 자바스크립트 발표자료욱진 양
 
Java advancd ed10
Java advancd ed10Java advancd ed10
Java advancd ed10hungrok
 
객체 지향 발담그기 JCO 컨퍼런스 14회
객체 지향 발담그기 JCO 컨퍼런스 14회객체 지향 발담그기 JCO 컨퍼런스 14회
객체 지향 발담그기 JCO 컨퍼런스 14회beom kyun choi
 
파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229Yong Joon Moon
 
DDD로 복잡함 다루기
DDD로 복잡함 다루기DDD로 복잡함 다루기
DDD로 복잡함 다루기beom kyun choi
 
안드로이드 개발자를 위한 스위프트
안드로이드 개발자를 위한 스위프트안드로이드 개발자를 위한 스위프트
안드로이드 개발자를 위한 스위프트병한 유
 
Es2015 Simple Overview
Es2015 Simple OverviewEs2015 Simple Overview
Es2015 Simple OverviewKim Hunmin
 
Java 강의자료 ed11
Java 강의자료 ed11Java 강의자료 ed11
Java 강의자료 ed11hungrok
 
Java script 강의자료_ed13
Java script 강의자료_ed13Java script 강의자료_ed13
Java script 강의자료_ed13hungrok
 

Was ist angesagt? (20)

헷갈리는 자바스크립트 정리
헷갈리는 자바스크립트 정리헷갈리는 자바스크립트 정리
헷갈리는 자바스크립트 정리
 
Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...
Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...
Javascript 실행 가능한 코드(Executable Code)와 실행 콘텍스트(Execution Context), Lexical En...
 
Startup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JSStartup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JS
 
프론트엔드스터디 E04 js function
프론트엔드스터디 E04 js function프론트엔드스터디 E04 js function
프론트엔드스터디 E04 js function
 
JDK 변천사
JDK 변천사JDK 변천사
JDK 변천사
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성
 
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
 
빠르게 활용하는 파이썬3 스터디(ch1~4)
빠르게 활용하는 파이썬3 스터디(ch1~4)빠르게 활용하는 파이썬3 스터디(ch1~4)
빠르게 활용하는 파이썬3 스터디(ch1~4)
 
함수적 사고 2장
함수적 사고 2장함수적 사고 2장
함수적 사고 2장
 
도메인구현 KSUG 20151128
도메인구현 KSUG 20151128도메인구현 KSUG 20151128
도메인구현 KSUG 20151128
 
Jquery javascript_ed10
Jquery javascript_ed10Jquery javascript_ed10
Jquery javascript_ed10
 
골때리는 자바스크립트 발표자료
골때리는 자바스크립트 발표자료골때리는 자바스크립트 발표자료
골때리는 자바스크립트 발표자료
 
Java advancd ed10
Java advancd ed10Java advancd ed10
Java advancd ed10
 
객체 지향 발담그기 JCO 컨퍼런스 14회
객체 지향 발담그기 JCO 컨퍼런스 14회객체 지향 발담그기 JCO 컨퍼런스 14회
객체 지향 발담그기 JCO 컨퍼런스 14회
 
파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229
 
DDD로 복잡함 다루기
DDD로 복잡함 다루기DDD로 복잡함 다루기
DDD로 복잡함 다루기
 
안드로이드 개발자를 위한 스위프트
안드로이드 개발자를 위한 스위프트안드로이드 개발자를 위한 스위프트
안드로이드 개발자를 위한 스위프트
 
Es2015 Simple Overview
Es2015 Simple OverviewEs2015 Simple Overview
Es2015 Simple Overview
 
Java 강의자료 ed11
Java 강의자료 ed11Java 강의자료 ed11
Java 강의자료 ed11
 
Java script 강의자료_ed13
Java script 강의자료_ed13Java script 강의자료_ed13
Java script 강의자료_ed13
 

Ähnlich wie Ji 개발 리뷰 (신림프로그래머)

Nodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjsNodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjs기동 이
 
02.실행환경 실습교재(데이터처리)
02.실행환경 실습교재(데이터처리)02.실행환경 실습교재(데이터처리)
02.실행환경 실습교재(데이터처리)Hankyo
 
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기현철 조
 
5-4. html5 offline and storage
5-4. html5 offline and storage5-4. html5 offline and storage
5-4. html5 offline and storageJinKyoungHeo
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기NAVER Engineering
 
Node.js
Node.jsNode.js
Node.jsymtech
 
Design pattern 옵저버
Design pattern 옵저버Design pattern 옵저버
Design pattern 옵저버Sukjin Yun
 
20131217 html5
20131217 html520131217 html5
20131217 html5DK Lee
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Testbeom kyun choi
 
Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1 나무기술(주) 최유석 20170912
Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1  나무기술(주) 최유석 20170912Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1  나무기술(주) 최유석 20170912
Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1 나무기술(주) 최유석 20170912Yooseok Choi
 
C# Game Server
C# Game ServerC# Game Server
C# Game Serverlactrious
 
Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나JeongHun Byeon
 
Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)Choonghyun Yang
 
I phone 2 release
I phone 2 releaseI phone 2 release
I phone 2 releaseJaehyeuk Oh
 
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅NAVER D2
 
PHP Log Tracking with ELK & Filebeat part#2
PHP Log Tracking with ELK & Filebeat part#2PHP Log Tracking with ELK & Filebeat part#2
PHP Log Tracking with ELK & Filebeat part#2Juwon Kim
 
자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기Jeado Ko
 

Ähnlich wie Ji 개발 리뷰 (신림프로그래머) (20)

Nodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjsNodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjs
 
02.실행환경 실습교재(데이터처리)
02.실행환경 실습교재(데이터처리)02.실행환경 실습교재(데이터처리)
02.실행환경 실습교재(데이터처리)
 
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
 
5-4. html5 offline and storage
5-4. html5 offline and storage5-4. html5 offline and storage
5-4. html5 offline and storage
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
 
Scala for play
Scala for playScala for play
Scala for play
 
Node.js
Node.jsNode.js
Node.js
 
Html5 performance
Html5 performanceHtml5 performance
Html5 performance
 
Node.js 기본
Node.js 기본Node.js 기본
Node.js 기본
 
Design pattern 옵저버
Design pattern 옵저버Design pattern 옵저버
Design pattern 옵저버
 
20131217 html5
20131217 html520131217 html5
20131217 html5
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
 
Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1 나무기술(주) 최유석 20170912
Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1  나무기술(주) 최유석 20170912Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1  나무기술(주) 최유석 20170912
Bigquery와 airflow를 이용한 데이터 분석 시스템 구축 v1 나무기술(주) 최유석 20170912
 
C# Game Server
C# Game ServerC# Game Server
C# Game Server
 
Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나
 
Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)
 
I phone 2 release
I phone 2 releaseI phone 2 release
I phone 2 release
 
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
 
PHP Log Tracking with ELK & Filebeat part#2
PHP Log Tracking with ELK & Filebeat part#2PHP Log Tracking with ELK & Filebeat part#2
PHP Log Tracking with ELK & Filebeat part#2
 
자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기
 

Mehr von beom kyun choi

TDD 발담그기 @ 공감세미나
TDD 발담그기 @ 공감세미나TDD 발담그기 @ 공감세미나
TDD 발담그기 @ 공감세미나beom kyun choi
 
keras 빨리 훑어보기(intro)
keras 빨리 훑어보기(intro)keras 빨리 훑어보기(intro)
keras 빨리 훑어보기(intro)beom kyun choi
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀beom kyun choi
 
Ddd start 부록 지앤선&ksug
Ddd start 부록 지앤선&ksugDdd start 부록 지앤선&ksug
Ddd start 부록 지앤선&ksugbeom kyun choi
 
Event source 학습 내용 공유
Event source 학습 내용 공유Event source 학습 내용 공유
Event source 학습 내용 공유beom kyun choi
 
ALS WS에 대한 이해 자료
ALS WS에 대한 이해 자료ALS WS에 대한 이해 자료
ALS WS에 대한 이해 자료beom kyun choi
 
리뷰의 기술 소개
리뷰의 기술 소개리뷰의 기술 소개
리뷰의 기술 소개beom kyun choi
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해beom kyun choi
 
자바8 스트림 API 소개
자바8 스트림 API 소개자바8 스트림 API 소개
자바8 스트림 API 소개beom kyun choi
 
자바8 람다식 소개
자바8 람다식 소개자바8 람다식 소개
자바8 람다식 소개beom kyun choi
 
하둡2 YARN 짧게 보기
하둡2 YARN 짧게 보기하둡2 YARN 짧게 보기
하둡2 YARN 짧게 보기beom kyun choi
 
차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)beom kyun choi
 
Hive 입문 발표 자료
Hive 입문 발표 자료Hive 입문 발표 자료
Hive 입문 발표 자료beom kyun choi
 
하둡 맵리듀스 훑어보기
하둡 맵리듀스 훑어보기하둡 맵리듀스 훑어보기
하둡 맵리듀스 훑어보기beom kyun choi
 

Mehr von beom kyun choi (20)

TDD 발담그기 @ 공감세미나
TDD 발담그기 @ 공감세미나TDD 발담그기 @ 공감세미나
TDD 발담그기 @ 공감세미나
 
keras 빨리 훑어보기(intro)
keras 빨리 훑어보기(intro)keras 빨리 훑어보기(intro)
keras 빨리 훑어보기(intro)
 
DDD 준비 서문래
DDD 준비 서문래DDD 준비 서문래
DDD 준비 서문래
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀
 
Ddd start 부록 지앤선&ksug
Ddd start 부록 지앤선&ksugDdd start 부록 지앤선&ksug
Ddd start 부록 지앤선&ksug
 
Event source 학습 내용 공유
Event source 학습 내용 공유Event source 학습 내용 공유
Event source 학습 내용 공유
 
Spring Boot 소개
Spring Boot 소개Spring Boot 소개
Spring Boot 소개
 
ALS WS에 대한 이해 자료
ALS WS에 대한 이해 자료ALS WS에 대한 이해 자료
ALS WS에 대한 이해 자료
 
리뷰의 기술 소개
리뷰의 기술 소개리뷰의 기술 소개
리뷰의 기술 소개
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
 
자바8 스트림 API 소개
자바8 스트림 API 소개자바8 스트림 API 소개
자바8 스트림 API 소개
 
자바8 람다식 소개
자바8 람다식 소개자바8 람다식 소개
자바8 람다식 소개
 
Zookeeper 소개
Zookeeper 소개Zookeeper 소개
Zookeeper 소개
 
하둡2 YARN 짧게 보기
하둡2 YARN 짧게 보기하둡2 YARN 짧게 보기
하둡2 YARN 짧게 보기
 
차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)차원축소 훑어보기 (PCA, SVD, NMF)
차원축소 훑어보기 (PCA, SVD, NMF)
 
Storm 훑어보기
Storm 훑어보기Storm 훑어보기
Storm 훑어보기
 
Hive 입문 발표 자료
Hive 입문 발표 자료Hive 입문 발표 자료
Hive 입문 발표 자료
 
HBase 훑어보기
HBase 훑어보기HBase 훑어보기
HBase 훑어보기
 
Flume 훑어보기
Flume 훑어보기Flume 훑어보기
Flume 훑어보기
 
하둡 맵리듀스 훑어보기
하둡 맵리듀스 훑어보기하둡 맵리듀스 훑어보기
하둡 맵리듀스 훑어보기
 

Ji 개발 리뷰 (신림프로그래머)

  • 1. JI 구현 이야기, 리뷰 1 신림프로그래머 최범균, 2014-10-14
  • 2. 내용 • 리뷰1 (오늘) • 시작: 기능 목록 초안, 초기 컴포넌트 식별 • 0.1 개발: 핵심 기능 구현, CLI 구현 • 일부 설계 과정, 일부 구현 결과물 • 리뷰2 (다음) • 0.2 개발: 웹 구현 • 일부 설계/구현 결과물 • 테스트 관련 생각할 거리: 단위 vs 통합, E2E 테스트 2
  • 3. 이야기의 시작, 반복(중복) [커맨드라인] 행렬 분해 [커맨드라인] 사용자용 데이터 생성 [커맨드라인] 아이템용 데이터 생성 [자바] 데이터 변환 후 저장 반복 여러 알고리즘에 대해 과정 유사 3
  • 4. 자동화 욕구 4 [커맨드라인] 행렬 분해 [커맨드라인] 사용자용 데이터 생성 [커맨드라인] 아이템용 데이터 생성 [자바] 데이터 변환 후 저장 반복 자동화 [프로세스] 경로, 타입, 숫자 등 많은 설정 단계별 실패 확인 데이터 변환 과정, 실패 처리 등 프로그램 설정 정보 경로, 타입, 숫자 등
  • 5. 기능 목록 버전 내용 0.1 1개 알고리즘에 대한 서비스 구현 콘솔에서 서비스 실행: 설정 파일 사용 5 0.2 웹 인터페이스 - 작업 설정 생성 - 작업 설정을 이용해서 백그라운드로 작업 실행 - 실행 내역 추적 0.3 다른 알고리즘 추가
  • 7. 주요 모델 • 분석 실행(프로세스) • 입력: 실행에 필요한 데이터 • 알고리즘 실행: 입력을 이용해서 특정 알고리즘 수행 • 결과 저장: 실행 결과를 원하는 형태로 보관 7
  • 8. 0.1 버전 상위 수준 설계 초안 입력 데이터 경로 제공 결과 보관 8 주요 인터페이스 핵심 컴포넌트 알고리즘 실행 설정 파일을 이용해서 분석 기능 실행
  • 9. 요구 상세 • Mahout을 이용해서 분석 1개 실행 • 로컬에 설치된 Mahout을 사용 • 기능 실행은 쉘스크립트를 이용해서 실행 • Mahout 실행 환경 • 로컬: 로컬 실행, 로컬 입력 파일 • 하둡: 하둡 클러스터(MR) 실행, HDFS 입력 파일 • 로컬에 설치된 하둡을 사용 • 결과 저장 • 로컬에 JSON 파일, (단순 테스트 목적) 콘솔 출력 9
  • 10. 요구 상세 • Mahout을 이용해서 분석 1개 실행 • 로컬에 설치된 Mahout을 사용 • 기능 실행은 쉘스크립트를 이용해서 실행 10 • Mahout 실행 환경 JVM에서 외부 쉘을 실행 사용자가 쉘을 마음대로 변경하면 안 되므로, 런타임에 쉘을 동적 생성 • 로컬: 로컬 실행, 로컬 입력 파일 • 하둡: 하둡 클러스터(MR) 실행, HDFS 입력 파일 • 로컬에 설치된 하둡을 사용 • 결과 저장 • 로컬에 JSON 파일, (단순 테스트 목적) 콘솔 출력 Mahout 실행시, 하둡 클러스 터 설정 가능하도록 입력 파일로 로컬 경로와 HDFS 경로를 사용할 수 있도록 하둡 결과 파일을 읽어와 처리
  • 11. 상위 수준 핵심 로직은 11 입력을 이용함 Mahout을 실행함 결과를 저장함
  • 12. 핵심 부분 설계 시작 12 class RecAnalyticsServiceSpec extends Specification { def “생성”() { def RecAnalyticsService service = new RecAnalyticsService() } } public class RecAnalyticsService { !} * 공간의 제약으로 이름 등 일부 변경 class RecAnalyticsServiceSpec extends Specification { def service = new RecAnalyticService() ! def “로그가 없을 경우”() { when: service.createRecommendation() then: thrown(NoDataException) } } public class RecAnalyticsService { public void createRecommendation() { throw new NoDataException(); } } ! public class NoDataException extends RuntimeEx…{ }
  • 13. 13 def service = new RecAnalyticService() ! def “로그가 없을 경우”() { when: service.createRecommendation() then: thrown(NoDataException) } ! def “로그가 있다면”() { setup: def ActivityStorage activityStorage = Mock() service.setActivityStorage(activityStorage) when: service.createRecomendation() then: “로그가 있으면 NoDataException이 발생하지 않음” 1 * activityStorage.hasActivityData() >> true notThrown(NoDataException) } ! public interface ActivityStorage { boolean hasActivityData(); } ! public class RecAnalyticsService { private ActivityStorage activityStorage; ! public void createRecommendation() { if (!activityStorage.hasActivityData()) throw new NoDataException(); } ! public void setActivityStorage(ActivityStorage …) { this.activityStorage = …; } }
  • 14. 14 def service = new RecAnalyticService() def ActivityStorage mockActivityStorage = Mock() ! def setup() { service.setActivityStorage(mockActivityStorage) } ! def “로그가 없을 경우”() { when: service.createRecommendation() then: thrown(NoDataException) } ! def “로그가 있다면”() { setup: mockActivityStorage.hasActivityData() >> true when: service.createRecomendation() then: “로그가 있으면 NoDataException이 발생하지 않음” notThrown(NoDataException) }
  • 15. 15 def service = new RecAnalyticService() def ActivityStorage mockActivityStorage = Mock() def MahoutRunner mockMH = Mock() ! def setup() { service.setActivityStorage(mockActivityStorage) service.setMahoutRunner(mockMH) } ! def “로그가 있다면”() { setup: mockActivityStorage.hasActivityData() >> true ! when: service.createRecomendation() then: “로그가 있으면 NoDataException이 발생하지 않음” notThrown(NoDataException) ! when: service.createRecomendation() then: “Mahout 이용 분석 실행 실패하면, 익셉션 발생” 1 * mockMH.run() >> { throw new MREx() } thrown(AnalyticServiceException) ! } public interface MahoutRunner { public void run(); } ! public class RecAnalyticsService { private ActivityStorage activityStorage; private MahoutRunner mahoutRunner; ! public void createRecommendation() { if (!activityStorage.hasActivityData()) throw new NoDataException(); try { mahoutRunner.run(); } catch(MREx ex) { throw new AnalyticServicException(ex); } } … // setter 추가 } ! public class MREx extends RuntimeException {} public class AnalyticServiceException … {…}
  • 16. 16 def service = new RecAnalyticService() def ActivityStorage mockActivityStorage = Mock() def MahoutRunner mockMH = Mock() def ResultSaver mockSaver = Mock() … // setup() def “로그가 있다면”() { setup: mockActivityStorage.hasActivityData() >> true def RecResult recResult = Mock() def UserItemSource uiSource = Mock() def SimItemSource siSource = Mock() recResult.getUserItem() >> uiSource recResult.getSimItem() >> recResult …// 다른 when-then when: service.createRecomendation() then: “Mahout 이용 분석 실행 실패하면, 익셉션 발생” 1 * mockMH.run(_) >> { throw new MREx() } thrown(AnalyticServiceException) ! when: service.createRecomendation() then: “Mahout 이용 분석 실행 성공하면, 결과 저장 시도” mockMH.run(mockActivityStorage) >> recResult 1 * mockSaver.saveUserItem(uiSource) 1 * mockSaver.saveSimItem(siSource) } public interface ResultSaver { void saveUserItem(UserItemSource source); void saveSimItem(SimItemSource source); } public interface UserItemSource {} public interface SimItemSource {} ! public interface RecResult { UserItemSource getUserItem(); SimItemSource getSimItem(); } ! public interface MahoutRunner { public void run(ActivityStorage activityStorage); } public class RecAnalyticsService { … private ResultSaver resultSaver; ! public void createRecommendation() { if (!activityStorage.hasActivityData()) throw new NoDataException(); RecResult result; try { result = mahoutRunner.run(activityStorage); } catch(MREx ex) { throw new AnalyticServicException(ex); } resultSaver.saveUserItem(result.getUserItem()); resultSaver.saveSimItem(result.getSimItem()) } … // setter
  • 17. 추가적인 실패/성공 시나리오를 진행하면서 점진적으로 구현/설계 완성 17
  • 18. 핵심 부분 설계 결과 18
  • 20. 설계 결과물 모듈 배치 결과 20 인터페이스 추출
  • 21. 구현/설계 진행 계속 유사한 방식으로 MahoutRunner 콘크리트 클래스의 구현/설계를 점진적으로 진행 21
  • 22. 인터페이스 정의를 어떻게? 22 이 인터페이스의 메서드를 정의하려면?
  • 23. 사용자 입장에서 메서드 도출 UserItemSource와 SimItemSource의 사용자 23 ▼ “ResultSaver의 콘크리트 클래스” public class ConsoleResultSaver implements … { ! public void saveUserItem(UserItemSource source) { List<UserItem> userItems = source.getUserItems(); for (UserItem ui: userItems) { … } } ! public void saveUserItem(UserItemSource source) { Iterator<UserItem> userItems = source.getIterator(); while(userItems.hasNext()) { UserItem ui = userItems.next(); … } } 1안 2안 대량 결과 데이터를 처리할 수 있어야 하기 때문에, 2안 선택
  • 24. 2안으로 구현 쉘 실행 결과로 만들어진 파일로부터 데이터를 읽어오는 Iterator 구현 필요 24 public interface UserItemSource { Iterator<UserItem> iterator(); }
  • 25. 2안, Iterator 구현 예 25 private class FileSystemSimItemIterator implements Iterator<SimItem> { private final SequenceFile.Reader reader; private final IntWritable key; private final VectorWritable value ; private SimItem nextItem; ! public FileSystemSimItemIterator( FileSystem fileSystem, Path simItemFile) { reader = createSequenceFileReader( fileSystem, similarItemFile, new Configuration()); key = new IntWritable(); value = new VectorWritable(); moveNext(); } ! @Override public boolean hasNext() { return nextItem != null; } ! @Override public SimilarItems next() { checkNextItemExists(); SimItem result = nextItem; moveNext(); return result; } private void checkNextItemsExists() { if (nextItem == null) throw new NoSuchElementException(); } ! private void moveNext() { boolean isNextRead = readNextKeyValue(); if (!isNextRead) { closeReader(); nextItem = null; return; } createNextItems(); } ! private boolean readNextKeyValue() { try { return reader.next(key, value); } catch (…) {…} } ! private void createNextItem() { List<ItemValue> allSimilarItems = new ArrayList<>(); for (Vector.Element ele : value.get().nonZeroes()) allSimilarItems.add(new ItemValue(ele.index(), ele.get())); nextItem = new SimItem(key.get(), allSimilarItems); } private void closeReader() { … } }
  • 28. CLI 요구 사항 • 작업 실행에 필요한 정보를 파일로 설정 • JSON 형식 사용 • 콘솔에서 설정 파일 경로를 지정해서 작업 실행 • 예) ji.sh -c recommendation.conf 28
  • 29. 핵심은 • 설정 파일을 읽어와 • 알맞게 분석 서비스 객체(예, RecAnalyticService)를 구 성한 뒤 • 분석 서비스를 실행 29
  • 30. 핵심 기능 구현/설계에 쓰인 테스트 코드(계속) class RecommendationDriverSpec extends Specification { def RecommendationDriver driver = new RecommendationDriver(); def ConfigLoader mockConfigLoader = Mock() def RecommendationAnalyticServiceFactory mockRecAnalyticServiceFactory = Mock() def RecommendationAnalyticService mockRecAnalyticService = Mock() def HelpPrinter mockHelpPrinter = Mock() def String configPath = "myPath" ! def setup() { driver.setConfigLoader(mockConfigLoader) driver.setFactory(mockRecAnalyticServiceFactory) driver.setHelpPrinter(mockHelpPrinter) } ! def "입력 인자 테스트, 비정상 입력일 때, 1 리턴해야 함"() { expect: driver.run() == 1 driver.run("--configFile") == 1 driver.run("-c") == 1 } ! def "입력 인자 테스트, 비정상 입력일 때, 도움말 출력함"() { 30 when: driver.run() then: 1 * mockHelpPrinter.print(_) } … 계속
  • 31. def "협업 객체 연동 테스트"() { setup: def recommendationConfig = new RecommendationConfig() ! when: def code = driver.run("--configFile", configPath) then: "설정 정보 로딩에 실패하면, 1을 리턴해야 함" 1 * mockConfigLoader.loadRecommendationConfig(configPath) >> { throw new ConfigLoaderException() } code == 1 31 ! when: code = driver.run("--configFile", configPath) then: "설정 정보 로딩에 성공했으나, 설정 정보를 바탕으로 추천서비스 객체 생성에 실패한 경우, 1을 리턴해야 함" 1 * mockConfigLoader.loadRecommendationConfig(configPath) >> recommendationConfig 1 * mockRecAnalyticServiceFactory.create(recommendationConfig) >> { throw new CreationException() } code == 1 ! when: code = driver.run("--configFile", configPath) then: "설정 정보 로딩에 성공, 추천서비스 객체 생성에 성공했으나, 추천 생성에 실패하면, 1을 리턴해야 함” 1 * mockConfigLoader.loadRecommendationConfig(configPath) >> recommendationConfig 1 * mockRecAnalyticServiceFactory.create(recommendationConfig) >> mockRecAnalyticService 1 * mockRecAnalyticService.createRecommendations() >> { throw new AnalyticsServiceException() } code == 1 ! when: code = driver.run("--configFile", configPath) then: "설정 정보 로딩 성공, 추천서비스 객체 생성에 성공하고, 추천 생성에 성공하면, 0을 리턴해야 함" 1 * mockConfigLoader.loadRecommendationConfig(configPath) >> recommendationConfig 1 * mockRecAnalyticServiceFactory.create(recommendationConfig) >> mockRecAnalyticService 1 * mockRecAnalyticService.createRecommendations() code == 0 }
  • 33. 역할? 33 1. 명령행 기본 옵션 검증 및 도움말 출력 2. 설정 파일 읽어와 분석 기능 실행
  • 34. 역할 분리 34 1. 명령행 기본 옵션 검증 및 도움말 출력 2. 설정 파일 읽어와 분석 기능 실행
  • 35. 설정 파일 읽기 구현(계속) 35 { rec: { fileSystem: { type: "local" }, activityDataStorage: { activityDataPath: "/some/path/viewlog" }, analyticService: { type: "mahout" }, mahoutRunner: { type: "shellScript", javaHome: "${env.JAVA_HOME}", … outputPathPrefix: "/some/path/result" }, resultStorage: { type: "console" } } } public class ConfigLoaderImpl implements ConfigLoader { @Override public RecConfig loadRecConfig(String configPath) { File file = new File(configPath); if (!file.exists()) throw new ConfigLoaderException(); ! RecConfig recConfig = new RecConfig(); ! ConfigValueHelper configValue = new ConfigValueHelper(file); configValue.set("rec/fileSystem/type", recConfig::setFileSystemType); configValue.set(“rec/fileSystem/namenode", value -> recConfig.setHdfsNameNode(value)); configValue.setBoolean("rec/mahoutRunner/mahoutLocal", recConfig::setMahoutRunnerMahoutLocal); ! … return configValue; } }
  • 36. 36 public class ConfigValueHelper { ! private final JsonNode rootNode; ! public ConfigValueHelper(File configFile) { ObjectMapper mapper = new ObjectMapper(); mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); try { rootNode = mapper.readTree(configFile); } catch (IOException e) { throw new ConfigLoaderException(e); } } ! public void set(String path, Consumer<String> consumer) { // setBoolean, setInt 등 유사하게 정의 getJsonNodeValueByPath(path, jsonNode -> json.asText()).ifPresent(consumer); // getJsonNodeValueByPath(path, JsonNode::asBoolean).ifPresent(consumer); 예, setBoolean의 구현 } ! private <T> Optional<T> getJsonNodeValueByPath(String path, Function<JsonNode, T> valueGetter) { String[] fields = path.split("/"); JsonNode currentNode = rootNode; for (String field : fields) { JsonNode childNode = currentNode.get(field); if (childNode == null) { currentNode = null; break; } currentNode = childNode; } return currentNode == null ? Optional.<T>empty() : Optional.of(valueGetter.apply(currentNode)); } }
  • 37. Factory 설정 정보의 값에 따라 모든 객체를 생성하고 조립 public class RecAnalyticServiceFactoryImpl implements RecAnalyticServiceFactory { @Override public RecommendationAnalyticService create(RecommendationConfig config) { checkAnalyticServiceType(config); MahoutRecAnalyticService service = new MahoutRecAnalyticService(); ServiceColleboratorFactory factory = ServiceColleboratorFactory.create(config); service.setActivityDataStorage(factory.createActivityDataStorage()); service.setMahoutRunner(factory.createMahoutRunner()); service.setResultSaver(factory.createResultSaver()); return service; } 37
  • 38. 기타 • analytic-service • 런타임에 쉘 생성: Velocity 이용해서 템플릿 처리 • CLI • 설정 파일 로딩시 플레이스홀더 변환 처리 • ${env.환경변수}, ${자바시스템프로퍼티} • e2e 테스트 (로컬, 하둡 클러스터 환경) • 메이븐 이용 멀티 프로젝트 사용 • API 위주 서브 프로젝트: spi-* • 주요 서브 프로젝트 • mahout-analytic-service, simple-data-storage • cli • 배포판 생성 서브 프로젝트: zip 파일로 생성 38