객체 지향 발담그기 jco 컨퍼런스 14회

43
객체 지향 발담그기 최범균, [email protected]

Upload: beom-kyun-choi

Post on 12-Jun-2015

7.143 views

Category:

Technology


6 download

DESCRIPTION

2014년 JCO 컨퍼런스 14회, 객체 지향 발들이기 세션 발표 자료.

TRANSCRIPT

Page 1: 객체 지향 발담그기 JCO 컨퍼런스 14회

객체 지향 발담그기 최범균, [email protected]

Page 2: 객체 지향 발담그기 JCO 컨퍼런스 14회

오늘의 주제

추상화 + 유연함

Page 3: 객체 지향 발담그기 JCO 컨퍼런스 14회

출처: http://www.flickr.com/photos/42402605@N07/5693120074

여러 클라우드에 올라간 파일들을 통합 관리하는 앱을 만들어보자.

Page 4: 객체 지향 발담그기 JCO 컨퍼런스 14회

빨리 선보이기 위한 최초 0.1 버전

•  대상 클라우드 o  드롭박스, 박스

•  주요 기능 o  각 클라우드의 목록 조회하기 o  각 클라우드에서 파일 다운로드 하기

Page 5: 객체 지향 발담그기 JCO 컨퍼런스 14회

파일 정보 표현

public class FileInfo { private CloudId cloudId; private String name; private long length; … // get 메서드 }

public enum CloudId { DROPBOX, BOX; }

Page 6: 객체 지향 발담그기 JCO 컨퍼런스 14회

파일 목록 조회 public List<FileInfo> getFileInfos(CloudId cloudId) { if (cloudId == CloudId.DROPBOX) { DropboxClient dc = …; List<DbFile> dbFiles = db.getFiles(); List<FileInfo> result = new ArrayList<>(); for (DbFile dbFile : dbFiles) { FileInfo fi = new FileInfo(); fi.setCloudId(CloudId.DROPBOX); … result.add(fi); } return result; } else if (cloudId == CloudId.BOX) { BoxService boxSvc = …; … // } }

Page 7: 객체 지향 발담그기 JCO 컨퍼런스 14회

파일 다운로드 (계속) public void download(FileInfo file, File localTarget) { if (file.getCloudId() == CloudId.DROPBOX) { DropboxClient dc = …; FileOutputStream out = new FileOutputStream(localTarget); dc.copy(file.getFileId(), out); out.close(); } else if (file.getCloudId() == CloudId.BOX) { // TODO } }

public class FileInfo { private CloudId cloudId; private String fileId; private String name; ...

for (DbFile dbFile : dbFiles) { FileInfo fi = new FileInfo(); fi.setFileId(dbFile.getId()); fi.setCloudId(cloudId); … }

동작하도록 반영

Page 8: 객체 지향 발담그기 JCO 컨퍼런스 14회

파일 다운로드 public void download(FileInfo file, File localTarget) { if (file.getCloudId() == CloudId.DROPBOX) { ... } else if (file.getCloudId() == CloudId.BOX) { BoxService boxSvc = …; InputStream is = boxSvc.getInputStream(file.getId()); FileOutputStream out = new FileOutputStream(localTarget); CopyUtil.copy(is, out); } }

Page 9: 객체 지향 발담그기 JCO 컨퍼런스 14회

야호!

•  무사히 동작하는 0.1 버전 완성 •  0.5 버전을 향해

o  기능 확대 §  파일 업로드 §  파일 삭제 §  검색 §  이미지 미리보기

Page 10: 객체 지향 발담그기 JCO 컨퍼런스 14회

0.5v 기능 추가 public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } }

public void delete(String fileId, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } }

public List<FileInfo> search(String query, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } }

Page 11: 객체 지향 발담그기 JCO 컨퍼런스 14회

음...

•  무사히 동작하는 0.5 버전 완성 •  베타 버전을 향해

o  클라우드 추가 §  S클라우드 §  D클라우드 §  N클라우드 §  ...

o  기능 추가 §  클라우드 간 파일 복사

Page 12: 객체 지향 발담그기 JCO 컨퍼런스 14회

과정 public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } }

public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } else if (cid == CloudId.BOX) { ... } }

public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } else if (cid == CloudId.SCLOUD) { ... } }

public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } else if (cid == CloudId.SCLOUD) { ScloudClient scClient = …; ... } }

코드복사

조건변경

구현변경

Page 13: 객체 지향 발담그기 JCO 컨퍼런스 14회

