후기 | 우아한 테크 코스 (프리코스)

전지웅·2022년 11월 27일
0
post-thumbnail

✍️ 우아한 테크 코스 후기

우아한 테크 코스란?

배달의 민족에서 장기간으로 하는 부트캠프라고 생각하면 될것 같다. 다른 부트캠프와 다른 점이라고 생각하는 것은 아무래도 스스로 발전할 수 있는 능력을 키우는 것입니다. 다른 파트와 협업을 하면서 소프트웨어 능력을 기를 수 있는 교육이다.

지원하게 된 계기

우아한 테크 코스를 지원하게 된 계기는 단순했다. js의 기초만 배운 것을 활용해보면서 나의 부족한 점을 스스로 찾아보고, 스스로 공부할 수 있는 동력이 필요했을 뿐이다. 그래서 이번 테크 코스의 프리코스를 통해서 js에 대해서 제대로 공부해보고, 어떻게 코드를 작성하는지, js 코딩 컨벤션등 많은 것을 배우고 싶어서 지원을 하게 되었다.

우테코 프리코스

원래는 프리코스는 1차를 합격한 사람들한테만 주어진 기회였다. 이번 5기에서는 많은 사람들에게 기회를 주고자 지원만 하면 프리코스에 참여를 할 수 있었다. 그래서 이번에 지원을 했던 것도있다. 우선 가장 큰 목표는 프리코스만 잘 해보자는 마인드로 시작을 했다. js를 제대로 배워본 적 없는 내가 1차 합격을 바라는 것은 아무래도 큰 욕심이였다. 그래서 프리코스만 제대로해서 js에 대한 지식을 쌓아나가자는 생각으로 시작을 했다.

온보딩

온보딩에서는 단순한 js를 통해서 알고리즘 문제를 푸는 것이였다. 총 7문제였다. 대부분이 문자열을 다루는 문제다 보니 파이썬으로 문제를 풀어온 나한테느 생각보다 쉽게 풀 수 있었다. 그러다 6번, 7번 문제가 생각을 조금 했어야 했다. 두 문제 전부 해시를 이용해서 풀어야 했기 때문에 조금 어려웠다. 프로그래머스에서 풀어본 적 있는 듯한 느낌이라 앞에 문제들에 비해서 조금 걸렸지만, 코드로 구현하는 것에 있어서는 금방 할 수 있었다.

6번 문제

🚀 기능 요구 사항

우아한테크코스에서는 교육생(이하 크루) 간 소통 시 닉네임을 사용한다. 간혹 비슷한 닉네임을 정하는 경우가 있는데, 이러할 경우 소통할 때 혼란을 불러일으킬 수 있다.

혼란을 막기 위해 크루들의 닉네임 중 같은 글자가 연속적으로 포함 될 경우 해당 닉네임 사용을 제한하려 한다. 이를 위해 같은 글자가 연속적으로 포함되는 닉네임을 신청한 크루들에게 알려주는 시스템을 만들려고 한다.

신청받은 닉네임 중 같은 글자가 연속적으로 포함 되는 닉네임을 작성한 지원자의 이메일 목록을 return 하도록 solution 메서드를 완성하라.

제한사항

  • 두 글자 이상의 문자가 연속적으로 순서에 맞추어 포함되어 있는 경우 중복으로 간주한다.
  • 크루는 1명 이상 10,000명 이하이다.
  • 이메일은 이메일 형식에 부합하며, 전체 길이는 11자 이상 20자 미만이다.
  • 신청할 수 있는 이메일은 email.com 도메인으로만 제한한다.
  • 닉네임은 한글만 가능하고 전체 길이는 1자 이상 20자 미만이다.
  • result는 이메일에 해당하는 부분의 문자열을 오름차순으로 정렬하고 중복은 제거한다.

실행 결과 예시

formsresult
[ ["jm@email.com", "제이엠"], ["jason@email.com", "제이슨"], ["woniee@email.com", "워니"], ["mj@email.com", "엠제이"], ["nowm@email.com", "이제엠"] ]["jason@email.com", "jm@email.com", "mj@email.com"]

