[우아한테크코스] 최종 코테 회고 및 최종 합격 후기

Nayoung·2024년 12월 31일
1


와아... 12/14에 봤던 우테코 최종 코테의 결과가 이번 27일 발표됐다. 믿기지 않게도 결과는 합격...! 프리코스부터 두 달간 우테코에 매우 몰입했기 때문에 막상 코테를 끝내고 나온 직후엔 후련한 마음이 들어 계속 웃음이 났던 기억이 난다. 엄마는 밝은 내 표정을 보고 잘본 것 같냐 기대하셨지만 내 대답은 "아니 망했어 ㅎ" 였다. 망했다면서 후련한 표정으로 계속 비식비식 웃는 나를 보며 의아해하셨지만 기능 구현도 다 못하고 나온 것치고 이상할 정도로 기분이 너무 후련해서 나도 신기했다. 그만큼 최선을 다했다는 생각이 들어 그랬던 것 같다.

그러나 코테가 끝나고 며칠이 지나자 조금 우울해졌었다. 이제 새해 계획을 세워야하는데 코테를 분명 망쳤다고 생각했기에 우테코를 제외한 일정을 세워야했지만 실낱같이 남은 기대감이 우테코 빠진 새해 계획 세우기를 주춤거리게 했다. 결국 최종 심사 결과 메일을 받을 때까지 신년 계획은 세우지 못한 채 한 구석 답답한 마음을 갖고 일상을 살았다.

그리고 코테를 본지 2주째 되던 27일, 최종 합격 메일을 받을 수 있었다! 합격 글자를 보자마자 심장이 쿵 떨어져 소리 먼저 꺅 지른 뒤 텍스트를 몇 번을 확인했는지 모르겠다. 혹시 간절한 마음이 만들어낸 환상일까 싶어서...(?)🤣

그렇게 너무 얼떨떨해 기쁜지도 모르겠는 하루가 지나자 매우 지독한 감기에 앓아누워버렸다. 병원에 가니 다행히 독감은 아니라고 했다. 긴장이 풀린 탓인지 요즘 감기가 유행이어서인지는 모르겠지만 정말 오랜만에 걸린 감기다. 열이 펄펄 끓어올라 합격의 에너지로 뭐라도 막 하고싶었던 마음과 달리 여태 약 먹고 자기만 반복했다. 기침을 너무 많이 했더니 진짜 온몸이 다 아프다...🥲

아무튼 감사하게도 나의 신년계획은 우테코로 가득차게 되었다. 이제 천천히 블로그 회고글을 마무리하고 우테코 시작 전까지 정보처리기사 필기를 취득해보려한다.

1. 최종 코테 준비

1.1 풀어본 문제들

우선 스터디원 분과 함께 이번년도 7기 프리코스 1~4주차 과제를 다시 풀어보았다. 2주차 과제까지는 각자 코테 환경과 같이 gpt 없이 5시간을 재고 풀어서 깃허브에 공유하고 코드리뷰를 남기는 식으로 진행했다. 그리고 3~4주차는 페어 프로그래밍을 하며 같이 풀었는데 이 경험이 정말 도움이 많이 되었다. 페어 프로그래밍이라는 걸 처음해본 나는 너무 민폐만 끼칠까봐 걱정했는데, 음... 역시 내가 더 너무 많이 얻어갔던 스터디같지만... 스터디원 분이 감사하게도 정말 친절하게 이끌어주시고 가르쳐주셔서 많이 배울 수 있었던 좋은 경험이었다. 그리고 스터디원 분이 로직을 짜실 때 느꼈던 게, 나는 뭔가 한치 앞만 보며 우다다 구현하느라 뒤에 가서 꼬이는 경우가 많았는데, 스터디원 분은 기능 단위 구현을 할 때도 몇 수 앞을 보면서 로직을 짜셔서 신기했다. 그리고 메소드 체이닝도 잘 활용하셔서 간편하게 코드가 완성되는 것도 신기했다.

페어 프로그래밍은 평소에 갇혀있던 내 개발 습관과 지식에서 벗어나 상대의 지식과 프로그래밍 접근 방식을 이해하고 배워볼 수 있어서 꼭 해보면 좋은 스터디 방식인 것 같다.