흔한 실수 public List<FileInfo> getFileInfos(CloudId cloudId) { if (cloudId == CloudId.DROPBOX) { … } else if (cloudId == CloudId.BOX) { … } else if (cloudId == CloudId.SCLOUD) { … } else if (cloudId == CloudId.DCLOUD) { … fi.setCloudId(CloudId.SCLOUD); … } else if (cloudId == CloudId.NCLOUD) { … } }

public void delete(String fileId, CloudId cid) { if (cid == CloudId.DROPBOX) { ... return result; } else if (cloudId == CloudId.BOX) { … // } else if (cloudId == CloudId.SCLOUD) { … // alert(“파일이 존재하지 않습니다.”); ... } else if (cloudId == CloudId.DCLOUD) { … alert(“파일이 존재하지 않습니다.”); ... } else if (cloudId == CloudId.NCLOUD) { … // } }

OO;;

음.. 파일이 있는데,

왜 없다 그러지?

Page 14: 객체 지향 발담그기 JCO 컨퍼런스 14회

클라우드 간 파일 복사 기능 고려사항

•  URL로부터 복사해서 가져오는 클라우드 존재 o  그런데, URL을 제공하지 않는 클라우드 존재

•  InputStream을 제공하면 되는 클라우드 존재 o  그런데, InputStream을 제공하지 않는 클라우드 존재

•  로컬 File을 제공해야 하는 클라우드 존재

Page 15: 객체 지향 발담그기 JCO 컨퍼런스 14회

클라우드 간 파일 복사 기능 구현 ... public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { // URL로 복사하기 지원 DropBoxClient dbClient = …; if (from == CloudId.BOX) { dbClient.copyFromUrl(“http://www.box.com/files/”+fileInfo.getFileId()); } else if (from == CloudId.SCLOUD) { ScloudClient sClient = …; InputStream is = sClient.getInputStream(fileInfo.getFileId()); dbClient.copyFromInputStream(is, fileInfo.getName()); } else if (from ==CloudId.DCLOUD) { dbClient.copyFromUrl(“http://www.dcloud.com/getfile?fileId=”+fileInfo.getFileId()); } else if (from == CloudId.NCLOUD) { NCloudClient nClient = …; File temp = File.createTemp(); nClient.save(fileInfo.getFileId(), temp); InputStream is = new FileInputStream(temp); dbClient.copyFromInputStream(is, fileInfo.getName()); } } else if (to == CloudId.BOX) { // 코드 계속 ….

Page 16: 객체 지향 발담그기 JCO 컨퍼런스 14회

클라우드 간 파일 복사 기능 구현 ... } else if (to == CloudId.BOX) { // URL로 복사하기 지원 BoxService boxService = …; if (from == CloudId.DROPBOX) { boxService.createFileByUrl(“http://www.dropbox.com/box/files/”+fileInfo.getFileId()); } else if (from == CloudId.SCLOUD) { ScloudClient sClient = …; InputStream is = sClient.getInputStream(fileInfo.getFileId()); boxService.uploadFile(is, fileInfo.getName()); } else if (from ==CloudId.DCLOUD) { boxService.createFileByUrl(“http://www.dcloud.com/getfile?fileId=”+fileInfo.getFileId()); } else if (from == CloudId.NCLOUD) { NCloudClient nClient = …; File temp = File.createTemp(); nClient.save(fileInfo.getFileId(), temp); boxService.uploadFile(temp, fileInfo.getName()); } } else if (to == CloudId.SCLOUD) { // 코드 계속 ….

Page 17: 객체 지향 발담그기 JCO 컨퍼런스 14회

클라우드 간 파일 복사 기능 구현 ... } else if (to == CloudId.SCLOUD) { ScloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.DCLOUD) { // 코드 계속 ….

Page 18: 객체 지향 발담그기 JCO 컨퍼런스 14회

클라우드 간 파일 복사 기능 코드 (if-else 구조만 표시)

public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { // URL로 복사하기 지원 DropBoxClient dbClient = …; if (from == CloudId.BOX) { ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.BOX) { // URL로 복사하기 지원 BoxService boxService = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.SCLOUD) { ScloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... }

} else if (to == CloudId.DCLOUD) { DcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.NCLOUD) { NcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.DCLOUD) { … ... } } } .

Page 19: 객체 지향 발담그기 JCO 컨퍼런스 14회

새로운 요구사항 public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { // URL로 복사하기 지원 DropBoxClient dbClient = …; if (from == CloudId.BOX) { ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.BOX) { // URL로 복사하기 지원 BoxService boxService = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.SCLOUD) { ScloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... }

} else if (to == CloudId.DCLOUD) { DcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.NCLOUD) { NcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.DCLOUD) { … ... } } } .

