오래전부터 우아한테크코스에 지원해야겠다고 다짐했었다. 우아한테크코스에 합격했으면 좋겠다는 마음이 크지만, 설령 합격하지 못하더라도 4주간의 프리코스를 통해 한층 더 성장하는 시간을 가질 수 있을 것이다. 열심히 몰입해서 많은 것을 얻어가고 싶었고. 이런 생각들은 자기소개서를 작성하며 더욱 커져갔다.
1주차 목표
프리코스 1주차에 내가 세운 목표는 다음과 같았다.
- 다음 주부터 중간고사가 시작되는데, 중간고사 대비 공부하는 시간과 프리코스에 투자하는 시간을 1:1 비율로 맞춘다.
- 프리코스를 할 때에는 프리코스에만 몰입한다.
- 프리코스를 진행하며 배운 점들을 글로 기록한다.
다음 주에 중간고사 시험을 응시해야되서 여유로운 상황은 아니었지만, 나에게 있어서 프리코스의 우선순위는 중간고사보다 높았기에 꾸준히 프리코스에 시간을 투자했다. 이런 상황이었기에 프리코스를 하는 와중에도 시험 공부를 해야되는 것 아닐까하는 불안감이 올라올 때가 있었다. 하지만 어차피 시험 공부만 한다고 했어도 항상 집중하며 공부하지는 않았을 것을 알았기에 프리코스를 할 때에는 프리코스에만, 시험공부를 할 때에는 시험 공부에만 몰입하려고 노력하면서 늘어지는(즉, 집중이 안되는) 시간을 없애려고 노력했다.
기능 요구 사항
10월 15일 오후 3시, 우아한테크코스 프리코스 1주차 미션이 공개되었다. 이번 주차의 학습 목표는 Git, GitHub, IDE 등 실제 개발 환경에 익숙해지는 것과 교육 분야에 적합한 프로그래밍 언어를 사용하여 간단한 문제를 해결하는 것이었다. 참여자들에게 요구된 구현 기능은 다음과 같았다.
입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.
- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다.
- 예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6
- 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다.
- 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.
- 사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 Error를 발생시킨 후 애플리케이션은 종료되어야 한다.
그리고, 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리하고, 커밋은 README.md에 정리한 기능 목록 단위로 추가해야 한다. 그렇다면 무작정 기능을 구현하려고 코딩을 시작하면 안되고, 어떤 기능들을 구현할 것인지 설계부터 시작해야한다. 그래서 기능 요구 사항을 계속 확인하며 어떤 기능을 어떻게 구현해야할지 생각했다.
설계한 기능 목록
설계했던 기능 목록은 다음과 같다(README.md에서 확인할 수 있다)
기능 목록
- 기능1. 초기 배열 생성(구분자 배열, 추출된 숫자 배열)
- 구분자 배열에는 기본 구분자인 쉼표와 콜론이 들어가있고, 앞으로 지정된 커스텀 구분자들이 배열에 추가된다.
- 추출된 숫자 배열은 일단 빈 배열이고, 숫자 추출 함수가 숫자를 추출하면 그 숫자를 이 배열에 추가하게 된다.
---
- 기능2. 사용자의 값 입력 및 출력
- 입력은 @woowacourse/mission-utils의 Console API에서 Console.readLineAsync()를 활용한다.
- 출력은 @woowacourse/mission-utils의 Console API에서 Console.print()를 활용한다.
---
- 기능3. 빈 문자열 처리
- 빈 문자열이 입력으로 들어오면 0를 return하는 함수를 생성한다.
---
- 기능4. 에러 출력
- "[ERROR]"로 시작하는 메시지와 함께 Error를 발생시킨 후 애플리케이션이 종료되는 함수를 생성한다.
---
- 기능5. 숫자 판별
- 문자가 숫자인지, 아니면 숫자가 아닌지 판별해서 숫자이면 true를, 숫자가 아니면 false를 return하는 함수를 생성한다.
---
- 기능6. 커스텀 구분자 지정
- 문자열에서 “//”와 그 뒷부분에 “\\n”이 있는지 인식하고, 그 사이에 있는 문자를 구분자 배열에 넣어주는 역할을 하는 함수를 생성한다.
- 이 함수는 커스텀 구분자를 구분자 배열에 넣은 이후에 문자열에서 “//문자\\n”을 삭제하는 역할도 수행한다.
---
- 기능7. 문자열 분할 및 숫자 추출
- 구분자 배열을 사용하여 문자열을 분할하고 추출된 숫자를 추출된 숫자 배열에 넣는 함수를 생성한다.
- 이 때 기능5.숫자 판별 함수를 사용하여 추출된 문자가 숫자가 아니라면 에러를 출력하고 프로그램을 종료한다.
---
- 기능8. 추출된 숫자 합 계산
- 추출된 숫자 배열에서 추출된 숫자들의 합을 계산하는 함수를 생성한다.
기능1부터 차례대로 구현했고, 기능이 잘 작동했는지 확인하기 위해 임시로 기능을 활용해서 출력을 함으로써 테스트를 진행했다.
async run() {
const INPUT = await Console.readLineAsync(
'덧셈할 문자열을 입력해주세요.\\n'
);
let result;
if (this.isEmpty(INPUT)) {
result = 0;
} else if (INPUT === 'error') {
return this.printError("'error'를 입력했습니다");
} else {
result = INPUT;
}
this.printResult(result);
}
isEmpty(str) {
return str.trim().length === 0;
}
printResult(result) {
Console.print(`결과 : ${result}`);
}
printError(errorMessage) {
Console.print(`[ERROR] : ${errorMessage}`);
return;
}
}
위 코드는 기능4. 에러 출력을 구현할 때의 코드인데, 임시로 ‘error’를 입력하면 에러 출력 함수인 printError가 실행되게 구현을 해놔서 잘 작동하는지 확인했다.
리팩토링
기능8까지 구현한 후에도 터미널에 ‘npm run test’ 명령어를 입력해서 진행되는 테스트를 통과하지 못했다. 분석해본 결과 원인은 총 두 가지였었는데,
- 양수가 아닌 값이 입력되었을 때에 대한 처리를 하지 않았다(입출력 요구 사항에서 입력은 구분자와 양수로 구성된 문자열이다)
- 에러가 발생하면 에러 문구를 출력하고 프로그램이 종료되어야 하는데, 나는 printError 함수를 통하여 에러 메시지를 출력하고 있었으므로 프로그램이 곧바로 종료되지 않았다.
1. 에러 처리 개선
먼저 에러 처리를 개선해줬다. 단순히 에러를 출력해주는 함수 printError를
printError(errorMessage) {
Console.print(`[ERROR] : ${errorMessage}`);
return;
}
에러를 직접 throw하는 함수 handleError 함수로 수정했다.
handleError(error) {
throw new Error(`[ERROR] ${error.message}`);
}
그리고 run 함수 전체를 try-catch문으로 감싸줬다.
2. 양수가 아닌 값 입력에 대한 오류 처리 추가
함수 validateNumber에서 num이 양수가 아니면 에러를 throw하도록 구현했다.
그리고 숫자가 아닌 문자 입력의 경우에도 에러를 throw하도록 변경했다.
if (isNaN(num)) {
throw new Error('숫자가 아닌 문자를 입력하셨습니다');
}
if (num <= 0) {
throw new Error('양수를 입력해주세요');
}
3. 커스텀 구분자로 숫자를 사용할 수 없게 처리
만약에 커스텀 구분자로 숫자를 사용하면 어떻게 처리해야할까? 커스텀 구분자가 5고, 353이 들어오면 답은 353이 되어야할까, 아니면 3+3=6이 되어야할까?
고민해본 결과 커스텀 구분자로 숫자를 사용할 수 없게 구현하는 것이 맞다는 결론을 내렸다.
if (!isNaN(CUSTOM_SEPARATOR)) {
throw new Error('커스텀 구분자로 숫자를 사용할 수 없습니다');
}
handleCustomSeparator 함수에서 커스텀 구분자가 숫자인 경우 에러를 throw하도록 구현했다.
4. 커스텀 구분자로 공백 허용
커스텀 구분자로 공백을 허용해야할까 고민했다. 기능 요구 사항에서는 ‘커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다’라고 작성되어있었다. 공백도 하나의 문자라고 판단했고, “//”와 “\n” 사이에 공백이 들어오면 공백을 커스텀 구분자로 지정될 수 있게 코드를 수정했다.
커스텀 구분자로 공백을 허용하기 위해서 정규표현식을 수정했다.
이랬던 정규표현식을
const CUSTOM_SEPARATOR_REGEX = /^\\/\\/(.)\\\\n(.*)$/;
다음과 같이 수정했다. (정규표현식에서 ‘\s’는 공백 문자를 나타낸다)
const CUSTOM_SEPARATOR_REGEX = /^\\/\\/(.|\\s)\\\\n(.*)$/;
전 커밋에서 커스텀 구분자로 숫자가 들어오는지 검사하는 로직을 추가했었다. 그런데 그 로직에서 공백 문자를 숫자로 받아들여서 에러가 발생했다.
따라서 해당 로직에 커스텀 구분자가 공백이라면 isNaN을 실행하지 않도록 코드를 수정했다.
if (CUSTOM_SEPARATOR !== ' ' && !isNaN(CUSTOM_SEPARATOR)) {
5. 변수 및 상수 네이밍 컨벤션 수정
프리코스를 시작할 때, 지원하기 페이지에서 제공한 자바스크립트 스타일 가이드 문서에서 다음과 같은 부분을 확인했었다.
위 내용을 확인하고 지금까지 별 고민없이, const가 붙은 모든 상수(명)을 대문자 스네이크 케이스로 네이밍을 했었다.
하지만 곰곰이 생각해보니 const가 붙었다고 전부 대문자 스네이크 케이스로 네이밍하는 것은 옳지 않은 것 같았다.
그래서 다음과 같이 수정했다.
- 클래스 상수 프로퍼티(SEPARATORS, EXTRACTED_NUMBERS)를 UPPER_SNAKE_CASE로 통일
- 지역 변수 및 매개변수를 camelCase로 변경 (input, proccessedInput, customSeparator 등)
- 정규표현식 상수(CUSTOM_SEPARATOR_REGEX, REGEX)를 UPPER_SNAKE_CASE로 유지
- 클래스 내 상수 참조를 일관되게 수정 (this.separators -> this.SEPARATORS)
코드를 다시 상세히 살펴보니 지역 변수 및 매개 변수는 camelCase로 변경하는 것이 맞는 것 같아 변경을 진행했다. 그리고 상수들은 UPPER_SNAKE_CASE로 유지했는데, 이 부분이 잘 지켜지지 않는 부분들이 있어서 다시 일관되게 수정해줬다.
6. 큰 숫자로 인한 오버플로우 예외 처리
만약에 오버플로우가 발생하는 큰 숫자가 입력되거나, 숫자의 합이 오버플로우가 발생하면 에러가 발생할 것이다.
자바스크립트에는 Number.MAX_SAFE_INTEGER 정적 데이터 속성을 통하여 자바스크립트의 최대 안전 정수 값을 나타낼 수 있다.
먼저 validateNumber 함수에 Number.MAX_SAFE_INTEGER 초과 시 예외 추가를 해주었고,
calculateSum 함수에 오버플로우 발생 가능성 검사 및 예외를 추가해주었다.
또한 에러 메시지에서는 에러 메시지에 최대 허용 숫자를 Number.MAX_SAFE_INTEGER를 통하여 명시해주었다. toLocalString()을 활용하여 가독성도 향상시켰다.
흐름도
App.js의 로직을 흐름도로 표시해보았다.
배운 점
프리코스 1주차 미션을 진행하면서 생각보다 많은 내용을 배우게 되었다. 하나씩 정리해보려 한다.
1. 프로젝트 개발 환경 버전 관리(Node.js, npm)
프로그래밍 요구 사항에서 ‘Node.js 20.17.0 버전에서 실행 가능해야 한다.’라는 문구가 있었다.
실제로 내 컴퓨터에 설치되어 있었던 Node.js는 20.11.0 버전이었는데, 터미널에 ‘npm install’을 입력했을 때 다음과 같은 에러가 발생했다.
따라서 Node.js를 삭제하고 공식 홈페이지에서 다시 설치해주었다. 그리고 다시 터미널에 ‘npm install’ 명령어를 입력했었는데, 에러가 계속 발생한 것이었다.
에러 메시지를 다시 살펴보니 npm의 버전이 필수 요구 버전보다 낮아서 터미널에 ‘npm install -g npm’ 명령어를 입력해줘서 다시 설치해주었고, 에러는 더 이상 발생하지 않았다.
이 경험을 통하여 프로젝트 개발 환경의 버전 관리에 대하여 더욱 명확히 인식할 수 있고, npm이 Node.js의 패키지 관리 시스템이라는 사실 또한 알 수 있었다.
2. 자바스크립트 클래스 안에서의 메서드 사용
처음에는 class App 안에서 function을 통하여 메서드(함수)를 선언하려고 했지만 에러가 발생했다.
자바스크립트 클래스 내부에서 메서드를 정의할 때는 function 키워드를 사용하지 않고, 메서드 이름 뒤에 바로 괄호와 중괄호를 사용하여 메서드를 정의한다는 사실을 알게 되었다.
3. 깃 커밋 컨벤션(AngularJS Git Commit Message Conventions)
‘지원하기’ 페이지에서 제공한 ‘AngularJS Git Commit Message Conventions’ 링크를 통하여 자세하고 일관되게 깃 커밋을 하는 방법을 배울 수 있었다.
AngularJS Git Commit Message Conventions은 <type>(<scope>): <subject>의 형식으로 이루어져있다.
여기서 type은
- feat: 새로운 기능
- fix: 버그 수정
- docs: 문서 변경
- style: 코드 포맷팅, 세미콜론 누락 등
- refactor: 코드 리팩토링
- test: 테스트 추가 또는 수정
- chore: 빌드 프로세스 또는 보조 도구 변경
(소문자로 작성한다)
제목<subject>은 변경 사항에 대한 간단한 설명을 작성하면 되는데, 명령형이고 현재 시제로 작성한다. 본문 또한 현재형으로 작성한다.
참고로 범위<scope>, 본문,푸터<footer>는 선택적이다.
4. throw를 통한 에러 처리
지원하기 페이지에 다음과 같이 명시되어 있다.
‘사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 Error를 발생시킨 후 애플리케이션은 종료되어야 한다.’
’프로그램 종료 시 process.exit()를 호출하지 않는다.’
위 사항들을 지키기 위해서는 throw를 통한 에러 처리를 해야 했는데, 지금까지 throw를 많이 사용해보지 않아서 이번 프리코스를 통하여 사용법을 배울 수 있었다.
run 메서드를 try-catch문으로 감싸서 다른 메서드에서 throw된 에러들을 catch에서 handleError 메서드를 통하여 처리할 수 있게 만들었다.
그리고 handleError 메서드에서 error.message를 활용했는데, error.message를 통하여 원래 발생한 에러의 구체적인 내용을 유지할 수 있다는 사실 또한 알게 되었다.
5. 정규표현식
이번 미션에서 커스텀 구분자를 다룰 때 다음과 같은 정규표현식을 활용했다.
const CUSTOM_SEPARATOR_REGEX = /^\\/\\/(.|\\s)\\\\n(.*)$/;
해석하자면,
- ^ : 문자열의 시작
- \\/\\/ : 문자열이 '//'로 시작해야 함을 나타낸다. '/'는 정규표현식에서 특별한 의미를 가지므로 이스케이프 처리되어 있다.
- (.|\\\\s) : 임의의 문자(.) 또는(|) 공백 문자(\\\\s)를 캡처합니다.(커스텀 구분자 부분)
- \\\\n : 개행 문자('\n')
- (.*) : 개행 문자 이후의 모든 문자를 캡처. → 실제로 분리될 문자열
- $ : 문자열의 끝
이번 미션을 통해 정규표현식을 통하여 문자열을 검색하는 경험을 할 수 있었다. 앞으로도 프로그래밍을 하면서 특정 문자를 찾아야되는 상황이 발생할 수 있는데, 그 상황에 대비하기 위하여 정규표현식에 더욱 익숙해져야겠다는 다짐을 했다.
6. 정적 데이터 속성 Number.MAX_SAFE_INTEGER
위에서 언급했듯이 큰 숫자로 인한 오버플로우 예외 처리를 할 때 정적 데이터 속성인 Number.MAX_SAFE_INTEGER를 사용했다.
정적 데이터 속성(Static Data Property)은 클래스나 객체 생성자에 직접 붙어있는 속성이다. 인스턴스를 생성하지 않고도 접근할 수 있고, 클래스나 생성자 함수의 모든 인스턴스가 공유한다. 일반적으로는 대문자로 작성하여 상수임을 나타낸다.
그리고 Number.MAX_SAFE_INTEGER는 Number 객체의 정적 데이터 속성이며, 자바스크립트에서 안전하게 표현할 수 있는 가장 큰 정수를 나타낸다.(2^53 - 1 또는 9,007,199,254,740,991)
앞으로도 오버플로우에 대한 예외 처리를 할 때 Number.MAX_SAFE_INTEGER는 유용하게 사용할 수 있기 때문에 기억해두고, 정적 데이터 속성에 대하여 더 익혀둬야겠다는 생각을 했다.
후기
프리코스 1주차를 진행하며 throw를 통한 에러처리, 정규표현식 등 다양한 개념을 배우고 또 응용해볼 수 있었다. 코딩을 하기 전에 먼저 설계를 하고, 기능별로 커밋을 하고, 또 내가 빠뜨린 부분이 있나 고민하면서 리팩토링을 하는 단계를 거쳤다. 이 과정들 가운데에서 평소보다 개발 진행이 느려 조금 더 서둘러야하지 않나하는 조급함이 피어났지만, 생각을 해가며 차분히 진행하는 것이 가장 빠른 길이라는 사실을 떠올리며 개발을 진행했다.
커밋을 보면 알겠지만, 아직 많은 부분이 서툴렀다는 생각이 든다. 보완해야할 점이 많다는 뜻이다. 자신의 코드를 제대로 평가받을 일이 많지 않았기에 작은 일 하나 진행하면서도 망설였던 것 같다. 하지만 다행인 점은 아직 2,3,4주차가 남아있다는 사실이다. 문제를 계속 고민하면서, 또 어떤 선택을 해야되는지 생각하면서 계속 성장할 수 있는 기간이 많이 남아있다. 이번 미션에 배운 내용들을 발판삼아, 다음 미션은 더 자신감있게 진행해보려 한다.
'우아한테크코스(프리코스)' 카테고리의 다른 글
전혀 예상치 못했던, 우아한테크코스 7기 최종 코딩테스트 후기 (3) | 2024.12.18 |
---|---|
우아한테크코스 7기 프리코스 최종 회고 (웹 프론트엔드) (7) | 2024.11.16 |
우아한테크코스 7기 프리코스 2주차 회고 (웹 프론트엔드) (2) | 2024.10.28 |