그리고 개인적으로는 5기와 6기 최종코테 문제도 실제 코테와 같은 조건으로 풀어보았다. 두 문제 다 생각보다 5시간 안에 기능 구현 후 리팩토링까지 하기에 넉넉했기 때문에 이 수준으로 나온다면 해볼만하다고 생각했었다.

하지만 역시나 걱정은 4주차 과제인 편의점 수준으로 나왔을 때였는데, 기능 구현 자체가 어려운 것보다 구현 시간이 너무 오래걸릴 것 같았기 때문이었다.
그래서 편의점 문제를 시간 재고 다시 풀어보았지만...이미 세번 째 푼 문제인데도 6시간이 넘게 걸렸다 ㅠ

1.2 준비해간 나만의 템플릿(?)

예고된 최종 코테의 주의사항은 기본적으로 오픈북이지만 AI 도구 사용은 금한다는 것이었다.

그래서 최종 코딩테스트를 보기 전, 나는 모든 프리코스에 공통적으로 쓰였던 InputVeiw.js, OutputView.js, Parser.js, Validator.js, Message.js 파일들을 재사용성 좋게 템플릿화 시켜 준비해갔다. 최종 코테가 어떤 식으로 나올지는 모르겠지만 기능 구현을 최대한 빨리하는 게 중요할 것 같았기 때문이다. 그래서 규칙을 위반하지 않는 한 빠르게 개발을 할 수 있는 환경을 최대한 준비해가고 싶었다.

내가 준비한 파일들 중 Validator.js 파일을 예시로 적어두자면 아래와 같이 자주 사용해야하는 검증 로직들을 쉽게 재활용할 수 있게 작성해갔다. Message.js는 에러 출력 메세지나 안내 메세지 등의 상수를 저장해둘 상수객체 파일이고 Definition.js는 구분자 상수 같은 정의 상수(?)들을 저장해둔 상수객체 파일이다.
인자를 string, array 와 같이 겹칠 위험이 큰 흔한 네이밍을 하는 게 맞는지는 모르겠으나, 일단 코테를 빠르게 완성시키기 위한 Validator이므로 받아야하는 인자의 타입으로 네이밍해두었다.

isStringLengthInRange 처럼 범위(숫자 상수) 안에 있는 값인지를 판단해야하는 경우 원래는 최솟값과 최댓값을 Definition.js에서 설정하게 했었지만 5기, 6기 문제를 풀 때 실용성을 따져보니 이렇게 인자로 받는 게 재활용하기 더 좋은 것 같아서 리팩토링하였다.

import { DEFINITION } from '../constants/Definition.js';
import { MESSAGE } from '../constants/Message.js';