문제를 보고 바로 해시를 이용해서 풀어야 겠다는 것은 알 수 있었다. 프로그래머스에서 비슷한 유형을 풀어보니 이제는 해결방법은 고민을 해야하지만, 무엇을 이용해서 풀어야 하는지에 대해서 바로 떠올릴 수 있다. js에도 dictionary타입이 있어 파이썬처럼 풀 수 있었다. 풀이는 간단하게 말하면 forms에 있는 이메일을 2개씩 잘라서 dictionary에 넣어서 있으면 계속 포함시키면서 일치하는 것을 확인하고, 겹치는 길이를 비교해서 result로 반환을 한다.

function problem6(forms) {
  var mydict = {};
  var answer = [];
  for (var i = 0; i < forms.length; i++) {
    for (var j = 0; j < forms[i][1].length - 1; j++) {
      var slice_str = forms[i][1].substring(j, j + 2);
      if (slice_str in mydict) mydict[slice_str].push(forms[i][0]);
      else mydict[slice_str] = [forms[i][0]];
    }
  }
  for (key of Object.keys(mydict)) {
    if (mydict[key].length > 1) {
      for (var i = 0; i < Object.keys(mydict[key]).length; i++) {
        answer.push(mydict[key][i]);
      }
    }
  }
  var set = new Set();
  answer.sort().forEach((item) => set.add(item));
  return [...set];
}

module.exports = problem6;

7번 문제

🚀 기능 요구 사항

레벨 2의 팀 프로젝트 미션으로 SNS(Social Networking Service)를 만들고자 하는 팀이 있다. 팀에 속한 크루 중 평소 알고리즘에 관심이 많은 미스터코는 친구 추천 알고리즘을 구현하고자 아래와 같은 규칙을 세웠다.

  • 사용자와 함께 아는 친구의 수 = 10점
  • 사용자의 타임 라인에 방문한 횟수 = 1점
    사용자 아이디 user와 친구 관계를 담은 이차원 배열 friends, 사용자 타임 라인 방문 기록 visitors가 매개변수로 주어질 때, 미스터코의 친구 추천 규칙에 따라 점수가 가장 높은 순으로 정렬하여 최대 5명을 return 하도록 solution 메서드를 완성하라. 이때 추천 점수가 0점인 경우 추천하지 않으며, 추천 점수가 같은 경우는 이름순으로 정렬한다.

제한사항

  • user는 길이가 1 이상 30 이하인 문자열이다.
  • friends는 길이가 1 이상 10,000 이하인 배열이다.
  • friends의 각 원소는 길이가 2인 배열로 [아이디 A, 아이디 B] 순으로 들어있다.
    • A와 B는 친구라는 의미이다.
    • 아이디는 길이가 1 이상 30 이하인 문자열이다.
  • visitors는 길이가 0 이상 10,000 이하인 배열이다.
  • 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
  • 동일한 친구 관계가 중복해서 주어지지 않는다.
  • 추천할 친구가 없는 경우는 주어지지 않는다.

실행 결과 예시

userfriendsvisitorsresult
"mrko"[ ["donut", "andole"], ["donut", "jun"], ["donut", "mrko"], ["shakevan", "andole"], ["shakevan", "jun"], ["shakevan", "mrko"] ]["bedi", "bedi", "donut", "bedi", "shakevan"]["andole", "jun", "bedi"]

7번 문제가 6번 문제에 비해서 떠올리기는 쉬웠다. 하지만 복잡하여 코드가 길었다. 이것도 dictionary를 이용해서 풀었다. 앞으로의 과제에서 indent를 2번이상 하면 안되서 이번과제부터 그것을 연습한다고, 함수로 많이 풀어서 썼다. 그래서 메인 함수의 코드는 간결하게 보인다.

function problem7(user, friends, visitors) {
  var sns = {};
  var temp = new Array();
  var answer = new Array();
  sns[user] = { score: 0, friends: [] };
  for (var i = 0; i < friends.length; i++) {
    make_connection(sns, friends[i][0], friends[i][1]);
  }
  for (friend of sns[user]["friends"]) {
    for (fof of sns[friend]["friends"]) {
      if (fof != user) sns[fof].score += 10;
    }
  }
  for (visit of visitors) visit_score(sns, visit);
  for (friend of sns[user]["friends"]) delete sns[friend];
  for (key of Object.keys(sns)) {
    if (sns[key].score > 0) temp.push({ name: key, score: sns[key].score });
  }
  var sortedSNS = temp.sort((a, b) => b.score - a.score);
  sortedSNS.sort((a, b) => {
    if (a.score == b.score) {
      let x = a.name;
      let y = b.name;
      if (x < y) return -1;
      if (x > y) return 1;
      return 0;
    }
  });
  for (var i = 0; i < 5; i++) {
    if (i == sortedSNS.length) break;
    answer.push(sortedSNS[i].name);
  }
  return answer;
}