클라우드 10개 추가

Page 20: 객체 지향 발담그기 JCO 컨퍼런스 14회

1개 클라우드 추가 비용

시간

개발 비용

예전엔 빨리 했는데…...

Page 21: 객체 지향 발담그기 JCO 컨퍼런스 14회

개발시간이 왜

증가하는가?

Page 22: 객체 지향 발담그기 JCO 컨퍼런스 14회

•  코드 구조가 길어지고 복잡해짐 o  새로운 클라우드 추가시 모든 메서드에 새로운 if 블록 추가

§  중첩 if-else는 복잡도 배로 증가 §  if-else가 많을수록 진척 더딤 (신중 모드)

•  관련 코드가 여러 곳에 분산됨 o  한 클라우드 처리와 관련된 코드가 여러 메서드에 흩어짐

•  결과적으로, 코드 가독성과 분석 속도 저하 o  코드 추가에 따른 노동 시간 증가 o  실수하기 쉽고, 이로 인한 불필요한 디버깅 시간 증가

개발 시간 증가 이유

Page 23: 객체 지향 발담그기 JCO 컨퍼런스 14회

해결책

데이터와기능을함께갖는

객체로추상화Abstraction

Page 24: 객체 지향 발담그기 JCO 컨퍼런스 14회

DROPBOX

추상화

BOX

SCLOUD

DCLOUD

NCLOUD

클라우드 파일시스템

Page 25: 객체 지향 발담그기 JCO 컨퍼런스 14회

클라우드 파일 시스템 설계

Page 26: 객체 지향 발담그기 JCO 컨퍼런스 14회

DROPBOX용 구현 (계속)