export const Validator = {
  isEmpty: (string) => {
    if (string === null || string.trim().length === 0 || !string) {
      throw new Error(MESSAGE.ERROR.IS_EMPTY);
    }
  },
  isMaxStringLength: (string, max) => {
    if (string.length > max) {
      throw new Error(MESSAGE.ERROR.IS_MAX_STRING_LENGTH);
    }
  },
  isStringLengthInRange: (string, min, max) => {
    if (string.length > max || string.length < min) {
      throw new Error(MESSAGE.ERROR.IS_NOT_STRING_LENGTH_RANGE);
    }
  },
  isMaxArrayLength: (array, max) => {
    if (array.length > max) {
      throw new Error(MESSAGE.ERROR.IS_MAX_ARRAY_LENGTH);
    }
  },
  isArrayLengthInRange: (array, min, max) => {
    if (array.length > max || array.length < min) {
      throw new Error(MESSAGE.ERROR.IS_NOT_ARRAY_LENGTH_RANGE);
    }
  },
  isCorrectArrayLength: (array, number) => {
    if (array.length !== number) {
      throw new Error('[ERROR] 입력 요소의 수가 알맞지 않습니다.');
    }
  },
  isSameInArray: (array) => {
    const arrayCopy = [...new Set([...array])];
    if (array.length !== arrayCopy.length) {
      throw new Error(MESSAGE.ERROR.IS_SAME_IN_ARRAY);
    }
  },
  isNumber: (number) => {
    if (typeof number !== 'number' || Number.isNaN(number)) {
      throw new Error(MESSAGE.ERROR.NOT_NUMBER);
    }
  },
  isMaxNumber: (number) => {
    if (number >= DEFINITION.MAX.NUMBER) {
      throw new Error(MESSAGE.ERROR.IS_MAX_NUMBER);
    }
  },
  isNumberInMinAndMax: (number, min, max) => {
    if (number < min || number > max) {
      throw new Error(MESSAGE.ERROR.IS_NOT_NUMBER_RANGE);
    }
  },
  isNaturalNumber: (number) => {
    if (
      number <= 0 ||
      !Number.isInteger(number) ||
      Number.isNaN(Number(number))
    ) {
      throw new Error(MESSAGE.ERROR.NOT_NATURAL_NUMBER);
    }
  },
  isInputInArray: (input, inputArray) => {
    if (inputArray.includes(input)) {
      throw new Error(MESSAGE.ERROR.IS_INPUT_IN_ARRAY);
    }
  },
  notInputInArray: (input, inputArray) => {
    if (!inputArray.includes(input)) {
      throw new Error(MESSAGE.ERROR.IS_INPUT_IN_ARRAY);
    }
  },
  isValidBooleanInput: (input) => {
    // {Y or N}의 입력값이 왔는지 확인하는 로직(Definition 에서 Y나 N대신 다른 값으로 쉽게 바꿀 수 있게 리팩토링했음)
    // 대문자 소문자도 정확히 같아야함
    if (!DEFINITION.VALID_TRUE_OR_FALSE.includes(input.trim())) {
      throw new Error(
        MESSAGE.ERROR.NOT_VALID_TRUE_OR_FALSE(
          DEFINITION.VALID_TRUE_INPUT,
          DEFINITION.VALID_FALSE_INPUT,
        ),
      );
    }
  },
  isValidEdge: (string) => {
    if (
      string[0] !== DEFINITION.VALID_EDGE_START ||
      string[string.length - 1] !== DEFINITION.VALID_EDGE_END
    ) {
      throw new Error(
        MESSAGE.ERROR.NOT_VALID_EDGE(
          DEFINITION.VALID_EDGE_START,
          DEFINITION.VALID_EDGE_END,
        ),
      );
    }
  },
  isFalseThrowError: (boolean, message) => {
    if (!boolean) {
      throw new Error(message);
    }
  },
};

1.3 돌아보니 아쉬웠던 점

Validator의 에러 메세지를 상수로 빼지말 걸 그랬다... 에러 메세지 안에 [ERROR]만 앞에 포함되어 있으면 구체적인 에러 메세지를 정해주지 않았던 프리코스와 달리 최종 코딩테스트에서는 에러 메세지의 문구까지 지정해주었기 때문이다. 뭐, 이거 조금 수정한다고 오래 걸리는 건 아니지만...좀 과한 준비였던 것 같다.

(아, 참고로 내 코드처럼 Validator를 작성해 테스트코드에 통과하려면, validator를 호출하는 상위 파일인 main.js나 app.js에서 추가 작업이 필요하다. throw new Error를 검사하지 않고 에러메세지를 출력하는지를 검사하는 테스트코드라면 7기 기준 MissionUtils.Console.print에 스파이를 붙여 에러메세지를 출력하는지를 확인하기 때문에, 상위 파일에서 try...catch 문을 써서 ...catch (error) MissionUtils.Console.print(error.message)와 같이 명시적으로 Console.print가 에러 "메세지"를 출력하도록 처리해주어야한다. 이렇게하면 Validator에서 던진 에러의 메세지를 프린트할 수 있다.)

그리고 프리코스 종료 후 받았던 안내 메일에 아래와 같은 안내가 있었는데,

물론 이 안내에 따라 1~4주차를 다시 구현해보고 4주차는 3번이나 풀어보긴했지만 그래도 내가 끝끝내 시간 내에 못 풀었던 4주차 미션을 이 부분을 되새기며 더 깊게 풀고 분석해볼걸 하는 후회가 남았다. 지난 기수 최종 코테 문제들을 풀 시간에 말이다. ㅠㅠ 스포하자면 최종 코테는 결국 4주차 편의점과 매우 유사하게 출제되었다.

코딩테스트도 결국 시험이기에 출제자의 의도와 일종의 시험 범위인 프리코스 4주차들 문제의 핵심을 파악해 공부하는 것이 좋았던 것 같다.

나름 준비하면서 내가 파악했던 7기 프리코스의 핵심은
[ 구분자 및 split 메서드 활용, 재고관리와 같은 상태관리 시스템, md로 작성된 줄글파일을 데이터로 파싱, new Date와 DateTimes(우테코 API)의 활용 ]이었다.