function make_connection(sns, idA, idB) {
  if (idA in sns) sns[idA]["friends"].push(idB);
  else sns[idA] = { score: 0, friends: [idB] };
  if (idB in sns) sns[idB]["friends"].push(idA);
  else sns[idB] = { score: 0, friends: [idA] };
}

function visit_score(sns, id) {
  if (id in sns) sns[id].score++;
  else sns[id] = { score: 1, friends: [] };
}

module.exports = problem7;

1주차

문제와 프로그래밍 요구사항 링크

https://github.com/wldnd2/javascript-baseball

프로그래밍 요구사항

  1. indent는 3이 넘지 않도록 한다. (2까지만 허용한다.)
    • indent 2를 넘기지 않게 하기 위해서는 함수 또는 메서드로 분리하여 만든다.
  2. 함수 또는 메서드가 한 가지 일만 하도록 최대한 작게 만든다.
  3. Jest를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.
  4. 링크에 첨부된 js코드 컨벤션에 따라서 코딩을 해야한다.
    • 우아한 테크 코스의 js 스타일은 Airbnb의 스타일을 따른다.
  5. 기능 단위로 커밋을 진행하는데, 커밋 메시지 컨벤션에 따라 커밋을 진행한다.

문제 소스 코드

1주차 야구 게임을 하면서 저번 학기 이후에 오랜만에 클래스와 메서드에 대해서 공부를 했다. 오랜만에 객체를 다루니깐 너무 색달랐고, js에서 어떻게 클래스를 선언하고, 메서드를 어떻게 사용하는지 등 많은 것을 새롭게 알게 되었다. 1주차가 되면서 요구사항이 많아지면서 play()메소드 하나에 다 넣는 것이 아니라 다 분리를 하였다. 이번 미션을 하면서 클래스와 메소드에 대해서 공부를 할 수 있었다. 아래코드에서 흐름을 이해하기는 쉬울 것이다. play()로 시작을 해서 계속해서 다른 메소드를 호출한다. 그래서 읽어 내려가면 계속해서 반복을 한다.

const MissionUtils = require("@woowacourse/mission-utils");
const Random = MissionUtils.Random;
const Console = MissionUtils.Console;

class App {
  constructor() {
    this.computer_random_number = [];
    this.strike = 0;
    this.ball = 0;
    this.nothing = "낫싱";
    this.finish = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.\n";
  }
  play() {
    Console.print("숫자 야구 게임을 시작합니다.");
    this.replay();
  }
  makeRandomNumber() {
    let randomNumber = new Set();
    while (1) {
      randomNumber.add(MissionUtils.Random.pickNumberInRange(1, 9));
      if (randomNumber.size == 3) break;
    }
    return Array.from(randomNumber);
  }
  replay() {
    // 기능 1번 구현
    this.computer_random_number = this.makeRandomNumber();
    // 기능 2번 구현
    this.userInput();
  }
  userInput() {
    Console.readLine("숫자를 입력해주세요 : ", (user_input) => {
      // 사용자의 입력 체크후 return 받은 값을 이용
      const user_number = this.checkUserInput(user_input);
      this.baseballCount(user_number);
    });
  }
  checkUserInput(user_input) {
    // user_input의 set 변환
    const user_input_set = new Set(user_input.split(""));
    let user_num = Array.from(user_input_set);
    // user_input의 길이 체크 & 중복 체크
    if (user_input.length != 3 || isNaN(Number(user_input)) || user_input_set.size != 3 || user_num.includes("0")) throw Error("잘못된 입력입니다.");
    // user_input_set을 숫자로 바꾸어 배열로 return
    user_num.forEach((element, index) => {
      user_num[index] = Number(element);
    });
    return user_num;
  }
  baseballCount(user_number) {
    this.strike = 0;
    this.ball = 0;
    // 완전 일치하면 3스트라이트 출력후 -> 다시 게임할지 질문
    if (user_number.join("") == this.computer_random_number.join("")) {
      this.strike = 3;
      Console.print(`${this.strike}스트라이크`);
      Console.print("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
      return this.endUserResponse();
    }
    // 스트라이크 & 볼 증가
    user_number.forEach((element, index) => {
      if (this.computer_random_number[index] == element) this.strike++;
      else if (this.computer_random_number.includes(element)) this.ball++;
    });
    if (!(this.strike + this.ball)) {
      Console.print(this.nothing);
    } else if (this.strike > 0 && this.ball == 0) {
      Console.print(`${this.strike}스트라이크`);
    } else if (this.ball > 0 && this.strike == 0) {
      Console.print(`${this.ball}`);
    } else {
      Console.print(`${this.ball}${this.strike}스트라이크`);
    }
    this.userInput();
  }
  endUserResponse() {
    Console.readLine(this.finish, (user_response) => {
      if (user_response == "1") return this.replay();
      else if (user_response == "2") return Console.close();
    });
  }
}

module.exports = App;

2주차

문제와 프로그래밍 요구사항 링크

https://github.com/wldnd2/javascript-lotto

추가된 프로그래밍 요구사항

1주차의 요구사항에 추가된 요구사항들은 아래와 같다.

