Preview
우아한 테크코스 - 프리코스 1주차에 참가했다.
미션 시작과 동시에 1번 문제를 풀면서 아주 야심찬 계획을 세웠다.
1주차인 만큼 기능 구현은 당연지사..
꼼꼼한 "기능 단위" 커밋 로그 남기기 / 1차로 완성된 코드를 다양한 예외 케이스들에 대한 고려 및 클린 코드로의 리팩토링 / 참고할 여러 코드 컨벤션에 맞추어 완성한 코드를 한번 더 마이그레이션 하기 / 코드를 수정한 모든 기록들까지 꼼꼼하게 남기려고 했다..
그럴싸한 계획이었다..
기능 구현 가운데 오류를 만나고, 예외 상황에 대해 헷갈리기 시작하며, 시간을 계속해서 잡아먹히기 전까지는..
-> 결론부터 말하면 완벽하게 시간 관리에 실패했다.
후반부엔 기능 구현에만 급급해서 커밋 로그도 몰아서 정리하면서 남기려다 결국 기능 단위가 아닌 문제 단위로 제출했다.
What was problem?
내가 세운 계획과 메뉴얼을 실행하기 위해서는 구현 자체에 드는 시간이 압도적으로 짧았어야 했다.
- 7문제에 대한 기초 기능 구현을 빠르게 완성시킨다.
- README.md 파일을 작성하며 기능 단위로 커밋을 진행한다.
- 코드를 하나씩 살펴보며, '예외 처리', '성능 향상' 등을 목표로 한 코드 자체적인 리팩토링을 한다.
- '에어비엔비 자바스크립트 컨벤션', '자체적으로 설정한 목표 컨벤션'에 맞추어 문법적인 변경과 난해한 코드에 대한 주석 달기 등 코드 외적인 스타일적 변경을 한다.
나의 경우에 프로세스 1번과 2번을 병행하다가, 후반부엔 1번에서 상당수 의 시간이 걸렸고, 기능 구현만 겨우 하여 제출하였다.
-> 2번 문제의 예외 상황에 대한 고민을 비롯해 한 가지 문제에서 다양한 테스트 케이스들에 대한 고민이 생기기 시작하자 6, 7번을 마무리 하지 못한 상황에서 계속해서 시간을 흘렀고, 결국 1번 기능 구현 단계부터 발목을 잡혔다.
README.md (컨벤션 + 각각의 미션들에 대해 기록한 파일)
💻 Section 0 - Applied Convention of JS-ONBOARDING & self manual for first_week mission.
참고한 js 코드 컨벤션: 에어비엔비 자바스크립트 코드 컨벤션.
프로그램 구현시 참고해본 내용:
-
indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
-
변수 선언시 var 를 사용하지 않는다. const 와 let 을 사용한다.
-
함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라.
-
함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.
- 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.
커밋 컨벤션:
🍎 chore: 자잘한 수정
📄 docs: 문서 관련
🖊 feat: 새로운 기능, 페이지 추가
🐞 fix: 버그 수정
💡 refactor: 코드 리팩토링
🧪 test: 테스트 관련
미션 진행시 순서(원래 계획).
0. 변경 사항, 및 수정 시 항상 add - commit을 함께 진행해준다. (README.md - 커밋 컨벤션에 맞게 커밋 로그 작성)
1. 코딩에 들어가기 전에 각 문항별로 기능을 정의해본다. -> README.md 파일에 명세한다.
2. 기능 단위로 코드를 작성하고 add - commit을 진행한다. (커밋 컨벤션에 맞게 커밋 로그 작성)
3. 전체 코드를 작성한 후에 npm test를 통해 예외 상황을 포함한 모든 test case들에 빠짐없이 잘 동작하는지 체크해본다.
4. 해당 코드가 README.md 파일의 "구현시 적용할 내용"에 fit 하는지 체크한다. -> 문제가 있는 부분은 수정한다.
5. 완성 후에 각각 함수에 대한 설명을 담은 주석을 붙여준다. -> 커밋 로그 작성. (add - commit / 커밋 컨벤션에 맞게!)
6. 전체 문제들의 솔루션들을 모아서 한번에 에어비엔비 자바스크립트 코드 컨벤션에 맞게 마이그레이션 한다.
📖 Section 1 - Problem 01. (책 펴기 놀이)
문제 해결을 위해 접근했던 방식.
- 메인 함수인 'problem1'에서 문제의 요구사항인 각각의 조건에 맞게 값을 반환해주는 기점을 나눠보았다.
- 매개변수들에 대해 예외 사항 생각해보기, 예외 처리 함수 작성.
- 문제의 핵심 기능인 각 자릿수의 합과 곱 중에서 가장 큰 값을 찾는 로직 작성. 메소드 분리.
- 여러 예외 상황들에 대한 테스트 케이스 추가, 검사 진행.
구현할 기능 목록.
- 주어진 기능 요구 사항에 맞게 배열 값을 확인 후 -1, 0, 1, 2 라는 숫자를 반환해주는 기능.
- 페이지 정보가 담겨있는 배열을 입력값으로 받아, 예외처리에 속하는 조건들을 검사해주는 기능.
- 페이지 값을 기준으로 각 자리 숫자를 더해보고, 곱해봐서 그 중 가장 큰 값을 반환시켜주는 기능.
예외 처리 케이스.
- 맨 첫과 끝 페이지는 펴지 않는다.
- 왼쪽 페이지는 홀수를, 오른쪽 페이지는 짝수를 반드시 갖는다.
- 페이지의 길이는 반드시 2 이다.
- 책 페이지이기 때문에 왼쪽 페이지와 오른쪽 페이지는 붙어있는 수여야 한다. (왼쪽 페이지 + 1 = 오른쪽 페이지).
🔐 Section 2 - Problem 02. (암호 해독)
문제 해결을 위해 접근했던 방식.
- 메인 함수인 'problem2'와 연속하는 중복 문자열을 삭제해주는 기능을 가진 함수로 두 개의 기능을 가진 함수로 나누었다.
- '연속되는 중복 문자열' 값을 구하기 위한 방법을 고민하다가, 반복문 내에서 '현재 참조중인 값'과 '마지막으로 참조한 값'을 비교해서, 값이 같으면 '연속되는 중복 문자열'의 조건을 충족하는 것으로, 현재 참조중인 값을 저장하고 있는 임의의 공간에 추가하지 않고, 마지막으로 참조한 값만 삭제시켜주면 로직이 완성되겠다고 생각했다. -> 로직 구현을 위해 배열과 pop 메소드를 활용하면 되겠다는 결론에 도달했다.
- 핵심 로직은 '연속하는 중복 문자들을 삭제해주는 기능'이다. -> 'deleteRepeate' 함수에 구현했다.
- 여러 다양한 형태의 문자열 테스트 케이스들을 추가해서 확인해보았다.
구현할 기능 목록.
- 매개변수를 받아서 연속하는 중복 문자들을 지워주는 함수를 호출해주고, 반환값을 기점으로 비어있는 문자열이면 빈 문자열을 반환해주는 함수.
- 연속하는 중복 문자들을 삭제해주는 기능.
주의할 점.
- 2개 이상의 연속되는 중복 문자열들이 있는 경우 한번에 중복되는 문자열을 다 제거해주어야한다.
🕹 Section 3 - Problem 03.(369 게임)
문제 해결을 위해 접근했던 방식.
- 핵심 로직이 '3', '6', '9'가 들어있는지 검사하고, 3, 6, 9 중에서 숫자가 나온만큼 카운트를 해서, 33의 경우 2번 333의 경우 3번을 카운팅 해주어야 한다. -> 정규 표현식을 통해 접근해야 겠다고 생각했다. -> 여러가지 정규식과 관련된 메소드들 중에서 .match 메소드를 이용하면 3, 6, 9중의 숫자들 중 각각의 숫자가 몇번 들어갔는지를 반환되는 배열의 길이를 통해 정확하게 체크할 수 있어서 해당 메소드를 채택했다.
- 특별하게 기능을 분할해서 구현할 내용이 없어서, 7개의 문제들 중 유일하게 메소드 분할 없이 구현했다.
- 여러 다양한 형태의 문자열 테스트 케이스들을 추가해서 확인해보았다.
구현할 기능 목록.
- 숫자 값을 받아, 1부터 해당 숫자 값까지 검사를하면서 3, 6, 9가 총 몇번이 반복되는지 카운트해서 해당 카운트 값을 반환해주는 기능.
주의할 점.
- 33, 333, 369 같은 숫자들의 경우 각각 1씩 카운트 하는게 아닌, 2, 3, 3으로 3, 6, 9 숫자들이 반복되는 횟수 한번에 하나씩 카운트 해주어야 한다.
🐸 Section 4 - Problem 04.(청개구리 문자열)
문제 해결을 위해 접근했던 방식.
- '알파벳'을 가지고 완전한 랜덤 매칭이 아닌, 일정한 패턴을 기준으로 매칭을 해주는 기능이 미션의 주요 포인트여서, 아스키코드를 활용해서 풀면 되겠다고 생각했다. charCodeAt, fromCharCode를 활용해서 매개변수로 받은 문자열의 각각 알파벳을 가지고 1. 알파벳 to 아스키코드, 2. 숫자를 가지고 일정한 패턴을 바탕으로 요구사항에 맞는 convert 기능 수행. 3. 다시 숫자 to 알파벳. 을 해주는 순서로 로직을 세분화해 접근했다.
- 일정한 패턴을 찾아내기 위해 고민하다가 1~10까지의 숫자를 빠르게 암산할 수 있는 방법인 대칭되는 숫자의 합(1+10 / 2+9 / 3+8 / 4+7 ... etc) x 반복되는 만큼의 수 + 나머지(5)의 개념을 적용해보았다. 대문자의 경우 각각의 알파벳이 대칭되는 수와의 합이 (65 + 90 / 66 + 89 ... etc) = 155으로 '155'를 기준으로 접근할 수 있고, 소문자의 경우 (97 + 122 / 96 + 121 ... etc) = 219로 '219'라는 숫자를 기준으로 접근할 수 있었다.
- 여러 다양한 형태의 문자열 테스트 케이스들을 추가해서 확인해보았다.
구현할 기능 목록.
- 문자열인 매개변수를 받아서 각각의 알파벳에 대칭하는 ASCII 코드로 변환해주는 기능. (메인 함수에서 작업할 예정)
- ASCII 코드로 변환된 코드를 155(대문자) / 219(소문자) 숫자를 기준으로 기능 요구 사항에 맞게 convert 해주는 기능. ()
- 변환된 ASCII 코드를 fromCharCode 메소드를 사용해 다시 숫자 to 문자열 형태로 변환해주는 기능.
💵 Section 5 - Problem 05.(효율적인 현금 출금)
문제 해결을 위해 접근했던 방식.
- 우선 실행 결과 예시에 나온 배열 값들의 의미 그대로 5만원권 부터 1원 짜리 동전까지 순서대로 담는 것이 규칙이어서, 해당 배열을 생성해서 조건을 검사하고, 값을 담을 때 사용해야 겠다고 생각했다.
- 핵심 기능인 화폐 단위별로 잘라내는 부분을 구현하기 위해 개발하다보니 비교하는 값만 변하고, 같은 동작을 반복하는 구조가 있어서, 처음에 재귀 형태를 사용할까 고민하다가, 그냥 메소드를 분할하고, 세분화한 메소드를 반복문 안에서 돌려서 구현해야겠다고 생각했다.
- 여러 다양한 형태의 문자열 테스트 케이스들을 추가해서 확인해보았다.
구현할 기능 목록.
- 현재 금액과 검사할 화폐 단위를 인자로 받아서 해당 화폐 단위로 바꿀 수 있는지 계산하고, 바꿀 수 있다면, 바꾼 화폐의 갯수와 남은 금액을 반환해주는 기능.
- 메인 함수인 2번 기능은 전체 금액을 인자값으로 받아와서, 배열과 반복문을 만들고 1번 함수를 조건에 맞게 호출해주고 계산에 필요한 값들을 넘겨주는 작업을 한다.
📛 Section 6 - Problem 06.(중복된 닉네임 체크)
문제 해결을 위해 접근했던 방식.
- 우선 핵심 로직인 2 글자 이상의 중복된 닉네임을 체크해주기 전에, 잘못된 값들에 대한 처리부터 우선적으로 진행했다.
- 요구 사항들에 맞게 순차적으로 메소드 하나당 해결할 문제를 1개씩 주고, 최대한 함수들을 세분화시켜서 구현해보았다.
- 핵심 로직을 구현하는데 있어서, 2 글자 이상을 기준점으로 잡고, 2글자 이상의 겹치는 단어들을 객체 형태로 담아두고(처음에 배열로 접근했으나 중복되는 배열들이 사라지지 않는 이슈가 생겨서 고민하다가 배열이 아닌 객체 자료구조 형태로 담아두기로 했다), 한번 더 반복문이 돌면서 정답인 계정들을 찾아서 email 들을 정답에 담아 반환시키는 구조로 짜보았다. (처음 접근할 때는 한번만 닉네임들을 돌면서 반복되는 단어를 찾고, 중복되는 단어를 바탕으로 검사를 진행하려고 했으나 복잡도가 폭발적으로 올라가는 이슈가 생겨서, 우선 중복되는 2글자 이상의 글자들을 담아 놓은 뒤에, 두번에 걸쳐 해당 단어들을 전체 닉네임에 검사하는 구조로 짜보았다)
- 여러 다양한 형태의 문자열 테스트 케이스들을 추가해서 확인해보았다.
구현할 기능 목록.
- 메인 메소드인 problem6에서는 forms 인자 값을 받아서 이메일, 닉네임을 각각 조건에 맞게 검사해주는 함수들에 담아 처리한다. 정상적인 값일 때를 기준으로 중복된 단어를 찾아주는 함수를 호출 -> 마지막으로 중복된 단어들을 담고 있는 단어장과 forms를 넘겨서 인자값으로 받은 유저들중에서 중복된 단어를 지닌 계정의 이메일을 반환해주는 함수를 호출하고, 반환값인 정답을 담은 유저 이메일 리스트를 sort 한뒤에 리턴한다.
- 두번째 기능은 이메일 체크 함수로, 제한 사항에 나와있는 기준점에 맞는 형식인지 하나하나의 이메일 값을 받아와서 체크하고 형식 조건에 맞는지 아닌지에 따라 정수형태로 반환해준다.
- 세번째 기능은 닉네임 체크 함수로, 제한 사항에 나와있는 기준점에 맞는 형식인지 하나하나의 닉네임 값을 받아와서 체크하고 형식 조건에 맞는지 아닌지에 따라 정수형태로 반환해준다.
- 네번째 기능은 2글자 이상의 중복된 글자가 현재 검사중인 닉네임에 있는지 체크해주는 함수로, 중복 단어장 안에 현재 검사중인 중복 문자가 들어가 있다면 1을 해당 객체의 키값으로 담고, 들어가 있지 않다면 0을 담아서 반환해준다. 여기서 검사는 하나의 계정에서 하나의 닉네임을 기준으로 진행한다.
- 다섯번째 기능은 2글자 이상의 중복 문자열 단어장과 forms를 인자값으로 받아와서, 전체 forms에 들어있는 계정들을 반복하면서 중복 단어장에 들어있는 단어가 포함된 계정의 이메일을 배열에 담아서 반환해준다.
- 마지막 여섯번째 기능은 2 ~ 3번에서 이메일과 닉네임의 형식 검사의 반환값을 정수형으로 받은 이유이기도 한데, 정수형의 합을 계산해서 변수에 담아두고, 해당 변수를 wrongForm 함수의 인자값으로 받아온다. 받아온 값을 기준으로 조건을 검사해서, 이메일과 닉네임중 어느쪽의 오류인지 문자열 형태로 반환시켜서 알려준다. (이메일의 오류, 닉네임의 오류, 두 형식 모두의 오류의 경우를 전부 체크할 수 있다.)
주의할 점.
- 단어가 2글자가 아닌 그 이상의 길이 일 수 있다.
- 중복된 문자열 단어가 하나가 아닐 수도 있다. (하나의 계정에서 여러개의 중복된 단어가 포함되어 있을 때 여러번 해당 계정이 추가되면 안된다.)
👯♀️ Section 7 - Problem 07.(sns 친구추천)
문제 해결을 위해 접근했던 방식.
- 우선 친구 추천 알고리즘인 사용자와 함께 아는 친구의 수의 배점 10점과 visitor라는 이름으로 들어오는 방문자 배열에서 방문 횟수에 대한 1번당 배점 1점을 기준으로 각각 전체 평가 점수를 담은 객체와 이를 바탕으로 각각 두개의 조건에 맞게 평가해주는 기능을 담은 함수들로 분할해서 접근해야겠다고 생각했다.
- 처음부터 기능들을 함수 단위로 잘게 쪼개고 접근해보려 했으나, 처음 접근할 때 각각의 핵심 기능들을 모조리 분할한 뒤에 짜는게 쉽지 않아서 우선 기능이 돌아가게 만든 뒤에 추가적으로 수정하는 과정을 거쳐가며 개발했다. 친구 목록을 담은 객체와 채점 알고리즘 기준으로 채점한 점수들을 담은 점수표 객체를 만들어서, 기능 요구 사항 구현에 접근해보았다.
- 시간 부족 이슈로 다양한 케이스들을 돌려보지 못하고, 주어진 케이스를 기반으로만 체크해보았다. (막바지 문제 기능 구현에 급급했다..)
구현할 기능 목록.
- 친구 추천 알고리즘에 따라 받아온 인자값을 바탕으로 채점표와 친구 목록 정보를 담을 객체를 만들어주고, 해당 채점을 진행할 함수를 호출해준다. ->
- 방문자의 채점을 진행하는 기능으로, 유저의 방문자 기록과 채점표를 인자값으로 받아, 기존에 방문한 횟수가 없으면 1점으로 초기화, 있으면 1점씩 더해주고, 채점표를 반환해준다.
- 친구 목록지 생성, 함께 아는 친구에 대한 채점을 진행하는 기능으로, 유저와 친구 관계를 담은 friends와 user 정보를 인자값으로 받아서 친구 목록을 작성. -> 친구 목록의 함께 아는 친구의 수에 대한 검사를 진행해서 채점을 하고, 친구 목록과 채점표를 반환해준다.
Review
What if? (만약 돌아간다면..?)
우선 집 나간 멘탈을 추스리기 위해 처음 경험해보는 단 기간 챌린지인 만큼 데이터가 부족했다고 스스로를 독려하는 중이다.. ^~^;;
만약 돌아간다면..
- 첫 하루를 풀로 사용하더라도 설정한 컨벤션을 먼저 머릿속에 익힌다.
- 기능 구현 단계부터 바로 컨벤션 적용.
- 기능을 완성할 때 마다 기능 단위로 커밋 진행.
- 코드를 완성한 후에 미처 못 맞춘 컨벤션이나 예외 처리가 있는 체크.
의 순서로 계획을 변경했을 것 같다.
: 문제를 풀면서 코드 작성과 동시에 문제 단위로 커밋 / README.md 파일 명세 / 컨벤션 적용을 했다면..
처음엔 문제당 할애 시간이 많이 소요되어 볼 수 있어도, 마무리 단계에 이르러서는
"기능 구현 완성"과 동시에 필요한 대부분의 할 일 task들을 처낼 수 있기 때문에
같은 작업을 반복할 필요 없이 훨씬 효율적인 방법이었을 것 같다.
Feedback
1주차 미션에 대한 피드백을 받고, 상당히 뜨끔했다.
"커밋 메시지를 의미 있게 작성한다" / "불필요한 console.log를 남기지 않는다" 등 내가 지키고자 했지만, 지키지 못했던 내용들이 많이 들어있었다.
코수타를 보면서 새롭게 알게된 인상 깊은 내용도 있었다.
-
테스트 케이스 기반의 개발론. 테스트 주도 개발(TDD)
- 요구 사항을 이해하고 바로 개발에 들어가지 않고, 먼저 예외 상황과 다양한 테스트 케이스들을 먼저 생각해본다. -> 충분히 여러가지 다양한 케이스들을 고려한 뒤에 개발을 진행한다.
: 1번과 같은 방법으로 개발을 진행했다면, 미처 생각하지 못했던 내용들에 대해 충분히 고민해보고 개발을 진행할 수 있게 된다.
: 내가 겪었던 시행착오와 상당 수의 시간을 잡아 먹혔던 부분이 바로 개발 중간 혹은 완성 단계에서의 예외 상황에 대한 이슈들이었다. 이런 부분을 조기에 잡아줄 수 있다는 점에서 상당히 인상 깊었다.
-
미션의 요구 사항 정의 및 풀이도 '정답'이 있는 것 처럼 사고하지 말고, 미션의 모든 부분에 대한 해석과 정의 풀이 과정까지도 스스로 결론 내린다고 생각하고 접근해라.
- 실무를 예시로 들어주시면서, 우리가 풀어야 할 수 많은 문제들에는 실제로 정답이 정해져 있는 내용들이 많다. -> 친절하게 정답이 나와 있지 않은 문제들에 대해 스스로 사고하고 판단해서 결론을 내리는 습관을 들이는 것 역시 개발자로서 중요한 소양이다.
Conclusion
1주차 미션 과정에서의 뼈 아픈 시간 관리 실패로 인한 여러 문제점들을 자양분 삼아..
2주차 미션 부터는 보다 체계적인 시간 관리 + 데드라인에서 1일 정도 여유를 남겨두고 완성해볼 계획이다.
1주차 미션에서의 피드백 내용들, 스스로 느낀 부족함과 보완점을 바탕으로 한층 더 성장해서 진행해보자.
매주 아쉬운 점과 스스로 느끼는 부족함에 대한 갈증을 원동력 삼아 성장한다면, 프리코스만으로도 많은 성장을 이뤄낼 수 있을 것 같아서 기분이 좋아졌다.