강신규
[우테코 프리코스/7기] 프리코스 1주 차 회고 본문
우아한테크코스(우테코) 7기에 참가하게 되었습니다. 물론 최종 합격하여 끝까지 완주하고 싶지만, 프리코스만으로도 많은 것을 배울 수 있을 것 같아 지원하게 되었습니다. 이번 프리코스의 목표는 다른 사람의 코드를 보며 배우고, 코드 리뷰를 통해 나 자신을 발전시키는 것입니다. 그리고 배운 것들을 이유 중심으로 정리하는 것 또한 중요한 목표로 삼았습니다.
최종코드 :
https://github.com/singyuKang/javascript-calculator-7/tree/singyuKang
문제 - 문자열 덧셈 계산기
입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현하는 문제였습니다.
함수의 구현에 따라 ReadMe 파일을 다음과 같이 정리하였습니다.
## 함수 구현
### InputView
- getUserInput : 사용자 입력을 받아 유효한 경우 반환하며, 그렇지 않은 경우 에러 처리
### OutputView
- printResult : 계산된 결과값을 출력
### InputValidation
- isValidateForm : 입력 형식이 올바른지 확인
- customDelimeterInputValidate : 커스텀 구분자를 사용하는 경우 입력 값의 유효성 확인
- defaultDelimterInputValidate : 기본 구분자를 사용하는 경우 입력 값의 유효성 확인
### CalculatorGenerator
- getResult : 계산된 결과 값 반환
- checkCustomDelimiter : 커스텀 구분자 사용 여부 확인
- calculatDelimiter : 기본 구분자를 사용하는 경우 값을 계산
- calculateCustomDelimiter : 커스텀 구분자를 사용하는 경우 값을 계산
- calculateNumber : 유효한 숫자 배열을 받아 값을 계산하여 반환
### Constants
#### ERROR_MESSAGE
- WRONG_INPUT_STRING : 입력 값이 문자열이 아닌 경우
- WRONG_INPUT_STRING_NEGATIVE : 입력 값에 음수가 포함된 경우
- WRONG_CUSTOM_DELIMETER : 커스텀 구분자 입력 시 잘못된 값이 있는 경우
- WRONG_DEFAULT_DELIMETER : 기본 구분자 입력 시 잘못된 값이 있는 경우
#### DEFAULT_DELIMITER
- 쉼표(,)와 콜론(:)을 기본 구분자로 사용
#### INPUT_MESSAGE , OUTPUT_MESSAGE
- COMPOUND_STRING : 입력 시 사용자에게 표시할 메시지
- RESULT : 계산 결과를 출력할 메시지
구현시작
초기에는 App.js 파일 하나에 모든 코드를 넣는 방식으로 구현을 시작했습니다. 하지만, 이런 구조로는 발전의 기회를 놓칠 것 같아 다른 분들의 코드를 살펴보니 기능 단위로 코드를 분리하여 작성하고 있었습니다. 평소에 이러한 연습이 부족했던 저는, 이번 기회를 통해 함수를 사용해 기능별로 코드를 나누는 방식을 시도해 보았습니다.
** 초기 코드 **
class App {
async run() {
let inputString = await MissionUtils.Console.readLineAsync(
"덧셈할 문자열을 입력해 주세요. "
);
Console.print(inputString);
const defaultDelimiters = /[,:]/; // 기본 구분자 쉼표(,)와 콜론(:)
let numbers = [];
let customDelimiter = null;
// 커스텀 구분자를 확인하는 부분
if (inputString.startsWith("//")) {
const delimiterEndIndex = inputString.indexOf("\\n");
console.log("🚀 ~ App ~ run ~ delimiterEndIndex:", delimiterEndIndex);
if (delimiterEndIndex === -1) {
Console.print("[ERROR]");
return;
}
customDelimiter = inputString.substring(2, delimiterEndIndex);
console.log("🚀 ~ App ~ run ~ customDelimiter:", customDelimiter);
inputString = inputString.substring(delimiterEndIndex + 2); // 구분자 정보 이후부터 숫자 시작
}
// 입력된 문자열을 구분자로 분리
const delimiter = customDelimiter
? new RegExp(`[${customDelimiter}]`)
: defaultDelimiters;
numbers = inputString.split(delimiter);
Console.print(numbers);
for (let number of numbers) {
if (isNaN(number) || number === "") {
Console.print("[ERROR]");
return;
}
}
const SUM = numbers.reduce((acc, cur) => acc + parseInt(cur, 10), 0);
Console.print(`결과 : ${SUM}`);
}
}
리팩토링 진행후
기능을 크게 3가지로 나누었습니다
1. 입력 처리만 담당하는 InputView
2. 입력된 값을 처리해주는 CalcuratorGenerator
3. 그 값을 출력을 담당해주는 OutputView
/**
* 계산기 메인
*/
class App {
async run() {
try {
const userInputString = await InputView.getUserInput();
const result = await CalculatorGenerator.getResult(userInputString);
OutputView.printResult(result);
} catch (error) {
throw error;
}
}
}
기능별로 코드를 분리하니 한눈에 보기도 쉬워졌고, 유지보수가 더욱 용이해졌습니다. 각 기능별로 코드를 수정할 수 있게 되니 협업 시에도 많은 도움이 될 것이라는 생각이 들었습니다.
에러처리
이번 과제에서 특히 신경을 쓴 부분은 에러 처리였습니다. 구현 자체는 단순했지만, 사용자가 잘못된 입력을 했을 때 이를 처리하는 것은 프론트엔드 개발자의 중요한 역할이라 생각하여, 많은 고민 끝에 Validation 로직을 작성하였습니다.
const InputValidation = {
isValidateForm(input) {
if (typeof input !== "string") {
throw new Error(ERROR_MESSAGE.WRONG_INPUT_STRING);
}
if (input.startsWith("//")) {
this.customDelimeterInputValidate(input);
} else {
this.defaultDelimterInputValidate(input);
}
},
/**
*
* 1. "//" 다음에 "\n"이 없으면 예외 처리
* 2. 구분자가 비어 있거나 길이가 1을 초과하는 경우 예외 처리
* 3. 구분자가 숫자일 경우 예외 처리
* 4. 구분자로 분리한 숫자 배열에서 숫자가 아닌 값 또는 빈 값이 있는 경우 예외 처리
*
*/
customDelimeterInputValidate(input) {
let intStr = input;
let customDelimeterEndIndex = intStr.indexOf("\\n");
if (customDelimeterEndIndex === -1) {
throw new Error(ERROR_MESSAGE.WRONG_CUSTOM_DELIMETER);
}
let customDelimeter = intStr.substring(2, customDelimeterEndIndex);
if (customDelimeter.length === 0 || customDelimeter.length > 1) {
throw new Error(ERROR_MESSAGE.WRONG_CUSTOM_DELIMETER);
}
if (!isNaN(customDelimeter)) {
throw new Error(ERROR_MESSAGE.WRONG_CUSTOM_DELIMETER);
}
let numbers = [];
intStr = intStr.substring(customDelimeterEndIndex + 2);
numbers = intStr.split(customDelimeter);
for (let number of numbers) {
if (isNaN(number)) {
throw new Error(ERROR_MESSAGE.WRONG_CUSTOM_DELIMETER);
}
if (Number(number) < 0) {
throw new Error(ERROR_MESSAGE.WRONG_CUSTOM_DELIMETER);
}
}
},
defaultDelimterInputValidate(input) {
if (input !== "") {
let numbers = [];
numbers = input.split(DEFAULT_DELIMITER);
for (let number of numbers) {
if (isNaN(number)) {
throw new Error(ERROR_MESSAGE.WRONG_DEFAULT_DELIMETER);
}
if (Number(number) < 0) {
throw new Error(ERROR_MESSAGE.WRONG_INPUT_STRING_NEGATIVE);
}
}
}
},
};
Object.freeze()
사용자가 입력한 메시지나 데이터를 임의로 변경하지 못하도록 해야 할 필요성을 느꼈고, 방법을 찾다가 Object.freeze()를 알게 되었습니다. 이를 통해 객체를 동결하여 변경 불가능하게 만드는 방법을 적용해보았습니다.
/**
* 메세지 , 디폴트 구분자 상수
*/
const ERROR_MESSAGE = {
WRONG_INPUT_STRING: Object.freeze(`[ERROR] 입력 형식이 잘못되었습니다.`),
WRONG_INPUT_STRING_NEGATIVE: Object.freeze(
`[ERROR] 음수는 입력할 수 없습니다.`
),
WRONG_CUSTOM_DELIMETER: Object.freeze(
`[ERROR] 커스텀 구분자가 올바르지 않습니다.`
),
WRONG_DEFAULT_DELIMETER: Object.freeze(
`[ERROR] 기본 구분자가 올바르지 않습니다.`
),
};
느낀점
프리코스를 진행하면서 기능별로 코드를 나누고 함수를 사용하는 이유에 대해 많이 배웠습니다. 특히, 입력 처리, 에러 처리, 출력 처리를 각각 함수로 나누니 재사용성도 높아졌고, 각 부분에 집중할 수 있어서 효율적이었습니다.
함수의 기능별 분리를 통해 코드를 이해하기 쉬워졌으며 유지보수에 용이해졌음을 깨닳았습니다.
사용자가 애플리케이션을 사용할 때, 잘못된 입력으로 인해 오류가 발생하면 어떤 문제가 발생했는지 명확하게 처리하고 전달하는 것이 중요하다는 것을 깨달았습니다. 이를 통해 사용자에게 발생한 문제를 정확하게 알리고, 이를 해결할 수 있도록 안내하는 것이 필요하다는 점을 배웠습니다.
'우아한테크코스' 카테고리의 다른 글
[우테코 프리코스/7기] 프리코스 4주 차 회고 (3) | 2024.11.13 |
---|---|
[우테코 프리코스/7기] 프리코스 3주 차 회고 (1) | 2024.11.12 |
[우테코 프리코스/7기] 프리코스 2주 차 회고 (1) | 2024.11.12 |