From b7453553bcaa251a1cb88e0090413437cd755e15 Mon Sep 17 00:00:00 2001 From: Head <40166539+HTMLhead@users.noreply.github.com> Date: Wed, 10 Oct 2018 15:12:47 +0900 Subject: [PATCH 1/5] =?UTF-8?q?lec3=20-=20Step2.=EB=8B=A4=EC=96=91?= =?UTF-8?q?=ED=95=9C=20=EC=B6=9C=EB=A0=A5=20=EC=A7=80=EC=9B=90=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Lecture3 - step1. 기본기능 구현 (#40) * 스켈레톤 코드 작성 * 수많은 에러가 담긴 todo객체의 add함수 추가(id의 값을 제대로 가지지 못함, todo객체의 getStatusNum수정 필요 * 랜덤한 id값을 못가져오는 것을 수정. checkOverlap함수를 통해서 id의 값이 같지않도록 구현 * todo.add함수기능 완벽하게 구현 * todo.update함수 완성 * id를 입력받아 할일을 제거해주는 todo.remove함수 완성, 오류가많은 todo.printTask함수 완성 * todo.printTask함수 완성, tag를 확인하는 함수 제거(printTask를 통해 각 할일들의 태그가 무엇인지 확인이 가능하므로) * todo.printTagRelate 함수 완성, todo.add함수에서 버그를 발견함 * 새로운 객체 안 배열에 저장하도록 만들어 문제 해결 * remove함수를 호출했을때, saveData.task배열에서 지워지는것 뿐 아니라 saveData.idArrays배열에서도 id를 지워지게 수정 * 테스트코드를 주석처리함 들여쓰기를 다시 맞춤 * saveData객체를 없애고 그 내용을 todo객체 안으로 집어 넣음, todo.idArrays의 값은 todo.add를 호출할때마다 추가되도록 수정. * if(saveData.idArrays.indexOf(ranNum) !== -1)-> if(this.idArrays.includes(ranNum))으로 변경 * 기나긴 삼항연산자 statusNum[obj.status]++로 해결 * todo.update함수에서 바뀐 상태의 상태를 반영하지 않는 버그를 찾아내서 수정 * 함수마다 간단한 주석을 달아줌 * 주석추가 * 피드백을 위한 주석 추가 * 같은 숫자를 반복하는 것을 todo.task로부터 찾아와 sync가 완벽하게 맞도록 수정 * 쓸데없는 todo.idArrays에 쓰이는 배열과 메서드 제거 * 변수명 변경 * add나revmoe, update함수를 입력할때마다 각각 인자를 다르게 받아 다르게 출력하는 함수를 만듬 * 해야할 일들 주석으로 정리 * 만들어야 할 함수들 미리 작성 * time obj를 saveTimeObj로 이름을 변경하고 saveTimeObj에 doing일때의 시간과 done일때의 시간을 인자로 입력받아 경과된 시간을 반환해주는 getTakeTime 함수 구현 * 업데이트할 객체를 인자로 받아 id값과업데이트 될때의 시간 값을 saveTImeObj의 객체에 저장해주는 함수 구현 * todo.add함수에서 새 객체를 만들때 걸린시간 이라는 항목을 추가함 * timeobj객체를 todo안으로 집어넣어 활용하기로 결정, 수정 * doingTimeArray와 takenTimeArray가 굳이 필요할것 같이 않아서 제거, 대신 task배열내의 객체에 timeData추가. 이를 이용해 updateDoingTime메서드 완성 * updateTakeTime메서드 구현. id가 같은 task.timeData값에 걸린 시간을 계산해서 저장해줌 * showTag메서드와 그 메서드를 위한 printByTag메서드 구현. printByTag는 tag과 status를 인자로 받아 같은인자와 tag를 출력, showTag메서드는 그 값을 세번 불러서 서로다른 3가지의 status값을 모두 출력. * show메서드 구현 만약 status가 done상태일때만 시간을 출력해주도록 함 * printByTag메서드의 버그 수정(상태가done인 항목을 입력해도 걸린 시간이 나오지 않았음), show메서드의 버그 수정(상태가done이면 doing항목을 출력하라고 입력했을때 done항목도 같이 출력되었었음) * 테스트 케이스 추가 * showAll메서드 구현, bind메서드를 이용함 * 같은태그를가짐과동시에 같은상태인것의 개수를 세는 함수 getSameTagAndStatusNum함수를 만듦 * show함수를 좀더 보기좋게 수정 * showTags를 위한 getSameTagArrays와 printSameTag함수를 만듬. 태그의 모든값이 담긴 배열을 만들고, 배열의 값을 통해 같은 값을 출력하는 함수를 만듬 * 태그가 같은 할 일의 개수를 계산해주는 함수 getSameTagNum을 만듬 * 함수의 쓰임새에 따라 정렬하여 보기편하게 수정 * 테스트 케이스들을 모두 주석처리하고 id의 경우의 수를 100가지로 늘림 * 사소한 버그 수정 --- todo.js | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 todo.js diff --git a/todo.js b/todo.js new file mode 100644 index 0000000..7ab76b8 --- /dev/null +++ b/todo.js @@ -0,0 +1,239 @@ +const todo = { + task: [], + + add: function (objToAdd) { + const notAddedLength = this.task.length + const newTodo = { + id: this.getRanNum(), + name: objToAdd.name, + status: 'todo', + tag: objToAdd.tag, + timeData: 0, + } + this.task.push(newTodo) + let statusNum = this.getStatusNum(this.task) + this.printChangeThing(newTodo, notAddedLength) + this.printStatusNum(statusNum) + },//해야할일과 id값을 추가해주는 함수 + + getRanNum: function () { + const ranNum = Math.floor(Math.random() * 100) + const idArrays = this.task.map(obj => obj.id) + if (idArrays.includes(ranNum)) { + return this.getRanNum() + } + return ranNum; + },//중복되지 않는 랜덤한 숫자를뽑아내는 함수 + + getStatusNum: function (accumulatedTask) { + const statusNum = { + todo: 0, + doing: 0, + done: 0 + } + accumulatedTask.forEach(obj => { + statusNum[obj.status]++ + }) + return statusNum + },//상태를 초기화 시켜주는 함수 + + printStatusNum: function (statusNum) { + console.log(`현재상태 todo : ${statusNum.todo}, doing: ${statusNum.doing}, done : ${statusNum.done}`) + },//상태를 출력해주는 함수 + + printChangeThing: function (objToPrint, beforeTaskLength, beforeTaskStatus) { + if (this.task.length > beforeTaskLength) { + console.log(`ID : ${objToPrint.id}, ${objToPrint.name} 항목이 추가되었습니다.`); + } else if (this.task.length < beforeTaskLength) { + console.log(`ID : ${objToPrint.id}, ${objToPrint.name} 삭제 완료`) + } else { + console.log(`ID: ${objToPrint.id}, ${objToPrint.name} 항목이 ${beforeTaskStatus} => ${objToPrint.status} 상태로 업데이트 되었습니다.`) + } + },//할일이 추가되거나 제거되거나 업데이트 될 때 적합한 내용을 출력해 주는 함수 + + update: function (objToUpdate) { + let beforeTaskStatus = [] + if (objToUpdate.nextstatus === 'doing') { + this.updateDoingTime(objToUpdate) + } else if (objToUpdate.nextstatus === 'done') { + this.updateTakeTime(objToUpdate) + } + this.task = this.task.map(taskObj => { + if (objToUpdate.id === taskObj.id) { + beforeTaskStatus.push(taskObj.status) + taskObj.status = objToUpdate.nextstatus.toLowerCase(); + return taskObj + } + return taskObj + }) + const changedTask = this.task.filter(taskObj => { + if (objToUpdate.id === taskObj.id) { + return taskObj + } + }) + let statusNum = this.getStatusNum(this.task) + this.printChangeThing(changedTask[0], this.task.length, beforeTaskStatus[0]) + this.printStatusNum(statusNum) + },//상태 업데이트 함수//주어진 정보의 시간을 넣을 수 있도록 수정 요망 + + updateDoingTime: function (objToUpdate) { + this.task.forEach(taskObj => { + if (taskObj.id === objToUpdate.id) { + taskObj.timeData = Date.now(); + } + }) + },//업데이트할 객체를 인자로 받아 task내의 timeData값을 변경. + + updateTakeTime: function (objToUpdate) { + this.task.forEach(taskObj => { + if (taskObj.id === objToUpdate.id) { + taskObj.timeData = this.getTakeTime(taskObj.timeData, Date.now()) + } + }) + },//업데이트할 객체를 인자로 받아 task내의 timeData의 값을 걸린 시간으로 변경. + + getTakeTime: function (doingTime, currentTime) { + let takenTime = '' + let takenMsecTime = currentTime - doingTime + const msecPerMinute = 1000 * 60, msecPerHour = msecPerMinute * 60, msecPerDay = msecPerHour * 24 + const takenDays = Math.floor(takenMsecTime / msecPerDay) + takenMsecTime = takenMsecTime - takenDays * msecPerDay + const takenHours = Math.floor(takenMsecTime / msecPerHour) + takenMsecTime = takenMsecTime - takenHours * msecPerHour + const takenMinutes = Math.floor(takenMsecTime / msecPerMinute) + takenMsecTime = takenMsecTime - takenMinutes * msecPerMinute + takenTime += takenDays + '일, ' + takenHours + '시간, ' + takenMinutes + '분' + return takenTime; + },//걸린 시간을 계산해주는 함수 + + remove: function (objToRemove) { + const notRemovedLength = todo.task.length + let filteredTask = this.task.filter(taskObj => taskObj.id === objToRemove.id) + let removedTask = this.task.filter(taskObj => taskObj.id !== objToRemove.id) + this.task = removedTask + this.printChangeThing(filteredTask[0], notRemovedLength) + },//할 일과 id값을 제거해주는 함수 + + show: function (status) { + console.log(`[${status} 상태인 할 일들]`) + this.task.forEach(taskObj => { + if (status === 'done' && taskObj.status === 'done') { + console.log(`ID : ${taskObj.id}, ${taskObj.name}, [${taskObj.tag}], ${taskObj.timeData}`) + } else if (taskObj.status === status) { + console.log(`ID : ${taskObj.id}, ${taskObj.name}, [${taskObj.tag}]`) + } + }) + },//인자로 입력받은 상태의 정보들을 출력해주는 함수 + + showTag: function (tag) { + const todoNum = this.getSameTagAndStatusNum(tag, 'todo') + console.log(`[todo, 총 ${todoNum}개]`) + this.printByTag(tag, 'todo'); + const doingNum = this.getSameTagAndStatusNum(tag, 'doing') + console.log(`[doing, 총 ${doingNum}개]`) + this.printByTag(tag, 'doing'); + const doneNum = this.getSameTagAndStatusNum(tag, 'done') + console.log(`[done, 총 ${doneNum}개]`) + this.printByTag(tag, 'done'); + },//수정필요, 여기에 showTags기능까지 넣어볼 것.//함수는 한가지의 일만 하는게 맞는듯 + + printByTag: function (tag, status) { + this.task.forEach(taskObj => { + if (taskObj.tag === tag && taskObj.status === status) { + if (status === 'done') { + console.log(`ID : ${taskObj.id}, ${taskObj.name}, ${taskObj.timeData}`) + return; + } + console.log(`ID : ${taskObj.id}, ${taskObj.name}`) + } + }) + },//tag의 값과 상태의 값을 인자로 받아 출력해주는 함수 + + getSameTagAndStatusNum: function (tag, status) { + let sameTagAndStatusNum = 0 + this.task.forEach(taskObj => { + if (taskObj.tag === tag && taskObj.status === status) { + sameTagAndStatusNum++ + } + }) + return sameTagAndStatusNum + },//태그와 상태가 같은 것들의 개수를 세어주는 함수 + + showTags: function () { + const taggedTask = this.task.filter(obj => { + return obj.tag !== undefined + }) + const sameTagArrays = this.getTagArrays(taggedTask); + sameTagArrays.forEach(tag => { + const sameTagNum = this.getSameTagNum(tag, taggedTask) + this.printSameTag(tag, taggedTask) + }) + },//태그에 따라 모든 값을 출력해주는 함수 + + getTagArrays: function(taggedTask) { + const sameTagArrays = []; + taggedTask.forEach(taggedTaskObj => { + if(!sameTagArrays.includes(taggedTaskObj.tag)) { + sameTagArrays.push(taggedTaskObj.tag) + } + }) + return sameTagArrays + },//현재 task배열 내에있는 모든 tag값들을 중복 없이 따로 모아놓는 배열을 만드는 함수 + + printSameTag: function(tag, taggedTask) { + console.log(`${tag}, 총 ${sameTagNum}개`) + taggedTask.forEach(taggedTaskObj => { + if(tag === taggedTaskObj.tag) { + console.log(`ID : ${taggedTaskObj.id}, ${taggedTaskObj.name}, [${taggedTaskObj.status}]`) + } + }) + },//tag의 값에 따라서 출력해주는 함수 + + getSameTagNum: function(tag, taggedTask) { + sameTagNum = 0 + taggedTask.forEach(taggedTaskObj => { + if(tag === taggedTaskObj.tag) { + sameTagNum++ + } + }) + return sameTagNum + },//같은 태그의 개수를 세어주는 함수 + + showAll: function () { + const statusNum = this.getStatusNum(this.task) + console.log(`총 ${this.task.length}개의 리스트를 가져왔습니다. + 지금부터 2초뒤에 todo내역을 출력합니다........`) + setTimeout(function () { + console.log(`[todo, 총${statusNum.todo}개]`); + this.show('todo') + console.log(`지금부터 3초뒤에 doing내역을 출력합니다.......`) + setTimeout(function () { + debugger; + console.log(`[doing, 총${statusNum.doing}개]`) + this.show('doing') + console.log(`지금부터 2초뒤에 done내역을 출력합니다........`) + setTimeout(function () { + console.log(`[done, 총${statusNum.done}개]`) + this.show('done') + }.bind(todo), 2000) + }.bind(todo), 3000) + }.bind(todo), 2000) + },//입력된 정보들의 상태에 따라 시간차로 출력해주는 함수 +}//해야 할일 객체 +// 테스트 + +todo.add({ name: '자바스크립트', tag: 'programming'}); +todo.add({ name: 'C++', tag: 'programming' }); +todo.add({ name: '회식', tag: '회사' }); +todo.add({ name: '노래연습', tag: '자기개발' }); +todo.add({ name: '과장님업무', tag: '회사' }) + +// todo.update({ id: 3, nextstatus: 'doing' }) +// todo.update({ id: 3, nextstatus: 'done' }) +// todo.update({ id: 2, nextstatus: 'done' }) +todo.showTag('programming') +todo.showTags(); +todo.show('todo') +todo.showAll(); + + From 82e620d86d7ec4d5fabd60f10d6cd3e5c316cfd3 Mon Sep 17 00:00:00 2001 From: jisuyoun Date: Thu, 18 Oct 2018 16:45:56 +0900 Subject: [PATCH 2/5] delete. code --- todo.js | 239 -------------------------------------------------------- 1 file changed, 239 deletions(-) delete mode 100644 todo.js diff --git a/todo.js b/todo.js deleted file mode 100644 index 7ab76b8..0000000 --- a/todo.js +++ /dev/null @@ -1,239 +0,0 @@ -const todo = { - task: [], - - add: function (objToAdd) { - const notAddedLength = this.task.length - const newTodo = { - id: this.getRanNum(), - name: objToAdd.name, - status: 'todo', - tag: objToAdd.tag, - timeData: 0, - } - this.task.push(newTodo) - let statusNum = this.getStatusNum(this.task) - this.printChangeThing(newTodo, notAddedLength) - this.printStatusNum(statusNum) - },//해야할일과 id값을 추가해주는 함수 - - getRanNum: function () { - const ranNum = Math.floor(Math.random() * 100) - const idArrays = this.task.map(obj => obj.id) - if (idArrays.includes(ranNum)) { - return this.getRanNum() - } - return ranNum; - },//중복되지 않는 랜덤한 숫자를뽑아내는 함수 - - getStatusNum: function (accumulatedTask) { - const statusNum = { - todo: 0, - doing: 0, - done: 0 - } - accumulatedTask.forEach(obj => { - statusNum[obj.status]++ - }) - return statusNum - },//상태를 초기화 시켜주는 함수 - - printStatusNum: function (statusNum) { - console.log(`현재상태 todo : ${statusNum.todo}, doing: ${statusNum.doing}, done : ${statusNum.done}`) - },//상태를 출력해주는 함수 - - printChangeThing: function (objToPrint, beforeTaskLength, beforeTaskStatus) { - if (this.task.length > beforeTaskLength) { - console.log(`ID : ${objToPrint.id}, ${objToPrint.name} 항목이 추가되었습니다.`); - } else if (this.task.length < beforeTaskLength) { - console.log(`ID : ${objToPrint.id}, ${objToPrint.name} 삭제 완료`) - } else { - console.log(`ID: ${objToPrint.id}, ${objToPrint.name} 항목이 ${beforeTaskStatus} => ${objToPrint.status} 상태로 업데이트 되었습니다.`) - } - },//할일이 추가되거나 제거되거나 업데이트 될 때 적합한 내용을 출력해 주는 함수 - - update: function (objToUpdate) { - let beforeTaskStatus = [] - if (objToUpdate.nextstatus === 'doing') { - this.updateDoingTime(objToUpdate) - } else if (objToUpdate.nextstatus === 'done') { - this.updateTakeTime(objToUpdate) - } - this.task = this.task.map(taskObj => { - if (objToUpdate.id === taskObj.id) { - beforeTaskStatus.push(taskObj.status) - taskObj.status = objToUpdate.nextstatus.toLowerCase(); - return taskObj - } - return taskObj - }) - const changedTask = this.task.filter(taskObj => { - if (objToUpdate.id === taskObj.id) { - return taskObj - } - }) - let statusNum = this.getStatusNum(this.task) - this.printChangeThing(changedTask[0], this.task.length, beforeTaskStatus[0]) - this.printStatusNum(statusNum) - },//상태 업데이트 함수//주어진 정보의 시간을 넣을 수 있도록 수정 요망 - - updateDoingTime: function (objToUpdate) { - this.task.forEach(taskObj => { - if (taskObj.id === objToUpdate.id) { - taskObj.timeData = Date.now(); - } - }) - },//업데이트할 객체를 인자로 받아 task내의 timeData값을 변경. - - updateTakeTime: function (objToUpdate) { - this.task.forEach(taskObj => { - if (taskObj.id === objToUpdate.id) { - taskObj.timeData = this.getTakeTime(taskObj.timeData, Date.now()) - } - }) - },//업데이트할 객체를 인자로 받아 task내의 timeData의 값을 걸린 시간으로 변경. - - getTakeTime: function (doingTime, currentTime) { - let takenTime = '' - let takenMsecTime = currentTime - doingTime - const msecPerMinute = 1000 * 60, msecPerHour = msecPerMinute * 60, msecPerDay = msecPerHour * 24 - const takenDays = Math.floor(takenMsecTime / msecPerDay) - takenMsecTime = takenMsecTime - takenDays * msecPerDay - const takenHours = Math.floor(takenMsecTime / msecPerHour) - takenMsecTime = takenMsecTime - takenHours * msecPerHour - const takenMinutes = Math.floor(takenMsecTime / msecPerMinute) - takenMsecTime = takenMsecTime - takenMinutes * msecPerMinute - takenTime += takenDays + '일, ' + takenHours + '시간, ' + takenMinutes + '분' - return takenTime; - },//걸린 시간을 계산해주는 함수 - - remove: function (objToRemove) { - const notRemovedLength = todo.task.length - let filteredTask = this.task.filter(taskObj => taskObj.id === objToRemove.id) - let removedTask = this.task.filter(taskObj => taskObj.id !== objToRemove.id) - this.task = removedTask - this.printChangeThing(filteredTask[0], notRemovedLength) - },//할 일과 id값을 제거해주는 함수 - - show: function (status) { - console.log(`[${status} 상태인 할 일들]`) - this.task.forEach(taskObj => { - if (status === 'done' && taskObj.status === 'done') { - console.log(`ID : ${taskObj.id}, ${taskObj.name}, [${taskObj.tag}], ${taskObj.timeData}`) - } else if (taskObj.status === status) { - console.log(`ID : ${taskObj.id}, ${taskObj.name}, [${taskObj.tag}]`) - } - }) - },//인자로 입력받은 상태의 정보들을 출력해주는 함수 - - showTag: function (tag) { - const todoNum = this.getSameTagAndStatusNum(tag, 'todo') - console.log(`[todo, 총 ${todoNum}개]`) - this.printByTag(tag, 'todo'); - const doingNum = this.getSameTagAndStatusNum(tag, 'doing') - console.log(`[doing, 총 ${doingNum}개]`) - this.printByTag(tag, 'doing'); - const doneNum = this.getSameTagAndStatusNum(tag, 'done') - console.log(`[done, 총 ${doneNum}개]`) - this.printByTag(tag, 'done'); - },//수정필요, 여기에 showTags기능까지 넣어볼 것.//함수는 한가지의 일만 하는게 맞는듯 - - printByTag: function (tag, status) { - this.task.forEach(taskObj => { - if (taskObj.tag === tag && taskObj.status === status) { - if (status === 'done') { - console.log(`ID : ${taskObj.id}, ${taskObj.name}, ${taskObj.timeData}`) - return; - } - console.log(`ID : ${taskObj.id}, ${taskObj.name}`) - } - }) - },//tag의 값과 상태의 값을 인자로 받아 출력해주는 함수 - - getSameTagAndStatusNum: function (tag, status) { - let sameTagAndStatusNum = 0 - this.task.forEach(taskObj => { - if (taskObj.tag === tag && taskObj.status === status) { - sameTagAndStatusNum++ - } - }) - return sameTagAndStatusNum - },//태그와 상태가 같은 것들의 개수를 세어주는 함수 - - showTags: function () { - const taggedTask = this.task.filter(obj => { - return obj.tag !== undefined - }) - const sameTagArrays = this.getTagArrays(taggedTask); - sameTagArrays.forEach(tag => { - const sameTagNum = this.getSameTagNum(tag, taggedTask) - this.printSameTag(tag, taggedTask) - }) - },//태그에 따라 모든 값을 출력해주는 함수 - - getTagArrays: function(taggedTask) { - const sameTagArrays = []; - taggedTask.forEach(taggedTaskObj => { - if(!sameTagArrays.includes(taggedTaskObj.tag)) { - sameTagArrays.push(taggedTaskObj.tag) - } - }) - return sameTagArrays - },//현재 task배열 내에있는 모든 tag값들을 중복 없이 따로 모아놓는 배열을 만드는 함수 - - printSameTag: function(tag, taggedTask) { - console.log(`${tag}, 총 ${sameTagNum}개`) - taggedTask.forEach(taggedTaskObj => { - if(tag === taggedTaskObj.tag) { - console.log(`ID : ${taggedTaskObj.id}, ${taggedTaskObj.name}, [${taggedTaskObj.status}]`) - } - }) - },//tag의 값에 따라서 출력해주는 함수 - - getSameTagNum: function(tag, taggedTask) { - sameTagNum = 0 - taggedTask.forEach(taggedTaskObj => { - if(tag === taggedTaskObj.tag) { - sameTagNum++ - } - }) - return sameTagNum - },//같은 태그의 개수를 세어주는 함수 - - showAll: function () { - const statusNum = this.getStatusNum(this.task) - console.log(`총 ${this.task.length}개의 리스트를 가져왔습니다. - 지금부터 2초뒤에 todo내역을 출력합니다........`) - setTimeout(function () { - console.log(`[todo, 총${statusNum.todo}개]`); - this.show('todo') - console.log(`지금부터 3초뒤에 doing내역을 출력합니다.......`) - setTimeout(function () { - debugger; - console.log(`[doing, 총${statusNum.doing}개]`) - this.show('doing') - console.log(`지금부터 2초뒤에 done내역을 출력합니다........`) - setTimeout(function () { - console.log(`[done, 총${statusNum.done}개]`) - this.show('done') - }.bind(todo), 2000) - }.bind(todo), 3000) - }.bind(todo), 2000) - },//입력된 정보들의 상태에 따라 시간차로 출력해주는 함수 -}//해야 할일 객체 -// 테스트 - -todo.add({ name: '자바스크립트', tag: 'programming'}); -todo.add({ name: 'C++', tag: 'programming' }); -todo.add({ name: '회식', tag: '회사' }); -todo.add({ name: '노래연습', tag: '자기개발' }); -todo.add({ name: '과장님업무', tag: '회사' }) - -// todo.update({ id: 3, nextstatus: 'doing' }) -// todo.update({ id: 3, nextstatus: 'done' }) -// todo.update({ id: 2, nextstatus: 'done' }) -todo.showTag('programming') -todo.showTags(); -todo.show('todo') -todo.showAll(); - - From 0517df66bdf4cad89dd81d57d88b6ebd887d7d88 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Mon, 22 Apr 2019 16:33:53 +0900 Subject: [PATCH 3/5] Step4-2 mission : TUNA & SNOW (#104) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create todosList.json * create todos.js * create app.js for program runnung * Rix the name of function rename getCommandArrayByRegexp to getCmdList * excuteCommand 함수 수정 : argument 개수 판별 추가 인자 개수로 1개 초과 나올 경우 즉시 종료 후 프로그램 다시 실행 * isValidCommand 함수 변경 : obeject argument 추가 외부 변수에 접근하지 않고 객체를 인자로 받을 수 있도록 변경 * idGenerator 함수 변경 : 상수가 아닌 인자로 random number 생성 상수 99999 제거 후, max 값과 min 값으로 random 넘버 생성 * fix the Equal operator to strcit Equal operator --- app.js | 68 ++++++++++++++++++++++++++++++++++++++ todos.js | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ todosList.json | 41 +++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 app.js create mode 100644 todos.js create mode 100644 todosList.json diff --git a/app.js b/app.js new file mode 100644 index 0000000..874a42e --- /dev/null +++ b/app.js @@ -0,0 +1,68 @@ +const todos = require('./todos.js'); + +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout +}); + +const getCmdList = (command) => { + const regexpForSeperateCommand = /[^\$]+/g; + return command.match(regexpForSeperateCommand); +}; + +const executeCommand = (commandArr) => { + + if (arguments.length != 1) return; + + if (commandArr.length == 2) { + todos[commandArr[0]](commandArr[1]); + } else if (commandArr.length == 3) { + todos[commandArr[0]](commandArr[1], commandArr[2]); + } +}; + +const isValidCommand = (command, obj) => { + let result = false; + if (Object.keys(obj).includes(command)) result = true; + return result; +} + +const delay = (time) => { + return new Promise(function(resolve, reject){ + setTimeout(resolve, time); + }); +} + +const runProgram = (readline) => { + + readline.setPrompt('명령하세요: '); + readline.prompt(); + readline.on('line', (userInput) => { + + try { + const commandArr = getCmdList(userInput); + const primaryCommand = commandArr[0]; + + if (!isValidCommand(primaryCommand, todos)) { + console.log("올바르지 않은 명령어입니다") + readline.prompt(); + } + + delay(0) + .then(() => {executeCommand(commandArr); return delay(0)}) + .then(() => {if (primaryCommand != 'show') return delay(2000)}) + .then(() => {if (primaryCommand == 'update') return delay(2500)}) + .then(() => {readline.prompt(); return delay(0)}) + + } catch (error) { + console.log("명령어를 다시 입력해주세요") + readline.prompt(); + } + + }).on('close', () => { + console.log("프로그램을 종료합니다."); + process.exit(); + }); +} + +runProgram(readline); \ No newline at end of file diff --git a/todos.js b/todos.js new file mode 100644 index 0000000..796ed1d --- /dev/null +++ b/todos.js @@ -0,0 +1,88 @@ +//todos.js +const originData = require('./todosList.json'); +const convertedData = JSON.parse(JSON.stringify(originData)).data; +const idGenerator = (max, min) => Math.floor(Math.random() * (max - min)) + 1; + +const getArrByCondition = (arr, condition) => { + return arr.reduce((acc, val) => { + if (condition(val)) acc.push(val); + return acc; + }, []); +}; + +const showStatusLazy = () => { + setTimeout(() => { + showData('all'); + }, 1000); +} + +const isDuplicated = (val, key, convertedData) => { + let result = false; + convertedData.forEach((element) => { + if (val === element[key]) result = true; + }) + return result; +} + +const showData = (type) => { + if (type === 'all') { + const numOfTodos = getArrByCondition(convertedData, (val) => {return val.status === "todo"}).length; + const numOfDoings = getArrByCondition(convertedData, (val) => {return val.status === "doing"}).length; + const numOfDones = getArrByCondition(convertedData, (val) => {return val.status === "done"}).length; + + console.log(`현재상태 : todo: ${numOfTodos}개, doing: ${numOfDoings}개, done: ${numOfDones}개`); + + } else { + const objArr = getArrByCondition(convertedData, (val) => {return (type === val.status)}); + let result = `${type} 리스트 : 총${objArr.length}건 :`; + getArrByCondition(objArr, (val) => {result += `, '${val.name}, ${val.id}번'`; return true}); + console.log(`${result}`); + } +} + +const addData = (name, tags) => { + const id = idGenerator(99999, 1); + + if (isDuplicated(id, "id", convertedData)) { + return addData(name, tags); + } + + let obj = { + name, + tags, + status: 'todo', + id, + }; + + convertedData.push(obj); + console.log(`${obj.name} 1개가 추가됐습니다.(id : ${obj.id})`); + showStatusLazy(); +} + +const deleteData = (id)=> { + const target = getArrByCondition(convertedData, (val) => {return val.id === id})[0]; + if (!target) {console.log('일치하는 id가 없습니다'); return;} + + convertedData.splice(convertedData.indexOf(target), 1); + console.log(`${target.name} ${target.status}가 목록에서 삭제됐습니다.`); + showStatusLazy(); +}; + +const updateData = (id, status)=> { + const target = getArrByCondition(convertedData, (val) => {return val.id === id})[0]; + if (!target) {console.log('일치하는 id가 없습니다'); return;} + + target.status = status; + + setTimeout(() => { + console.log(`${target.name}가 ${status}로 상태가 변경되었습니다`); + showStatusLazy(); + }, 3000); +} + +module.exports = { + "show" : showData, + "add" : addData, + "update" : updateData, + "delete" : deleteData, +}; \ No newline at end of file diff --git a/todosList.json b/todosList.json new file mode 100644 index 0000000..1734fe5 --- /dev/null +++ b/todosList.json @@ -0,0 +1,41 @@ +{ + "result" : true, + "data": [ + { + "name" : "자바스크립트 공부하기", + "tags" : ["programming", "javascript"], + "status" : "todo", + "id" : 12123123 + }, + { + "name" : "그림 그리기", + "tags" : ["picture", "favorite"], + "status" : "doing", + "id" : 123 + }, + { + "name" : "파이썬 공부하기", + "tags" : ["programming", "javascript"], + "status" : "done", + "id" : 213232 + }, + { + "name" : "자바 공부하기", + "tags" : ["programming", "java"], + "status" : "todo", + "id" : 123345 + }, + { + "name" : "음악 듣기", + "tags" : ["music", "favorite"], + "status" : "doing", + "id" : 12123 + }, + { + "name" : "정규식 공부하기", + "tags" : ["programming", "RegExp"], + "status" : "done", + "id" : 1234 + } + ] +} From 430a7d3fed0703cf6fe7b63fa8200c57d7b6b9fa Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Mon, 29 Apr 2019 13:30:57 +0900 Subject: [PATCH 4/5] step-5-2 : TUNA, YM (#112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create todosList.json * create todos.js * create app.js for program runnung * Rix the name of function rename getCommandArrayByRegexp to getCmdList * excuteCommand 함수 수정 : argument 개수 판별 추가 인자 개수로 1개 초과 나올 경우 즉시 종료 후 프로그램 다시 실행 * isValidCommand 함수 변경 : obeject argument 추가 외부 변수에 접근하지 않고 객체를 인자로 받을 수 있도록 변경 * idGenerator 함수 변경 : 상수가 아닌 인자로 random number 생성 상수 99999 제거 후, max 값과 min 값으로 random 넘버 생성 * fix the Equal operator to strcit Equal operator * create todosData.json to load the mock data * create app.js to run todos application * add the module : readLine module * add the program constructor * add the run function : to run the program * create commandParser.js file : to parse the user's input String * add the executeCmd function Object : to execute the command line * create the instruction.js file : to instruct command * add the Instruction module * implements the CommandParser method to parse the command line * create utils.js to use common * add the getArrByCondition function : get array by condition arguments * add the modules : data status, utils moudule * add the show function to show status of data * add getRandomId function * add the show function * add the adding object function * add the delete function * add the update function : update object async function to update objcet * 인자에 따라 async하게 작동할 수 있도록 메서드 추가 * add the delay function : utils.js add the Promise function to delay the function * create exceptionHadling.js file * add the runtimeExceptions missingSeperatorException notExsitException sameStatusException commandMissingException * add the exception handling to run the application throw isValidSeperator Exception, isValidCommand catch and restart program * add the modules to run program * add the isValidCommand function * add the exception handling about executCmd function * add the module : ExceptionHandling module * fix datas * fix the runProgram function : missing argument * add the exception handling about add, update function * Rename constructor function : Program to RunTodoApp * Add module Objects in RunTodoApp constructor add cmdArr, commandParser, utils, instruction, customException module Obejct * Instruction 생성자에 모듈 객체 추가 Instruction 생성 시 utils, customException 객체 생성 되도록 추가 * show 함수 리팩토링 : 내부 분기를 everyStatus, sigleStatus function 분리 * 생성자 객체를 이용하도록 prototype object 수정 * CustomerException 에러 객체 리팩토링 * getRandomId function에서 매직넘버 삭제, 생성자 인수 추가 생성자에서 max 값과 min 값을 받을 수 있도록 수정 * 생성자 함수에 모듈 객체 추가 * Rename ExceptionHandling to CustomException * 함수 이름 변경 : getradomId to getRandomID * 함수 바뀐 이름 getRandomID 반영 --- oop/CustomException.js | 37 ++++++++++++++++++ oop/app.js | 57 +++++++++++++++++++++++++++ oop/commandParser.js | 40 +++++++++++++++++++ oop/instruction.js | 87 ++++++++++++++++++++++++++++++++++++++++++ oop/todosData.json | 23 +++++++++++ oop/utils.js | 25 ++++++++++++ 6 files changed, 269 insertions(+) create mode 100644 oop/CustomException.js create mode 100644 oop/app.js create mode 100644 oop/commandParser.js create mode 100644 oop/instruction.js create mode 100644 oop/todosData.json create mode 100644 oop/utils.js diff --git a/oop/CustomException.js b/oop/CustomException.js new file mode 100644 index 0000000..c3f3a78 --- /dev/null +++ b/oop/CustomException.js @@ -0,0 +1,37 @@ +function CustomException() { + +} + +CustomException.prototype = { + + missingSeperatorException (input) { + if (!this.isValidSeperator(input)) throw new Error("구분자 $가 존재하지 않습니다"); + }, + notExistIdException () { + throw new Error(`찾으시는 id가 존재하지 않습니다`); + }, + sameStatusException () { + throw new Error(`같은 상태로 업데이트 할 수 없습니다`); + }, + + CommandMissingException (command, arr) { + if (!this.isValidCommand(command, arr)) + throw new Error(`올바른 명령어가 아닙니다`); + }, + + isValidSeperator (input) { + let result = true; + const regexp = /\$/g; + if (input.match(regexp) == null) result = false; + + return result; + }, + isValidCommand(command, arr) { + let result = false; + if (arr.includes(command)) result = true; + return result; + }, + +}; + +module.exports = CustomException; \ No newline at end of file diff --git a/oop/app.js b/oop/app.js new file mode 100644 index 0000000..3a0b6ed --- /dev/null +++ b/oop/app.js @@ -0,0 +1,57 @@ +const readLine = require('readline').createInterface( { + input:process.stdin, + output:process.stdout, +}); +const CommandParser = require('./commandParser.js'); +const Utils = require('./utils.js'); +const Instruction = require('./instruction.js'); +const CustomException = require('./CustomException.js'); + +function RunTodoApp() { + this.cmdArr = ['show','delete','update','add']; + this.commandParser = new CommandParser(); + this.utils = new Utils(); + this.instruction = new Instruction(); + this.customException = new CustomException(); +} + +RunTodoApp.prototype = { + + runProgram (readline) { + readline.setPrompt('명령하세요: '); + readline.prompt(); + readline.on('line', (userInput) => { + + try { + + this.customException.missingSeperatorException(userInput); + const cmdList = this.commandParser.getCmdList(userInput); + + this.customException.CommandMissingException(cmdList[0], this.cmdArr); + this.commandParser.executeCmd(cmdList); + + this.utils.delay(0) + .then(() => {if (cmdList[0] === 'update') return this.utils.delay(3500);}) + .then(() => {return this.utils.delay(1000);}) + .then(() => {if (cmdList[0] !== 'show') this.instruction.show('all');}) + .catch(function(e) {console.log(e);}) + .then(() => {readLine.prompt();}); + + } catch(e) { + console.error(e.message); + readLine.prompt(); + } + + }).on('close', () => { + console.log("프로그램을 종료합니다."); + process.exit(); + }); + } + +}; + + +const run = (() => { + const runTodoApp = new RunTodoApp(); + runTodoApp.runProgram(readLine); +})(); \ No newline at end of file diff --git a/oop/commandParser.js b/oop/commandParser.js new file mode 100644 index 0000000..1cbeed3 --- /dev/null +++ b/oop/commandParser.js @@ -0,0 +1,40 @@ +const Instruction = require('./instruction.js'); +const CustomException = require('./customException.js'); + +function CommandParser () { + this.instruction = new Instruction(); + this.customException = new CustomException(); +} + +CommandParser.prototype = { + getCmdList(input) { + const regexp = /[^\$]+/g; + return input.match(regexp); + }, + + executeCmd(command) { + try { + + if (command.length === 2) { + this.instruction[command[0]](command[1]); + } else if (command.length === 3) { + this.instruction[command[0]](command[1], command[2]); + } else { + this.customException.CommandMissingException(); + } + + } catch (e) { + console.error(e.message); + return; + } + + }, + + isValidCommand(command, arr) { + let result = false; + if (arr.includes(command)) result = true; + return result; + }, +}; + +module.exports = CommandParser; \ No newline at end of file diff --git a/oop/instruction.js b/oop/instruction.js new file mode 100644 index 0000000..f86f5b6 --- /dev/null +++ b/oop/instruction.js @@ -0,0 +1,87 @@ +const originData = require('./todosdata.json'); +const convertedData = JSON.parse(JSON.stringify(originData)).data; + +const Utils = require('./utils.js'); +const CustomException = require('./customException.js'); + +function Instruction() { + this.utils = new Utils(); + this.customException = new CustomException(); + this.minIdNum = 1; + this.maxIdNum = 99999; +} + +Instruction.prototype = { + + everyStatus (convertedData) { + let [numOfTodos, numOfDoings, numOfDones] = [0,0,0]; + convertedData.forEach((value) => { + if (value.status === 'todo') numOfTodos++; + else if (value.status === 'doing') numOfDoings++; + else if (value.status === 'done') numOfDones++; + }); + console.log(`현재상태 : todo: ${numOfTodos}개, doing: ${numOfDoings}개, done: ${numOfDones}개`); + }, + + singleStatus (convertedData, status) { + const tasks = this.utils.getArrByCondition(convertedData, (val) => { return (status === val.status);}); + let message = `${status}리스트 총 ${tasks.length}건 : `; + tasks.forEach( obj => { + message += `'${obj.name}, ${obj.id}번,' `; + }); + console.log(message); + }, + + show (status) { + const statusArr = ['all', 'todo', 'doing', 'done']; + if (status === 'all') { + this.everyStatus(convertedData); + } else if (statusArr.includes(status)) { + this.singleStatus(convertedData, status); + } + }, + + add (name, tags) { + const id = this.utils.getRandomId(this.maxIdNum, this.minIdNum); + let obj = { + name, + tags, + status: 'todo', + id, + }; + convertedData.push(obj); + const message = `${obj.name} 1개가 추가됐습니다.(id : ${obj.id})`; + console.log(message); + }, + + delete (id) { + const targetObj = this.utils.getArrByCondition(convertedData, (val) => { return id == val.id;})[0]; + try { + if (!targetObj) this.customException.notExistIdException(); + } catch (e) { + console.error(e.message); + return; + } + convertedData.splice(convertedData.indexOf(targetObj), 1); + let message = `${targetObj.name}이 ${targetObj.status}에서 삭제되었습니다.`; + console.log(message); + }, + + update (id, status) { + const targetObj = this.utils.getArrByCondition(convertedData, (val) => { return id == val.id;})[0]; + try { + if (!targetObj) this.customException.notExistIdException(); + if (targetObj.status === status) this.customException.sameStatusException(); + } catch (e) { + console.error(e.message); + return; + } + targetObj.status = status; + const message = `${targetObj.name}가 ${targetObj.status}로 상태가 변경되었습니다`; + setTimeout(() => { + console.log(message); + }, 3000); + } +}; + +module.exports = Instruction; \ No newline at end of file diff --git a/oop/todosData.json b/oop/todosData.json new file mode 100644 index 0000000..282d7ba --- /dev/null +++ b/oop/todosData.json @@ -0,0 +1,23 @@ +{ + "result" : true, + "data": [ + { + "name" : "자바스크립트 공부하기", + "tags" : ["programming", "javascript"], + "status" : "todo", + "id" : 123 + }, + { + "name" : "그림 그리기", + "tags" : ["picture", "favorite"], + "status" : "doing", + "id" : 312323 + }, + { + "name" : "파이썬 공부하기", + "tags" : ["programming", "javascript"], + "status" : "done", + "id" : 213232 + } + ] +} \ No newline at end of file diff --git a/oop/utils.js b/oop/utils.js new file mode 100644 index 0000000..684baf9 --- /dev/null +++ b/oop/utils.js @@ -0,0 +1,25 @@ +function Utils () { + +} + +Utils.prototype = { + + getArrByCondition : (arr, condition) => { + return arr.reduce((acc, val) => { + if (condition(val)) acc.push(val); + return acc; + }, []); + }, + + getRandomId : (max, min) => { + return Math.floor(Math.random() * (max-min)) + 1; + }, + + delay : (time) => { + return new Promise(function(resolve, reject){ + setTimeout(resolve, time); + }); + }, +}; + +module.exports = Utils; \ No newline at end of file From acb07c640f6dd12e06e655ff9b63f8b3ae4bef58 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Thu, 2 May 2019 18:42:08 +0900 Subject: [PATCH 5/5] =?UTF-8?q?Step5-3=20:=20TUNA=20&=20YM=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=82=B4=EC=9A=A9=20=EB=B0=98=EC=98=81=20(#136)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : resolve conflict * fix : 리뷰 내용 추가 REAMD.md * Rename : lowerCase to uppercase * Rename : cmd to command * Refactor : 배열을 변수에 할당 후 네이밍 * Refactor : remove result variable * Refactor : object to destructuring * Fix : else if to else * Refactor : modified try block * feat : create getLogObj method * Refactor : class to Object * Fix : this.utils to Utils object * Fix : remove duplicated method * fix : fix the indent --- README.md | 75 +++++++++++++++- class/README.md | 73 ++++++++++++++++ class/app.js | 43 +++++++++ class/commandParser.js | 42 +++++++++ class/customException.js | 33 +++++++ class/instruction.js | 183 +++++++++++++++++++++++++++++++++++++++ class/todosdata.json | 23 +++++ class/utils.js | 30 +++++++ 8 files changed, 500 insertions(+), 2 deletions(-) create mode 100644 class/README.md create mode 100644 class/app.js create mode 100644 class/commandParser.js create mode 100644 class/customException.js create mode 100644 class/instruction.js create mode 100644 class/todosdata.json create mode 100644 class/utils.js diff --git a/README.md b/README.md index d86263b..90f246d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,73 @@ -# todo -레벨2 +## STEP5-3 : TUNA & YM 리뷰 내용 반영 + +# +#### 1. comment : 전체코드의 들여쓰기가 너무 깊은데요? 좋은 indent 가 무엇인지 한번찾아볼래요? + +- 전체 코드 indent 수정 : tab 인덴트를 space * 4 로 수정 +- 원인 : pair를 위해 구름 ide에서 작업 중, local으로 옮기면서 indent 오반영 + +원래 ide의 설정은 tab이 space * 4 의 indent로 설정되어 있습니다. +앞으로 들여쓰기가 올바르게 되어있는지, 불편하지 않은지 신경쓰도록 하겠습니다 + +# + +#### 2. comment : 주석이 필요한.. arguments.length 로 구별하는 방식을 어떻게 개선할 수 있을까요? + +- 리뷰 이전 +```javascript +if (argument.length === 3) // updaste +... +} else if (arguments.length === 2) { // add, delete +``` + +- 리뷰 이후 : 의미가 불분명해 주석이 필요했던 if statement 수정 + +```javascript +if (command === 'update') { + ... +} else if (command === 'add' || command === 'delete') { + ... +} +``` + +# + +#### 3. utils 클래스의 deepCopy 메서드의 이해 + +- 원래 object.assign으로 얕은 복사를 썼으나, 예상한 방식대로 동작하지 않아 깊은 복사를 지원하는 deepCopy 유틸을 만들었습니다 +- 메서드는 검색을 통해 참고했으나, 참조 레퍼런스의 순수한 값만 복사하는 deep copy의 작동 방식은 이해했습니다 + +# + +#### 4. comment : 시작하는 것은 굳이 class로 만들지 않아도 될거 같아요. 사용일 될 일이 정말 없을 거 같은 부분이기도 하니까요. + +```javascript +const RunTodoApp = class { + constructor() { + this.cmdArr = ['show','delete','update','add','undo','redo']; + this.commandParser = new CommandParser(); +``` + +- 프로그램 실행을 담당하는 app.js의 class를 제거한 후, 리팩토링했습니다. + +# + +#### 5. comment : promise패턴을 사용했는데요. then메서드를 사용했을때의 장점은 무엇이라고 생각되세요? + +```javascript + this.utils.delay(0) + .then(() => {if (cmdList[0] === 'update') return this.utils.delay(3500);}) +``` + +- then메서드를 사용했을 경우, 새로운 프로미스 객체가 반환되어 여러 개의 Promise를 체이닝할 수 있습니다. +- 따라서, 여러 개의 비동기 작업을 수행할 경우, 보다 쉽게 비동기를 제어할 수 있다는 장점이 가장 큰 장점 같습니다. +- error 처리 또한 catch로 손쉽게 할 수 있다는 점도 장점입니다. + +# + +#### 6. comment : 이부분은 객체리터럴 vs. 클래스! 중 어떤 것이 더 어울릴까요? +```javascript +const CustomException = class { +``` +- 에러 처리는 변하는 값이 아닌 특수한 상황에서만 사용되는 고정된 값입니다. 따라서 동적인 인스턴스를 지원하는 class를 사요할 필요 없이 +속도가 빠르고 메모리 자원을 낭비하지 않는 리터럴이 적합하다 생각합니다. \ No newline at end of file diff --git a/class/README.md b/class/README.md new file mode 100644 index 0000000..90f246d --- /dev/null +++ b/class/README.md @@ -0,0 +1,73 @@ +## STEP5-3 : TUNA & YM 리뷰 내용 반영 + +# +#### 1. comment : 전체코드의 들여쓰기가 너무 깊은데요? 좋은 indent 가 무엇인지 한번찾아볼래요? + +- 전체 코드 indent 수정 : tab 인덴트를 space * 4 로 수정 +- 원인 : pair를 위해 구름 ide에서 작업 중, local으로 옮기면서 indent 오반영 + +원래 ide의 설정은 tab이 space * 4 의 indent로 설정되어 있습니다. +앞으로 들여쓰기가 올바르게 되어있는지, 불편하지 않은지 신경쓰도록 하겠습니다 + +# + +#### 2. comment : 주석이 필요한.. arguments.length 로 구별하는 방식을 어떻게 개선할 수 있을까요? + +- 리뷰 이전 +```javascript +if (argument.length === 3) // updaste +... +} else if (arguments.length === 2) { // add, delete +``` + +- 리뷰 이후 : 의미가 불분명해 주석이 필요했던 if statement 수정 + +```javascript +if (command === 'update') { + ... +} else if (command === 'add' || command === 'delete') { + ... +} +``` + +# + +#### 3. utils 클래스의 deepCopy 메서드의 이해 + +- 원래 object.assign으로 얕은 복사를 썼으나, 예상한 방식대로 동작하지 않아 깊은 복사를 지원하는 deepCopy 유틸을 만들었습니다 +- 메서드는 검색을 통해 참고했으나, 참조 레퍼런스의 순수한 값만 복사하는 deep copy의 작동 방식은 이해했습니다 + +# + +#### 4. comment : 시작하는 것은 굳이 class로 만들지 않아도 될거 같아요. 사용일 될 일이 정말 없을 거 같은 부분이기도 하니까요. + +```javascript +const RunTodoApp = class { + constructor() { + this.cmdArr = ['show','delete','update','add','undo','redo']; + this.commandParser = new CommandParser(); +``` + +- 프로그램 실행을 담당하는 app.js의 class를 제거한 후, 리팩토링했습니다. + +# + +#### 5. comment : promise패턴을 사용했는데요. then메서드를 사용했을때의 장점은 무엇이라고 생각되세요? + +```javascript + this.utils.delay(0) + .then(() => {if (cmdList[0] === 'update') return this.utils.delay(3500);}) +``` + +- then메서드를 사용했을 경우, 새로운 프로미스 객체가 반환되어 여러 개의 Promise를 체이닝할 수 있습니다. +- 따라서, 여러 개의 비동기 작업을 수행할 경우, 보다 쉽게 비동기를 제어할 수 있다는 장점이 가장 큰 장점 같습니다. +- error 처리 또한 catch로 손쉽게 할 수 있다는 점도 장점입니다. + +# + +#### 6. comment : 이부분은 객체리터럴 vs. 클래스! 중 어떤 것이 더 어울릴까요? +```javascript +const CustomException = class { +``` +- 에러 처리는 변하는 값이 아닌 특수한 상황에서만 사용되는 고정된 값입니다. 따라서 동적인 인스턴스를 지원하는 class를 사요할 필요 없이 +속도가 빠르고 메모리 자원을 낭비하지 않는 리터럴이 적합하다 생각합니다. \ No newline at end of file diff --git a/class/app.js b/class/app.js new file mode 100644 index 0000000..62f0c6a --- /dev/null +++ b/class/app.js @@ -0,0 +1,43 @@ +const readline = require('readline').createInterface( { + input:process.stdin, + output:process.stdout, +}); + +const CommandParser = require('./commandParser.js'); +const Utils = require('./utils.js'); +const Instruction = require('./instruction.js'); +const CustomException = require('./customException.js'); + +const commandArr = ['show','delete','update','add','undo','redo']; +const commandParser = new CommandParser(); +const instruction = new Instruction(); + +const run = (() => { + readline.setPrompt('명령하세요: '); + readline.prompt(); + readline.on('line', (userInput) => { + + try { + CustomException.missingSeperatorException(userInput); + const commandList = commandParser.getCommandList(userInput); + + CustomException.CommandMissingException(commandList[0], commandArr); + commandParser.executeCommand(commandList); + + Utils.delay(0) + .then(() => {if (commandList[0] === 'update') return Utils.delay(3500);}) + .then(() => {return Utils.delay(1000);}) + .then(() => {if (commandList[0] !== 'show') instruction.show('all');}) + .catch(function(e) {console.log(e);}) + .then(() => {readline.prompt();}); + + } catch(e) { + console.error(e.message); + readline.prompt(); + } + +}).on('close', () => { + console.log("프로그램을 종료합니다."); + process.exit(); + }); +})(); \ No newline at end of file diff --git a/class/commandParser.js b/class/commandParser.js new file mode 100644 index 0000000..87f8fe5 --- /dev/null +++ b/class/commandParser.js @@ -0,0 +1,42 @@ +const Instruction = require('./instruction.js'); +const CustomException = require('./customException.js'); + +const CommandParser = class { + + constructor() { + this.instruction = new Instruction(); + } + + getCommandList(input) { + const regexp = /[^\$]+|undo|redo/g; + return input.match(regexp); + } + + executeCommand(command) { + try { + + let [primaryCommand, firstSubCommand, secondSubCommand] = [command[0], command[1], command[2]]; + + if (command.length === 1) { + this.instruction[primaryCommand](); + } else if (command.length === 2) { + this.instruction[primaryCommand](firstSubCommand); + } else if (command.length === 3) { + this.instruction[primaryCommand](firstSubCommand, secondSubCommand); + } else { + CustomException.CommandMissingException(); + } + + } catch (e) { + console.error(e.message); + return; + } + } + + isValidCommand(command, arr) { + if (arr.includes(command)) return true; + return false; + } +}; + +module.exports = CommandParser; \ No newline at end of file diff --git a/class/customException.js b/class/customException.js new file mode 100644 index 0000000..59cc4bf --- /dev/null +++ b/class/customException.js @@ -0,0 +1,33 @@ +const CustomException = { + missingSeperatorException : function(input) { + if (!this.isValidSeperator(input)) throw new Error("구분자 $가 존재하지 않습니다"); + }, + + notExistIdException : function() { + throw new Error(`찾으시는 id가 존재하지 않습니다`); + }, + + sameStatusException : function() { + throw new Error(`같은 상태로 업데이트 할 수 없습니다`); + }, + + CommandMissingException : function(command, arr) { + if (!this.isValidCommand(command, arr)) + throw new Error(`올바른 명령어가 아닙니다`); + }, + + isValidSeperator : function(input) { + let result = true; + const regexp = /\$|undo|redo/g; + if (input.match(regexp) == null) result = false; + + return result; + }, + + isValidCommand : function(command, arr) { + if (arr.includes(command)) return true; + return false; + } +}; + +module.exports = CustomException; \ No newline at end of file diff --git a/class/instruction.js b/class/instruction.js new file mode 100644 index 0000000..563e923 --- /dev/null +++ b/class/instruction.js @@ -0,0 +1,183 @@ +const originData = require('./todosdata.json'); +const convertedData = JSON.parse(JSON.stringify(originData)).data; +const Utils = require('./utils.js'); +const CustomException = require('./customException.js'); + +const Instruction = class { + constructor() { + this.minIdNum = 1; + this.maxIdNum = 99999; + + this.commandHistory = { + historyArr : [], + pointer : -1, + }; + } + + everyStatus (convertedData) { + let [numOfTodos, numOfDoings, numOfDones] = [0,0,0]; + convertedData.forEach(({status}) => { + if (status === 'todo') numOfTodos++; + else if (status === 'doing') numOfDoings++; + else if (status === 'done') numOfDones++; + }); + + console.log(`현재상태 : todo: ${numOfTodos}개, doing: ${numOfDoings}개, done: ${numOfDones}개`); + } + + singleStatus (convertedData, status) { + const tasks = Utils.getArrByCondition(convertedData, (val) => { return (status === val.status);}); + let message = `${status}리스트 총 ${tasks.length}건 : `; + tasks.forEach( obj => { + message += `'${obj.name}, ${obj.id}번,' `; + }); + console.log(message); + } + + show(status) { + if (status === 'all') { + this.everyStatus(convertedData); + } else { + this.singleStatus(convertedData, status); + } + } + + add(name, tags) { + const id = Utils.getRandomId(this.maxIdNum, this.minIdNum); + let obj = { + name, + tags, + status: 'todo', + id, + }; + convertedData.push(obj); + if (arguments.length !== 3) { + this.createHistoryObj('add' ,obj); + } + const message = `${obj.name} 1개가 추가됐습니다.(id : ${obj.id})`; + console.log(message); + } + + delete(id) { + try { + + const targetObj = Utils.getArrByCondition(convertedData, (val) => { return id == val.id;})[0]; + if (!targetObj) customException.notExistIdException(); + + convertedData.splice(convertedData.indexOf(targetObj), 1); + + if (arguments.length == 1) { + this.createHistoryObj('delete', obj); + } + + let message = `${targetObj.name}이 ${targetObj.status}에서 삭제되었습니다.`; + console.log(message); + + } catch (e) { + console.error(e.message); + return; + } + } + + update(id, status) { + const targetObj = Utils.getArrByCondition(convertedData, (val) => { return id == val.id;})[0]; + try { + if (!targetObj) CustomException.notExistIdException(); + if (targetObj.status === status) CustomException.sameStatusException(); + } catch (e) { + console.error(e.message); + return; + } + + let preObj = {}; + if (arguments.length === 2) { + preObj = Utils.deepCopy(targetObj); + } + + targetObj.status = status; + const nextObj = Utils.deepCopy(targetObj); + + if (arguments.length === 2) { + this.createHistoryObj('update', preObj, nextObj); + } + const message = `${targetObj.name}가 ${targetObj.status}로 상태가 변경되었습니다`; + + setTimeout(() => { + console.log(message); + }, 3000); + } + + pushObj(arr, obj) { + if (arr.length === 3) { + arr.shift(); + arr.push(obj); + } else if (arr.length < 3) { + arr.push(obj); + } + } + + createHistoryObj(command, ...obj) { + let historyObj = {}; + if (command === 'update') { + historyObj = { + command, + pre: obj[0], + next: obj[1] + }; + } else if (command === 'add' || command === 'delete') { + historyObj = { + command, + pre: obj[0] + }; + } + this.pushObj(this.commandHistory.historyArr, historyObj); + this.commandHistory.pointer++; + } + + makeLogObj(commandHistory) { + const targetObject = commandHistory.historyArr[this.commandHistory.pointer]; + return {command : targetObject.cmd, preObj : targetObject.pre, nextObj : targetObject.next, checkSum : true, message : ``}; + } + + undo() { + + const log = this.makeLogObj(this.commandHistory); + + if (log.command === 'add') { + this.delete(log.preObj.id, log.checkSum); + log.message += `${log.preObj.id}번 항목 '${log.preObj.name}'가 ${log.preObj.status} 상태에서 삭제됐습니다`; + + } else if (log.command === 'delete') { + convertedData.push(log.preObj); + log.message += `${log.preObj.id}번 항목 '${log.preObj.name}'가 삭제에서 ${log.preObj.status} 상태로 변경됐습니다`; + + } else if (log.command === 'update') { + this.update(log.preObj.id, log.preObj.status, log.checkSum); + } + + console.log(log.message); + if (this.commandHistory.pointer > -1) this.commandHistory.pointer--; + } + + redo() { + + if (this.commandHistory.pointer < 2) this.commandHistory.pointer++; + + const log = this.makeLogObj(this.commandHistory); + + if (log.command === 'add') { + convertedData.push(log.preObj); + message += `${log.preObj.id}번 항목 '${log.preObj.name}'가 ${log.preObj.status}에 추가되었습니다`; + + } else if (log.command === 'delete') { + this.delete(log.preObj.id, log.checkSum); + message += `${log.preObj.id}번 항목 '${log.preObj.name}'가 ${log.preObj.status} 상태에서 삭제됐습니다`; + + } else if (log.command === 'update') { + this.update(log.nextObj.id, log.nextObj.status, log.checkSum); + } + console.log(log.message); + } +}; + +module.exports = Instruction; \ No newline at end of file diff --git a/class/todosdata.json b/class/todosdata.json new file mode 100644 index 0000000..282d7ba --- /dev/null +++ b/class/todosdata.json @@ -0,0 +1,23 @@ +{ + "result" : true, + "data": [ + { + "name" : "자바스크립트 공부하기", + "tags" : ["programming", "javascript"], + "status" : "todo", + "id" : 123 + }, + { + "name" : "그림 그리기", + "tags" : ["picture", "favorite"], + "status" : "doing", + "id" : 312323 + }, + { + "name" : "파이썬 공부하기", + "tags" : ["programming", "javascript"], + "status" : "done", + "id" : 213232 + } + ] +} \ No newline at end of file diff --git a/class/utils.js b/class/utils.js new file mode 100644 index 0000000..e722613 --- /dev/null +++ b/class/utils.js @@ -0,0 +1,30 @@ +const Utils = { + getArrByCondition : function(arr, condition) { + return arr.reduce((acc, val) => { + if (condition(val)) acc.push(val); + return acc; + }, []); + }, + + getRandomId : function(max, min) { + return Math.floor(Math.random() * (max-min)) + 1; + }, + + delay : function(time) { + return new Promise(function(resolve, reject){ + setTimeout(resolve, time); + }); + }, + + deepCopy : function(obj) { + if (obj === null || typeof(obj) !== "object") return obj; + + let copy = {}; + for(let key in obj) { + copy[key] = this.deepCopy(obj[key]); + } + return copy; + } +}; + +module.exports = Utils; \ No newline at end of file