  1. 함수 또는 메서드의 길이가 15라인을 넘어가지 않도록 구현한다.
  • 함수 또는 메서드가 한 가지 일만 잘 하도록 구현한다.
  1. else를 지양한다.
  • if조건절에서 값을 return하는 방식으로 구현하는 else를 사용하지 않아도 된다.
  • 때로는 if/else, switch문을 사용하는 것이 더 깔끔해 보일 수 있다. 어느 경우에 쓰는 것이 적절할지 스스로 고민해 본다.
  1. 도메인 로직에 단위 테스트를 구현해야 한다. 단, UI(Console.readLine, Console.print) 로직에 대한 단위 테스트는 제외한다.

솔직히 말하면 2주차에서 나는 풀리퀘스트를 하지 못했다. 입력을 하면 정확하게 잘 돌아가지만, 테스트를 하면 계속 테스트중 1개가 틀려서 통괄르 하지 못했다. 최대한 내가 무엇지 잘못되었는지 게속 찾아 봤지만 못해서 ApplicationTest.js의 코드를 분석해볼려고 했다. 이것을 공부하다 보니 모듈화를 이해하고, TDD(테스트 주도 개발)이라는 것을 알게 되었다.

TDD와 모듈화를 공부해보면서 내가 아직 많이 부족하구나를 많이 알게 되었다. 계속 미션에 쫓겨 정확한 개념보다는 빨리 쓰기 위해서 필요한 것만 공부를 했다. 그렇다보니 처음 내가 계획한 목표와는 다르게 프리코스를 하고 있었던 것이다. 2주차까지 하면서 배운것도 많다보니 이것만으로도 충분하다고 생각을 하여 아쉽지만 2주차에서 풀리퀘스트를 멈추었다.

프리코스를 하면서 배운점

프리코스를 하면서 확실한건 js에 대해서 공부를 확실하게 하면서 적절한 사용법과 읽기 편한 코드를 어떻게 작성하는지에 대해서 알게 되었다. 온보딩에서는 js의 기본적인 문법과 es6를 알게 되었고, 1주차에서는 깃허브 커밋 컨벤션과 클래스, 메서드, 동기/비동기를 2주차에서는 모듈화, TDD, MVC패턴등 다양한 개념들을 접할 수 있었다. 프리코스는 확실히 요구사항이 정확하게 주어지다 보니 코드를 짜면서 어떻게 짜야하는지에 대해서 스스로 알아갈 수 있었다. 그리고 깃허브에 프리코스를 하는 사람들끼리 서로의 코드를 리뷰하면서 어떻게하면 더좋은 코드인지를 서로 서로 도와주면서 많은 도움을 받을수 있어 정말 좋았다. 그리고 자신의 코드 뿐만 아니라 다른 사람들의 코드를 보면서 자신이 부족한 점을 알 수 있어 좋은시간이였다.

짧은 시간동안 많은 것을 배우고, 어떻게 공부를 해나가야 하는지에 대해서 알 수 있어서 좋았다.

정리해야 할 개념들

  1. 동기/비동기 (promise)
  2. 클래스
  3. 모듈화
  4. MVC패턴
  5. TDD - jest
  6. 싱글톤

더 읽어보기

1.js코드 컨벤션
2.커밋 컨벤션

profile
경북대학교 컴퓨터학부 글로벌SW융합 전공

0개의 댓글