오늘은 프리코스 1주차 미션을 진행하면서, 느끼고 배운 것을 기록으로 남기겠습니다. 😊

이전까지는 개발을 진행하기에 앞서 구체적인 문서화를 토대로 개발을 진행해본 적이 없었습니다. 그래서 매번 간단한 기능을 구현하는 것 조차 많은 시간이 소요되었고, 다음 구현해야 할 일들을 잊어버리는 경우도 허다했죠.
그래서 "개발을 진행하기 전에 문서화를 먼저 해야겠다"라고 생각을 하고 있었는데, 이번 프리코스의 과제 진행 요구 사항에 기능을 구현하기 전 docs/README.md에 구현할 기능 목록을 정리해 추가한다 라고 나와있어, 문서화를 먼저 진행한 후에 개발을 진행하였습니다.
문서화를 처음 해보기에 문제를 5~10번 정도 읽어보며, 제 나름대로 문제의 큰 흐름을 다음과 같이 정리하였습니다.
요구 사항
1. 게임 시작
2. 컴퓨터 숫자 생성
3. 사용자 입력
4. 컴퓨터와 사용자 숫자 비교
5. 결과 출력
6. 게임 재시작 여부
7. 게임 종료
과거 문서화를 대충 하고 구현을 할 때에는 그려지지 않던 흐름이, 문서를 작성하고 난 후 명확히 보이기 시작했습니다. 정말 너무 신기했습니다...🙃
큰 흐름을 정리하고난 후, 이 흐름들을 유기적으로 연결하기 위해 세부적인 기능들을 다음과 같이 추가하였습니다.
요구 사항
1. 게임 시작
[ ] 게임 시작 사용자에게 알리기
2. 컴퓨터 숫자 생성
[ ] 컴퓨터 숫자 랜덤으로 생성하기
3. 사용자 입력
[ ] 사용자에게 숫자 입력 받기
[ ] 입력받은 숫자가 3개가 아닐 경우 강제 종료하기
[ ] 입력받은 숫자에 중복이 포함될 경우 강제 종료하기
[ ] 입력받은 숫자에 문자가 포함될 경우 강제 종료하기
4. 컴퓨터와 사용자 숫자 비교
[ ] 컴퓨터와 사용자의 숫자를 비교하기
5. 결과 출력
[ ] 비교 결과를 화면에 출력하기
[ ] 비교 결과가 일치하지 않는다면 재입력 받기
[ ] 비교 결과가 일치한다면 정답 메시지와 재시작 여부를 묻기
6. 게임 재시작 여부
[ ] 1 입력 시, 게임을 재시작하기
[ ] 2 입력 시, 게임을 종료하기
[ ] 이외의 값 입력 시, 게임을 강제 종료하기
7. 게임 종료
문서화를 진행하며 모든 세부 기능을 정리하고 나니 구현해야 할 기능 요구 사항들이 명확해졌을 뿐만 아니라, 문제에서 제시하는 프로그래밍 요구 사항 및 과제 진행 요구 사항에 대한 부분들이 보이기 시작했습니다.
프로그래밍 요구 사항 및 과제 진행 요구 사항
[ ] Fork & Clone을 한다.
[ ] docs/README.md에 구현할 기능 목록을 정리한다.
[ ] Node.js 18.17.1 버전에서 정상적으로 동작한다.
[ ] `@woowacourse/mission-utils`의 `Random` 및 `Console` API를 사용하여 구현한다.
[ ] 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.
[ ] 요구 사항에 명시된 출력값 형식을 지켜 구현한다.
[ ] js 파일 외에 어떠한 파일도 변경하지 않는다.
[ ] 모든 테스트가 성공하는지 확인한다.
[ ] 자바스크립트 코드 컨벤션을 지켰는지 확인한다.
문서화를 완료하고 난 후, 작성한 문서를 읽었을 때 구현해야 할 기능과 방향성을 제시해주었습니다. 그리고 조금 더 구체적으로 생각해보아야 할 부분들, 리팩토링이 필요할 수도 있는 부분들까지 말이죠.
문서화에 대한 중요성은 알고 있었지만, 문서화의 힘을 알지 못하고 문서화를 하지 않았던 과거의 저의 무지에 대해 반성하게 되는 시간인 것 같습니다.
리팩토링에 대해서는 이후에 얘기하겠지만, 문서화를 통한 개발은 저에게 새로운 세상을 알려준 것만 같았습니다. 문서에 작성한 순서에 맞춰 개발을 진행했기에 개발 도중 방향성을 잃지 않았고, 개발시간을 크게 단축 시킬 수 있었습니다. 🎉
"어떻게 해야 재사용성 높은 기능을 구현할 수 있을까?", "어떻게 해야 사용자에게 더 좋은 UI를 제공할 수 있을까"" 등의 고민은 개발자라면 누구나 가지고 있는 고민이라고 생각합니다.
저의 경우 담당하고 있는 파트를 작업하면서 그 즉시 해결하려고 했었습니다. 그래서 항상 일정이 미뤄지고, 원활한 작업이 이루어지지 않게 되었죠.
그러나, 문서화는 제가 가지고 있던 이러한 생각들을 깨 부수듯 큰 도움이 되어주었습니다. 문서화는 해야할 일들을 명확하게 순서대로 정리하여, 그 일에만 집중할 수 있게 해주었습니다.
그 결과, 모든 기능을 구현한 후 아래와 같이 리팩토링이 필요한 내역에 대해 다음과 같이 작성할 수 있었습니다.
리팩토링
[ ] 에러 메시지, 게임 안내 메시지와 같은 문자열은 상수로 치환하기
[ ] 사용자 입력을 검증하는 에러 체크 단일 함수로 리팩토링하기
[ ] 사용자 입력 변수 및 검증 함수 네이밍 수정하기
[ ] 조건문이 명시되지 않은 반복문에 대해 조건문 포함하기
[ ] 비교 결과를 반환하는 함수 출력 함수로 변경하기
[ ] 에러 메시지를 담은 상수 파일과 에러 체크 함수 통합하기
[ ] 숫자 중복 검사 함수 리팩토링하기
위 리팩토링 문서의 내용 모두가 기능 구현이 완성된 직후 작성된 것들은 아닙니다. 어떤 것은 리팩토링 도중에 추가되어진 것도 있습니다.
첫 포커스를 잡은 건 사용자 UI 였습니다. 프론트엔드 개발자는 사용자와의 최 접전에서 이야기를 나누기 때문에 사용자 UI를 최우선적으로 고려하자라고 생각하였습니다.
하지만, 위 문제의 경우 사용자 UI를 고려해야할 상황이 마땅하지 않은 것 같아 건너뛰었습니다.
두 번째로 다른 사람이 나의 코드를 볼 때 편안하고, 빠르게 볼 수 있도록, 즉 원활한 협업이 이루어질 수 있도록 주석과 네이밍에 대해 포커싱을 잡았습니다.
import {
...,
GAME_EXIT,
} from "./utils/validation.js";
do {
...
userSelectNumber = await Console.readLineAsync("");
validateUserSelectNumber(userSelectNumber);
} while (userSelectNumber !== GAME_EXIT);
과거의 저라면, 위 코드에서 크게 부족한 점을 찾지 못했을 것입니다. 물론 개인 프로젝트를 진행하는 입장에서 중요한 얘기가 아닐 수 있지만, 협업을 진행하고 있다면 이야기는 달라질 수 있습니다.
여러분은 위 코드를 보고 어떤 코드인지 바로 이해가 가시나요? 그렇다면 아래 코드는 어떤가요?
import {
...,
GAME_EXIT,
} from "./utils/validation.js";
do {
...
// 1(GAME_RESTART) 재시작, 2(GAME_EXIT) 종료
userSelectNumber = await Console.readLineAsync("");
validateUserSelectNumber(userSelectNumber);
} while (userSelectNumber !== GAME_EXIT);
고작 한 줄의 주석으로 우리는 더 쉽게 이 코드를 이해할 수 있게 되었습니다. 고작 이 주석 한 줄은 같이 협업하는 혹은 코드 리뷰를 해주는 개발자의 시간을 아껴주었습니다.
어떠신가요!? 굉장하지 않나요??😊🤗
마지막으로는 시간 복잡도와 공간 복잡도에 대해 생각하였습니다. 자료구조와 알고리즘을 공부하며 이런생각을 한 적이 있었습니다.
자료구조랑 알고리즘은 백준 문제 풀때만 알면 되는 거 아니야?
그렇다면 아래 코드를 확인해 봅시다.
function isDuplicationError(userInputNumber) {
for (let i = 0; i < userInputNumber.length; i++) {
if (userInputNumber.includes(userInputNumber[i])) {
throw new Error(DUPLICATION_ERROR);
}
}
return;
}
현재 주어진 코드는 중복을 검사하는 코드입니다. 중복을 검사하는데 필요한 시간 복잡도가 O(N^2) 인 것이 보이시나요? 물론 현재 주어진 문제에서 입력받는 숫자가 100의 자리수이기 때문에 시간 복잡도를 줄이기 위한 고려 대상이 아닐 수 있습니다.
하지만, 입력받는 수의 길이가 3이 아니라 100, 1000 아니 1억이 된다면 어떻게 될까요? 1억 * 1억,,, 아마 컴퓨터가 멈추다 못해 살려달라고 비는 상황이 올 수도 있을 것입니다.
문서화는 시간복잡도의 측면까지 고려할 수 있게 해주었습니다.
function isDuplicationError(userInputNumber) {
const uniqueNumber = new Set(userInputNumber);
if (uniqueNumber.size !== 3) {
throw new Error(DUPLICATION_ERROR);
}
return;
}
중복을 허용하지 않는 Set 자료구조를 이용하여 중복 검사를 실시할 수 있었고, 시간복잡도를 O(N) 으로 줄일 수 있었습니다. 수의 길이가 1억이 오면 단 1억번만 계산을 하면 끝이 나는 것이 되었죠!
문서화를 통해 체계적으로 작업 함으로써 주석과 네이밍 그리고 시간복잡도를 리팩토링 할 수 있었던 것 같습니다. 과거에는 생각지도 못했던 부분들을, 문서화를 통해 이루어내니 너무 뿌듯하고 보람찬 시간이였던 것 같습니다. 👏👏
이전 문서화 없이 개발할 때와 달리, 문서화를 통한 개발은 능률 뿐만이 아니라 소스코드를 더 구체적으로 분석할 수 있게 도와준 것 같습니다. 아까 위에서 언급한 주석 한줄의 추가는 사실 문서화가 되어있지 않은 상태였더라면, 찾지 못했을 것입니다.
아직까지 문서화에 익숙하지 않은 터라 숫자를 랜덤으로 생성하는 구체적인 방법, 중복이 포함된 것을 어떻게 체크할 것인지 등에 대한 방법들이 명확하게 작성되어 있지 않은 것을 보고 과거에 제가 얼마나 추상적으로 개발을 했는지 알 수 있었습니다.
다음 미션부터는 명확하게 기능들을 작성하고 해보아야겠습니다. 🎉