나머지들은 파악한 대로 잘 준비해갔지만...Date 객체는 공부하지 않고 갔다. 이유는... 그냥 까먹었다...블로그에 포스팅하면서 공부해야지 미뤄두다가...
그리고 결국 내가 생각했던 7기 프리코스의 핵심대로 문제는 출제되었다. 그런데 생각보다 기본 기능을 구현하기 위해 자바스크립트의 Date 객체를 활용해야하는 문제가 나왔고 자바스크립트가 Date 객체에 어떤 메서드들을 갖고 있고 각 메소드들은 어떻게 사용되는지를 전혀 몰랐던 나는 급하게 구글링하며 메서드 사용법을 현장에서 익혔다. ㅠㅠ 기능 구현 과정은 아래에 자세히 후술하겠지만, 아무튼 여기서 꽤 시간을 잡아먹은 이슈로... 기능 구현을 조금 남기고 마무리하지 못하게되었었다.

만약 내년도 우테코에 도전할 사람이 내 포스팅을 읽게된다면 준비하는데에 참고가 되길 바라며 아쉬웠던 점을 아래 요약해보겠다.

  • 상수 파일은 딱히 미리 준비 안해도 될 것 같다.
  • 지난 기수 코테 문제들을 풀어보는 것보다 프리코스 과제들을 최종 코테와 같은 환경에서 완벽하게 풀어내는 것에 더 집중하는 게 좋은 것 같다. 지난 기수 코테 문제들은 완전 여유 남을 때 긴장풀 겸 가볍게 풀어보는 걸로 추천...!
  • 자주 사용하는 Validator와 같은 파일들은 템플릿화 시켜서 준비해가면 시간 단축에 꽤 용이하다!
  • 프리코스 과제들을 회고하며 여기서 어떤 로직이 핵심이었는지를 생각해보고 활용한 자바스크립트 내장 api에 대해 완벽히 숙지하고 가면 좋을 것 같다!

2. 최종 코테

7기 최종 코딩테스트는 "출석부" 문제였다. csv로 작성된 출석 기록들을 데이터로 파싱해서 요구하는 출석부 기능을 구현하면 됐다.

그리고 내가 코딩테스트를 망쳤다고 생각한 이유이다. 기본 기능 테스트도 다 통과를 못한 채 내게 됐다. Date 객체 메소드 활용처럼 미리 공부해왔다면 버리지않았을 시간을 잡아먹느라 시간 분배에 실패했고, 1~20분만 더 있었어도 기능을 완성해낼 수 있었을 것 같아 너무나 아쉬웠던 결과였다.

그런데 '돌아가는 쓰레기'라도 만들어서 내라고 할만큼 기능 구현을 강조하는 우테코 코테에 어떻게 합격할 수 있었는지는 아직도 잘 모르겠다.
생각보다 코테 비중보다 자소서와 소감문, 프리코스 점수, 블로그 같은 게 더 비중이 높았던 걸까? 아니면 활발한 코드리뷰의 흔적?

3. 소감

아무튼 감사하게도 우테코에 합격하게 되었다. 이 회고글을 마치는 지금, 새해를 1시간 앞두고 있다. 정말 많은 일이 있었던 한 해였는데, 우테코 합격으로 마무리할 수 있게되어 정말 기쁘다. 평소 디자이너로서 참여한 프로젝트에 조금씩 자바스크립트를 적용해보는 등 개발 업무에 관심은 있었지만 비전공자인 내가 갑자기 진로를 바꿔 개발자로 일할 수 있을지는 모르겠어서 사실 개발자로 취업을 하고 싶다는 생각은 하지 않았었다. 그저 자바스크립트/리액트를 조금 아는 디자이너라고 말할 수 있게 되는 것을 목표로 삼고 있을 뿐이었는데, 문득 찾아온 이 기회가 나도 개발자가 될 수 있는지에 대한 답을 줄 것 같아서 마음이 싱숭생숭 설레는 밤이다.

내년 이맘쯤의 나는 어떤 경험을 하고 어떤 성장을 했을까? 기대를 담아, 신년 계획을 세우러 가야겠다.

profile
프론트엔드 개발자로 성장하고 싶은 그래픽 디자이너입니다!

0개의 댓글