public class DropBoxFileSystem implements CloudFileSystem { private DropBoxClient dbClient = new DropBoxClient(...); @Override public List<CloudFile> getFiles() { List<DbFile> dbFiles = dbClient.getFiles(); List<CloudFile> results = new ArrayList<>(dbFiles.size()); for (DbFile file : dbFiles) { DropBoxCloudFile cf = new DropBoxCloudFile(file, dbClient); results.add(cf); } return results; }

Page 27: 객체 지향 발담그기 JCO 컨퍼런스 14회

DROPBOX용 구현 public class DropBoxCloudFile implements CloudFile { private DropBoxClient dbClient; private DbFile dbFile; public DropBoxCloudFile( DbFile dbFile, dbClient) { this.dbFile = dbFile; this.dbClient = dbClient; } public String getId() { return dbFile.getId(); } public boolean hasUrl() { return true; } public String getUrl() { return dbFile.getFileUrl(); }

public String getName() { return dbFile.getFileName(); } public InputStream getInputStream() { return dbClient .createStreamOfFile(dbFile); } public void write(OutputStream out) { ... } public void delete() { dbClient.deleteFile(dbFile.getId()); } … }

Page 28: 객체 지향 발담그기 JCO 컨퍼런스 14회

v0.1의 파일 목록/다운로드 기능 구현

public List<CloudFile> getFileInfos(CloudId cloudId) { CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(cloudId); return fileSystem.getFiles(); } public void download(CloudFile file, File localTarget) { file.write(new FileOutputStream(localTarget)); }

현재 드롭박스 지원만 구현 된 상태

Page 29: 객체 지향 발담그기 JCO 컨퍼런스 14회

BOX 클라우드 지원 추가

Page 30: 객체 지향 발담그기 JCO 컨퍼런스 14회

v0.1의 파일 목록/다운로드 기능 구현 (다시)

박스 지원도 반영 (코드 그대로네… 응?)

public List<CloudFile> getFileInfos(CloudId cloudId) { CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(cloudId); return fileSystem.getFiles(); } public void download(CloudFile file, File localTarget) { file.write(new FileOutputStream(localTarget)); }

Page 31: 객체 지향 발담그기 JCO 컨퍼런스 14회

복잡했던 파일 복사 기능의 변신: copyFrom(CloudFile file) 추가

public void copy(CloudFile file, CloudId target) { CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(target); fileSystem.copyFrom(file); }

Page 32: 객체 지향 발담그기 JCO 컨퍼런스 14회

복잡했던 파일 복사 기능의 변신: 파일시스템 별 copyFrom 메서드

-- DropBoxFileSystem private DropBoxClient dbClient = new DropBoxClient(...); public void copyFrom(CloudFile file) { if (file.hasUrl()) dbClient.copyFromUrl(file.getUrl()); else dbClient.copyFromInputStream(file.getInputStream(), file.getName()); }

-- NcloudFileSystem private NcloudClient nClient = new NCloudClient(...); public void copyFrom(CloudFile file) { File tempFile = File.createTemp(); file.write(new FileOutputStream(tempFile)); nClient.upload(tempFile, file.getName()); }

Page 33: 객체 지향 발담그기 JCO 컨퍼런스 14회

추상화를 잘 했더니 public class CloudFileManeger { public List<CloudFile> getFileInfos(CloudId cloudId) { CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(cloudId); return fileSystem.getFiles(); } public void download(CloudFile file, File localTarget) { file.write(new FileOutputStream(localTarget)); } public void copy(CloudFile file, CloudId target) { CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(target); fileSystem.copyFrom(file); } … ... }

추상화된 타입으로만 핵심 기능 구현

가능

Page 34: 객체 지향 발담그기 JCO 컨퍼런스 14회

추상화를 잘 했더니

코드 수정없이

새로운 클라우드 지원을

추가

Page 35: 객체 지향 발담그기 JCO 컨퍼런스 14회

이것이 바로 OCP Open-Closed principle

Closed for Modification 수정엔 닫혀 있음

Open for Extenstion 확장에 열려 있음

Page 36: 객체 지향 발담그기 JCO 컨퍼런스 14회

추상화를 잘 했더니: 기능이 만들어 짐

•  URL 제공 여부 및 구성 코드가 메서드로 이동

public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { DropBoxClient dbClient = …; if (from == CloudId.BOX) { dbClient.copyFromUrl( “http://www.box.com/files/”+ fileInfo.getFileId()); } else if (from == CloudId.SCLOUD) { ScloudClient sClient = …; InputStream is = sClient.getInputStream( fileInfo.getFileId()); dbClient.copyFromInputStream(is, fileInfo.getName());

-- DropBoxFileSystem private DropBoxClient dbClient = ...; public void copyFrom(CloudFile file) { if (file.hasUrl()) dbClient.copyFromUrl(file.getUrl()); else dbClient.copyFromInputStream( file.getInputStream(), file.getName()); }

Page 37: 객체 지향 발담그기 JCO 컨퍼런스 14회

이것이 바로 캡슐화Encapsulation

•  내부 구현을 외부에 감춤 o  내부 구현을 변경해도 이를 사용하는 코드 영향 최소화

-- DropBoxFileSystem private DropBoxClient dbClient = ... public void copyFrom(CloudFile file) { if (file.hasUrl()) dbClient.copyFromUrl(file.getUrl()); else dbClient.copyFromInputStream( file.getInputStream(), file.getName()); }

URL 생성 규칙이 변경되어도 이 코드는 바뀌지 않음

URL 제공 여부가 변경되어도 이 코드는 바뀌지 않음

Page 38: 객체 지향 발담그기 JCO 컨퍼런스 14회

OCP/캡슐화가 적용된 코드의 1개 클라우드 추가 비용

시간

개발 비용

그냥 막 작성한 코드

OCP/캡슐화 등 좋은 설계를 가진 코드

Page 39: 객체 지향 발담그기 JCO 컨퍼런스 14회

이것이 객체 지향을 해야하는 이유

비용 (즉, 변경의 유연함)

Page 40: 객체 지향 발담그기 JCO 컨퍼런스 14회

추상화는 언제?

•  유연함이 필요한 시점(변화가 생기는 시점) o  예, 1개 클라우드 지원에서 2개 클라우드로 확장 o  예, 물류업체 추가

•  아직 구현할 수 없는 부분이 존재할 때 o  예, 물류 업체와의 연동 프로토콜 미확정

§  물류 시스템 연동을 추상화해서 인터페이스로 표현

•  추상화(모델링)에는 비용이 발생 o  과도한 추상화(모델링)는 불필요한 복잡도를 증가시킴

Page 41: 객체 지향 발담그기 JCO 컨퍼런스 14회

정리

•  개발 시간을 줄이기 위해 필요한 것 중 하나, o  변화를 수용할 수 있는 설계/구현

•  잘 된 추상화/캡슐화가 해 주는 것 o  변화에 대한 유연함 제공 o  이는 개발 비용(즉, 시간) 절감 효과!

•  익혀야 할 것 → 객체 지향 o  캡슐화, 추상화, 다형성, SOLID

•  더불어 익힐 것 o  좋은 코드, 패턴, 테스트 주도 개발, 리팩토링 등

Page 42: 객체 지향 발담그기 JCO 컨퍼런스 14회

참고 서적

Page 43: 객체 지향 발담그기 JCO 컨퍼런스 14회

감사합니다.

연락은 트위터(@madvirus) 또는 이메일로([email protected])