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/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/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 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 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 + } + ] +}